Completed
Push — authenticator-refactor ( 3617c4...16f104 )
by Sam
05:36
created

FunctionalTest::setUp()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 29
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 13
nc 8
nop 0
dl 0
loc 29
rs 8.5806
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Dev;
4
5
use SilverStripe\Control\Session;
6
use SilverStripe\Control\HTTPResponse;
7
use SilverStripe\Core\Config\Config;
8
use SilverStripe\Security\BasicAuth;
9
use SilverStripe\Security\Member;
10
use SilverStripe\Security\Security;
11
use SilverStripe\Security\SecurityToken;
12
use SilverStripe\View\SSViewer;
13
use PHPUnit_Framework_AssertionFailedError;
14
use SimpleXMLElement;
15
16
/**
17
 * SilverStripe-specific testing object designed to support functional testing of your web app.  It simulates get/post
18
 * requests, form submission, and can validate resulting HTML, looking up content by CSS selector.
19
 *
20
 * The example below shows how it works.
21
 *
22
 * <code>
23
 *   public function testMyForm() {
24
 *   // Visit a URL
25
 *   $this->get("your/url");
26
 *
27
 *   // Submit a form on the page that you get in response
28
 *   $this->submitForm("MyForm_ID", "action_dologin", array("Email" => "invalid email ^&*&^"));
29
 *
30
 *   // Validate the content that is returned
31
 *   $this->assertExactMatchBySelector("#MyForm_ID p.error", array("That email address is invalid."));
32
 *  }
33
 * </code>
34
 */
35
class FunctionalTest extends SapphireTest
36
{
37
    /**
38
     * Set this to true on your sub-class to disable the use of themes in this test.
39
     * This can be handy for functional testing of modules without having to worry about whether a user has changed
40
     * behaviour by replacing the theme.
41
     *
42
     * @var bool
43
     */
44
    protected static $disable_themes = false;
45
46
    /**
47
     * Set this to true on your sub-class to use the draft site by default for every test in this class.
48
     *
49
     * @var bool
50
     */
51
    protected static $use_draft_site = false;
52
53
    /**
54
     * @var TestSession
55
     */
56
    protected $mainSession = null;
57
58
    /**
59
     * CSSContentParser for the most recently requested page.
60
     *
61
     * @var CSSContentParser
62
     */
63
    protected $cssParser = null;
64
65
    /**
66
     * If this is true, then 30x Location headers will be automatically followed.
67
     * If not, then you will have to manaully call $this->mainSession->followRedirection() to follow them.
68
     * However, this will let you inspect the intermediary headers
69
     *
70
     * @var bool
71
     */
72
    protected $autoFollowRedirection = true;
73
74
    /**
75
     * Returns the {@link Session} object for this test
76
     *
77
     * @return Session
78
     */
79
    public function session()
80
    {
81
        return $this->mainSession->session();
82
    }
83
84
    protected function setUp()
85
    {
86
        // Skip calling FunctionalTest directly.
87
        if (get_class($this) == __CLASS__) {
88
            $this->markTestSkipped(sprintf('Skipping %s ', get_class($this)));
89
        }
90
91
        parent::setUp();
92
        $this->mainSession = new TestSession();
93
94
        // Disable theme, if necessary
95
        if (static::get_disable_themes()) {
96
            SSViewer::config()->update('theme_enabled', false);
97
        }
98
99
        // Switch to draft site, if necessary
100
        if (static::get_use_draft_site()) {
101
            $this->useDraftSite();
102
        }
103
104
        // Unprotect the site, tests are running with the assumption it's off. They will enable it on a case-by-case
105
        // basis.
106
        BasicAuth::protect_entire_site(false);
107
108
        $this->session()->inst_clear('loggedInAs');
109
        Security::setCurrentUser(null);
110
111
        SecurityToken::disable();
112
    }
113
114
    protected function tearDown()
115
    {
116
        SecurityToken::enable();
117
118
        parent::tearDown();
119
        unset($this->mainSession);
120
    }
121
122
    /**
123
     * Run a test while mocking the base url with the provided value
124
     * @param string $url The base URL to use for this test
125
     * @param callable $callback The test to run
126
     */
127
    protected function withBaseURL($url, $callback)
128
    {
129
        $oldBase = Config::inst()->get('SilverStripe\\Control\\Director', 'alternate_base_url');
130
        Config::inst()->update('SilverStripe\\Control\\Director', 'alternate_base_url', $url);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface SilverStripe\Config\Coll...nfigCollectionInterface as the method update() does only exist in the following implementations of said interface: SilverStripe\Config\Coll...\MemoryConfigCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
131
        $callback($this);
132
        Config::inst()->update('SilverStripe\\Control\\Director', 'alternate_base_url', $oldBase);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface SilverStripe\Config\Coll...nfigCollectionInterface as the method update() does only exist in the following implementations of said interface: SilverStripe\Config\Coll...\MemoryConfigCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
133
    }
134
135
    /**
136
     * Run a test while mocking the base folder with the provided value
137
     * @param string $folder The base folder to use for this test
138
     * @param callable $callback The test to run
139
     */
140
    protected function withBaseFolder($folder, $callback)
141
    {
142
        $oldFolder = Config::inst()->get('SilverStripe\\Control\\Director', 'alternate_base_folder');
143
        Config::inst()->update('SilverStripe\\Control\\Director', 'alternate_base_folder', $folder);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface SilverStripe\Config\Coll...nfigCollectionInterface as the method update() does only exist in the following implementations of said interface: SilverStripe\Config\Coll...\MemoryConfigCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
144
        $callback($this);
145
        Config::inst()->update('SilverStripe\\Control\\Director', 'alternate_base_folder', $oldFolder);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface SilverStripe\Config\Coll...nfigCollectionInterface as the method update() does only exist in the following implementations of said interface: SilverStripe\Config\Coll...\MemoryConfigCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
146
    }
147
148
    /**
149
     * Submit a get request
150
     * @uses Director::test()
151
     *
152
     * @param string $url
153
     * @param Session $session
154
     * @param array $headers
155
     * @param array $cookies
156
     * @return HTTPResponse
157
     */
158 View Code Duplication
    public function get($url, $session = null, $headers = null, $cookies = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
159
    {
160
        $this->cssParser = null;
161
        $response = $this->mainSession->get($url, $session, $headers, $cookies);
162
        if ($this->autoFollowRedirection && is_object($response) && $response->getHeader('Location')) {
163
            $response = $this->mainSession->followRedirection();
164
        }
165
        return $response;
166
    }
167
168
    /**
169
     * Submit a post request
170
     *
171
     * @uses Director::test()
172
     * @param string $url
173
     * @param array $data
174
     * @param array $headers
175
     * @param Session $session
176
     * @param string $body
177
     * @param array $cookies
178
     * @return HTTPResponse
179
     */
180 View Code Duplication
    public function post($url, $data, $headers = null, $session = null, $body = null, $cookies = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
181
    {
182
        $this->cssParser = null;
183
        $response = $this->mainSession->post($url, $data, $headers, $session, $body, $cookies);
184
        if ($this->autoFollowRedirection && is_object($response) && $response->getHeader('Location')) {
185
            $response = $this->mainSession->followRedirection();
186
        }
187
        return $response;
188
    }
189
190
    /**
191
     * Submit the form with the given HTML ID, filling it out with the given data.
192
     * Acts on the most recent response.
193
     *
194
     * Any data parameters have to be present in the form, with exact form field name
195
     * and values, otherwise they are removed from the submission.
196
     *
197
     * Caution: Parameter names have to be formatted
198
     * as they are in the form submission, not as they are interpreted by PHP.
199
     * Wrong: array('mycheckboxvalues' => array(1 => 'one', 2 => 'two'))
200
     * Right: array('mycheckboxvalues[1]' => 'one', 'mycheckboxvalues[2]' => 'two')
201
     *
202
     * @see http://www.simpletest.org/en/form_testing_documentation.html
203
     *
204
     * @param string $formID HTML 'id' attribute of a form (loaded through a previous response)
205
     * @param string $button HTML 'name' attribute of the button (NOT the 'id' attribute)
206
     * @param array $data Map of GET/POST data.
207
     * @return HTTPResponse
208
     */
209
    public function submitForm($formID, $button = null, $data = array())
210
    {
211
        $this->cssParser = null;
212
        $response = $this->mainSession->submitForm($formID, $button, $data);
213
        if ($this->autoFollowRedirection && is_object($response) && $response->getHeader('Location')) {
214
            $response = $this->mainSession->followRedirection();
215
        }
216
        return $response;
217
    }
218
219
    /**
220
     * Return the most recent content
221
     *
222
     * @return string
223
     */
224
    public function content()
225
    {
226
        return $this->mainSession->lastContent();
227
    }
228
229
    /**
230
     * Find an attribute in a SimpleXMLElement object by name.
231
     * @param SimpleXMLElement $object
232
     * @param string $attribute Name of attribute to find
233
     * @return SimpleXMLElement object of the attribute
234
     */
235
    public function findAttribute($object, $attribute)
236
    {
237
        $found = false;
238
        foreach ($object->attributes() as $a => $b) {
239
            if ($a == $attribute) {
240
                $found = $b;
241
            }
242
        }
243
        return $found;
244
    }
245
246
    /**
247
     * Return a CSSContentParser for the most recent content.
248
     *
249
     * @return CSSContentParser
250
     */
251
    public function cssParser()
252
    {
253
        if (!$this->cssParser) {
254
            $this->cssParser = new CSSContentParser($this->mainSession->lastContent());
255
        }
256
        return $this->cssParser;
257
    }
258
259
    /**
260
     * Assert that the most recently queried page contains a number of content tags specified by a CSS selector.
261
     * The given CSS selector will be applied to the HTML of the most recent page.  The content of every matching tag
262
     * will be examined. The assertion fails if one of the expectedMatches fails to appear.
263
     *
264
     * Note: &nbsp; characters are stripped from the content; make sure that your assertions take this into account.
265
     *
266
     * @param string $selector A basic CSS selector, e.g. 'li.jobs h3'
267
     * @param array|string $expectedMatches The content of at least one of the matched tags
268
     * @param string $message
269
     * @throws PHPUnit_Framework_AssertionFailedError
270
     */
271
    public function assertPartialMatchBySelector($selector, $expectedMatches, $message = null)
272
    {
273
        if (is_string($expectedMatches)) {
274
            $expectedMatches = array($expectedMatches);
275
        }
276
277
        $items = $this->cssParser()->getBySelector($selector);
278
279
        $actuals = array();
280 View Code Duplication
        if ($items) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
281
            foreach ($items as $item) {
282
                $actuals[trim(preg_replace('/\s+/', ' ', (string)$item))] = true;
283
            }
284
        }
285
286
        $message = $message ?:
287
        "Failed asserting the CSS selector '$selector' has a partial match to the expected elements:\n'"
288
            . implode("'\n'", $expectedMatches) . "'\n\n"
289
            . "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals)) . "'";
290
291
        foreach ($expectedMatches as $match) {
292
            $this->assertTrue(isset($actuals[$match]), $message);
293
        }
294
    }
295
296
    /**
297
     * Assert that the most recently queried page contains a number of content tags specified by a CSS selector.
298
     * The given CSS selector will be applied to the HTML of the most recent page.  The full HTML of every matching tag
299
     * will be examined. The assertion fails if one of the expectedMatches fails to appear.
300
     *
301
     * Note: &nbsp; characters are stripped from the content; make sure that your assertions take this into account.
302
     *
303
     * @param string $selector A basic CSS selector, e.g. 'li.jobs h3'
304
     * @param array|string $expectedMatches The content of *all* matching tags as an array
305
     * @param string $message
306
     * @throws PHPUnit_Framework_AssertionFailedError
307
     */
308
    public function assertExactMatchBySelector($selector, $expectedMatches, $message = null)
309
    {
310
        if (is_string($expectedMatches)) {
311
            $expectedMatches = array($expectedMatches);
312
        }
313
314
        $items = $this->cssParser()->getBySelector($selector);
315
316
        $actuals = array();
317 View Code Duplication
        if ($items) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
318
            foreach ($items as $item) {
319
                $actuals[] = trim(preg_replace('/\s+/', ' ', (string)$item));
320
            }
321
        }
322
323
        $message = $message ?:
324
                "Failed asserting the CSS selector '$selector' has an exact match to the expected elements:\n'"
325
                . implode("'\n'", $expectedMatches) . "'\n\n"
326
            . "Instead the following elements were found:\n'" . implode("'\n'", $actuals) . "'";
327
328
        $this->assertTrue($expectedMatches == $actuals, $message);
329
    }
330
331
    /**
332
     * Assert that the most recently queried page contains a number of content tags specified by a CSS selector.
333
     * The given CSS selector will be applied to the HTML of the most recent page.  The content of every matching tag
334
     * will be examined. The assertion fails if one of the expectedMatches fails to appear.
335
     *
336
     * Note: &nbsp; characters are stripped from the content; make sure that your assertions take this into account.
337
     *
338
     * @param string $selector A basic CSS selector, e.g. 'li.jobs h3'
339
     * @param array|string $expectedMatches The content of at least one of the matched tags
340
     * @param string $message
341
     * @throws PHPUnit_Framework_AssertionFailedError
342
     */
343
    public function assertPartialHTMLMatchBySelector($selector, $expectedMatches, $message = null)
344
    {
345
        if (is_string($expectedMatches)) {
346
            $expectedMatches = array($expectedMatches);
347
        }
348
349
        $items = $this->cssParser()->getBySelector($selector);
350
351
        $actuals = array();
352
        if ($items) {
353
            /** @var SimpleXMLElement $item */
354
            foreach ($items as $item) {
355
                $actuals[$item->asXML()] = true;
356
            }
357
        }
358
359
        $message = $message ?:
360
                "Failed asserting the CSS selector '$selector' has a partial match to the expected elements:\n'"
361
                . implode("'\n'", $expectedMatches) . "'\n\n"
362
            . "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals)) . "'";
363
364
        foreach ($expectedMatches as $match) {
365
            $this->assertTrue(isset($actuals[$match]), $message);
366
        }
367
    }
368
369
    /**
370
     * Assert that the most recently queried page contains a number of content tags specified by a CSS selector.
371
     * The given CSS selector will be applied to the HTML of the most recent page.  The full HTML of every matching tag
372
     * will be examined. The assertion fails if one of the expectedMatches fails to appear.
373
     *
374
     * Note: &nbsp; characters are stripped from the content; make sure that your assertions take this into account.
375
     *
376
     * @param string $selector A basic CSS selector, e.g. 'li.jobs h3'
377
     * @param array|string $expectedMatches The content of *all* matched tags as an array
378
     * @param string $message
379
     * @throws PHPUnit_Framework_AssertionFailedError
380
     */
381
    public function assertExactHTMLMatchBySelector($selector, $expectedMatches, $message = null)
382
    {
383
        $items = $this->cssParser()->getBySelector($selector);
384
385
        $actuals = array();
386
        if ($items) {
387
            /** @var SimpleXMLElement $item */
388
            foreach ($items as $item) {
389
                $actuals[] = $item->asXML();
390
            }
391
        }
392
393
        $message = $message ?:
394
            "Failed asserting the CSS selector '$selector' has an exact match to the expected elements:\n'"
395
            . implode("'\n'", $expectedMatches) . "'\n\n"
396
            . "Instead the following elements were found:\n'" . implode("'\n'", $actuals) . "'";
397
398
        $this->assertTrue($expectedMatches == $actuals, $message);
399
    }
400
401
    /**
402
     * Log in as the given member
403
     *
404
     * @param Member|int|string $member The ID, fixture codename, or Member object of the member that you want to log in
405
     */
406
    public function logInAs($member)
407
    {
408
        if (is_numeric($member)) {
409
            $member = DataObject::get_by_id(Member::class, $member);
410
        } elseif (!is_object($member)) {
411
            $member = $this->objFromFixture('SilverStripe\\Security\\Member', $member);
412
        }
413
414
        $this->session()->inst_set('loggedInAs', $member->ID);
415
        Security::setCurrentUser($member);
416
    }
417
418
419
    /**
420
     * Log in as the given member
421
     *
422
     * @param Member|int|string $member The ID, fixture codename, or Member object of the member that you want to log in
0 ignored issues
show
Bug introduced by
There is no parameter named $member. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
423
     */
424
    public function logOut()
425
    {
426
        $this->session()->inst_set('loggedInAs', null);
427
        Security::setCurrentUser(null);
428
    }
429
430
    /**
431
     * Use the draft (stage) site for testing.
432
     * This is helpful if you're not testing publication functionality and don't want "stage management" cluttering
433
     * your test.
434
     *
435
     * @param bool $enabled toggle the use of the draft site
436
     */
437
    public function useDraftSite($enabled = true)
438
    {
439
        if ($enabled) {
440
            $this->session()->inst_set('readingMode', 'Stage.Stage');
441
            $this->session()->inst_set('unsecuredDraftSite', true);
442
        } else {
443
            $this->session()->inst_set('readingMode', 'Stage.Live');
444
            $this->session()->inst_set('unsecuredDraftSite', false);
445
        }
446
    }
447
448
    /**
449
     * @return bool
450
     */
451
    public static function get_disable_themes()
452
    {
453
        return static::$disable_themes;
454
    }
455
456
    /**
457
     * @return bool
458
     */
459
    public static function get_use_draft_site()
460
    {
461
        return static::$use_draft_site;
462
    }
463
}
464