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); }