mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merged from branches/2.3
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@75845 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
4965352bc3
commit
9b75cb1987
@ -364,11 +364,10 @@ class SSViewer extends Object {
|
||||
|
||||
if(isset($_GET['debug_profile'])) Profiler::unmark("SSViewer::process", " for $template");
|
||||
|
||||
|
||||
// If we have our crazy base tag, then fix # links referencing the current page.
|
||||
if(strpos($output, '<base') !== false) {
|
||||
$thisURLRelativeToBase = Director::makeRelative(Director::absoluteURL($_SERVER['REQUEST_URI']));
|
||||
$output = preg_replace('/(<a[^>+]href *= *")#/i', '\\1' . $thisURLRelativeToBase . '#', $output);
|
||||
$output = preg_replace('/(<a[^>+]href *= *)"#/i', '\\1"' . $thisURLRelativeToBase . '#', $output);
|
||||
}
|
||||
|
||||
return $output;
|
||||
|
@ -64,35 +64,35 @@ class ComponentSet extends DataObjectSet {
|
||||
* Find the extra field data for a single row of the relationship
|
||||
* join table, given the known child ID.
|
||||
*
|
||||
* @todo This should return casted fields, like Enum, Varchar, Date
|
||||
* instead of just the raw value of the field.
|
||||
*
|
||||
* @param string $componentName The name of the component
|
||||
* @param int $childID The ID of the child for the relationship
|
||||
* @return array Map of fieldName => fieldValue
|
||||
* @param string|null $fieldName To get a specific extra data field, specify it here
|
||||
* @return array|string Array of field => value or single string of value
|
||||
*/
|
||||
function getExtraData($componentName, $childID) {
|
||||
function getExtraData($componentName, $childID, $fieldName = null) {
|
||||
$ownerObj = $this->ownerObj;
|
||||
$parentField = $this->ownerClass . 'ID';
|
||||
$childField = ($this->childClass == $this->ownerClass) ? 'ChildID' : ($this->childClass . 'ID');
|
||||
$result = array();
|
||||
|
||||
if(!isset($componentName)) {
|
||||
user_error('ComponentSet::getExtraData() passed a NULL component name', E_USER_ERROR);
|
||||
}
|
||||
if(!$componentName) return false;
|
||||
|
||||
if(!is_numeric($childID)) {
|
||||
user_error('ComponentSet::getExtraData() passed a non-numeric child ID', E_USER_ERROR);
|
||||
}
|
||||
|
||||
// @todo Optimize into a single query instead of one per extra field
|
||||
$extraFields = $ownerObj->many_many_extraFields($componentName);
|
||||
if($extraFields) {
|
||||
foreach($extraFields as $fieldName => $dbFieldSpec) {
|
||||
$query = DB::query("SELECT \"$fieldName\" FROM \"$this->tableName\" WHERE \"$parentField\" = {$this->ownerObj->ID} AND \"$childField\" = {$childID}");
|
||||
$value = $query->value();
|
||||
$result[$fieldName] = $value;
|
||||
}
|
||||
}
|
||||
if(!$extraFields) return false;
|
||||
|
||||
return $result;
|
||||
if($fieldName && !empty($extraFields[$fieldName])) {
|
||||
$query = DB::query("SELECT $fieldName FROM {$this->tableName} WHERE $parentField = '{$this->ownerObj->ID}' AND $childField = '{$childID}'");
|
||||
return $query->value();
|
||||
} else {
|
||||
$fields = array();
|
||||
foreach($extraFields as $fieldName => $fieldSpec) {
|
||||
$query = DB::query("SELECT $fieldName FROM {$this->tableName} WHERE $parentField = '{$this->ownerObj->ID}' AND $childField = '{$childID}'");
|
||||
$fields[$fieldName] = $query->value();
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,15 +25,21 @@ class Hierarchy extends DataObjectDecorator {
|
||||
* @param string $titleEval PHP code to evaluate to start each child - this should include '<li>'
|
||||
* @param string $extraArg Extra arguments that will be passed on to children, for if they overload this function.
|
||||
* @param boolean $limitToMarked Display only marked children.
|
||||
* @param string $childrenMethod The name of the method used to get children from each object
|
||||
* @param boolean $rootCall Set to true for this first call, and then to false for calls inside the recursion. You should not change this.
|
||||
* @return string
|
||||
*/
|
||||
public function getChildrenAsUL($attributes = "", $titleEval = '"<li>" . $child->Title', $extraArg = null, $limitToMarked = false, $rootCall = true) {
|
||||
public function getChildrenAsUL($attributes = "", $titleEval = '"<li>" . $child->Title', $extraArg = null, $limitToMarked = false, $childrenMethod = "AllChildrenIncludingDeleted", $rootCall = true) {
|
||||
if($limitToMarked && $rootCall) {
|
||||
$this->markingFinished();
|
||||
}
|
||||
|
||||
$children = $this->owner->AllChildrenIncludingDeleted($extraArg);
|
||||
if($this->owner->hasMethod($childrenMethod)) {
|
||||
$children = $this->owner->$childrenMethod($extraArg);
|
||||
} else {
|
||||
user_error(sprintf("Can't find the method '%s' on class '%s' for getting tree children",
|
||||
$childrenMethod, get_class($this->owner)), E_USER_ERROR);
|
||||
}
|
||||
|
||||
if($children) {
|
||||
if($attributes) {
|
||||
@ -46,7 +52,7 @@ class Hierarchy extends DataObjectDecorator {
|
||||
if(!$limitToMarked || $child->isMarked()) {
|
||||
$foundAChild = true;
|
||||
$output .= eval("return $titleEval;") . "\n" .
|
||||
$child->getChildrenAsUL("", $titleEval, $extraArg, $limitToMarked, false) . "</li>\n";
|
||||
$child->getChildrenAsUL("", $titleEval, $extraArg, $limitToMarked, $childrenMethod, false) . "</li>\n";
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,13 +75,13 @@ class Hierarchy extends DataObjectDecorator {
|
||||
* @param int $minCount The minimum amount of nodes to mark.
|
||||
* @return int The actual number of nodes marked.
|
||||
*/
|
||||
public function markPartialTree($minCount = 30, $context = null) {
|
||||
public function markPartialTree($minCount = 30, $context = null, $childrenMethod = "AllChildrenIncludingDeleted") {
|
||||
$this->markedNodes = array($this->owner->ID => $this->owner);
|
||||
$this->owner->markUnexpanded();
|
||||
|
||||
// foreach can't handle an ever-growing $nodes list
|
||||
while(list($id, $node) = each($this->markedNodes)) {
|
||||
$this->markChildren($node, $context);
|
||||
$this->markChildren($node, $context, $childrenMethod);
|
||||
|
||||
if($minCount && sizeof($this->markedNodes) >= $minCount) {
|
||||
break;
|
||||
@ -139,8 +145,14 @@ class Hierarchy extends DataObjectDecorator {
|
||||
* Mark all children of the given node that match the marking filter.
|
||||
* @param DataObject $node Parent node.
|
||||
*/
|
||||
public function markChildren($node, $context = null) {
|
||||
$children = $node->AllChildrenIncludingDeleted($context);
|
||||
public function markChildren($node, $context = null, $childrenMethod = "AllChildrenIncludingDeleted") {
|
||||
if($node->hasMethod($childrenMethod)) {
|
||||
$children = $node->$childrenMethod($context);
|
||||
} else {
|
||||
user_error(sprintf("Can't find the method '%s' on class '%s' for getting tree children",
|
||||
$childrenMethod, get_class($this->owner)), E_USER_ERROR);
|
||||
}
|
||||
|
||||
$node->markExpanded();
|
||||
if($children) {
|
||||
foreach($children as $child) {
|
||||
@ -171,10 +183,10 @@ class Hierarchy extends DataObjectDecorator {
|
||||
*/
|
||||
public function markingClasses() {
|
||||
$classes = '';
|
||||
if(!$this->expanded) {
|
||||
if(!$this->isExpanded()) {
|
||||
$classes .= " unexpanded";
|
||||
}
|
||||
if(!$this->treeOpened) {
|
||||
if(!$this->isTreeOpened()) {
|
||||
$classes .= " closed";
|
||||
}
|
||||
return $classes;
|
||||
@ -229,42 +241,42 @@ class Hierarchy extends DataObjectDecorator {
|
||||
* True if this DataObject is marked.
|
||||
* @var boolean
|
||||
*/
|
||||
protected $marked = false;
|
||||
protected static $marked = array();
|
||||
|
||||
/**
|
||||
* True if this DataObject is expanded.
|
||||
* @var boolean
|
||||
*/
|
||||
protected $expanded = false;
|
||||
protected static $expanded = array();
|
||||
|
||||
/**
|
||||
* True if this DataObject is opened.
|
||||
* @var boolean
|
||||
*/
|
||||
protected $treeOpened = false;
|
||||
protected static $treeOpened = array();
|
||||
|
||||
/**
|
||||
* Mark this DataObject as expanded.
|
||||
*/
|
||||
public function markExpanded() {
|
||||
$this->marked = true;
|
||||
$this->expanded = true;
|
||||
self::$marked[ClassInfo::baseDataClass($this->owner->class)][$this->owner->ID] = true;
|
||||
self::$expanded[ClassInfo::baseDataClass($this->owner->class)][$this->owner->ID] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this DataObject as unexpanded.
|
||||
*/
|
||||
public function markUnexpanded() {
|
||||
$this->marked = true;
|
||||
$this->expanded = false;
|
||||
self::$marked[ClassInfo::baseDataClass($this->owner->class)][$this->owner->ID] = true;
|
||||
self::$expanded[ClassInfo::baseDataClass($this->owner->class)][$this->owner->ID] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this DataObject's tree as opened.
|
||||
*/
|
||||
public function markOpened() {
|
||||
$this->marked = true;
|
||||
$this->treeOpened = true;
|
||||
self::$marked[ClassInfo::baseDataClass($this->owner->class)][$this->owner->ID] = true;
|
||||
self::$treeOpened[ClassInfo::baseDataClass($this->owner->class)][$this->owner->ID] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -272,7 +284,9 @@ class Hierarchy extends DataObjectDecorator {
|
||||
* @return boolean
|
||||
*/
|
||||
public function isMarked() {
|
||||
return $this->marked;
|
||||
$baseClass = ClassInfo::baseDataClass($this->owner->class);
|
||||
$id = $this->owner->ID;
|
||||
return isset(self::$marked[$baseClass][$id]) ? self::$marked[$baseClass][$id] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -280,14 +294,18 @@ class Hierarchy extends DataObjectDecorator {
|
||||
* @return boolean
|
||||
*/
|
||||
public function isExpanded() {
|
||||
return $this->expanded;
|
||||
$baseClass = ClassInfo::baseDataClass($this->owner->class);
|
||||
$id = $this->owner->ID;
|
||||
return isset(self::$expanded[$baseClass][$id]) ? self::$expanded[$baseClass][$id] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this DataObject's tree is opened.
|
||||
*/
|
||||
public function isTreeOpened() {
|
||||
return $this->treeOpened;
|
||||
$baseClass = ClassInfo::baseDataClass($this->owner->class);
|
||||
$id = $this->owner->ID;
|
||||
return isset(self::$treeOpened[$baseClass][$id]) ? self::$treeOpened[$baseClass][$id] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -478,6 +496,15 @@ class Hierarchy extends DataObjectDecorator {
|
||||
return $this->_cache_allChildrenIncludingDeleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the children that this page had, including pages that were deleted
|
||||
* from both stage & live.
|
||||
*/
|
||||
public function AllHistoricalChildren() {
|
||||
return Versioned::get_including_deleted(ClassInfo::baseDataClass($this->owner->class),
|
||||
"ParentID = " . (int)$this->owner->ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of children
|
||||
* @return int
|
||||
|
@ -1222,6 +1222,13 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
$this->fieldLabel('ClassName'),
|
||||
$this->getClassDropdown()
|
||||
),
|
||||
|
||||
new OptionsetField("ParentType", "Page location", array(
|
||||
"root" => _t("SiteTree.PARENTTYPE_ROOT", "Top-level page"),
|
||||
"subpage" => _t("SiteTree.PARENTTYPE_SUBPAGE", "Sub-page underneath a parent page (choose below)"),
|
||||
)),
|
||||
new TreeDropdownField("ParentID", $this->fieldLabel('ParentID'), 'SiteTree'),
|
||||
|
||||
new CheckboxField("ShowInMenus", $this->fieldLabel('ShowInMenus')),
|
||||
new CheckboxField("ShowInSearch", $this->fieldLabel('ShowInSearch')),
|
||||
/*, new TreeMultiselectField("MultipleParents", "Page appears within", "SiteTree")*/
|
||||
@ -1318,6 +1325,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
$labels['MetaKeywords'] = _t('SiteTree.METAKEYWORDS', "Keywords");
|
||||
$labels['ExtraMeta'] = _t('SiteTree.METAEXTRA', "Custom Meta Tags");
|
||||
$labels['ClassName'] = _t('SiteTree.PAGETYPE', "Page type", PR_MEDIUM, 'Classname of a page object');
|
||||
$labels['ParentType'] = _t('SiteTree.PARENTTYPE', "Page location", PR_MEDIUM);
|
||||
$labels['ParentID'] = _t('SiteTree.PARENTID', "Parent page", PR_MEDIUM);
|
||||
$labels['ShowInMenus'] =_t('SiteTree.SHOWINMENUS', "Show in menus?");
|
||||
$labels['ShowInSearch'] = _t('SiteTree.SHOWINSEARCH', "Show in search?");
|
||||
$labels['ProvideComments'] = _t('SiteTree.ALLOWCOMMENTS', "Allow comments on this page?");
|
||||
@ -1370,11 +1379,15 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
|
||||
if($this->IsDeletedFromStage) {
|
||||
if($this->can('CMSEdit')) {
|
||||
// "restore"
|
||||
$actions->push(new FormAction('revert',_t('CMSMain.RESTORE','Restore')));
|
||||
|
||||
// "delete from live"
|
||||
$actions->push(new FormAction('deletefromlive',_t('CMSMain.DELETEFP','Delete from the published site')));
|
||||
if($this->ExistsOnLive) {
|
||||
// "restore"
|
||||
$actions->push(new FormAction('revert',_t('CMSMain.RESTORE','Restore')));
|
||||
// "delete from live"
|
||||
$actions->push(new FormAction('deletefromlive',_t('CMSMain.DELETEFP','Delete from the published site')));
|
||||
} else {
|
||||
// "restore"
|
||||
$actions->push(new FormAction('restore',_t('CMSMain.RESTORE','Restore')));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if($this->canEdit()) {
|
||||
@ -1481,6 +1494,29 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
$this->extend('onAfterRevertToLive');
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the content in the active copy of this SiteTree page to the stage site.
|
||||
* @return The SiteTree object.
|
||||
*/
|
||||
function doRestoreToStage() {
|
||||
// if no record can be found on draft stage (meaning it has been "deleted from draft" before),
|
||||
// create an empty record
|
||||
if(!DB::query("SELECT ID FROM SiteTree WHERE ID = $this->ID")->value()) {
|
||||
DB::query("INSERT INTO SiteTree SET ID = $this->ID");
|
||||
}
|
||||
|
||||
$oldStage = Versioned::current_stage();
|
||||
Versioned::reading_stage('Stage');
|
||||
$this->forceChange();
|
||||
$this->writeWithoutVersion();
|
||||
|
||||
$result = DataObject::get_by_id($this->class, $this->ID);
|
||||
|
||||
Versioned::reading_stage($oldStage);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this page is new - that is, if it has yet to have been written
|
||||
* to the database.
|
||||
@ -1589,11 +1625,13 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
|
||||
// sort alphabetically, and put current on top
|
||||
asort($result);
|
||||
$currentPageTypeName = $result[$currentClass];
|
||||
unset($result[$currentClass]);
|
||||
$result = array_reverse($result);
|
||||
$result[$currentClass] = $currentPageTypeName;
|
||||
$result = array_reverse($result);
|
||||
if($currentClass) {
|
||||
$currentPageTypeName = $result[$currentClass];
|
||||
unset($result[$currentClass]);
|
||||
$result = array_reverse($result);
|
||||
$result[$currentClass] = $currentPageTypeName;
|
||||
$result = array_reverse($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
@ -1695,7 +1733,11 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
*/
|
||||
function TreeTitle() {
|
||||
if($this->IsDeletedFromStage) {
|
||||
$tag ="del title=\"" . _t('SiteTree.REMOVEDFROMDRAFT', 'Removed from draft site') . "\"";
|
||||
if($this->ExistsOnLive) {
|
||||
$tag ="del title=\"" . _t('SiteTree.REMOVEDFROMDRAFT', 'Removed from draft site') . "\"";
|
||||
} else {
|
||||
$tag ="del class=\"deletedOnLive\" title=\"" . _t('SiteTree.DELETEDPAGE', 'Deleted page') . "\"";
|
||||
}
|
||||
} elseif($this->IsAddedToStage) {
|
||||
$tag = "ins title=\"" . _t('SiteTree.ADDEDTODRAFT', 'Added to draft site') . "\"";
|
||||
} elseif($this->IsModifiedOnStage) {
|
||||
@ -1775,9 +1817,16 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
if($this->isNew()) return false;
|
||||
|
||||
$stageVersion = Versioned::get_versionnumber_by_stage('SiteTree', 'Stage', $this->ID);
|
||||
$liveVersion = Versioned::get_versionnumber_by_stage('SiteTree', 'Live', $this->ID);
|
||||
|
||||
return (!$stageVersion && $liveVersion);
|
||||
// Return true for both completely deleted pages and for pages just deleted from stage.
|
||||
return !$stageVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this page exists on the live site
|
||||
*/
|
||||
function getExistsOnLive() {
|
||||
return (bool)Versioned::get_versionnumber_by_stage('SiteTree', 'Live', $this->ID);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1860,5 +1909,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
return $entities;
|
||||
}
|
||||
|
||||
function getParentType() {
|
||||
return $this->ParentID == 0 ? 'root' : 'subpage';
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
@ -106,10 +106,10 @@ class Versioned extends DataObjectDecorator {
|
||||
* Create a temporary table mapping each database record to its version on the given date.
|
||||
* This is used by the versioning system to return database content on that date.
|
||||
* @param string $baseTable The base table.
|
||||
* @param string $date The date.
|
||||
* @param string $date The date. If omitted, then the latest version of each page will be returned.
|
||||
* @todo Ensure that this is DB abstracted
|
||||
*/
|
||||
protected function requireArchiveTempTable($baseTable, $date) {
|
||||
protected static function requireArchiveTempTable($baseTable, $date = null) {
|
||||
if(!isset(self::$createdArchiveTempTable[$baseTable])) {
|
||||
self::$createdArchiveTempTable[$baseTable] = true;
|
||||
|
||||
@ -123,6 +123,7 @@ class Versioned extends DataObjectDecorator {
|
||||
GROUP BY \"RecordID\"");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An array of DataObject extensions that may require versioning for extra tables
|
||||
* The array value is a set of suffixes to form these table names, assuming a preceding '_'.
|
||||
@ -724,6 +725,33 @@ class Versioned extends DataObjectDecorator {
|
||||
return new $className($record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the equivalent of a DataObject::get() call, querying the latest
|
||||
* version of each page stored in the (class)_versions tables.
|
||||
*
|
||||
* In particular, this will query deleted records as well as active ones.
|
||||
*/
|
||||
static function get_including_deleted($class, $filter = "", $sort = "") {
|
||||
$oldStage = Versioned::$reading_stage;
|
||||
Versioned::$reading_stage = null;
|
||||
|
||||
$SNG = singleton($class);
|
||||
|
||||
// Build query
|
||||
$query = $SNG->buildVersionSQL($filter, $sort);
|
||||
$baseTable = ClassInfo::baseDataClass($class);
|
||||
self::requireArchiveTempTable($baseTable);
|
||||
$query->from["_Archive$baseTable"] = "INNER JOIN `_Archive$baseTable`
|
||||
ON `_Archive$baseTable`.RecordID = `{$baseTable}_versions`.RecordID
|
||||
AND `_Archive$baseTable`.Version = `{$baseTable}_versions`.Version";
|
||||
|
||||
// Process into a DataObjectSet
|
||||
$result = $SNG->buildDataObjectSet($query->execute());
|
||||
|
||||
Versioned::$reading_stage = $oldStage;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataObject
|
||||
*/
|
||||
|
@ -458,69 +458,85 @@ JS;
|
||||
* this method.
|
||||
*/
|
||||
function getCustomFieldsFor($childData) {
|
||||
if($this->detailFormFields instanceof Fieldset) {
|
||||
if($this->detailFormFields instanceof FieldSet) {
|
||||
return $this->detailFormFields;
|
||||
} else {
|
||||
$fieldsMethod = $this->detailFormFields;
|
||||
|
||||
if(!is_string($fieldsMethod)) {
|
||||
$this->detailFormFields = 'getCMSFields';
|
||||
$fieldsMethod = 'getCMSFields';
|
||||
}
|
||||
|
||||
if(!$childData->hasMethod($fieldsMethod)) {
|
||||
$fieldsMethod = 'getCMSFields';
|
||||
}
|
||||
|
||||
$fields = $childData->$fieldsMethod();
|
||||
}
|
||||
|
||||
if(!$this->relationAutoSetting) {
|
||||
return $fields;
|
||||
$fieldsMethod = $this->detailFormFields;
|
||||
|
||||
if(!is_string($fieldsMethod)) {
|
||||
$this->detailFormFields = 'getCMSFields';
|
||||
$fieldsMethod = 'getCMSFields';
|
||||
}
|
||||
|
||||
$parentClass = DataObject::get_by_id($this->getParentClass(), $this->sourceID());
|
||||
$manyManyExtraFields = $parentClass->many_many_extraFields($this->name);
|
||||
if($manyManyExtraFields) {
|
||||
foreach($manyManyExtraFields as $fieldName => $fieldSpec) {
|
||||
$dbField = new Varchar('ctf[extraFields][' . $fieldName . ']');
|
||||
$fields->addFieldToTab('Root.Main', $dbField->scaffoldFormField($fieldName));
|
||||
}
|
||||
if(!$childData->hasMethod($fieldsMethod)) {
|
||||
$fieldsMethod = 'getCMSFields';
|
||||
}
|
||||
|
||||
return $fields;
|
||||
return $childData->$fieldsMethod();
|
||||
}
|
||||
|
||||
function getFieldsFor($childData) {
|
||||
// See if our parent class has any many_many relations by this source class
|
||||
$parentClass = DataObject::get_by_id($this->getParentClass(), $this->sourceID());
|
||||
if($this->sourceID()) {
|
||||
$parentClass = DataObject::get_by_id($this->getParentClass(), $this->sourceID());
|
||||
} else {
|
||||
$parentClass = singleton($this->getParentClass());
|
||||
}
|
||||
|
||||
$manyManyRelations = $parentClass->many_many();
|
||||
$manyManyRelationName = null;
|
||||
$manyManyComponentSet = null;
|
||||
|
||||
$hasManyRelations = $parentClass->has_many();
|
||||
$hasManyRelationName = null;
|
||||
$hasManyComponentSet = null;
|
||||
|
||||
if($manyManyRelations) foreach($manyManyRelations as $relation => $class) {
|
||||
if($class == $this->sourceClass()) {
|
||||
$manyManyRelationName = $relation;
|
||||
}
|
||||
}
|
||||
|
||||
if($hasManyRelations) foreach($hasManyRelations as $relation => $class) {
|
||||
if($class == $this->sourceClass()) {
|
||||
$hasManyRelationName = $relation;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the relation value to related records
|
||||
if(!$childData->ID && $this->getParentClass()) {
|
||||
// make sure the relation-link is existing, even if we just add the sourceClass and didn't save it
|
||||
$parentIDName = $this->getParentIdName( $this->getParentClass(), $this->sourceClass() );
|
||||
$parentIDName = $this->getParentIdName($this->getParentClass(), $this->sourceClass());
|
||||
$childData->$parentIDName = $this->sourceID();
|
||||
}
|
||||
|
||||
$detailFields = $this->getCustomFieldsFor($childData);
|
||||
|
||||
// Loading of extra field values for editing an existing record
|
||||
if($manyManyRelationName && $childData->ID) {
|
||||
if($manyManyRelationName) {
|
||||
$manyManyComponentSet = $parentClass->getManyManyComponents($manyManyRelationName);
|
||||
$extraData = $manyManyComponentSet->getExtraData($manyManyRelationName, $childData->ID);
|
||||
if($extraData) foreach($extraData as $fieldName => $fieldValue) {
|
||||
$field = $detailFields->dataFieldByName('ctf[extraFields][' . $fieldName . ']');
|
||||
$field->setValue($fieldValue);
|
||||
$extraFieldsSpec = $parentClass->many_many_extraFields($this->name);
|
||||
|
||||
$extraData = null;
|
||||
if($childData && $childData->ID) {
|
||||
$extraData = $manyManyComponentSet->getExtraData($manyManyRelationName, $childData->ID);
|
||||
}
|
||||
|
||||
if($extraFieldsSpec) foreach($extraFieldsSpec as $fieldName => $fieldSpec) {
|
||||
// @todo Create the proper DBField type instead of hardcoding Varchar
|
||||
$fieldObj = new Varchar($fieldName);
|
||||
|
||||
if(isset($extraData[$fieldName])) {
|
||||
$fieldObj->setValue($extraData[$fieldName]);
|
||||
}
|
||||
|
||||
$detailFields->addFieldToTab('Root.Main', $fieldObj->scaffoldFormField($fieldName));
|
||||
}
|
||||
}
|
||||
|
||||
if($hasManyRelationName && $childData->ID) {
|
||||
$hasManyComponentSet = $parentClass->getComponents($hasManyRelationName);
|
||||
}
|
||||
|
||||
// the ID field confuses the Controller-logic in finding the right view for ReferencedField
|
||||
@ -535,17 +551,23 @@ JS;
|
||||
$detailFields->push(new HiddenField('ctf[ClassName]', '', $this->sourceClass()));
|
||||
|
||||
if($this->getParentClass()) {
|
||||
$detailFields->push(new HiddenField('ctf[parentClass]', '', $this->getParentClass()));
|
||||
|
||||
if($manyManyRelationName && $this->relationAutoSetting) {
|
||||
$detailFields->push(new HiddenField('ctf[manyManyRelation]', '', $manyManyRelationName));
|
||||
$detailFields->push(new HiddenField('ctf[parentClass]', '', $this->getParentClass()));
|
||||
}
|
||||
|
||||
if($hasManyRelationName && $this->relationAutoSetting) {
|
||||
$detailFields->push(new HiddenField('ctf[hasManyRelation]', '', $hasManyRelationName));
|
||||
}
|
||||
|
||||
if($manyManyRelationName || $hasManyRelationName) {
|
||||
$detailFields->push(new HiddenField('ctf[sourceID]', '', $this->sourceID()));
|
||||
}
|
||||
|
||||
$parentIdName = $this->getParentIdName($this->getParentClass(), $this->sourceClass());
|
||||
if($parentIdName) {
|
||||
// add relational fields
|
||||
$detailFields->push(new HiddenField('ctf[parentClass]', '', $this->getParentClass()));
|
||||
|
||||
if($parentIdName) {
|
||||
if($this->relationAutoSetting) {
|
||||
// Hack for model admin: model admin will have included a dropdown for the relation itself
|
||||
$detailFields->removeByName($parentIdName);
|
||||
@ -614,6 +636,8 @@ JS;
|
||||
* even if there is no action relevant for the main controller (to provide the instance of ComplexTableField
|
||||
* which in turn saves the record.
|
||||
*
|
||||
* This is for adding new item records. {@link ComplexTableField_ItemRequest::saveComplexTableField()}
|
||||
*
|
||||
* @see Form::ReferencedField
|
||||
*/
|
||||
function saveComplexTableField($data, $form, $params) {
|
||||
@ -638,6 +662,14 @@ JS;
|
||||
$componentSet->add($childData, $extraFields);
|
||||
}
|
||||
|
||||
if(isset($data['ctf']['hasManyRelation'])) {
|
||||
$parentRecord = DataObject::get_by_id($data['ctf']['parentClass'], (int) $data['ctf']['sourceID']);
|
||||
$relationName = $data['ctf']['hasManyRelation'];
|
||||
|
||||
$componentSet = $parentRecord->getComponents($relationName);
|
||||
$componentSet->add($childData);
|
||||
}
|
||||
|
||||
$referrer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null;
|
||||
|
||||
$closeLink = sprintf(
|
||||
@ -784,6 +816,8 @@ class ComplexTableField_ItemRequest extends RequestHandler {
|
||||
* even if there is no action relevant for the main controller (to provide the instance of ComplexTableField
|
||||
* which in turn saves the record.
|
||||
*
|
||||
* This is for editing existing item records. {@link ComplexTableField::saveComplexTableField()}
|
||||
*
|
||||
* @see Form::ReferencedField
|
||||
*/
|
||||
function saveComplexTableField($data, $form, $request) {
|
||||
@ -819,6 +853,7 @@ class ComplexTableField_ItemRequest extends RequestHandler {
|
||||
'<a href="' . $this->Link() . '">"' . $dataObject->Title . '"</a>',
|
||||
$closeLink
|
||||
);
|
||||
|
||||
$form->sessionMessage($message, 'good');
|
||||
|
||||
Director::redirectBack();
|
||||
|
@ -95,6 +95,14 @@ class Form extends RequestHandler {
|
||||
|
||||
protected $messageType;
|
||||
|
||||
/**
|
||||
* Should we redirect the user back down to the
|
||||
* the form on validation errors rather then just the page
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $redirectToFormOnValidationError = false;
|
||||
|
||||
protected $security = true;
|
||||
|
||||
/**
|
||||
@ -214,11 +222,16 @@ class Form extends RequestHandler {
|
||||
return $response;
|
||||
}
|
||||
} else {
|
||||
Director::redirectBack();
|
||||
return;
|
||||
if($this->getRedirectToFormOnValidationError()) {
|
||||
if($pageURL = $request->getHeader('Referer')) {
|
||||
return Director::redirect($pageURL . '#' . $this->FormName());
|
||||
}
|
||||
}
|
||||
return Director::redirectBack();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Protection against CSRF attacks
|
||||
if($this->securityTokenEnabled()) {
|
||||
$securityID = Session::get('SecurityID');
|
||||
@ -279,6 +292,27 @@ class Form extends RequestHandler {
|
||||
$this->transform(new ReadonlyTransformation());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the user should be redirected back down to the
|
||||
* form on the page upon validation errors in the form or if
|
||||
* they just need to redirect back to the page
|
||||
*
|
||||
* @param bool Redirect to the form
|
||||
*/
|
||||
public function setRedirectToFormOnValidationError($bool) {
|
||||
$this->redirectToFormOnValidationError = $bool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the user should be redirected back down to the
|
||||
* form on the page upon validation errors
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getRedirectToFormOnValidationError() {
|
||||
return $this->redirectToFormOnValidationError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an error message to a field on this form. It will be saved into the session
|
||||
* and used the next time this form is displayed.
|
||||
|
@ -579,23 +579,6 @@ class DataObjectTest extends SapphireTest {
|
||||
// @todo test has_many and many_many relations
|
||||
}
|
||||
|
||||
function testManyManyExtraFields() {
|
||||
$player = $this->fixture->objFromFixture('DataObjectTest_Player', 'player1');
|
||||
$team = $this->fixture->objFromFixture('DataObjectTest_Team', 'team1');
|
||||
|
||||
// Extra fields are immediately available on the Team class (defined in $many_many_extraFields)
|
||||
$teamExtraFields = $team->many_many_extraFields('Players');
|
||||
$this->assertEquals($teamExtraFields, array(
|
||||
'Position' => 'Varchar(100)'
|
||||
));
|
||||
|
||||
// We'll have to go through the relation to get the extra fields on Player
|
||||
$playerExtraFields = $player->many_many_extraFields('Teams');
|
||||
$this->assertEquals($playerExtraFields, array(
|
||||
'Position' => 'Varchar(100)'
|
||||
));
|
||||
}
|
||||
|
||||
function testPopulateDefaults() {
|
||||
$obj = new DataObjectTest_WithDefaults();
|
||||
$this->assertEquals(
|
||||
@ -624,6 +607,23 @@ class DataObjectTest extends SapphireTest {
|
||||
$this->assertEquals($changedPage->ClassName, 'RedirectorPage');
|
||||
}
|
||||
|
||||
function testManyManyExtraFields() {
|
||||
$player = $this->fixture->objFromFixture('DataObjectTest_Player', 'player1');
|
||||
$team = $this->fixture->objFromFixture('DataObjectTest_Team', 'team1');
|
||||
|
||||
// Extra fields are immediately available on the Team class (defined in $many_many_extraFields)
|
||||
$teamExtraFields = $team->many_many_extraFields('Players');
|
||||
$this->assertEquals($teamExtraFields, array(
|
||||
'Position' => 'Varchar(100)'
|
||||
));
|
||||
|
||||
// We'll have to go through the relation to get the extra fields on Player
|
||||
$playerExtraFields = $player->many_many_extraFields('Teams');
|
||||
$this->assertEquals($playerExtraFields, array(
|
||||
'Position' => 'Varchar(100)'
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DataObjectTest_Player extends Member implements TestOnly {
|
||||
|
@ -10,12 +10,17 @@ class ErrorPageTest extends FunctionalTest {
|
||||
function test404ErrorPage() {
|
||||
$page = $this->objFromFixture('ErrorPage', '404');
|
||||
|
||||
/* The page is an instance of ErrorPage */
|
||||
$this->assertTrue($page instanceof ErrorPage, 'The page is an instance of ErrorPage');
|
||||
$response = $this->get($page->URLSegment);
|
||||
|
||||
/* A standard error is shown */
|
||||
$this->assertEquals($response->getBody(), 'The requested page couldn\'t be found.', 'A standard error is shown');
|
||||
|
||||
/* When the page is published, an error page with the theme is shown instead */
|
||||
$page->publish('Stage', 'Live', false);
|
||||
|
||||
$response = $this->get($page->URLSegment);
|
||||
|
||||
/* We have body text from the error page */
|
||||
/* There is body text from the error page */
|
||||
$this->assertNotNull($response->getBody(), 'We have body text from the error page');
|
||||
|
||||
/* Status code of the HTTPResponse for error page is "404" */
|
||||
|
@ -66,9 +66,13 @@ if(class_exists('SiteTreeCMSWorkflow')) {
|
||||
function testActionsDeletedFromStageRecord() {
|
||||
$page = new Page();
|
||||
$page->write();
|
||||
$pageID = $page->ID;
|
||||
$page->publish('Stage', 'Live');
|
||||
$page->deleteFromStage('Stage');
|
||||
|
||||
// Get the live version of the page
|
||||
$page = Versioned::get_one_by_stage("SiteTree", "Live", "`SiteTree`.ID = $pageID");
|
||||
|
||||
$author = $this->objFromFixture('Member', 'cmseditor');
|
||||
$this->session()->inst_set('loggedInAs', $author->ID);
|
||||
|
||||
|
@ -167,6 +167,45 @@ class SiteTreeTest extends SapphireTest {
|
||||
$this->assertFalse($modifiedOnDraftPage->IsAddedToStage);
|
||||
$this->assertTrue($modifiedOnDraftPage->IsModifiedOnStage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a page can be completely deleted and restored to the stage site
|
||||
*/
|
||||
function testRestoreToStage() {
|
||||
$page = $this->objFromFixture('Page', 'about');
|
||||
$pageID = $page->ID;
|
||||
$page->delete();
|
||||
$this->assertTrue(!DataObject::get_by_id("Page", $pageID));
|
||||
|
||||
$deletedPage = Versioned::get_latest_version('SiteTree', $pageID);
|
||||
$resultPage = $deletedPage->doRestoreToStage();
|
||||
|
||||
$requeriedPage = DataObject::get_by_id("Page", $pageID);
|
||||
|
||||
$this->assertEquals($pageID, $resultPage->ID);
|
||||
$this->assertEquals($pageID, $requeriedPage->ID);
|
||||
$this->assertEquals('About Us', $requeriedPage->Title);
|
||||
$this->assertEquals('Page', $requeriedPage->class);
|
||||
|
||||
|
||||
$page2 = $this->objFromFixture('Page', 'staff');
|
||||
$page2ID = $page2->ID;
|
||||
$page2->doUnpublish();
|
||||
$page2->delete();
|
||||
|
||||
// Check that if we restore while on the live site that the content still gets pushed to
|
||||
// stage
|
||||
Versioned::reading_stage('Live');
|
||||
$deletedPage = Versioned::get_latest_version('SiteTree', $page2ID);
|
||||
$deletedPage->doRestoreToStage();
|
||||
$this->assertTrue(!Versioned::get_one_by_stage("Page", "Live", "`SiteTree`.ID = " . $page2ID));
|
||||
|
||||
Versioned::reading_stage('Stage');
|
||||
$requeriedPage = DataObject::get_by_id("Page", $page2ID);
|
||||
$this->assertEquals('Staff', $requeriedPage->Title);
|
||||
$this->assertEquals('Page', $requeriedPage->class);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// We make these extend page since that's what all page types are expected to do
|
||||
|
246
tests/forms/ComplexTableFieldTest.php
Normal file
246
tests/forms/ComplexTableFieldTest.php
Normal file
@ -0,0 +1,246 @@
|
||||
<?php
|
||||
/**
|
||||
* @package sapphire
|
||||
* @subpackage tests
|
||||
*/
|
||||
class ComplexTableFieldTest extends FunctionalTest {
|
||||
|
||||
static $fixture_file = 'sapphire/tests/forms/ComplexTableFieldTest.yml';
|
||||
|
||||
/**
|
||||
* An instance of {@link Controller} used for
|
||||
* running tests against.
|
||||
*
|
||||
* @var Controller object
|
||||
*/
|
||||
protected $controller;
|
||||
|
||||
/**
|
||||
* An instance of {@link Form} that is taken
|
||||
* from the test controller, used for testing.
|
||||
*
|
||||
* @var Form object
|
||||
*/
|
||||
protected $form;
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->controller = new ComplexTableFieldTest_Controller();
|
||||
$this->manyManyForm = $this->controller->ManyManyForm();
|
||||
}
|
||||
|
||||
function testCorrectNumberOfRowsInTable() {
|
||||
$field = $this->manyManyForm->dataFieldByName('Players');
|
||||
$parser = new CSSContentParser($field->FieldHolder());
|
||||
|
||||
/* There are 2 players (rows) in the table */
|
||||
$this->assertEquals(count($parser->getBySelector('tbody tr')), 2, 'There are 2 players (rows) in the table');
|
||||
|
||||
/* There are 2 CTF items in the DataObjectSet */
|
||||
$this->assertEquals($field->Items()->Count(), 2, 'There are 2 CTF items in the DataObjectSet');
|
||||
}
|
||||
|
||||
function testDetailFormDisplaysWithCorrectFields() {
|
||||
$field = $this->manyManyForm->dataFieldByName('Players');
|
||||
$detailForm = $field->add();
|
||||
$parser = new CSSContentParser($detailForm);
|
||||
|
||||
/* There is a field called "Name", which is a text input */
|
||||
$this->assertEquals(count($parser->getBySelector('#Name input')), 1, 'There is a field called "Name", which is a text input');
|
||||
|
||||
/* There is a field called "Role" - this field is the extra field for $many_many_extraFields */
|
||||
$this->assertEquals(count($parser->getBySelector('#Role input')), 1, 'There is a field called "Role" - this field is the extra field for $many_many_extraFields');
|
||||
}
|
||||
|
||||
function testAddingManyManyNewPlayerWithExtraData() {
|
||||
$team = DataObject::get_one('ComplexTableFieldTest_Team', "Name = 'The Awesome People'");
|
||||
|
||||
$this->post('ComplexTableFieldTest_Controller/ManyManyForm/field/Players/AddForm', array(
|
||||
'Name' => 'Bobby Joe',
|
||||
'ctf' => array(
|
||||
'extraFields' => array(
|
||||
'Role' => 'Goalie',
|
||||
'Position' => 'Player',
|
||||
'DateJoined' => '2008-10-10'
|
||||
),
|
||||
'ClassName' => 'ComplexTableFieldTest_Player',
|
||||
'manyManyRelation' => 'Players',
|
||||
'parentClass' => 'ComplexTableFieldTest_Team',
|
||||
'sourceID' => $team->ID
|
||||
)
|
||||
));
|
||||
|
||||
/* Retrieve the new player record we created */
|
||||
$newPlayer = DataObject::get_one('ComplexTableFieldTest_Player', "Name = 'Bobby Joe'");
|
||||
|
||||
/* A new ComplexTableFieldTest_Player record was created, Name = "Bobby Joe" */
|
||||
$this->assertNotNull($newPlayer, 'A new ComplexTableFieldTest_Player record was created, Name = "Bobby Joe"');
|
||||
|
||||
/* Get the many-many related Teams to the new player that were automatically linked by CTF */
|
||||
$teams = $newPlayer->getManyManyComponents('Teams');
|
||||
|
||||
/* Automatic many-many relation was set correctly on the new player */
|
||||
$this->assertEquals($teams->Count(), 1, 'Automatic many-many relation was set correctly on the new player');
|
||||
|
||||
/* The extra fields have the correct value */
|
||||
$extraFields = $teams->getExtraData('Teams', $team->ID);
|
||||
|
||||
/* There are 3 extra fields */
|
||||
$this->assertEquals(count($extraFields), 3, 'There are 3 extra fields');
|
||||
|
||||
/* The three extra fields have the correct values */
|
||||
$this->assertEquals($extraFields['Role'], 'Goalie', 'The extra field "Role" has the correct value');
|
||||
$this->assertEquals($extraFields['Position'], 'Player', 'The extra field "Position" has the correct value');
|
||||
$this->assertEquals($extraFields['DateJoined'], '2008-10-10', 'The extra field "DateJoined" has the correct value');
|
||||
}
|
||||
|
||||
function testAddingHasManyData() {
|
||||
$team = DataObject::get_one('ComplexTableFieldTest_Team', "Name = 'The Awesome People'");
|
||||
|
||||
$this->post('ComplexTableFieldTest_Controller/HasManyForm/field/Sponsors/AddForm', array(
|
||||
'Name' => 'Jim Beam',
|
||||
'ctf' => array(
|
||||
'ClassName' => 'ComplexTableFieldTest_Sponsor',
|
||||
'hasManyRelation' => 'Sponsors',
|
||||
'parentClass' => 'ComplexTableFieldTest_Team',
|
||||
'sourceID' => $team->ID
|
||||
)
|
||||
));
|
||||
|
||||
/* Retrieve the new sponsor record we created */
|
||||
$newSponsor = DataObject::get_one('ComplexTableFieldTest_Sponsor', "Name = 'Jim Beam'");
|
||||
|
||||
/* A new ComplexTableFieldTest_Sponsor record was created, Name = "Jim Beam" */
|
||||
$this->assertNotNull($newSponsor, 'A new ComplexTableFieldTest_Sponsor record was created, Name = "Jim Beam"');
|
||||
|
||||
/* Get the has-one related Team to the new sponsor that were automatically linked by CTF */
|
||||
$teamID = $newSponsor->TeamID;
|
||||
|
||||
/* Automatic many-many relation was set correctly on the new player */
|
||||
$this->assertTrue($teamID > 0, 'Automatic has-many/has-one relation was set correctly on the sponsor');
|
||||
|
||||
/* The other side of the relation works as well */
|
||||
$team = DataObject::get_by_id('ComplexTableFieldTest_Team', $teamID);
|
||||
|
||||
/* Let's get the Sponsors component */
|
||||
$sponsor = $team->getComponents('Sponsors')->First();
|
||||
|
||||
/* The sponsor is the same as the one we added */
|
||||
$this->assertEquals($newSponsor->ID, $sponsor->ID, 'The sponsor is the same as the one we added');
|
||||
}
|
||||
|
||||
}
|
||||
class ComplexTableFieldTest_Controller extends Controller {
|
||||
|
||||
function Link($action = null) {
|
||||
return "ComplexTableFieldTest_Controller/$action";
|
||||
}
|
||||
|
||||
function ManyManyForm() {
|
||||
$team = DataObject::get_one('ComplexTableFieldTest_Team', "Name = 'The Awesome People'");
|
||||
|
||||
$playersField = new ComplexTableField(
|
||||
$this,
|
||||
'Players',
|
||||
'ComplexTableFieldTest_Player',
|
||||
ComplexTableFieldTest_Player::$summary_fields,
|
||||
'getCMSFields'
|
||||
);
|
||||
|
||||
$playersField->setParentClass('ComplexTableFieldTest_Team');
|
||||
|
||||
$form = new Form(
|
||||
$this,
|
||||
'ManyManyForm',
|
||||
new FieldSet(
|
||||
new HiddenField('ID', '', $team->ID),
|
||||
$playersField
|
||||
),
|
||||
new FieldSet(
|
||||
new FormAction('doSubmit', 'Submit')
|
||||
)
|
||||
);
|
||||
|
||||
$form->disableSecurityToken();
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
function HasManyForm() {
|
||||
$team = DataObject::get_one('ComplexTableFieldTest_Team', "Name = 'The Awesome People'");
|
||||
|
||||
$sponsorsField = new ComplexTableField(
|
||||
$this,
|
||||
'Sponsors',
|
||||
'ComplexTableFieldTest_Sponsor',
|
||||
ComplexTableFieldTest_Sponsor::$summary_fields,
|
||||
'getCMSFields'
|
||||
);
|
||||
|
||||
$sponsorsField->setParentClass('ComplexTableFieldTest_Team');
|
||||
|
||||
$form = new Form(
|
||||
$this,
|
||||
'HasManyForm',
|
||||
new FieldSet(
|
||||
new HiddenField('ID', '', $team->ID),
|
||||
$sponsorsField
|
||||
),
|
||||
new FieldSet(
|
||||
new FormAction('doSubmit', 'Submit')
|
||||
)
|
||||
);
|
||||
|
||||
$form->disableSecurityToken();
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
}
|
||||
class ComplexTableFieldTest_Player extends DataObject implements TestOnly {
|
||||
|
||||
public static $db = array(
|
||||
'Name' => 'Varchar(100)'
|
||||
);
|
||||
|
||||
public static $many_many = array(
|
||||
'Teams' => 'ComplexTableFieldTest_Team'
|
||||
);
|
||||
|
||||
public static $many_many_extraFields = array(
|
||||
'Teams' => array(
|
||||
'Role' => 'Varchar(100)',
|
||||
'Position' => "Enum('Admin,Player,Coach','Admin')",
|
||||
'DateJoined' => 'Date'
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
class ComplexTableFieldTest_Team extends DataObject implements TestOnly {
|
||||
|
||||
public static $db = array(
|
||||
'Name' => 'Varchar(100)'
|
||||
);
|
||||
|
||||
public static $belongs_many_many = array(
|
||||
'Players' => 'ComplexTableFieldTest_Player'
|
||||
);
|
||||
|
||||
public static $has_many = array(
|
||||
'Sponsors' => 'ComplexTableFieldTest_Sponsor'
|
||||
);
|
||||
|
||||
}
|
||||
class ComplexTableFieldTest_Sponsor extends DataObject implements TestOnly {
|
||||
|
||||
public static $db = array(
|
||||
'Name' => 'Varchar(100)'
|
||||
);
|
||||
|
||||
public static $has_one = array(
|
||||
'Team' => 'ComplexTableFieldTest_Team'
|
||||
);
|
||||
|
||||
}
|
||||
?>
|
15
tests/forms/ComplexTableFieldTest.yml
Normal file
15
tests/forms/ComplexTableFieldTest.yml
Normal file
@ -0,0 +1,15 @@
|
||||
ComplexTableFieldTest_Player:
|
||||
p1:
|
||||
Name: Joe Bloggs
|
||||
p2:
|
||||
Name: Some Guy
|
||||
ComplexTableFieldTest_Team:
|
||||
t1:
|
||||
Name: The Awesome People
|
||||
t2:
|
||||
Name: Incredible Four
|
||||
ComplexTableFieldTest_Sponsor:
|
||||
s1:
|
||||
Name: Coca Cola
|
||||
s2:
|
||||
Name: Pepsi
|
Loading…
Reference in New Issue
Block a user