API Implement APP object

API Refactor of Session
This commit is contained in:
Damian Mooyman 2017-06-07 18:07:33 +12:00
parent 306d801258
commit de079c041d
71 changed files with 2083 additions and 1671 deletions

View File

@ -1,8 +1,7 @@
<?php
use SilverStripe\ORM\DB;
use SilverStripe\ORM\DataModel;
use SilverStripe\Control\Director;
use SilverStripe\ORM\DB;
/**
@ -145,8 +144,6 @@ if(!$url) {
$_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());
Director::direct($url);

View File

@ -82,12 +82,12 @@
"SilverStripe\\Framework\\Tests\\Behaviour\\": "tests/behat/src/"
},
"files": [
"src/Core/Constants.php",
"src/Dev/PhpUnitShim.php"
"src/includes/constants.php"
]
},
"include-path": [
"src/",
"src/includes/",
"thirdparty/"
],
"scripts": {

View File

@ -1335,6 +1335,7 @@ After (`mysite/_config/config.yml`):
* `findAnAdministrator` use `DefaultAdminService::findOrCreateDefaultAdmin()` instead
* `Member` methods deprecated:
* `checkPassword`. Use Authenticator::checkPassword() instead
* `RequestFilter` changed. $session and $dataModel variables removed from preRequest / postRequest
#### <a name="overview-general-removed"></a>General and Core Removed API
@ -1523,6 +1524,7 @@ The below methods have been added or had their functionality updated to `DBDate`
#### <a name="overview-orm-removed"></a>ORM Removed API
* `DataModel` removed
* `DataObject::can*` methods no longer accept a member ID. These must now be passed a Member object or left null
* `DataObject::db` removed and replaced with `DataObjectSchema::fieldSpec` and `DataObjectSchema::fieldSpecs`
* `DataObject::manyManyComponent` moved to `DataObjectSchema`

228
main.php
View File

@ -1,226 +1,26 @@
<?php
use SilverStripe\ORM\DB;
use SilverStripe\ORM\DataModel;
use SilverStripe\Security\Security;
use SilverStripe\Security\Permission;
use SilverStripe\Core\Startup\ParameterConfirmationToken;
use SilverStripe\Core\Startup\ErrorControlChain;
use SilverStripe\Control\Session;
use SilverStripe\Control\Director;
/************************************************************************************
************************************************************************************
** **
** If you can read this text in your browser then you don't have PHP installed. **
** Please install PHP 5.5.0 or higher . **
** Please install PHP 5.6.0 or higher **
** **
************************************************************************************
************************************************************************************/
if (version_compare(phpversion(), '5.5.0', '<')) {
header($_SERVER['SERVER_PROTOCOL'] . " 500 Server Error");
echo str_replace('$PHPVersion', phpversion(), file_get_contents("Dev/Install/php5-required.html"));
die();
}
use SilverStripe\Core\AppKernel;
use SilverStripe\Core\HTTPApplication;
use SilverStripe\Core\Startup\ErrorControlChainMiddleware;
use SilverStripe\Core\Startup\OutputMiddleware;
use SilverStripe\Control\HTTPRequest;
/**
* Main file that handles every page request.
*
* The main.php does a number of set-up activities for the request.
*
* - Includes the .env file in your webroot
* - Gets an up-to-date manifest from {@link ManifestBuilder}
* - Sets up error handlers with {@link Debug::loadErrorHandlers()}
* - Calls {@link DB::connect()}, passing it the global variable $databaseConfig that should
* be defined in an _config.php
* - Sets up the default director rules using {@link Director::$rules}
*
* After that, it calls {@link Director::direct()}, which is responsible for doing most of the
* real work.
*
* CONFIGURING THE WEBSERVER
*
* To use SilverStripe, every request that doesn't point directly to a file should be rewritten to
* framework/main.php?url=(url). For example, http://www.example.com/about-us/rss would be rewritten
* to http://www.example.com/framework/main.php?url=about-us/rss
*
* It's important that requests that point directly to a file aren't rewritten; otherwise, visitors
* won't be able to download any CSS, JS, image files, or other downloads.
*
* On Apache, RewriteEngine can be used to do this.
*
* @see Director::direct()
*/
require __DIR__ . '/src/includes/autoload.php';
// require composers autoloader, unless it is already installed
if(!class_exists('Composer\\Autoload\\ClassLoader', false)) {
if (file_exists($autoloadPath = dirname(__DIR__) . '/vendor/autoload.php')) {
require_once $autoloadPath;
}
else {
if (!headers_sent()) {
header($_SERVER['SERVER_PROTOCOL'] . " 500 Server Error");
header('Content-Type: text/plain');
}
echo "Failed to include composer's autoloader, unable to continue\n";
exit(1);
}
}
// IIS will sometimes generate this.
if(!empty($_SERVER['HTTP_X_ORIGINAL_URL'])) {
$_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL'];
}
// Enable the entity loader to be able to load XML in Zend_Locale_Data
libxml_disable_entity_loader(false);
/**
* Figure out the request URL
*/
global $url;
// Helper to safely parse and load a querystring fragment
$parseQuery = function($query) {
parse_str($query, $_GET);
if ($_GET) $_REQUEST = array_merge((array)$_REQUEST, (array)$_GET);
};
// Apache rewrite rules and IIS use this
if (isset($_GET['url']) && php_sapi_name() !== 'cli-server') {
// Prevent injection of url= querystring argument by prioritising any leading url argument
if(isset($_SERVER['QUERY_STRING']) &&
preg_match('/^(?<url>url=[^&?]*)(?<query>.*[&?]url=.*)$/', $_SERVER['QUERY_STRING'], $results)
) {
$queryString = $results['query'].'&'.$results['url'];
$parseQuery($queryString);
}
$url = $_GET['url'];
// IIS includes get variables in url
$i = strpos($url, '?');
if($i !== false) {
$url = substr($url, 0, $i);
}
// Lighttpd and PHP 5.4's built-in webserver use this
} else {
// Get raw URL -- still needs to be decoded below (after parsing out query string).
$url = $_SERVER['REQUEST_URI'];
// Querystring args need to be explicitly parsed
if(strpos($url,'?') !== false) {
list($url, $query) = explode('?',$url,2);
$parseQuery($query);
}
// Decode URL now that it has been separated from query string.
$url = urldecode($url);
// Pass back to the webserver for files that exist
if(php_sapi_name() === 'cli-server' && file_exists(BASE_PATH . $url) && is_file(BASE_PATH . $url)) {
return false;
}
}
// Remove base folders from the URL if webroot is hosted in a subfolder
if (substr(strtolower($url), 0, strlen(BASE_URL)) == strtolower(BASE_URL)) $url = substr($url, strlen(BASE_URL));
/**
* Include SilverStripe's core code
*/
require_once('Core/Startup/ErrorControlChain.php');
require_once('Core/Startup/ParameterConfirmationToken.php');
// Prepare tokens and execute chain
$reloadToken = ParameterConfirmationToken::prepare_tokens(array('isTest', 'isDev', 'flush'));
$chain = new ErrorControlChain();
$chain
->then(function($chain) use ($reloadToken) {
// If no redirection is necessary then we can disable error supression
if (!$reloadToken) $chain->setSuppression(false);
// Load in core
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')) {
$reloadToken->reloadWithToken();
return;
}
// 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;
})
// Finally if a token was requested but there was an error while figuring out if it's allowed, do it anyway
->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']) {
// Is there an _ss_environment.php file?
if(file_exists(BASE_PATH . '/_ss_environment.php') || file_exists(dirname(BASE_PATH) . '/_ss_environment.php')) {
header($_SERVER['SERVER_PROTOCOL'] . " 500 Server Error");
$dv = new SilverStripe\Dev\DebugView();
echo $dv->renderHeader();
echo $dv->renderInfo(
"Configuraton Error",
Director::absoluteBaseURL()
);
echo $dv->renderParagraph(
'You need to replace your _ss_environment.php file with a .env file, or with environment variables.<br><br>'
. 'See the <a href="https://docs.silverstripe.org/en/4/getting_started/environment_management/">'
. 'Environment Management</a> docs for more information.'
);
echo $dv->renderFooter();
die();
}
if(!file_exists(BASE_PATH . '/install.php')) {
header($_SERVER['SERVER_PROTOCOL'] . " 500 Server Error");
die('SilverStripe Framework requires a $databaseConfig defined.');
}
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
$s = (isset($_SERVER['SSL']) || (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) ? 's' : '';
$installURL = "http$s://" . $host . BASE_URL . '/install.php';
// The above dirname() will equate to "\" on Windows when installing directly from http://localhost (not using
// a sub-directory), this really messes things up in some browsers. Let's get rid of the backslashes
$installURL = str_replace('\\', '', $installURL);
header("Location: $installURL");
die();
}
// Direct away - this is the "main" function, that hands control to the appropriate controller
DataModel::set_inst(new DataModel());
Director::direct($url, DataModel::inst());
// Default application
$request = HTTPRequest::createFromEnvironment();
$kernel = new AppKernel();
$app = new HTTPApplication($kernel);
$app->addMiddleware(new OutputMiddleware());
$app->addMiddleware(new ErrorControlChainMiddleware($app, $request));
$app->handle($request);

View File

@ -1,9 +0,0 @@
<?php
/**
* @package framework
* @subpackage core
*
* Alternative main.php file for servers that need the php5 extension
*/
include("main.php");
?>

View File

@ -3,9 +3,7 @@
namespace SilverStripe\Control;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\Debug;
use SilverStripe\ORM\DataModel;
use SilverStripe\ORM\FieldType\DBHTMLText;
use SilverStripe\Security\BasicAuth;
use SilverStripe\Security\Member;
@ -47,13 +45,6 @@ class Controller extends RequestHandler implements TemplateGlobalProvider
*/
protected $action;
/**
* The {@link Session} object for this controller.
*
* @var Session
*/
protected $session;
/**
* Stack of current controllers. Controller::$controller_stack[0] is the current controller.
*
@ -152,16 +143,14 @@ class Controller extends RequestHandler implements TemplateGlobalProvider
* @todo setDataModel and setRequest are redundantly called in parent::handleRequest() - sort this out
*
* @param HTTPRequest $request
* @param DataModel $model
*/
protected function beforeHandleRequest(HTTPRequest $request, DataModel $model)
protected function beforeHandleRequest(HTTPRequest $request)
{
//Push the current controller to protect against weird session issues
$this->pushCurrent();
//Set up the internal dependencies (request, response, datamodel)
//Set up the internal dependencies (request, response)
$this->setRequest($request);
$this->setResponse(new HTTPResponse());
$this->setDataModel($model);
//kick off the init functionality
$this->doInit();
}
@ -192,24 +181,22 @@ class Controller extends RequestHandler implements TemplateGlobalProvider
* and end the method with $this->afterHandleRequest()
*
* @param HTTPRequest $request
* @param DataModel $model
*
* @return HTTPResponse
*/
public function handleRequest(HTTPRequest $request, DataModel $model)
public function handleRequest(HTTPRequest $request)
{
if (!$request) {
user_error("Controller::handleRequest() not passed a request!", E_USER_ERROR);
}
//set up the controller for the incoming request
$this->beforeHandleRequest($request, $model);
$this->beforeHandleRequest($request);
//if the before handler manipulated the response in a way that we shouldn't proceed, then skip our request
// handling
if (!$this->getResponse()->isFinished()) {
//retrieve the response for the request
$response = parent::handleRequest($request, $model);
$response = parent::handleRequest($request);
//prepare the response (we can receive an assortment of response types (strings/objects/HTTPResponses)
$this->prepareResponse($response);
@ -597,14 +584,6 @@ class Controller extends RequestHandler implements TemplateGlobalProvider
public function pushCurrent()
{
array_unshift(self::$controller_stack, $this);
// Create a new session object
if (!$this->session) {
if (isset(self::$controller_stack[1])) {
$this->session = self::$controller_stack[1]->getSession();
} else {
$this->session = Injector::inst()->create('SilverStripe\\Control\\Session', array());
}
}
}
/**
@ -653,26 +632,6 @@ class Controller extends RequestHandler implements TemplateGlobalProvider
return $this->getResponse() && $this->getResponse()->getHeader('Location');
}
/**
* Get the Session object representing this Controller's session.
*
* @return Session
*/
public function getSession()
{
return $this->session;
}
/**
* Set the Session object.
*
* @param Session $session
*/
public function setSession(Session $session)
{
$this->session = $session;
}
/**
* Joins two or more link segments together, putting a slash between them if necessary. Use this
* for building the results of {@link Link()} methods. If either of the links have query strings,

View File

@ -2,15 +2,14 @@
namespace SilverStripe\Control;
use InvalidArgumentException;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Kernel;
use SilverStripe\Dev\Deprecation;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\ORM\ArrayLib;
use SilverStripe\ORM\DataModel;
use SilverStripe\Versioned\Versioned;
use SilverStripe\View\Requirements;
use SilverStripe\View\Requirements_Backend;
@ -120,112 +119,41 @@ class Director implements TemplateGlobalProvider
*
* @uses handleRequest() rule-lookup logic is handled by this.
* @uses TestController::handleRequest() This handles the page logic for a Director::direct() call.
* @param string $url
* @param DataModel $model
* @param HTTPRequest $request
* @return HTTPResponse
* @throws HTTPResponse_Exception
*/
public static function direct($url, DataModel $model)
public static function direct(HTTPRequest $request)
{
// check allowed hosts
if (getenv('SS_ALLOWED_HOSTS') && !Director::is_cli()) {
$all_allowed_hosts = explode(',', getenv('SS_ALLOWED_HOSTS'));
if (!in_array(static::host(), $all_allowed_hosts)) {
throw new HTTPResponse_Exception('Invalid Host', 400);
}
}
// Validate $_FILES array before merging it with $_POST
foreach ($_FILES as $k => $v) {
if (is_array($v['tmp_name'])) {
$v = ArrayLib::array_values_recursive($v['tmp_name']);
foreach ($v as $tmpFile) {
if ($tmpFile && !is_uploaded_file($tmpFile)) {
user_error("File upload '$k' doesn't appear to be a valid upload", E_USER_ERROR);
}
}
} else {
if ($v['tmp_name'] && !is_uploaded_file($v['tmp_name'])) {
user_error("File upload '$k' doesn't appear to be a valid upload", E_USER_ERROR);
}
}
}
$req = new HTTPRequest(
(isset($_SERVER['X-HTTP-Method-Override']))
? $_SERVER['X-HTTP-Method-Override']
: $_SERVER['REQUEST_METHOD'],
$url,
$_GET,
ArrayLib::array_merge_recursive((array) $_POST, (array) $_FILES),
@file_get_contents('php://input')
);
$headers = self::extract_request_headers($_SERVER);
foreach ($headers as $header => $value) {
$req->addHeader($header, $value);
}
// Initiate an empty session - doesn't initialize an actual PHP session until saved (see below)
$session = Session::create(isset($_SESSION) ? $_SESSION : array());
// Only resume a session if its not started already, and a session identifier exists
if (!isset($_SESSION) && Session::request_contains_session_id()) {
$session->inst_start();
}
$output = RequestProcessor::singleton()->preRequest($req, $session, $model);
// Pre-request
$output = RequestProcessor::singleton()->preRequest($request);
if ($output === false) {
// @TODO Need to NOT proceed with the request in an elegant manner
throw new HTTPResponse_Exception(_t('SilverStripe\\Control\\Director.INVALID_REQUEST', 'Invalid request'), 400);
return new HTTPResponse(_t(__CLASS__.'.INVALID_REQUEST', 'Invalid request'), 400);
}
$result = Director::handleRequest($req, $session, $model);
// Generate output
$result = static::handleRequest($request);
// Save session data. Note that inst_save() will start/resume the session if required.
$session->inst_save();
$request->getSession()->save();
// Return code for a redirection request
if (is_string($result) && substr($result, 0, 9) == 'redirect:') {
$url = substr($result, 9);
if (Director::is_cli()) {
// on cli, follow SilverStripe redirects automatically
Director::direct(
str_replace(Director::absoluteBaseURL(), '', $url),
DataModel::inst()
);
return;
} else {
$response = new HTTPResponse();
$response->redirect($url);
$res = RequestProcessor::singleton()->postRequest($req, $response, $model);
if ($res !== false) {
$response->output();
}
}
// Handle a controller
} elseif ($result) {
if ($result instanceof HTTPResponse) {
$response = $result;
} else {
$response = new HTTPResponse();
$response->setBody($result);
}
$res = RequestProcessor::singleton()->postRequest($req, $response, $model);
if ($res !== false) {
$response->output();
} else {
// @TODO Proper response here.
throw new HTTPResponse_Exception("Invalid response");
}
//$controllerObj->getSession()->inst_save();
// @todo: Refactor into CLIApplication
if ($result->isRedirect() && static::is_cli()) {
$url = Director::makeRelative($result->getHeader('Location'));
$request = clone $request;
$request->setUrl($url);
return static::direct($request);
}
// Post-request handling
$postRequest = RequestProcessor::singleton()->postRequest($request, $result);
if ($postRequest === false) {
return new HTTPResponse(_t(__CLASS__ . '.REQUEST_ABORTED', 'Request aborted'), 500);
}
// Return
return $result;
}
/**
@ -278,7 +206,7 @@ class Director implements TemplateGlobalProvider
}
if (!$session) {
$session = Session::create([]);
$session = new Session([]);
}
$cookieJar = $cookies instanceof Cookie_Backend
? $cookies
@ -340,15 +268,14 @@ class Director implements TemplateGlobalProvider
try {
// Pre-request filtering
$model = DataModel::inst();
$requestProcessor = Injector::inst()->get(RequestProcessor::class);
$output = $requestProcessor->preRequest($request, $session, $model);
$output = $requestProcessor->preRequest($request);
if ($output === false) {
throw new HTTPResponse_Exception(_t('SilverStripe\\Control\\Director.INVALID_REQUEST', 'Invalid request'), 400);
}
// Process request
$result = Director::handleRequest($request, $session, $model);
$result = Director::handleRequest($request);
// Ensure that the result is an HTTPResponse object
if (is_string($result)) {
@ -395,15 +322,14 @@ class Director implements TemplateGlobalProvider
*
* @skipUpgrade
* @param HTTPRequest $request
* @param Session $session
* @param DataModel $model
* @return HTTPResponse|string
* @return HTTPResponse
*/
protected static function handleRequest(HTTPRequest $request, Session $session, DataModel $model)
protected static function handleRequest(HTTPRequest $request)
{
$rules = Director::config()->uninherited('rules');
foreach ($rules as $pattern => $controllerOptions) {
// Normalise route rule
if (is_string($controllerOptions)) {
if (substr($controllerOptions, 0, 2) == '->') {
$controllerOptions = array('Redirect' => substr($controllerOptions, 2));
@ -412,7 +338,9 @@ class Director implements TemplateGlobalProvider
}
}
if (($arguments = $request->match($pattern, true)) !== false) {
// Match pattern
$arguments = $request->match($pattern, true);
if ($arguments !== false) {
$request->setRouteParams($controllerOptions);
// controllerOptions provide some default arguments
$arguments = array_merge($controllerOptions, $arguments);
@ -424,24 +352,20 @@ class Director implements TemplateGlobalProvider
// Handle redirection
if (isset($arguments['Redirect'])) {
return "redirect:" . Director::absoluteURL($arguments['Redirect'], true);
} else {
// Find the controller name
$controller = $arguments['Controller'];
$controllerObj = Injector::inst()->create($controller);
$controllerObj->setSession($session);
// Redirection
$response = new HTTPResponse();
$response->redirect(static::absoluteURL($arguments['Redirect']));
return $response;
}
try {
$result = $controllerObj->handleRequest($request, $model);
} catch (HTTPResponse_Exception $responseException) {
$result = $responseException->getResponse();
}
if (!is_object($result) || $result instanceof HTTPResponse) {
return $result;
}
// Find the controller name
$controller = $arguments['Controller'];
$controllerObj = Injector::inst()->create($controller);
user_error("Bad result from url " . $request->getURL() . " handled by " .
get_class($controllerObj)." controller: ".get_class($result), E_USER_WARNING);
try {
return $controllerObj->handleRequest($request);
} catch (HTTPResponse_Exception $responseException) {
return $responseException->getResponse();
}
}
}
@ -852,36 +776,6 @@ class Director implements TemplateGlobalProvider
}
}
/**
* Takes a $_SERVER data array and extracts HTTP request headers.
*
* @param array $server
*
* @return array
*/
public static function extract_request_headers(array $server)
{
$headers = array();
foreach ($server as $key => $value) {
if (substr($key, 0, 5) == 'HTTP_') {
$key = substr($key, 5);
$key = strtolower(str_replace('_', ' ', $key));
$key = str_replace(' ', '-', ucwords($key));
$headers[$key] = $value;
}
}
if (isset($server['CONTENT_TYPE'])) {
$headers['Content-Type'] = $server['CONTENT_TYPE'];
}
if (isset($server['CONTENT_LENGTH'])) {
$headers['Content-Length'] = $server['CONTENT_LENGTH'];
}
return $headers;
}
/**
* Given a filesystem reference relative to the site root, return the full file-system path.
*
@ -1073,47 +967,7 @@ class Director implements TemplateGlobalProvider
*/
public static function is_cli()
{
return (php_sapi_name() == "cli");
}
/**
* Set the environment type of the current site.
*
* Typically, a SilverStripe site have a number of environments:
* - Development environments, such a copy on your local machine.
* - Test sites, such as the one you show the client before going live.
* - The live site itself.
*
* The behaviour of these environments often varies slightly. For example, development sites may
* have errors dumped to the screen, and order confirmation emails might be sent to the developer
* instead of the client.
*
* To help with this, SilverStripe supports the notion of an environment type. The environment
* type can be dev, test, or live.
*
* Dev mode can also be forced by putting ?isDev=1 in your URL, which will ask you to log in and
* then push the site into dev mode for the remainder of the session. Putting ?isDev=0 onto the URL
* can turn it back.
*
* Test mode can also be forced by putting ?isTest=1 in your URL, which will ask you to log in and
* then push the site into test mode for the remainder of the session. Putting ?isTest=0 onto the URL
* can turn it back.
*
* Generally speaking, these methods will be called from your _config.php file.
*
* Once the environment type is set, it can be checked with {@link Director::isDev()},
* {@link Director::isTest()}, and {@link Director::isLive()}.
*
* @param string $environment
*/
public static function set_environment_type($environment)
{
if (!in_array($environment, ['dev', 'test', 'live'])) {
throw new InvalidArgumentException(
"Director::set_environment_type passed '$environment'. It should be passed dev, test, or live"
);
}
self::$environment_type = $environment;
return php_sapi_name() === "cli";
}
/**
@ -1124,22 +978,9 @@ class Director implements TemplateGlobalProvider
*/
public static function get_environment_type()
{
// Check saved session
if ($env = self::session_environment()) {
return $env;
}
// Check set
if (self::$environment_type) {
return self::$environment_type;
}
// Check getenv
if ($env = getenv('SS_ENVIRONMENT_TYPE')) {
return $env;
}
return 'live';
/** @var Kernel $kernel */
$kernel = Injector::inst()->get(Kernel::class);
return $kernel->getEnvironment();
}
/**
@ -1175,37 +1016,6 @@ class Director implements TemplateGlobalProvider
return self::get_environment_type() === 'test';
}
/**
* Check or update any temporary environment specified in the session.
*
* @return null|string
*/
public static function session_environment()
{
// Set session from querystring
if (isset($_GET['isDev'])) {
if (isset($_SESSION)) {
unset($_SESSION['isTest']); // In case we are changing from test mode
$_SESSION['isDev'] = $_GET['isDev'];
}
return 'dev';
} elseif (isset($_GET['isTest'])) {
if (isset($_SESSION)) {
unset($_SESSION['isDev']); // In case we are changing from dev mode
$_SESSION['isTest'] = $_GET['isTest'];
}
return 'test';
}
// Check session
if (isset($_SESSION['isDev']) && $_SESSION['isDev']) {
return 'dev';
} elseif (isset($_SESSION['isTest']) && $_SESSION['isTest']) {
return 'test';
} else {
return null;
}
}
/**
* Returns an array of strings of the method names of methods on the call that should be exposed
* as global variables in the templates.

View File

@ -96,6 +96,72 @@ class Email extends ViewableData
return \Swift_Validate::email($address);
}
/**
* Get send_all_emails_to
*
* @return array Keys are addresses, values are names
*/
public static function getSendAllEmailsTo()
{
return static::mergeConfiguredEmails('send_all_emails_to', 'SS_SEND_ALL_EMAILS_TO');
}
/**
* Get cc_all_emails_to
*
* @return array
*/
public static function getCCAllEmailsTo()
{
return static::mergeConfiguredEmails('cc_all_emails_to', 'SS_CC_ALL_EMAILS_TO');
}
/**
* Get bcc_all_emails_to
*
* @return array
*/
public static function getBCCAllEmailsTo()
{
return static::mergeConfiguredEmails('bcc_all_emails_to', 'SS_BCC_ALL_EMAILS_TO');
}
/**
* Get send_all_emails_from
*
* @return array
*/
public static function getSendAllEmailsFrom()
{
return static::mergeConfiguredEmails('send_all_emails_from', 'SS_SEND_ALL_EMAILS_FROM');
}
/**
* Normalise email list from config merged with env vars
*
* @param string $config Config key
* @param string $env Env variable key
* @return array Array of email addresses
*/
protected static function mergeConfiguredEmails($config, $env)
{
// Normalise config list
$normalised = [];
$source = (array)static::config()->get($config);
foreach ($source as $address => $name) {
if ($address && !is_numeric($address)) {
$normalised[$address] = $name;
} elseif ($name) {
$normalised[$name] = null;
}
}
$extra = getenv($env);
if ($extra) {
$normalised[$extra] = null;
}
return $normalised;
}
/**
* Encode an email-address to protect it from spambots.
* At the moment only simple string substitutions,

View File

@ -11,36 +11,29 @@ class SwiftPlugin implements \Swift_Events_SendListener
*/
public function beforeSendPerformed(\Swift_Events_SendEvent $evt)
{
/** @var \Swift_Message $message */
$message = $evt->getMessage();
$sendAllTo = Email::config()->send_all_emails_to;
$ccAllTo = Email::config()->cc_all_emails_to;
$bccAllTo = Email::config()->bcc_all_emails_to;
$sendAllFrom = Email::config()->send_all_emails_from;
$sendAllTo = Email::getSendAllEmailsTo();
if (!empty($sendAllTo)) {
$this->setTo($message, $sendAllTo);
}
$ccAllTo = Email::getCCAllEmailsTo();
if (!empty($ccAllTo)) {
if (!is_array($ccAllTo)) {
$ccAllTo = array($ccAllTo => null);
}
foreach ($ccAllTo as $address => $name) {
$message->addCc($address, $name);
}
}
$bccAllTo = Email::getBCCAllEmailsTo();
if (!empty($bccAllTo)) {
if (!is_array($bccAllTo)) {
$bccAllTo = array($bccAllTo => null);
}
foreach ($bccAllTo as $address => $name) {
$message->addBcc($address, $name);
}
}
$sendAllFrom = Email::getSendAllEmailsFrom();
if (!empty($sendAllFrom)) {
$this->setFrom($message, $sendAllFrom);
}
@ -48,7 +41,7 @@ class SwiftPlugin implements \Swift_Events_SendListener
/**
* @param \Swift_Mime_Message $message
* @param string $to
* @param array|string $to
*/
protected function setTo($message, $to)
{
@ -70,7 +63,7 @@ class SwiftPlugin implements \Swift_Events_SendListener
/**
* @param \Swift_Mime_Message $message
* @param string $from
* @param array|string $from
*/
protected function setFrom($message, $from)
{

View File

@ -3,7 +3,6 @@
namespace SilverStripe\Control;
use SilverStripe\Core\Flushable;
use SilverStripe\ORM\DataModel;
use SilverStripe\Core\ClassInfo;
/**
@ -11,37 +10,17 @@ use SilverStripe\Core\ClassInfo;
*/
class FlushRequestFilter implements RequestFilter
{
/**
* @inheritdoc
*
* @param HTTPRequest $request
* @param Session $session
* @param DataModel $model
*
* @return bool
*/
public function preRequest(HTTPRequest $request, Session $session, DataModel $model)
public function preRequest(HTTPRequest $request)
{
if (array_key_exists('flush', $request->getVars())) {
foreach (ClassInfo::implementorsOf(Flushable::class) as $class) {
$class::flush();
}
}
return true;
}
/**
* @inheritdoc
*
* @param HTTPRequest $request
* @param HTTPResponse $response
* @param DataModel $model
*
* @return bool
*/
public function postRequest(HTTPRequest $request, HTTPResponse $response, DataModel $model)
public function postRequest(HTTPRequest $request, HTTPResponse $response)
{
return true;
}

View File

@ -125,6 +125,11 @@ class HTTPRequest implements ArrayAccess
*/
protected $unshiftedButParsedParts = 0;
/**
* @var Session
*/
protected $session;
/**
* Construct a HTTPRequest from a URL relative to the site root.
*
@ -138,12 +143,194 @@ class HTTPRequest implements ArrayAccess
{
$this->httpMethod = strtoupper(self::detect_method($httpMethod, $postVars));
$this->setUrl($url);
$this->getVars = (array) $getVars;
$this->postVars = (array) $postVars;
$this->body = $body;
}
/**
* Create HTTPRequest instance from the current environment variables.
* May throw errors if request is invalid.
*
* @throws HTTPResponse_Exception
* @return static
*/
public static function createFromEnvironment()
{
// Health-check prior to creating environment
$variables = static::variablesFromEnvironment();
return self::createFromVariables($variables, @file_get_contents('php://input'));
}
/**
* Takes a $_SERVER data array and extracts HTTP request headers.
*
* @param array $server
*
* @return array
*/
public static function extractRequestHeaders(array $server)
{
$headers = array();
foreach ($server as $key => $value) {
if (substr($key, 0, 5) == 'HTTP_') {
$key = substr($key, 5);
$key = strtolower(str_replace('_', ' ', $key));
$key = str_replace(' ', '-', ucwords($key));
$headers[$key] = $value;
}
}
if (isset($server['CONTENT_TYPE'])) {
$headers['Content-Type'] = $server['CONTENT_TYPE'];
}
if (isset($server['CONTENT_LENGTH'])) {
$headers['Content-Length'] = $server['CONTENT_LENGTH'];
}
return $headers;
}
/**
* Clean up HTTP global vars for $_GET / $_REQUEST prior to bootstrapping
* Will also populate the $_GET['url'] var safely
*
* @param array $variables
* @return array Cleaned variables
*/
public static function cleanEnvironment(array $variables)
{
// IIS will sometimes generate this.
if (!empty($variables['_SERVER']['HTTP_X_ORIGINAL_URL'])) {
$variables['_SERVER']['REQUEST_URI'] = $variables['_SERVER']['HTTP_X_ORIGINAL_URL'];
}
// Override REQUEST_METHOD
if (isset($variables['_SERVER']['X-HTTP-Method-Override'])) {
$variables['_SERVER']['REQUEST_METHOD'] = $variables['_SERVER']['X-HTTP-Method-Override'];
}
// Prevent injection of url= querystring argument by prioritising any leading url argument
if (isset($variables['_SERVER']['QUERY_STRING']) &&
preg_match('/^(?<url>url=[^&?]*)(?<query>.*[&?]url=.*)$/', $variables['_SERVER']['QUERY_STRING'], $results)
) {
$queryString = $results['query'].'&'.$results['url'];
parse_str($queryString, $variables['_GET']);
}
// Decode url from REQUEST_URI if not passed via $_GET['url']
if (!isset($variables['_GET']['url'])) {
$url = $variables['_SERVER']['REQUEST_URI'];
// Querystring args need to be explicitly parsed
if (strpos($url, '?') !== false) {
list($url, $queryString) = explode('?', $url, 2);
parse_str($queryString);
}
// Ensure $_GET['url'] is set
$variables['_GET']['url'] = urldecode($url);
}
// Remove base folders from the URL if webroot is hosted in a subfolder
if (substr(strtolower($variables['_GET']['url']), 0, strlen(BASE_URL)) === strtolower(BASE_URL)) {
$variables['_GET']['url'] = substr($variables['_GET']['url'], strlen(BASE_URL));
}
// Merge $_FILES into $_POST
$variables['_POST'] = array_merge((array)$variables['_POST'], (array)$variables['_FILES']);
// Merge $_POST, $_GET, and $_COOKIE into $_REQUEST
$variables['_REQUEST'] = array_merge(
(array)$variables['_GET'],
(array)$variables['_POST'],
(array)$variables['_COOKIE']
);
return $variables;
}
/**
* Validate environment vars prior to building HTTPRequest
* Error conditions will raise HTTPResponse_Exceptions
*
* @throws HTTPResponse_Exception
* @return array
*/
protected static function variablesFromEnvironment()
{
// Validate $_FILES array before merging it with $_POST
foreach ($_FILES as $key => $value) {
if (is_array($value['tmp_name'])) {
$tempFiles = ArrayLib::array_values_recursive($value['tmp_name']);
} else {
$tempFiles = [ $value['tmp_name'] ];
}
foreach ($tempFiles as $tmpFile) {
if ($tmpFile && !is_uploaded_file($tmpFile)) {
throw new HTTPResponse_Exception(
"File upload '{$key}' doesn't appear to be a valid upload",
400
);
}
}
}
// Validate hostname
if (getenv('SS_ALLOWED_HOSTS') && !Director::is_cli()) {
$all_allowed_hosts = explode(',', getenv('SS_ALLOWED_HOSTS'));
if (!in_array(Director::host(), $all_allowed_hosts)) {
throw new HTTPResponse_Exception('Invalid Host', 400);
}
}
return [
'_SERVER' => $_SERVER,
'_GET' => $_GET,
'_POST' => $_POST,
'_FILES' => $_FILES,
'_SESSION' => isset($_SESSION) ? $_SESSION : null,
'_COOKIE' => $_COOKIE
];
}
/**
* Build HTTPRequest from given variables
*
* @param array $variables
* @param string $input Request body
* @return HTTPRequest
*/
public static function createFromVariables(array $variables, $input)
{
$variables = static::cleanEnvironment($variables);
// Strip `url` out of querystring
$url = $variables['_GET']['url'];
unset($variables['_GET']['url']);
// Build request
$request = new HTTPRequest(
$variables['_SERVER']['REQUEST_METHOD'],
$url,
$variables['_GET'],
$variables['_POST'],
$input
);
// Add headers
$headers = static::extractRequestHeaders($variables['_SERVER']);
foreach ($headers as $header => $value) {
$request->addHeader($header, $value);
}
// Initiate an empty session - doesn't initialize an actual PHP session (see HTTPApplication)
$session = new Session($variables['_SESSION']);
$request->setSession($session);
return $request;
}
/**
* Allow the setting of a URL
*
@ -435,21 +622,15 @@ class HTTPRequest implements ArrayAccess
return $this->requestVar($offset);
}
/**
* @ignore
* @param string $offset
* @param mixed $value
*/
public function offsetSet($offset, $value)
{
$this->getVars[$offset] = $value;
}
/**
* @ignore
* @param mixed $offset
*/
public function offsetUnset($offset)
{
unset($this->getVars[$offset]);
unset($this->postVars[$offset]);
}
/**
@ -866,4 +1047,22 @@ class HTTPRequest implements ArrayAccess
return $origMethod;
}
}
/**
* @return Session
*/
public function getSession()
{
return $this->session;
}
/**
* @param Session $session
* @return $this
*/
public function setSession(Session $session)
{
$this->session = $session;
return $this;
}
}

View File

@ -2,8 +2,6 @@
namespace SilverStripe\Control;
use SilverStripe\ORM\DataModel;
/**
* A request filter is an object that's executed before and after a
* request occurs. By returning 'false' from the preRequest method,
@ -14,24 +12,20 @@ use SilverStripe\ORM\DataModel;
*/
interface RequestFilter
{
/**
* Filter executed before a request processes
*
* @param HTTPRequest $request Request container object
* @param Session $session Request session
* @param DataModel $model Current DataModel
* @return boolean Whether to continue processing other filters. Null or true will continue processing (optional)
*/
public function preRequest(HTTPRequest $request, Session $session, DataModel $model);
public function preRequest(HTTPRequest $request);
/**
* Filter executed AFTER a request
*
* @param HTTPRequest $request Request container object
* @param HTTPResponse $response Response output object
* @param DataModel $model Current DataModel
* @return boolean Whether to continue processing other filters. Null or true will continue processing (optional)
* @param HTTPResponse $response
* @return bool Whether to continue processing other filters. Null or true will continue processing (optional)
*/
public function postRequest(HTTPRequest $request, HTTPResponse $response, DataModel $model);
public function postRequest(HTTPRequest $request, HTTPResponse $response);
}

View File

@ -6,7 +6,6 @@ use InvalidArgumentException;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\Debug;
use SilverStripe\ORM\DataModel;
use SilverStripe\Security\Security;
use SilverStripe\Security\PermissionFailureException;
use SilverStripe\Security\Permission;
@ -123,22 +122,9 @@ class RequestHandler extends ViewableData
$this->setRequest(new NullHTTPRequest());
// This will prevent bugs if setDataModel() isn't called.
$this->model = DataModel::inst();
parent::__construct();
}
/**
* Set the DataModel for this request.
*
* @param DataModel $model
*/
public function setDataModel($model)
{
$this->model = $model;
}
/**
* Handles URL requests.
*
@ -156,10 +142,9 @@ class RequestHandler extends ViewableData
* customise the controller.
*
* @param HTTPRequest $request The object that is reponsible for distributing URL parsing
* @param DataModel $model
* @return HTTPResponse|RequestHandler|string|array
*/
public function handleRequest(HTTPRequest $request, DataModel $model)
public function handleRequest(HTTPRequest $request)
{
// $handlerClass is used to step up the class hierarchy to implement url_handlers inheritance
if ($this->brokenOnConstruct) {
@ -170,7 +155,6 @@ class RequestHandler extends ViewableData
}
$this->setRequest($request);
$this->setDataModel($model);
$match = $this->findAction($request);
@ -237,7 +221,7 @@ class RequestHandler extends ViewableData
if ($result instanceof HasRequestHandler) {
$result = $result->getRequestHandler();
}
$returnValue = $result->handleRequest($request, $model);
$returnValue = $result->handleRequest($request);
// Array results can be used to handle
if (is_array($returnValue)) {

View File

@ -3,7 +3,6 @@
namespace SilverStripe\Control;
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\ORM\DataModel;
/**
* Represents a request processer that delegates pre and post request handling to nested request filters
@ -34,10 +33,10 @@ class RequestProcessor implements RequestFilter
$this->filters = $filters;
}
public function preRequest(HTTPRequest $request, Session $session, DataModel $model)
public function preRequest(HTTPRequest $request)
{
foreach ($this->filters as $filter) {
$res = $filter->preRequest($request, $session, $model);
$res = $filter->preRequest($request);
if ($res === false) {
return false;
}
@ -45,10 +44,10 @@ class RequestProcessor implements RequestFilter
return null;
}
public function postRequest(HTTPRequest $request, HTTPResponse $response, DataModel $model)
public function postRequest(HTTPRequest $request, HTTPResponse $response)
{
foreach ($this->filters as $filter) {
$res = $filter->postRequest($request, $response, $model);
$res = $filter->postRequest($request, $response);
if ($res === false) {
return false;
}

View File

@ -2,10 +2,8 @@
namespace SilverStripe\Control;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\Deprecation;
use BadMethodCallException;
use SilverStripe\Core\Config\Configurable;
/**
* Handles all manipulation of the session.
@ -89,7 +87,7 @@ use SilverStripe\Dev\Deprecation;
*/
class Session
{
use Injectable;
use Configurable;
/**
* Set session timeout in seconds.
@ -130,12 +128,23 @@ class Session
private static $cookie_secure = false;
/**
* Session data
* Session data.
* Will be null if session has not been started
*
* @var array|null
*/
protected $data = array();
protected $data = null;
/**
* @var array
*/
protected $changedData = array();
/**
* Get user agent for this request
*
* @return string
*/
protected function userAgent()
{
if (isset($_SERVER['HTTP_USER_AGENT'])) {
@ -148,123 +157,75 @@ class Session
/**
* Start PHP session, then create a new Session object with the given start data.
*
* @param $data array|Session Can be an array of data (such as $_SESSION) or another Session object to clone.
* @param array|null|Session $data Can be an array of data (such as $_SESSION) or another Session object to clone.
* If null, this session is treated as unstarted.
*/
public function __construct($data)
{
if ($data instanceof Session) {
$data = $data->inst_getAll();
$data = $data->getAll();
}
$this->data = $data;
}
/**
* Init this session instance before usage
*/
public function init()
{
if (!$this->isStarted()) {
$this->start();
}
// Funny business detected!
if (isset($this->data['HTTP_USER_AGENT'])) {
if ($this->data['HTTP_USER_AGENT'] != $this->userAgent()) {
// Funny business detected!
$this->inst_clearAll();
$this->inst_destroy();
$this->inst_start();
if ($this->data['HTTP_USER_AGENT'] !== $this->userAgent()) {
$this->clearAll();
$this->destroy();
$this->start();
}
}
}
/**
* Add a value to a specific key in the session array
*
* @param string $name
* @param mixed $val
* Destroy existing session and restart
*/
public static function add_to_array($name, $val)
public function restart()
{
return self::current_session()->inst_addToArray($name, $val);
$this->destroy();
$this->init();
}
/**
* Set a key/value pair in the session
* Determine if this session has started
*
* @param string $name Key
* @param string|array $val Value
* @return bool
*/
public static function set($name, $val)
public function isStarted()
{
return self::current_session()->inst_set($name, $val);
return isset($this->data);
}
/**
* Return a specific value by session key
* Begin session
*
* @param string $name Key to lookup
* @return mixed
* @param string $sid
*/
public static function get($name)
public function start($sid = null)
{
return self::current_session()->inst_get($name);
}
/**
* Return all the values in session
*
* @return array
*/
public static function get_all()
{
return self::current_session()->inst_getAll();
}
/**
* Clear a given session key, value pair.
*
* @param string $name Key to lookup
*/
public static function clear($name)
{
return self::current_session()->inst_clear($name);
}
/**
* Clear all the values
*
* @return void
*/
public static function clear_all()
{
self::current_session()->inst_clearAll();
self::$default_session = null;
}
/**
* Save all the values in our session to $_SESSION
*/
public static function save()
{
return self::current_session()->inst_save();
}
protected static $default_session = null;
protected static function current_session()
{
if (Controller::has_curr()) {
return Controller::curr()->getSession();
} else {
if (!self::$default_session) {
self::$default_session = Injector::inst()->create('SilverStripe\\Control\\Session', isset($_SESSION) ? $_SESSION : array());
}
return self::$default_session;
if ($this->isStarted()) {
throw new BadMethodCallException("Session has already started");
}
}
public function inst_start($sid = null)
{
$path = Config::inst()->get('SilverStripe\\Control\\Session', 'cookie_path');
$path = $this->config()->get('cookie_path');
if (!$path) {
$path = Director::baseURL();
}
$domain = Config::inst()->get('SilverStripe\\Control\\Session', 'cookie_domain');
$secure = Director::is_https() && Config::inst()->get('SilverStripe\\Control\\Session', 'cookie_secure');
$session_path = Config::inst()->get('SilverStripe\\Control\\Session', 'session_store_path');
$timeout = Config::inst()->get('SilverStripe\\Control\\Session', 'timeout');
$domain = $this->config()->get('cookie_domain');
$secure = Director::is_https() && $this->config()->get('cookie_secure');
$session_path = $this->config()->get('session_store_path');
$timeout = $this->config()->get('timeout');
// Director::baseURL can return absolute domain names - this extracts the relevant parts
// for the session otherwise we can get broken session cookies
@ -300,6 +261,8 @@ class Session
session_start();
$this->data = isset($_SESSION) ? $_SESSION : array();
} else {
$this->data = [];
}
// Modify the timeout behaviour so it's the *inactive* time before the session expires.
@ -310,28 +273,41 @@ class Session
}
}
public function inst_destroy($removeCookie = true)
/**
* Destroy this session
*
* @param bool $removeCookie
*/
public function destroy($removeCookie = true)
{
if (session_id()) {
if ($removeCookie) {
$path = Config::inst()->get('SilverStripe\\Control\\Session', 'cookie_path') ?: Director::baseURL();
$domain = Config::inst()->get('SilverStripe\\Control\\Session', 'cookie_domain');
$secure = Config::inst()->get('SilverStripe\\Control\\Session', 'cookie_secure');
$path = $this->config()->get('cookie_path') ?: Director::baseURL();
$domain = $this->config()->get('cookie_domain');
$secure = $this->config()->get('cookie_secure');
Cookie::force_expiry(session_name(), $path, $domain, $secure, true);
}
session_destroy();
// Clean up the superglobal - session_destroy does not do it.
// http://nz1.php.net/manual/en/function.session-destroy.php
unset($_SESSION);
$this->data = array();
}
// Clean up the superglobal - session_destroy does not do it.
// http://nz1.php.net/manual/en/function.session-destroy.php
unset($_SESSION);
$this->data = null;
}
public function inst_set($name, $val)
/**
* Set session value
*
* @param string $name
* @param mixed $val
* @return $this
*/
public function set($name, $val)
{
if (!$this->isStarted()) {
throw new BadMethodCallException("Session cannot be modified until it's started");
}
// Quicker execution path for "."-free names
if (strpos($name, '.') === false) {
$this->data[$name] = $val;
@ -360,10 +336,21 @@ class Session
$diffVar = $val;
}
}
return $this;
}
public function inst_addToArray($name, $val)
/**
* Merge value with array
*
* @param string $name
* @param mixed $val
*/
public function addToArray($name, $val)
{
if (!$this->isStarted()) {
throw new BadMethodCallException("Session cannot be modified until it's started");
}
$names = explode('.', $name);
// We still want to do this even if we have strict path checking for legacy code
@ -379,8 +366,18 @@ class Session
$diffVar[sizeof($var)-1] = $val;
}
public function inst_get($name)
/**
* Get session value
*
* @param string $name
* @return mixed
*/
public function get($name)
{
if (!$this->isStarted()) {
throw new BadMethodCallException("Session cannot be accessed until it's started");
}
// Quicker execution path for "."-free names
if (strpos($name, '.') === false) {
if (isset($this->data[$name])) {
@ -407,8 +404,18 @@ class Session
}
}
public function inst_clear($name)
/**
* Clear session value
*
* @param string $name
* @return $this
*/
public function clear($name)
{
if (!$this->isStarted()) {
throw new BadMethodCallException("Session cannot be modified until it's started");
}
$names = explode('.', $name);
// We still want to do this even if we have strict path checking for legacy code
@ -418,7 +425,7 @@ class Session
foreach ($names as $n) {
// don't clear a record that doesn't exist
if (!isset($var[$n])) {
return;
return $this;
}
$var = &$var[$n];
}
@ -432,38 +439,54 @@ class Session
$var = null;
$diffVar = null;
}
return $this;
}
public function inst_clearAll()
/**
* Clear all values
*/
public function clearAll()
{
if (!$this->isStarted()) {
throw new BadMethodCallException("Session cannot be modified until it's started");
}
if ($this->data && is_array($this->data)) {
foreach (array_keys($this->data) as $key) {
$this->inst_clear($key);
$this->clear($key);
}
}
}
public function inst_getAll()
/**
* Get all values
*
* @return array|null
*/
public function getAll()
{
return $this->data;
}
public function inst_finalize()
/**
* Set user agent key
*/
public function finalize()
{
$this->inst_set('HTTP_USER_AGENT', $this->userAgent());
$this->set('HTTP_USER_AGENT', $this->userAgent());
}
/**
* Save data to session
* Only save the changes, so that anyone manipulating $_SESSION directly doesn't get burned.
*/
public function inst_save()
public function save()
{
if ($this->changedData) {
$this->inst_finalize();
$this->finalize();
if (!isset($_SESSION)) {
$this->inst_start();
if (!$this->isStarted()) {
$this->start();
}
$this->recursivelyApply($this->changedData, $_SESSION);
@ -493,55 +516,11 @@ class Session
/**
* Return the changed data, for debugging purposes.
*
* @return array
*/
public function inst_changedData()
public function changedData()
{
return $this->changedData;
}
/**
* Sets the appropriate form message in session, with type. This will be shown once,
* for the form specified.
*
* @param string $formname the form name you wish to use ( usually $form->FormName() )
* @param string $message the message you wish to add to it
* @param string $type the type of message
*/
public static function setFormMessage($formname, $message, $type)
{
Session::set("FormInfo.$formname.formError.message", $message);
Session::set("FormInfo.$formname.formError.type", $type);
}
/**
* Is there a session ID in the request?
* @return bool
*/
public static function request_contains_session_id()
{
$secure = Director::is_https() && Config::inst()->get('SilverStripe\\Control\\Session', 'cookie_secure');
$name = $secure ? 'SECSESSID' : session_name();
return isset($_COOKIE[$name]) || isset($_REQUEST[$name]);
}
/**
* Initialize session.
*
* @param string $sid Start the session with a specific ID
*/
public static function start($sid = null)
{
self::current_session()->inst_start($sid);
}
/**
* Destroy the active session.
*
* @param bool $removeCookie If set to TRUE, removes the user's cookie, FALSE does not remove
*/
public static function destroy($removeCookie = true)
{
self::current_session()->inst_destroy($removeCookie);
}
}

413
src/Core/AppKernel.php Normal file
View File

@ -0,0 +1,413 @@
<?php
namespace SilverStripe\Core;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Psr\Log\LoggerInterface;
use SilverStripe\Config\Collections\CachedConfigCollection;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Core\Cache\ManifestCacheFactory;
use SilverStripe\Core\Config\ConfigLoader;
use SilverStripe\Core\Config\CoreConfigFactory;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Injector\SilverStripeServiceConfigurationLocator;
use SilverStripe\Core\Manifest\ClassLoader;
use SilverStripe\Core\Manifest\ClassManifest;
use SilverStripe\Core\Manifest\ModuleLoader;
use SilverStripe\Core\Manifest\ModuleManifest;
use SilverStripe\Dev\DebugView;
use SilverStripe\Dev\Install\DatabaseAdapterRegistry;
use SilverStripe\Logging\ErrorHandler;
use SilverStripe\ORM\DB;
use SilverStripe\View\ThemeManifest;
use SilverStripe\View\ThemeResourceLoader;
class AppKernel extends CoreKernel
{
public function __construct()
{
// Initialise the dependency injector as soon as possible, as it is
// subsequently used by some of the following code
$injector = new Injector(array('locator' => SilverStripeServiceConfigurationLocator::class));
$this->setContainer($injector);
Injector::set_inst($injector);
// Manifest cache factory
$manifestCacheFactory = $this->buildManifestCacheFactory();
// Class loader
$classLoader = ClassLoader::inst();
$classLoader->pushManifest(new ClassManifest(BASE_PATH, $manifestCacheFactory));
$this->setClassLoader($classLoader);
// Module loader
$moduleLoader = ModuleLoader::inst();
$moduleManifest = new ModuleManifest(BASE_PATH, $manifestCacheFactory);
$moduleLoader->pushManifest($moduleManifest);
$this->setModuleLoader($moduleLoader);
// Config loader
// @todo refactor CoreConfigFactory
$configFactory = new CoreConfigFactory($manifestCacheFactory, $this->getEnvironment());
$configManifest = $configFactory->createRoot();
$configLoader = ConfigLoader::inst();
$configLoader->pushManifest($configManifest);
$this->setConfigLoader($configLoader);
// Load template manifest
$themeResourceLoader = ThemeResourceLoader::inst();
$themeResourceLoader->addSet('$default', new ThemeManifest(
BASE_PATH,
project(),
$manifestCacheFactory
));
$this->setThemeResourceLoader($themeResourceLoader);
}
public function getEnvironment()
{
// Check saved session
$env = $this->sessionEnvironment();
if ($env) {
return $env;
}
// Check set
if ($this->enviroment) {
return $this->enviroment;
}
// Check getenv
if ($env = getenv('SS_ENVIRONMENT_TYPE')) {
return $env;
}
return self::LIVE;
}
/**
* Check or update any temporary environment specified in the session.
*
* @return null|string
*/
protected function sessionEnvironment()
{
// Check isDev in querystring
if (isset($_GET['isDev'])) {
if (isset($_SESSION)) {
unset($_SESSION['isTest']); // In case we are changing from test mode
$_SESSION['isDev'] = $_GET['isDev'];
}
return self::DEV;
}
// Check isTest in querystring
if (isset($_GET['isTest'])) {
if (isset($_SESSION)) {
unset($_SESSION['isDev']); // In case we are changing from dev mode
$_SESSION['isTest'] = $_GET['isTest'];
}
return self::TEST;
}
// Check session
if (!empty($_SESSION['isDev'])) {
return self::DEV;
}
if (!empty($_SESSION['isTest'])) {
return self::TEST;
}
// no session environment
return null;
}
/**
* @throws HTTPResponse_Exception
*/
public function boot()
{
$this->bootPHP();
$this->bootManifests();
$this->bootErrorHandling();
$this->bootDatabase();
}
/**
* Configure database
*
* @throws HTTPResponse_Exception
*/
protected function bootDatabase()
{
// Check if a DB is named
$name = $this->getDatabaseName();
// Gracefully fail if no DB is configured
if (empty($name)) {
$this->detectLegacyEnvironment();
$this->redirectToInstaller();
}
// Set default database config
$databaseConfig = $this->getDatabaseConfig();
$databaseConfig['database'] = $this->getDatabaseName();
DB::setConfig($databaseConfig);
}
/**
* Check if there's a legacy _ss_environment.php file
*
* @throws HTTPResponse_Exception
*/
protected function detectLegacyEnvironment()
{
// Is there an _ss_environment.php file?
if (!file_exists(BASE_PATH . '/_ss_environment.php') &&
!file_exists(dirname(BASE_PATH) . '/_ss_environment.php')
) {
return;
}
// Build error response
$dv = new DebugView();
$body =
$dv->renderHeader() .
$dv->renderInfo(
"Configuraton Error",
Director::absoluteBaseURL()
) .
$dv->renderParagraph(
'You need to replace your _ss_environment.php file with a .env file, or with environment variables.<br><br>'
. 'See the <a href="https://docs.silverstripe.org/en/4/getting_started/environment_management/">'
. 'Environment Management</a> docs for more information.'
) .
$dv->renderFooter();
// Raise error
$response = new HTTPResponse($body, 500);
throw new HTTPResponse_Exception($response);
}
/**
* If missing configuration, redirect to install.php
*/
protected function redirectToInstaller()
{
// Error if installer not available
if (!file_exists(BASE_PATH . '/install.php')) {
throw new HTTPResponse_Exception(
'SilverStripe Framework requires a $databaseConfig defined.',
500
);
}
// Redirect to installer
$response = new HTTPResponse();
$response->redirect(Director::absoluteURL('install.php'));
throw new HTTPResponse_Exception($response);
}
/**
* Load database config from environment
*
* @return array
*/
protected function getDatabaseConfig()
{
// Check global config
global $databaseConfig;
if (!empty($databaseConfig)) {
return $databaseConfig;
}
/** @skipUpgrade */
$databaseConfig = [
"type" => getenv('SS_DATABASE_CLASS') ?: 'MySQLDatabase',
"server" => getenv('SS_DATABASE_SERVER') ?: 'localhost',
"username" => getenv('SS_DATABASE_USERNAME') ?: null,
"password" => getenv('SS_DATABASE_PASSWORD') ?: null,
];
// Set the port if called for
$dbPort = getenv('SS_DATABASE_PORT');
if ($dbPort) {
$databaseConfig['port'] = $dbPort;
}
// Set the timezone if called for
$dbTZ = getenv('SS_DATABASE_TIMEZONE');
if ($dbTZ) {
$databaseConfig['timezone'] = $dbTZ;
}
// For schema enabled drivers:
$dbSchema = getenv('SS_DATABASE_SCHEMA');
if ($dbSchema) {
$databaseConfig["schema"] = $dbSchema;
}
// For SQlite3 memory databases (mainly for testing purposes)
$dbMemory = getenv('SS_DATABASE_MEMORY');
if ($dbMemory) {
$databaseConfig["memory"] = $dbMemory;
}
// Allow database adapters to handle their own configuration
DatabaseAdapterRegistry::autoconfigure();
return $databaseConfig;
}
/**
* Get name of database
*
* @return string
*/
protected function getDatabaseName()
{
$prefix = getenv('SS_DATABASE_PREFIX') ?: 'SS_';
// Check globals
global $database;
if (!empty($database)) {
return $prefix.$database;
}
global $databaseConfig;
if (!empty($databaseConfig['database'])) {
return $databaseConfig['database']; // Note: Already includes prefix
}
// Check environment
$database = getenv('SS_DATABASE_NAME');
if ($database) {
return $prefix.$database;
}
// Auto-detect name
$chooseName = getenv('SS_DATABASE_CHOOSE_NAME');
if ($chooseName) {
// Find directory to build name from
$loopCount = (int)$chooseName;
$databaseDir = BASE_PATH;
for ($i = 0; $i < $loopCount-1; $i++) {
$databaseDir = dirname($databaseDir);
}
// Build name
$database = str_replace('.', '', basename($databaseDir));
return $prefix.$database;
}
// no DB name (may be optional for some connectors)
return null;
}
/**
* Initialise PHP with default variables
*/
protected function bootPHP()
{
if ($this->getEnvironment() === self::LIVE) {
// limited to fatal errors and warnings in live mode
error_reporting(E_ALL & ~(E_DEPRECATED | E_STRICT | E_NOTICE));
} else {
// Report all errors in dev / test mode
error_reporting(E_ALL | E_STRICT);
}
global $_increase_time_limit_max;
$_increase_time_limit_max = -1;
/**
* Ensure we have enough memory
*/
increase_memory_limit_to('64M');
/**
* Ensure we don't run into xdebug's fairly conservative infinite recursion protection limit
*/
increase_xdebug_nesting_level_to(200);
/**
* Set default encoding
*/
mb_http_output('UTF-8');
mb_internal_encoding('UTF-8');
mb_regex_encoding('UTF-8');
/**
* Enable better garbage collection
*/
gc_enable();
}
/**
* @return ManifestCacheFactory
*/
protected function buildManifestCacheFactory()
{
return new ManifestCacheFactory([
'namespace' => 'manifestcache',
'directory' => getTempFolder(),
]);
}
/**
* Boot all manifests
*/
protected function bootManifests()
{
// Regenerate the manifest if ?flush is set, or if the database is being built.
// The coupling is a hack, but it removes an annoying bug where new classes
// referenced in _config.php files can be referenced during the build process.
$flush = isset($_GET['flush']) ||
trim($_GET['url'], '/') === trim(BASE_URL . '/dev/build', '/');
// Setup autoloader
$this->getClassLoader()->init(false, $flush);
// Find modules
$this->getModuleLoader()->init(false, $flush);
// Flush config
if ($flush) {
$config = $this->getConfigLoader()->getManifest();
if ($config instanceof CachedConfigCollection) {
$config->setFlush($flush);
}
}
// After loading config, boot _config.php files
$this->getModuleLoader()->getManifest()->activateConfig();
// Find default templates
$defaultSet = $this->getThemeResourceLoader()->getSet('$default');
if ($defaultSet instanceof ThemeManifest) {
$defaultSet->init(false, $flush);
}
}
/**
* Turn on error handling
*/
protected function bootErrorHandling()
{
// Register error handler
$errorHandler = Injector::inst()->get(ErrorHandler::class);
$errorHandler->start();
// Register error log file
$errorLog = getenv('SS_ERROR_LOG');
if ($errorLog) {
$logger = Injector::inst()->get(LoggerInterface::class);
if ($logger instanceof Logger) {
$logger->pushHandler(new StreamHandler(BASE_PATH . '/' . $errorLog, Logger::WARNING));
} else {
user_error("SS_ERROR_LOG setting only works with Monolog, you are using another logger", E_USER_WARNING);
}
}
}
}

23
src/Core/Application.php Normal file
View File

@ -0,0 +1,23 @@
<?php
namespace SilverStripe\Core;
/**
* Identifies a class as a root silverstripe application
*/
interface Application
{
/**
* Get the kernel for this application
*
* @return Kernel
*/
public function getKernel();
/**
* Invoke the application control chain
*
* @param callable $callback
*/
public function execute(callable $callback);
}

View File

@ -2,6 +2,7 @@
namespace SilverStripe\Core\Config;
use BadMethodCallException;
use SilverStripe\Config\Collections\ConfigCollectionInterface;
/**
@ -36,6 +37,9 @@ class ConfigLoader
*/
public function getManifest()
{
if (empty($this->manifests)) {
throw new BadMethodCallException("No config manifests available");
}
return $this->manifests[count($this->manifests) - 1];
}

View File

@ -2,22 +2,16 @@
namespace SilverStripe\Core\Config;
use Monolog\Handler\ErrorLogHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Psr\Log\LoggerInterface;
use Psr\SimpleCache\CacheInterface;
use SilverStripe\Config\Collections\CachedConfigCollection;
use SilverStripe\Config\Collections\MemoryConfigCollection;
use SilverStripe\Config\Transformer\PrivateStaticTransformer;
use SilverStripe\Config\Transformer\YamlTransformer;
use SilverStripe\Control\Director;
use SilverStripe\Core\Cache\CacheFactory;
use SilverStripe\Core\Config\Middleware\ExtensionMiddleware;
use SilverStripe\Core\Config\Middleware\InheritanceMiddleware;
use SilverStripe\Core\Manifest\ClassLoader;
use SilverStripe\Core\Manifest\ModuleLoader;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Finder\Finder;
/**
@ -26,19 +20,19 @@ use Symfony\Component\Finder\Finder;
class CoreConfigFactory
{
/**
* @var static
* @var CacheFactory
*/
protected static $inst = null;
protected $cacheFactory = null;
/**
* @return static
* @var string
*/
public static function inst()
protected $environment = null;
public function __construct(CacheFactory $cacheFactory, $environment)
{
if (!self::$inst) {
self::$inst = new static();
}
return self::$inst;
$this->cacheFactory = $cacheFactory;
$this->environment = $environment;
}
/**
@ -46,20 +40,17 @@ class CoreConfigFactory
* This will be an immutable cached config,
* which conditionally generates a nested "core" config.
*
* @param bool $flush
* @param CacheFactory $cacheFactory
* @return CachedConfigCollection
*/
public function createRoot($flush, CacheFactory $cacheFactory)
public function createRoot()
{
$instance = new CachedConfigCollection();
// Create config cache
$cache = $cacheFactory->create(CacheInterface::class.'.configcache', [
$cache = $this->cacheFactory->create(CacheInterface::class.'.configcache', [
'namespace' => 'configcache'
]);
$instance->setCache($cache);
$instance->setFlush($flush);
// Set collection creator
$instance->setCollectionCreator(function () {
@ -171,8 +162,7 @@ class CoreConfigFactory
}
)
->addRule('environment', function ($env) {
$current = Director::get_environment_type();
return strtolower($current) === strtolower($env);
return strtolower($this->environment) === strtolower($env);
})
->addRule('moduleexists', function ($module) {
return ModuleLoader::inst()->getManifest()->moduleExists($module);

131
src/Core/CoreKernel.php Normal file
View File

@ -0,0 +1,131 @@
<?php
namespace SilverStripe\Core;
use InvalidArgumentException;
use SilverStripe\Core\Config\ConfigLoader;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Manifest\ClassLoader;
use SilverStripe\Core\Manifest\ModuleLoader;
use SilverStripe\View\ThemeResourceLoader;
/**
* Simple Kernel container
*/
class CoreKernel implements Kernel
{
/**
* @var Injector
*/
protected $container = null;
/**
* @var string
*/
protected $enviroment = null;
/**
* @var ClassLoader
*/
protected $classLoader = null;
/**
* @var ModuleLoader
*/
protected $moduleLoader = null;
/**
* @var ConfigLoader
*/
protected $configLoader = null;
/**
* @var ThemeResourceLoader
*/
protected $themeResourceLoader = null;
public function boot()
{
}
public function shutdown()
{
}
public function nest()
{
// TODO: Implement nest() method.
}
public function getContainer()
{
return $this->container;
}
public function setContainer(Injector $container)
{
$this->container = $container;
$container->registerService($this, Kernel::class);
return $this;
}
public function getClassLoader()
{
return $this->classLoader;
}
public function setClassLoader(ClassLoader $classLoader)
{
$this->classLoader = $classLoader;
return $this;
}
public function getModuleLoader()
{
return $this->moduleLoader;
}
public function setModuleLoader(ModuleLoader $moduleLoader)
{
$this->moduleLoader = $moduleLoader;
return $this;
}
public function getEnvironment()
{
return $this->enviroment ?: self::LIVE;
}
public function setEnvironment($environment)
{
if (!in_array($environment, [self::DEV, self::TEST, self::LIVE])) {
throw new InvalidArgumentException(
"Director::set_environment_type passed '$environment'. It should be passed dev, test, or live"
);
}
$this->enviroment = $environment;
return $this;
}
public function getConfigLoader()
{
return $this->configLoader;
}
public function setConfigLoader($configLoader)
{
$this->configLoader = $configLoader;
return $this;
}
public function getThemeResourceLoader()
{
return $this->themeResourceLoader;
}
public function setThemeResourceLoader($themeResourceLoader)
{
$this->themeResourceLoader = $themeResourceLoader;
return $this;
}
}

View File

@ -0,0 +1,115 @@
<?php
namespace SilverStripe\Core;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
/**
* Invokes the HTTP application within an ErrorControlChain
*/
class HTTPApplication implements Application
{
/**
* @var callable[]
*/
protected $middlewares = [];
/**
* @return callable[]
*/
public function getMiddlewares()
{
return $this->middlewares;
}
/**
* @param callable[] $middlewares
* @return $this
*/
public function setMiddlewares($middlewares)
{
$this->middlewares = $middlewares;
return $this;
}
/**
* @param callable $middleware
* @return $this
*/
public function addMiddleware($middleware)
{
$this->middlewares[] = $middleware;
return $this;
}
/**
* Call middleware
*
* @param callable $last Last config to call
* @return HTTPResponse
*/
protected function callMiddleware($last)
{
// Reverse middlewares
$next = $last;
foreach (array_reverse($this->getMiddlewares()) as $middleware) {
$next = function () use ($middleware, $next) {
return call_user_func($middleware, $next);
};
}
return call_user_func($next);
}
/**
* @var Kernel
*/
protected $kernel;
public function __construct(Kernel $kernel)
{
$this->kernel = $kernel;
}
/**
* Get the kernel for this application
*
* @return Kernel
*/
public function getKernel()
{
return $this->kernel;
}
/**
* Handle the given HTTP request
*
* @param HTTPRequest $request
* @return HTTPResponse
*/
public function handle(HTTPRequest $request)
{
// Ensure boot is invoked
return $this->execute(function () use ($request) {
// Start session and execute
$request->getSession()->init();
return Director::direct($request);
});
}
/**
* Safely boot the application and execute the given main action
*
* @param callable $callback
* @return HTTPResponse
*/
public function execute(callable $callback)
{
return $this->callMiddleware(function () use ($callback) {
// Pre-request boot
$this->getKernel()->boot();
return call_user_func($callback);
});
}
}

View File

@ -202,21 +202,18 @@ class Injector implements ContainerInterface
{
$this->injectMap = array();
$this->serviceCache = array(
'Injector' => $this,
'Injector' => $this,
);
$this->specs = array(
'Injector' => array('class' => 'SilverStripe\\Core\\Injector\\Injector')
);
$this->specs = [
'Injector' => ['class' => static::class]
];
$this->autoProperties = array();
$creatorClass = isset($config['creator'])
? $config['creator']
: 'SilverStripe\\Core\\Injector\\InjectionCreator';
: InjectionCreator::class;
$locatorClass = isset($config['locator'])
? $config['locator']
: 'SilverStripe\\Core\\Injector\\SilverStripeServiceConfigurationLocator';
: SilverStripeServiceConfigurationLocator::class;
$this->objectCreator = new $creatorClass;
$this->configLocator = new $locatorClass;
@ -860,7 +857,6 @@ class Injector implements ContainerInterface
$this->specs[$registerAt] = array('class' => get_class($service));
$this->serviceCache[$registerAt] = $service;
$this->inject($service);
}
/**

View File

@ -51,7 +51,7 @@ class SilverStripeServiceConfigurationLocator implements ServiceConfigurationLoc
return $this->configs[$name];
}
$config = Config::inst()->get('SilverStripe\\Core\\Injector\\Injector', $name);
$config = Config::inst()->get(Injector::class, $name);
$this->configs[$name] = $config;
return $config;
}

120
src/Core/Kernel.php Normal file
View File

@ -0,0 +1,120 @@
<?php
namespace SilverStripe\Core;
use SilverStripe\Core\Config\ConfigLoader;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Manifest\ClassLoader;
use SilverStripe\Core\Manifest\ModuleLoader;
use SilverStripe\View\ThemeResourceLoader;
/**
* Represents the core state of a SilverStripe application
* Based loosely on symfony/http-kernel's KernelInterface component
*/
interface Kernel
{
/**
* Test environment
*/
const TEST = 'test';
/**
* Dev environment
*/
const DEV = 'dev';
/**
* Live (default) environment
*/
const LIVE = 'live';
/*
* Boots the current kernel
*/
public function boot();
/**
* Shutdowns the kernel.
*/
public function shutdown();
/**
* Nests this kernel, all components, and returns the nested value.
*
* @return static
*/
public function nest();
/**
* @return Injector
*/
public function getContainer();
/**
* Sets injector
*
* @param Injector $container
* @return $this
*/
public function setContainer(Injector $container);
/**
* @return ClassLoader
*/
public function getClassLoader();
/**
* @param ClassLoader $classLoader
* @return $this
*/
public function setClassLoader(ClassLoader $classLoader);
/**
* @return ModuleLoader
*/
public function getModuleLoader();
/**
* @param ModuleLoader $moduleLoader
* @return $this
*/
public function setModuleLoader(ModuleLoader $moduleLoader);
/**
* @return ConfigLoader
*/
public function getConfigLoader();
/**
* @param ConfigLoader $configLoader
* @return $this
*/
public function setConfigLoader($configLoader);
/**
* @return ThemeResourceLoader
*/
public function getThemeResourceLoader();
/**
* @param ThemeResourceLoader $themeResourceLoader
* @return $this
*/
public function setThemeResourceLoader($themeResourceLoader);
/**
* One of dev, live, or test
*
* @return string
*/
public function getEnvironment();
/**
* Sets new environment
*
* @param string $environment
* @return $this
*/
public function setEnvironment($environment);
}

View File

@ -19,7 +19,9 @@ class ClassLoader
private static $instance;
/**
* @var array Map of 'instance' (ClassManifest) and other options.
* Map of 'instance' (ClassManifest) and other options.
*
* @var array
*/
protected $manifests = array();
@ -113,6 +115,23 @@ class ClassLoader
return false;
}
/**
* Initialise the class loader
*
* @param bool $includeTests
* @param bool $forceRegen
*/
public function init($includeTests = false, $forceRegen = false)
{
foreach ($this->manifests as $manifest) {
/** @var ClassManifest $instance */
$instance = $manifest['instance'];
$instance->init($includeTests, $forceRegen);
}
$this->registerAutoloader();
}
/**
* Returns true if a class or interface name exists in the manifest.
*

View File

@ -28,6 +28,13 @@ class ClassManifest
*/
protected $base;
/**
* Used to build cache during boot
*
* @var CacheFactory
*/
protected $cacheFactory;
/**
* Set if including test classes
*
@ -56,7 +63,7 @@ class ClassManifest
*
* @var array
*/
protected $classes = array();
protected $classes = array();
/**
* List of root classes with no parent class
@ -122,27 +129,30 @@ class ClassManifest
* from the cache or re-scanning for classes.
*
* @param string $base The manifest base path.
* @param bool $includeTests Include the contents of "tests" directories.
* @param bool $forceRegen Force the manifest to be regenerated.
* @param CacheFactory $cacheFactory Optional cache to use. Set to null to not cache.
*/
public function __construct(
$base,
$includeTests = false,
$forceRegen = false,
CacheFactory $cacheFactory = null
) {
public function __construct($base, CacheFactory $cacheFactory = null)
{
$this->base = $base;
$this->tests = $includeTests;
$this->cacheFactory = $cacheFactory;
$this->cacheKey = 'manifest';
}
/**
* Initialise the class manifest
*
* @param bool $includeTests
* @param bool $forceRegen
*/
public function init($includeTests = false, $forceRegen = false)
{
// build cache from factory
if ($cacheFactory) {
$this->cache = $cacheFactory->create(
if ($this->cacheFactory) {
$this->cache = $this->cacheFactory->create(
CacheInterface::class.'.classmanifest',
[ 'namespace' => 'classmanifest' . ($includeTests ? '_tests' : '') ]
);
}
$this->cacheKey = 'manifest';
if (!$forceRegen && $this->cache && ($data = $this->cache->get($this->cacheKey))) {
$this->classes = $data['classes'];

View File

@ -85,4 +85,17 @@ class ModuleLoader
{
return count($this->manifests);
}
/**
* Initialise the module loader
*
* @param bool $includeTests
* @param bool $forceRegen
*/
public function init($includeTests = false, $forceRegen = false)
{
foreach ($this->manifests as $manifest) {
$manifest->init($includeTests, $forceRegen);
}
}
}

View File

@ -26,11 +26,11 @@ class ModuleManifest
protected $cacheKey;
/**
* Whether `test` directories should be searched when searching for configuration
* Factory to use to build cache
*
* @var bool
* @var CacheFactory
*/
protected $includeTests;
protected $cacheFactory;
/**
* @var CacheInterface
@ -87,19 +87,24 @@ class ModuleManifest
* from the cache or re-scanning for classes.
*
* @param string $base The project base path.
* @param bool $includeTests
* @param bool $forceRegen Force the manifest to be regenerated.
* @param CacheFactory $cacheFactory Cache factory to use
*/
public function __construct($base, $includeTests = false, $forceRegen = false, CacheFactory $cacheFactory = null)
public function __construct($base, CacheFactory $cacheFactory = null)
{
$this->base = $base;
$this->cacheKey = sha1($base).'_modules';
$this->includeTests = $includeTests;
$this->cacheKey = sha1($base) . '_modules';
$this->cacheFactory = $cacheFactory;
}
/**
* @param bool $includeTests
* @param bool $forceRegen Force the manifest to be regenerated.
*/
public function init($includeTests = false, $forceRegen = false)
{
// build cache from factory
if ($cacheFactory) {
$this->cache = $cacheFactory->create(
if ($this->cacheFactory) {
$this->cache = $this->cacheFactory->create(
CacheInterface::class.'.modulemanifest',
[ 'namespace' => 'modulemanifest' . ($includeTests ? '_tests' : '') ]
);

View File

@ -0,0 +1,146 @@
<?php
namespace SilverStripe\Core\Startup;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Core\Application;
use SilverStripe\Security\Permission;
use SilverStripe\Security\Security;
/**
* Decorates application bootstrapping with errorcontrolchain
*/
class ErrorControlChainMiddleware
{
/**
* @var Application
*/
protected $application = null;
/**
* @var HTTPRequest
*/
protected $request = null;
/**
* Build error control chain for an application
*
* @param Application $application
* @param HTTPRequest $request
*/
public function __construct(Application $application, HTTPRequest $request)
{
$this->application = $application;
$this->request = $request;
}
/**
* @param callable $next
* @return HTTPResponse
*/
public function __invoke(callable $next)
{
$result = null;
// Prepare tokens and execute chain
$reloadToken = ParameterConfirmationToken::prepare_tokens(
['isTest', 'isDev', 'flush'],
$this->getRequest()
);
$chain = new ErrorControlChain();
$chain
->then(function () use ($chain, $reloadToken, $next, &$result) {
// If no redirection is necessary then we can disable error supression
if (!$reloadToken) {
$chain->setSuppression(false);
}
try {
// Check if a token is requesting a redirect
if ($reloadToken) {
$result = $this->safeReloadWithToken($reloadToken);
} else {
// If no reload necessary, process application
$result = call_user_func($next);
}
} catch (HTTPResponse_Exception $exception) {
$result = $exception->getResponse();
}
})
// Finally if a token was requested but there was an error while figuring out if it's allowed, do it anyway
->thenIfErrored(function () use ($reloadToken, &$result) {
if ($reloadToken) {
$result = $reloadToken->reloadWithToken();
}
})
->execute();
return $result;
}
/**
* Reload application with the given token, but only if either the user is authenticated,
* or authentication is impossible.
*
* @param ParameterConfirmationToken $reloadToken
* @return HTTPResponse
*/
protected function safeReloadWithToken($reloadToken)
{
// Safe reload requires manual boot
$this->getApplication()->getKernel()->boot();
// Ensure session is started
$this->getRequest()->getSession()->init();
// 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()->get('login_url'));
$loginPage .= "?BackURL=" . urlencode($this->getRequest()->getURL());
$result = new HTTPResponse();
$result->redirect($loginPage);
return $result;
}
/**
* @return Application
*/
public function getApplication()
{
return $this->application;
}
/**
* @param Application $application
* @return $this
*/
public function setApplication(Application $application)
{
$this->application = $application;
return $this;
}
/**
* @return HTTPRequest
*/
public function getRequest()
{
return $this->request;
}
/**
* @param HTTPRequest $request
* @return $this
*/
public function setRequest(HTTPRequest $request)
{
$this->request = $request;
return $this;
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace SilverStripe\Core\Startup;
use SilverStripe\Control\HTTPResponse;
/**
* Emits response to the browser
*/
class OutputMiddleware
{
protected $defaultResponse = null;
/**
* Construct output middleware with a default response
* (prevent WSOD)
*
* @param string $defaultResponse Provide default text to echo
* if no response could be generated
*/
public function __construct($defaultResponse = null)
{
$this->defaultResponse = $defaultResponse;
}
public function __invoke(callable $next)
{
/** @var HTTPResponse $response */
$response = call_user_func($next);
if ($response) {
$response->output();
} elseif ($this->defaultResponse) {
echo $this->defaultResponse;
}
return $response;
}
}

View File

@ -2,7 +2,10 @@
namespace SilverStripe\Core\Startup;
use SilverStripe\Control\Director;
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Core\Convert;
use SilverStripe\Security\RandomGenerator;
/**
@ -25,6 +28,11 @@ class ParameterConfirmationToken
*/
protected $parameterName = null;
/**
* @var HTTPRequest
*/
protected $request = null;
/**
* The parameter given
*
@ -88,17 +96,19 @@ class ParameterConfirmationToken
* Create a new ParameterConfirmationToken
*
* @param string $parameterName Name of the querystring parameter to check
* @param HTTPRequest $request
*/
public function __construct($parameterName)
public function __construct($parameterName, HTTPRequest $request)
{
// Store the parameter name
$this->parameterName = $parameterName;
$this->request = $request;
// Store the parameter value
$this->parameter = isset($_GET[$parameterName]) ? $_GET[$parameterName] : null;
$this->parameter = $request->getVar($parameterName);
// If the token provided is valid, mark it as such
$token = isset($_GET[$parameterName.'token']) ? $_GET[$parameterName.'token'] : null;
$token = $request->getVar($parameterName.'token');
if ($this->checkToken($token)) {
$this->token = $token;
}
@ -151,7 +161,7 @@ class ParameterConfirmationToken
*/
public function suppress()
{
unset($_GET[$this->parameterName]);
$this->request->offsetUnset($this->parameterName);
}
/**
@ -167,81 +177,45 @@ class ParameterConfirmationToken
);
}
/** What to use instead of BASE_URL. Must not contain protocol or host. @var string */
static public $alternateBaseURL = null;
protected function currentAbsoluteURL()
/**
* Get redirect url, excluding querystring
*
* @return string
*/
protected function currentURL()
{
global $url;
// Are we http or https? Replicates Director::is_https() without its dependencies/
$proto = 'http';
// See https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
// See https://support.microsoft.com/en-us/kb/307347
$headerOverride = false;
if (TRUSTED_PROXY) {
$headers = (getenv('SS_TRUSTED_PROXY_PROTOCOL_HEADER')) ? array(getenv('SS_TRUSTED_PROXY_PROTOCOL_HEADER')) : null;
if (!$headers) {
// Backwards compatible defaults
$headers = array('HTTP_X_FORWARDED_PROTO', 'HTTP_X_FORWARDED_PROTOCOL', 'HTTP_FRONT_END_HTTPS');
}
foreach ($headers as $header) {
$headerCompareVal = ($header === 'HTTP_FRONT_END_HTTPS' ? 'on' : 'https');
if (!empty($_SERVER[$header]) && strtolower($_SERVER[$header]) == $headerCompareVal) {
$headerOverride = true;
break;
}
}
}
if ($headerOverride) {
$proto = 'https';
} elseif ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) {
$proto = 'https';
} elseif (isset($_SERVER['SSL'])) {
$proto = 'https';
}
$parts = array_filter(array(
// What's our host
Director::host(),
// SilverStripe base
self::$alternateBaseURL !== null ? self::$alternateBaseURL : BASE_URL,
// And URL including base script (eg: if it's index.php/page/url/)
(defined('BASE_SCRIPT_URL') ? '/' . BASE_SCRIPT_URL : '') . $url,
));
// Join together with protocol into our current absolute URL, avoiding duplicated "/" characters
return "$proto://" . preg_replace('#/{2,}#', '/', implode('/', $parts));
return Controller::join_links(
BASE_URL,
'/',
$this->request->getURL(false)
);
}
/**
* Forces a reload of the request with the token included
* This method will terminate the script with `die`
*
* @return HTTPResponse
*/
public function reloadWithToken()
{
$location = $this->currentAbsoluteURL();
// Merge get params with current url
$params = array_merge($this->request->getVars(), $this->params());
$location = Controller::join_links(
$this->currentURL(),
'?'.http_build_query($params)
);
$locationJS = Convert::raw2js($location);
$locationATT = Convert::raw2att($location);
$body = <<<HTML
<script>location.href='$locationJS';</script>
<noscript><meta http-equiv="refresh" content="0; url=$locationATT"></noscript>
You are being redirected. If you are not redirected soon, <a href="$locationATT">click here to continue the flush</a>
HTML;
// What's our GET params (ensuring they include the original parameter + a new token)
$params = array_merge($_GET, $this->params());
unset($params['url']);
if ($params) {
$location .= '?'.http_build_query($params);
}
// And redirect
if (headers_sent()) {
echo "
<script>location.href='$location';</script>
<noscript><meta http-equiv='refresh' content='0; url=$location'></noscript>
You are being redirected. If you are not redirected soon, <a href='$location'>click here to continue the flush</a>
";
} else {
header('location: '.$location, true, 302);
}
die;
// Build response
$result = new HTTPResponse($body);
$result->redirect($location);
return $result;
}
/**
@ -249,13 +223,14 @@ You are being redirected. If you are not redirected soon, <a href='$location'>cl
* return the non-validated token with the highest priority
*
* @param array $keys List of token keys in ascending priority (low to high)
* @param HTTPRequest $request
* @return ParameterConfirmationToken The token container for the unvalidated $key given with the highest priority
*/
public static function prepare_tokens($keys)
public static function prepare_tokens($keys, HTTPRequest $request)
{
$target = null;
foreach ($keys as $key) {
$token = new ParameterConfirmationToken($key);
$token = new ParameterConfirmationToken($key, $request);
// Validate this token
if ($token->reloadRequired()) {
$token->suppress();

View File

@ -1,111 +0,0 @@
<?php
/**
* Returns the temporary folder path that silverstripe should use for its cache files.
*
* @param string $base The base path to use for determining the temporary path
* @return string Path to temp
*/
function getTempFolder($base = null)
{
$parent = getTempParentFolder($base);
// The actual temp folder is a subfolder of getTempParentFolder(), named by username
$subfolder = $parent . DIRECTORY_SEPARATOR . getTempFolderUsername();
if (!@file_exists($subfolder)) {
mkdir($subfolder);
}
return $subfolder;
}
/**
* Returns as best a representation of the current username as we can glean.
*
* @return string
*/
function getTempFolderUsername()
{
$user = getenv('APACHE_RUN_USER');
if (!$user) {
$user = getenv('USER');
}
if (!$user) {
$user = getenv('USERNAME');
}
if (!$user && function_exists('posix_getpwuid') && function_exists('posix_getuid')) {
$userDetails = posix_getpwuid(posix_getuid());
$user = $userDetails['name'];
}
if (!$user) {
$user = 'unknown';
}
$user = preg_replace('/[^A-Za-z0-9_\-]/', '', $user);
return $user;
}
/**
* Return the parent folder of the temp folder.
* The temp folder will be a subfolder of this, named by username.
* This structure prevents permission problems.
*
* @param string $base
* @return string
* @throws Exception
*/
function getTempParentFolder($base = null)
{
if (!$base && defined('BASE_PATH')) {
$base = BASE_PATH;
}
// first, try finding a silverstripe-cache dir built off the base path
$tempPath = $base . DIRECTORY_SEPARATOR . 'silverstripe-cache';
if (@file_exists($tempPath)) {
if ((fileperms($tempPath) & 0777) != 0777) {
@chmod($tempPath, 0777);
}
return $tempPath;
}
// failing the above, try finding a namespaced silverstripe-cache dir in the system temp
$tempPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR .
'silverstripe-cache-php' . preg_replace('/[^\w-\.+]+/', '-', PHP_VERSION) .
str_replace(array(' ', '/', ':', '\\'), '-', $base);
if (!@file_exists($tempPath)) {
$oldUMask = umask(0);
@mkdir($tempPath, 0777);
umask($oldUMask);
// if the folder already exists, correct perms
} else {
if ((fileperms($tempPath) & 0777) != 0777) {
@chmod($tempPath, 0777);
}
}
$worked = @file_exists($tempPath) && @is_writable($tempPath);
// failing to use the system path, attempt to create a local silverstripe-cache dir
if (!$worked) {
$tempPath = $base . DIRECTORY_SEPARATOR . 'silverstripe-cache';
if (!@file_exists($tempPath)) {
$oldUMask = umask(0);
@mkdir($tempPath, 0777);
umask($oldUMask);
}
$worked = @file_exists($tempPath) && @is_writable($tempPath);
}
if (!$worked) {
throw new Exception(
'Permission problem gaining access to a temp folder. ' .
'Please create a folder named silverstripe-cache in the base folder ' .
'of the installation and ensure it has the correct permissions'
);
}
return $tempPath;
}

View File

@ -21,7 +21,7 @@ class DevBuildController extends Controller
{
if (Director::is_cli()) {
$da = DatabaseAdmin::create();
return $da->handleRequest($request, $this->model);
return $da->handleRequest($request);
} else {
$renderer = DebugView::create();
echo $renderer->renderHeader();
@ -29,7 +29,7 @@ class DevBuildController extends Controller
echo "<div class=\"build\">";
$da = DatabaseAdmin::create();
$response = $da->handleRequest($request, $this->model);
$response = $da->handleRequest($request);
echo "</div>";
echo $renderer->renderFooter();

View File

@ -2,13 +2,13 @@
namespace SilverStripe\Dev;
use SilverStripe\Assets\File;
use SilverStripe\ORM\DataModel;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\DataObject;
use SilverStripe\Core\Config\Config;
use InvalidArgumentException;
use Exception;
use InvalidArgumentException;
use SilverStripe\Assets\File;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DB;
/**
* A blueprint on how to create instances of a certain {@link DataObject} subclass.
@ -94,7 +94,7 @@ class FixtureBlueprint
try {
$class = $this->class;
$schema = DataObject::getSchema();
$obj = DataModel::inst()->$class->newObject();
$obj = Injector::inst()->create($class);
// If an ID is explicitly passed, then we'll sort out the initial write straight away
// This is just in case field setters triggered by the population code in the next block

View File

@ -1,18 +1,18 @@
<html>
<head>
<title>PHP 5.5.0 is required</title>
<title>PHP 5.6.0 is required</title>
<link rel="stylesheet" type="text/css" href="framework/src/Dev/Install/client/styles/install.css">
</head>
<body>
<div id="BgContainer">
<div id="Container">
<div id="Header">
<h1>PHP 5.5.0 required</h1>
<h1>PHP 5.6.0 required</h1>
<div class="left">
<h3>To run SilverStripe, please install PHP 5.5.0 or greater.</h3>
<h3>To run SilverStripe, please install PHP 5.6.0 or greater.</h3>
<p>We have detected that you are running PHP version <b>$PHPVersion</b>. In order to run SilverStripe,
you must have PHP version 5.5.0 or higher.<p/>
you must have PHP version 5.6.0 or higher.<p/>
<p>If you are running on a shared host, you may need to ask your hosting provider how to do this.</p>
</div>

View File

@ -1,14 +0,0 @@
<?php
// Ensure this class can be autoloaded when installed without dev dependencies.
// It's included by default through composer's autoloading.
// class_exists() triggers PSR-4 autoloaders, which should discover if PHPUnit is installed.
// TODO Factor out SapphireTest references from non-dev core code (avoid autoloading in the first place)
namespace {
if (!class_exists('PHPUnit_Framework_TestCase')) {
class PHPUnit_Framework_TestCase
{
}
}
}

View File

@ -2,25 +2,27 @@
namespace SilverStripe\Dev;
use Exception;
use LogicException;
use PHPUnit_Framework_TestCase;
use SilverStripe\CMS\Controllers\RootURLController;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Cookie;
use SilverStripe\Control\Director;
use SilverStripe\Control\Email\Email;
use SilverStripe\Control\Email\Mailer;
use SilverStripe\Control\Session;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Director;
use SilverStripe\Control\Tests\FakeController;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Config\ConfigLoader;
use SilverStripe\Core\Config\CoreConfigFactory;
use SilverStripe\Core\Config\DefaultConfig;
use SilverStripe\Core\Config\Middleware\ExtensionMiddleware;
use SilverStripe\Core\Extension;
use SilverStripe\Core\Flushable;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Manifest\ClassManifest;
use SilverStripe\Core\Manifest\ClassLoader;
use SilverStripe\Core\Manifest\ClassManifest;
use SilverStripe\Core\Resettable;
use SilverStripe\i18n\i18n;
use SilverStripe\ORM\DataExtension;
@ -28,22 +30,20 @@ use SilverStripe\ORM\SS_List;
use SilverStripe\Security\IdentityStore;
use SilverStripe\Versioned\Versioned;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataModel;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\ORM\DB;
use SilverStripe\Security\Member;
use SilverStripe\Security\Security;
use SilverStripe\ORM\SS_List;
use SilverStripe\Security\Group;
use SilverStripe\Security\Member;
use SilverStripe\Security\Permission;
use SilverStripe\Security\Security;
use SilverStripe\Versioned\Versioned;
use SilverStripe\View\Requirements;
use SilverStripe\View\SSViewer;
use SilverStripe\View\ThemeResourceLoader;
use SilverStripe\View\ThemeManifest;
use PHPUnit_Framework_TestCase;
use SilverStripe\View\ThemeResourceLoader;
use Translatable;
use LogicException;
use Exception;
/**
* Test case class for the Sapphire framework.
@ -274,9 +274,6 @@ class SapphireTest extends PHPUnit_Framework_TestCase
$resettable::reset();
}
if (Controller::has_curr()) {
Controller::curr()->setSession(Session::create(array()));
}
Security::clear_database_is_ready();
// Set up test routes
@ -284,9 +281,6 @@ class SapphireTest extends PHPUnit_Framework_TestCase
$fixtureFiles = $this->getFixturePaths();
// Todo: this could be a special test model
$this->model = DataModel::inst();
// Set up fixture
if ($fixtureFiles || $this->usesDatabase) {
if (!self::using_temp_db()) {

View File

@ -333,11 +333,23 @@ class Form extends ViewableData implements HasRequestHandler
*/
public function clearFormState()
{
Session::clear("FormInfo.{$this->FormName()}.result");
Session::clear("FormInfo.{$this->FormName()}.data");
$this
->getSession()
->clear("FormInfo.{$this->FormName()}.result")
->clear("FormInfo.{$this->FormName()}.data");
return $this;
}
/**
* Get session for this form
*
* @return Session
*/
protected function getSession()
{
return $this->getRequestHandler()->getRequest()->getSession();
}
/**
* Return any form data stored in the session
*
@ -345,7 +357,7 @@ class Form extends ViewableData implements HasRequestHandler
*/
public function getSessionData()
{
return Session::get("FormInfo.{$this->FormName()}.data");
return $this->getSession()->get("FormInfo.{$this->FormName()}.data");
}
/**
@ -356,7 +368,7 @@ class Form extends ViewableData implements HasRequestHandler
*/
public function setSessionData($data)
{
Session::set("FormInfo.{$this->FormName()}.data", $data);
$this->getSession()->set("FormInfo.{$this->FormName()}.data", $data);
return $this;
}
@ -367,7 +379,7 @@ class Form extends ViewableData implements HasRequestHandler
*/
public function getSessionValidationResult()
{
$resultData = Session::get("FormInfo.{$this->FormName()}.result");
$resultData = $this->getSession()->get("FormInfo.{$this->FormName()}.result");
if (isset($resultData)) {
return unserialize($resultData);
}
@ -396,7 +408,7 @@ class Form extends ViewableData implements HasRequestHandler
// Serialise
$resultData = $result ? serialize($result) : null;
Session::set("FormInfo.{$this->FormName()}.result", $resultData);
$this->getSession()->set("FormInfo.{$this->FormName()}.result", $resultData);
return $this;
}

View File

@ -2,23 +2,21 @@
namespace SilverStripe\Forms\GridField;
use InvalidArgumentException;
use LogicException;
use SilverStripe\Control\HasRequestHandler;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Control\RequestHandler;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\FormField;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\ORM\DataModel;
use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\Session;
use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Control\RequestHandler;
use SilverStripe\Forms\FormField;
use SilverStripe\Forms\Form;
use LogicException;
use InvalidArgumentException;
use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\ORM\SS_List;
use SilverStripe\View\HTML;
/**
@ -910,7 +908,7 @@ class GridField extends FormField
foreach ($data as $dataKey => $dataValue) {
if (preg_match('/^action_gridFieldAlterAction\?StateID=(.*)/', $dataKey, $matches)) {
$stateChange = Session::get($matches[1]);
$stateChange = $request->getSession()->get($matches[1]);
$actionName = $stateChange['actionName'];
$arguments = array();
@ -972,11 +970,10 @@ class GridField extends FormField
* @todo copy less code from RequestHandler.
*
* @param HTTPRequest $request
* @param DataModel $model
* @return array|RequestHandler|HTTPResponse|string
* @throws HTTPResponse_Exception
*/
public function handleRequest(HTTPRequest $request, DataModel $model)
public function handleRequest(HTTPRequest $request)
{
if ($this->brokenOnConstruct) {
user_error(
@ -989,7 +986,6 @@ class GridField extends FormField
}
$this->setRequest($request);
$this->setDataModel($model);
$fieldData = $this->getRequest()->requestVar($this->getName());
@ -1038,7 +1034,7 @@ class GridField extends FormField
if ($result instanceof HasRequestHandler) {
$result = $result->getRequestHandler();
}
$returnValue = $result->handleRequest($request, $model);
$returnValue = $result->handleRequest($request);
if (is_array($returnValue)) {
throw new LogicException(
@ -1066,7 +1062,7 @@ class GridField extends FormField
}
}
return parent::handleRequest($request, $model);
return parent::handleRequest($request);
}
/**

View File

@ -2,17 +2,16 @@
namespace SilverStripe\Forms\GridField;
use Closure;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Control\RequestHandler;
use SilverStripe\Core\Extensible;
use SilverStripe\ORM\DataModel;
use SilverStripe\ORM\DataObject;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Extensible;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\Validator;
use SilverStripe\Forms\FieldList;
use Closure;
use SilverStripe\Forms\Validator;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\Filterable;
/**
@ -118,7 +117,7 @@ class GridFieldDetailForm implements GridField_URLHandler
$this->setValidator($record->getCMSValidator());
}
return $handler->handleRequest($request, DataModel::inst());
return $handler->handleRequest($request);
}
/**

View File

@ -2,6 +2,7 @@
namespace SilverStripe\Forms\GridField;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Session;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\FormAction;
@ -89,7 +90,9 @@ class GridField_FormAction extends FormAction
// Ensure $id doesn't contain only numeric characters
$id = 'gf_' . substr(md5(serialize($state)), 0, 8);
Session::set($id, $state);
$session = Controller::curr()->getRequest()->getSession();
$session->set($id, $state);
$actionData['StateID'] = $id;
return array_merge(

View File

@ -2,19 +2,19 @@
namespace SilverStripe\ORM;
use InvalidArgumentException;
use LogicException;
use SilverStripe\Control\Cookie;
use SilverStripe\Control\Director;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Convert;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Control\Director;
use SilverStripe\Control\Cookie;
use SilverStripe\Dev\Deprecation;
use SilverStripe\ORM\Connect\Database;
use SilverStripe\ORM\Connect\DBConnector;
use SilverStripe\ORM\Connect\DBSchemaManager;
use SilverStripe\ORM\Connect\Query;
use SilverStripe\ORM\Queries\SQLExpression;
use SilverStripe\ORM\Connect\Database;
use InvalidArgumentException;
use LogicException;
/**
* Global database interface, complete with static methods.
@ -34,9 +34,19 @@ class DB
/**
* The global database connection.
*
* @var Database
*/
private static $connections = array();
protected static $connections = [];
/**
* List of configurations for each connection
*
* @var array List of configs each in the $databaseConfig format
*/
protected static $configs = [];
/**
* The last SQL query run.
@ -77,6 +87,13 @@ class DB
if (isset(self::$connections[$name])) {
return self::$connections[$name];
}
// lazy connect
$config = static::getConfig($name);
if ($config) {
return static::connect($config, $name);
}
return null;
}
@ -247,7 +264,7 @@ class DB
}
/**
* Connect to a database.
* Specify connection to a database
*
* Given the database configuration, this method will create the correct
* subclass of {@link SS_Database}.
@ -259,14 +276,13 @@ class DB
*/
public static function connect($databaseConfig, $label = 'default')
{
// This is used by the "testsession" module to test up a test session using an alternative name
if ($name = self::get_alternative_database_name()) {
$databaseConfig['database'] = $name;
}
if (!isset($databaseConfig['type']) || empty($databaseConfig['type'])) {
user_error("DB::connect: Not passed a valid database config", E_USER_ERROR);
throw new InvalidArgumentException("DB::connect: Not passed a valid database config");
}
self::$connection_attempted = true;
@ -283,6 +299,30 @@ class DB
return $conn;
}
/**
* Set config for a lazy-connected database
*
* @param array $databaseConfig
* @param string $name
*/
public static function setConfig($databaseConfig, $name = 'default')
{
static::$configs[$name] = $databaseConfig;
}
/**
* Get the named connection config
*
* @param string $name
* @return mixed
*/
public static function getConfig($name)
{
if (isset(static::$configs[$name])) {
return static::$configs[$name];
}
}
/**
* Returns true if a database connection has been attempted.
* In particular, it lets the caller know if we're still so early in the execution pipeline that

View File

@ -49,13 +49,6 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
*/
protected $dataQuery;
/**
* The DataModel from which this DataList comes.
*
* @var DataModel
*/
protected $model;
/**
* Create a new DataList.
* No querying is done on construction, but the initial query schema is set up.
@ -70,16 +63,6 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
parent::__construct();
}
/**
* Set the DataModel
*
* @param DataModel $model
*/
public function setDataModel(DataModel $model)
{
$this->model = $model;
}
/**
* Get the dataClass name for this DataList, ie the DataObject ClassName
*
@ -796,7 +779,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
$class = $row['RecordClassName'];
}
$item = Injector::inst()->create($class, $row, false, $this->model, $this->getQueryParams());
$item = Injector::inst()->create($class, $row, false, $this->getQueryParams());
return $item;
}
@ -1119,7 +1102,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
public function newObject($initialFields = null)
{
$class = $this->dataClass;
return Injector::inst()->create($class, $initialFields, false, $this->model);
return Injector::inst()->create($class, $initialFields, false);
}
/**

View File

@ -1,80 +0,0 @@
<?php
namespace SilverStripe\ORM;
/**
* Representation of a DataModel - a collection of DataLists for each different
* data type.
*
* Usage:
* <code>
* $model = new DataModel;
* $mainMenu = $model->SiteTree->where('"ParentID" = 0 AND "ShowInMenus" = 1');
* </code>
*/
class DataModel
{
/**
* @var DataModel
*/
protected static $inst;
/**
* @var array $customDataLists
*/
protected $customDataLists = array();
/**
* Get the global DataModel.
*
* @return DataModel
*/
public static function inst()
{
if (!self::$inst) {
self::$inst = new self;
}
return self::$inst;
}
/**
* Set the global DataModel, used when data is requested from static
* methods.
*
* @param DataModel $inst
*/
public static function set_inst(DataModel $inst)
{
self::$inst = $inst;
}
/**
* @param string
*
* @return DataList
*/
public function __get($class)
{
if (isset($this->customDataLists[$class])) {
return clone $this->customDataLists[$class];
} else {
$list = DataList::create($class);
$list->setDataModel($this);
return $list;
}
}
/**
* @param string $class
* @param DataList $item
*/
public function __set($class, $item)
{
$item = clone $item;
$item->setDataModel($this);
$this->customDataLists[$class] = $item;
}
}

View File

@ -141,11 +141,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
*/
public $destroyed = false;
/**
* The DataModel from this this object comes
*/
protected $model;
/**
* Data stored in this objects database record. An array indexed by fieldname.
*
@ -289,10 +284,9 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* for populating data on new records.
* @param boolean $isSingleton This this to true if this is a singleton() object, a stub for calling methods.
* Singletons don't have their defaults set.
* @param DataModel $model
* @param array $queryParams List of DataQuery params necessary to lazy load, or load related objects.
*/
public function __construct($record = null, $isSingleton = false, $model = null, $queryParams = array())
public function __construct($record = null, $isSingleton = false, $queryParams = array())
{
parent::__construct();
@ -365,10 +359,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
HTTP::register_modification_date($record['LastEdited']);
}
// this must be called before populateDefaults(), as field getters on a DataObject
// may call getComponent() and others, which rely on $this->model being set.
$this->model = $model ? $model : DataModel::inst();
// Must be called after parent constructor
if (!$isSingleton && (!isset($this->record['ID']) || !$this->record['ID'])) {
$this->populateDefaults();
@ -378,17 +368,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$this->changed = array();
}
/**
* Set the DataModel
* @param DataModel $model
* @return DataObject $this
*/
public function setDataModel(DataModel $model)
{
$this->model = $model;
return $this;
}
/**
* Destroy all of this objects dependant objects and local caches.
* You'll need to call this to get the memory of an object that has components or extensions freed.
@ -415,7 +394,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$map = $this->toMap();
unset($map['Created']);
/** @var static $clone */
$clone = Injector::inst()->create(static::class, $map, false, $this->model);
$clone = Injector::inst()->create(static::class, $map, false, $this->getSourceQueryParams());
$clone->ID = 0;
$clone->invokeWithExtensions('onBeforeDuplicate', $this, $doWrite, $manyMany);
@ -1437,7 +1416,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
// - move the details of the delete code in the DataQuery system
// - update the code to just delete the base table, and rely on cascading deletes in the DB to do the rest
// obviously, that means getting requireTable() to configure cascading deletes ;-)
$srcQuery = DataList::create(static::class, $this->model)
$srcQuery = DataList::create(static::class)
->filter('ID', $this->ID)
->dataQuery()
->query();
@ -1520,7 +1499,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
}
if (empty($component)) {
$component = $this->model->$class->newObject();
$component = Injector::inst()->create($class);
}
} elseif ($class = $schema->belongsToComponent(static::class, $componentName)) {
$joinField = $schema->getRemoteJoinField(static::class, $componentName, 'belongs_to', $polymorphic);
@ -2827,9 +2806,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
. ' arguments');
}
$result = DataList::create(get_called_class());
$result->setDataModel(DataModel::inst());
return $result;
return DataList::create(get_called_class());
}
if ($join) {
@ -2847,7 +2824,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$result = $result->limit($limit);
}
$result->setDataModel(DataModel::inst());
return $result;
}

View File

@ -70,7 +70,7 @@ class ManyManyThroughList extends RelationList
if ($joinRow) {
$joinClass = $this->manipulator->getJoinClass();
$joinQueryParams = $this->manipulator->extractInheritableQueryParameters($this->dataQuery);
$joinRecord = Injector::inst()->create($joinClass, $joinRow, false, $this->model, $joinQueryParams);
$joinRecord = Injector::inst()->create($joinClass, $joinRow, false, $joinQueryParams);
$record->setJoin($joinRecord, $joinAlias);
}

View File

@ -180,9 +180,12 @@ class BasicAuth
*/
public static function protect_entire_site($protect = true, $code = 'ADMIN', $message = null)
{
static::config()->set('entire_site_protected', $protect);
static::config()->set('entire_site_protected_code', $code);
static::config()->set('entire_site_protected_message', $message);
static::config()
->set('entire_site_protected', $protect)
->set('entire_site_protected_code', $code);
if ($message) {
static::config()->set('entire_site_protected_message', $message);
}
}
/**
@ -197,7 +200,6 @@ class BasicAuth
$config = static::config();
$request = Controller::curr()->getRequest();
if ($config->get('entire_site_protected')) {
/** @noinspection ExceptionsAnnotatingAndHandlingInspection */
static::requireLogin(
$request,
$config->get('entire_site_protected_message'),

View File

@ -94,14 +94,14 @@ class CMSSecurity extends Security
$message = parent::getLoginMessage($messageType);
if ($message) {
return $message;
}
}
// Format
return _t(
return _t(
__CLASS__.'.LOGIN_MESSAGE',
'<p>Your session has timed out due to inactivity</p>'
);
}
);
}
/**
* Check if there is a logged in member
@ -187,7 +187,7 @@ PHP
$controller = $this->getResponseController(_t(__CLASS__.'.SUCCESS', 'Success'));
$backURLs = array(
$this->getRequest()->requestVar('BackURL'),
Session::get('BackURL'),
$this->getRequest()->getSession()->get('BackURL'),
Director::absoluteURL(AdminRootController::config()->get('url_base'), true),
);
$backURL = null;
@ -201,7 +201,7 @@ PHP
$controller = $controller->customise(array(
'Content' => DBField::create_field(DBHTMLText::class, _t(
__CLASS__.'.SUCCESSCONTENT',
'<p>Login success. If you are not automatically redirected '.
'<p>Login success. If you are not automatically redirected ' .
'<a target="_top" href="{link}">click here</a></p>',
'Login message displayed in the cms popup once a user has re-authenticated themselves',
array('link' => Convert::raw2att($backURL))

View File

@ -275,14 +275,14 @@ class Member extends DataObject
{
Deprecation::notice('5.0', 'Use DefaultAdminService::findOrCreateDefaultAdmin() instead');
return DefaultAdminService::singleton()->findOrCreateDefaultAdmin();
}
}
/**
* Check if the passed password matches the stored one (if the member is not locked out).
*
* @deprecated 4.0.0...5.0.0 Use Authenticator::checkPassword() instead
*
* @param string $password
* @param string $password
* @return ValidationResult
*/
public function checkPassword($password)
@ -294,12 +294,12 @@ class Member extends DataObject
$authenticators = Security::singleton()->getApplicableAuthenticators(Authenticator::CHECK_PASSWORD);
foreach ($authenticators as $authenticator) {
$authenticator->checkPassword($this, $password, $result);
if (!$result->isValid()) {
if (!$result->isValid()) {
break;
}
}
return $result;
}
}
return $result;
}
/**
* Check if this user is the currently configured default admin

View File

@ -28,7 +28,8 @@ class ChangePasswordForm extends Form
*/
public function __construct($controller, $name, $fields = null, $actions = null)
{
$backURL = $controller->getBackURL() ?: Session::get('BackURL');
$backURL = $controller->getBackURL()
?: $controller->getRequest()->getSession()->get('BackURL');
if (!$fields) {
$fields = $this->getFormFields();

View File

@ -6,7 +6,6 @@ namespace SilverStripe\Security\MemberAuthenticator;
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Control\RequestHandler;
use SilverStripe\Control\Session;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\ORM\FieldType\DBField;
@ -79,7 +78,8 @@ class ChangePasswordHandler extends RequestHandler
return $this->redirect($this->link);
}
if (Session::get('AutoLoginHash')) {
$session = $this->getRequest()->getSession();
if ($session->get('AutoLoginHash')) {
$message = DBField::create_field(
'HTMLFragment',
'<p>' . _t(
@ -157,7 +157,7 @@ class ChangePasswordHandler extends RequestHandler
}
// Store the hash for the change password form. Will be unset after reload within the ChangePasswordForm.
Session::set('AutoLoginHash', $member->encryptWithUserSettings($token));
$this->getRequest()->getSession()->set('AutoLoginHash', $member->encryptWithUserSettings($token));
}
/**
@ -214,14 +214,15 @@ class ChangePasswordHandler extends RequestHandler
return $this->redirectBackToForm();
}
$session = $this->getRequest()->getSession();
if (!$member) {
if (Session::get('AutoLoginHash')) {
$member = Member::member_from_autologinhash(Session::get('AutoLoginHash'));
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');
$session->clear('AutoLoginHash');
return $this->redirect($this->addBackURLParam(Security::singleton()->Link('login')));
}
@ -278,7 +279,7 @@ class ChangePasswordHandler extends RequestHandler
}
// TODO Add confirmation message to login redirect
Session::clear('AutoLoginHash');
$session->clear('AutoLoginHash');
// Redirect to backurl
$backURL = $this->getBackURL();

View File

@ -143,7 +143,7 @@ class MemberLoginForm extends BaseLoginForm
$emailField->setAttribute('autofocus', 'true');
if (Security::config()->get('remember_username')) {
$emailField->setValue(Session::get('SessionForms.MemberLoginForm.Email'));
$emailField->setValue($this->getSession()->get('SessionForms.MemberLoginForm.Email'));
} else {
// Some browsers won't respect this attribute unless it's added to the form
$this->setAttribute('autocomplete', 'off');
@ -195,7 +195,8 @@ class MemberLoginForm extends BaseLoginForm
{
parent::restoreFormState();
$forceMessage = Session::get('MemberLoginForm.force_message');
$session = Controller::curr()->getRequest()->getSession();
$forceMessage = $session->get('MemberLoginForm.force_message');
if (($member = Security::getCurrentUser()) && !$forceMessage) {
$message = _t(
'SilverStripe\\Security\\Member.LOGGEDINAS',
@ -207,7 +208,7 @@ class MemberLoginForm extends BaseLoginForm
// Reset forced message
if ($forceMessage) {
Session::set('MemberLoginForm.force_message', false);
$session->set('MemberLoginForm.force_message', false);
}
return $this;

View File

@ -2,10 +2,10 @@
namespace SilverStripe\Security\MemberAuthenticator;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Cookie;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\Session;
use SilverStripe\Security\AuthenticationHandler;
use SilverStripe\Security\Member;
@ -47,7 +47,7 @@ class SessionAuthenticationHandler implements AuthenticationHandler
{
// If ID is a bad ID it will be treated as if the user is not logged in, rather than throwing a
// ValidationException
$id = Session::get($this->getSessionVariable());
$id = $request->getSession()->get($this->getSessionVariable());
if (!$id) {
return null;
}
@ -64,7 +64,8 @@ class SessionAuthenticationHandler implements AuthenticationHandler
public function logIn(Member $member, $persistent = false, HTTPRequest $request = null)
{
static::regenerateSessionId();
Session::set($this->getSessionVariable(), $member->ID);
$request = $request ?: Controller::curr()->getRequest();
$request->getSession()->set($this->getSessionVariable(), $member->ID);
// This lets apache rules detect whether the user has logged in
// @todo make this a setting on the authentication handler
@ -102,6 +103,7 @@ class SessionAuthenticationHandler implements AuthenticationHandler
*/
public function logOut(HTTPRequest $request = null)
{
Session::clear($this->getSessionVariable());
$request = $request ?: Controller::curr()->getRequest();
$request->getSession()->clear($this->getSessionVariable());
}
}

View File

@ -19,7 +19,6 @@ use SilverStripe\Dev\Deprecation;
use SilverStripe\Dev\TestOnly;
use SilverStripe\Forms\Form;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataModel;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\FieldType\DBField;
@ -328,7 +327,7 @@ class Security extends Controller implements TemplateGlobalProvider
{
self::set_ignore_disallowed_actions(true);
if (!$controller) {
if (!$controller && Controller::has_curr()) {
$controller = Controller::curr();
}
@ -357,7 +356,7 @@ class Security extends Controller implements TemplateGlobalProvider
$messageSet = $configMessageSet;
} else {
$messageSet = array(
'default' => _t(
'default' => _t(
'SilverStripe\\Security\\Security.NOTEPAGESECURED',
"That page is secured. Enter your credentials below and we will send "
. "you right along."
@ -408,7 +407,7 @@ class Security extends Controller implements TemplateGlobalProvider
static::singleton()->setLoginMessage($message, ValidationResult::TYPE_WARNING);
Session::set("BackURL", $_SERVER['REQUEST_URI']);
$controller->getRequest()->getSession()->set("BackURL", $_SERVER['REQUEST_URI']);
// TODO AccessLogEntry needs an extension to handle permission denied errors
// Audit logging hook
@ -569,6 +568,21 @@ class Security extends Controller implements TemplateGlobalProvider
return null;
}
public function getRequest()
{
// Support Security::singleton() where a request isn't always injected
$request = parent::getRequest();
if ($request) {
return $request;
}
if (Controller::has_curr() && Controller::curr() !== $this) {
return Controller::curr()->getRequest();
}
return null;
}
/**
* Prepare the controller for handling the response to this request
*
@ -593,7 +607,6 @@ class Security extends Controller implements TemplateGlobalProvider
$holderPage->ID = -1 * random_int(1, 10000000);
$controller = ModelAsController::controller_for($holderPage);
$controller->setDataModel($this->model);
$controller->doInit();
return $controller;
@ -628,14 +641,15 @@ class Security extends Controller implements TemplateGlobalProvider
*/
protected function getLoginMessage(&$messageType = null)
{
$message = Session::get('Security.Message.message');
$session = $this->getRequest()->getSession();
$message = $session->get('Security.Message.message');
$messageType = null;
if (empty($message)) {
return null;
}
$messageType = Session::get('Security.Message.type');
$messageCast = Session::get('Security.Message.cast');
$messageType = $session->get('Security.Message.type');
$messageCast = $session->get('Security.Message.cast');
if ($messageCast !== ValidationResult::CAST_HTML) {
$message = Convert::raw2xml($message);
}
@ -655,9 +669,12 @@ class Security extends Controller implements TemplateGlobalProvider
$messageType = ValidationResult::TYPE_WARNING,
$messageCast = ValidationResult::CAST_TEXT
) {
Session::set('Security.Message.message', $message);
Session::set('Security.Message.type', $messageType);
Session::set('Security.Message.cast', $messageCast);
Controller::curr()
->getRequest()
->getSession()
->set("Security.Message.message", $message)
->set("Security.Message.type", $messageType)
->set("Security.Message.cast", $messageCast);
}
/**
@ -665,7 +682,10 @@ class Security extends Controller implements TemplateGlobalProvider
*/
public static function clearLoginMessage()
{
Session::clear('Security.Message');
Controller::curr()
->getRequest()
->getSession()
->clear("Security.Message");
}
@ -752,7 +772,7 @@ class Security extends Controller implements TemplateGlobalProvider
// Process each of the handlers
$results = array_map(
function (RequestHandler $handler) {
return $handler->handleRequest($this->getRequest(), DataModel::inst());
return $handler->handleRequest($this->getRequest());
},
$handlers
);
@ -794,7 +814,7 @@ class Security extends Controller implements TemplateGlobalProvider
*/
protected function delegateToHandler(RequestHandler $handler, $title, array $templates = [])
{
$result = $handler->handleRequest($this->getRequest(), DataModel::inst());
$result = $handler->handleRequest($this->getRequest());
// Return the customised controller - used to render in a Form
// Post requests are expected to be login posts, so they'll be handled downstairs
@ -958,7 +978,7 @@ class Security extends Controller implements TemplateGlobalProvider
$service = DefaultAdminService::singleton();
return $service->findOrCreateDefaultAdmin();
}
}
/**
* Flush the default admin credentials
@ -972,7 +992,6 @@ class Security extends Controller implements TemplateGlobalProvider
DefaultAdminService::clearDefaultAdmin();
}
/**
* Set a default admin in dev-mode
*
@ -1092,7 +1111,7 @@ class Security extends Controller implements TemplateGlobalProvider
return [
'password' => $encryptor->encrypt($password, $salt, $member),
'salt' => $salt,
'salt' => $salt,
'algorithm' => $algorithm,
'encryptor' => $encryptor
];
@ -1243,11 +1262,11 @@ class Security extends Controller implements TemplateGlobalProvider
public static function get_template_global_variables()
{
return [
"LoginURL" => "login_url",
"LogoutURL" => "logout_url",
"LoginURL" => "login_url",
"LogoutURL" => "logout_url",
"LostPasswordURL" => "lost_password_url",
"CurrentMember" => "getCurrentUser",
"currentUser" => "getCurrentUser"
"CurrentMember" => "getCurrentUser",
"currentUser" => "getCurrentUser"
];
}
}

View File

@ -150,7 +150,8 @@ class SecurityToken implements TemplateGlobalProvider
*/
public function getValue()
{
$value = Session::get($this->getName());
$session = Controller::curr()->getRequest()->getSession();
$value = $session->get($this->getName());
// only regenerate if the token isn't already set in the session
if (!$value) {
@ -166,7 +167,8 @@ class SecurityToken implements TemplateGlobalProvider
*/
public function setValue($val)
{
Session::set($this->getName(), $val);
$session = Controller::curr()->getRequest()->getSession();
$session->set($this->getName(), $val);
}
/**

View File

@ -2,9 +2,8 @@
namespace SilverStripe\View;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataModel;
use InvalidArgumentException;
use SilverStripe\ORM\DataList;
class GenericTemplateGlobalProvider implements TemplateGlobalProvider
{
@ -61,8 +60,6 @@ class GenericTemplateGlobalProvider implements TemplateGlobalProvider
*/
public static function getDataList($className)
{
$list = new DataList($className);
$list->setDataModel(DataModel::inst());
return $list;
return DataList::create($className);
}
}

View File

@ -21,13 +21,6 @@ class ThemeManifest implements ThemeList
*/
protected $base;
/**
* Include tests
*
* @var bool
*/
protected $tests;
/**
* Path to application code
*
@ -56,39 +49,44 @@ class ThemeManifest implements ThemeList
*/
protected $themes = null;
/**
* @var CacheFactory
*/
protected $cacheFactory= null;
/**
* Constructs a new template manifest. The manifest is not actually built
* or loaded from cache until needed.
*
* @param string $base The base path.
* @param string $project Path to application code
*
* @param bool $includeTests Include tests in the manifest.
* @param bool $forceRegen Force the manifest to be regenerated.
* @param CacheFactory $cacheFactory Cache factory to generate backend cache with
*/
public function __construct(
$base,
$project,
$includeTests = false,
$forceRegen = false,
CacheFactory $cacheFactory = null
) {
$this->base = $base;
$this->tests = $includeTests;
public function __construct($base, $project, CacheFactory $cacheFactory = null)
{
$this->base = $base;
$this->project = $project;
$this->cacheFactory = $cacheFactory;
}
/**
* @param bool $includeTests Include tests in the manifest
* @param bool $forceRegen Force the manifest to be regenerated.
*/
public function init($includeTests = false, $forceRegen = false)
{
// build cache from factory
if ($cacheFactory) {
$this->cache = $cacheFactory->create(
if ($this->cacheFactory) {
$this->cache = $this->cacheFactory->create(
CacheInterface::class.'.thememanifest',
[ 'namespace' => 'thememanifest' . ($includeTests ? '_tests' : '') ]
);
}
$this->cacheKey = $this->getCacheKey();
if ($forceRegen) {
$this->regenerate();
$this->cacheKey = $this->getCacheKey($includeTests);
if (!$forceRegen && $this->cache && ($data = $this->cache->get($this->cacheKey))) {
$this->themes = $data;
} else {
$this->regenerate($includeTests);
}
}
@ -104,36 +102,37 @@ class ThemeManifest implements ThemeList
* Generate a unique cache key to avoid manifest cache collisions.
* We compartmentalise based on the base path, the given project, and whether
* or not we intend to include tests.
*
* @param bool $includeTests
* @return string
*/
public function getCacheKey()
public function getCacheKey($includeTests = false)
{
return sha1(sprintf(
"manifest-%s-%s-%u",
$this->base,
$this->project,
$this->tests
$includeTests
));
}
public function getThemes()
{
if ($this->themes === null) {
$this->init();
}
return $this->themes;
}
/**
* Regenerates the manifest by scanning the base path.
*
* @param bool $includeTests
*/
public function regenerate()
public function regenerate($includeTests = false)
{
$finder = new ManifestFileFinder();
$finder->setOptions(array(
'include_themes' => false,
'ignore_dirs' => array('node_modules', THEMES_DIR),
'ignore_tests' => !$this->tests,
'ignore_tests' => !$includeTests,
'dir_callback' => array($this, 'handleDirectory')
));
@ -172,16 +171,4 @@ class ThemeManifest implements ThemeList
array_push($this->themes, $path);
}
}
/**
* Initialise the manifest
*/
protected function init()
{
if ($this->cache && ($data = $this->cache->get($this->cacheKey))) {
$this->themes = $data;
} else {
$this->regenerate();
}
}
}

View File

@ -1,172 +1,7 @@
<?php
/**
* Configure SilverStripe from the environment variables.
* Usage: Put "require_once('conf/ConfigureFromEnv.php');" into your _config.php file.
* @deprecated 4.0...5.0
*
* If you include this file, you will be able to use the following variables to control
* your site.
*
* Your database connection will be set up using these defines:
* - SS_DATABASE_CLASS: The database class to use, MySQLDatabase, MSSQLDatabase, etc. defaults to
* MySQLPDODatabase
* - SS_DATABASE_SERVER: The database server to use, defaulting to localhost
* - SS_DATABASE_USERNAME: The database username (mandatory)
* - SS_DATABASE_PASSWORD: The database password (mandatory)
* - SS_DATABASE_PORT: The database port
* - SS_DATABASE_SUFFIX: A suffix to add to the database name.
* - SS_DATABASE_PREFIX: A prefix to add to the database name.
* - SS_DATABASE_TIMEZONE: Set the database timezone to something other than the system timezone.
* - SS_DATABASE_MEMORY: Use in-memory state if possible. Useful for testing, currently only
* supported by the SQLite database adapter.
*
* There is one more setting that is intended to be used by people who work on SilverStripe.
* - SS_DATABASE_CHOOSE_NAME: Boolean/Int. If set, then the system will choose a default database name for you if
* one isn't give in the $database variable. The database name will be "SS_" followed by the name of the folder
* into which you have installed SilverStripe. If this is enabled, it means that the phpinstaller will work out of
* the box without the installer needing to alter any files. This helps prevent accidental changes to the
* environment.
*
* If SS_DATABASE_CHOOSE_NAME is an integer greater than one, then an ancestor folder will be used for the database
* name. This is handy for a site that's hosted from /sites/examplesite/www or /buildbot/allmodules-2.3/build. If
* it's 2, the parent folder will be chosen; if it's 3 the grandparent, and so on.
*
* You can configure the environment with this define:
*
* - SS_ENVIRONMENT_TYPE: The environment type: dev, test or live.
*
* You can configure the default admin with these defines:
*
* - SS_DEFAULT_ADMIN_USERNAME: The username of the default admin - this is a non-database user with administrative
* privileges.
* - SS_DEFAULT_ADMIN_PASSWORD: The password of the default admin.
* - SS_USE_BASIC_AUTH: Protect the site with basic auth (good for test sites)
*
* Email:
* - SS_SEND_ALL_EMAILS_TO: If you set this define, all emails will be redirected to this address.
* - SS_SEND_ALL_EMAILS_FROM: If you set this define, all emails will be send from this address.
* Placeholder empty file
*/
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Psr\Log\LoggerInterface;
use SilverStripe\Control\Email\Email;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\Install\DatabaseAdapterRegistry;
use SilverStripe\Security\BasicAuth;
use SilverStripe\Security\DefaultAdminService;
global $database;
// No database provided
if (!isset($database) || !$database) {
if (!($database = getenv('SS_DATABASE_NAME')) && $chooseName = getenv('SS_DATABASE_CHOOSE_NAME')) {
$loopCount = (int)$chooseName;
$databaseDir = BASE_PATH;
for ($i=0; $i<$loopCount-1; $i++) {
$databaseDir = dirname($databaseDir);
}
$database = getenv('SS_DATABASE_PREFIX') ?: 'SS_';
$database .= basename($databaseDir);
$database = str_replace('.', '', $database);
}
}
if ($dbUser = getenv('SS_DATABASE_USERNAME')) {
global $databaseConfig;
// Checks if the database global is defined (if present, wraps with prefix and suffix)
$databaseNameWrapper = function ($name) {
if (!$name) {
return '';
} else {
return (getenv('SS_DATABASE_PREFIX') ?: '')
. $name
. (getenv('SS_DATABASE_SUFFIX') ?: '');
}
};
/** @skipUpgrade */
$databaseConfig = array(
"type" => getenv('SS_DATABASE_CLASS') ?: 'MySQLPDODatabase',
"server" => getenv('SS_DATABASE_SERVER') ?: 'localhost',
"username" => $dbUser,
"password" => getenv('SS_DATABASE_PASSWORD'),
"database" => $databaseNameWrapper($database),
);
// Set the port if called for
if ($dbPort = getenv('SS_DATABASE_PORT')) {
$databaseConfig['port'] = $dbPort;
}
// Set the timezone if called for
if ($dbTZ = getenv('SS_DATABASE_TIMEZONE')) {
$databaseConfig['timezone'] = $dbTZ;
}
// For schema enabled drivers:
if ($dbSchema = getenv('SS_DATABASE_SCHEMA')) {
$databaseConfig["schema"] = $dbSchema;
}
// For SQlite3 memory databases (mainly for testing purposes)
if ($dbMemory = getenv('SS_DATABASE_MEMORY')) {
$databaseConfig["memory"] = $dbMemory;
}
}
if ($sendAllEmailsTo = getenv('SS_SEND_ALL_EMAILS_TO')) {
Email::config()->send_all_emails_to = $sendAllEmailsTo;
}
if ($sendAllEmailsFrom = getenv('SS_SEND_ALL_EMAILS_FROM')) {
Email::config()->send_all_emails_from = $sendAllEmailsFrom;
}
if ($defaultAdminUser = getenv('SS_DEFAULT_ADMIN_USERNAME')) {
if (!$defaultAdminPass = getenv('SS_DEFAULT_ADMIN_PASSWORD')) {
user_error(
"SS_DEFAULT_ADMIN_PASSWORD must be defined in your environment,"
. "if SS_DEFAULT_ADMIN_USERNAME is defined. See "
. "http://doc.silverstripe.org/framework/en/topics/environment-management for more information",
E_USER_ERROR
);
} else {
DefaultAdminService::setDefaultAdmin($defaultAdminUser, $defaultAdminPass);
}
}
if ($useBasicAuth = getenv('SS_USE_BASIC_AUTH')) {
BasicAuth::config()->entire_site_protected = $useBasicAuth;
}
if ($errorLog = getenv('SS_ERROR_LOG')) {
$logger = Injector::inst()->get(LoggerInterface::class);
if ($logger instanceof Logger) {
$logger->pushHandler(new StreamHandler(BASE_PATH . '/' . $errorLog, Logger::WARNING));
} else {
user_error("SS_ERROR_LOG setting only works with Monolog, you are using another logger", E_USER_WARNING);
}
}
// Allow database adapters to handle their own configuration
DatabaseAdapterRegistry::autoconfigure();
unset(
$envType,
$chooseName,
$loopCount,
$databaseDir,
$i,
$databaseNameWrapper,
$dbUser,
$dbPort,
$dbTZ,
$dbSchema,
$dbMemory,
$sendAllEmailsTo,
$sendAllEmailsFrom,
$defaultAdminUser,
$defaultAdminPass,
$useBasicAuth,
$errorLog,
$logger
);

28
src/includes/autoload.php Normal file
View File

@ -0,0 +1,28 @@
<?php
// Check PHP version
if (version_compare(phpversion(), '5.6.0', '<')) {
header($_SERVER['SERVER_PROTOCOL'] . " 500 Server Error");
echo str_replace(
'$PHPVersion',
phpversion(),
file_get_contents(__DIR__ . "/../Dev/Install/php5-required.html")
);
die();
}
// Init composer autoload
call_user_func(function () {
$candidates = [
__DIR__ . '/../../vendor/autoload.php',
__DIR__ . '/../../../vendor/autoload.php',
getcwd() . '/vendor/autoload.php',
];
foreach ($candidates as $candidate) {
if (file_exists($candidate)) {
require_once $candidate;
return;
}
}
die("Failed to include composer's autoloader, unable to continue");
});

View File

@ -27,6 +27,8 @@ use SilverStripe\Control\Util\IPUtils;
* headers from the given client are trustworthy (e.g. from a reverse proxy).
*/
require_once __DIR__ . '/functions.php';
///////////////////////////////////////////////////////////////////////////////
// ENVIRONMENT CONFIG
@ -57,15 +59,17 @@ if (!defined('BASE_PATH')) {
// Allow a first class env var to be set that disables .env file loading
if (!getenv('SS_IGNORE_DOT_ENV')) {
foreach ([ BASE_PATH, dirname(BASE_PATH) ] as $path) {
try {
(new Dotenv($path))->load();
} catch (InvalidPathException $e) {
// no .env found - no big deal
continue;
call_user_func(function () {
foreach ([BASE_PATH, dirname(BASE_PATH)] as $path) {
try {
(new Dotenv($path))->load();
} catch (InvalidPathException $e) {
// no .env found - no big deal
continue;
}
break;
}
break;
}
});
}
/**
@ -142,11 +146,7 @@ if (defined('CUSTOM_INCLUDE_PATH')) {
set_include_path(CUSTOM_INCLUDE_PATH . PATH_SEPARATOR . get_include_path());
}
/**
* Define the temporary folder if it wasn't defined yet
*/
require_once __DIR__ . '/TempPath.php';
// Define the temporary folder if it wasn't defined yet
if (!defined('TEMP_FOLDER')) {
define('TEMP_FOLDER', getTempFolder(BASE_PATH));
}

View File

@ -1,124 +1,8 @@
<?php
use SilverStripe\Core\Cache\ManifestCacheFactory;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Config\CoreConfigFactory;
use SilverStripe\Core\Config\ConfigLoader;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Injector\SilverStripeServiceConfigurationLocator;
use SilverStripe\Core\Manifest\ClassManifest;
use SilverStripe\Core\Manifest\ClassLoader;
use SilverStripe\Control\Director;
use SilverStripe\Core\Manifest\ModuleLoader;
use SilverStripe\Core\Manifest\ModuleManifest;
use SilverStripe\i18n\i18n;
use SilverStripe\Logging\ErrorHandler;
/**
* This file is the Framework bootstrap. It will get your environment ready to call Director::direct().
*
* It takes care of:
* - Checking of PHP memory limit
* - Including all the files needed to get the manifest built
* - Building and including the manifest
*
* @todo This file currently contains a lot of bits and pieces, and its various responsibilities should probably be
* moved into different subsystems.
* @todo A lot of this stuff is very order-dependent. This could be decoupled.
*/
/**
* All errors are reported, including E_STRICT by default *unless* the site is in
* live mode, where reporting is limited to fatal errors and warnings (see later in this file)
*/
error_reporting(E_ALL | E_STRICT);
global $_increase_time_limit_max;
$_increase_time_limit_max = -1;
/**
* Ensure we have enough memory
*/
increase_memory_limit_to('64M');
/**
* Ensure we don't run into xdebug's fairly conservative infinite recursion protection limit
*/
increase_xdebug_nesting_level_to(200);
/**
* Set default encoding
*/
mb_http_output('UTF-8');
mb_internal_encoding('UTF-8');
mb_regex_encoding('UTF-8');
/**
* Enable better garbage collection
*/
gc_enable();
// Initialise the dependency injector as soon as possible, as it is
// subsequently used by some of the following code
$injector = new Injector(array('locator' => SilverStripeServiceConfigurationLocator::class));
Injector::set_inst($injector);
///////////////////////////////////////////////////////////////////////////////
// MANIFEST
// Regenerate the manifest if ?flush is set, or if the database is being built.
// The coupling is a hack, but it removes an annoying bug where new classes
// referenced in _config.php files can be referenced during the build process.
$requestURL = isset($_REQUEST['url']) ? trim($_REQUEST['url'], '/') : false;
$flush = (isset($_GET['flush']) || $requestURL === trim(BASE_URL . '/dev/build', '/'));
// Manifest cache factory
$manifestCacheFactory = new ManifestCacheFactory([
'namespace' => 'manifestcache',
'directory' => getTempFolder(),
]);
// Build class manifest
$manifest = new ClassManifest(BASE_PATH, false, $flush, $manifestCacheFactory);
// Register SilverStripe's class map autoload
$loader = ClassLoader::inst();
$loader->registerAutoloader();
$loader->pushManifest($manifest);
// Init module manifest
$moduleManifest = new ModuleManifest(BASE_PATH, false, $flush, $manifestCacheFactory);
ModuleLoader::inst()->pushManifest($moduleManifest);
// Build config manifest
$configManifest = CoreConfigFactory::inst()->createRoot($flush, $manifestCacheFactory);
ConfigLoader::inst()->pushManifest($configManifest);
// After loading config, boot _config.php files
ModuleLoader::inst()->getManifest()->activateConfig();
// Load template manifest
SilverStripe\View\ThemeResourceLoader::inst()->addSet('$default', new SilverStripe\View\ThemeManifest(
BASE_PATH,
project(),
false,
$flush,
$manifestCacheFactory
));
// If in live mode, ensure deprecation, strict and notices are not reported
if (Director::isLive()) {
error_reporting(E_ALL & ~(E_DEPRECATED | E_STRICT | E_NOTICE));
}
///////////////////////////////////////////////////////////////////////////////
// POST-MANIFEST COMMANDS
/**
* Load error handlers
*/
$errorHandler = Injector::inst()->get(ErrorHandler::class);
$errorHandler->start();
///////////////////////////////////////////////////////////////////////////////
// HELPER FUNCTIONS
@ -156,23 +40,23 @@ function project()
}
/**
* This is the main translator function. Returns the string defined by $entity according to the
* currently set locale.
*
* Also supports pluralisation of strings. Pass in a `count` argument, as well as a
* default value with `|` pipe-delimited options for each plural form.
*
* @param string $entity Entity that identifies the string. It must be in the form
* "Namespace.Entity" where Namespace will be usually the class name where this
* string is used and Entity identifies the string inside the namespace.
* @param mixed $arg,... Additional arguments are parsed as such:
* - Next string argument is a default. Pass in a `|` pipe-delimeted value with `{count}`
* to do pluralisation.
* - Any other string argument after default is context for i18nTextCollector
* - Any array argument in any order is an injection parameter list. Pass in a `count`
* injection parameter to pluralise.
* @return string
*/
* This is the main translator function. Returns the string defined by $entity according to the
* currently set locale.
*
* Also supports pluralisation of strings. Pass in a `count` argument, as well as a
* default value with `|` pipe-delimited options for each plural form.
*
* @param string $entity Entity that identifies the string. It must be in the form
* "Namespace.Entity" where Namespace will be usually the class name where this
* string is used and Entity identifies the string inside the namespace.
* @param mixed $arg,... Additional arguments are parsed as such:
* - Next string argument is a default. Pass in a `|` pipe-delimeted value with `{count}`
* to do pluralisation.
* - Any other string argument after default is context for i18nTextCollector
* - Any array argument in any order is an injection parameter list. Pass in a `count`
* injection parameter to pluralise.
* @return string
*/
function _t($entity, $arg = null)
{
// Pass args directly to handle deprecation
@ -261,11 +145,11 @@ function translate_memstring($memString)
{
switch (strtolower(substr($memString, -1))) {
case "k":
return round(substr($memString, 0, -1)*1024);
return round(substr($memString, 0, -1) * 1024);
case "m":
return round(substr($memString, 0, -1)*1024*1024);
return round(substr($memString, 0, -1) * 1024 * 1024);
case "g":
return round(substr($memString, 0, -1)*1024*1024*1024);
return round(substr($memString, 0, -1) * 1024 * 1024 * 1024);
default:
return round($memString);
}
@ -322,3 +206,114 @@ function get_increase_time_limit_max()
global $_increase_time_limit_max;
return $_increase_time_limit_max;
}
/**
* Returns the temporary folder path that silverstripe should use for its cache files.
*
* @param string $base The base path to use for determining the temporary path
* @return string Path to temp
*/
function getTempFolder($base = null)
{
$parent = getTempParentFolder($base);
// The actual temp folder is a subfolder of getTempParentFolder(), named by username
$subfolder = $parent . DIRECTORY_SEPARATOR . getTempFolderUsername();
if (!@file_exists($subfolder)) {
mkdir($subfolder);
}
return $subfolder;
}
/**
* Returns as best a representation of the current username as we can glean.
*
* @return string
*/
function getTempFolderUsername()
{
$user = getenv('APACHE_RUN_USER');
if (!$user) {
$user = getenv('USER');
}
if (!$user) {
$user = getenv('USERNAME');
}
if (!$user && function_exists('posix_getpwuid') && function_exists('posix_getuid')) {
$userDetails = posix_getpwuid(posix_getuid());
$user = $userDetails['name'];
}
if (!$user) {
$user = 'unknown';
}
$user = preg_replace('/[^A-Za-z0-9_\-]/', '', $user);
return $user;
}
/**
* Return the parent folder of the temp folder.
* The temp folder will be a subfolder of this, named by username.
* This structure prevents permission problems.
*
* @param string $base
* @return string
* @throws Exception
*/
function getTempParentFolder($base = null)
{
if (!$base && defined('BASE_PATH')) {
$base = BASE_PATH;
}
// first, try finding a silverstripe-cache dir built off the base path
$tempPath = $base . DIRECTORY_SEPARATOR . 'silverstripe-cache';
if (@file_exists($tempPath)) {
if ((fileperms($tempPath) & 0777) != 0777) {
@chmod($tempPath, 0777);
}
return $tempPath;
}
// failing the above, try finding a namespaced silverstripe-cache dir in the system temp
$tempPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR .
'silverstripe-cache-php' . preg_replace('/[^\w-\.+]+/', '-', PHP_VERSION) .
str_replace(array(' ', '/', ':', '\\'), '-', $base);
if (!@file_exists($tempPath)) {
$oldUMask = umask(0);
@mkdir($tempPath, 0777);
umask($oldUMask);
// if the folder already exists, correct perms
} else {
if ((fileperms($tempPath) & 0777) != 0777) {
@chmod($tempPath, 0777);
}
}
$worked = @file_exists($tempPath) && @is_writable($tempPath);
// failing to use the system path, attempt to create a local silverstripe-cache dir
if (!$worked) {
$tempPath = $base . DIRECTORY_SEPARATOR . 'silverstripe-cache';
if (!@file_exists($tempPath)) {
$oldUMask = umask(0);
@mkdir($tempPath, 0777);
umask($oldUMask);
}
$worked = @file_exists($tempPath) && @is_writable($tempPath);
}
if (!$worked) {
throw new Exception(
'Permission problem gaining access to a temp folder. ' .
'Please create a folder named silverstripe-cache in the base folder ' .
'of the installation and ensure it has the correct permissions'
);
}
return $tempPath;
}

View File

@ -5,7 +5,7 @@
// Connect to database
use SilverStripe\ORM\DB;
require_once __DIR__ . '/../../src/Core/Core.php';
require_once __DIR__ . '/../../src/Core/functions.php';
require_once __DIR__ . '/../php/Control/FakeController.php';
// Bootstrap a mock project configuration

View File

@ -3,8 +3,9 @@
namespace SilverStripe\Control\Tests;
use InvalidArgumentException;
use PHPUnit_Framework_Error;
use SilverStripe\Control\RequestHandler;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\Tests\ControllerTest\AccessBaseController;
use SilverStripe\Control\Tests\ControllerTest\AccessSecuredController;
use SilverStripe\Control\Tests\ControllerTest\AccessWildcardSecuredController;
@ -15,13 +16,8 @@ use SilverStripe\Control\Tests\ControllerTest\IndexSecuredController;
use SilverStripe\Control\Tests\ControllerTest\SubController;
use SilverStripe\Control\Tests\ControllerTest\TestController;
use SilverStripe\Control\Tests\ControllerTest\UnsecuredController;
use SilverStripe\Core\Config\Config;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Dev\Deprecation;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\ORM\DataModel;
use SilverStripe\Security\Member;
use SilverStripe\Security\Security;
use SilverStripe\View\SSViewer;
@ -539,7 +535,7 @@ class ControllerTest extends FunctionalTest
/* Handle the request to create conditions where improperly passing the action to the viewer might fail */
$controller = new ControllerTest\ContainerController();
try {
$controller->handleRequest($request, DataModel::inst());
$controller->handleRequest($request);
} catch (ControllerTest\SubController_Exception $e) {
$this->fail($e->getMessage());
}

View File

@ -5,9 +5,7 @@ namespace SilverStripe\Control\Tests\DirectorTest;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Control\RequestFilter;
use SilverStripe\Control\Session;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataModel;
class TestRequestFilter implements RequestFilter, TestOnly
{
@ -17,22 +15,24 @@ class TestRequestFilter implements RequestFilter, TestOnly
public $failPre = false;
public $failPost = false;
public function preRequest(HTTPRequest $request, Session $session, DataModel $model)
public function preRequest(HTTPRequest $request)
{
++$this->preCalls;
if ($this->failPre) {
return false;
}
return true;
}
public function postRequest(HTTPRequest $request, HTTPResponse $response, DataModel $model)
public function postRequest(HTTPRequest $request, HTTPResponse $response)
{
++$this->postCalls;
if ($this->failPost) {
return false;
}
return true;
}
public function reset()

View File

@ -16,14 +16,11 @@ class FakeController extends Controller
{
parent::__construct();
$session = Injector::inst()->create(Session::class, isset($_SESSION) ? $_SESSION : array());
$this->setSession($session);
$this->pushCurrent();
$session = new Session(isset($_SESSION) ? $_SESSION : array());
$request = new HTTPRequest('GET', '/');
$request->setSession($session);
$this->setRequest($request);
$this->setResponse(new HTTPResponse());
$this->doInit();

View File

@ -134,7 +134,7 @@ class ParameterConfirmationTokenTest extends SapphireTest
$_SERVER['HTTP_HOST'] = $host;
ParameterConfirmationToken::$alternateBaseURL = $base;
$this->assertEquals('http://'.implode('/', $urlAnswer) . $urlSlash, $token->currentAbsoluteURL());
$this->assertEquals('http://'.implode('/', $urlAnswer) . $urlSlash, $token->currentURL());
}
}
}

View File

@ -11,8 +11,8 @@ use SilverStripe\Dev\TestOnly;
class ParameterConfirmationTokenTest_Token extends ParameterConfirmationToken implements TestOnly
{
public function currentAbsoluteURL()
public function currentURL()
{
return parent::currentAbsoluteURL();
return parent::currentURL();
}
}

View File

@ -118,10 +118,10 @@ class GridFieldDeleteActionTest extends SapphireTest
if (Security::getCurrentUser()) {
Security::setCurrentUser(null);
}
$this->setExpectedException(ValidationException::class);
$this->expectException(ValidationException::class);
$stateID = 'testGridStateActionField';
Session::set(
Controller::curr()->getRequest()->getSession()->set(
$stateID,
array(
'grid' => '',

View File

@ -25,6 +25,7 @@ use SilverStripe\Forms\Tests\GridField\GridFieldTest\Permissions;
use SilverStripe\Forms\Tests\GridField\GridFieldTest\Player;
use SilverStripe\Forms\Tests\GridField\GridFieldTest\Team;
use SilverStripe\ORM\ArrayList;
use SilverStripe\Security\Group;
use SilverStripe\Security\Member;
class GridFieldTest extends SapphireTest
@ -115,9 +116,9 @@ class GridFieldTest extends SapphireTest
public function testGridFieldModelClass()
{
$obj = new GridField('testfield', 'testfield', Member::get());
$this->assertEquals('SilverStripe\\Security\\Member', $obj->getModelClass(), 'Should return Member');
$obj->setModelClass('SilverStripe\\ORM\\DataModel');
$this->assertEquals('SilverStripe\\ORM\\DataModel', $obj->getModelClass(), 'Should return Member');
$this->assertEquals(Member::class, $obj->getModelClass(), 'Should return Member');
$obj->setModelClass(Group::class);
$this->assertEquals(Group::class, $obj->getModelClass(), 'Should return Group');
}
/**

View File

@ -79,15 +79,16 @@ class SecurityTest extends FunctionalTest
$controller = new SecurityTest\NullController();
$controller->setResponse(new HTTPResponse());
$session = Controller::curr()->getRequest()->getSession();
Security::permissionFailure($controller, array('default' => 'Oops, not allowed'));
$this->assertEquals('Oops, not allowed', Session::get('Security.Message.message'));
$this->assertEquals('Oops, not allowed', $session->get('Security.Message.message'));
// Test that config values are used correctly
Config::inst()->update(Security::class, 'default_message_set', 'stringvalue');
Security::permissionFailure($controller);
$this->assertEquals(
'stringvalue',
Session::get('Security.Message.message'),
$session->get('Security.Message.message'),
'Default permission failure message value was not present'
);
@ -96,7 +97,7 @@ class SecurityTest extends FunctionalTest
Security::permissionFailure($controller);
$this->assertEquals(
'arrayvalue',
Session::get('Security.Message.message'),
$session->get('Security.Message.message'),
'Default permission failure message value was not present'
);
@ -474,14 +475,14 @@ class SecurityTest extends FunctionalTest
);
}
}
$msg = _t(
'SilverStripe\\Security\\Member.ERRORLOCKEDOUT2',
'Your account has been temporarily disabled because of too many failed attempts at ' .
'logging in. Please try again in {count} minutes.',
null,
array('count' => Member::config()->lock_out_delay_mins)
);
$this->assertHasMessage($msg);
$msg = _t(
'SilverStripe\\Security\\Member.ERRORLOCKEDOUT2',
'Your account has been temporarily disabled because of too many failed attempts at ' .
'logging in. Please try again in {count} minutes.',
null,
array('count' => Member::config()->lock_out_delay_mins)
);
$this->assertHasMessage($msg);
$this->doTestLoginForm('testuser@example.com', '1nitialPassword');
@ -563,14 +564,14 @@ class SecurityTest extends FunctionalTest
$attempt = DataObject::get_one(
LoginAttempt::class,
array(
'"LoginAttempt"."Email"' => 'testuser@example.com'
'"LoginAttempt"."Email"' => 'testuser@example.com'
)
);
$this->assertTrue(is_object($attempt));
$member = DataObject::get_one(
Member::class,
array(
'"Member"."Email"' => 'testuser@example.com'
'"Member"."Email"' => 'testuser@example.com'
)
);
$this->assertEquals($attempt->Status, 'Failure');