From 13b358a8dd4de1c2311e98d18135f6d0b754417a Mon Sep 17 00:00:00 2001 From: Sean Harvey Date: Wed, 29 Apr 2009 00:07:39 +0000 Subject: [PATCH] Merged from branches/2.3 git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@75582 467b73ca-7a2a-4603-9d3b-597d59a354a9 --- api/RestfulServer.php | 1 + core/Requirements.php | 7 +- core/model/DataObject.php | 11 +- core/model/DataObjectSet.php | 79 ++++++-------- core/model/ErrorPage.php | 9 +- core/model/Image.php | 4 +- core/model/SiteTree.php | 20 ++-- css/ComplexTableField.css | 6 -- dev/BulkLoader.php | 3 +- email/Mailer.php | 53 +++++++-- filesystem/File.php | 9 +- forms/ComplexTableField.php | 73 ++++++------- forms/HasManyComplexTableField.php | 16 ++- forms/HtmlEditorField.php | 2 +- forms/ScaffoldingComplexTableField.php | 51 +-------- forms/TableField.php | 12 ++- forms/TableListField.php | 15 ++- javascript/TableField.js | 6 ++ security/Group.php | 17 ++- security/LoginAttempt.php | 9 +- security/Member.php | 16 ++- security/MemberLoginForm.php | 19 +++- tests/DataObjectSetTest.php | 143 +++++++++++++++++++++++++ tests/forms/RequirementsTest.php | 20 ++++ tests/i18n/i18nTest.php | 9 +- tests/model/DataObjectSetTest.php | 70 ------------ tests/security/MemberTest.php | 4 + tests/security/SecurityTest.php | 60 +++++++++-- 28 files changed, 478 insertions(+), 266 deletions(-) create mode 100644 tests/DataObjectSetTest.php delete mode 100644 tests/model/DataObjectSetTest.php diff --git a/api/RestfulServer.php b/api/RestfulServer.php index 393119851..844f04706 100644 --- a/api/RestfulServer.php +++ b/api/RestfulServer.php @@ -294,6 +294,7 @@ class RestfulServer extends Controller { $formatter = DataFormatter::for_extension($extension); }elseif($includeAcceptHeader && !empty($accept) && $accept != '*/*') { $formatter = DataFormatter::for_mimetypes($mimetypes); + if(!$formatter) $formatter = DataFormatter::for_extension(self::$default_extension); } elseif(!empty($contentType)) { $formatter = DataFormatter::for_mimetype($contentType); } else { diff --git a/core/Requirements.php b/core/Requirements.php index 35b70dc32..99ececd11 100644 --- a/core/Requirements.php +++ b/core/Requirements.php @@ -699,10 +699,15 @@ class Requirements_Backend { } elseif(Director::fileExists($fileOrUrl)) { $prefix = Director::absoluteBaseURL(); $mtimesuffix = ""; + $suffix = ''; + if(strpos($fileOrUrl, '?') !== false) { + $suffix = '&' . substr($fileOrUrl, strpos($fileOrUrl, '?')+1); + $fileOrUrl = substr($fileOrUrl, 0, strpos($fileOrUrl, '?')); + } if(Requirements::get_suffix_requirements()) { $mtimesuffix = "?m=" . filemtime(Director::baseFolder() . '/' . $fileOrUrl); } - return "{$prefix}{$fileOrUrl}{$mtimesuffix}"; + return "{$prefix}{$fileOrUrl}{$mtimesuffix}{$suffix}"; } else { return false; } diff --git a/core/model/DataObject.php b/core/model/DataObject.php index 7d0debf9f..d6cdec1ae 100644 --- a/core/model/DataObject.php +++ b/core/model/DataObject.php @@ -2826,11 +2826,13 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity * * @uses $field_labels * @uses FormField::name_to_label() + * + * @param boolean $includerelations a boolean value to indicate if the labels returned include relation fields * * @return array of all element labels if no argument given * @return string of label if field */ - public function fieldLabels() { + public function fieldLabels($includerelations = true) { $customLabels = $this->stat('field_labels'); $autoLabels = array(); @@ -2845,6 +2847,11 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity 'has_many' => (array) Object::uninherited_static($ancestorClass, 'has_many'), 'many_many' => (array) Object::uninherited_static($ancestorClass, 'many_many') ); + if($includerelations){ + $types['has_one'] = (array)singleton($ancestorClass)->uninherited('has_one', true); + $types['has_many'] = (array)singleton($ancestorClass)->uninherited('has_many', true); + $types['many_many'] = (array)singleton($ancestorClass)->uninherited('many_many', true); + } foreach($types as $type => $attrs) { foreach($attrs as $name => $spec) $autoLabels[$name] = _t("{$ancestorClass}.{$type}_{$name}",FormField::name_to_label($name)); @@ -3176,4 +3183,4 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity } -?> \ No newline at end of file +?> diff --git a/core/model/DataObjectSet.php b/core/model/DataObjectSet.php index 85ae4119e..e4d40a0c0 100644 --- a/core/model/DataObjectSet.php +++ b/core/model/DataObjectSet.php @@ -137,25 +137,20 @@ class DataObjectSet extends ViewableData implements IteratorAggregate { } /** - * Returns an array of ID => $titleField - * @param string $index The field you wish to use as a key for the array - * @param string $titleField The field or method you wish to use to get values for the map - * @param string $emptyString String for no option selected - * @param bool $sort If you want to sort the map based on $titleField - * @return array - */ - public function toDropDownMap($index = "ID",$titleField = "Title",$emptyString = null, $sort = false){ - $map = array(); - foreach($this->items as $item) { - $map[$item->$index] = $item->$titleField; - } - if($emptyString) { - $map = array("0"=>"$emptyString") + $map; - } - if($sort) { - asort($map); - } - return $map; + * Returns an array of ID => Title for the items in this set. + * + * This is an alias of {@link DataObjectSet->map()} + * + * @deprecated 2.5 Please use map() instead + * + * @param string $index The field to use as a key for the array + * @param string $titleField The field (or method) to get values for the map + * @param string $emptyString Empty option text e.g "(Select one)" + * @param bool $sort Sort the map alphabetically based on the $titleField value + * @return array + */ + public function toDropDownMap($index = 'ID', $titleField = 'Title', $emptyString = null, $sort = false) { + return $this->map($index, $titleField, $emptyString, $sort); } /** @@ -591,6 +586,10 @@ class DataObjectSet extends ViewableData implements IteratorAggregate { /** * Returns the dataset as an array of ID => "FirstName Surname" + * + * @deprecated 2.4 Please use map() instead, pointing the title + * to a method name that does the concatenation of values. + * * @param string $key Field name to index the array. * @param array $values An array of fieldnames to insert in array * @param boolean $withdash Add dashes inbetween values @@ -613,38 +612,26 @@ class DataObjectSet extends ViewableData implements IteratorAggregate { } /** - * Returns the dataset as an array of ID => Title. - * - * @todo Duplication from toDropdownMap() + * Returns an array of ID => Title for the items in this set. * - * @param string $key The field you wish to use as a key for the array - * @param string $value The field or method you wish to use to get values for the map - * @param string $includeBlank String for no option selected - * @return array + * @param string $index The field to use as a key for the array + * @param string $titleField The field (or method) to get values for the map + * @param string $emptyString Empty option text e.g "(Select one)" + * @param bool $sort Sort the map alphabetically based on the $titleField value + * @return array */ - public function map($key = "ID", $value = "Title", $includeBlank=null) { + public function map($index = 'ID', $titleField = 'Title', $emptyString = null, $sort = false) { $map = array(); - /* Don't do this, add this locally. - * Reasons: 1: In some case this blank value don't/mustn't present. - 2: In some case, this balnk value should be customised, such as (Select from below) - 3: In some case, the key need to be explicitly "0", cos "" and "0" need to be treated differently - */ - //$map[''] = "(Select)"; - - /* Instead do this as an option */ - if($includeBlank) $map[''] = $includeBlank; - - foreach($this->items as $item ){ - if(is_array($value)){ - foreach($value as $individul){ - if($map[$item->$key]) $map[$item->$key] .=" - "; - $map[$item->$key] .= $item->$individul; - } - }else{ - $map[$item->$key] = $item->$value; + if($this->items) { + foreach($this->items as $item) { + $map[$item->$index] = ($item->hasMethod($titleField)) ? $item->$titleField() : $item->$titleField; } } + + if($emptyString) $map = array('0' => "$emptyString") + $map; + if($sort) asort($map); + return $map; } @@ -653,7 +640,7 @@ class DataObjectSet extends ViewableData implements IteratorAggregate { * * Question: should any args be passed to the filter function? * - * @todo deprecate toDropdownMap() and map_multiple(), rename this method to map() + * @deprecated 2.4 Please use map() instead */ public function filter_map($key, $value) { $map = array(); diff --git a/core/model/ErrorPage.php b/core/model/ErrorPage.php index 6b3255bb1..e0c6797b7 100755 --- a/core/model/ErrorPage.php +++ b/core/model/ErrorPage.php @@ -133,8 +133,13 @@ class ErrorPage extends Page { return $this->extension_instances['Versioned']->publish($fromStage, $toStage, $createNewVersion); } - function fieldLabels() { - $labels = parent::fieldLabels(); + /** + * + * @param boolean $includerelations a boolean value to indicate if the labels returned include relation fields + * + */ + function fieldLabels($includerelations = true) { + $labels = parent::fieldLabels($includerelations); $labels['ErrorCode'] = _t('ErrorPage.CODE', "Error code"); return $labels; diff --git a/core/model/Image.php b/core/model/Image.php index 7b2fd8f4b..887c42d6e 100755 --- a/core/model/Image.php +++ b/core/model/Image.php @@ -389,9 +389,9 @@ class Image extends File { /** * Get the orientation of this image. - * @return ORIENTATION_SQUARE | ORIENTATION_PORTRAIT | ORIENTATION_LANDSCAPE + * @return ORIENTATION_SQUARE | ORIENTATION_PORTRAIT | ORIENTATION_LANDSCAPE */ - function getOrienation() { + function getOrientation() { $width = $this->getWidth(); $height = $this->getHeight(); if($width > $height) { diff --git a/core/model/SiteTree.php b/core/model/SiteTree.php index c36fd87cb..b91687585 100644 --- a/core/model/SiteTree.php +++ b/core/model/SiteTree.php @@ -1302,8 +1302,13 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid return $fields; } - function fieldLabels() { - $labels = parent::fieldLabels(); + /** + * + * @param boolean $includerelations a boolean value to indicate if the labels returned include relation fields + * + */ + function fieldLabels($includerelations = true) { + $labels = parent::fieldLabels($includerelations); $labels['Title'] = _t('SiteTree.PAGETITLE', "Page name"); $labels['MenuTitle'] = _t('SiteTree.MENUTITLE', "Navigation label"); @@ -1324,11 +1329,14 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid $labels['CanViewType'] = _t('SiteTree.Viewers', 'Viewers Groups'); $labels['CanEditType'] = _t('SiteTree.Editors', 'Editors Groups'); $labels['ToDo'] = _t('SiteTree.ToDo', 'Todo Notes'); - $labels['Parent'] = _t('SiteTree.has_one_Parent', 'Parent Page', PR_MEDIUM, 'The parent page in the site hierarchy'); $labels['Comments'] = _t('SiteTree.Comments', 'Comments'); - $labels['LinkTracking'] = _t('SiteTree.many_many_LinkTracking', 'Link Tracking'); - $labels['ImageTracking'] = _t('SiteTree.many_many_ImageTracking', 'Image Tracking'); - $labels['BackLinkTracking'] = _t('SiteTree.many_many_BackLinkTracking', 'Backlink Tracking'); + + if($includerelations){ + $labels['Parent'] = _t('SiteTree.has_one_Parent', 'Parent Page', PR_MEDIUM, 'The parent page in the site hierarchy'); + $labels['LinkTracking'] = _t('SiteTree.many_many_LinkTracking', 'Link Tracking'); + $labels['ImageTracking'] = _t('SiteTree.many_many_ImageTracking', 'Image Tracking'); + $labels['BackLinkTracking'] = _t('SiteTree.many_many_BackLinkTracking', 'Backlink Tracking'); + } return $labels; } diff --git a/css/ComplexTableField.css b/css/ComplexTableField.css index 0480a41cf..4c8c14658 100755 --- a/css/ComplexTableField.css +++ b/css/ComplexTableField.css @@ -21,12 +21,6 @@ font-size: 11px; } -/* popup */ -.ComplexTableField_ItemRequest_Popup { - height: 100%; - overflow: auto; -} - /* table */ .ComplexTableField { margin-bottom: 10px; diff --git a/dev/BulkLoader.php b/dev/BulkLoader.php index e363b9a23..77613497d 100644 --- a/dev/BulkLoader.php +++ b/dev/BulkLoader.php @@ -205,7 +205,8 @@ abstract class BulkLoader extends ViewableData { $spec = array(); // get database columns (fieldlabels include fieldname as a key) - $spec['fields'] = (array)singleton($this->objectClass)->fieldLabels(); + // using $$includerelations flag as false, so that it only contain $db fields + $spec['fields'] = (array)singleton($this->objectClass)->fieldLabels($includerelations=false); $has_ones = singleton($this->objectClass)->has_one(); $has_manys = singleton($this->objectClass)->has_many(); diff --git a/email/Mailer.php b/email/Mailer.php index 1b0e451c3..04d24b798 100644 --- a/email/Mailer.php +++ b/email/Mailer.php @@ -110,7 +110,7 @@ function htmlEmail($to, $from, $subject, $htmlContent, $attachedFiles = false, $ // Include any specified attachments as additional parts foreach($attachedFiles as $file) { - if($file['tmp_name'] && $file['name']) { + if(isset($file['tmp_name']) && isset($file['name'])) { $messageParts[] = encodeFileForEmail($file['tmp_name'], $file['name']); } else { $messageParts[] = encodeFileForEmail($file); @@ -200,7 +200,7 @@ function plaintextEmail($to, $from, $subject, $plainContent, $attachedFiles, $cu // Include any specified attachments as additional parts foreach($attachedFiles as $file) { - if($file['tmp_name'] && $file['name']) { + if(isset($file['tmp_name']) && isset($file['name'])) { $messageParts[] = encodeFileForEmail($file['tmp_name'], $file['name']); } else { $messageParts[] = encodeFileForEmail($file); @@ -329,10 +329,45 @@ function processHeaders($headers, $body = false) { return $res; } -/* +/** * Encode the contents of a file for emailing, including headers + * + * $file can be an array, in which case it expects these members: + * 'filename' - the filename of the file + * 'contents' - the raw binary contents of the file as a string + * and can optionally include these members: + * 'mimetype' - the mimetype of the file (calculated from filename if missing) + * 'contentLocation' - the 'Content-Location' header value for the file + * + * $file can also be a string, in which case it is assumed to be the filename + * + * h5. contentLocation + * + * Content Location is one of the two methods allowed for embedding images into an html email. It's also the simplest, and best supported + * + * Assume we have an email with this in the body: + * + * + * + * To display the image, an email viewer would have to download the image from the web every time it is displayed. Due to privacy issues, most + * viewers will not display any images unless the user clicks 'Show images in this email'. Not optimal. + * + * However, we can also include a copy of this image as an attached file in the email. By giving it a contentLocation of "http://example.com/image.gif" + * most email viewers will use this attached copy instead of downloading it. Better, most viewers will show it without a 'Show images in this email' + * conformation. + * + * Here is an example of passing this information through Email.php: + * + * $email = new Email(); + * $email->attachments[] = array( + * 'filename' => BASE_PATH . "/themes/mytheme/images/header.gif", + * 'contents' => file_get_contents(BASE_PATH . "/themes/mytheme/images/header.gif"), + * 'mimetype' => 'image/gif', + * 'contentLocation' => Director::absoluteBaseURL() . "/themes/mytheme/images/header.gif" + * ); + * */ -function encodeFileForEmail($file, $destFileName = false, $disposition = "attachment", $extraHeaders = "") { +function encodeFileForEmail($file, $destFileName = false, $disposition = NULL, $extraHeaders = "") { if(!$file) { user_error("encodeFileForEmail: not passed a filename and/or data", E_USER_WARNING); return; @@ -354,6 +389,8 @@ function encodeFileForEmail($file, $destFileName = false, $disposition = "attach $mimeType = $file['mimetype'] ? $file['mimetype'] : getMimeType($file['filename']); if(!$mimeType) $mimeType = "application/unknown"; + if (empty($disposition)) $disposition = isset($file['contentLocation']) ? 'inline' : 'attachment'; + // Encode for emailing if (substr($file['mimetype'], 0, 4) != 'text') { $encoding = "base64"; @@ -366,8 +403,12 @@ function encodeFileForEmail($file, $destFileName = false, $disposition = "attach } $headers = "Content-type: $mimeType;\n\tname=\"$base\"\n". - "Content-Transfer-Encoding: $encoding\n". - "Content-Disposition: $disposition;\n\tfilename=\"$base\"\n" . $extraHeaders . "\n"; + "Content-Transfer-Encoding: $encoding\n". + "Content-Disposition: $disposition;\n\tfilename=\"$base\"\n" ; + + if ( isset($file['contentLocation']) ) $headers .= 'Content-Location: ' . $file['contentLocation'] . "\n" ; + + $headers .= $extraHeaders . "\n"; // Return completed packet return $headers . $file['contents']; diff --git a/filesystem/File.php b/filesystem/File.php index 4ff0ea589..4fbd56551 100755 --- a/filesystem/File.php +++ b/filesystem/File.php @@ -598,8 +598,13 @@ class File extends DataObject { self::$cache_file_fields = null; } - function fieldLabels() { - $labels = parent::fieldLabels(); + /** + * + * @param boolean $includerelations a boolean value to indicate if the labels returned include relation fields + * + */ + function fieldLabels($includerelations = true) { + $labels = parent::fieldLabels($includerelations); $labels['Name'] = _t('File.Name', 'Name'); $labels['Title'] = _t('File.Title', 'Title'); $labels['Filename'] = _t('File.Filename', 'Filename'); diff --git a/forms/ComplexTableField.php b/forms/ComplexTableField.php index 5baa99dec..50d4040fb 100755 --- a/forms/ComplexTableField.php +++ b/forms/ComplexTableField.php @@ -200,14 +200,6 @@ class ComplexTableField extends TableListField { $this->detailFormFields = $detailFormFields; $this->controller = $controller; $this->pageSize = 10; - - Requirements::javascript(THIRDPARTY_DIR . "/greybox/AmiJS.js"); - Requirements::javascript(THIRDPARTY_DIR . "/greybox/greybox.js"); - Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); - Requirements::javascript(SAPPHIRE_DIR . '/javascript/TableListField.js'); - Requirements::javascript(SAPPHIRE_DIR . "/javascript/ComplexTableField.js"); - Requirements::css(THIRDPARTY_DIR . "/greybox/greybox.css"); - Requirements::css(SAPPHIRE_DIR . "/css/ComplexTableField.css"); parent::__construct($name, $sourceClass, $fieldList, $sourceFilter, $sourceSort, $sourceJoin); @@ -246,6 +238,15 @@ class ComplexTableField extends TableListField { * @return String */ function FieldHolder() { + Requirements::javascript(THIRDPARTY_DIR . "/greybox/AmiJS.js"); + Requirements::javascript(THIRDPARTY_DIR . "/greybox/greybox.js"); + Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); + Requirements::javascript(SAPPHIRE_DIR . '/javascript/TableListField.js'); + Requirements::javascript(SAPPHIRE_DIR . "/javascript/ComplexTableField.js"); + Requirements::css(THIRDPARTY_DIR . "/greybox/greybox.css"); + Requirements::css(SAPPHIRE_DIR . "/css/TableListField.css"); + Requirements::css(SAPPHIRE_DIR . "/css/ComplexTableField.css"); + // set caption if required if($this->popupCaption) { $id = $this->id(); @@ -1014,36 +1015,13 @@ class ComplexTableField_Item extends TableListField_Item { */ class ComplexTableField_Popup extends Form { protected $sourceClass; + protected $dataObject; function __construct($controller, $name, $fields, $validator, $readonly, $dataObject) { $this->dataObject = $dataObject; - - /** - * WARNING: DO NOT CHANGE THE ORDER OF THESE JS FILES - * Some have special requirements. - */ - //Requirements::css(CMS_DIR . '/css/layout.css'); - Requirements::css(SAPPHIRE_DIR . '/css/Form.css'); - Requirements::css(SAPPHIRE_DIR . '/css/ComplexTableField_popup.css'); - Requirements::css(CMS_DIR . '/css/typography.css'); - Requirements::css(CMS_DIR . '/css/cms_right.css'); - Requirements::javascript(THIRDPARTY_DIR . "/prototype.js"); - Requirements::javascript(THIRDPARTY_DIR . "/behaviour.js"); - Requirements::javascript(THIRDPARTY_DIR . "/prototype_improvements.js"); - Requirements::javascript(THIRDPARTY_DIR . "/scriptaculous/scriptaculous.js"); - Requirements::javascript(THIRDPARTY_DIR . "/scriptaculous/controls.js"); - Requirements::javascript(THIRDPARTY_DIR . "/layout_helpers.js"); - Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); - Requirements::javascript(CMS_DIR . "/javascript/LeftAndMain.js"); - Requirements::javascript(CMS_DIR . "/javascript/LeftAndMain_right.js"); - Requirements::javascript(SAPPHIRE_DIR . "/javascript/TableField.js"); - Requirements::javascript(SAPPHIRE_DIR . "/javascript/ComplexTableField.js"); - Requirements::javascript(SAPPHIRE_DIR . "/javascript/ComplexTableField_popup.js"); - - if($this->dataObject->hasMethod('getRequirementsForPopup')) { - $this->dataObject->getRequirementsForPopup(); - } + + Requirements::clear(); $actions = new FieldSet(); if(!$readonly) { @@ -1059,8 +1037,31 @@ class ComplexTableField_Popup extends Form { parent::__construct($controller, $name, $fields, $actions, $validator); } - function FieldHolder() { - return $this->renderWith('ComplexTableField_Form'); + function forTemplate() { + $ret = parent::forTemplate(); + + /** + * WARNING: DO NOT CHANGE THE ORDER OF THESE JS FILES + * Some have special requirements. + */ + Requirements::css(SAPPHIRE_DIR . '/css/Form.css'); + Requirements::css(SAPPHIRE_DIR . '/css/ComplexTableField_popup.css'); + Requirements::css(CMS_DIR . '/css/typography.css'); + Requirements::css(CMS_DIR . '/css/cms_right.css'); + Requirements::javascript(THIRDPARTY_DIR . "/prototype.js"); + Requirements::javascript(THIRDPARTY_DIR . "/behaviour.js"); + Requirements::javascript(THIRDPARTY_DIR . "/prototype_improvements.js"); + Requirements::javascript(THIRDPARTY_DIR . "/scriptaculous/scriptaculous.js"); + Requirements::javascript(THIRDPARTY_DIR . "/scriptaculous/controls.js"); + Requirements::javascript(THIRDPARTY_DIR . "/layout_helpers.js"); + Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); + Requirements::javascript(SAPPHIRE_DIR . "/javascript/ComplexTableField_popup.js"); + + if($this->dataObject->hasMethod('getRequirementsForPopup')) { + $this->dataObject->getRequirementsForPopup(); + } + + return $ret; } } diff --git a/forms/HasManyComplexTableField.php b/forms/HasManyComplexTableField.php index 105bff475..8d69528a3 100644 --- a/forms/HasManyComplexTableField.php +++ b/forms/HasManyComplexTableField.php @@ -22,11 +22,6 @@ class HasManyComplexTableField extends ComplexTableField { function __construct($controller, $name, $sourceClass, $fieldList = null, $detailFormFields = null, $sourceFilter = "", $sourceSort = "", $sourceJoin = "") { parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields, $sourceFilter, $sourceSort, $sourceJoin); - - Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); - Requirements::javascript(SAPPHIRE_DIR . "/javascript/HasManyFileField.js"); - Requirements::javascript(SAPPHIRE_DIR . '/javascript/RelationComplexTableField.js'); - Requirements::css(SAPPHIRE_DIR . '/css/HasManyFileField.css'); $this->Markable = true; @@ -39,6 +34,17 @@ class HasManyComplexTableField extends ComplexTableField { } + function FieldHolder() { + $ret = parent::FieldHolder(); + + Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); + Requirements::javascript(SAPPHIRE_DIR . "/javascript/HasManyFileField.js"); + Requirements::javascript(SAPPHIRE_DIR . '/javascript/RelationComplexTableField.js'); + Requirements::css(SAPPHIRE_DIR . '/css/HasManyFileField.css'); + + return $ret; + } + /** * Try to determine the DataObject that this field is built on top of */ diff --git a/forms/HtmlEditorField.php b/forms/HtmlEditorField.php index 1c2f0fd85..f9570d35a 100755 --- a/forms/HtmlEditorField.php +++ b/forms/HtmlEditorField.php @@ -15,7 +15,7 @@ class HtmlEditorField extends TextareaField { /** * Construct a new HtmlEditor field */ - function __construct($name, $title = null, $rows = 20, $cols = 20, $value = "", $form = null) { + function __construct($name, $title = null, $rows = 30, $cols = 20, $value = "", $form = null) { parent::__construct($name, $title, $rows, $cols, $value, $form); $this->extraClass = 'typography'; } diff --git a/forms/ScaffoldingComplexTableField.php b/forms/ScaffoldingComplexTableField.php index b3ac9c616..f9ad336f0 100644 --- a/forms/ScaffoldingComplexTableField.php +++ b/forms/ScaffoldingComplexTableField.php @@ -5,9 +5,7 @@ * @package forms * @subpackage fields-relational */ -class ScaffoldingComplexTableField_Popup extends Form { - protected $sourceClass; - protected $dataObject; +class ScaffoldingComplexTableField_Popup extends ComplexTableField_Popup { public static $allowed_actions = array( 'filter', 'record', 'httpSubmission', 'handleAction', 'handleField' @@ -15,45 +13,9 @@ class ScaffoldingComplexTableField_Popup extends Form { function __construct($controller, $name, $fields, $validator, $readonly, $dataObject) { $this->dataObject = $dataObject; + + Requirements::clear(); - /** - * WARNING: DO NOT CHANGE THE ORDER OF THESE JS FILES - * Some have special requirements. - */ - //Requirements::css(CMS_DIR . 'css/layout.css'); - Requirements::css(SAPPHIRE_DIR . '/css/Form.css'); - Requirements::css(SAPPHIRE_DIR . '/css/ComplexTableField_popup.css'); - Requirements::css(CMS_DIR . '/css/typography.css'); - Requirements::css(CMS_DIR . '/css/cms_right.css'); - Requirements::css(THIRDPARTY_DIR . '/jquery/plugins/autocomplete/jquery.ui.autocomplete.css'); - Requirements::javascript(THIRDPARTY_DIR . "/prototype.js"); - Requirements::javascript(THIRDPARTY_DIR . "/behaviour.js"); - Requirements::javascript(THIRDPARTY_DIR . "/prototype_improvements.js"); - Requirements::javascript(THIRDPARTY_DIR . "/scriptaculous/scriptaculous.js"); - Requirements::javascript(THIRDPARTY_DIR . "/scriptaculous/controls.js"); - Requirements::javascript(THIRDPARTY_DIR . "/layout_helpers.js"); - Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); - Requirements::javascript(CMS_DIR . "/javascript/LeftAndMain.js"); - Requirements::javascript(CMS_DIR . "/javascript/LeftAndMain_right.js"); - Requirements::javascript(SAPPHIRE_DIR . "/javascript/TableField.js"); - Requirements::javascript(SAPPHIRE_DIR . "/javascript/ComplexTableField.js"); - Requirements::javascript(SAPPHIRE_DIR . "/javascript/ComplexTableField_popup.js"); - // jQuery requirements (how many of these are actually needed?) - Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js'); - Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery_improvements.js'); - Requirements::javascript(THIRDPARTY_DIR . '/jquery/plugins/livequery/jquery.livequery.js'); - Requirements::javascript(THIRDPARTY_DIR . '/jquery/ui/ui.core.js'); - Requirements::javascript(THIRDPARTY_DIR . '/jquery/ui/ui.tabs.js'); - Requirements::javascript(THIRDPARTY_DIR . '/jquery/plugins/form/jquery.form.js'); - Requirements::javascript(THIRDPARTY_DIR . '/jquery/plugins/dimensions/jquery.dimensions.js'); - Requirements::javascript(THIRDPARTY_DIR . '/jquery/plugins/autocomplete/jquery.ui.autocomplete.js'); - Requirements::javascript(SAPPHIRE_DIR . '/javascript/ScaffoldComplexTableField.js'); - Requirements::javascript(CMS_DIR . '/javascript/ModelAdmin.js'); - - if($this->dataObject->hasMethod('getRequirementsForPopup')) { - $this->dataObject->getRequirementsForPopup(); - } - $actions = new FieldSet(); if(!$readonly) { $actions->push( @@ -64,13 +26,8 @@ class ScaffoldingComplexTableField_Popup extends Form { $fields->push(new HiddenField("ComplexTableField_Path", Director::absoluteBaseURL())); - parent::__construct($controller, $name, $fields, $actions, $validator); + parent::__construct($controller, $name, $fields, $validator, $readonly, $dataObject); } - - function FieldHolder() { - return $this->renderWith('ComplexTableField_Form'); - } - /** * Handle a generic action passed in by the URL mapping. diff --git a/forms/TableField.php b/forms/TableField.php index cc4f66ace..9115e1de5 100644 --- a/forms/TableField.php +++ b/forms/TableField.php @@ -113,9 +113,6 @@ class TableField extends TableListField { $sourceFilter = "\"$filterField\" = '" . Convert::raw2sql($sourceFilter) . "'"; } parent::__construct($name, $sourceClass, $fieldList, $sourceFilter, $sourceSort, $sourceJoin); - - Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); - Requirements::javascript(SAPPHIRE_DIR . '/javascript/TableField.js'); } /** @@ -475,6 +472,15 @@ class TableField extends TableListField { * Sets the template to be rendered with */ function FieldHolder() { + Requirements::javascript(THIRDPARTY_DIR . '/prototype.js'); + Requirements::javascript(THIRDPARTY_DIR . '/behaviour.js'); + Requirements::javascript(THIRDPARTY_DIR . '/prototype_improvements.js'); + Requirements::javascript(THIRDPARTY_DIR . '/scriptaculous/effects.js'); + Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); + Requirements::javascript(SAPPHIRE_DIR . '/javascript/TableListField.js'); + Requirements::javascript(SAPPHIRE_DIR . '/javascript/TableField.js'); + Requirements::css(SAPPHIRE_DIR . '/css/TableListField.css'); + return $this->renderWith($this->template); } diff --git a/forms/TableListField.php b/forms/TableListField.php index bd117aabe..2f374e4f1 100755 --- a/forms/TableListField.php +++ b/forms/TableListField.php @@ -248,14 +248,6 @@ class TableListField extends FormField { $this->readOnly = false; parent::__construct($name); - - Requirements::javascript(THIRDPARTY_DIR . '/prototype.js'); - Requirements::javascript(THIRDPARTY_DIR . '/behaviour.js'); - Requirements::javascript(THIRDPARTY_DIR . '/prototype_improvements.js'); - Requirements::javascript(THIRDPARTY_DIR . '/scriptaculous/effects.js'); - Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); - Requirements::javascript(SAPPHIRE_DIR . '/javascript/TableListField.js'); - Requirements::css(SAPPHIRE_DIR . '/css/TableListField.css'); } /** @@ -283,6 +275,13 @@ class TableListField extends FormField { } function FieldHolder() { + Requirements::javascript(THIRDPARTY_DIR . '/prototype.js'); + Requirements::javascript(THIRDPARTY_DIR . '/behaviour.js'); + Requirements::javascript(THIRDPARTY_DIR . '/prototype_improvements.js'); + Requirements::javascript(THIRDPARTY_DIR . '/scriptaculous/effects.js'); + Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); + Requirements::javascript(SAPPHIRE_DIR . '/javascript/TableListField.js'); + Requirements::css(SAPPHIRE_DIR . '/css/TableListField.css'); if($this->clickAction) { $id = $this->id(); diff --git a/javascript/TableField.js b/javascript/TableField.js index 673f7a191..c41c620f3 100755 --- a/javascript/TableField.js +++ b/javascript/TableField.js @@ -162,6 +162,12 @@ TableField.prototype = { if(newRow.id != "new"){ this.resetNames(newRow); } + + // Make sure all inputs have unique IDs + for(var i = 0; i < inputs.length; i++) { + inputs[i].id += "-" + this.newRowID; + } + // Change the ID to a unique one newRow.id = "New_" + this.newRowID; diff --git a/security/Group.php b/security/Group.php index 1f1eecc9a..1bfc01b2d 100644 --- a/security/Group.php +++ b/security/Group.php @@ -111,17 +111,24 @@ class Group extends DataObject { return $fields; } - function fieldLabels() { - $labels = parent::fieldLabels(); + /** + * + * @param boolean $includerelations a boolean value to indicate if the labels returned include relation fields + * + */ + function fieldLabels($includerelations = true) { + $labels = parent::fieldLabels($includerelations); $labels['Title'] = _t('SecurityAdmin.GROUPNAME', 'Group name'); $labels['Description'] = _t('Group.Description', 'Description'); $labels['Code'] = _t('Group.Code', 'Group Code', PR_MEDIUM, 'Programmatical code identifying a group'); $labels['Locked'] = _t('Group.Locked', 'Locked?', PR_MEDIUM, 'Group is locked in the security administration area'); $labels['Sort'] = _t('Group.Sort', 'Sort Order'); $labels['IPRestrictions'] = _t('Group.IPRestrictions', 'IP Address Restrictions'); - $labels['Parent'] = _t('Group.Parent', 'Parent Group', PR_MEDIUM, 'One group has one parent group'); - $labels['Permissions'] = _t('Group.has_many_Permissions', 'Permissions', PR_MEDIUM, 'One group has many permissions'); - $labels['Members'] = _t('Group.many_many_Members', 'Members', PR_MEDIUM, 'One group has many members'); + if($includerelations){ + $labels['Parent'] = _t('Group.Parent', 'Parent Group', PR_MEDIUM, 'One group has one parent group'); + $labels['Permissions'] = _t('Group.has_many_Permissions', 'Permissions', PR_MEDIUM, 'One group has many permissions'); + $labels['Members'] = _t('Group.many_many_Members', 'Members', PR_MEDIUM, 'One group has many members'); + } return $labels; } diff --git a/security/LoginAttempt.php b/security/LoginAttempt.php index cc41a2399..d09750e03 100644 --- a/security/LoginAttempt.php +++ b/security/LoginAttempt.php @@ -33,8 +33,13 @@ class LoginAttempt extends DataObject { static $belongs_many_many = array(); - function fieldLabels() { - $labels = parent::fieldLabels(); + /** + * + * @param boolean $includerelations a boolean value to indicate if the labels returned include relation fields + * + */ + function fieldLabels($includerelations = true) { + $labels = parent::fieldLabels($includerelations); $labels['Email'] = _t('LoginAttempt.Email', 'Email Address'); $labels['Status'] = _t('LoginAttempt.Status', 'Status'); $labels['IP'] = _t('LoginAttempt.IP', 'IP Address'); diff --git a/security/Member.php b/security/Member.php index 186c5b843..20d0429a8 100644 --- a/security/Member.php +++ b/security/Member.php @@ -559,6 +559,8 @@ class Member extends DataObject { user_error('Member::inGroup(): Wrong format for $group parameter', E_USER_ERROR); } + if(!$groupCheckObj) return false; + $groupCandidateObjs = ($strict) ? $this->getManyManyComponents("Groups") : $this->Groups(); if($groupCandidateObjs) foreach($groupCandidateObjs as $groupCandidateObj) { if($groupCandidateObj->ID == $groupCheckObj->ID) return true; @@ -874,8 +876,13 @@ class Member extends DataObject { return $fields; } - function fieldLabels() { - $labels = parent::fieldLabels(); + /** + * + * @param boolean $includerelations a boolean value to indicate if the labels returned include relation fields + * + */ + function fieldLabels($includerelations = true) { + $labels = parent::fieldLabels($includerelations); $labels['FirstName'] = _t('Member.FIRSTNAME'); $labels['Surname'] = _t('Member.SURNAME'); @@ -886,8 +893,9 @@ class Member extends DataObject { $labels['PasswordExpiry'] = _t('Member.db_PasswordExpiry', 'Password Expiry Date', PR_MEDIUM, 'Password expiry date'); $labels['LockedOutUntil'] = _t('Member.db_LockedOutUntil', 'Locked out until', PR_MEDIUM, 'Security related date'); $labels['Locale'] = _t('Member.db_Locale', 'Interface Locale'); - $labels['Groups'] = _t('Member.belongs_many_many_Groups', 'Groups', PR_MEDIUM, 'Security Groups this member belongs to'); - + if($includerelations){ + $labels['Groups'] = _t('Member.belongs_many_many_Groups', 'Groups', PR_MEDIUM, 'Security Groups this member belongs to'); + } return $labels; } diff --git a/security/MemberLoginForm.php b/security/MemberLoginForm.php index d8b8b34df..09c6d5a42 100644 --- a/security/MemberLoginForm.php +++ b/security/MemberLoginForm.php @@ -43,9 +43,22 @@ class MemberLoginForm extends LoginForm { $backURL = Session::get('BackURL'); } - if($checkCurrentUser && Member::currentUserID()) { - $fields = new FieldSet(); - $actions = new FieldSet(new FormAction("logout", _t('Member.BUTTONLOGINOTHER', "Log in as someone else"))); + // We assume if session is storing a member ID, that member exists in the DB + $sessMemberExistsInDB = true; + if($sessionMemberID = Member::currentUserID()) { + $sessMemberInDB = DataObject::get_by_id('Member', $sessionMemberID); + if(!($sessMemberInDB && $sessMemberInDB->exists())) { + $sessMemberExistsInDB = false; + } + } + + if($checkCurrentUser && Member::currentUserID() && $sessMemberExistsInDB) { + $fields = new FieldSet( + new HiddenField("AuthenticationMethod", null, $this->authenticator_class, $this) + ); + $actions = new FieldSet( + new FormAction("logout", _t('Member.BUTTONLOGINOTHER', "Log in as someone else")) + ); } else { if(!$fields) { $fields = new FieldSet( diff --git a/tests/DataObjectSetTest.php b/tests/DataObjectSetTest.php new file mode 100644 index 000000000..61caedbf9 --- /dev/null +++ b/tests/DataObjectSetTest.php @@ -0,0 +1,143 @@ +'one')), + $two = new DataObject(array('Title'=>'two')), + $three = new DataObject(array('Title'=>'three')), + $four = new DataObject(array('Title'=>'four')) + )); + + // test Pos() with foreach() + $i = 0; + foreach($set as $item) { + $i++; + $this->assertEquals($i, $item->Pos(), "Iterator position is set correctly on ViewableData when iterated with foreach()"); + } + + // test Pos() manually + $this->assertEquals(1, $one->Pos()); + $this->assertEquals(2, $two->Pos()); + $this->assertEquals(3, $three->Pos()); + $this->assertEquals(4, $four->Pos()); + + // test DataObjectSet->Count() + $this->assertEquals(4, $set->Count()); + + // test DataObjectSet->First() + $this->assertSame($one, $set->First()); + + // test DataObjectSet->Last() + $this->assertSame($four, $set->Last()); + + // test ViewableData->First() + $this->assertTrue($one->First()); + $this->assertFalse($two->First()); + $this->assertFalse($three->First()); + $this->assertFalse($four->First()); + + // test ViewableData->Last() + $this->assertFalse($one->Last()); + $this->assertFalse($two->Last()); + $this->assertFalse($three->Last()); + $this->assertTrue($four->Last()); + + // test ViewableData->Middle() + $this->assertFalse($one->Middle()); + $this->assertTrue($two->Middle()); + $this->assertTrue($three->Middle()); + $this->assertFalse($four->Middle()); + + // test ViewableData->Even() + $this->assertFalse($one->Even()); + $this->assertTrue($two->Even()); + $this->assertFalse($three->Even()); + $this->assertTrue($four->Even()); + + // test ViewableData->Odd() + $this->assertTrue($one->Odd()); + $this->assertFalse($two->Odd()); + $this->assertTrue($three->Odd()); + $this->assertFalse($four->Odd()); + } + + /** + * Test {@link DataObjectSet->Count()} + */ + function testCount() { + $comments = DataObject::get('PageComment'); + + /* There are a total of 8 items in the set */ + $this->assertEquals($comments->Count(), 8, 'There are a total of 8 items in the set'); + } + + /** + * Test {@link DataObjectSet->First()} + */ + function testFirst() { + $comments = DataObject::get('PageComment'); + + /* The first object is Joe's comment */ + $this->assertEquals($comments->First()->ID, 1, 'The first object has an ID of "1"'); + $this->assertEquals($comments->First()->Name, 'Joe', 'The first object has a Name field value of "Joe"'); + } + + /** + * Test {@link DataObjectSet->Last()} + */ + function testLast() { + $comments = DataObject::get('PageComment'); + + /* The last object is Dean's comment */ + $this->assertEquals($comments->Last()->ID, 8, 'The last object has an ID of "8"'); + $this->assertEquals($comments->Last()->Name, 'Dean', 'The last object has a Name field value of "Dean"'); + } + + /** + * Test {@link DataObjectSet->map()} + */ + function testMap() { + $comments = DataObject::get('PageComment'); + + /* Now we get a map of all the PageComment records */ + $map = $comments->map('ID', 'Title', '(Select one)'); + + $expectedMap = array( + 0 => '(Select one)', + 1 => 'Joe', + 2 => 'Jane', + 3 => 'Bob', + 4 => 'Bob', + 5 => 'Ernie', + 6 => 'Jimmy', + 7 => 'Dean', + 8 => 'Dean' + ); + + /* There are 9 items in the map. 8 are records. 1 is the empty value */ + $this->assertEquals(count($map), 9, 'There are 9 items in the map. 8 are records. 1 is the empty value'); + + /* We have the same map as our expected map, asserted above */ + $this->assertSame($expectedMap, $map, 'The map we generated is exactly the same as the asserted one'); + + /* toDropDownMap() is an alias of map() - let's make a map from that */ + $map2 = $comments->toDropDownMap('ID', 'Title', '(Select one)'); + + /* There are 9 items in the map. 8 are records. 1 is the empty value */ + $this->assertEquals(count($map), 9, 'There are 9 items in the map. 8 are records. 1 is the empty value.'); + + /* We have the same map as our expected map, asserted above */ + $this->assertSame($expectedMap, $map2, 'The map we generated is exactly the same as the asserted one'); + } + +} +?> \ No newline at end of file diff --git a/tests/forms/RequirementsTest.php b/tests/forms/RequirementsTest.php index 26ad21aa3..c184e7789 100644 --- a/tests/forms/RequirementsTest.php +++ b/tests/forms/RequirementsTest.php @@ -122,6 +122,26 @@ class RequirementsTest extends SapphireTest { Requirements::delete_combined_files('RequirementsTest_bc.js'); } + function testArgsInUrls() { + // Clear previous requirements + Requirements::clear(); + + // clearing all previously generated requirements (just in case) + Requirements::clear_combined_files(); + Requirements::delete_combined_files('RequirementsTest_bc.js'); + + Requirements::javascript(SAPPHIRE_DIR . '/tests/forms/RequirementsTest_a.js?test=1&test=2&test=3'); + Requirements::css(SAPPHIRE_DIR . '/tests/forms/RequirementsTest_a.css?test=1&test=2&test=3'); + + $html = Requirements::includeInHTML(false, self::$html_template); + + /* Javascript has correct path */ + $this->assertTrue((bool)preg_match('/src=".*\/RequirementsTest_a\.js\?m=\d\d+&test=1&test=2&test=3/', $html), 'javascript has correct path'); + + /* CSS has correct path */ + $this->assertTrue((bool)preg_match('/href=".*\/RequirementsTest_a\.css\?m=\d\d+&test=1&test=2&test=3/', $html), 'css has correct path'); + } + /** * This is a bit of a hack, as it alters the Requirements * statics globally for all tests. diff --git a/tests/i18n/i18nTest.php b/tests/i18n/i18nTest.php index 2817a7a65..51b53edee 100644 --- a/tests/i18n/i18nTest.php +++ b/tests/i18n/i18nTest.php @@ -219,8 +219,13 @@ class i18nTest_DataObject extends DataObject implements TestOnly { 'ManyManyRelation' => 'Member' ); - function fieldLabels() { - $labels = parent::fieldLabels(); + /** + * + * @param boolean $includerelations a boolean value to indicate if the labels returned include relation fields + * + */ + function fieldLabels($includerelations = true) { + $labels = parent::fieldLabels($includerelations); $labels['MyProperty'] = _t('i18nTest_DataObject.MyProperty', 'My Property'); return $labels; diff --git a/tests/model/DataObjectSetTest.php b/tests/model/DataObjectSetTest.php deleted file mode 100644 index cde60d732..000000000 --- a/tests/model/DataObjectSetTest.php +++ /dev/null @@ -1,70 +0,0 @@ -'one')), - $two = new DataObject(array('Title'=>'two')), - $three = new DataObject(array('Title'=>'three')), - $four = new DataObject(array('Title'=>'four')) - )); - - // test Pos() with foreach() - $i = 0; - foreach($set as $item) { - $i++; - $this->assertEquals($i, $item->Pos(), "Iterator position is set correctly on ViewableData when iterated with foreach()"); - } - - // test Pos() manually - $this->assertEquals(1, $one->Pos()); - $this->assertEquals(2, $two->Pos()); - $this->assertEquals(3, $three->Pos()); - $this->assertEquals(4, $four->Pos()); - - // test DataObjectSet->Count() - $this->assertEquals(4, $set->Count()); - - // test DataObjectSet->First() - $this->assertSame($one, $set->First()); - - // test DataObjectSet->Last() - $this->assertSame($four, $set->Last()); - - // test ViewableData->First() - $this->assertTrue($one->First()); - $this->assertFalse($two->First()); - $this->assertFalse($three->First()); - $this->assertFalse($four->First()); - - // test ViewableData->Last() - $this->assertFalse($one->Last()); - $this->assertFalse($two->Last()); - $this->assertFalse($three->Last()); - $this->assertTrue($four->Last()); - - // test ViewableData->Middle() - $this->assertFalse($one->Middle()); - $this->assertTrue($two->Middle()); - $this->assertTrue($three->Middle()); - $this->assertFalse($four->Middle()); - - // test ViewableData->Even() - $this->assertFalse($one->Even()); - $this->assertTrue($two->Even()); - $this->assertFalse($three->Even()); - $this->assertTrue($four->Even()); - - // test ViewableData->Odd() - $this->assertTrue($one->Odd()); - $this->assertFalse($two->Odd()); - $this->assertTrue($three->Odd()); - $this->assertFalse($four->Odd()); - } - -} -?> \ No newline at end of file diff --git a/tests/security/MemberTest.php b/tests/security/MemberTest.php index fd1bd7da6..f66630995 100644 --- a/tests/security/MemberTest.php +++ b/tests/security/MemberTest.php @@ -255,5 +255,9 @@ class MemberTest extends SapphireTest { $accountingmember->inGroup($managementgroup), 'Users of group are not members of any siblings' ); + $this->assertFalse( + $staffmember->inGroup('does-not-exist'), + 'Non-existant group returns false' + ); } } \ No newline at end of file diff --git a/tests/security/SecurityTest.php b/tests/security/SecurityTest.php index af2f2c81d..0acab0017 100644 --- a/tests/security/SecurityTest.php +++ b/tests/security/SecurityTest.php @@ -37,13 +37,61 @@ class SecurityTest extends FunctionalTest { parent::tearDown(); } - function testExternalBackUrlRedirectionDisallowed() { - $page = new SiteTree(); - $page->URLSegment = 'testpage'; - $page->Title = 'Testpage'; - $page->write(); - $page->publish('Stage','Live'); + function testLogInAsSomeoneElse() { + $member = DataObject::get_one('Member'); + + /* Log in with any user that we can find */ + $this->session()->inst_set('loggedInAs', $member->ID); + + /* View the Security/login page */ + $this->get('Security/login'); + $items = $this->cssParser()->getBySelector('#MemberLoginForm_LoginForm input.action'); + + /* We have only 1 input, one to allow the user to log in as someone else */ + $this->assertEquals(count($items), 1, 'There is 1 input, allowing the user to log in as someone else.'); + + $this->autoFollowRedirection = true; + + /* Submit the form, using only the logout action and a hidden field for the authenticator */ + $response = $this->submitForm( + 'MemberLoginForm_LoginForm', + null, + array( + 'AuthenticationMethod' => 'MemberAuthenticator', + 'action_dologout' => 1, + ) + ); + + /* We get a good response */ + $this->assertEquals($response->getStatusCode(), 200, 'We have a 200 OK response'); + $this->assertNotNull($response->getBody(), 'There is body content on the page'); + + /* Log the user out */ + $this->session()->inst_set('loggedInAs', null); + } + + function testMemberIDInSessionDoesntExistInDatabaseHasToLogin() { + /* Log in with a Member ID that doesn't exist in the DB */ + $this->session()->inst_set('loggedInAs', 500); + + $this->autoFollowRedirection = true; + + /* Attempt to get into the admin section */ + $this->get('admin'); + + $items = $this->cssParser()->getBySelector('#MemberLoginForm_LoginForm input.text'); + + /* We have 2 text inputs - one for email, and another for the password */ + $this->assertEquals(count($items), 2, 'There are 2 inputs - one for email, another for password'); + + $this->autoFollowRedirection = false; + + /* Log the user out */ + $this->session()->inst_set('loggedInAs', null); + } + + function testExternalBackUrlRedirectionDisallowed() { // Test internal relative redirect $response = $this->doTestLoginForm('noexpiry@silverstripe.com', '1nitialPassword', 'testpage'); $this->assertEquals(302, $response->getStatusCode());