Passed
Pull Request — 4 (#10241)
by Nicolaas
14:03
created

TaskRunner::index()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 50
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 32
c 1
b 0
f 0
nc 8
nop 0
dl 0
loc 50
rs 8.7857
1
<?php
2
3
namespace SilverStripe\Dev;
4
5
use ReflectionClass;
6
use SilverStripe\Control\Controller;
7
use SilverStripe\Control\Director;
8
use SilverStripe\Control\HTTPRequest;
9
use SilverStripe\Core\ClassInfo;
10
use SilverStripe\Core\Config\Configurable;
11
use SilverStripe\Core\Convert;
12
use SilverStripe\Core\Injector\Injector;
13
use SilverStripe\Core\Manifest\ModuleResourceLoader;
14
use SilverStripe\ORM\ArrayList;
15
use SilverStripe\Security\Permission;
16
use SilverStripe\Security\Security;
17
use SilverStripe\View\ArrayData;
18
use SilverStripe\View\ViewableData;
19
20
class TaskRunner extends Controller
21
{
22
23
    use Configurable;
24
25
    private static $url_handlers = [
0 ignored issues
show
introduced by
The private property $url_handlers is not used, and could be removed.
Loading history...
26
        '' => 'index',
27
        '$TaskName' => 'runTask'
28
    ];
29
30
    private static $allowed_actions = [
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
31
        'index',
32
        'runTask',
33
    ];
34
35
    /**
36
     * @var array
37
     */
38
    private static $css = [
0 ignored issues
show
introduced by
The private property $css is not used, and could be removed.
Loading history...
39
        'silverstripe/framework:client/styles/task-runner.css',
40
    ];
41
42
    protected function init()
43
    {
44
        parent::init();
45
46
        $allowAllCLI = DevelopmentAdmin::config()->get('allow_all_cli');
47
        $canAccess = (
48
            Director::isDev()
49
            // We need to ensure that DevelopmentAdminTest can simulate permission failures when running
50
            // "dev/tasks" from CLI.
51
            || (Director::is_cli() && $allowAllCLI)
52
            || Permission::check("ADMIN")
53
        );
54
        if (!$canAccess) {
55
            Security::permissionFailure($this);
56
        }
57
    }
58
59
    public function index()
60
    {
61
        $baseUrl = Director::absoluteBaseURL();
62
        $tasks = $this->getTasks();
63
        $filter = (string) trim($request->requestVar('q'));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $request seems to be never defined.
Loading history...
64
        if($filter) {
65
            $tasks = array_filter(
66
                $tasks,
67
                function ($v) use ($filter) {
68
                    $t = $v['title'] ?? '';
69
                    $d = $v['description'] ?? '';
70
                    return 
71
                        stripos((string) $t, $filter) !== false &&
72
                        stripos((string) $d, $filter) !== false;
73
                }
74
            );
75
        }
76
        if (Director::is_cli()) {
77
            // CLI mode
78
            $output = 'SILVERSTRIPE DEVELOPMENT TOOLS: Tasks' . PHP_EOL . '--------------------------' . PHP_EOL . PHP_EOL;
79
80
            foreach ($tasks as $task) {
81
                $output .= sprintf(' * %s: sake dev/tasks/%s%s', $task['title'], $task['segment'], PHP_EOL);
82
            }
83
84
            return $output;
85
        }
86
87
        $list = ArrayList::create();
88
89
        foreach ($tasks as $task) {
90
            $list->push(ArrayData::create([
91
                'TaskLink' => $baseUrl . 'dev/tasks/' . $task['segment'],
92
                'Title' => $task['title'],
93
                'Description' => $task['description'],
94
            ]));
95
        }
96
97
        $renderer = DebugView::create();
98
        $header = $renderer->renderHeader();
99
        $header = $this->addCssToHeader($header);
100
101
        $data = [
102
            'Tasks' => $list,
103
            'Header' => $header,
104
            'Footer' => $renderer->renderFooter(),
105
            'Info' => $renderer->renderInfo('SilverStripe Development Tools: Tasks', $baseUrl),
106
        ];
107
108
        return ViewableData::create()->renderWith(static::class, $data);
109
    }
110
111
    /**
112
     * Runs a BuildTask
113
     * @param HTTPRequest $request
114
     */
115
    public function runTask($request)
116
    {
117
        $name = $request->param('TaskName');
118
        $tasks = $this->getTasks();
119
120
        $title = function ($content) {
121
            printf(Director::is_cli() ? "%s\n\n" : '<h1>%s</h1>', $content);
122
        };
123
124
        $message = function ($content) {
125
            printf(Director::is_cli() ? "%s\n" : '<p>%s</p>', $content);
126
        };
127
128
        foreach ($tasks as $task) {
129
            if ($task['segment'] == $name) {
130
                /** @var BuildTask $inst */
131
                $inst = Injector::inst()->create($task['class']);
132
                $title(sprintf('Running Task %s', $inst->getTitle()));
133
134
                if (!$inst->isEnabled()) {
135
                    $message('The task is disabled');
136
                    return;
137
                }
138
139
                $inst->run($request);
140
                return;
141
            }
142
        }
143
144
        $message(sprintf('The build task "%s" could not be found', Convert::raw2xml($name)));
145
    }
146
147
    /**
148
     * @return array Array of associative arrays for each task (Keys: 'class', 'title', 'description')
149
     */
150
    protected function getTasks()
151
    {
152
        $availableTasks = [];
153
154
        $taskClasses = ClassInfo::subclassesFor(BuildTask::class);
155
        // remove the base class
156
        array_shift($taskClasses);
157
158
        foreach ($taskClasses as $class) {
159
            if (!$this->taskEnabled($class)) {
160
                continue;
161
            }
162
163
            $singleton = BuildTask::singleton($class);
164
            $description = $singleton->getDescription();
165
            $description = trim($description);
166
167
            $desc = (Director::is_cli())
168
                ? Convert::html2raw($description)
169
                : $description;
170
171
            $availableTasks[] = [
172
                'class' => $class,
173
                'title' => $singleton->getTitle(),
174
                'segment' => $singleton->config()->segment ?: str_replace('\\', '-', $class),
175
                'description' => $desc,
176
            ];
177
        }
178
179
        return $availableTasks;
180
    }
181
182
    /**
183
     * @param string $class
184
     * @return boolean
185
     */
186
    protected function taskEnabled($class)
187
    {
188
        $reflectionClass = new ReflectionClass($class);
189
        if ($reflectionClass->isAbstract()) {
190
            return false;
191
        } elseif (!singleton($class)->isEnabled()) {
192
            return false;
193
        }
194
195
        return true;
196
    }
197
198
    /**
199
     * Inject task runner CSS into the heaader
200
201
     * @param string $header
202
     * @return string
203
     */
204
    protected function addCssToHeader($header)
205
    {
206
        $css = (array) $this->config()->get('css');
207
208
        if (!$css) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $css 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...
209
            return $header;
210
        }
211
212
        foreach ($css as $include) {
213
            $path = ModuleResourceLoader::singleton()->resolveURL($include);
214
215
            // inject CSS into the heaader
216
            $element = sprintf('<link rel="stylesheet" type="text/css" href="%s" />', $path);
217
            $header = str_replace('</head>', $element . '</head>', $header);
218
        }
219
220
        return $header;
221
    }
222
}
223