Completed
Pull Request — master (#6337)
by Daniel
10:15
created

DevelopmentAdmin   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 190
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 11

Importance

Changes 0
Metric Value
dl 0
loc 190
rs 10
c 0
b 0
f 0
wmc 26
lcom 0
cbo 11

8 Methods

Rating   Name   Duplication   Size   Complexity  
B init() 0 27 6
B index() 0 29 5
B runRegisteredController() 0 22 5
A get_links() 0 12 3
A getRegisteredController() 0 11 2
A buildDefaults() 0 19 3
A generatesecuretoken() 0 14 1
A errors() 0 4 1
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\ORM\Versioning\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
        Versioned::set_stage(Versioned::DRAFT);
71
    }
72
73
    public function index()
74
    {
75
        // Web mode
76
        if (!Director::is_cli()) {
77
            $renderer = DebugView::create();
78
            echo $renderer->renderHeader();
79
            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...
80
            $base = Director::baseURL();
81
82
            echo '<div class="options"><ul>';
83
            $evenOdd = "odd";
84
            foreach (self::get_links() as $action => $description) {
85
                echo "<li class=\"$evenOdd\"><a href=\"{$base}dev/$action\"><b>/dev/$action:</b>"
86
                    . " $description</a></li>\n";
87
                $evenOdd = ($evenOdd == "odd") ? "even" : "odd";
88
            }
89
90
            echo $renderer->renderFooter();
91
92
        // CLI mode
93
        } else {
94
            echo "SILVERSTRIPE DEVELOPMENT TOOLS\n--------------------------\n\n";
95
            echo "You can execute any of the following commands:\n\n";
96
            foreach (self::get_links() as $action => $description) {
97
                echo "  sake dev/$action: $description\n";
98
            }
99
            echo "\n\n";
100
        }
101
    }
102
103
    public function runRegisteredController(HTTPRequest $request)
104
    {
105
        $controllerClass = null;
106
107
        $baseUrlPart = $request->param('Action');
108
        $reg = Config::inst()->get(__CLASS__, 'registered_controllers');
109
        if (isset($reg[$baseUrlPart])) {
110
            $controllerClass = $reg[$baseUrlPart]['controller'];
111
        }
112
113
        if ($controllerClass && class_exists($controllerClass)) {
114
            return $controllerClass::create();
115
        }
116
117
        $msg = 'Error: no controller registered in '.__CLASS__.' for: '.$request->param('Action');
118
        if (Director::is_cli()) {
119
            // in CLI we cant use httpError because of a bug with stuff being in the output already, see DevAdminControllerTest
120
            throw new Exception($msg);
121
        } else {
122
            $this->httpError(500, $msg);
123
        }
124
    }
125
126
127
128
129
    /*
130
	 * Internal methods
131
	 */
132
133
    /**
134
     * @return array of url => description
135
     */
136
    protected static function get_links()
137
    {
138
        $links = array();
139
140
        $reg = Config::inst()->get(__CLASS__, 'registered_controllers');
141
        foreach ($reg as $registeredController) {
142
            foreach ($registeredController['links'] as $url => $desc) {
143
                $links[$url] = $desc;
144
            }
145
        }
146
        return $links;
147
    }
148
149
    protected function getRegisteredController($baseUrlPart)
150
    {
151
        $reg = Config::inst()->get(__CLASS__, 'registered_controllers');
152
153
        if (isset($reg[$baseUrlPart])) {
154
            $controllerClass = $reg[$baseUrlPart]['controller'];
155
            return $controllerClass;
156
        }
157
158
        return null;
159
    }
160
161
162
163
164
    /*
165
	 * Unregistered (hidden) actions
166
	 */
167
168
    /**
169
     * Build the default data, calling requireDefaultRecords on all
170
     * DataObject classes
171
     * Should match the $url_handlers rule:
172
     *      'build/defaults' => 'buildDefaults',
173
     */
174
    public function buildDefaults()
175
    {
176
        $da = DatabaseAdmin::create();
177
178
        $renderer = null;
179
        if (!Director::is_cli()) {
180
            $renderer = DebugView::create();
181
            echo $renderer->renderHeader();
182
            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...
183
            echo "<div style=\"margin: 0 2em\">";
184
        }
185
186
        $da->buildDefaults();
187
188
        if (!Director::is_cli()) {
189
            echo "</div>";
190
            echo $renderer->renderFooter();
191
        }
192
    }
193
194
    /**
195
     * Generate a secure token which can be used as a crypto key.
196
     * Returns the token and suggests PHP configuration to set it.
197
     */
198
    public function generatesecuretoken()
199
    {
200
        $generator = Injector::inst()->create('SilverStripe\\Security\\RandomGenerator');
201
        $token = $generator->randomToken('sha1');
202
        $body = <<<TXT
203
Generated new token. Please add the following code to your YAML configuration:
204
205
Security:
206
  token: $token
207
208
TXT;
209
        $response = new HTTPResponse($body);
210
        return $response->addHeader('Content-Type', 'text/plain');
211
    }
212
213
    public function errors()
214
    {
215
        $this->redirect("Debug_");
216
    }
217
}
218