Passed
Pull Request — 4 (#10241)
by Nicolaas
07:37 queued 01:05
created

TaskRunner::index()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 50
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 6
eloc 32
c 4
b 0
f 0
nc 8
nop 1
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
    /**
60
     * list of options - can be filtered by adding the `q` request variable (e.g. `/dev/tasks/?q=test` OR `vendor/bin/sake dev/tasks q=test`)
61
     * @param  HTTPRequest $index
62
     * @return DBHTMLText|string - depends on whether it is `Director::is_cli()` request (returns string) or not (returns DBHTMLText)
0 ignored issues
show
Bug introduced by
The type SilverStripe\Dev\DBHTMLText 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...
63
     */
64
    public function index($request = null)
65
    {
66
        $baseUrl = Director::absoluteBaseURL();
67
        $tasks = $this->getTasks();
68
        $filter = (string) trim($request->requestVar('q'));
69
        if($filter) {
70
            $tasks = array_filter(
71
                $tasks,
72
                function ($v) use ($filter) {
73
                    $t = $v['title'] ?? '';
74
                    $d = $v['description'] ?? '';
75
                    return
76
                        stripos((string) $t, $filter) !== false &&
77
                        stripos((string) $d, $filter) !== false;
78
                }
79
            );
80
        }
81
        if (Director::is_cli()) {
82
            // CLI mode
83
            $output = 'SILVERSTRIPE DEVELOPMENT TOOLS: Tasks' . PHP_EOL . '--------------------------' . PHP_EOL . PHP_EOL;
84
85
            foreach ($tasks as $task) {
86
                $output .= sprintf(' * %s: sake dev/tasks/%s%s', $task['title'], $task['segment'], PHP_EOL);
87
            }
88
89
            return $output;
90
        }
91
92
        $list = ArrayList::create();
93
94
        foreach ($tasks as $task) {
95
            $list->push(ArrayData::create([
96
                'TaskLink' => $baseUrl . 'dev/tasks/' . $task['segment'],
97
                'Title' => $task['title'],
98
                'Description' => $task['description'],
99
            ]));
100
        }
101
102
        $renderer = DebugView::create();
103
        $header = $renderer->renderHeader();
104
        $header = $this->addCssToHeader($header);
105
106
        $data = [
107
            'Tasks' => $list,
108
            'Header' => $header,
109
            'Footer' => $renderer->renderFooter(),
110
            'Info' => $renderer->renderInfo('SilverStripe Development Tools: Tasks', $baseUrl),
111
        ];
112
113
        return ViewableData::create()->renderWith(static::class, $data);
114
    }
115
116
    /**
117
     * Runs a BuildTask
118
     * @param HTTPRequest $request
119
     */
120
    public function runTask($request)
121
    {
122
        $name = $request->param('TaskName');
123
        $tasks = $this->getTasks();
124
125
        $title = function ($content) {
126
            printf(Director::is_cli() ? "%s\n\n" : '<h1>%s</h1>', $content);
127
        };
128
129
        $message = function ($content) {
130
            printf(Director::is_cli() ? "%s\n" : '<p>%s</p>', $content);
131
        };
132
133
        foreach ($tasks as $task) {
134
            if ($task['segment'] == $name) {
135
                /** @var BuildTask $inst */
136
                $inst = Injector::inst()->create($task['class']);
137
                $title(sprintf('Running Task %s', $inst->getTitle()));
138
139
                if (!$inst->isEnabled()) {
140
                    $message('The task is disabled');
141
                    return;
142
                }
143
144
                $inst->run($request);
145
                return;
146
            }
147
        }
148
149
        $message(sprintf('The build task "%s" could not be found', Convert::raw2xml($name)));
150
    }
151
152
    /**
153
     * @return array Array of associative arrays for each task (Keys: 'class', 'title', 'description')
154
     */
155
    protected function getTasks()
156
    {
157
        $availableTasks = [];
158
159
        $taskClasses = ClassInfo::subclassesFor(BuildTask::class);
160
        // remove the base class
161
        array_shift($taskClasses);
162
163
        foreach ($taskClasses as $class) {
164
            if (!$this->taskEnabled($class)) {
165
                continue;
166
            }
167
168
            $singleton = BuildTask::singleton($class);
169
            $description = $singleton->getDescription();
170
            $description = trim($description);
171
172
            $desc = (Director::is_cli())
173
                ? Convert::html2raw($description)
174
                : $description;
175
176
            $availableTasks[] = [
177
                'class' => $class,
178
                'title' => $singleton->getTitle(),
179
                'segment' => $singleton->config()->segment ?: str_replace('\\', '-', $class),
180
                'description' => $desc,
181
            ];
182
        }
183
184
        return $availableTasks;
185
    }
186
187
    /**
188
     * @param string $class
189
     * @return boolean
190
     */
191
    protected function taskEnabled($class)
192
    {
193
        $reflectionClass = new ReflectionClass($class);
194
        if ($reflectionClass->isAbstract()) {
195
            return false;
196
        } elseif (!singleton($class)->isEnabled()) {
197
            return false;
198
        }
199
200
        return true;
201
    }
202
203
    /**
204
     * Inject task runner CSS into the heaader
205
206
     * @param string $header
207
     * @return string
208
     */
209
    protected function addCssToHeader($header)
210
    {
211
        $css = (array) $this->config()->get('css');
212
213
        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...
214
            return $header;
215
        }
216
217
        foreach ($css as $include) {
218
            $path = ModuleResourceLoader::singleton()->resolveURL($include);
219
220
            // inject CSS into the heaader
221
            $element = sprintf('<link rel="stylesheet" type="text/css" href="%s" />', $path);
222
            $header = str_replace('</head>', $element . '</head>', $header);
223
        }
224
225
        return $header;
226
    }
227
}
228