public function tearDown() { while ($this->nestingLevel > 0) { $this->nestingLevel--; Config::unnest(); } parent::tearDown(); }
public function testDefaultClasses() { Config::nest(); FormField::config()->update('default_classes', array('class1')); $field = new FormField('MyField'); $this->assertContains('class1', $field->extraClass(), 'Class list does not contain expected class'); FormField::config()->update('default_classes', array('class1', 'class2')); $field = new FormField('MyField'); $this->assertContains('class1 class2', $field->extraClass(), 'Class list does not contain expected class'); FormField::config()->update('default_classes', array('class3')); $field = new FormField('MyField'); $this->assertContains('class3', $field->extraClass(), 'Class list does not contain expected class'); $field->removeExtraClass('class3'); $this->assertNotContains('class3', $field->extraClass(), 'Class list contains unexpected class'); TextField::config()->update('default_classes', array('textfield-class')); $field = new TextField('MyField'); //check default classes inherit $this->assertContains('class3', $field->extraClass(), 'Class list does not contain inherited class'); $this->assertContains('textfield-class', $field->extraClass(), 'Class list does not contain expected class'); Config::unnest(); }
public function testShortCodeParsedInTemplateHelpers() { $parser = ShortcodeParser::get('HTMLTextTest'); $parser->register('shortcode', function ($arguments, $content, $parser, $tagName, $extra) { return 'Replaced short code with this. <a href="home">home</a>'; }); ShortcodeParser::set_active('HTMLTextTest'); /** @var DBHTMLText $field */ $field = DBField::create_field('HTMLText', '<p>[shortcode]</p>'); $this->assertEquals('<p>Replaced short code with this. <a href="home">home</a></p>', $field->HTMLATT()); $this->assertEquals('%3Cp%3EReplaced+short+code+with+this.+%3Ca+href%3D%22home%22%3Ehome%3C%2Fa%3E%3C%2Fp%3E', $field->URLATT()); $this->assertEquals('%3Cp%3EReplaced%20short%20code%20with%20this.%20%3Ca%20href%3D%22home%22%3Ehome%3C%2Fa%3E%3C%2Fp%3E', $field->RAWURLATT()); $this->assertEquals('<p>Replaced short code with this. <a href="home">home</a></p>', $field->ATT()); $this->assertEquals('<p>Replaced short code with this. <a href="home">home</a></p>', $field->RAW()); $this->assertEquals('\\x3cp\\x3eReplaced short code with this. \\x3ca href=\\"home\\"\\x3ehome\\x3c/a\\x3e\\x3c/p\\x3e', $field->JS()); $this->assertEquals('<p>Replaced short code with this. <a href="home">home</a></p>', $field->HTML()); $this->assertEquals('<p>Replaced short code with this. <a href="home">home</a></p>', $field->XML()); $this->assertEquals('Repl...', $field->LimitCharacters(4, '...')); $this->assertEquals('Replaced...', $field->LimitCharactersToClosestWord(10, '...')); $this->assertEquals('Replaced...', $field->LimitWordCount(1, '...')); $this->assertEquals('<p>replaced short code with this. <a href="home">home</a></p>', $field->LowerCase()); $this->assertEquals('<P>REPLACED SHORT CODE WITH THIS. <A HREF="HOME">HOME</A></P>', $field->UpperCase()); $this->assertEquals('Replaced short code with this. home', $field->Plain()); Config::nest(); Config::inst()->update('SilverStripe\\Control\\Director', 'alternate_base_url', 'http://example.com/'); $this->assertEquals('<p>Replaced short code with this. <a href="http://example.com/home">home</a></p>', $field->AbsoluteLinks()); Config::unnest(); $this->assertEquals('Replaced short code with this.', $field->LimitSentences(1)); $this->assertEquals('Replaced short code with this.', $field->FirstSentence()); $this->assertEquals('Replaced short...', $field->Summary(2)); $this->assertEquals('Replaced short code with this. home', $field->FirstParagraph()); $this->assertEquals('Replaced <span class="highlight">short</span> <span class="highlight">code</span> with this. home', $field->ContextSummary(500, 'short code')); ShortcodeParser::set_active('default'); }
/** * Test against a theme. * * @param string $themeBaseDir themes directory * @param string $theme Theme name * @param callable $callback * @throws Exception */ protected function useTestTheme($themeBaseDir, $theme, $callback) { Config::nest(); if (strpos($themeBaseDir, BASE_PATH) === 0) { $themeBaseDir = substr($themeBaseDir, strlen(BASE_PATH)); } SSViewer::config()->update('theme_enabled', true); SSViewer::set_themes([$themeBaseDir . '/themes/' . $theme, '$default']); $e = null; try { $callback(); } catch (Exception $e) { /* NOP for now, just save $e */ } Config::unnest(); if ($e) { throw $e; } }
/** * Returns a json array of a search results that can be used by for example Jquery.ui.autosuggestion * * @param GridField $gridField * @param HTTPRequest $request * @return string */ public function doSearch($gridField, $request) { $dataClass = $gridField->getModelClass(); $allList = $this->searchList ? $this->searchList : DataList::create($dataClass); $searchFields = $this->getSearchFields() ? $this->getSearchFields() : $this->scaffoldSearchFields($dataClass); if (!$searchFields) { throw new LogicException(sprintf('GridFieldAddExistingAutocompleter: No searchable fields could be found for class "%s"', $dataClass)); } $params = array(); foreach ($searchFields as $searchField) { $name = strpos($searchField, ':') !== FALSE ? $searchField : "{$searchField}:StartsWith"; $params[$name] = $request->getVar('gridfield_relationsearch'); } $results = $allList->subtract($gridField->getList())->filterAny($params)->sort(strtok($searchFields[0], ':'), 'ASC')->limit($this->getResultsLimit()); $json = array(); Config::nest(); SSViewer::config()->update('source_file_comments', false); $viewer = SSViewer::fromString($this->resultsFormat); foreach ($results as $result) { $title = html_entity_decode($viewer->process($result)); $json[] = array('label' => $title, 'value' => $title, 'id' => $result->ID); } Config::unnest(); return Convert::array2json($json); }
public function testPermissionFailureSetsCorrectFormMessages() { Config::nest(); // Controller that doesn't attempt redirections $controller = new SecurityTest_NullController(); $controller->setResponse(new HTTPResponse()); Security::permissionFailure($controller, array('default' => 'Oops, not allowed')); $this->assertEquals('Oops, not allowed', Session::get('Security.Message.message')); // Test that config values are used correctly Config::inst()->update('SilverStripe\\Security\\Security', 'default_message_set', 'stringvalue'); Security::permissionFailure($controller); $this->assertEquals('stringvalue', Session::get('Security.Message.message'), 'Default permission failure message value was not present'); Config::inst()->remove('SilverStripe\\Security\\Security', 'default_message_set'); Config::inst()->update('SilverStripe\\Security\\Security', 'default_message_set', array('default' => 'arrayvalue')); Security::permissionFailure($controller); $this->assertEquals('arrayvalue', Session::get('Security.Message.message'), 'Default permission failure message value was not present'); // Test that non-default messages work. // NOTE: we inspect the response body here as the session message has already // been fetched and output as part of it, so has been removed from the session $this->logInWithPermission('EDITOR'); Config::inst()->update('SilverStripe\\Security\\Security', 'default_message_set', array('default' => 'default', 'alreadyLoggedIn' => 'You are already logged in!')); Security::permissionFailure($controller); $this->assertContains('You are already logged in!', $controller->getResponse()->getBody(), 'Custom permission failure message was ignored'); Security::permissionFailure($controller, array('default' => 'default', 'alreadyLoggedIn' => 'One-off failure message')); $this->assertContains('One-off failure message', $controller->getResponse()->getBody(), "Message set passed to Security::permissionFailure() didn't override Config values"); Config::unnest(); }
/** * 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 HTTPResponse object. * * @uses Controller::handleRequest() 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 HTTPRequest $request The {@see SS_HTTP_Request} object generated as a part of this request. * * @return HTTPResponse * * @throws 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('SilverStripe\\Control\\Session', array()); } $cookieJar = $cookies instanceof Cookie_Backend ? $cookies : Injector::inst()->createWithArgs('SilverStripe\\Control\\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(); Cookie::config()->update('report_errors', false); Requirements::set_backend(Requirements_Backend::create()); // 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, 'SilverStripe\\Control\\Cookie_Backend'); $_SERVER['REQUEST_URI'] = Director::baseURL() . $urlWithQuerystring; $request = new 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('SilverStripe\\Control\\RequestProcessor')->preRequest($request, $session, $model); if ($output === false) { $onCleanup(); throw new 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 HTTPResponse object if (is_string($result)) { if (substr($result, 0, 9) == 'redirect:') { $response = new HTTPResponse(); $response->redirect(substr($result, 9)); $result = $response; } else { $result = new HTTPResponse($result); } } $output = Injector::inst()->get('SilverStripe\\Control\\RequestProcessor')->postRequest($request, $result, $model); if ($output === false) { $onCleanup(); throw new HTTPResponse_Exception("Invalid response"); } // Return valid response $onCleanup(); return $result; }
public function testNest() { // Check basic config $this->assertEquals(3, Config::inst()->get('ConfigTest_TestNest', 'foo')); $this->assertEquals(5, Config::inst()->get('ConfigTest_TestNest', 'bar')); // Test nest copies data Config::nest(); $this->assertEquals(3, Config::inst()->get('ConfigTest_TestNest', 'foo')); $this->assertEquals(5, Config::inst()->get('ConfigTest_TestNest', 'bar')); // Test nested data can be updated Config::inst()->update('ConfigTest_TestNest', 'foo', 4); $this->assertEquals(4, Config::inst()->get('ConfigTest_TestNest', 'foo')); $this->assertEquals(5, Config::inst()->get('ConfigTest_TestNest', 'bar')); // Test unnest restores data Config::unnest(); $this->assertEquals(3, Config::inst()->get('ConfigTest_TestNest', 'foo')); $this->assertEquals(5, Config::inst()->get('ConfigTest_TestNest', 'bar')); }
public function testGetAllowedMaxFileSize() { Config::nest(); // Check the max file size uses the config values $configMaxFileSizes = array('[image]' => '1k', 'txt' => 1000); Config::inst()->update('SilverStripe\\Assets\\Upload_Validator', 'default_max_file_size', $configMaxFileSizes); $v = new UploadTest_Validator(); $retrievedSize = $v->getAllowedMaxFileSize('[image]'); $this->assertEquals(1024, $retrievedSize, 'Max file size check on default values failed (config category set check)'); $retrievedSize = $v->getAllowedMaxFileSize('txt'); $this->assertEquals(1000, $retrievedSize, 'Max file size check on default values failed (config extension set check)'); // Check instance values for max file size $maxFileSizes = array('[document]' => 2000, 'txt' => '4k'); $v = new UploadTest_Validator(); $v->setAllowedMaxFileSize($maxFileSizes); $retrievedSize = $v->getAllowedMaxFileSize('[document]'); $this->assertEquals(2000, $retrievedSize, 'Max file size check on instance values failed (instance category set check)'); // Check that the instance values overwrote the default values // ie. The max file size will not exist for [image] $retrievedSize = $v->getAllowedMaxFileSize('[image]'); $this->assertFalse($retrievedSize, 'Max file size check on instance values failed (config overridden check)'); // Check a category that has not been set before $retrievedSize = $v->getAllowedMaxFileSize('[archive]'); $this->assertFalse($retrievedSize, 'Max file size check on instance values failed (category not set check)'); // Check a file extension that has not been set before $retrievedSize = $v->getAllowedMaxFileSize('mp3'); $this->assertFalse($retrievedSize, 'Max file size check on instance values failed (extension not set check)'); $retrievedSize = $v->getAllowedMaxFileSize('txt'); $this->assertEquals(4096, $retrievedSize, 'Max file size check on instance values failed (instance extension set check)'); // Check a wildcard max file size against a file with an extension $v = new UploadTest_Validator(); $v->setAllowedMaxFileSize(2000); $retrievedSize = $v->getAllowedMaxFileSize('.jpg'); $this->assertEquals(2000, $retrievedSize, 'Max file size check on instance values failed (wildcard max file size)'); Config::unnest(); }
public function testEnvironmentRules() { foreach (array('dev', 'test', 'live') as $env) { Config::nest(); Config::inst()->update('SilverStripe\\Control\\Director', 'environment_type', $env); $config = $this->getConfigFixtureValue('Environment'); foreach (array('dev', 'test', 'live') as $check) { $this->assertEquals($env == $check ? $check : 'not' . $check, @$config[ucfirst($check) . 'Environment'], 'Only & except rules correctly detect environment'); } Config::unnest(); } }
/** * @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('SilverStripe\\Assets\\File', 'update_filesystem', false); $this->invokeCallbacks('beforeCreate', array($identifier, &$data, &$fixtures)); try { $class = $this->class; $schema = DataObject::getSchema(); $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) { if ($schema->manyManyComponent($class, $fieldName) || $schema->hasManyComponent($class, $fieldName) || $schema->hasOneComponent($class, $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) { $isManyMany = $schema->manyManyComponent($class, $fieldName); $isHasMany = $schema->hasManyComponent($class, $fieldName); if ($isManyMany && $isHasMany) { throw new InvalidArgumentException("{$fieldName} is both many_many and has_many"); } if ($isManyMany || $isHasMany) { $obj->write(); // Many many components need a little extra work to extract extrafields if (is_array($fieldVal) && $isManyMany) { // handle lists of many_many relations. Each item can // specify the many_many_extraFields against each // related item. foreach ($fieldVal as $relVal) { // Check for many_many_extrafields $extrafields = []; if (is_array($relVal)) { // Item is either first row, or key in yet another nested array $item = key($relVal); if (is_array($relVal[$item]) && count($relVal) === 1) { // Extra fields from nested array $extrafields = $relVal[$item]; } else { // Extra fields from subsequent items array_shift($relVal); $extrafields = $relVal; } } else { $item = $relVal; } $id = $this->parseValue($item, $fixtures); $obj->getManyManyComponents($fieldName)->add($id, $extrafields); } } else { $items = is_array($fieldVal) ? $fieldVal : preg_split('/ *, */', trim($fieldVal)); $parsedItems = []; 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 ($isHasMany) { $obj->getComponents($fieldName)->setByIDList($parsedItems); } elseif ($isManyMany) { $obj->getManyManyComponents($fieldName)->setByIDList($parsedItems); } } } else { $hasOneField = preg_replace('/ID$/', '', $fieldName); if ($className = $schema->hasOneComponent($class, $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; }