public function __construct() { $this->brokenOnConstruct = false; // Check necessary to avoid class conflicts before manifest is rebuilt if (class_exists('NullHTTPRequest')) { $this->setRequest(new NullHTTPRequest()); } // This will prevent bugs if setDataModel() isn't called. $this->model = DataModel::inst(); parent::__construct(); }
/** * @param string $identifier Unique identifier for this fixture type * @param array $data Map of property names to their values. * @param array $fixtures Map of fixture names to an associative array of their in-memory * identifiers mapped to their database IDs. Used to look up * existing fixtures which might be referenced in the $data attribute * via the => notation. * @return DataObject * @throws Exception */ public function createObject($identifier, $data = null, $fixtures = null) { // We have to disable validation while we import the fixtures, as the order in // which they are imported doesnt guarantee valid relations until after the import is complete. // Also disable filesystem manipulations Config::nest(); Config::inst()->update('SilverStripe\\ORM\\DataObject', 'validation_enabled', false); Config::inst()->update('File', 'update_filesystem', false); $this->invokeCallbacks('beforeCreate', array($identifier, &$data, &$fixtures)); try { $class = $this->class; $obj = DataModel::inst()->{$class}->newObject(); // 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 // Call $this->write(). (For example, in FileTest) if (isset($data['ID'])) { $obj->ID = $data['ID']; // The database needs to allow inserting values into the foreign key column (ID in our case) $conn = DB::get_conn(); $baseTable = DataObject::getSchema()->baseDataTable($class); if (method_exists($conn, 'allowPrimaryKeyEditing')) { $conn->allowPrimaryKeyEditing($baseTable, true); } $obj->write(false, true); if (method_exists($conn, 'allowPrimaryKeyEditing')) { $conn->allowPrimaryKeyEditing($baseTable, false); } } // Populate defaults if ($this->defaults) { foreach ($this->defaults as $fieldName => $fieldVal) { if (isset($data[$fieldName]) && $data[$fieldName] !== false) { continue; } if (is_callable($fieldVal)) { $obj->{$fieldName} = $fieldVal($obj, $data, $fixtures); } else { $obj->{$fieldName} = $fieldVal; } } } // Populate overrides if ($data) { foreach ($data as $fieldName => $fieldVal) { // Defer relationship processing if ($obj->manyManyComponent($fieldName) || $obj->hasManyComponent($fieldName) || $obj->hasOneComponent($fieldName)) { continue; } $this->setValue($obj, $fieldName, $fieldVal, $fixtures); } } $obj->write(); // Save to fixture before relationship processing in case of reflexive relationships if (!isset($fixtures[$class])) { $fixtures[$class] = array(); } $fixtures[$class][$identifier] = $obj->ID; // Populate all relations if ($data) { foreach ($data as $fieldName => $fieldVal) { if ($obj->manyManyComponent($fieldName) || $obj->hasManyComponent($fieldName)) { $obj->write(); $parsedItems = array(); if (is_array($fieldVal)) { // handle lists of many_many relations. Each item can // specify the many_many_extraFields against each // related item. foreach ($fieldVal as $relVal) { $item = key($relVal); $id = $this->parseValue($item, $fixtures); $parsedItems[] = $id; array_shift($relVal); $obj->getManyManyComponents($fieldName)->add($id, $relVal); } } else { $items = preg_split('/ *, */', trim($fieldVal)); foreach ($items as $item) { // Check for correct format: =><relationname>.<identifier>. // Ignore if the item has already been replaced with a numeric DB identifier if (!is_numeric($item) && !preg_match('/^=>[^\\.]+\\.[^\\.]+/', $item)) { throw new InvalidArgumentException(sprintf('Invalid format for relation "%s" on class "%s" ("%s")', $fieldName, $class, $item)); } $parsedItems[] = $this->parseValue($item, $fixtures); } if ($obj->hasManyComponent($fieldName)) { $obj->getComponents($fieldName)->setByIDList($parsedItems); } elseif ($obj->manyManyComponent($fieldName)) { $obj->getManyManyComponents($fieldName)->setByIDList($parsedItems); } } } else { $hasOneField = preg_replace('/ID$/', '', $fieldName); if ($className = $obj->hasOneComponent($hasOneField)) { $obj->{$hasOneField . 'ID'} = $this->parseValue($fieldVal, $fixtures, $fieldClass); // Inject class for polymorphic relation if ($className === 'SilverStripe\\ORM\\DataObject') { $obj->{$hasOneField . 'Class'} = $fieldClass; } } } } } $obj->write(); // If LastEdited was set in the fixture, set it here if ($data && array_key_exists('LastEdited', $data)) { $this->overrideField($obj, 'LastEdited', $data['LastEdited'], $fixtures); } } catch (Exception $e) { Config::unnest(); throw $e; } Config::unnest(); $this->invokeCallbacks('afterCreate', array($obj, $identifier, &$data, &$fixtures)); return $obj; }
/** * Test a URL request, returning a response object. This method is the counterpart of * Director::direct() that is used in functional testing. It will execute the URL given, and * return the result as an SS_HTTPResponse object. * * @uses getControllerForURL() The rule-lookup logic is handled by this. * @uses Controller::run() Handles the page logic for a Director::direct() call. * * @param string $url The URL to visit. * @param array $postVars The $_POST & $_FILES variables. * @param array|Session $session The {@link Session} object representing the current session. * By passing the same object to multiple calls of Director::test(), you can simulate a persisted * session. * @param string $httpMethod The HTTP method, such as GET or POST. It will default to POST if * postVars is set, GET otherwise. Overwritten by $postVars['_method'] if present. * @param string $body The HTTP body. * @param array $headers HTTP headers with key-value pairs. * @param array|Cookie_Backend $cookies to populate $_COOKIE. * @param HTTP_Request $request The {@see HTTP_Request} object generated as a part of this request. * * @return SS_HTTPResponse * * @throws SS_HTTPResponse_Exception */ public static function test($url, $postVars = null, $session = array(), $httpMethod = null, $body = null, $headers = array(), $cookies = array(), &$request = null) { Config::nest(); Injector::nest(); // These are needed so that calling Director::test() does not muck with whoever is calling it. // Really, it's some inappropriate coupling and should be resolved by making less use of statics. $oldReadingMode = Versioned::get_reading_mode(); $getVars = array(); if (!$httpMethod) { $httpMethod = $postVars || is_array($postVars) ? "POST" : "GET"; } if (!$session) { $session = Injector::inst()->create('Session', array()); } $cookieJar = $cookies instanceof Cookie_Backend ? $cookies : Injector::inst()->createWithArgs('Cookie_Backend', array($cookies ?: array())); // Back up the current values of the superglobals $existingRequestVars = isset($_REQUEST) ? $_REQUEST : array(); $existingGetVars = isset($_GET) ? $_GET : array(); $existingPostVars = isset($_POST) ? $_POST : array(); $existingSessionVars = isset($_SESSION) ? $_SESSION : array(); $existingCookies = isset($_COOKIE) ? $_COOKIE : array(); $existingServer = isset($_SERVER) ? $_SERVER : array(); $existingRequirementsBackend = Requirements::backend(); Config::inst()->update('Cookie', 'report_errors', false); Requirements::set_backend(Injector::inst()->create('Requirements_Backend')); // Set callback to invoke prior to return $onCleanup = function () use($existingRequestVars, $existingGetVars, $existingPostVars, $existingSessionVars, $existingCookies, $existingServer, $existingRequirementsBackend, $oldReadingMode) { // Restore the super globals $_REQUEST = $existingRequestVars; $_GET = $existingGetVars; $_POST = $existingPostVars; $_SESSION = $existingSessionVars; $_COOKIE = $existingCookies; $_SERVER = $existingServer; Requirements::set_backend($existingRequirementsBackend); // These are needed so that calling Director::test() does not muck with whoever is calling it. // Really, it's some inappropriate coupling and should be resolved by making less use of statics Versioned::set_reading_mode($oldReadingMode); Injector::unnest(); // Restore old CookieJar, etc Config::unnest(); }; if (strpos($url, '#') !== false) { $url = substr($url, 0, strpos($url, '#')); } // Handle absolute URLs if (parse_url($url, PHP_URL_HOST)) { $bits = parse_url($url); // If a port is mentioned in the absolute URL, be sure to add that into the HTTP host if (isset($bits['port'])) { $_SERVER['HTTP_HOST'] = $bits['host'] . ':' . $bits['port']; } else { $_SERVER['HTTP_HOST'] = $bits['host']; } } // Ensure URL is properly made relative. // Example: url passed is "/ss31/my-page" (prefixed with BASE_URL), this should be changed to "my-page" $url = self::makeRelative($url); $urlWithQuerystring = $url; if (strpos($url, '?') !== false) { list($url, $getVarsEncoded) = explode('?', $url, 2); parse_str($getVarsEncoded, $getVars); } // Replace the super globals with appropriate test values $_REQUEST = ArrayLib::array_merge_recursive((array) $getVars, (array) $postVars); $_GET = (array) $getVars; $_POST = (array) $postVars; $_SESSION = $session ? $session->inst_getAll() : array(); $_COOKIE = $cookieJar->getAll(false); Injector::inst()->registerService($cookieJar, 'Cookie_Backend'); $_SERVER['REQUEST_URI'] = Director::baseURL() . $urlWithQuerystring; $request = new SS_HTTPRequest($httpMethod, $url, $getVars, $postVars, $body); if ($headers) { foreach ($headers as $k => $v) { $request->addHeader($k, $v); } } // Pre-request filtering // @see issue #2517 $model = DataModel::inst(); $output = Injector::inst()->get('RequestProcessor')->preRequest($request, $session, $model); if ($output === false) { $onCleanup(); throw new SS_HTTPResponse_Exception(_t('Director.INVALID_REQUEST', 'Invalid request'), 400); } // TODO: Pass in the DataModel $result = Director::handleRequest($request, $session, $model); // Ensure that the result is an SS_HTTPResponse object if (is_string($result)) { if (substr($result, 0, 9) == 'redirect:') { $response = new SS_HTTPResponse(); $response->redirect(substr($result, 9)); $result = $response; } else { $result = new SS_HTTPResponse($result); } } $output = Injector::inst()->get('RequestProcessor')->postRequest($request, $result, $model); if ($output === false) { $onCleanup(); throw new SS_HTTPResponse_Exception("Invalid response"); } // Return valid response $onCleanup(); return $result; }
return $reloadToken->reloadWithToken(); } // Fail and redirect the user to the login page $loginPage = Director::absoluteURL(Security::config()->login_url); $loginPage .= "?BackURL=" . urlencode($_SERVER['REQUEST_URI']); header('location: ' . $loginPage, true, 302); die; })->thenIfErrored(function () use($reloadToken) { if ($reloadToken) { $reloadToken->reloadWithToken(); } })->execute(); global $databaseConfig; // Redirect to the installer if no database is selected if (!isset($databaseConfig) || !isset($databaseConfig['database']) || !$databaseConfig['database']) { if (!file_exists(BASE_PATH . '/install.php')) { header($_SERVER['SERVER_PROTOCOL'] . " 500 Server Error"); die('SilverStripe Framework requires a $databaseConfig defined.'); } $s = isset($_SERVER['SSL']) || isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off' ? 's' : ''; $installURL = "http{$s}://" . $_SERVER['HTTP_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());
/** * Return all objects matching the filter * sub-classes are automatically selected and included * * @param string $callerClass The class of objects to be returned * @param string|array $filter A filter to be inserted into the WHERE clause. * Supports parameterised queries. See SQLSelect::addWhere() for syntax examples. * @param string|array $sort A sort expression to be inserted into the ORDER * BY clause. If omitted, self::$default_sort will be used. * @param string $join Deprecated 3.0 Join clause. Use leftJoin($table, $joinClause) instead. * @param string|array $limit A limit expression to be inserted into the LIMIT clause. * @param string $containerClass The container class to return the results in. * * @todo $containerClass is Ignored, why? * * @return DataList The objects matching the filter, in the class specified by $containerClass */ public static function get($callerClass = null, $filter = "", $sort = "", $join = "", $limit = null, $containerClass = DataList::class) { if ($callerClass == null) { $callerClass = get_called_class(); if ($callerClass == self::class) { throw new \InvalidArgumentException('Call <classname>::get() instead of DataObject::get()'); } if ($filter || $sort || $join || $limit || $containerClass != DataList::class) { throw new \InvalidArgumentException('If calling <classname>::get() then you shouldn\'t pass any other' . ' arguments'); } $result = DataList::create(get_called_class()); $result->setDataModel(DataModel::inst()); return $result; } if ($join) { throw new \InvalidArgumentException('The $join argument has been removed. Use leftJoin($table, $joinClause) instead.'); } $result = DataList::create($callerClass)->where($filter)->sort($sort); if ($limit && strpos($limit, ',') !== false) { $limitArguments = explode(',', $limit); $result = $result->limit($limitArguments[1], $limitArguments[0]); } elseif ($limit) { $result = $result->limit($limit); } $result->setDataModel(DataModel::inst()); return $result; }
public function setUp() { //nest config and injector for each test so they are effectively sandboxed per test Config::nest(); Injector::nest(); $this->originalReadingMode = Versioned::get_reading_mode(); // We cannot run the tests on this abstract class. if (get_class($this) == __CLASS__) { $this->markTestSkipped(sprintf('Skipping %s ', get_class($this))); return; } // Mark test as being run $this->originalIsRunningTest = self::$is_running_test; self::$is_running_test = true; // i18n needs to be set to the defaults or tests fail i18n::set_locale(i18n::config()->get('default_locale')); i18n::config()->date_format = null; i18n::config()->time_format = null; // Set default timezone consistently to avoid NZ-specific dependencies date_default_timezone_set('UTC'); // Remove password validation $this->originalMemberPasswordValidator = Member::password_validator(); $this->originalRequirements = Requirements::backend(); Member::set_password_validator(null); Cookie::config()->update('report_errors', false); if (class_exists('SilverStripe\\CMS\\Controllers\\RootURLController')) { RootURLController::reset(); } if (class_exists('Translatable')) { Translatable::reset(); } Versioned::reset(); DataObject::reset(); if (class_exists('SilverStripe\\CMS\\Model\\SiteTree')) { SiteTree::reset(); } Hierarchy::reset(); if (Controller::has_curr()) { Controller::curr()->setSession(Session::create(array())); } Security::$database_is_ready = null; // Add controller-name auto-routing // @todo Fix to work with namespaced controllers Director::config()->update('rules', array('$Controller//$Action/$ID/$OtherID' => '*')); $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()) { self::create_temp_db(); } DataObject::singleton()->flushCache(); self::empty_temp_db(); foreach ($this->requireDefaultRecordsFrom as $className) { $instance = singleton($className); if (method_exists($instance, 'requireDefaultRecords')) { $instance->requireDefaultRecords(); } if (method_exists($instance, 'augmentDefaultRecords')) { $instance->augmentDefaultRecords(); } } foreach ($fixtureFiles as $fixtureFilePath) { $fixture = YamlFixture::create($fixtureFilePath); $fixture->writeInto($this->getFixtureFactory()); } $this->logInWithPermission("ADMIN"); } // Preserve memory settings $this->originalMemoryLimit = ini_get('memory_limit'); // turn off template debugging SSViewer::config()->update('source_file_comments', false); // Clear requirements Requirements::clear(); // Set up email $this->mailer = new TestMailer(); Injector::inst()->registerService($this->mailer, 'SilverStripe\\Control\\Email\\Mailer'); Email::config()->remove('send_all_emails_to'); }
public function setUp() { //nest config and injector for each test so they are effectively sandboxed per test Config::nest(); Injector::nest(); $this->originalReadingMode = Versioned::get_reading_mode(); // We cannot run the tests on this abstract class. if (get_class($this) == "SapphireTest") { $this->markTestSkipped(sprintf('Skipping %s ', get_class($this))); return; } // Mark test as being run $this->originalIsRunningTest = self::$is_running_test; self::$is_running_test = true; // i18n needs to be set to the defaults or tests fail i18n::set_locale(Config::inst()->get('i18n', 'default_locale')); i18n::config()->date_format = null; i18n::config()->time_format = null; // Set default timezone consistently to avoid NZ-specific dependencies date_default_timezone_set('UTC'); // Remove password validation $this->originalMemberPasswordValidator = Member::password_validator(); $this->originalRequirements = Requirements::backend(); Member::set_password_validator(null); Config::inst()->update('Cookie', 'report_errors', false); if (class_exists('SilverStripe\\CMS\\Controllers\\RootURLController')) { RootURLController::reset(); } if (class_exists('Translatable')) { Translatable::reset(); } Versioned::reset(); DataObject::reset(); if (class_exists('SilverStripe\\CMS\\Model\\SiteTree')) { SiteTree::reset(); } Hierarchy::reset(); if (Controller::has_curr()) { Controller::curr()->setSession(Injector::inst()->create('Session', array())); } Security::$database_is_ready = null; // Add controller-name auto-routing Config::inst()->update('Director', 'rules', array('$Controller//$Action/$ID/$OtherID' => '*')); $fixtureFile = static::get_fixture_file(); $prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_'; // Todo: this could be a special test model $this->model = DataModel::inst(); // Set up fixture if ($fixtureFile || $this->usesDatabase) { if (!self::using_temp_db()) { self::create_temp_db(); } singleton('SilverStripe\\ORM\\DataObject')->flushCache(); self::empty_temp_db(); foreach ($this->requireDefaultRecordsFrom as $className) { $instance = singleton($className); if (method_exists($instance, 'requireDefaultRecords')) { $instance->requireDefaultRecords(); } if (method_exists($instance, 'augmentDefaultRecords')) { $instance->augmentDefaultRecords(); } } if ($fixtureFile) { $pathForClass = $this->getCurrentAbsolutePath(); $fixtureFiles = is_array($fixtureFile) ? $fixtureFile : array($fixtureFile); $i = 0; foreach ($fixtureFiles as $fixtureFilePath) { // Support fixture paths relative to the test class, rather than relative to webroot // String checking is faster than file_exists() calls. $isRelativeToFile = strpos('/', $fixtureFilePath) === false || preg_match('/^\\.\\./', $fixtureFilePath); if ($isRelativeToFile) { $resolvedPath = realpath($pathForClass . '/' . $fixtureFilePath); if ($resolvedPath) { $fixtureFilePath = $resolvedPath; } } $fixture = Injector::inst()->create('YamlFixture', $fixtureFilePath); $fixture->writeInto($this->getFixtureFactory()); $this->fixtures[] = $fixture; // backwards compatibility: Load first fixture into $this->fixture if ($i == 0) { $this->fixture = $fixture; } $i++; } } $this->logInWithPermission("ADMIN"); } // Preserve memory settings $this->originalMemoryLimit = ini_get('memory_limit'); // turn off template debugging Config::inst()->update('SSViewer', 'source_file_comments', false); // Clear requirements Requirements::clear(); // Set up email $this->originalMailer = Email::mailer(); $this->mailer = new TestMailer(); Injector::inst()->registerService($this->mailer, 'Mailer'); Config::inst()->remove('Email', 'send_all_emails_to'); }
/** * This allows templates to create a new `DataList` from a known * DataObject class name, and call methods such as aggregates. * * The common use case is for partial caching: * <code> * <% cached List(Member).max(LastEdited) %> * loop members here * <% end_cached %> * </code> * * @param string $className * @return DataList */ public static function getDataList($className) { $list = new DataList($className); $list->setDataModel(DataModel::inst()); return $list; }
function testFieldMessageEscapeHtml() { $form = $this->getStubForm(); $form->getController()->handleRequest(new HTTPRequest('GET', '/'), DataModel::inst()); // stub out request $form->addErrorMessage('key1', '<em>Escaped HTML</em>', 'good', true); $form->setupFormErrors(); $parser = new CSSContentParser($result = $form->forTemplate()); $messageEls = $parser->getBySelector('#Form_Form_key1_Holder .message'); $this->assertContains('<em>Escaped HTML</em>', $messageEls[0]->asXML()); $form = $this->getStubForm(); $form->getController()->handleRequest(new HTTPRequest('GET', '/'), DataModel::inst()); // stub out request $form->addErrorMessage('key1', '<em>Unescaped HTML</em>', 'good', false); $form->setupFormErrors(); $parser = new CSSContentParser($form->forTemplate()); $messageEls = $parser->getBySelector('#Form_Form_key1_Holder .message'); $this->assertContains('<em>Unescaped HTML</em>', $messageEls[0]->asXML()); }
/** * * @param GridField $gridField * @param HTTPRequest $request * @return GridFieldDetailForm_ItemRequest */ public function handleItem($gridField, $request) { // Our getController could either give us a true Controller, if this is the top-level GridField. // It could also give us a RequestHandler in the form of GridFieldDetailForm_ItemRequest if this is a // nested GridField. $requestHandler = $gridField->getForm()->getController(); /** @var DataObject $record */ if (is_numeric($request->param('ID'))) { $record = $gridField->getList()->byID($request->param("ID")); } else { $record = Object::create($gridField->getModelClass()); } $handler = $this->getItemRequestHandler($gridField, $record, $requestHandler); // if no validator has been set on the GridField and the record has a // CMS validator, use that. if (!$this->getValidator() && (method_exists($record, 'getCMSValidator') || $record instanceof Object && $record->hasMethod('getCMSValidator'))) { $this->setValidator($record->getCMSValidator()); } return $handler->handleRequest($request, DataModel::inst()); }
public function testSubActions() { /* If a controller action returns another controller, ensure that the $action variable is correctly forwarded */ $response = $this->get("ControllerTest_ContainerController/subcontroller/subaction"); $this->assertEquals('subaction', $response->getBody()); $request = new SS_HTTPRequest('GET', 'ControllerTest_ContainerController/subcontroller/substring/subvieweraction'); /* Shift to emulate the director selecting the controller */ $request->shift(); /* 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()); } catch (ControllerTest_SubController_Exception $e) { $this->fail($e->getMessage()); } }
public function __construct() { $this->brokenOnConstruct = false; $this->setRequest(new NullHTTPRequest()); // This will prevent bugs if setDataModel() isn't called. $this->model = DataModel::inst(); parent::__construct(); }