Merge pull request #1078

This commit is contained in:
Ingo Schommer 2013-01-29 18:04:15 +01:00
commit 6cb1570282
20 changed files with 237 additions and 67 deletions

View File

@ -41,6 +41,7 @@
* *
* Email: * Email:
* - SS_SEND_ALL_EMAILS_TO: If you set this define, all emails will be redirected to this address. * - SS_SEND_ALL_EMAILS_TO: If you set this define, all emails will be redirected to this address.
* - SS_SEND_ALL_EMAILS_FROM: If you set this define, all emails will be send from this address.
* *
* @package framework * @package framework
* @subpackage core * @subpackage core
@ -105,7 +106,10 @@ if(defined('SS_DATABASE_USERNAME') && defined('SS_DATABASE_PASSWORD')) {
} }
if(defined('SS_SEND_ALL_EMAILS_TO')) { if(defined('SS_SEND_ALL_EMAILS_TO')) {
Email::send_all_emails_to(SS_SEND_ALL_EMAILS_TO); Config::inst()->update("Email","send_all_emails_to", SS_SEND_ALL_EMAILS_TO);
}
if(defined('SS_SEND_ALL_EMAILS_FROM')) {
Config::inst()->update("Email","send_all_emails_from", SS_SEND_ALL_EMAILS_FROM);
} }
if(defined('SS_DEFAULT_ADMIN_USERNAME')) { if(defined('SS_DEFAULT_ADMIN_USERNAME')) {

View File

@ -454,7 +454,7 @@ class Controller extends RequestHandler implements TemplateGlobalProvider {
public function redirect($url, $code=302) { public function redirect($url, $code=302) {
if(!$this->response) $this->response = new SS_HTTPResponse(); if(!$this->response) $this->response = new SS_HTTPResponse();
if($this->response->getHeader('Location')) { if($this->response->getHeader('Location') && $this->response->getHeader('Location') != $url) {
user_error("Already directed to " . $this->response->getHeader('Location') user_error("Already directed to " . $this->response->getHeader('Location')
. "; now trying to direct to $url", E_USER_WARNING); . "; now trying to direct to $url", E_USER_WARNING);
return; return;

View File

@ -725,6 +725,9 @@ class Director implements TemplateGlobalProvider {
$matched = false; $matched = false;
if($patterns) { if($patterns) {
// Calling from the command-line?
if(!isset($_SERVER['REQUEST_URI'])) return;
// protect portions of the site based on the pattern // protect portions of the site based on the pattern
$relativeURL = self::makeRelative(Director::absoluteURL($_SERVER['REQUEST_URI'])); $relativeURL = self::makeRelative(Director::absoluteURL($_SERVER['REQUEST_URI']));
foreach($patterns as $pattern) { foreach($patterns as $pattern) {

View File

@ -220,11 +220,17 @@ class SS_HTTPResponse {
<meta http-equiv=\"refresh\" content=\"1; url=$url\" /> <meta http-equiv=\"refresh\" content=\"1; url=$url\" />
<script type=\"text/javascript\">setTimeout('window.location.href = \"$url\"', 50);</script>"; <script type=\"text/javascript\">setTimeout('window.location.href = \"$url\"', 50);</script>";
} else { } else {
if(!headers_sent()) { $line = $file = null;
if(!headers_sent($file, $line)) {
header($_SERVER['SERVER_PROTOCOL'] . " $this->statusCode " . $this->getStatusDescription()); header($_SERVER['SERVER_PROTOCOL'] . " $this->statusCode " . $this->getStatusDescription());
foreach($this->headers as $header => $value) { foreach($this->headers as $header => $value) {
header("$header: $value", true, $this->statusCode); header("$header: $value", true, $this->statusCode);
} }
} else {
// It's critical that these status codes are sent; we need to report a failure if not.
if($this->statusCode >= 300) {
user_error("Couldn't set response type to $this->statusCode because of output on line $line of $file", E_USER_WARNING);
}
} }
// Only show error pages or generic "friendly" errors if the status code signifies // Only show error pages or generic "friendly" errors if the status code signifies

View File

@ -415,7 +415,7 @@ class Session {
protected function recursivelyApply($data, &$dest) { protected function recursivelyApply($data, &$dest) {
foreach($data as $k => $v) { foreach($data as $k => $v) {
if(is_array($v)) { if(is_array($v)) {
if(!isset($dest[$k])) $dest[$k] = array(); if(!isset($dest[$k]) || !is_array($dest[$k])) $dest[$k] = array();
$this->recursivelyApply($v, $dest[$k]); $this->recursivelyApply($v, $dest[$k]);
} else { } else {
$dest[$k] = $v; $dest[$k] = $v;

View File

@ -213,6 +213,7 @@ class Debug {
public static function noticeHandler($errno, $errstr, $errfile, $errline, $errcontext) { public static function noticeHandler($errno, $errstr, $errfile, $errline, $errcontext) {
if(error_reporting() == 0) return; if(error_reporting() == 0) return;
ini_set('display_errors', 0);
// Send out the error details to the logger for writing // Send out the error details to the logger for writing
SS_Log::log( SS_Log::log(
@ -227,7 +228,9 @@ class Debug {
); );
if(Director::isDev()) { if(Director::isDev()) {
self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Notice"); return self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Notice");
} else {
return false;
} }
} }
@ -242,8 +245,10 @@ class Debug {
*/ */
public static function warningHandler($errno, $errstr, $errfile, $errline, $errcontext) { public static function warningHandler($errno, $errstr, $errfile, $errline, $errcontext) {
if(error_reporting() == 0) return; if(error_reporting() == 0) return;
ini_set('display_errors', 0);
if(self::$send_warnings_to) { if(self::$send_warnings_to) {
self::emailError(self::$send_warnings_to, $errno, $errstr, $errfile, $errline, $errcontext, "Warning"); return self::emailError(self::$send_warnings_to, $errno, $errstr, $errfile, $errline, $errcontext, "Warning");
} }
// Send out the error details to the logger for writing // Send out the error details to the logger for writing
@ -263,7 +268,9 @@ class Debug {
} }
if(Director::isDev()) { if(Director::isDev()) {
self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Warning"); return self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Warning");
} else {
return false;
} }
} }
@ -279,6 +286,8 @@ class Debug {
* @param unknown_type $errcontext * @param unknown_type $errcontext
*/ */
public static function fatalHandler($errno, $errstr, $errfile, $errline, $errcontext) { public static function fatalHandler($errno, $errstr, $errfile, $errline, $errcontext) {
ini_set('display_errors', 0);
if(self::$send_errors_to) { if(self::$send_errors_to) {
self::emailError(self::$send_errors_to, $errno, $errstr, $errfile, $errline, $errcontext, "Error"); self::emailError(self::$send_errors_to, $errno, $errstr, $errfile, $errline, $errcontext, "Error");
} }
@ -300,11 +309,10 @@ class Debug {
} }
if(Director::isDev() || Director::is_cli()) { if(Director::isDev() || Director::is_cli()) {
self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Error"); return self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Error");
} else { } else {
self::friendlyError(); return self::friendlyError();
} }
exit(1);
} }
/** /**
@ -363,6 +371,7 @@ class Debug {
$renderer->writeFooter(); $renderer->writeFooter();
} }
} }
return false;
} }
/** /**
@ -652,7 +661,7 @@ class Debug {
$_SESSION['Security']['Message']['type'] = 'warning'; $_SESSION['Security']['Message']['type'] = 'warning';
$_SESSION['BackURL'] = $_SERVER['REQUEST_URI']; $_SESSION['BackURL'] = $_SERVER['REQUEST_URI'];
header($_SERVER['SERVER_PROTOCOL'] . " 302 Found"); header($_SERVER['SERVER_PROTOCOL'] . " 302 Found");
header("Location: " . Director::baseURL() . "Security/login"); header("Location: " . Director::baseURL() . Security::login_url());
die(); die();
} }
} }
@ -679,7 +688,7 @@ function exceptionHandler($exception) {
$file = $exception->getFile(); $file = $exception->getFile();
$line = $exception->getLine(); $line = $exception->getLine();
$context = $exception->getTrace(); $context = $exception->getTrace();
Debug::fatalHandler($errno, $message, $file, $line, $context); return Debug::fatalHandler($errno, $message, $file, $line, $context);
} }
/** /**
@ -698,21 +707,18 @@ function errorHandler($errno, $errstr, $errfile, $errline) {
case E_ERROR: case E_ERROR:
case E_CORE_ERROR: case E_CORE_ERROR:
case E_USER_ERROR: case E_USER_ERROR:
Debug::fatalHandler($errno, $errstr, $errfile, $errline, debug_backtrace()); return Debug::fatalHandler($errno, $errstr, $errfile, $errline, debug_backtrace());
break;
case E_WARNING: case E_WARNING:
case E_CORE_WARNING: case E_CORE_WARNING:
case E_USER_WARNING: case E_USER_WARNING:
Debug::warningHandler($errno, $errstr, $errfile, $errline, debug_backtrace()); return Debug::warningHandler($errno, $errstr, $errfile, $errline, debug_backtrace());
break;
case E_NOTICE: case E_NOTICE:
case E_USER_NOTICE: case E_USER_NOTICE:
case E_DEPRECATED: case E_DEPRECATED:
case E_USER_DEPRECATED: case E_USER_DEPRECATED:
case E_STRICT: case E_STRICT:
Debug::noticeHandler($errno, $errstr, $errfile, $errline, debug_backtrace()); return Debug::noticeHandler($errno, $errstr, $errfile, $errline, debug_backtrace());
break;
} }
} }

View File

@ -205,8 +205,16 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
$className = get_class($this); $className = get_class($this);
$fixtureFile = eval("return {$className}::\$fixture_file;"); $fixtureFile = eval("return {$className}::\$fixture_file;");
$prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_'; $prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_';
// Set up email
$this->originalMailer = Email::mailer();
$this->mailer = new TestMailer();
Email::set_mailer($this->mailer);
Config::inst()->remove('Email', 'send_all_emails_to');
Email::send_all_emails_to(null);
// Todo: this could be a special test model // Todo: this could be a special test model
$this->model = DataModel::inst(); $this->model = DataModel::inst();
@ -259,12 +267,6 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
$this->logInWithPermission("ADMIN"); $this->logInWithPermission("ADMIN");
} }
// Set up email
$this->originalMailer = Email::mailer();
$this->mailer = new TestMailer();
Email::set_mailer($this->mailer);
Email::send_all_emails_to(null);
// Preserve memory settings // Preserve memory settings
$this->originalMemoryLimit = ini_get('memory_limit'); $this->originalMemoryLimit = ini_get('memory_limit');

View File

@ -96,8 +96,12 @@ class TestSession {
$form->setField(new SimpleByName($k), $v); $form->setField(new SimpleByName($k), $v);
} }
if($button) $submission = $form->submitButton(new SimpleByName($button)); if($button) {
else $submission = $form->submit(); $submission = $form->submitButton(new SimpleByName($button));
if(!$submission) throw new Exception("Can't find button '$button' to submit as part of test.");
} else {
$submission = $form->submit();
}
$url = Director::makeRelative($form->getAction()->asString()); $url = Director::makeRelative($form->getAction()->asString());
@ -137,6 +141,15 @@ class TestSession {
return $this->lastResponse; return $this->lastResponse;
} }
/**
* Return the fake HTTP_REFERER; set each time get() or post() is called.
*
* @return string
*/
public function lastUrl() {
return $this->lastUrl;
}
/** /**
* Get the most recent response's content * Get the most recent response's content
*/ */

View File

@ -125,16 +125,54 @@ class Email extends ViewableData {
static $admin_email_address = ''; static $admin_email_address = '';
/** /**
* Send every email generated by the Email class to the given address.
*
* It will also add " [addressed to (email), cc to (email), bcc to (email)]" to the end of the subject line
*
* To set this, set Email.send_all_emails_to in your yml config file.
* It can also be set in _ss_environment.php with SS_SEND_ALL_EMAILS_TO.
*
* @param string $send_all_emails_to Email-Address * @param string $send_all_emails_to Email-Address
*/ */
protected static $send_all_emails_to = null; protected static $send_all_emails_to = null;
/** /**
* Send every email generated by the Email class *from* the given address.
* It will also add " [, from to (email)]" to the end of the subject line
*
* To set this, set Email.send_all_emails_from in your yml config file.
* It can also be set in _ss_environment.php with SS_SEND_ALL_EMAILS_FROM.
*
* @param string $send_all_emails_from Email-Address
*/
protected static $send_all_emails_from = null;
/**
* BCC every email generated by the Email class to the given address.
* It won't affect the original delivery in the same way that send_all_emails_to does. It just adds a BCC header
* with the given email address. Note that you can only call this once - subsequent calls will overwrite the
* configuration variable.
*
* This can be used when you have a system that relies heavily on email and you want someone to be checking all
* correspondence.
*
* To set this, set Email.bcc_all_emails_to in your yml config file.
*
* @param string $bcc_all_emails_to Email-Address * @param string $bcc_all_emails_to Email-Address
*/ */
protected static $bcc_all_emails_to = null; protected static $bcc_all_emails_to = null;
/** /**
* CC every email generated by the Email class to the given address.
* It won't affect the original delivery in the same way that send_all_emails_to does. It just adds a CC header
* with the given email address. Note that you can only call this once - subsequent calls will overwrite the
* configuration variable.
*
* This can be used when you have a system that relies heavily on email and you want someone to be checking all
* correspondence.
*
* To set this, set Email.cc_all_emails_to in your yml config file.
*
* @param string $cc_all_emails_to Email-Address * @param string $cc_all_emails_to Email-Address
*/ */
protected static $cc_all_emails_to = null; protected static $cc_all_emails_to = null;
@ -399,37 +437,45 @@ class Email extends ViewableData {
if(project()) $headers['X-SilverStripeSite'] = project(); if(project()) $headers['X-SilverStripeSite'] = project();
$to = $this->to; $to = $this->to;
$from = $this->from;
$subject = $this->subject; $subject = $this->subject;
if(self::$send_all_emails_to) { if($sendAllTo = $this->config()->send_all_emails_to) {
$subject .= " [addressed to $to"; $subject .= " [addressed to $to";
$to = self::$send_all_emails_to; $to = $sendAllTo;
if($this->cc) $subject .= ", cc to $this->cc"; if($this->cc) $subject .= ", cc to $this->cc";
if($this->bcc) $subject .= ", bcc to $this->bcc"; if($this->bcc) $subject .= ", bcc to $this->bcc";
$subject .= ']'; $subject .= ']';
unset($headers['Cc']);
unset($headers['Bcc']);
} else { } else {
if($this->cc) $headers['Cc'] = $this->cc; if($this->cc) $headers['Cc'] = $this->cc;
if($this->bcc) $headers['Bcc'] = $this->bcc; if($this->bcc) $headers['Bcc'] = $this->bcc;
} }
if(self::$cc_all_emails_to) { if($ccAllTo = $this->config()->cc_all_emails_to) {
if(!empty($headers['Cc']) && trim($headers['Cc'])) { if(!empty($headers['Cc']) && trim($headers['Cc'])) {
$headers['Cc'] .= ', ' . self::$cc_all_emails_to; $headers['Cc'] .= ', ' . $ccAllTo;
} else { } else {
$headers['Cc'] = self::$cc_all_emails_to; $headers['Cc'] = $ccAllTo;
} }
} }
if(self::$bcc_all_emails_to) { if($bccAllTo = $this->config()->bcc_all_emails_to) {
if(!empty($headers['Bcc']) && trim($headers['Bcc'])) { if(!empty($headers['Bcc']) && trim($headers['Bcc'])) {
$headers['Bcc'] .= ', ' . self::$bcc_all_emails_to; $headers['Bcc'] .= ', ' . $bccAllTo;
} else { } else {
$headers['Bcc'] = self::$bcc_all_emails_to; $headers['Bcc'] = $bccAllTo;
} }
} }
if($sendAllfrom = $this->config()->send_all_emails_from) {
if($from) $subject .= " [from $from]";
$from = $sendAllfrom;
}
Requirements::restore(); Requirements::restore();
return self::mailer()->sendPlain($to, $this->from, $subject, $this->body, $this->attachments, $headers); return self::mailer()->sendPlain($to, $from, $subject, $this->body, $this->attachments, $headers);
} }
/** /**
@ -459,40 +505,49 @@ class Email extends ViewableData {
if(project()) $headers['X-SilverStripeSite'] = project(); if(project()) $headers['X-SilverStripeSite'] = project();
$to = $this->to; $to = $this->to;
$from = $this->from;
$subject = $this->subject; $subject = $this->subject;
if(self::$send_all_emails_to) { if($sendAllTo = $this->config()->send_all_emails_to) {
$subject .= " [addressed to $to"; $subject .= " [addressed to $to";
$to = self::$send_all_emails_to; $to = $sendAllTo;
if($this->cc) $subject .= ", cc to $this->cc"; if($this->cc) $subject .= ", cc to $this->cc";
if($this->bcc) $subject .= ", bcc to $this->bcc"; if($this->bcc) $subject .= ", bcc to $this->bcc";
$subject .= ']'; $subject .= ']';
unset($headers['Cc']); unset($headers['Cc']);
unset($headers['Bcc']); unset($headers['Bcc']);
} else { } else {
if($this->cc) $headers['Cc'] = $this->cc; if($this->cc) $headers['Cc'] = $this->cc;
if($this->bcc) $headers['Bcc'] = $this->bcc; if($this->bcc) $headers['Bcc'] = $this->bcc;
} }
if(self::$cc_all_emails_to) {
if($ccAllTo = $this->config()->cc_all_emails_to) {
if(!empty($headers['Cc']) && trim($headers['Cc'])) { if(!empty($headers['Cc']) && trim($headers['Cc'])) {
$headers['Cc'] .= ', ' . self::$cc_all_emails_to; $headers['Cc'] .= ', ' . $ccAllTo;
} else { } else {
$headers['Cc'] = self::$cc_all_emails_to; $headers['Cc'] = $ccAllTo;
} }
} }
if(self::$bcc_all_emails_to) { if($bccAllTo = $this->config()->bcc_all_emails_to) {
if(!empty($headers['Bcc']) && trim($headers['Bcc'])) { if(!empty($headers['Bcc']) && trim($headers['Bcc'])) {
$headers['Bcc'] .= ', ' . self::$bcc_all_emails_to; $headers['Bcc'] .= ', ' . $bccAllTo;
} else { } else {
$headers['Bcc'] = self::$bcc_all_emails_to; $headers['Bcc'] = $bccAllTo;
} }
} }
if($sendAllfrom = $this->config()->send_all_emails_from) {
if($from) $subject .= " [from $from]";
$from = $sendAllfrom;
}
Requirements::restore(); Requirements::restore();
return self::mailer()->sendHTML($to, $this->from, $subject, $this->body, $this->attachments, $headers, return self::mailer()->sendHTML($to, $from, $subject, $this->body, $this->attachments, $headers,
$this->plaintext_body); $this->plaintext_body);
} }

View File

@ -291,7 +291,7 @@ function encodeMultipart($parts, $contentType, $headers = false) {
*/ */
function wrapImagesInline($htmlContent) { function wrapImagesInline($htmlContent) {
global $_INLINED_IMAGES; global $_INLINED_IMAGES;
$_INLINED_IMAGES = null; $_INLINED_IMAGES = array();
$replacedContent = imageRewriter($htmlContent, 'wrapImagesInline_rewriter($URL)'); $replacedContent = imageRewriter($htmlContent, 'wrapImagesInline_rewriter($URL)');
@ -303,8 +303,8 @@ function wrapImagesInline($htmlContent) {
// Make all the image parts // Make all the image parts
global $_INLINED_IMAGES; global $_INLINED_IMAGES;
foreach($_INLINED_IMAGES as $url => $cid) { if($_INLINED_IMAGES) foreach($_INLINED_IMAGES as $url => $cid) {
$multiparts[] = encodeFileForEmail($url, false, "inline", "Content-ID: <$cid>\n"); $multiparts[] = encodeFileForEmail(BASE_PATH . '/' . $url, false, "inline", "Content-ID: <$cid>\n");
} }
// Merge together in a multipart // Merge together in a multipart
@ -312,10 +312,10 @@ function wrapImagesInline($htmlContent) {
return processHeaders($headers, $body); return processHeaders($headers, $body);
} }
function wrapImagesInline_rewriter($url) { function wrapImagesInline_rewriter($url) {
$url = relativiseURL($url); $url = Director::makeRelative($url);
global $_INLINED_IMAGES; global $_INLINED_IMAGES;
if(!$_INLINED_IMAGES[$url]) { if(!isset($_INLINED_IMAGES[$url])) {
$identifier = "automatedmessage." . rand(1000,1000000000) . "@silverstripe.com"; $identifier = "automatedmessage." . rand(1000,1000000000) . "@silverstripe.com";
$_INLINED_IMAGES[$url] = $identifier; $_INLINED_IMAGES[$url] = $identifier;
} }
@ -384,6 +384,7 @@ function encodeFileForEmail($file, $destFileName = false, $disposition = NULL, $
$file = array('filename' => $file); $file = array('filename' => $file);
$fh = fopen($file['filename'], "rb"); $fh = fopen($file['filename'], "rb");
if ($fh) { if ($fh) {
$file['contents'] = "";
while(!feof($fh)) $file['contents'] .= fread($fh, 10000); while(!feof($fh)) $file['contents'] .= fread($fh, 10000);
fclose($fh); fclose($fh);
} }
@ -393,12 +394,12 @@ function encodeFileForEmail($file, $destFileName = false, $disposition = NULL, $
if(!$destFileName) $base = basename($file['filename']); if(!$destFileName) $base = basename($file['filename']);
else $base = $destFileName; else $base = $destFileName;
$mimeType = $file['mimetype'] ? $file['mimetype'] : HTTP::get_mime_type($file['filename']); $mimeType = !empty($file['mimetype']) ? $file['mimetype'] : HTTP::get_mime_type($file['filename']);
if(!$mimeType) $mimeType = "application/unknown"; if(!$mimeType) $mimeType = "application/unknown";
if (empty($disposition)) $disposition = isset($file['contentLocation']) ? 'inline' : 'attachment'; if (empty($disposition)) $disposition = isset($file['contentLocation']) ? 'inline' : 'attachment';
// Encode for emailing // Encode for emailing
if (substr($file['mimetype'], 0, 4) != 'text') { if (substr($mimeType, 0, 4) != 'text') {
$encoding = "base64"; $encoding = "base64";
$file['contents'] = chunk_split(base64_encode($file['contents'])); $file['contents'] = chunk_split(base64_encode($file['contents']));
} else { } else {

View File

@ -250,7 +250,9 @@ class Form extends RequestHandler {
// Protection against CSRF attacks // Protection against CSRF attacks
$token = $this->getSecurityToken(); $token = $this->getSecurityToken();
if(!$token->checkRequest($request)) { if(!$token->checkRequest($request)) {
$this->httpError(400, "Sorry, your session has timed out."); $this->httpError(400, _t("Form.CSRF_FAILED_MESSAGE",
"There seems to have been a technical problem. Please click the back button,"
. " refresh your browser, and try again."));
} }
// Determine the action button clicked // Determine the action button clicked

View File

@ -35,7 +35,7 @@ class RequiredFields extends Validator {
* Clears all the validation from this object. * Clears all the validation from this object.
*/ */
public function removeValidation(){ public function removeValidation(){
$this->required = null; $this->required = array();
} }
/** /**

View File

@ -55,6 +55,9 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
* @return ArrayIterator * @return ArrayIterator
*/ */
public function getIterator() { public function getIterator() {
foreach($this->items as $i => $item) {
if(is_array($item)) $this->items[$i] = new ArrayData($item);
}
return new ArrayIterator($this->items); return new ArrayIterator($this->items);
} }

View File

@ -302,7 +302,7 @@ class Date extends DBField {
* @return boolean * @return boolean
*/ */
public function InPast() { public function InPast() {
return strtotime($this->value) < time(); return strtotime($this->value) < SS_Datetime::now()->Format('U');
} }
/** /**
@ -310,7 +310,7 @@ class Date extends DBField {
* @return boolean * @return boolean
*/ */
public function InFuture() { public function InFuture() {
return strtotime($this->value) > time(); return strtotime($this->value) > SS_Datetime::now()->Format('U');
} }
/** /**
@ -318,7 +318,7 @@ class Date extends DBField {
* @return boolean * @return boolean
*/ */
public function IsToday() { public function IsToday() {
return (date('Y-m-d', strtotime($this->value)) == date('Y-m-d', time())); return (date('Y-m-d', strtotime($this->value)) == SS_Datetime::now()->Format('Y-m-d'));
} }
/** /**

View File

@ -136,8 +136,21 @@ class HTMLText extends Text {
return ShortcodeParser::get_active()->parse($this->value); return ShortcodeParser::get_active()->parse($this->value);
} }
/**
* Returns true if the field has meaningful content.
* Excludes null content like <h1></h1>, <p></p> ,etc
*
* @return boolean
*/
public function exists() { public function exists() {
return parent::exists() && $this->value != '<p></p>'; // If it's blank, it's blank
if(!parent::exists()) return false;
// If it's got a content tag
if(preg_match('/<(img|embed|object|iframe)[^>]*>/i', $this->value)) return true;
// If it's just one or two tags on its own (and not the above) it's empty. This might be <p></p> or <h1></h1> or whatever.
if(preg_match('/^[\\s]*(<[^>]+>[\\s]*){1,2}$/', $this->value)) return false;
// Otherwise its content is genuine content
return true;
} }
public function scaffoldFormField($title = null, $params = null) { public function scaffoldFormField($title = null, $params = null) {

View File

@ -242,7 +242,10 @@ class Security extends Controller {
// Audit logging hook // Audit logging hook
$controller->extend('permissionDenied', $member); $controller->extend('permissionDenied', $member);
$controller->redirect("Security/login?BackURL=" . urlencode($_SERVER['REQUEST_URI'])); $controller->redirect(
Config::inst()->get('Security', 'login_url')
. "?BackURL=" . urlencode($_SERVER['REQUEST_URI'])
);
} }
return; return;
} }
@ -927,8 +930,25 @@ class Security extends Controller {
public static function set_ignore_disallowed_actions($flag) { public static function set_ignore_disallowed_actions($flag) {
self::$ignore_disallowed_actions = $flag; self::$ignore_disallowed_actions = $flag;
} }
public static function ignore_disallowed_actions() { public static function ignore_disallowed_actions() {
return self::$ignore_disallowed_actions; return self::$ignore_disallowed_actions;
} }
protected static $login_url = "Security/login";
/**
* Set a custom log-in URL if you have built your own log-in page.
*/
public static function set_login_url($loginUrl) {
self::$login_url = $loginUrl;
}
/**
* Get the URL of the log-in page.
* Defaults to Security/login but can be re-set with {@link set_login_url()}
*/
public static function login_url() {
return self::$login_url;
}
} }

View File

@ -1,4 +1,9 @@
<form $FormAttributes> <form $FormAttributes>
<% if Message %>
<p id="{$FormName}_error" class="message $MessageType">$Message</p>
<% else %>
<p id="{$FormName}_error" class="message $MessageType" style="display: none"></p>
<% end_if %>
<fieldset> <fieldset>
<% loop Fields %> <% loop Fields %>
$FieldHolder $FieldHolder

View File

@ -252,7 +252,7 @@ class DirectorTest extends SapphireTest {
} }
public function testForceSSLOnSubPagesPattern() { public function testForceSSLOnSubPagesPattern() {
$_SERVER['REQUEST_URI'] = Director::baseURL() . 'Security/login'; $_SERVER['REQUEST_URI'] = Director::baseURL() . Config::inst()->get('Security', 'login_url');
$output = Director::forceSSL(array('/^Security/')); $output = Director::forceSSL(array('/^Security/'));
$this->assertEquals($output, 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); $this->assertEquals($output, 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
} }

View File

@ -139,4 +139,38 @@ class HTMLTextTest extends SapphireTest {
$data = DBField::create_field('HTMLText', '"this is a test"'); $data = DBField::create_field('HTMLText', '"this is a test"');
$this->assertEquals($data->ATT(), '&quot;this is a test&quot;'); $this->assertEquals($data->ATT(), '&quot;this is a test&quot;');
} }
function testExists() {
$h = new HTMLText;
$h->setValue("");
$this->assertFalse($h->exists());
$h->setValue("<p></p>");
$this->assertFalse($h->exists());
$h->setValue("<p> </p>");
$this->assertFalse($h->exists());
$h->setValue("<h2/>");
$this->assertFalse($h->exists());
$h->setValue("<h2></h2>");
$this->assertFalse($h->exists());
$h->setValue("something");
$this->assertTrue($h->exists());
$h->setValue("<img src=\"dummy.png\">");
$this->assertTrue($h->exists());
$h->setValue("<img src=\"dummy.png\"><img src=\"dummy.png\">");
$this->assertTrue($h->exists());
$h->setValue("<p><img src=\"dummy.png\"></p>");
$this->assertTrue($h->exists());
$h->setValue("<iframe src=\"http://www.google.com\"></iframe>");
$this->assertTrue($h->exists());
$h->setValue("<embed src=\"test.swf\">");
$this->assertTrue($h->exists());
$h->setValue("<object width=\"400\" height=\"400\" data=\"test.swf\"></object>");
$this->assertTrue($h->exists());
$h->setValue("<p>test</p>");
$this->assertTrue($h->exists());
}
} }

View File

@ -57,7 +57,10 @@ class SecurityTest extends FunctionalTest {
$response = $this->get('SecurityTest_SecuredController'); $response = $this->get('SecurityTest_SecuredController');
$this->assertEquals(302, $response->getStatusCode()); $this->assertEquals(302, $response->getStatusCode());
$this->assertContains('Security/login', $response->getHeader('Location')); $this->assertContains(
Config::inst()->get('Security', 'login_url'),
$response->getHeader('Location')
);
$this->logInWithPermission('ADMIN'); $this->logInWithPermission('ADMIN');
$response = $this->get('SecurityTest_SecuredController'); $response = $this->get('SecurityTest_SecuredController');
@ -74,7 +77,7 @@ class SecurityTest extends FunctionalTest {
$this->session()->inst_set('loggedInAs', $member->ID); $this->session()->inst_set('loggedInAs', $member->ID);
/* View the Security/login page */ /* View the Security/login page */
$response = $this->get('Security/login'); $response = $this->get(Config::inst()->get('Security', 'login_url'));
$items = $this->cssParser()->getBySelector('#MemberLoginForm_LoginForm input.action'); $items = $this->cssParser()->getBySelector('#MemberLoginForm_LoginForm input.action');
@ -108,7 +111,7 @@ class SecurityTest extends FunctionalTest {
$this->autoFollowRedirection = true; $this->autoFollowRedirection = true;
/* Attempt to get into the admin section */ /* Attempt to get into the admin section */
$response = $this->get('Security/login/'); $response = $this->get(Config::inst()->get('Security', 'login_url'));
$items = $this->cssParser()->getBySelector('#MemberLoginForm_LoginForm input.text'); $items = $this->cssParser()->getBySelector('#MemberLoginForm_LoginForm input.text');
@ -396,7 +399,7 @@ class SecurityTest extends FunctionalTest {
public function doTestLoginForm($email, $password, $backURL = 'test/link') { public function doTestLoginForm($email, $password, $backURL = 'test/link') {
$this->get('Security/logout'); $this->get('Security/logout');
$this->session()->inst_set('BackURL', $backURL); $this->session()->inst_set('BackURL', $backURL);
$this->get('Security/login'); $this->get(Config::inst()->get('Security', 'login_url'));
return $this->submitForm( return $this->submitForm(
"MemberLoginForm_LoginForm", "MemberLoginForm_LoginForm",