Completed
Push — master ( a9e1ce...416b3d )
by Sam
22s
created

DevelopmentAdmin::init()   C

Complexity

Conditions 7
Paths 24

Size

Total Lines 29
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 14
nc 24
nop 0
dl 0
loc 29
rs 6.7272
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(
32
        '' => 'index',
33
        'build/defaults' => 'buildDefaults',
34
        'generatesecuretoken' => 'generatesecuretoken',
35
        '$Action' => 'runRegisteredController',
36
    );
37
38
    private static $allowed_actions = array(
39
        'index',
40
        'buildDefaults',
41
        'runRegisteredController',
42
        'generatesecuretoken',
43
    );
44
45
    protected function init()
46
    {
47
        parent::init();
48
49
        // Special case for dev/build: Defer permission checks to DatabaseAdmin->init() (see #4957)
50
        $requestedDevBuild = (stripos($this->getRequest()->getURL(), 'dev/build') === 0)
51
            && (stripos($this->getRequest()->getURL(), 'dev/build/defaults') === false);
52
53
        // We allow access to this controller regardless of live-status or ADMIN permission only
54
        // if on CLI.  Access to this controller is always allowed in "dev-mode", or of the user is ADMIN.
55
        $canAccess = (
56
            $requestedDevBuild
57
            || Director::isDev()
58
            || Director::is_cli()
59
            // Its important that we don't run this check if dev/build was requested
60
            || Permission::check("ADMIN")
61
        );
62
        if (!$canAccess) {
63
            Security::permissionFailure($this);
64
            return;
65
        }
66
67
        // Backwards compat: Default to "draft" stage, which is important
68
        // for tasks like dev/build which call DataObject->requireDefaultRecords(),
69
        // but also for other administrative tasks which have assumptions about the default stage.
70
        if (class_exists(Versioned::class)) {
71
            Versioned::set_stage(Versioned::DRAFT);
72
        }
73
    }
74
75
    public function index()
76
    {
77
        // Web mode
78
        if (!Director::is_cli()) {
79
            $renderer = DebugView::create();
80
            echo $renderer->renderHeader();
81
            echo $renderer->renderInfo("SilverStripe Development Tools", Director::absoluteBaseURL());
0 ignored issues
show
Security Bug introduced by
It seems like \SilverStripe\Control\Director::absoluteBaseURL() targeting SilverStripe\Control\Director::absoluteBaseURL() can also be of type false; however, SilverStripe\Dev\DebugView::renderInfo() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
82
            $base = Director::baseURL();
83
84
            echo '<div class="options"><ul>';
85
            $evenOdd = "odd";
86
            foreach (self::get_links() as $action => $description) {
87
                echo "<li class=\"$evenOdd\"><a href=\"{$base}dev/$action\"><b>/dev/$action:</b>"
88
                    . " $description</a></li>\n";
89
                $evenOdd = ($evenOdd == "odd") ? "even" : "odd";
90
            }
91
92
            echo $renderer->renderFooter();
93
94
        // CLI mode
95
        } else {
96
            echo "SILVERSTRIPE DEVELOPMENT TOOLS\n--------------------------\n\n";
97
            echo "You can execute any of the following commands:\n\n";
98
            foreach (self::get_links() as $action => $description) {
99
                echo "  sake dev/$action: $description\n";
100
            }
101
            echo "\n\n";
102
        }
103
    }
104
105
    public function runRegisteredController(HTTPRequest $request)
106
    {
107
        $controllerClass = null;
108
109
        $baseUrlPart = $request->param('Action');
110
        $reg = Config::inst()->get(__CLASS__, 'registered_controllers');
111
        if (isset($reg[$baseUrlPart])) {
112
            $controllerClass = $reg[$baseUrlPart]['controller'];
113
        }
114
115
        if ($controllerClass && class_exists($controllerClass)) {
116
            return $controllerClass::create();
117
        }
118
119
        $msg = 'Error: no controller registered in '.__CLASS__.' for: '.$request->param('Action');
120
        if (Director::is_cli()) {
121
            // in CLI we cant use httpError because of a bug with stuff being in the output already, see DevAdminControllerTest
122
            throw new Exception($msg);
123
        } else {
124
            $this->httpError(500, $msg);
125
        }
126
    }
127
128
129
130
131
    /*
132
	 * Internal methods
133
	 */
134
135
    /**
136
     * @return array of url => description
137
     */
138
    protected static function get_links()
139
    {
140
        $links = array();
141
142
        $reg = Config::inst()->get(__CLASS__, 'registered_controllers');
143
        foreach ($reg as $registeredController) {
144
            foreach ($registeredController['links'] as $url => $desc) {
145
                $links[$url] = $desc;
146
            }
147
        }
148
        return $links;
149
    }
150
151
    protected function getRegisteredController($baseUrlPart)
152
    {
153
        $reg = Config::inst()->get(__CLASS__, 'registered_controllers');
154
155
        if (isset($reg[$baseUrlPart])) {
156
            $controllerClass = $reg[$baseUrlPart]['controller'];
157
            return $controllerClass;
158
        }
159
160
        return null;
161
    }
162
163
164
165
166
    /*
167
	 * Unregistered (hidden) actions
168
	 */
169
170
    /**
171
     * Build the default data, calling requireDefaultRecords on all
172
     * DataObject classes
173
     * Should match the $url_handlers rule:
174
     *      'build/defaults' => 'buildDefaults',
175
     */
176
    public function buildDefaults()
177
    {
178
        $da = DatabaseAdmin::create();
179
180
        $renderer = null;
181
        if (!Director::is_cli()) {
182
            $renderer = DebugView::create();
183
            echo $renderer->renderHeader();
184
            echo $renderer->renderInfo("Defaults Builder", Director::absoluteBaseURL());
0 ignored issues
show
Security Bug introduced by
It seems like \SilverStripe\Control\Director::absoluteBaseURL() targeting SilverStripe\Control\Director::absoluteBaseURL() can also be of type false; however, SilverStripe\Dev\DebugView::renderInfo() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
185
            echo "<div style=\"margin: 0 2em\">";
186
        }
187
188
        $da->buildDefaults();
189
190
        if (!Director::is_cli()) {
191
            echo "</div>";
192
            echo $renderer->renderFooter();
193
        }
194
    }
195
196
    /**
197
     * Generate a secure token which can be used as a crypto key.
198
     * Returns the token and suggests PHP configuration to set it.
199
     */
200
    public function generatesecuretoken()
201
    {
202
        $generator = Injector::inst()->create('SilverStripe\\Security\\RandomGenerator');
203
        $token = $generator->randomToken('sha1');
204
        $body = <<<TXT
205
Generated new token. Please add the following code to your YAML configuration:
206
207
Security:
208
  token: $token
209
210
TXT;
211
        $response = new HTTPResponse($body);
212
        return $response->addHeader('Content-Type', 'text/plain');
213
    }
214
215
    public function errors()
216
    {
217
        $this->redirect("Debug_");
218
    }
219
}
220