Passed
Pull Request — 4 (#8209)
by Ingo
09:07
created

DevelopmentAdmin::init()   B

Complexity

Conditions 8
Paths 36

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
nc 36
nop 0
dl 0
loc 28
rs 8.4444
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Dev;
4
5
use SilverStripe\Core\Config\Config;
6
use SilverStripe\Core\Injector\Injector;
7
use SilverStripe\Control\Director;
8
use SilverStripe\Control\HTTPRequest;
9
use SilverStripe\Control\HTTPResponse;
10
use SilverStripe\Control\Controller;
11
use SilverStripe\Versioned\Versioned;
12
use SilverStripe\ORM\DatabaseAdmin;
13
use SilverStripe\Security\Permission;
14
use SilverStripe\Security\Security;
15
use Exception;
16
17
/**
18
 * Base class for development tools.
19
 *
20
 * Configured in framework/_config/dev.yml, with the config key registeredControllers being
21
 * used to generate the list of links for /dev.
22
 *
23
 * @todo documentation for how to add new unit tests and tasks
24
 * @todo do we need buildDefaults and generatesecuretoken? if so, register in the list
25
 * @todo cleanup errors() it's not even an allowed action, so can go
26
 * @todo cleanup index() html building
27
 */
28
class DevelopmentAdmin extends Controller
29
{
30
31
    private static $url_handlers = array(
0 ignored issues
show
introduced by
The private property $url_handlers is not used, and could be removed.
Loading history...
32
        '' => 'index',
33
        'build/defaults' => 'buildDefaults',
34
        'generatesecuretoken' => 'generatesecuretoken',
35
        '$Action' => 'runRegisteredController',
36
    );
37
38
    private static $allowed_actions = array(
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
39
        'index',
40
        'buildDefaults',
41
        'runRegisteredController',
42
        'generatesecuretoken',
43
    );
44
45
    /**
46
     * Assume that CLI equals admin permissions
47
     * If set to false, normal permission model will apply even in CLI mode
48
     * Applies to all development admin tasks (E.g. TaskRunner, DatabaseAdmin)
49
     *
50
     * @config
51
     * @var bool
52
     */
53
    private static $allow_all_cli = true;
0 ignored issues
show
introduced by
The private property $allow_all_cli is not used, and could be removed.
Loading history...
54
55
    protected function init()
56
    {
57
        parent::init();
58
59
        // Special case for dev/build: Defer permission checks to DatabaseAdmin->init() (see #4957)
60
        $requestedDevBuild = (stripos($this->getRequest()->getURL(), 'dev/build') === 0)
61
            && (stripos($this->getRequest()->getURL(), 'dev/build/defaults') === false);
62
63
        // We allow access to this controller regardless of live-status or ADMIN permission only
64
        // if on CLI.  Access to this controller is always allowed in "dev-mode", or of the user is ADMIN.
65
        $allowAllCLI = static::config()->get('allow_all_cli');
66
        $canAccess = (
67
            $requestedDevBuild
68
            || Director::isDev()
69
            || (Director::is_cli() && $allowAllCLI)
70
            // Its important that we don't run this check if dev/build was requested
71
            || Permission::check("ADMIN")
72
        );
73
        if (!$canAccess) {
74
            Security::permissionFailure($this);
75
            return;
76
        }
77
78
        // Backwards compat: Default to "draft" stage, which is important
79
        // for tasks like dev/build which call DataObject->requireDefaultRecords(),
80
        // but also for other administrative tasks which have assumptions about the default stage.
81
        if (class_exists(Versioned::class)) {
82
            Versioned::set_stage(Versioned::DRAFT);
83
        }
84
    }
85
86
    public function index()
87
    {
88
        // Web mode
89
        if (!Director::is_cli()) {
90
            $renderer = DebugView::create();
91
            echo $renderer->renderHeader();
92
            echo $renderer->renderInfo("SilverStripe Development Tools", Director::absoluteBaseURL());
93
            $base = Director::baseURL();
94
95
            echo '<div class="options"><ul>';
96
            $evenOdd = "odd";
97
            foreach (self::get_links() as $action => $description) {
98
                echo "<li class=\"$evenOdd\"><a href=\"{$base}dev/$action\"><b>/dev/$action:</b>"
99
                    . " $description</a></li>\n";
100
                $evenOdd = ($evenOdd == "odd") ? "even" : "odd";
101
            }
102
103
            echo $renderer->renderFooter();
104
105
        // CLI mode
106
        } else {
107
            echo "SILVERSTRIPE DEVELOPMENT TOOLS\n--------------------------\n\n";
108
            echo "You can execute any of the following commands:\n\n";
109
            foreach (self::get_links() as $action => $description) {
110
                echo "  sake dev/$action: $description\n";
111
            }
112
            echo "\n\n";
113
        }
114
    }
115
116
    public function runRegisteredController(HTTPRequest $request)
117
    {
118
        $controllerClass = null;
119
120
        $baseUrlPart = $request->param('Action');
121
        $reg = Config::inst()->get(__CLASS__, 'registered_controllers');
122
        if (isset($reg[$baseUrlPart])) {
123
            $controllerClass = $reg[$baseUrlPart]['controller'];
124
        }
125
126
        if ($controllerClass && class_exists($controllerClass)) {
127
            return $controllerClass::create();
128
        }
129
130
        $msg = 'Error: no controller registered in ' . __CLASS__ . ' for: ' . $request->param('Action');
131
        if (Director::is_cli()) {
132
            // in CLI we cant use httpError because of a bug with stuff being in the output already, see DevAdminControllerTest
133
            throw new Exception($msg);
134
        } else {
135
            $this->httpError(404, $msg);
136
        }
137
    }
138
139
140
141
142
    /*
143
     * Internal methods
144
     */
145
146
    /**
147
     * @return array of url => description
148
     */
149
    protected static function get_links()
150
    {
151
        $links = [];
152
153
        $reg = Config::inst()->get(__CLASS__, 'registered_controllers');
154
        foreach ($reg as $registeredController) {
155
            if (isset($registeredController['links'])) {
156
                foreach ($registeredController['links'] as $url => $desc) {
157
                    $links[$url] = $desc;
158
                }
159
            }
160
        }
161
        return $links;
162
    }
163
164
    protected function getRegisteredController($baseUrlPart)
165
    {
166
        $reg = Config::inst()->get(__CLASS__, 'registered_controllers');
167
168
        if (isset($reg[$baseUrlPart])) {
169
            $controllerClass = $reg[$baseUrlPart]['controller'];
170
            return $controllerClass;
171
        }
172
173
        return null;
174
    }
175
176
177
178
179
    /*
180
     * Unregistered (hidden) actions
181
     */
182
183
    /**
184
     * Build the default data, calling requireDefaultRecords on all
185
     * DataObject classes
186
     * Should match the $url_handlers rule:
187
     *      'build/defaults' => 'buildDefaults',
188
     */
189
    public function buildDefaults()
190
    {
191
        $da = DatabaseAdmin::create();
192
193
        $renderer = null;
194
        if (!Director::is_cli()) {
195
            $renderer = DebugView::create();
196
            echo $renderer->renderHeader();
197
            echo $renderer->renderInfo("Defaults Builder", Director::absoluteBaseURL());
198
            echo "<div class=\"build\">";
199
        }
200
201
        $da->buildDefaults();
202
203
        if (!Director::is_cli()) {
204
            echo "</div>";
205
            echo $renderer->renderFooter();
206
        }
207
    }
208
209
    /**
210
     * Generate a secure token which can be used as a crypto key.
211
     * Returns the token and suggests PHP configuration to set it.
212
     */
213
    public function generatesecuretoken()
214
    {
215
        $generator = Injector::inst()->create('SilverStripe\\Security\\RandomGenerator');
216
        $token = $generator->randomToken('sha1');
217
        $body = <<<TXT
218
Generated new token. Please add the following code to your YAML configuration:
219
220
Security:
221
  token: $token
222
223
TXT;
224
        $response = new HTTPResponse($body);
225
        return $response->addHeader('Content-Type', 'text/plain');
226
    }
227
228
    public function errors()
229
    {
230
        $this->redirect("Debug_");
231
    }
232
}
233