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()); }