Passed
Push — master ( d3a1c2...061090 )
by Nicolaas
02:20
created

AllLinks::ListOfAllControllerMethods()   C

Complexity

Conditions 12
Paths 156

Size

Total Lines 69
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 10
Bugs 2 Features 0
Metric Value
cc 12
eloc 39
c 10
b 2
f 0
nc 156
nop 0
dl 0
loc 69
rs 6.4999

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Sunnysideup\TemplateOverview\Api;
4
5
6
use ReflectionClass;
7
use ReflectionMethod;
8
9
use Sunnysideup\TemplateOverview\Api\SiteTreeDetails;
10
11
12
use Sunnysideup\TemplateOverview\Api\AllLinks;
13
use Sunnysideup\TemplateOverview\Api\W3cValidateApi;
14
15
16
use SilverStripe\Control\Director;
17
use SilverStripe\Core\Convert;
18
use SilverStripe\Security\Member;
19
use SilverStripe\Security\Group;
20
use SilverStripe\Core\Injector\Injector;
21
use SilverStripe\CMS\Model\SiteTree;
22
use SilverStripe\CMS\Controllers\ContentController;
23
use SilverStripe\Core\ClassInfo;
24
use SilverStripe\Admin\ModelAdmin;
25
use SilverStripe\Admin\LeftAndMain;
26
use SilverStripe\Admin\CMSMenu;
27
use SilverStripe\ORM\DataObject;
28
use SilverStripe\ORM\DB;
29
use SilverStripe\Control\Controller;
30
use SilverStripe\Control\HTTPApplication;
31
use SilverStripe\Control\HTTPRequestBuilder;
32
use SilverStripe\Core\Manifest\ClassLoader;
33
use SilverStripe\Dev\SapphireTest;
34
use SilverStripe\Dev\BuildTask;
35
use SilverStripe\Dev\TaskRunner;
36
use SilverStripe\Core\Config\Config;
37
use SilverStripe\Core\CoreKernel;
38
use SilverStripe\Core\Startup\ErrorControlChainMiddleware;
39
use SilverStripe\Versioned\Versioned;
40
41
42
use SilverStripe\Core\Config\Configurable;
43
use SilverStripe\Core\Injector\Injectable;
44
use SilverStripe\Core\Extensible;
45
46
class AllLinks
47
{
48
49
    use Extensible;
50
    use Injectable;
51
    use Configurable;
52
53
    public static function is_admin_link($link)
54
    {
55
        return substr(ltrim($link, '/'),  0 , 5) === 'admin' ? true : false;
56
    }
57
58
    /**
59
     * url snippets that if found in links should exclude the link altogether.
60
     * e.g. 'admin/registry'
61
     *
62
     * @var array
63
     */
64
    private static $exclude_list = [];
65
66
    /**
67
     * List of alternative links for modeladmins
68
     * e.g. 'admin/archive' => 'CMSEditLinkForTestPurposesNOTINUSE'
69
     *
70
     * @var array
71
     */
72
    private static $model_admin_alternatives = [];
1 ignored issue
show
introduced by
The private property $model_admin_alternatives is not used, and could be removed.
Loading history...
73
74
75
    /**
76
     * @var int
77
     */
78
    private static $number_of_examples = 7;
79
80
    /**
81
     * @var array
82
     */
83
    private static $custom_links = [];
84
85
    /**
86
     * @var array
87
     */
88
    private static $controller_name_space_filter = [];
89
90
    /**
91
    * @var array
92
    */
93
    protected $allNonCMSLinks = [];
94
95
    /**
96
    * @var array
97
    */
98
    protected $pagesOnFrontEnd = [];
99
100
    /**
101
    * @var array
102
    */
103
    protected $dataObjectsOnFrontEnd = [];
104
105
    /**
106
    * @var array
107
    */
108
    protected $otherControllerMethods = [];
109
110
    /**
111
    * @var array
112
    */
113
    protected $customNonCMSLinks = [];
114
115
116
    /**
117
    * @var array
118
    */
119
    protected $allCMSLinks = [];
120
121
    /**
122
    * @var array
123
    */
124
    protected $pagesInCMS = [];
125
    /**
126
     * @var array
127
     */
128
    protected $dataObjectsInCMS = [];
129
130
    /**
131
     * @var array
132
     */
133
    protected $modelAdmins = [];
134
135
    /**
136
     * @var array
137
     */
138
    protected $leftAndMainLnks = [];
139
140
141
    /**
142
     * @var array
143
     */
144
    protected $customCMSLinks = [];
145
146
    /**
147
     * @var array
148
     * Pages to check by class name. For example, for "ClassPage", will check the first instance of the cart page.
149
     */
150
    protected $siteTreeClassNames = [];
151
152
    /**
153
     * returns an array of allNonCMSLinks => [] , allCMSLinks => [], otherControllerMethods => []
154
     * @return array
155
     */
156
    public function getAllLinks()
157
    {
158
159
        $this->siteTreeClassNames = $this->listOfAllSiteTreeClasses();
160
161
        foreach($this->Config()->get('custom_links') as $link) {
162
            $link = '/'.ltrim($link, '/').'/';
163
            if(self::is_admin_link($link)) {
164
                $this->customCMSLinks[] = $link;
165
            } else {
166
                $this->customNonCMSLinks[] = $link;
167
            }
168
        }
169
        $this->pagesOnFrontEnd = $this->ListOfPagesLinks(false);
170
        $this->dataObjectsOnFrontEnd = $this->ListOfDataObjectsLinks(false);
171
172
        $this->allNonCMSLinks = $this->addToArrayOfLinks($this->allNonCMSLinks, $this->pagesOnFrontEnd);
173
        $this->allNonCMSLinks = $this->addToArrayOfLinks($this->allNonCMSLinks, $this->dataObjectsOnFrontEnd);
174
        $this->allNonCMSLinks = $this->addToArrayOfLinks($this->allNonCMSLinks, $this->customNonCMSLinks);
175
        sort($this->allNonCMSLinks);
176
177
        $this->pagesInCMS = $this->ListOfPagesLinks(true);
178
        $this->dataObjectsInCMS = $this->ListOfDataObjectsLinks(true);
179
        $this->models = $this->ListOfAllModelAdmins();
0 ignored issues
show
Bug Best Practice introduced by
The property models does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
180
        $this->leftAndMainLnks = $this->ListOfAllLeftAndMains();
181
182
        $this->allCMSLinks = $this->addToArrayOfLinks($this->allCMSLinks, $this->pagesInCMS);
183
        $this->allCMSLinks = $this->addToArrayOfLinks($this->allCMSLinks, $this->dataObjectsInCMS);
184
        $this->allCMSLinks = $this->addToArrayOfLinks($this->allCMSLinks, $this->modelAdmins);
185
        $this->allCMSLinks = $this->addToArrayOfLinks($this->allCMSLinks, $this->leftAndMainLnks);
186
        $this->allCMSLinks = $this->addToArrayOfLinks($this->allCMSLinks, $this->customCMSLinks);
187
        sort($this->allCMSLinks);
188
189
        $this->otherControllerMethods = $this->ListOfAllControllerMethods();
190
191
        return [
192
            'allNonCMSLinks' => $this->allNonCMSLinks,
193
            'allCMSLinks' => $this->allCMSLinks,
194
            'otherLinks' => $this->otherControllerMethods,
195
        ];
196
    }
197
198
199
    /**
200
     * Takes {@link #$classNames}, gets the URL of the first instance of it
201
     * (will exclude extensions of the class) and
202
     * appends to the {@link #$urls} list to be checked
203
     *
204
     * @param bool $pageInCMS
205
     *
206
     * @return array
207
     */
208
    private function ListOfPagesLinks($pageInCMS = false)
209
    {
210
        //first() will return null or the object
211
        $return = [];
212
        foreach ($this->siteTreeClassNames as $class) {
213
            for($i = 0; $i < $this->Config()->number_of_examples; $i++) {
214
                $excludedClasses = $this->arrayExcept($this->siteTreeClassNames, $class);
215
                $page = Versioned::get_by_stage($class, Versioned::LIVE)
216
                    ->exclude(["ClassName" => $excludedClasses])
217
                    ->sort(DB::get_conn()->random().' ASC')
218
                    ->first();
219
                if (! $page) {
220
                    $page = Versioned::get_by_stage($class, Versioned::DRAFT)
221
                        ->exclude(["ClassName" => $excludedClasses])
222
                        ->sort(DB::get_conn()->random().' ASC')
223
                        ->first();
224
                }
225
                if($page) {
226
                    if ($pageInCMS) {
227
                        $url = $page->CMSEditLink();
228
                        $return[] = $url;
229
                        $return[] = str_replace('/edit/', '/settings/', $url);
230
                        $return[] = str_replace('/edit/', '/history/', $url);
231
                    } else {
232
                        $url = $page->Link();
233
                        $return[] = $url;
234
                    }
235
                }
236
            }
237
        }
238
239
        return $return;
240
    }
241
242
    /**
243
     *
244
     * @param bool $inCMS
245
     *
246
     * @return array
247
     */
248
    private function ListOfDataObjectsLinks($inCMS = false)
249
    {
250
        //first() will return null or the object
251
        $return = [];
252
        $list = ClassInfo::subclassesFor(DataObject::class);
253
        foreach ($list as $class) {
254
            if(! in_array($class, array_merge($this->siteTreeClassNames, [DataObject::class]))) {
255
                if($this->isValidClass($class)) {
256
                    for($i = 0; $i < $this->Config()->number_of_examples; $i++) {
257
                        $obj = DataObject::get_one(
258
                            $class,
259
                            ["ClassName" => $class],
260
                            null,
261
                            DB::get_conn()->random().' ASC'
262
                        );
263
                        if ($obj) {
264
                            if ($inCMS) {
265
                                if($obj->hasMethod('CMSEditLink')) {
266
                                    $return[] = $obj->CMSEditLink();
267
                                }
268
                                if($obj->hasMethod('CMSAddLink')) {
269
                                    $return[] = $obj->CMSAddLink();
270
                                }
271
                                if($obj->hasMethod('CMSListLink')) {
272
                                    $return[] = $obj->CMSListLink();
273
                                }
274
                                if($obj->hasMethod('PreviewLink')) {
275
                                    $return[] = $obj->PreviewLink();
276
                                }
277
                            } else {
278
                                if($obj->hasMethod('Link')) {
279
                                    $return[] = $obj->Link();
280
                                }
281
                                if($obj->hasMethod('getLink')) {
282
                                    $return[] = $obj->getLink();
283
                                }
284
285
                            }
286
                        }
287
                    }
288
                }
289
            }
290
        }
291
292
        return $return;
293
    }
294
295
296
    /**
297
     *
298
     * @return array
299
     */
300
    private function ListOfAllLeftAndMains()
301
    {
302
        //first() will return null or the object
303
        $return = [];
304
        $list = CMSMenu::get_cms_classes();
305
        foreach ($list as $class) {
306
            if($this->isValidClass($class)) {
307
                $obj = Injector::inst()->get($class);
308
                if ($obj) {
309
                    $url = $obj->Link();
310
                    if($url) {
311
                        $return[] = $url;
312
                    }
313
                }
314
            }
315
        }
316
317
        return $return;
318
    }
319
320
321
    /**
322
     * returns a list of all model admin links
323
     * @return array
324
     */
325
    public function ListOfAllModelAdmins()
326
    {
327
        $links = [];
328
        $modelAdmins = CMSMenu::get_cms_classes(ModelAdmin::class);
329
        if ($modelAdmins && count($modelAdmins)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $modelAdmins of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
330
            foreach ($modelAdmins as $modelAdmin) {
331
                $obj = singleton($modelAdmin);
332
                $modelAdminLink = '/'.$obj->Link();
333
                $modelAdminLinkArray = explode("?", $modelAdminLink);
334
                $modelAdminLink = $modelAdminLinkArray[0];
335
                //$extraVariablesLink = $modelAdminLinkArray[1];
336
                $links[] = $modelAdminLink;
337
                $modelsToAdd = $obj->getManagedModels();
338
                if ($modelsToAdd && count($modelsToAdd)) {
339
                    foreach ($modelsToAdd as $key => $model) {
340
                        if (is_array($model) || !is_subclass_of($model, DataObject::class)) {
341
                            $model = $key;
342
                        }
343
                        if (!is_subclass_of($model, DataObject::class)) {
344
                            continue;
345
                        }
346
                        $modelLink = $modelAdminLink.$this->sanitiseClassName($model)."/";
347
                        for($i = 0; $i < $this->Config()->number_of_examples; $i++) {
348
                            $item = $model::get()
349
                                ->sort(DB::get_conn()->random().' ASC')
350
                                ->First();
351
                            $exceptionMethod = '';
352
                            foreach($this->Config()->get('model_admin_alternatives') as $test => $method) {
353
                                if(! $method) {
354
                                    $method = 'do-not-use';
355
                                }
356
                                if(strpos($modelAdminLink, $test) !== false) {
357
                                    $exceptionMethod = $method;
358
                                }
359
                            }
360
                            if($exceptionMethod) {
361
                                if($item && $item->hasMethod($exceptionMethod)) {
362
                                    $links = array_merge($links, $item->$exceptionMethod($modelAdminLink));
363
                                }
364
                            } else {
365
                                //needs to stay here for exception!
366
                                $links[] = $modelLink;
367
                                $links[] = $modelLink."EditForm/field/".$this->sanitiseClassName($model)."/item/new/";
368
                                if ($item) {
369
                                    $links[] = $modelLink."EditForm/field/".$this->sanitiseClassName($model)."/item/".$item->ID."/edit/";
370
                                }
371
                            }
372
                        }
373
                    }
374
                }
375
            }
376
        }
377
378
        return $links;
379
    }
380
381
    /**
382
     * @todo break up!
383
     * @return array
384
     */
385
    public function ListOfAllControllerMethods()
386
    {
387
        $controllerLinks = [];
388
        $allowedActions = [];
389
        $finalArray = [];
390
        $finalFinalArray = [];
391
392
        $classes = ClassInfo::subclassesFor(Controller::class);
393
        $isValid = AllLinksControllerInfo::set_valid_name_spaces($this->Config()->controller_name_space_filter);
0 ignored issues
show
Bug introduced by
The type Sunnysideup\TemplateOver...\AllLinksControllerInfo was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Unused Code introduced by
The assignment to $isValid is dead and can be removed.
Loading history...
394
395
        //foreach($manifest as $class => $compareFilePath) {
396
        //if(stripos($compareFilePath, $absFolderPath) === 0) $matchedClasses[] = $class;
397
        //}
398
        // $manifest = ClassLoader::inst()->getManifest()->getClasses();
399
        foreach ($classes as $className) {
400
            $isValid = AllLinksControllerInfo::is_valid_controller($className);
401
            if(! $isValid) {
402
                continue;
403
            }
404
            $controllerLinks[$className] = '';
405
406
            //main links
407
408
            //custom links
409
            $customLinks = AllLinksControllerInfo::find_custom_links($className);
410
            foreach($customLinks as $customLink) {
411
                $finalArray[$customLink] = $className;
412
            }
413
            $link = AllLinksControllerInfo::find_link($className);
414
            if($link) {
415
                $controllerLinks[$className] = $link;
416
            }
417
            $allowedActions[$className] = AllLinksControllerInfo::find_allowed_actions($className);
418
        }
419
        // die('---');
420
        //construct array!
421
        foreach ($allowedActions as $className  => $methods) {
422
            $link = $controllerLinks[$className];
423
            if($link) {
424
                $finalArray[$link] = $className;
425
            } else {
426
                $link = '???';
427
            }
428
            if(substr($link, -1) !== '/') {
429
                $link = $link.'/';
430
            }
431
            if(is_array($methods)) {
432
                foreach($methods as $method) {
433
                    unset($allowedActions[$className][$method]);
434
                    $finalArray[$link.$method.'/'] = $className;
435
                }
436
            }
437
        }
438
439
        foreach($finalArray as $link => $className) {
440
            $finalFinalArray[] = [
441
                'ClassName' => $className,
442
                'Link' => $link,
443
            ];
444
        }
445
        usort($finalFinalArray, function ($a, $b) {
446
            if ($a['ClassName'] !== $b['ClassName']) {
447
               return $a['ClassName'] <=> $b['ClassName'];
448
            }
449
450
            return $a['Link'] <=> $b['Link'];
451
        });
452
453
        return $finalFinalArray;
454
    }
455
456
457
458
    /**
459
      * Pushes an array of items to an array
460
      * @param Array $array Array to push items to (will overwrite)
461
      * @param Array $pushArray Array of items to push to $array.
462
      */
463
    private function addToArrayOfLinks($array, $pushArray)
464
    {
465
        $excludeList = $this->Config()->exclude_list;
466
        foreach ($pushArray as $pushItem) {
467
            //clean
468
            if(self::is_admin_link($pushItem)) {
469
                $pushItem = str_replace('?stage=Stage', '', $pushItem);
470
            }
471
            $pushItem = $this->sanitiseClassName($pushItem);
472
            $pushItem = '/'.Director::makeRelative($pushItem);
473
474
            if(is_array($excludeList) && count($excludeList)) {
475
                foreach($excludeList as $excludeItem) {
476
                    if(stripos($pushItem, $excludeItem) !== false) {
477
                        continue 2;
478
                    }
479
                }
480
            }
481
            if(! in_array($pushItem, $array)) {
482
                array_push($array, $pushItem);
483
            }
484
        }
485
        return $array;
486
    }
487
488
    /**
489
     * returns a list of all SiteTree Classes
490
     * @return array
491
     */
492
    private function listOfAllSiteTreeClasses()
493
    {
494
        $pages = [];
495
        $siteTreeDetails = Injector::inst()->get(SiteTreeDetails::class);
496
        $list = $siteTreeDetails->ListOfAllSiteTreeClasses();
497
        foreach ($list as $page) {
498
            $pages[] = $page->ClassName;
499
        }
500
501
        return $pages;
502
    }
503
504
    /**
505
      * Takes an array, takes one item out, and returns new array
506
      * @param Array $array Array which will have an item taken out of it.
507
      * @param $exclusion Item to be taken out of $array
0 ignored issues
show
Bug introduced by
The type Sunnysideup\TemplateOverview\Api\Item was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
508
      * @return Array New array.
509
      */
510
    private function arrayExcept($array, $exclusion)
511
    {
512
        $newArray = $array;
513
        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...
514
            if ($newArray[$i] == $exclusion) {
515
                unset($newArray[$i]);
516
            }
517
        }
518
        return $newArray;
519
    }
520
521
522
523
    /**
524
     * Sanitise a model class' name for inclusion in a link
525
     *
526
     * @param string $class
527
     * @return string
528
     */
529
    protected function sanitiseClassName($class)
530
    {
531
        return str_replace('\\', '-', $class);
532
    }
533
534
535
    protected function isValidClass($class)
536
    {
537
        $obj = new ReflectionClass($class);
538
        if($obj->isAbstract()) {
539
            return false;
540
        }
541
        return true;
542
    }
543
544
545
546
547
}
548