public function __destruct()
 {
     // Shift off anything else that's on the stack.  This can happen if something throws
     // an exception that causes a premature TestSession::__destruct() call
     while (Controller::has_curr() && Controller::curr() !== $this->controller) {
         Controller::curr()->popCurrent();
     }
     if (Controller::has_curr()) {
         $this->controller->popCurrent();
     }
 }
 public function __construct($url, File $file = null)
 {
     parent::__construct($url, $file);
     $this->embed = Embed::create($url);
     if (!$this->embed) {
         $controller = Controller::curr();
         $response = $controller->getResponse();
         $response->addHeader('X-Status', rawurlencode(_t('HTMLEditorField.URLNOTANOEMBEDRESOURCE', "The URL '{url}' could not be turned into a media resource.", "The given URL is not a valid Oembed resource; the embed element couldn't be created.", array('url' => $url))));
         $response->setStatusCode(404);
         throw new HTTPResponse_Exception($response);
     }
 }
 /**
  * Helper method for responding to a back action request
  * @param string $successMessage The message to return as a notification.
  * Can have up to two %d's in it. The first will be replaced by the number of successful
  * changes, the second by the number of failures
  * @param array $status A status array like batchactions builds. Should be
  * key => value pairs, the key can be any string: "error" indicates errors, anything
  * else indicates a type of success. The value is an array. We don't care what's in it,
  * we just use count($value) to find the number of items that succeeded or failed
  * @return string
  */
 public function response($successMessage, $status)
 {
     $count = 0;
     $errors = 0;
     foreach ($status as $k => $v) {
         switch ($k) {
             case 'error':
                 $errors += count($v);
                 break;
             case 'success':
                 $count += count($v);
                 break;
         }
     }
     $response = Controller::curr()->getResponse();
     if ($response) {
         $response->setStatusCode(200, sprintf($successMessage, $count, $errors));
     }
     return Convert::raw2json($status);
 }
 /**
  * Tests that the appropriate sortable headers are generated
  */
 public function testRenderHeaders()
 {
     // Generate sortable header and extract HTML
     $list = new DataList('GridFieldSortableHeaderTest_Team');
     $config = new GridFieldConfig_RecordEditor();
     /** @skipUpgrade */
     $form = new Form(Controller::curr(), 'Form', new FieldList(), new FieldList());
     $gridField = new GridField('testfield', 'testfield', $list, $config);
     $gridField->setForm($form);
     $compontent = $gridField->getConfig()->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldSortableHeader');
     $htmlFragment = $compontent->getHTMLFragments($gridField);
     // Check that the output shows name and hat as sortable fields, but not city
     $this->assertContains('<span class="non-sortable">City</span>', $htmlFragment['header']);
     $this->assertContains('value="Name" class="action grid-field__sort" id="action_SetOrderName"', $htmlFragment['header']);
     $this->assertContains('value="Cheerleader Hat" class="action grid-field__sort" id="action_SetOrderCheerleader-Hat-Colour"', $htmlFragment['header']);
     // Check inverse of above
     $this->assertNotContains('value="City" class="action grid-field__sort" id="action_SetOrderCity"', $htmlFragment['header']);
     $this->assertNotContains('<span class="non-sortable">Name</span>', $htmlFragment['header']);
     $this->assertNotContains('<span class="non-sortable">Cheerleader Hat</span>', $htmlFragment['header']);
 }
 /**
  * @param array $record
  * @return bool
  */
 protected function write(array $record)
 {
     ini_set('display_errors', 0);
     // TODO: This coupling isn't ideal
     // See https://github.com/silverstripe/silverstripe-framework/issues/4484
     if (Controller::has_curr()) {
         $response = Controller::curr()->getResponse();
     } else {
         $response = new HTTPResponse();
     }
     // If headers have been sent then these won't be used, and may throw errors that we wont' want to see.
     if (!headers_sent()) {
         $response->setStatusCode($this->statusCode);
         $response->addHeader("Content-Type", $this->contentType);
     } else {
         // To supress errors aboot errors
         $response->setStatusCode(200);
     }
     $response->setBody($record['formatted']);
     $response->output();
     return false === $this->bubble;
 }
 /**
  * Test a submission of this form.
  * @param string $action
  * @param array $data
  * @return HTTPResponse the response object that the handling controller produces.  You can interrogate this in
  * your unit test.
  * @throws HTTPResponse_Exception
  */
 public function testSubmission($action, $data)
 {
     $data['action_' . $action] = true;
     return Director::test($this->FormAction(), $data, Controller::curr()->getSession());
 }
 /**
  * Checks if we're on a controller where we should filter. ie. Are we loading the SiteTree?
  *
  * @return bool
  */
 public function showingCMSTree()
 {
     if (!Controller::has_curr()) {
         return false;
     }
     $controller = Controller::curr();
     return $controller instanceof LeftAndMain && in_array($controller->getAction(), array("treeview", "listview", "getsubtree"));
 }
 public function tearDown()
 {
     // Preserve memory settings
     ini_set('memory_limit', $this->originalMemoryLimit ? $this->originalMemoryLimit : -1);
     // Restore email configuration
     $this->mailer = null;
     // Restore password validation
     if ($this->originalMemberPasswordValidator) {
         Member::set_password_validator($this->originalMemberPasswordValidator);
     }
     // Restore requirements
     if ($this->originalRequirements) {
         Requirements::set_backend($this->originalRequirements);
     }
     // Mark test as no longer being run - we use originalIsRunningTest to allow for nested SapphireTest calls
     self::$is_running_test = $this->originalIsRunningTest;
     $this->originalIsRunningTest = null;
     // Reset mocked datetime
     DBDatetime::clear_mock_now();
     // Stop the redirection that might have been requested in the test.
     // Note: Ideally a clean Controller should be created for each test.
     // Now all tests executed in a batch share the same controller.
     $controller = Controller::has_curr() ? Controller::curr() : null;
     if ($controller && ($response = $controller->getResponse()) && $response->getHeader('Location')) {
         $response->setStatusCode(200);
         $response->removeHeader('Location');
     }
     Versioned::set_reading_mode($this->originalReadingMode);
     //unnest injector / config now that tests are over
     Injector::unnest();
     Config::unnest();
 }
 /**
  * Register that we've had a permission failure trying to view the given page
  *
  * This will redirect to a login page.
  * If you don't provide a messageSet, a default will be used.
  *
  * @param Controller $controller The controller that you were on to cause the permission
  *                               failure.
  * @param string|array $messageSet The message to show to the user. This
  *                                 can be a string, or a map of different
  *                                 messages for different contexts.
  *                                 If you pass an array, you can use the
  *                                 following keys:
  *                                   - default: The default message
  *                                   - alreadyLoggedIn: The message to
  *                                                      show if the user
  *                                                      is already logged
  *                                                      in and lacks the
  *                                                      permission to
  *                                                      access the item.
  *
  * The alreadyLoggedIn value can contain a '%s' placeholder that will be replaced with a link
  * to log in.
  * @return HTTPResponse
  */
 public static function permissionFailure($controller = null, $messageSet = null)
 {
     self::set_ignore_disallowed_actions(true);
     if (!$controller) {
         $controller = Controller::curr();
     }
     if (Director::is_ajax()) {
         $response = $controller ? $controller->getResponse() : new HTTPResponse();
         $response->setStatusCode(403);
         if (!Member::currentUser()) {
             $response->setBody(_t('ContentController.NOTLOGGEDIN', 'Not logged in'));
             $response->setStatusDescription(_t('ContentController.NOTLOGGEDIN', 'Not logged in'));
             // Tell the CMS to allow re-aunthentication
             if (CMSSecurity::enabled()) {
                 $response->addHeader('X-Reauthenticate', '1');
             }
         }
         return $response;
     }
     // Prepare the messageSet provided
     if (!$messageSet) {
         if ($configMessageSet = static::config()->get('default_message_set')) {
             $messageSet = $configMessageSet;
         } else {
             $messageSet = array('default' => _t('Security.NOTEPAGESECURED', "That page is secured. Enter your credentials below and we will send " . "you right along."), 'alreadyLoggedIn' => _t('Security.ALREADYLOGGEDIN', "You don't have access to this page.  If you have another account that " . "can access that page, you can log in again below.", "%s will be replaced with a link to log in."));
         }
     }
     if (!is_array($messageSet)) {
         $messageSet = array('default' => $messageSet);
     }
     $member = Member::currentUser();
     // Work out the right message to show
     if ($member && $member->exists()) {
         $response = $controller ? $controller->getResponse() : new HTTPResponse();
         $response->setStatusCode(403);
         //If 'alreadyLoggedIn' is not specified in the array, then use the default
         //which should have been specified in the lines above
         if (isset($messageSet['alreadyLoggedIn'])) {
             $message = $messageSet['alreadyLoggedIn'];
         } else {
             $message = $messageSet['default'];
         }
         // Somewhat hackish way to render a login form with an error message.
         $me = new Security();
         $form = $me->LoginForm();
         $form->sessionMessage($message, 'warning');
         Session::set('MemberLoginForm.force_message', 1);
         $loginResponse = $me->login();
         if ($loginResponse instanceof HTTPResponse) {
             return $loginResponse;
         }
         $response->setBody((string) $loginResponse);
         $controller->extend('permissionDenied', $member);
         return $response;
     } else {
         $message = $messageSet['default'];
     }
     Session::set("Security.Message.message", $message);
     Session::set("Security.Message.type", 'warning');
     Session::set("BackURL", $_SERVER['REQUEST_URI']);
     // TODO AccessLogEntry needs an extension to handle permission denied errors
     // Audit logging hook
     $controller->extend('permissionDenied', $member);
     return $controller->redirect(Config::inst()->get('SilverStripe\\Security\\Security', 'login_url') . "?BackURL=" . urlencode($_SERVER['REQUEST_URI']));
 }
 /**
  * Checks if the current HTTP-Request is an "Ajax-Request" by checking for a custom header set by
  * jQuery or whether a manually set request-parameter 'ajax' is present.
  *
  * @return bool
  */
 public static function is_ajax()
 {
     if (Controller::has_curr()) {
         return Controller::curr()->getRequest()->isAjax();
     } else {
         return isset($_REQUEST['ajax']) || isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == "XMLHttpRequest";
     }
 }
 /**
  * Log login attempt
  * TODO We could handle this with an extension
  *
  * @param array $data
  * @param Member $member
  * @param bool $success
  */
 protected static function record_login_attempt($data, $member, $success)
 {
     if (!Security::config()->login_recording) {
         return;
     }
     // Check email is valid
     /** @skipUpgrade */
     $email = isset($data['Email']) ? $data['Email'] : null;
     if (is_array($email)) {
         throw new InvalidArgumentException("Bad email passed to MemberAuthenticator::authenticate(): {$email}");
     }
     $attempt = new LoginAttempt();
     if ($success) {
         // successful login (member is existing with matching password)
         $attempt->MemberID = $member->ID;
         $attempt->Status = 'Success';
         // Audit logging hook
         $member->extend('authenticated');
     } else {
         // Failed login - we're trying to see if a user exists with this email (disregarding wrong passwords)
         $attempt->Status = 'Failure';
         if ($member) {
             // Audit logging hook
             $attempt->MemberID = $member->ID;
             $member->extend('authenticationFailed');
         } else {
             // Audit logging hook
             Member::singleton()->extend('authenticationFailedUnknownUser', $data);
         }
     }
     $attempt->Email = $email;
     $attempt->IP = Controller::curr()->getRequest()->getIP();
     $attempt->write();
 }
 public function __construct()
 {
     parent::__construct(Controller::curr(), __CLASS__, new FieldList(new TextField('Email'), new TextField('Surname'), new TextField('ID'), new TextField('FirstName')), new FieldList(new FormAction('someAction')));
 }
 /**
  * Login in the user and figure out where to redirect the browser.
  *
  * The $data has this format
  * array(
  *   'AuthenticationMethod' => 'MemberAuthenticator',
  *   'Email' => '*****@*****.**',
  *   'Password' => '1nitialPassword',
  *   'BackURL' => 'test/link',
  *   [Optional: 'Remember' => 1 ]
  * )
  *
  * @param array $data
  * @return HTTPResponse
  */
 protected function logInUserAndRedirect($data)
 {
     Session::clear('SessionForms.MemberLoginForm.Email');
     Session::clear('SessionForms.MemberLoginForm.Remember');
     if (Member::currentUser()->isPasswordExpired()) {
         if (isset($_REQUEST['BackURL']) && ($backURL = $_REQUEST['BackURL'])) {
             Session::set('BackURL', $backURL);
         }
         /** @skipUpgrade */
         $cp = ChangePasswordForm::create($this->controller, 'ChangePasswordForm');
         $cp->sessionMessage(_t('Member.PASSWORDEXPIRED', 'Your password has expired. Please choose a new one.'), 'good');
         return $this->controller->redirect('Security/changepassword');
     }
     // Absolute redirection URLs may cause spoofing
     if (!empty($_REQUEST['BackURL'])) {
         $url = $_REQUEST['BackURL'];
         if (Director::is_site_url($url)) {
             $url = Director::absoluteURL($url);
         } else {
             // Spoofing attack, redirect to homepage instead of spoofing url
             $url = Director::absoluteBaseURL();
         }
         return $this->controller->redirect($url);
     }
     // If a default login dest has been set, redirect to that.
     if ($url = Security::config()->default_login_dest) {
         $url = Controller::join_links(Director::absoluteBaseURL(), $url);
         return $this->controller->redirect($url);
     }
     // Redirect the user to the page where they came from
     $member = Member::currentUser();
     if ($member) {
         $firstname = Convert::raw2xml($member->FirstName);
         if (!empty($data['Remember'])) {
             Session::set('SessionForms.MemberLoginForm.Remember', '1');
             $member->logIn(true);
         } else {
             $member->logIn();
         }
         Session::set('Security.Message.message', _t('Member.WELCOMEBACK', "Welcome Back, {firstname}", array('firstname' => $firstname)));
         Session::set("Security.Message.type", "good");
     }
     return Controller::curr()->redirectBack();
 }
 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;
     }
 }
 /**
  * Get all menu items that the passed member can view.
  * Defaults to {@link Member::currentUser()}.
  *
  * @param Member $member
  * @return array
  */
 public static function get_viewable_menu_items($member = null)
 {
     if (!$member && $member !== FALSE) {
         $member = Member::currentUser();
     }
     $viewableMenuItems = array();
     $allMenuItems = self::get_menu_items();
     if ($allMenuItems) {
         foreach ($allMenuItems as $code => $menuItem) {
             // exclude all items which have a controller to perform permission
             // checks on
             if ($menuItem->controller) {
                 $controllerObj = singleton($menuItem->controller);
                 if (Controller::has_curr()) {
                     // Necessary for canView() to have request data available,
                     // e.g. to check permissions against LeftAndMain->currentPage()
                     $controllerObj->setRequest(Controller::curr()->getRequest());
                     if (!$controllerObj->canView($member)) {
                         continue;
                     }
                 }
             }
             $viewableMenuItems[$code] = $menuItem;
         }
     }
     return $viewableMenuItems;
 }
 /**
  * Output the feed to the browser.
  *
  * TODO: Pass $response object to ->outputToBrowser() to loosen dependence on global state for easier testing/prototyping so dev can inject custom HTTPResponse instance.
  *
  * @return DBHTMLText
  */
 public function outputToBrowser()
 {
     $prevState = SSViewer::config()->get('source_file_comments');
     SSViewer::config()->update('source_file_comments', false);
     $response = Controller::curr()->getResponse();
     if (is_int($this->lastModified)) {
         HTTP::register_modification_timestamp($this->lastModified);
         $response->addHeader("Last-Modified", gmdate("D, d M Y H:i:s", $this->lastModified) . ' GMT');
     }
     if (!empty($this->etag)) {
         HTTP::register_etag($this->etag);
     }
     if (!headers_sent()) {
         HTTP::add_cache_headers();
         $response->addHeader("Content-Type", "application/rss+xml; charset=utf-8");
     }
     SSViewer::config()->update('source_file_comments', $prevState);
     return $this->renderWith($this->getTemplates());
 }