mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Merged from branches/nzct-trunk. Use 'svn log -c <changeset> -g' for full commit message. Merge includes stability fixes and minor refactor of TableListField and ComplexTableField.
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@63806 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
f2abae719e
commit
634ed7b70c
@ -48,12 +48,6 @@ Object::useCustomClass('Datetime','SSDatetime',true);
|
|||||||
$path = Director::baseFolder().'/sapphire/parsers/';
|
$path = Director::baseFolder().'/sapphire/parsers/';
|
||||||
set_include_path(get_include_path() . PATH_SEPARATOR . $path);
|
set_include_path(get_include_path() . PATH_SEPARATOR . $path);
|
||||||
|
|
||||||
/**
|
|
||||||
* Register the {@link OpenIDAuthenticator OpenID authenticator}
|
|
||||||
*/
|
|
||||||
Authenticator::register_authenticator('MemberAuthenticator');
|
|
||||||
Authenticator::set_default_authenticator('MemberAuthenticator');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a default language different than english
|
* Define a default language different than english
|
||||||
*/
|
*/
|
||||||
|
@ -12,7 +12,8 @@ abstract class CliController extends Controller {
|
|||||||
|
|
||||||
function index() {
|
function index() {
|
||||||
// Always re-compile the manifest (?flush=1)
|
// Always re-compile the manifest (?flush=1)
|
||||||
ManifestBuilder::compileManifest();
|
ManifestBuilder::update_db_tables(DB::getConn()->tableList(), $_ALL_CLASSES);
|
||||||
|
ManifestBuilder::write_manifest();
|
||||||
|
|
||||||
foreach( ClassInfo::subclassesFor( $this->class ) as $subclass ) {
|
foreach( ClassInfo::subclassesFor( $this->class ) as $subclass ) {
|
||||||
echo $subclass;
|
echo $subclass;
|
||||||
|
@ -23,7 +23,7 @@ class ArrayData extends ViewableData {
|
|||||||
public function __construct($array) {
|
public function __construct($array) {
|
||||||
if(is_object($array)) {
|
if(is_object($array)) {
|
||||||
$this->array = self::object_to_array($array);
|
$this->array = self::object_to_array($array);
|
||||||
} elseif(is_array($array) && ArrayLib::is_associative($array)) {
|
} elseif(is_array($array) && (ArrayLib::is_associative($array) || count($array) === 0)) {
|
||||||
$this->array = $array;
|
$this->array = $array;
|
||||||
} else {
|
} else {
|
||||||
$this->array = $array;
|
$this->array = $array;
|
||||||
|
@ -44,8 +44,7 @@ class Convert extends Object {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
$val = str_replace(array('&','"',"'",'<','>'),array('&','"',''','<','>'),$val);
|
$val = str_replace(array('&','"',"'",'<','>'),array('&','"',''','<','>'),$val);
|
||||||
$val = preg_replace('^[a-zA-Z0-9\-_]','_', $val);
|
$val = preg_replace('/[^a-zA-Z0-9\-_]*/','', $val);
|
||||||
$val = preg_replace('^[0-9]*','', $val); //
|
|
||||||
return $val;
|
return $val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1705,10 +1705,9 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($fields) foreach($fields as $name => $level) {
|
if ($fields) foreach($fields as $name => $level) {
|
||||||
if(!isset($this->original[$name])) continue;
|
|
||||||
$changedFields[$name] = array(
|
$changedFields[$name] = array(
|
||||||
'before' => $this->original[$name],
|
'before' => array_key_exists($name, $this->original) ? $this->original[$name] : null,
|
||||||
'after' => $this->record[$name],
|
'after' => array_key_exists($name, $this->record) ? $this->record[$name] : null,
|
||||||
'level' => $level
|
'level' => $level
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1733,18 +1732,15 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
|||||||
} else {
|
} else {
|
||||||
$defaults = $this->stat('defaults');
|
$defaults = $this->stat('defaults');
|
||||||
// if a field is not existing or has strictly changed
|
// if a field is not existing or has strictly changed
|
||||||
if(!isset($this->record[$fieldName]) || $this->record[$fieldName] !== $val) {
|
if(!array_key_exists($fieldName, $this->record) || $this->record[$fieldName] !== $val) {
|
||||||
// TODO Add check for php-level defaults which are not set in the db
|
// TODO Add check for php-level defaults which are not set in the db
|
||||||
// TODO Add check for hidden input-fields (readonly) which are not set in the db
|
// TODO Add check for hidden input-fields (readonly) which are not set in the db
|
||||||
if(
|
// At the very least, the type has changed
|
||||||
// Main non type-based check
|
$this->changed[$fieldName] = 1;
|
||||||
(isset($this->record[$fieldName]) && $this->record[$fieldName] != $val)
|
|
||||||
) {
|
if(!array_key_exists($fieldName, $this->record) || $this->record[$fieldName] != $val) {
|
||||||
// Non-strict check fails, so value really changed, e.g. "abc" != "cde"
|
// Value has changed as well, not just the type
|
||||||
$this->changed[$fieldName] = 2;
|
$this->changed[$fieldName] = 2;
|
||||||
} else {
|
|
||||||
// Record change-level 1 if only the type changed, e.g. 0 !== NULL
|
|
||||||
$this->changed[$fieldName] = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// value is always saved back when strict check succeeds
|
// value is always saved back when strict check succeeds
|
||||||
|
@ -424,27 +424,31 @@ class SiteTree extends DataObject {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a breadcrumb trail to this page.
|
* Return a breadcrumb trail to this page. Excludes "hidden" pages
|
||||||
|
* (with ShowInMenus=0).
|
||||||
*
|
*
|
||||||
* @param int $maxDepth The maximum depth to traverse.
|
* @param int $maxDepth The maximum depth to traverse.
|
||||||
* @param boolean $unlinked Do not make page names links
|
* @param boolean $unlinked Do not make page names links
|
||||||
* @param string $stopAtPageType ClassName of a page to stop the upwards traversal.
|
* @param string $stopAtPageType ClassName of a page to stop the upwards traversal.
|
||||||
|
* @param boolean $showHidden Include pages marked with the attribute ShowInMenus = 0
|
||||||
* @return string The breadcrumb trail.
|
* @return string The breadcrumb trail.
|
||||||
*/
|
*/
|
||||||
public function Breadcrumbs($maxDepth = 20, $unlinked = false,
|
public function Breadcrumbs($maxDepth = 20, $unlinked = false, $stopAtPageType = false, $showHidden = false) {
|
||||||
$stopAtPageType = false) {
|
|
||||||
$page = $this;
|
$page = $this;
|
||||||
$parts = array();
|
$parts = array();
|
||||||
$i = 0;
|
$i = 0;
|
||||||
while(($page && (sizeof($parts) < $maxDepth)) ||
|
while(
|
||||||
($stopAtPageType && $page->ClassName != $stopAtPageType)) {
|
$page
|
||||||
if($page->ShowInMenus || ($page->ID == $this->ID)) {
|
&& (!$maxDepth || sizeof($parts) < $maxDepth)
|
||||||
if($page->URLSegment == 'home') {
|
&& ($stopAtPageType && $page->ClassName != $stopAtPageType)
|
||||||
$hasHome = true;
|
) {
|
||||||
|
if($showHidden || $page->ShowInMenus || ($page->ID == $this->ID)) {
|
||||||
|
if($page->URLSegment == 'home') $hasHome = true;
|
||||||
|
if(($page->ID == $this->ID) || $unlinked) {
|
||||||
|
$parts[] = Convert::raw2xml($page->Title);
|
||||||
|
} else {
|
||||||
|
$parts[] = ("<a href=\"" . $page->Link() . "\">" . Convert::raw2xml($page->Title) . "</a>");
|
||||||
}
|
}
|
||||||
$parts[] = (($page->ID == $this->ID) || $unlinked)
|
|
||||||
? Convert::raw2xml($page->Title)
|
|
||||||
: ("<a href=\"" . $page->Link() . "\">" . Convert::raw2xml($page->Title) . "</a>");
|
|
||||||
}
|
}
|
||||||
$page = $page->Parent;
|
$page = $page->Parent;
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,10 @@ class Boolean extends DBField {
|
|||||||
return ($this->value) ? "yes" : "no";
|
return ($this->value) ? "yes" : "no";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function NiceAsBoolean() {
|
||||||
|
return ($this->value) ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves this field to the given data object.
|
* Saves this field to the given data object.
|
||||||
*/
|
*/
|
||||||
|
@ -24,27 +24,6 @@
|
|||||||
.ComplexTableField {
|
.ComplexTableField {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
.ComplexTableField .PageControls {
|
|
||||||
margin: 5px 0;
|
|
||||||
text-align:center;
|
|
||||||
display:block;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
background:#ebeadb;
|
|
||||||
border: 1px #cbc7b7 solid;
|
|
||||||
}
|
|
||||||
.ComplexTableField .PageControls * {
|
|
||||||
display:inline;
|
|
||||||
vertical-align: middle;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.ComplexTableField .PageControls .Last{
|
|
||||||
float:right; display:block;
|
|
||||||
width:40px; text-align:right;
|
|
||||||
}
|
|
||||||
.ComplexTableField .PageControls .First{
|
|
||||||
float:left; display:block;
|
|
||||||
width:40px; text-align:left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ComplexTableField tbody td {
|
.ComplexTableField tbody td {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -171,6 +171,33 @@ form .TableField .message {
|
|||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.TableListField .PageControls {
|
||||||
|
margin: 5px 0;
|
||||||
|
text-align:center;
|
||||||
|
display:block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
background:#ebeadb;
|
||||||
|
border: 1px #cbc7b7 solid;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.TableListField .PageControls * {
|
||||||
|
display:inline;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.TableListField .PageControls .Last{
|
||||||
|
display: block;
|
||||||
|
width: 40px;
|
||||||
|
text-align: right;
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
.TableListField .PageControls .First{
|
||||||
|
float:left; display:block;
|
||||||
|
width:40px; text-align:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#Pagination {
|
#Pagination {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
@ -93,7 +93,12 @@ class Email extends ViewableData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function attachFile($filename, $attachedFilename = null, $mimetype = null) {
|
public function attachFile($filename, $attachedFilename = null, $mimetype = null) {
|
||||||
$this->attachFileFromString(file_get_contents(Director::getAbsFile($filename)), $attachedFilename, $mimetype);
|
$absoluteFileName = Director::getAbsFile($filename);
|
||||||
|
if(file_exists($absoluteFileName)) {
|
||||||
|
$this->attachFileFromString(file_get_contents($absoluteFileName), $attachedFilename, $mimetype);
|
||||||
|
} else {
|
||||||
|
user_error("Could not attach '$absoluteFileName' to email. File does not exist.", E_USER_NOTICE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setFormat($format) {
|
public function setFormat($format) {
|
||||||
|
@ -40,7 +40,7 @@ class BankAccountField extends FormField {
|
|||||||
$field = new FieldGroup($this->name);
|
$field = new FieldGroup($this->name);
|
||||||
$field->setID("{$this->name}_Holder");
|
$field->setID("{$this->name}_Holder");
|
||||||
|
|
||||||
$valueArr = $this->valueArray;
|
$valueArr = $this->valueArr;
|
||||||
|
|
||||||
$valueArr = self::convert_format_nz($valueArr);
|
$valueArr = self::convert_format_nz($valueArr);
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ class CompositeField extends FormField {
|
|||||||
protected $columnCount = null;
|
protected $columnCount = null;
|
||||||
|
|
||||||
public function __construct($children = null) {
|
public function __construct($children = null) {
|
||||||
if(is_a($children, 'FieldSet')) {
|
if($children instanceof FieldSet) {
|
||||||
$this->children = $children;
|
$this->children = $children;
|
||||||
} elseif(is_array($children)) {
|
} elseif(is_array($children)) {
|
||||||
$this->children = new FieldSet($children);
|
$this->children = new FieldSet($children);
|
||||||
@ -239,7 +239,7 @@ class CompositeField extends FormField {
|
|||||||
|
|
||||||
$valid = true;
|
$valid = true;
|
||||||
foreach($this->children as $idx => $child){
|
foreach($this->children as $idx => $child){
|
||||||
$valid = ($child->validate($validator) && $valid);
|
$valid = ($child && $child->validate($validator) && $valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $valid;
|
return $valid;
|
||||||
|
@ -38,9 +38,8 @@ class DateField extends TextField {
|
|||||||
return $field;
|
return $field;
|
||||||
}
|
}
|
||||||
|
|
||||||
function jsValidation($formID = null)
|
function jsValidation() {
|
||||||
{
|
$formID = $this->form->FormName();
|
||||||
if(!$formID)$formID = $this->form->FormName();
|
|
||||||
$error = _t('DateField.VALIDATIONJS', 'Please enter a valid date format (DD/MM/YYYY).');
|
$error = _t('DateField.VALIDATIONJS', 'Please enter a valid date format (DD/MM/YYYY).');
|
||||||
$jsFunc =<<<JS
|
$jsFunc =<<<JS
|
||||||
Behaviour.register({
|
Behaviour.register({
|
||||||
@ -77,7 +76,7 @@ JS;
|
|||||||
{
|
{
|
||||||
$validator->validationError(
|
$validator->validationError(
|
||||||
$this->name,
|
$this->name,
|
||||||
_t('DateField.VALIDDATEFORMAT', "Please enter a valid date format (DD/MM/YYYY)."),
|
_t('DateField.VALIDDATEFORMAT', "Please enter a valid date format (DD/MM/YYYY)."),
|
||||||
"validation",
|
"validation",
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
@ -130,5 +129,9 @@ class DateField_Disabled extends DateField {
|
|||||||
function php() {
|
function php() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validate($validator) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
@ -370,7 +370,7 @@ class TableField extends TableListField {
|
|||||||
if($dataObjects) {
|
if($dataObjects) {
|
||||||
foreach ($dataObjects as $objectid => $fieldValues) {
|
foreach ($dataObjects as $objectid => $fieldValues) {
|
||||||
// we have to "sort" new data first, and process it in a seperate saveData-call (see setValue())
|
// we have to "sort" new data first, and process it in a seperate saveData-call (see setValue())
|
||||||
if($objectid === "new") continue;
|
if($objectid === "new") continue;
|
||||||
|
|
||||||
// extra data was creating fields, but
|
// extra data was creating fields, but
|
||||||
if($this->extraData) {
|
if($this->extraData) {
|
||||||
@ -381,7 +381,7 @@ class TableField extends TableListField {
|
|||||||
$obj = new $this->sourceClass();
|
$obj = new $this->sourceClass();
|
||||||
|
|
||||||
if($ExistingValues) {
|
if($ExistingValues) {
|
||||||
$obj->ID = $objectid;
|
$obj = DataObject::get_by_id($this->sourceClass, $objectid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy: Use the filter as a predefined relationship-ID
|
// Legacy: Use the filter as a predefined relationship-ID
|
||||||
@ -553,7 +553,10 @@ JS;
|
|||||||
if($data['methodName'] != 'delete'){
|
if($data['methodName'] != 'delete'){
|
||||||
$fields = $this->FieldSet();
|
$fields = $this->FieldSet();
|
||||||
$fields = new FieldSet($fields);
|
$fields = new FieldSet($fields);
|
||||||
|
foreach($fields as $field){
|
||||||
|
$valid = $field->validate($this) && $valid;
|
||||||
|
}
|
||||||
|
return $valid;
|
||||||
}else{
|
}else{
|
||||||
return $valid;
|
return $valid;
|
||||||
}
|
}
|
||||||
|
@ -211,6 +211,8 @@ class TableListField extends FormField {
|
|||||||
*/
|
*/
|
||||||
public $fieldFormatting = array();
|
public $fieldFormatting = array();
|
||||||
|
|
||||||
|
public $csvFieldFormatting = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
@ -227,6 +229,8 @@ class TableListField extends FormField {
|
|||||||
*/
|
*/
|
||||||
protected $extraLinkParams;
|
protected $extraLinkParams;
|
||||||
|
|
||||||
|
protected $__cachedQuery;
|
||||||
|
|
||||||
function __construct($name, $sourceClass, $fieldList = null, $sourceFilter = null,
|
function __construct($name, $sourceClass, $fieldList = null, $sourceFilter = null,
|
||||||
$sourceSort = null, $sourceJoin = null) {
|
$sourceSort = null, $sourceJoin = null) {
|
||||||
|
|
||||||
@ -259,6 +263,19 @@ class TableListField extends FormField {
|
|||||||
return $this->FieldHolder();
|
return $this->FieldHolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static $url_handlers = array(
|
||||||
|
'item/$ID' => 'handleItem',
|
||||||
|
'$Action!' => '$Action',
|
||||||
|
);
|
||||||
|
|
||||||
|
function sourceClass() {
|
||||||
|
return $this->sourceClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleItem($request) {
|
||||||
|
return new TableListField_ItemRequest($this, $request->param('ID'));
|
||||||
|
}
|
||||||
|
|
||||||
function FieldHolder() {
|
function FieldHolder() {
|
||||||
|
|
||||||
if($this->clickAction) {
|
if($this->clickAction) {
|
||||||
@ -321,7 +338,7 @@ JS
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->__cachedSQL) {
|
if($this->__cachedQuery) {
|
||||||
$query = $this->__cachedQuery;
|
$query = $this->__cachedQuery;
|
||||||
} else {
|
} else {
|
||||||
$query = $this->__cachedQuery = $this->getQuery();
|
$query = $this->__cachedQuery = $this->getQuery();
|
||||||
@ -423,7 +440,7 @@ JS
|
|||||||
*/
|
*/
|
||||||
function getQuery() {
|
function getQuery() {
|
||||||
if($this->customQuery) {
|
if($this->customQuery) {
|
||||||
$query = $this->customQuery;
|
$query = clone $this->customQuery;
|
||||||
$baseClass = ClassInfo::baseDataClass($this->sourceClass);
|
$baseClass = ClassInfo::baseDataClass($this->sourceClass);
|
||||||
$query->select[] = "{$baseClass}.ID AS ID";
|
$query->select[] = "{$baseClass}.ID AS ID";
|
||||||
$query->select[] = "{$baseClass}.ClassName AS ClassName";
|
$query->select[] = "{$baseClass}.ClassName AS ClassName";
|
||||||
@ -448,7 +465,7 @@ JS
|
|||||||
$query->orderby = $SQL_sort;
|
$query->orderby = $SQL_sort;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return clone $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCsvQuery() {
|
function getCsvQuery() {
|
||||||
@ -605,6 +622,8 @@ JS
|
|||||||
$summaryFields[] = new ArrayData(array(
|
$summaryFields[] = new ArrayData(array(
|
||||||
'Function' => $function,
|
'Function' => $function,
|
||||||
'SummaryValue' => $summaryValue,
|
'SummaryValue' => $summaryValue,
|
||||||
|
'Name' => DBField::create('Varchar', $fieldName),
|
||||||
|
'Title' => DBField::create('Varchar', $fieldTitle),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return new DataObjectSet($summaryFields);
|
return new DataObjectSet($summaryFields);
|
||||||
@ -879,6 +898,7 @@ JS
|
|||||||
function export() {
|
function export() {
|
||||||
$now = Date("d-m-Y-H-i");
|
$now = Date("d-m-Y-H-i");
|
||||||
$fileName = "export-$now.csv";
|
$fileName = "export-$now.csv";
|
||||||
|
|
||||||
$separator = $this->csvSeparator;
|
$separator = $this->csvSeparator;
|
||||||
$csvColumns = ($this->fieldListCsv) ? $this->fieldListCsv : $this->fieldList;
|
$csvColumns = ($this->fieldListCsv) ? $this->fieldListCsv : $this->fieldList;
|
||||||
$fileData = "";
|
$fileData = "";
|
||||||
@ -889,14 +909,15 @@ JS
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get data
|
// get data
|
||||||
$dataQuery = $this->getCsvQuery();
|
if(isset($this->customSourceItems)){
|
||||||
$records = $dataQuery->execute();
|
$items = $this->customSourceItems;
|
||||||
|
}else{
|
||||||
$sourceClass = $this->sourceClass;
|
$dataQuery = $this->getCsvQuery();
|
||||||
$dataobject = new $sourceClass();
|
$records = $dataQuery->execute();
|
||||||
|
$sourceClass = $this->sourceClass;
|
||||||
// @todo Will create a large unpaginated dataobjectset based on how many records are in table (performance issue)
|
$dataobject = new $sourceClass();
|
||||||
$items = $dataobject->buildDataObjectSet($records, 'DataObjectSet');
|
$items = $dataobject->buildDataObjectSet($records, 'DataObjectSet');
|
||||||
|
}
|
||||||
|
|
||||||
$fieldItems = new DataObjectSet();
|
$fieldItems = new DataObjectSet();
|
||||||
if($items && $items->count()) foreach($items as $item) {
|
if($items && $items->count()) foreach($items as $item) {
|
||||||
@ -911,26 +932,31 @@ JS
|
|||||||
|
|
||||||
if($fieldItems) {
|
if($fieldItems) {
|
||||||
foreach($fieldItems as $fieldItem) {
|
foreach($fieldItems as $fieldItem) {
|
||||||
|
$columnData = array();
|
||||||
$fields = $fieldItem->Fields();
|
$fields = $fieldItem->Fields();
|
||||||
foreach($fields as $field) {
|
foreach($fields as $field) {
|
||||||
// replace <br/ >s with newlines for csv
|
|
||||||
$field->Value = str_replace('<br />', "\n", $field->Value);
|
|
||||||
// remove double quotes
|
|
||||||
$field->Value = str_replace('"', "", $field->Value);
|
|
||||||
$fileData .= "\"" . $field->Value . "\"";
|
|
||||||
if($field->Last()) {
|
|
||||||
$fileData .= "\n";
|
|
||||||
} else {
|
|
||||||
$fileData .= $this->csvSeparator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$value = $field->Value;
|
||||||
|
|
||||||
|
// TODO This should be replaced with casting
|
||||||
|
if(array_key_exists($field->Name, $this->csvFieldFormatting)) {
|
||||||
|
$format = str_replace('$value', "__VAL__", $this->csvFieldFormatting[$columnName]);
|
||||||
|
$format = preg_replace('/\$([A-Za-z0-9-_]+)/','$item->$1', $format);
|
||||||
|
$format = str_replace('__VAL__', '$value', $format);
|
||||||
|
eval('$value = "' . $format . '";');
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = str_replace(array("\r", "\n"), "\n", $value);
|
||||||
|
$tmpColumnData = "\"" . str_replace("\"", "\"\"", $value) . "\"";
|
||||||
|
$columnData[] = $tmpColumnData;
|
||||||
|
}
|
||||||
|
$fileData .= implode($separator, $columnData);
|
||||||
|
$fileData .= "\n";
|
||||||
}
|
}
|
||||||
return HTTPRequest::send_file($fileData, $fileName);
|
return HTTPRequest::send_file($fileData, $fileName);
|
||||||
} else {
|
} else {
|
||||||
user_error("No records found", E_USER_ERROR);
|
user_error("No records found", E_USER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -948,12 +974,20 @@ JS
|
|||||||
Requirements::css(CMS_DIR . '/css/typography.css');
|
Requirements::css(CMS_DIR . '/css/typography.css');
|
||||||
Requirements::css(CMS_DIR . '/css/cms_right.css');
|
Requirements::css(CMS_DIR . '/css/cms_right.css');
|
||||||
Requirements::css(SAPPHIRE_DIR . '/css/TableListField_print.css');
|
Requirements::css(SAPPHIRE_DIR . '/css/TableListField_print.css');
|
||||||
$vd = new ViewableData();
|
|
||||||
return $vd->customise(array(
|
unset($this->cachedSourceItems);
|
||||||
'Content' => $this->customise(array(
|
$oldShowPagination = $this->showPagination;
|
||||||
'Print' => true
|
$this->showPagination = false;
|
||||||
))->renderWith($this->template)
|
$oldLimit = ini_get('max_execution_time');
|
||||||
))->renderWith('TableListField_printable');
|
set_time_limit(0);
|
||||||
|
|
||||||
|
|
||||||
|
$result = $this->renderWith(array($this->template . '_printable', 'TableListField_printable'));
|
||||||
|
|
||||||
|
$this->showPagination = $oldShowPagination;
|
||||||
|
set_time_limit($oldLimit);
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function PrintLink() {
|
function PrintLink() {
|
||||||
@ -1010,6 +1044,10 @@ JS
|
|||||||
$this->fieldFormatting = $formatting;
|
$this->fieldFormatting = $formatting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setCSVFieldFormatting($formatting) {
|
||||||
|
$this->csvFieldFormatting = $formatting;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return String
|
* @return String
|
||||||
*/
|
*/
|
||||||
@ -1024,19 +1062,19 @@ JS
|
|||||||
// adding this to TODO probably add a method to the classes
|
// adding this to TODO probably add a method to the classes
|
||||||
// to return they're translated string
|
// to return they're translated string
|
||||||
// added by ruibarreiros @ 27/11/2007
|
// added by ruibarreiros @ 27/11/2007
|
||||||
return singleton($this->sourceClass)->singular_name();
|
return $this->sourceClass ? singleton($this->sourceClass)->singular_name() : $this->Name();
|
||||||
}
|
}
|
||||||
|
|
||||||
function NameSingular() {
|
function NameSingular() {
|
||||||
// same as Title()
|
// same as Title()
|
||||||
// added by ruibarreiros @ 27/11/2007
|
// added by ruibarreiros @ 27/11/2007
|
||||||
return singleton($this->sourceClass)->singular_name();
|
return $this->sourceClass ? singleton($this->sourceClass)->singular_name() : $this->Name();
|
||||||
}
|
}
|
||||||
|
|
||||||
function NamePlural() {
|
function NamePlural() {
|
||||||
// same as Title()
|
// same as Title()
|
||||||
// added by ruibarreiros @ 27/11/2007
|
// added by ruibarreiros @ 27/11/2007
|
||||||
return singleton($this->sourceClass)->plural_name();
|
return $this->sourceClass ? singleton($this->sourceClass)->plural_name() : $this->Name();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTemplate($template) {
|
function setTemplate($template) {
|
||||||
@ -1216,6 +1254,16 @@ class TableListField_Item extends ViewableData {
|
|||||||
return $this->parent->Can($mode);
|
return $this->parent->Can($mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Link() {
|
||||||
|
if($this->parent->getForm()) {
|
||||||
|
return Controller::join_links($this->parent->Link() . "item/" . $this->item->ID);
|
||||||
|
} else {
|
||||||
|
// allow for instanciation of this FormField outside of a controller/form
|
||||||
|
// context (e.g. for unit tests)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all row-based actions not disallowed through permissions.
|
* Returns all row-based actions not disallowed through permissions.
|
||||||
* See TableListField->Action for a similiar dummy-function to work
|
* See TableListField->Action for a similiar dummy-function to work
|
||||||
@ -1241,16 +1289,6 @@ class TableListField_Item extends ViewableData {
|
|||||||
return $allowedActions;
|
return $allowedActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Link() {
|
|
||||||
if($this->parent->getForm()) {
|
|
||||||
return Controller::join_links($this->parent->Link() . "item/" . $this->item->ID);
|
|
||||||
} else {
|
|
||||||
// allow for instanciation of this FormField outside of a controller/form
|
|
||||||
// context (e.g. for unit tests)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function BaseLink() {
|
function BaseLink() {
|
||||||
user_error("TableListField_Item::BaseLink() deprecated, use Link() instead", E_USER_NOTICE);
|
user_error("TableListField_Item::BaseLink() deprecated, use Link() instead", E_USER_NOTICE);
|
||||||
return $this->Link();
|
return $this->Link();
|
||||||
@ -1293,7 +1331,78 @@ class TableListField_Item extends ViewableData {
|
|||||||
function isReadonly() {
|
function isReadonly() {
|
||||||
return $this->parent->Can('delete');
|
return $this->parent->Can('delete');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TableListField_ItemRequest extends RequestHandlingData {
|
||||||
|
protected $ctf;
|
||||||
|
protected $itemID;
|
||||||
|
protected $methodName;
|
||||||
|
|
||||||
|
static $url_handlers = array(
|
||||||
|
'$Action!' => '$Action',
|
||||||
|
'' => 'index',
|
||||||
|
);
|
||||||
|
|
||||||
|
function Link() {
|
||||||
|
return $this->ctf->Link() . '/item/' . $this->itemID;
|
||||||
|
}
|
||||||
|
|
||||||
|
function __construct($ctf, $itemID) {
|
||||||
|
$this->ctf = $ctf;
|
||||||
|
$this->itemID = $itemID;
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete() {
|
||||||
|
if($this->ctf->Can('delete') !== true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->dataObj()->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the data object being manipulated
|
||||||
|
*/
|
||||||
|
function dataObj() {
|
||||||
|
// used to discover fields if requested and for population of field
|
||||||
|
if(is_numeric($this->itemID)) {
|
||||||
|
// we have to use the basedataclass, otherwise we might exclude other subclasses
|
||||||
|
return DataObject::get_by_id(ClassInfo::baseDataClass(Object::getCustomClass($this->ctf->sourceClass())), $this->itemID);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the db-fieldname of the currently used has_one-relationship.
|
||||||
|
*/
|
||||||
|
function getParentIdName( $parentClass, $childClass ) {
|
||||||
|
return $this->getParentIdNameRelation( $childClass, $parentClass, 'has_one' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manually overwrites the parent-ID relations.
|
||||||
|
* @see setParentClass()
|
||||||
|
*
|
||||||
|
* @param String $str Example: FamilyID (when one Individual has_one Family)
|
||||||
|
*/
|
||||||
|
function setParentIdName($str) {
|
||||||
|
$this->parentIdName = $str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the db-fieldname of the currently used relationship.
|
||||||
|
*/
|
||||||
|
function getParentIdNameRelation($parentClass, $childClass, $relation) {
|
||||||
|
if($this->parentIdName) return $this->parentIdName;
|
||||||
|
|
||||||
|
$relations = singleton($parentClass)->$relation();
|
||||||
|
$classes = ClassInfo::ancestry($childClass);
|
||||||
|
foreach($relations as $k => $v) {
|
||||||
|
if(array_key_exists($v, $classes)) return $k . 'ID';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
?>
|
?>
|
@ -4,13 +4,16 @@ GB_RefreshLink = "";
|
|||||||
ComplexTableField = Class.create();
|
ComplexTableField = Class.create();
|
||||||
ComplexTableField.prototype = {
|
ComplexTableField.prototype = {
|
||||||
|
|
||||||
// TODO adjust dynamically
|
// These are defaults used if setPopupSize encounters errors
|
||||||
popupWidth: 560,
|
defaultPopupWidth: 560,
|
||||||
popupHeight: 390,
|
defaultPopupHeight: 390,
|
||||||
|
|
||||||
initialize: function() {
|
initialize: function() {
|
||||||
var rules = {};
|
var rules = {};
|
||||||
rules['#'+this.id+' table.data a.popuplink'] = {onclick: this.openPopup.bind(this)};
|
rules['#'+this.id+' table.data a.popuplink'] = {onclick: this.openPopup.bind(this)};
|
||||||
|
|
||||||
|
// Assume that the delete link uses the deleteRecord method
|
||||||
|
rules['#'+this.id+' table.data a.deletelink'] = {onclick: this.deleteRecord.bind(this)};
|
||||||
rules['#'+this.id+' table.data tbody td'] = {onclick: this.openPopup.bind(this)};
|
rules['#'+this.id+' table.data tbody td'] = {onclick: this.openPopup.bind(this)};
|
||||||
|
|
||||||
// invoke row action-link based on default-action set in classname
|
// invoke row action-link based on default-action set in classname
|
||||||
@ -25,10 +28,22 @@ ComplexTableField.prototype = {
|
|||||||
}
|
}
|
||||||
Behaviour.register('ComplexTableField_'+this.id,rules);
|
Behaviour.register('ComplexTableField_'+this.id,rules);
|
||||||
|
|
||||||
|
this.setPopupSize();
|
||||||
|
|
||||||
// HACK If already in a popup, we can't allow add (doesn't save existing relation correctly)
|
// HACK If already in a popup, we can't allow add (doesn't save existing relation correctly)
|
||||||
if(window != top) $$('#'+this.id+' table.data a.addlink').each(function(el) {Element.hide(el);});
|
if(window != top) $$('#'+this.id+' table.data a.addlink').each(function(el) {Element.hide(el);});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setPopupSize: function() {
|
||||||
|
try {
|
||||||
|
this.popupHeight = parseInt($(this.id + '_PopupHeight').value);
|
||||||
|
this.popupWidth = parseInt($(this.id + '_PopupWidth').value);
|
||||||
|
} catch (ex) {
|
||||||
|
this.popupHeight = this.defaultPopupHeight;
|
||||||
|
this.popupWidth = this.defaultPopupWidth;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
getDefaultAction: function() {
|
getDefaultAction: function() {
|
||||||
// try to get link class from <td class="action default"><a href="...
|
// try to get link class from <td class="action default"><a href="...
|
||||||
var links = $$('#'+this.id+' table.data tbody .default a');
|
var links = $$('#'+this.id+' table.data tbody .default a');
|
||||||
@ -45,6 +60,8 @@ ComplexTableField.prototype = {
|
|||||||
// of opening a nested lightwindow
|
// of opening a nested lightwindow
|
||||||
if(window != top) return true;
|
if(window != top) return true;
|
||||||
|
|
||||||
|
this.setPopupSize();
|
||||||
|
|
||||||
var el,type;
|
var el,type;
|
||||||
var popupLink = "";
|
var popupLink = "";
|
||||||
if(_popupLink) {
|
if(_popupLink) {
|
||||||
|
@ -102,6 +102,7 @@ function require(fieldName,cachedError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var baseEl;
|
var baseEl;
|
||||||
|
var fieldHolder = el;
|
||||||
|
|
||||||
// Sometimes require events are triggered of
|
// Sometimes require events are triggered of
|
||||||
// associative elements like labels ;-p
|
// associative elements like labels ;-p
|
||||||
@ -155,7 +156,8 @@ function require(fieldName,cachedError) {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
if(!hasHadFormError()) {
|
if(!hasHadFormError()) {
|
||||||
clearErrorMessage(baseEl.parentNode);
|
if(baseEl) fieldHolder = baseEl.parentNode;
|
||||||
|
clearErrorMessage(fieldHolder);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -195,6 +197,12 @@ function findParentLabel(el) {
|
|||||||
return findParentLabel(el.parentNode);
|
return findParentLabel(el.parentNode);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Try to find a label with a for value of this field.
|
||||||
|
if(el.id) {
|
||||||
|
var labels = $$('label[for=' + el.id + ']');
|
||||||
|
if(labels && labels.length > 0) return labels[0].innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
return findParentLabel(el.parentNode);
|
return findParentLabel(el.parentNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ abstract class Authenticator extends Object {
|
|||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private static $authenticators = array();
|
private static $authenticators = array('MemberAuthenticator');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to influence the order of authenticators on the login-screen
|
* Used to influence the order of authenticators on the login-screen
|
||||||
@ -24,7 +24,7 @@ abstract class Authenticator extends Object {
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private static $default_authenticator = '';
|
private static $default_authenticator = 'MemberAuthenticator';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,7 +107,9 @@ abstract class Authenticator extends Object {
|
|||||||
*/
|
*/
|
||||||
public static function unregister_authenticator($authenticator) {
|
public static function unregister_authenticator($authenticator) {
|
||||||
if(call_user_func(array($authenticator, 'on_unregister')) === true) {
|
if(call_user_func(array($authenticator, 'on_unregister')) === true) {
|
||||||
unset(self::$authenticators[$authenticator]);
|
if(in_array($authenticator, self::$authenticators)) {
|
||||||
|
unset(self::$authenticators[array_search($authenticator, self::$authenticators)]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ abstract class LoginForm extends Form {
|
|||||||
|
|
||||||
public function getAuthenticator() {
|
public function getAuthenticator() {
|
||||||
if(!class_exists($this->authenticator_class) || !is_subclass_of($this->authenticator_class, 'Authenticator')) {
|
if(!class_exists($this->authenticator_class) || !is_subclass_of($this->authenticator_class, 'Authenticator')) {
|
||||||
user_error('The form uses an invalid authenticator class!', E_USER_ERROR);
|
user_error("The form uses an invalid authenticator class! '{$this->authenticator_class}' is not a subclass of 'Authenticator'", E_USER_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
*/
|
*/
|
||||||
class MemberLoginForm extends LoginForm {
|
class MemberLoginForm extends LoginForm {
|
||||||
|
|
||||||
|
protected $authenticator_class = 'MemberAuthenticator';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
@ -22,11 +24,13 @@ class MemberLoginForm extends LoginForm {
|
|||||||
* @param bool $checkCurrentUser If set to TRUE, it will be checked if a
|
* @param bool $checkCurrentUser If set to TRUE, it will be checked if a
|
||||||
* the user is currently logged in, and if
|
* the user is currently logged in, and if
|
||||||
* so, only a logout button will be rendered
|
* so, only a logout button will be rendered
|
||||||
|
* @param string $authenticatorClassName Name of the authenticator class that this form uses.
|
||||||
*/
|
*/
|
||||||
function __construct($controller, $name, $fields = null, $actions = null,
|
function __construct($controller, $name, $fields = null, $actions = null,
|
||||||
$checkCurrentUser = true) {
|
$checkCurrentUser = true) {
|
||||||
|
|
||||||
$this->authenticator_class = 'MemberAuthenticator';
|
// This is now set on the class directly to make it easier to create subclasses
|
||||||
|
// $this->authenticator_class = $authenticatorClassName;
|
||||||
|
|
||||||
$customCSS = project() . '/css/member_login.css';
|
$customCSS = project() . '/css/member_login.css';
|
||||||
if(Director::fileExists($customCSS)) {
|
if(Director::fileExists($customCSS)) {
|
||||||
@ -48,10 +52,16 @@ class MemberLoginForm extends LoginForm {
|
|||||||
new HiddenField("AuthenticationMethod", null, $this->authenticator_class, $this),
|
new HiddenField("AuthenticationMethod", null, $this->authenticator_class, $this),
|
||||||
new TextField("Email", _t('Member.EMAIL'),
|
new TextField("Email", _t('Member.EMAIL'),
|
||||||
Session::get('SessionForms.MemberLoginForm.Email'), null, $this),
|
Session::get('SessionForms.MemberLoginForm.Email'), null, $this),
|
||||||
new EncryptField("Password", _t('Member.PASSWORD'), null, $this),
|
new EncryptField("Password", _t('Member.PASSWORD'), null, $this)
|
||||||
new CheckboxField("Remember", _t('Member.REMEMBERME', "Remember me next time?"),
|
|
||||||
Session::get('SessionForms.MemberLoginForm.Remember'), $this)
|
|
||||||
);
|
);
|
||||||
|
if(Security::$autologin_enabled) {
|
||||||
|
$fields->push(new CheckboxField(
|
||||||
|
"Remember",
|
||||||
|
_t('Member.REMEMBERME', "Remember me next time?"),
|
||||||
|
Session::get('SessionForms.MemberLoginForm.Remember'),
|
||||||
|
$this
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(!$actions) {
|
if(!$actions) {
|
||||||
$actions = new FieldSet(
|
$actions = new FieldSet(
|
||||||
@ -109,7 +119,7 @@ class MemberLoginForm extends LoginForm {
|
|||||||
Session::clear("BackURL");
|
Session::clear("BackURL");
|
||||||
Director::redirect($backURL);
|
Director::redirect($backURL);
|
||||||
} else {
|
} else {
|
||||||
Director::redirect(Security::default_login_dest());
|
Director::redirectBack();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Session::set('SessionForms.MemberLoginForm.Email', $data['Email']);
|
Session::set('SessionForms.MemberLoginForm.Email', $data['Email']);
|
||||||
@ -187,7 +197,7 @@ class MemberLoginForm extends LoginForm {
|
|||||||
$member->sendInfo('forgotPassword', array('PasswordResetLink' =>
|
$member->sendInfo('forgotPassword', array('PasswordResetLink' =>
|
||||||
Security::getPasswordResetLink($member->AutoLoginHash)));
|
Security::getPasswordResetLink($member->AutoLoginHash)));
|
||||||
|
|
||||||
Director::redirect('Security/passwordsent/?email=' . urlencode($data['Email']));
|
Director::redirect('Security/passwordsent/' . urlencode($data['Email']));
|
||||||
|
|
||||||
} else if($data['Email']) {
|
} else if($data['Email']) {
|
||||||
$this->sessionMessage(
|
$this->sessionMessage(
|
||||||
|
@ -52,6 +52,14 @@ class Security extends Controller {
|
|||||||
*/
|
*/
|
||||||
protected static $useSalt = true;
|
protected static $useSalt = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Showing "Remember me"-checkbox
|
||||||
|
* on loginform, and saving encrypted credentials to a cookie.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public static $autologin_enabled = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Location of word list to use for generating passwords
|
* Location of word list to use for generating passwords
|
||||||
*
|
*
|
||||||
@ -207,8 +215,7 @@ class Security extends Controller {
|
|||||||
|
|
||||||
$authenticators = Authenticator::get_authenticators();
|
$authenticators = Authenticator::get_authenticators();
|
||||||
if(in_array($authenticator, $authenticators)) {
|
if(in_array($authenticator, $authenticators)) {
|
||||||
return call_user_func(array($authenticator, 'get_login_form'),
|
return call_user_func(array($authenticator, 'get_login_form'), $this);
|
||||||
$this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<% if Markable %><th width="16"> </th><% end_if %>
|
<% if Markable %><th width="16"> </th><% end_if %>
|
||||||
<th><i>$SummaryTitle</i></th>
|
<th><i>$SummaryTitle</i></th>
|
||||||
<% control SummaryFields %>
|
<% control SummaryFields %>
|
||||||
<th<% if Function %> class="$Function"<% end_if %>>$SummaryValue</th>
|
<th class="field-$Name.HTMLATT<% if Function %> $Function<% end_if %>">$SummaryValue</th>
|
||||||
<% end_control %>
|
<% end_control %>
|
||||||
<% if Can(delete) %><th width="18"> </th><% end_if %>
|
<% if Can(delete) %><th width="18"> </th><% end_if %>
|
@ -1,9 +1,9 @@
|
|||||||
<div id="$id" class="$CSSClasses TableField field">
|
<div id="$id" class="$CSSClasses field">
|
||||||
<% if Print %><% else %><% include TableListField_PageControls %><% end_if %>
|
<% if Print %><% else %><% include TableListField_PageControls %><% end_if %>
|
||||||
<table class="data">
|
<table class="data">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<% if Markable %><th width="16"> </th><% end_if %>
|
<% if Markable %><th width="16"><% if MarkableTitle %>$MarkableTitle<% else %> <% end_if %></th><% end_if %>
|
||||||
<% if Print %>
|
<% if Print %>
|
||||||
<% control Headings %>
|
<% control Headings %>
|
||||||
<th class="$Name">
|
<th class="$Name">
|
||||||
|
@ -241,6 +241,20 @@ class DataObjectTest extends SapphireTest {
|
|||||||
),
|
),
|
||||||
'Changed fields are correctly detected while ignoring type changes (level=2)'
|
'Changed fields are correctly detected while ignoring type changes (level=2)'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$newPage = new Page();
|
||||||
|
$newPage->Title = "New Page Title";
|
||||||
|
$this->assertEquals(
|
||||||
|
$newPage->getChangedFields(false, 2),
|
||||||
|
array(
|
||||||
|
'Title' => array(
|
||||||
|
'before' => null,
|
||||||
|
'after' => 'New Page Title',
|
||||||
|
'level' => 2
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'Initialised fields are correctly detected as full changes'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testRandomSort() {
|
function testRandomSort() {
|
||||||
|
@ -11,6 +11,11 @@ class TableListFieldTest extends SapphireTest {
|
|||||||
"D" => "Col D",
|
"D" => "Col D",
|
||||||
"E" => "Col E",
|
"E" => "Col E",
|
||||||
));
|
));
|
||||||
|
// A TableListField must be inside a form for its links to be generated
|
||||||
|
$form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldSet(
|
||||||
|
$table
|
||||||
|
), new FieldSet());
|
||||||
|
|
||||||
$result = $table->FieldHolder();
|
$result = $table->FieldHolder();
|
||||||
|
|
||||||
// Do a quick check to ensure that some of the D() and getE() values got through
|
// Do a quick check to ensure that some of the D() and getE() values got through
|
||||||
@ -28,6 +33,11 @@ class TableListFieldTest extends SapphireTest {
|
|||||||
"D" => "Col D",
|
"D" => "Col D",
|
||||||
"E" => "Col E",
|
"E" => "Col E",
|
||||||
));
|
));
|
||||||
|
// A TableListField must be inside a form for its links to be generated
|
||||||
|
$form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldSet(
|
||||||
|
$table
|
||||||
|
), new FieldSet());
|
||||||
|
|
||||||
$items = $table->sourceItems();
|
$items = $table->sourceItems();
|
||||||
$this->assertNotNull($items);
|
$this->assertNotNull($items);
|
||||||
|
|
||||||
@ -44,6 +54,11 @@ class TableListFieldTest extends SapphireTest {
|
|||||||
"D" => "Col D",
|
"D" => "Col D",
|
||||||
"E" => "Col E",
|
"E" => "Col E",
|
||||||
));
|
));
|
||||||
|
// A TableListField must be inside a form for its links to be generated
|
||||||
|
$form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldSet(
|
||||||
|
$table
|
||||||
|
), new FieldSet());
|
||||||
|
|
||||||
$table->ShowPagination = true;
|
$table->ShowPagination = true;
|
||||||
$table->PageSize = 2;
|
$table->PageSize = 2;
|
||||||
|
|
||||||
@ -63,6 +78,11 @@ class TableListFieldTest extends SapphireTest {
|
|||||||
"D" => "Col D",
|
"D" => "Col D",
|
||||||
"E" => "Col E",
|
"E" => "Col E",
|
||||||
));
|
));
|
||||||
|
// A TableListField must be inside a form for its links to be generated
|
||||||
|
$form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldSet(
|
||||||
|
$table
|
||||||
|
), new FieldSet());
|
||||||
|
|
||||||
$table->ShowPagination = true;
|
$table->ShowPagination = true;
|
||||||
$table->PageSize = 2;
|
$table->PageSize = 2;
|
||||||
$_REQUEST['ctf']['Tester']['start'] = 2;
|
$_REQUEST['ctf']['Tester']['start'] = 2;
|
||||||
@ -73,6 +93,46 @@ class TableListFieldTest extends SapphireTest {
|
|||||||
$itemMap = $items->toDropdownMap("ID", "A") ;
|
$itemMap = $items->toDropdownMap("ID", "A") ;
|
||||||
$this->assertEquals(array(3 => "a3", 4 => "a4"), $itemMap);
|
$this->assertEquals(array(3 => "a3", 4 => "a4"), $itemMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testCsvExport() {
|
||||||
|
$table = new TableListField("Tester", "TableListFieldTest_CsvExport", array(
|
||||||
|
"A" => "Col A",
|
||||||
|
"B" => "Col B"
|
||||||
|
));
|
||||||
|
|
||||||
|
$form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldSet(
|
||||||
|
$table
|
||||||
|
), new FieldSet());
|
||||||
|
|
||||||
|
$csvResponse = $table->export();
|
||||||
|
|
||||||
|
$csvOutput = $csvResponse->getBody();
|
||||||
|
|
||||||
|
$this->assertNotEquals($csvOutput, false);
|
||||||
|
|
||||||
|
// Create a temporary file and write the CSV to it.
|
||||||
|
$csvFileName = tempnam(TEMP_FOLDER, 'csv-export');
|
||||||
|
$csvFile = fopen($csvFileName, 'w');
|
||||||
|
fwrite($csvFile, $csvOutput);
|
||||||
|
fclose($csvFile);
|
||||||
|
|
||||||
|
$csvFile = fopen($csvFileName, 'r');
|
||||||
|
$csvRow = fgetcsv($csvFile);
|
||||||
|
$this->assertEquals(
|
||||||
|
$csvRow,
|
||||||
|
array('Col A', 'Col B')
|
||||||
|
);
|
||||||
|
|
||||||
|
$csvRow = fgetcsv($csvFile);
|
||||||
|
$this->assertEquals(
|
||||||
|
$csvRow,
|
||||||
|
array('"A field, with a comma"', 'A second field')
|
||||||
|
);
|
||||||
|
|
||||||
|
fclose($csvFile);
|
||||||
|
|
||||||
|
unlink($csvFileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TableListFieldTest_Obj extends DataObject implements TestOnly {
|
class TableListFieldTest_Obj extends DataObject implements TestOnly {
|
||||||
@ -89,5 +149,17 @@ class TableListFieldTest_Obj extends DataObject implements TestOnly {
|
|||||||
function getE() {
|
function getE() {
|
||||||
return $this->A . '-e';
|
return $this->A . '-e';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TableListFieldTest_CsvExport extends DataObject implements TestOnly {
|
||||||
|
static $db = array(
|
||||||
|
"A" => "Varchar",
|
||||||
|
"B" => "Varchar"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class TableListFieldTest_TestController extends Controller {
|
||||||
|
function Link() {
|
||||||
|
return "TableListFieldTest_TestController/";
|
||||||
|
}
|
||||||
}
|
}
|
@ -20,3 +20,7 @@ TableListFieldTest_Obj:
|
|||||||
B: b5
|
B: b5
|
||||||
C: c5
|
C: c5
|
||||||
|
|
||||||
|
TableListFieldTest_CsvExport:
|
||||||
|
exportone:
|
||||||
|
A: "\"A field, with a comma\""
|
||||||
|
B: A second field
|
@ -10,6 +10,33 @@ class SecurityTest extends FunctionalTest {
|
|||||||
|
|
||||||
protected $autoFollowRedirection = false;
|
protected $autoFollowRedirection = false;
|
||||||
|
|
||||||
|
protected $priorAuthenticators = array();
|
||||||
|
|
||||||
|
protected $priorDefaultAuthenticator = null;
|
||||||
|
|
||||||
|
function setUp() {
|
||||||
|
// This test assumes that MemberAuthenticator is present and the default
|
||||||
|
$this->priorAuthenticators = Authenticator::get_authenticators();
|
||||||
|
$this->priorDefaultAuthenticator = Authenticator::get_default_authenticator();
|
||||||
|
|
||||||
|
Authenticator::register('MemberAuthenticator');
|
||||||
|
Authenticator::set_default_authenticator('MemberAuthenticator');
|
||||||
|
|
||||||
|
parent::setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
function tearDown() {
|
||||||
|
// Restore selected authenticator
|
||||||
|
|
||||||
|
// MemberAuthenticator might not actually be present
|
||||||
|
if(!in_array('MemberAuthenticator', $this->priorAuthenticators)) {
|
||||||
|
Authenticator::unregister('MemberAuthenticator');
|
||||||
|
}
|
||||||
|
Authenticator::set_default_authenticator($this->priorDefaultAuthenticator);
|
||||||
|
|
||||||
|
parent::tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that the login form redirects to the change password form after logging in with an expired password
|
* Test that the login form redirects to the change password form after logging in with an expired password
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user