public function testRelativeFixturePath()
 {
     $relPath = ltrim(FRAMEWORK_DIR . '/tests/testing/YamlFixtureTest.yml', '/');
     $obj = Injector::inst()->create('SilverStripe\\Dev\\YamlFixture', $relPath);
     $this->assertEquals(Director::baseFolder() . '/' . $relPath, $obj->getFixtureFile());
     $this->assertNull($obj->getFixtureString());
 }
 /**
  * @param $locale
  */
 public function __construct($locale = null)
 {
     $this->defaultLocale = $locale ? $locale : i18n::get_lang_from_locale(i18n::config()->get('default_locale'));
     $this->basePath = Director::baseFolder();
     $this->baseSavePath = Director::baseFolder();
     parent::__construct();
 }
 public function setUp()
 {
     parent::setUp();
     Director::config()->update('rules', array('FormTest_Controller' => 'FormTest_Controller'));
     // Suppress themes
     SSViewer::config()->remove('theme');
 }
 public function preRequest(HTTPRequest $request, Session $session, DataModel $model)
 {
     // Bootstrap session so that Session::get() accesses the right instance
     $dummyController = new Controller();
     $dummyController->setSession($session);
     $dummyController->setRequest($request);
     $dummyController->pushCurrent();
     // Block non-authenticated users from setting the stage mode
     if (!Versioned::can_choose_site_stage($request)) {
         $permissionMessage = sprintf(_t("ContentController.DRAFT_SITE_ACCESS_RESTRICTION", 'You must log in with your CMS password in order to view the draft or archived content. ' . '<a href="%s">Click here to go back to the published site.</a>'), Convert::raw2xml(Controller::join_links(Director::baseURL(), $request->getURL(), "?stage=Live")));
         // Force output since RequestFilter::preRequest doesn't support response overriding
         $response = Security::permissionFailure($dummyController, $permissionMessage);
         $session->inst_save();
         $dummyController->popCurrent();
         // Prevent output in testing
         if (class_exists('SilverStripe\\Dev\\SapphireTest', false) && SapphireTest::is_running_test()) {
             throw new HTTPResponse_Exception($response);
         }
         $response->output();
         die;
     }
     Versioned::choose_site_stage();
     $dummyController->popCurrent();
     return true;
 }
 protected function init()
 {
     parent::init();
     // Unless called from the command line, all CliControllers need ADMIN privileges
     if (!Director::is_cli() && !Permission::check("ADMIN")) {
         Security::permissionFailure();
     }
 }
 /**
  * Require basic authentication.  Will request a username and password if none is given.
  *
  * Used by {@link Controller::init()}.
  *
  * @throws HTTPResponse_Exception
  *
  * @param string $realm
  * @param string|array $permissionCode Optional
  * @param boolean $tryUsingSessionLogin If true, then the method with authenticate against the
  *  session log-in if those credentials are disabled.
  * @return Member|bool $member
  */
 public static function requireLogin($realm, $permissionCode = null, $tryUsingSessionLogin = true)
 {
     $isRunningTests = class_exists('SilverStripe\\Dev\\SapphireTest', false) && SapphireTest::is_running_test();
     if (!Security::database_is_ready() || Director::is_cli() && !$isRunningTests) {
         return true;
     }
     /*
      * Enable HTTP Basic authentication workaround for PHP running in CGI mode with Apache
      * Depending on server configuration the auth header may be in HTTP_AUTHORIZATION or
      * REDIRECT_HTTP_AUTHORIZATION
      *
      * The follow rewrite rule must be in the sites .htaccess file to enable this workaround
      * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
      */
     $authHeader = isset($_SERVER['HTTP_AUTHORIZATION']) ? $_SERVER['HTTP_AUTHORIZATION'] : (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) ? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] : null);
     $matches = array();
     if ($authHeader && preg_match('/Basic\\s+(.*)$/i', $authHeader, $matches)) {
         list($name, $password) = explode(':', base64_decode($matches[1]));
         $_SERVER['PHP_AUTH_USER'] = strip_tags($name);
         $_SERVER['PHP_AUTH_PW'] = strip_tags($password);
     }
     $member = null;
     if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
         $member = MemberAuthenticator::authenticate(array('Email' => $_SERVER['PHP_AUTH_USER'], 'Password' => $_SERVER['PHP_AUTH_PW']), null);
     }
     if (!$member && $tryUsingSessionLogin) {
         $member = Member::currentUser();
     }
     // If we've failed the authentication mechanism, then show the login form
     if (!$member) {
         $response = new HTTPResponse(null, 401);
         $response->addHeader('WWW-Authenticate', "Basic realm=\"{$realm}\"");
         if (isset($_SERVER['PHP_AUTH_USER'])) {
             $response->setBody(_t('BasicAuth.ERRORNOTREC', "That username / password isn't recognised"));
         } else {
             $response->setBody(_t('BasicAuth.ENTERINFO', "Please enter a username and password."));
         }
         // Exception is caught by RequestHandler->handleRequest() and will halt further execution
         $e = new HTTPResponse_Exception(null, 401);
         $e->setResponse($response);
         throw $e;
     }
     if ($permissionCode && !Permission::checkMember($member->ID, $permissionCode)) {
         $response = new HTTPResponse(null, 401);
         $response->addHeader('WWW-Authenticate', "Basic realm=\"{$realm}\"");
         if (isset($_SERVER['PHP_AUTH_USER'])) {
             $response->setBody(_t('BasicAuth.ERRORNOTADMIN', "That user is not an administrator."));
         }
         // Exception is caught by RequestHandler->handleRequest() and will halt further execution
         $e = new HTTPResponse_Exception(null, 401);
         $e->setResponse($response);
         throw $e;
     }
     return $member;
 }
 /**
  * Render or return a backtrace from the given scope.
  *
  * @param mixed $returnVal
  * @param bool $ignoreAjax
  * @param array $ignoredFunctions
  * @return mixed
  */
 public static function backtrace($returnVal = false, $ignoreAjax = false, $ignoredFunctions = null)
 {
     $plainText = Director::is_cli() || Director::is_ajax() && !$ignoreAjax;
     $result = self::get_rendered_backtrace(debug_backtrace(), $plainText, $ignoredFunctions);
     if ($returnVal) {
         return $result;
     } else {
         echo $result;
         return null;
     }
 }
 /**
  * Provide downloadable url
  *
  * @param string $path
  * @return string|null
  */
 public function getPublicUrl($path)
 {
     $rootPath = realpath(BASE_PATH);
     $filesPath = realpath($this->pathPrefix);
     if (stripos($filesPath, $rootPath) === 0) {
         $dir = substr($filesPath, strlen($rootPath));
         return Controller::join_links(Director::baseURL(), $dir, $path);
     }
     // File outside of webroot can't be used
     return null;
 }
 public function run($request)
 {
     if (!Permission::check('ADMIN') && !Director::is_cli()) {
         $response = Security::permissionFailure();
         if ($response) {
             $response->output();
         }
         die;
     }
     SapphireTest::delete_all_temp_dbs();
 }
 /**
  * Check if jQuery UI locale settings exists for the current locale
  * @return boolean
  */
 function regionalSettingsExist()
 {
     $lang = $this->getLang();
     $localeFile = THIRDPARTY_DIR . "/jquery-ui/datepicker/i18n/jquery.ui.datepicker-{$lang}.js";
     if (file_exists(Director::baseFolder() . '/' . $localeFile)) {
         $this->jqueryLocaleFile = $localeFile;
         return true;
     } else {
         // file goes before internal en_US settings,
         // but both will validate
         return $lang == 'en';
     }
 }
 public function index()
 {
     if (!Director::is_cli()) {
         return "The SilverStripe Interactive Command-line doesn't work in a web browser." . " Use 'sake interactive' from the command-line to run.";
     }
     /* Try using PHP_Shell if it exists */
     @(include 'php-shell-cmd.php');
     /* Fall back to our simpler interface */
     if (empty($__shell)) {
         set_error_handler(array($this, 'error_handler'));
         echo "SilverStripe Interactive Command-line (REPL interface). Type help for hints.\n\n";
         while (true) {
             echo CLI::text("?> ", "cyan");
             echo CLI::start_colour("yellow");
             $command = trim(fgets(STDIN, 4096));
             echo CLI::end_colour();
             if ($command == 'help' || $command == '?') {
                 print "help or ? to exit\n";
                 print "quit or \\q to exit\n";
                 print "install PHP_Shell for a more advanced interface with" . " auto-completion and readline support\n\n";
                 continue;
             }
             if ($command == 'quit' || $command == '\\q') {
                 break;
             }
             // Simple command processing
             if (substr($command, -1) == ';') {
                 $command = substr($command, 0, -1);
             }
             $is_print = preg_match('/^\\s*print/i', $command);
             $is_return = preg_match('/^\\s*return/i', $command);
             if (!$is_print && !$is_return) {
                 $command = "return ({$command})";
             }
             $command .= ";";
             try {
                 $result = eval($command);
                 if (!$is_print) {
                     print_r($result);
                 }
                 echo "\n";
             } catch (Exception $__repl_exception) {
                 echo CLI::start_colour("red");
                 printf('%s (code: %d) got thrown' . PHP_EOL, get_class($__repl_exception), $__repl_exception->getCode());
                 print $__repl_exception;
                 echo "\n";
             }
         }
     }
 }
 public function build($request)
 {
     if (Director::is_cli()) {
         $da = DatabaseAdmin::create();
         return $da->handleRequest($request, $this->model);
     } else {
         $renderer = DebugView::create();
         echo $renderer->renderHeader();
         echo $renderer->renderInfo("Environment Builder", Director::absoluteBaseURL());
         echo "<div class=\"build\">";
         $da = DatabaseAdmin::create();
         $response = $da->handleRequest($request, $this->model);
         echo "</div>";
         echo $renderer->renderFooter();
         return $response;
     }
 }
 public function testConfigVary()
 {
     $body = "<html><head></head><body><h1>Mysite</h1></body></html>";
     $response = new HTTPResponse($body, 200);
     Director::config()->update('environment_type', 'live');
     HTTP::set_cache_age(30);
     HTTP::add_cache_headers($response);
     $v = $response->getHeader('Vary');
     $this->assertNotEmpty($v);
     $this->assertContains("Cookie", $v);
     $this->assertContains("X-Forwarded-Protocol", $v);
     $this->assertContains("User-Agent", $v);
     $this->assertContains("Accept", $v);
     HTTP::config()->update('vary', '');
     $response = new HTTPResponse($body, 200);
     HTTP::add_cache_headers($response);
     $v = $response->getHeader('Vary');
     $this->assertEmpty($v);
 }
 protected function getAndCheckForError($url)
 {
     $this->logInWithPermission('ADMIN');
     if (Director::is_cli()) {
         // when in CLI the admin controller throws exceptions
         ob_start();
         try {
             $this->get($url);
         } catch (Exception $e) {
             ob_end_clean();
             return true;
         }
         ob_end_clean();
         return false;
     } else {
         // when in http the admin controller sets a response header
         ob_start();
         $resp = $this->get($url);
         ob_end_clean();
         return $resp->isError();
     }
 }
 /**
  * @param string $filepath
  * @param boolean $preview
  *
  * @return null|BulkLoader_Result
  */
 protected function processAll($filepath, $preview = false)
 {
     $filepath = Director::getAbsFile($filepath);
     $files = $this->splitFile($filepath);
     $result = null;
     $last = null;
     try {
         foreach ($files as $file) {
             $last = $file;
             $next = $this->processChunk($file, false);
             if ($result instanceof BulkLoader_Result) {
                 $result->merge($next);
             } else {
                 $result = $next;
             }
             @unlink($file);
         }
     } catch (Exception $e) {
         print "Failed to parse {$last}\n";
     }
     return $result;
 }
 /**
  * Set a cookie
  *
  * @param string $name The name of the cookie
  * @param string $value The value for the cookie to hold
  * @param int $expiry The number of days until expiry; 0 indicates a cookie valid for the current session
  * @param string $path The path to save the cookie on (falls back to site base)
  * @param string $domain The domain to make the cookie available on
  * @param boolean $secure Can the cookie only be sent over SSL?
  * @param boolean $httpOnly Prevent the cookie being accessible by JS
  */
 public function set($name, $value, $expiry = 90, $path = null, $domain = null, $secure = false, $httpOnly = true)
 {
     //are we setting or clearing a cookie? false values are reserved for clearing cookies (see PHP manual)
     $clear = false;
     if ($value === false || $value === '' || $expiry < 0) {
         $clear = true;
         $value = false;
     }
     //expiry === 0 is a special case where we set a cookie for the current user session
     if ($expiry !== 0) {
         //don't do the maths if we are clearing
         $expiry = $clear ? -1 : DBDatetime::now()->Format('U') + 86400 * $expiry;
     }
     //set the path up
     $path = $path ? $path : Director::baseURL();
     //send the cookie
     $this->outputCookie($name, $value, $expiry, $path, $domain, $secure, $httpOnly);
     //keep our variables in check
     if ($clear) {
         unset($this->new[$name], $this->current[$name]);
     } else {
         $this->new[$name] = $this->current[$name] = $value;
     }
 }
 /**
  * Create a response with the given error code
  *
  * @param int $code
  * @return HTTPResponse
  */
 protected function createErrorResponse($code)
 {
     $response = new HTTPResponse('', $code);
     // Show message in dev
     if (!Director::isLive()) {
         $response->setBody($response->getStatusDescription());
     }
     return $response;
 }
 /**
  * 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);
     }
 }
 /**
  * Test a submission of this form.
  * @param string $action
  * @param array $data
  * @return HTTPResponse the response object that the handling controller produces.  You can interrogate this in
  * your unit test.
  * @throws HTTPResponse_Exception
  */
 public function testSubmission($action, $data)
 {
     $data['action_' . $action] = true;
     return Director::test($this->FormAction(), $data, Controller::curr()->getSession());
 }
use SilverStripe\Security\BasicAuth;
use SilverStripe\Security\Security;
/*
 * _ss_environment.php handler
 */
if (defined('SS_ENVIRONMENT_FILE')) {
    // Only perform validation if SS_ENVIRONMENT_FILE is actually set, which is to say, there is an
    // _ss_environment.php file
    foreach (array('SS_DATABASE_PASSWORD', 'SS_DATABASE_USERNAME', 'SS_ENVIRONMENT_TYPE') as $reqDefine) {
        if (!defined($reqDefine)) {
            user_error("{$reqDefine} must be defined in your _ss_environment.php." . "See http://doc.silverstripe.org/framework/en/topics/environment-management for more information", E_USER_ERROR);
        }
    }
}
if (defined('SS_ENVIRONMENT_TYPE')) {
    Director::config()->environment_type = SS_ENVIRONMENT_TYPE;
}
global $database;
// No database provided
if (!isset($database) || !$database) {
    if (defined('SS_DATABASE_NAME')) {
        $database = SS_DATABASE_NAME;
    } else {
        if (defined('SS_DATABASE_CHOOSE_NAME') && SS_DATABASE_CHOOSE_NAME) {
            $loopCount = (int) SS_DATABASE_CHOOSE_NAME;
            $databaseDir = BASE_PATH;
            for ($i = 0; $i < $loopCount - 1; $i++) {
                $databaseDir = dirname($databaseDir);
            }
            $database = "SS_" . basename($databaseDir);
            $database = str_replace('.', '', $database);
 /**
  * Get the absolute URL to this resource
  *
  * @return string
  */
 public function getAbsoluteURL()
 {
     if (!$this->exists()) {
         return null;
     }
     return Director::absoluteURL($this->getURL());
 }
 /**
  * Allow the setting of a URL
  *
  * This is here so that RootURLController can change the URL of the request
  * without us loosing all the other info attached (like headers)
  *
  * @param string $url The new URL
  * @return HTTPRequest The updated request
  */
 public function setUrl($url)
 {
     $this->url = $url;
     // Normalize URL if its relative (strictly speaking), or has leading slashes
     if (Director::is_relative_url($url) || preg_match('/^\\//', $url)) {
         $this->url = preg_replace(array('/\\/+/', '/^\\//', '/\\/$/'), array('/', '', ''), $this->url);
     }
     if (preg_match('/^(.*)\\.([A-Za-z][A-Za-z0-9]*)$/', $this->url, $matches)) {
         $this->url = $matches[1];
         $this->extension = $matches[2];
     }
     if ($this->url) {
         $this->dirParts = preg_split('|/+|', $this->url);
     } else {
         $this->dirParts = array();
     }
     return $this;
 }
define('SS_ENVIRONMENT_TYPE', 'dev');

/* This defines a default database user */
define('SS_DATABASE_SERVER', 'localhost');
define('SS_DATABASE_USERNAME', '<user>');
define('SS_DATABASE_PASSWORD', '<password>');
define('SS_DATABASE_NAME', '<database>');
--------------------------------------------------

Once you have done that, run 'composer install' or './framework/sake dev/build' to create
an empty database.

For more information, please read this page in our docs:
http://docs.silverstripe.org/en/getting_started/environment_management/


ENVCONTENT;
    exit(1);
}
DB::connect($databaseConfig);
// Get the request URL from the querystring arguments
$url = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : null;
if (!$url) {
    echo 'Please specify an argument to cli-script.php/sake. For more information, visit' . ' http://docs.silverstripe.org/en/developer_guides/cli' . "\n";
    die;
}
$_SERVER['REQUEST_URI'] = BASE_URL . '/' . $url;
// Direct away - this is the "main" function, that hands control to the apporopriate controller
DataModel::set_inst(new DataModel());
Director::direct($url, DataModel::inst());
 /**
  * Convenience function to return the admin route config.
  * Looks for the {@link Director::$rules} for the current admin Controller.
  *
  * @return string
  */
 public static function get_admin_route()
 {
     $rules = Director::config()->rules;
     $adminRoute = array_search(__CLASS__, $rules);
     return $adminRoute ?: static::config()->url_base;
 }
 /**
  * Change the password
  *
  * @param array $data The user submitted data
  * @return HTTPResponse
  */
 public function doChangePassword(array $data)
 {
     if ($member = Member::currentUser()) {
         // The user was logged in, check the current password
         if (empty($data['OldPassword']) || !$member->checkPassword($data['OldPassword'])->valid()) {
             $this->clearMessage();
             $this->sessionMessage(_t('Member.ERRORPASSWORDNOTMATCH', "Your current password does not match, please try again"), "bad");
             // redirect back to the form, instead of using redirectBack() which could send the user elsewhere.
             return $this->controller->redirect($this->controller->Link('changepassword'));
         }
     }
     if (!$member) {
         if (Session::get('AutoLoginHash')) {
             $member = Member::member_from_autologinhash(Session::get('AutoLoginHash'));
         }
         // The user is not logged in and no valid auto login hash is available
         if (!$member) {
             Session::clear('AutoLoginHash');
             return $this->controller->redirect($this->controller->Link('login'));
         }
     }
     // Check the new password
     if (empty($data['NewPassword1'])) {
         $this->clearMessage();
         $this->sessionMessage(_t('Member.EMPTYNEWPASSWORD', "The new password can't be empty, please try again"), "bad");
         // redirect back to the form, instead of using redirectBack() which could send the user elsewhere.
         return $this->controller->redirect($this->controller->Link('changepassword'));
     } else {
         if ($data['NewPassword1'] == $data['NewPassword2']) {
             $isValid = $member->changePassword($data['NewPassword1']);
             if ($isValid->valid()) {
                 // Clear locked out status
                 $member->LockedOutUntil = null;
                 $member->FailedLoginCount = null;
                 $member->write();
                 if ($member->canLogIn()->valid()) {
                     $member->logIn();
                 }
                 // TODO Add confirmation message to login redirect
                 Session::clear('AutoLoginHash');
                 if (!empty($_REQUEST['BackURL']) && Director::is_site_url($_REQUEST['BackURL'])) {
                     $url = Director::absoluteURL($_REQUEST['BackURL']);
                     return $this->controller->redirect($url);
                 } else {
                     // Redirect to default location - the login form saying "You are logged in as..."
                     $redirectURL = HTTP::setGetVar('BackURL', Director::absoluteBaseURL(), $this->controller->Link('login'));
                     return $this->controller->redirect($redirectURL);
                 }
             } else {
                 $this->clearMessage();
                 $this->sessionMessage(_t('Member.INVALIDNEWPASSWORD', "We couldn't accept that password: {password}", array('password' => nl2br("\n" . Convert::raw2xml($isValid->starredList())))), "bad", false);
                 // redirect back to the form, instead of using redirectBack() which could send the user elsewhere.
                 return $this->controller->redirect($this->controller->Link('changepassword'));
             }
         } else {
             $this->clearMessage();
             $this->sessionMessage(_t('Member.ERRORNEWPASSWORD', "You have entered your new password differently, try again"), "bad");
             // redirect back to the form, instead of using redirectBack() which could send the user elsewhere.
             return $this->controller->redirect($this->controller->Link('changepassword'));
         }
     }
 }
 /**
  * Build the default data, calling requireDefaultRecords on all
  * DataObject classes
  * Should match the $url_handlers rule:
  *		'build/defaults' => 'buildDefaults',
  */
 public function buildDefaults()
 {
     $da = DatabaseAdmin::create();
     $renderer = null;
     if (!Director::is_cli()) {
         $renderer = DebugView::create();
         echo $renderer->renderHeader();
         echo $renderer->renderInfo("Defaults Builder", Director::absoluteBaseURL());
         echo "<div style=\"margin: 0 2em\">";
     }
     $da->buildDefaults();
     if (!Director::is_cli()) {
         echo "</div>";
         echo $renderer->renderFooter();
     }
 }
 /**
  * @return array Array of associative arrays for each task (Keys: 'class', 'title', 'description')
  */
 protected function getTasks()
 {
     $availableTasks = array();
     $taskClasses = ClassInfo::subclassesFor('SilverStripe\\Dev\\BuildTask');
     // remove the base class
     array_shift($taskClasses);
     foreach ($taskClasses as $class) {
         if (!$this->taskEnabled($class)) {
             continue;
         }
         $singleton = BuildTask::singleton($class);
         $desc = Director::is_cli() ? Convert::html2raw($singleton->getDescription()) : $singleton->getDescription();
         $availableTasks[] = array('class' => $class, 'title' => $singleton->getTitle(), 'segment' => $singleton->config()->segment ?: str_replace('\\', '-', $class), 'description' => $desc);
     }
     return $availableTasks;
 }
 /**
  * Return an appropriate base tag for the given template.
  * It will be closed on an XHTML document, and unclosed on an HTML document.
  *
  * @param string $contentGeneratedSoFar The content of the template generated so far; it should contain
  * the DOCTYPE declaration.
  * @return string
  */
 public static function get_base_tag($contentGeneratedSoFar)
 {
     $base = Director::absoluteBaseURL();
     // Is the document XHTML?
     if (preg_match('/<!DOCTYPE[^>]+xhtml/i', $contentGeneratedSoFar)) {
         return "<base href=\"{$base}\" />";
     } else {
         return "<base href=\"{$base}\"><!--[if lte IE 6]></base><![endif]-->";
     }
 }
 /**
  * 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}");
         }
     }
 }
 /**
  * @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);
 }