protected function init()
 {
     parent::init();
     $isRunningTests = class_exists('SilverStripe\\Dev\\SapphireTest', false) && SapphireTest::is_running_test();
     $canAccess = Director::isDev() || Director::is_cli() && !$isRunningTests || Permission::check("ADMIN");
     if (!$canAccess) {
         Security::permissionFailure($this);
     }
 }
 protected function init()
 {
     parent::init();
     // We allow access to this controller regardless of live-status or ADMIN permission only
     // if on CLI or with the database not ready. The latter makes it less errorprone to do an
     // initial schema build without requiring a default-admin login.
     // Access to this controller is always allowed in "dev-mode", or of the user is ADMIN.
     $isRunningTests = class_exists('SilverStripe\\Dev\\SapphireTest', false) && SapphireTest::is_running_test();
     $canAccess = Director::isDev() || !Security::database_is_ready() || Director::is_cli() && !$isRunningTests || Permission::check("ADMIN");
     if (!$canAccess) {
         Security::permissionFailure($this, "This page is secured and you need administrator rights to access it. " . "Enter your credentials below and we will send you right along.");
     }
 }
 protected function init()
 {
     parent::init();
     // Special case for dev/build: Defer permission checks to DatabaseAdmin->init() (see #4957)
     $requestedDevBuild = stripos($this->getRequest()->getURL(), 'dev/build') === 0 && stripos($this->getRequest()->getURL(), 'dev/build/defaults') === false;
     // We allow access to this controller regardless of live-status or ADMIN permission only
     // if on CLI.  Access to this controller is always allowed in "dev-mode", or of the user is ADMIN.
     $canAccess = $requestedDevBuild || Director::isDev() || Director::is_cli() || Permission::check("ADMIN");
     if (!$canAccess) {
         Security::permissionFailure($this);
         return;
     }
     // check for valid url mapping
     // lacking this information can cause really nasty bugs,
     // e.g. when running Director::test() from a FunctionalTest instance
     global $_FILE_TO_URL_MAPPING;
     if (Director::is_cli()) {
         if (isset($_FILE_TO_URL_MAPPING)) {
             $testPath = BASE_PATH;
             $matched = false;
             while ($testPath && $testPath != "/" && !preg_match('/^[A-Z]:\\\\$/', $testPath)) {
                 if (isset($_FILE_TO_URL_MAPPING[$testPath])) {
                     $matched = true;
                     break;
                 }
                 $testPath = dirname($testPath);
             }
             if (!$matched) {
                 echo 'Warning: You probably want to define ' . 'an entry in $_FILE_TO_URL_MAPPING that covers "' . Director::baseFolder() . '"' . "\n";
             }
         } else {
             echo 'Warning: You probably want to define $_FILE_TO_URL_MAPPING in ' . 'your _ss_environment.php as instructed on the "sake" page of the doc.silverstripe.org wiki' . "\n";
         }
     }
     // Backwards compat: Default to "draft" stage, which is important
     // for tasks like dev/build which call DataObject->requireDefaultRecords(),
     // but also for other administrative tasks which have assumptions about the default stage.
     Versioned::set_stage(Versioned::DRAFT);
 }
 /**
  * Add the appropriate caching headers to the response, including If-Modified-Since / 304 handling.
  * Note that setting HTTP::$cache_age will overrule any cache headers set by PHP's
  * session_cache_limiter functionality. It is your responsibility to ensure only cacheable data
  * is in fact cached, and HTTP::$cache_age isn't set when the HTTP body contains session-specific
  * content.
  *
  * Omitting the $body argument or passing a string is deprecated; in these cases, the headers are
  * output directly.
  *
  * @param HTTPResponse $body
  */
 public static function add_cache_headers($body = null)
 {
     $cacheAge = self::$cache_age;
     // Validate argument
     if ($body && !$body instanceof HTTPResponse) {
         user_error("HTTP::add_cache_headers() must be passed an HTTPResponse object", E_USER_WARNING);
         $body = null;
     }
     // Development sites have frequently changing templates; this can get stuffed up by the code
     // below.
     if (Director::isDev()) {
         $cacheAge = 0;
     }
     // The headers have been sent and we don't have an HTTPResponse object to attach things to; no point in
     // us trying.
     if (headers_sent() && !$body) {
         return;
     }
     // Populate $responseHeaders with all the headers that we want to build
     $responseHeaders = array();
     $cacheControlHeaders = Config::inst()->get(__CLASS__, 'cache_control');
     // currently using a config setting to cancel this, seems to be so that the CMS caches ajax requests
     if (function_exists('apache_request_headers') && Config::inst()->get(__CLASS__, 'cache_ajax_requests')) {
         $requestHeaders = array_change_key_case(apache_request_headers(), CASE_LOWER);
         if (isset($requestHeaders['x-requested-with']) && $requestHeaders['x-requested-with'] == 'XMLHttpRequest') {
             $cacheAge = 0;
         }
     }
     if ($cacheAge > 0) {
         $cacheControlHeaders['max-age'] = self::$cache_age;
         // Set empty pragma to avoid PHP's session_cache_limiter adding conflicting caching information,
         // defaulting to "nocache" on most PHP configurations (see http://php.net/session_cache_limiter).
         // Since it's a deprecated HTTP 1.0 option, all modern HTTP clients and proxies should
         // prefer the caching information indicated through the "Cache-Control" header.
         $responseHeaders["Pragma"] = "";
         // To do: User-Agent should only be added in situations where you *are* actually
         // varying according to user-agent.
         $vary = Config::inst()->get(__CLASS__, 'vary');
         if ($vary && strlen($vary)) {
             $responseHeaders['Vary'] = $vary;
         }
     } else {
         $contentDisposition = null;
         if ($body) {
             // Grab header for checking. Unfortunately HTTPRequest uses a mistyped variant.
             $contentDisposition = $body->getHeader('Content-disposition');
             if (!$contentDisposition) {
                 $contentDisposition = $body->getHeader('Content-Disposition');
             }
         }
         if ($body && Director::is_https() && isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE') == true && strstr($contentDisposition, 'attachment;') == true) {
             // IE6-IE8 have problems saving files when https and no-cache are used
             // (http://support.microsoft.com/kb/323308)
             // Note: this is also fixable by ticking "Do not save encrypted pages to disk" in advanced options.
             $cacheControlHeaders['max-age'] = 3;
             // Set empty pragma to avoid PHP's session_cache_limiter adding conflicting caching information,
             // defaulting to "nocache" on most PHP configurations (see http://php.net/session_cache_limiter).
             // Since it's a deprecated HTTP 1.0 option, all modern HTTP clients and proxies should
             // prefer the caching information indicated through the "Cache-Control" header.
             $responseHeaders["Pragma"] = "";
         } else {
             $cacheControlHeaders['no-cache'] = "true";
             $cacheControlHeaders['no-store'] = "true";
         }
     }
     foreach ($cacheControlHeaders as $header => $value) {
         if (is_null($value)) {
             unset($cacheControlHeaders[$header]);
         } elseif (is_bool($value) && $value || $value === "true") {
             $cacheControlHeaders[$header] = $header;
         } else {
             $cacheControlHeaders[$header] = $header . "=" . $value;
         }
     }
     $responseHeaders['Cache-Control'] = implode(', ', $cacheControlHeaders);
     unset($cacheControlHeaders, $header, $value);
     if (self::$modification_date && $cacheAge > 0) {
         $responseHeaders["Last-Modified"] = self::gmt_date(self::$modification_date);
         // Chrome ignores Varies when redirecting back (http://code.google.com/p/chromium/issues/detail?id=79758)
         // which means that if you log out, you get redirected back to a page which Chrome then checks against
         // last-modified (which passes, getting a 304)
         // when it shouldn't be trying to use that page at all because it's the "logged in" version.
         // By also using and etag that includes both the modification date and all the varies
         // values which we also check against we can catch this and not return a 304
         $etagParts = array(self::$modification_date, serialize($_COOKIE));
         $etagParts[] = Director::is_https() ? 'https' : 'http';
         if (isset($_SERVER['HTTP_USER_AGENT'])) {
             $etagParts[] = $_SERVER['HTTP_USER_AGENT'];
         }
         if (isset($_SERVER['HTTP_ACCEPT'])) {
             $etagParts[] = $_SERVER['HTTP_ACCEPT'];
         }
         $etag = sha1(implode(':', $etagParts));
         $responseHeaders["ETag"] = $etag;
         // 304 response detection
         if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
             $ifModifiedSince = strtotime(stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']));
             // As above, only 304 if the last request had all the same varies values
             // (or the etag isn't passed as part of the request - but with chrome it always is)
             $matchesEtag = !isset($_SERVER['HTTP_IF_NONE_MATCH']) || $_SERVER['HTTP_IF_NONE_MATCH'] == $etag;
             if ($ifModifiedSince >= self::$modification_date && $matchesEtag) {
                 if ($body) {
                     $body->setStatusCode(304);
                     $body->setBody('');
                 } else {
                     header('HTTP/1.0 304 Not Modified');
                     die;
                 }
             }
         }
         $expires = time() + $cacheAge;
         $responseHeaders["Expires"] = self::gmt_date($expires);
     }
     if (self::$etag) {
         $responseHeaders['ETag'] = self::$etag;
     }
     // etag needs to be a quoted string according to HTTP spec
     if (!empty($responseHeaders['ETag']) && 0 !== strpos($responseHeaders['ETag'], '"')) {
         $responseHeaders['ETag'] = sprintf('"%s"', $responseHeaders['ETag']);
     }
     // Now that we've generated them, either output them or attach them to the HTTPResponse as appropriate
     foreach ($responseHeaders as $k => $v) {
         if ($body) {
             // Set the header now if it's not already set.
             if ($body->getHeader($k) === null) {
                 $body->addHeader($k, $v);
             }
         } elseif (!headers_sent()) {
             header("{$k}: {$v}");
         }
     }
 }
 public function parseTemplateContent($content, $template = "")
 {
     return $this->getParser()->compileString($content, $template, Director::isDev() && SSViewer::config()->get('source_file_comments'));
 }
 /**
  * Allows the display and benchmarking of queries as they are being run
  *
  * @param string $sql Query to run, and single parameter to callback
  * @param callable $callback Callback to execute code
  * @param array $parameters Parameters for any parameterised query
  * @return mixed Result of query
  */
 protected function benchmarkQuery($sql, $callback, $parameters = array())
 {
     if (isset($_REQUEST['showqueries']) && Director::isDev()) {
         $this->queryCount++;
         $starttime = microtime(true);
         $result = $callback($sql);
         $endtime = round(microtime(true) - $starttime, 4);
         // replace parameters as closely as possible to what we'd expect the DB to put in
         if (strtolower($_REQUEST['showqueries']) == 'inline') {
             $sql = DB::inline_parameters($sql, $parameters);
         }
         Debug::message("\n{$sql}\n{$endtime}s\n", false);
         return $result;
     } else {
         return $callback($sql);
     }
 }
 /**
  * @uses LeftAndMainExtension->init()
  * @uses LeftAndMainExtension->accessedCMS()
  * @uses CMSMenu
  */
 protected function init()
 {
     parent::init();
     SSViewer::config()->update('rewrite_hash_links', false);
     ContentNegotiator::config()->update('enabled', false);
     // set language
     $member = Member::currentUser();
     if (!empty($member->Locale)) {
         i18n::set_locale($member->Locale);
     }
     if (!empty($member->DateFormat)) {
         i18n::config()->date_format = $member->DateFormat;
     }
     if (!empty($member->TimeFormat)) {
         i18n::config()->time_format = $member->TimeFormat;
     }
     // can't be done in cms/_config.php as locale is not set yet
     CMSMenu::add_link('Help', _t('LeftAndMain.HELP', 'Help', 'Menu title'), $this->config()->help_link, -2, array('target' => '_blank'));
     // Allow customisation of the access check by a extension
     // Also all the canView() check to execute Controller::redirect()
     if (!$this->canView() && !$this->getResponse()->isFinished()) {
         // When access /admin/, we should try a redirect to another part of the admin rather than be locked out
         $menu = $this->MainMenu();
         foreach ($menu as $candidate) {
             if ($candidate->Link && $candidate->Link != $this->Link() && $candidate->MenuItem->controller && singleton($candidate->MenuItem->controller)->canView()) {
                 $this->redirect($candidate->Link);
                 return;
             }
         }
         if (Member::currentUser()) {
             Session::set("BackURL", null);
         }
         // if no alternate menu items have matched, return a permission error
         $messageSet = array('default' => _t('LeftAndMain.PERMDEFAULT', "You must be logged in to access the administration area; please enter your credentials below."), 'alreadyLoggedIn' => _t('LeftAndMain.PERMALREADY', "I'm sorry, but you can't access that part of the CMS.  If you want to log in as someone else, do" . " so below."), 'logInAgain' => _t('LeftAndMain.PERMAGAIN', "You have been logged out of the CMS.  If you would like to log in again, enter a username and" . " password below."));
         Security::permissionFailure($this, $messageSet);
         return;
     }
     // Don't continue if there's already been a redirection request.
     if ($this->redirectedTo()) {
         return;
     }
     // Audit logging hook
     if (empty($_REQUEST['executeForm']) && !$this->getRequest()->isAjax()) {
         $this->extend('accessedCMS');
     }
     // Set the members html editor config
     if (Member::currentUser()) {
         HTMLEditorConfig::set_active_identifier(Member::currentUser()->getHtmlEditorConfigForCMS());
     }
     // Set default values in the config if missing.  These things can't be defined in the config
     // file because insufficient information exists when that is being processed
     $htmlEditorConfig = HTMLEditorConfig::get_active();
     $htmlEditorConfig->setOption('language', i18n::get_tinymce_lang());
     Requirements::customScript("\n\t\t\twindow.ss = window.ss || {};\n\t\t\twindow.ss.config = " . $this->getCombinedClientConfig() . ";\n\t\t");
     Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/client/dist/js/vendor.js');
     Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/client/dist/js/bundle.js');
     Requirements::css(ltrim(FRAMEWORK_ADMIN_DIR . '/client/dist/styles/bundle.css', '/'));
     Requirements::add_i18n_javascript(ltrim(FRAMEWORK_DIR . '/client/lang', '/'), false, true);
     Requirements::add_i18n_javascript(FRAMEWORK_ADMIN_DIR . '/client/lang', false, true);
     if ($this->config()->session_keepalive_ping) {
         Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/client/dist/js/LeftAndMain.Ping.js');
     }
     if (Director::isDev()) {
         // TODO Confuses jQuery.ondemand through document.write()
         Requirements::javascript(ADMIN_THIRDPARTY_DIR . '/jquery-entwine/src/jquery.entwine.inspector.js');
         Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/client/dist/js/leaktools.js');
     }
     // Custom requirements
     $extraJs = $this->stat('extra_requirements_javascript');
     if ($extraJs) {
         foreach ($extraJs as $file => $config) {
             if (is_numeric($file)) {
                 $file = $config;
             }
             Requirements::javascript($file);
         }
     }
     $extraCss = $this->stat('extra_requirements_css');
     if ($extraCss) {
         foreach ($extraCss as $file => $config) {
             if (is_numeric($file)) {
                 $file = $config;
                 $config = array();
             }
             Requirements::css($file, isset($config['media']) ? $config['media'] : null);
         }
     }
     $extraThemedCss = $this->stat('extra_requirements_themedCss');
     if ($extraThemedCss) {
         foreach ($extraThemedCss as $file => $config) {
             if (is_numeric($file)) {
                 $file = $config;
                 $config = array();
             }
             Requirements::themedCSS($file, isset($config['media']) ? $config['media'] : null);
         }
     }
     $dummy = null;
     $this->extend('init', $dummy);
     // Assign default cms theme and replace user-specified themes
     SSViewer::set_themes($this->config()->admin_themes);
     //set the reading mode for the admin to stage
     Versioned::set_stage(Versioned::DRAFT);
 }
 /**
  * Called once per test case ({@link SapphireTest} subclass).
  * This is different to {@link setUp()}, which gets called once
  * per method. Useful to initialize expensive operations which
  * don't change state for any called method inside the test,
  * e.g. dynamically adding an extension. See {@link tearDownOnce()}
  * for tearing down the state again.
  */
 public function setUpOnce()
 {
     //nest config and injector for each suite so they are effectively sandboxed
     Config::nest();
     Injector::nest();
     $isAltered = false;
     if (!Director::isDev()) {
         user_error('Tests can only run in "dev" mode', E_USER_ERROR);
     }
     // Remove any illegal extensions that are present
     foreach ($this->illegalExtensions as $class => $extensions) {
         foreach ($extensions as $extension) {
             if ($class::has_extension($extension)) {
                 if (!isset($this->extensionsToReapply[$class])) {
                     $this->extensionsToReapply[$class] = array();
                 }
                 $this->extensionsToReapply[$class][] = $extension;
                 $class::remove_extension($extension);
                 $isAltered = true;
             }
         }
     }
     // Add any required extensions that aren't present
     foreach ($this->requiredExtensions as $class => $extensions) {
         $this->extensionsToRemove[$class] = array();
         foreach ($extensions as $extension) {
             if (!$class::has_extension($extension)) {
                 if (!isset($this->extensionsToRemove[$class])) {
                     $this->extensionsToReapply[$class] = array();
                 }
                 $this->extensionsToRemove[$class][] = $extension;
                 $class::add_extension($extension);
                 $isAltered = true;
             }
         }
     }
     // If we have made changes to the extensions present, then migrate the database schema.
     if ($isAltered || $this->extensionsToReapply || $this->extensionsToRemove || $this->extraDataObjects) {
         if (!self::using_temp_db()) {
             self::create_temp_db();
         }
         $this->resetDBSchema(true);
     }
     // clear singletons, they're caching old extension info
     // which is used in DatabaseAdmin->doBuild()
     Injector::inst()->unregisterAllObjects();
     // Set default timezone consistently to avoid NZ-specific dependencies
     date_default_timezone_set('UTC');
 }
 /**
  * This function will return true if the site is in a live environment. For information about
  * environment types, see {@link Director::set_environment_type()}.
  *
  * @return bool
  */
 public static function isLive()
 {
     return !(Director::isDev() || Director::isTest());
 }
    /**
     * Check if the user has permissions to run URL debug tools,
     * else redirect them to log in.
     */
    public static function require_developer_login()
    {
        if (Director::isDev()) {
            return;
        }
        if (isset($_SESSION['loggedInAs'])) {
            // We have to do some raw SQL here, because this method is called in Object::defineMethods().
            // This means we have to be careful about what objects we create, as we don't want Object::defineMethods()
            // being called again.
            // This basically calls Permission::checkMember($_SESSION['loggedInAs'], 'ADMIN');
            // @TODO - Rewrite safely using DataList::filter
            $memberID = $_SESSION['loggedInAs'];
            $permission = DB::prepared_query('
				SELECT "ID" FROM "Permission"
				INNER JOIN "Group_Members" ON "Permission"."GroupID" = "Group_Members"."GroupID"
				WHERE "Permission"."Code" = ?
				AND "Permission"."Type" = ?
				AND "Group_Members"."MemberID" = ?', array('ADMIN', Permission::GRANT_PERMISSION, $memberID))->value();
            if ($permission) {
                return;
            }
        }
        // This basically does the same as
        // Security::permissionFailure(null, "You need to login with developer access to make use of debugging tools.")
        // We have to do this because of how early this method is called in execution.
        $_SESSION['SilverStripe\\Security\\Security']['Message']['message'] = "You need to login with developer access to make use of debugging tools.";
        $_SESSION['SilverStripe\\Security\\Security']['Message']['type'] = 'warning';
        $_SESSION['BackURL'] = $_SERVER['REQUEST_URI'];
        header($_SERVER['SERVER_PROTOCOL'] . " 302 Found");
        header("Location: " . Director::baseURL() . Security::login_url());
        die;
    }
    require_once 'Core/Core.php';
    // Connect to database
    global $databaseConfig;
    if ($databaseConfig) {
        DB::connect($databaseConfig);
    }
    // Check if a token is requesting a redirect
    if (!$reloadToken) {
        return;
    }
    // Otherwise, we start up the session if needed
    if (!isset($_SESSION) && Session::request_contains_session_id()) {
        Session::start();
    }
    // Next, check if we're in dev mode, or the database doesn't have any security data, or we are admin
    if (Director::isDev() || !Security::database_is_ready() || Permission::check('ADMIN')) {
        return $reloadToken->reloadWithToken();
    }
    // Fail and redirect the user to the login page
    $loginPage = Director::absoluteURL(Security::config()->login_url);
    $loginPage .= "?BackURL=" . urlencode($_SERVER['REQUEST_URI']);
    header('location: ' . $loginPage, true, 302);
    die;
})->thenIfErrored(function () use($reloadToken) {
    if ($reloadToken) {
        $reloadToken->reloadWithToken();
    }
})->execute();
global $databaseConfig;
// Redirect to the installer if no database is selected
if (!isset($databaseConfig) || !isset($databaseConfig['database']) || !$databaseConfig['database']) {
    /**
     * Send this HTTPReponse to the browser
     */
    public function output()
    {
        // Attach appropriate X-Include-JavaScript and X-Include-CSS headers
        if (Director::is_ajax()) {
            Requirements::include_in_response($this);
        }
        if (in_array($this->statusCode, self::$redirect_codes) && headers_sent($file, $line)) {
            $url = Director::absoluteURL($this->headers['Location'], true);
            $urlATT = Convert::raw2htmlatt($url);
            $urlJS = Convert::raw2js($url);
            $title = Director::isDev() ? "{$urlATT}... (output started on {$file}, line {$line})" : "{$urlATT}...";
            echo <<<EOT
<p>Redirecting to <a href="{$urlATT}" title="Click this link if your browser does not redirect you">{$title}</a></p>
<meta http-equiv="refresh" content="1; url={$urlATT}" />
<script type="application/javascript">setTimeout(function(){
\twindow.location.href = "{$urlJS}";
}, 50);</script>
EOT;
        } else {
            $line = $file = null;
            if (!headers_sent($file, $line)) {
                header($_SERVER['SERVER_PROTOCOL'] . " {$this->statusCode} " . $this->getStatusDescription());
                foreach ($this->headers as $header => $value) {
                    //etags need to be quoted
                    if (strcasecmp('etag', $header) === 0 && 0 !== strpos($value, '"')) {
                        $value = sprintf('"%s"', $value);
                    }
                    header("{$header}: {$value}", true, $this->statusCode);
                }
            } else {
                // It's critical that these status codes are sent; we need to report a failure if not.
                if ($this->statusCode >= 300) {
                    user_error("Couldn't set response type to {$this->statusCode} because " . "of output on line {$line} of {$file}", E_USER_WARNING);
                }
            }
            // Only show error pages or generic "friendly" errors if the status code signifies
            // an error, and the response doesn't have any body yet that might contain
            // a more specific error description.
            if (Director::isLive() && $this->isError() && !$this->body) {
                $formatter = Injector::inst()->get('FriendlyErrorFormatter');
                echo $formatter->format(array('code' => $this->statusCode));
            } else {
                echo $this->body;
            }
        }
    }
 /**
  * Returns false if the non-prefilterable parts of the rule aren't met, and true if they are
  *
  * @param array $rules
  * @return bool|string
  */
 public function matchesVariantRules($rules)
 {
     $matches = "undefined";
     // Needs to be truthy, but not true
     foreach ($rules as $k => $v) {
         switch (strtolower($k)) {
             case 'classexists':
             case 'moduleexists':
                 break;
             case 'environment':
                 switch (strtolower($v)) {
                     case 'live':
                         $matches = $matches && Director::isLive();
                         break;
                     case 'test':
                         $matches = $matches && Director::isTest();
                         break;
                     case 'dev':
                         $matches = $matches && Director::isDev();
                         break;
                     default:
                         user_error('Unknown environment ' . $v . ' in config fragment', E_USER_ERROR);
                 }
                 break;
             case 'envvarset':
                 $matches = $matches && isset($_ENV[$v]);
                 break;
             case 'constantdefined':
                 $matches = $matches && defined($v);
                 break;
             default:
                 $matches = $matches && (isset($_ENV[$k]) && $_ENV[$k] == $v || defined($k) && constant($k) == $v);
                 break;
         }
         if ($matches === false) {
             return $matches;
         }
     }
     return $matches;
 }
 /**
  * Determine if deprecation notices should be displayed
  *
  * @return bool
  */
 public static function get_enabled()
 {
     // Deprecation is only available on dev
     if (!Director::isDev()) {
         return false;
     }
     if (isset(self::$enabled)) {
         return self::$enabled;
     }
     if (defined('SS_DEPRECATION_ENABLED')) {
         return SS_DEPRECATION_ENABLED;
     }
     return true;
 }
 /**
  * Tests isDev, isTest, isLive set from querystring
  */
 public function testQueryIsEnvironment()
 {
     // Reset
     unset($_SESSION['isDev']);
     unset($_SESSION['isLive']);
     unset($_GET['isTest']);
     unset($_GET['isDev']);
     $_SESSION = $_SESSION ?: array();
     // Test isDev=1
     $_GET['isDev'] = '1';
     $this->assertTrue(Director::isDev());
     $this->assertFalse(Director::isTest());
     $this->assertFalse(Director::isLive());
     // Test persistence
     unset($_GET['isDev']);
     $this->assertTrue(Director::isDev());
     $this->assertFalse(Director::isTest());
     $this->assertFalse(Director::isLive());
     // Test change to isTest
     $_GET['isTest'] = '1';
     $this->assertFalse(Director::isDev());
     $this->assertTrue(Director::isTest());
     $this->assertFalse(Director::isLive());
     // Test persistence
     unset($_GET['isTest']);
     $this->assertFalse(Director::isDev());
     $this->assertTrue(Director::isTest());
     $this->assertFalse(Director::isLive());
 }