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('&lt;p&gt;Replaced short code with this. &lt;a href=&quot;home&quot;&gt;home&lt;/a&gt;&lt;/p&gt;', $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('&lt;p&gt;Replaced short code with this. &lt;a href=&quot;home&quot;&gt;home&lt;/a&gt;&lt;/p&gt;', $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('&lt;p&gt;Replaced short code with this. &lt;a href=&quot;home&quot;&gt;home&lt;/a&gt;&lt;/p&gt;', $field->HTML());
     $this->assertEquals('&lt;p&gt;Replaced short code with this. &lt;a href=&quot;home&quot;&gt;home&lt;/a&gt;&lt;/p&gt;', $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;
 }