mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
(merged from branches/roa. use "svn log -c <changeset> -g <module-svn-path>" for detailed commit message)
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@60207 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
6bd5da7e6e
commit
b5776e0438
@ -54,19 +54,13 @@ class RestfulServer extends Controller {
|
||||
*/
|
||||
function index() {
|
||||
ContentNegotiator::disable();
|
||||
|
||||
$requestMethod = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
if(!isset($this->urlParams['ClassName'])) return $this->notFound();
|
||||
$className = $this->urlParams['ClassName'];
|
||||
$id = (isset($this->urlParams['ID'])) ? $this->urlParams['ID'] : null;
|
||||
$relation = (isset($this->urlParams['Relation'])) ? $this->urlParams['Relation'] : null;
|
||||
|
||||
// This is a little clumsy and should be improved with the new TokenisedURL that's coming
|
||||
if(strpos($relation,'.') !== false) list($relation, $extension) = explode('.', $relation, 2);
|
||||
else if(strpos($id,'.') !== false) list($id, $extension) = explode('.', $id, 2);
|
||||
else if(strpos($className,'.') !== false) list($className, $extension) = explode('.', $className, 2);
|
||||
else $extension = null;
|
||||
$extension = $this->request->getExtension();
|
||||
|
||||
// Determine mime-type from extension
|
||||
$contentMap = array(
|
||||
@ -142,6 +136,8 @@ class RestfulServer extends Controller {
|
||||
|
||||
} else {
|
||||
$obj = DataObject::get($className, "");
|
||||
// show empty serialized result when no records are present
|
||||
if(!$obj) $obj = new DataObjectSet();
|
||||
if(!singleton($className)->stat('api_access')) {
|
||||
return $this->permissionFailure();
|
||||
}
|
||||
|
@ -442,6 +442,21 @@ class Controller extends RequestHandlingData {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins two link segments together, putting a slash between them if necessary.
|
||||
* Use this for building the results of Link() methods.
|
||||
*/
|
||||
static function join_links() {
|
||||
$args = func_get_args();
|
||||
|
||||
$result = array_shift($args);
|
||||
foreach($args as $arg) {
|
||||
if(substr($result,-1) != '/' && $arg[0] != '/') $result .= "/$arg";
|
||||
else $result .= $arg;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
@ -32,6 +32,22 @@ class HTTPRequest extends Object implements ArrayAccess {
|
||||
|
||||
protected $unshiftedButParsedParts = 0;
|
||||
|
||||
function isGET() {
|
||||
return $this->httpMethod == 'GET';
|
||||
}
|
||||
|
||||
function isPOST() {
|
||||
return $this->httpMethod == 'POST';
|
||||
}
|
||||
|
||||
function isPUT() {
|
||||
return $this->httpMethod == 'PUT';
|
||||
}
|
||||
|
||||
function isDELETE() {
|
||||
return $this->httpMethod == 'DELETE';
|
||||
}
|
||||
|
||||
function getVars() {
|
||||
return $this->getVars;
|
||||
}
|
||||
@ -53,6 +69,10 @@ class HTTPRequest extends Object implements ArrayAccess {
|
||||
if(isset($this->getVars[$name])) return $this->getVars[$name];
|
||||
}
|
||||
|
||||
function getExtension() {
|
||||
return $this->extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the existence of a key-value pair in the request to be checked using
|
||||
* array syntax, so isset($request['title']) will check for $_POST['title'] and $_GET['title]
|
||||
@ -195,6 +215,7 @@ class HTTPRequest extends Object implements ArrayAccess {
|
||||
if($v || !isset($this->allParams[$k])) $this->allParams[$k] = $v;
|
||||
}
|
||||
|
||||
if($arguments === array()) $arguments['_matched'] = true;
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
@ -222,9 +243,8 @@ class HTTPRequest extends Object implements ArrayAccess {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the give pattern is an empty pattern - that is, one that only matches completely parsed
|
||||
* URLs. It will also return true if this is a completely parsed URL and the pattern contains only variable
|
||||
* references.
|
||||
* Returns true if this is a URL that will match without shifting off any of the URL.
|
||||
* This is used by the request handler to prevent infinite parsing loops.
|
||||
*/
|
||||
function isEmptyPattern($pattern) {
|
||||
if(preg_match('/^([A-Za-z]+) +(.*)$/', $pattern, $matches)) {
|
||||
@ -232,10 +252,6 @@ class HTTPRequest extends Object implements ArrayAccess {
|
||||
}
|
||||
|
||||
if(trim($pattern) == "") return true;
|
||||
|
||||
if(!$this->dirParts) {
|
||||
return preg_replace('/\$[A-Za-z][A-Za-z0-9]*(\/|$)/','',$pattern) == "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -253,6 +269,5 @@ class HTTPRequest extends Object implements ArrayAccess {
|
||||
*/
|
||||
function allParsed() {
|
||||
return sizeof($this->dirParts) <= $this->unshiftedButParsedParts;
|
||||
|
||||
}
|
||||
}
|
@ -23,6 +23,8 @@
|
||||
* {@link RequestHandlingData::handleRequest()} is where this behaviour is implemented.
|
||||
*/
|
||||
class RequestHandlingData extends ViewableData {
|
||||
protected $request = null;
|
||||
|
||||
/**
|
||||
* The default URL handling rules. This specifies that the next component of the URL corresponds to a method to
|
||||
* be called on this RequestHandlingData object.
|
||||
@ -68,10 +70,12 @@ class RequestHandlingData extends ViewableData {
|
||||
* @uses HTTPRequest
|
||||
*/
|
||||
function handleRequest($request) {
|
||||
$this->request = $request;
|
||||
|
||||
foreach($this->stat('url_handlers') as $rule => $action) {
|
||||
if(isset($_GET['debug_request'])) Debug::message("Testing '$rule' with '" . $request->remaining() . "' on $this->class");
|
||||
if(isset($_REQUEST['debug_request'])) Debug::message("Testing '$rule' with '" . $request->remaining() . "' on $this->class");
|
||||
if($params = $request->match($rule, true)) {
|
||||
if(isset($_GET['debug_request'])) Debug::message("Rule '$rule' matched on $this->class");
|
||||
if(isset($_REQUEST['debug_request'])) Debug::message("Rule '$rule' matched on $this->class");
|
||||
|
||||
// Actions can reference URL parameters, eg, '$Action/$ID/$OtherID' => '$Action',
|
||||
if($action[0] == '$') $action = $params[substr($action,1)];
|
||||
|
@ -616,9 +616,14 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
foreach($this->record as $fieldName => $fieldValue) {
|
||||
if(isset($this->changed[$fieldName]) && $this->changed[$fieldName] && $fieldType = $classSingleton->fieldExists($fieldName)) {
|
||||
$fieldObj = $this->obj($fieldName);
|
||||
$fieldObj->setValue($this->record[$fieldName], $this->record);
|
||||
if(!isset($manipulation[$class])) $manipulation[$class] = array();
|
||||
if($fieldObj) $fieldObj->writeToManipulation($manipulation[$class]);
|
||||
|
||||
// if database column doesn't correlate to a DBField instance, set up a default Varchar DBField
|
||||
// (used mainly for has_one/has_many)
|
||||
if(!$fieldObj) $fieldObj = DBField::create('Varchar', $this->record[$fieldName], $fieldName);
|
||||
|
||||
$fieldObj->setValue($this->record[$fieldName], $this->record);
|
||||
$fieldObj->writeToManipulation($manipulation[$class]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1051,7 +1056,7 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
return $candidate;
|
||||
}
|
||||
} else {
|
||||
eval("\$items = array_merge((array){$class}::\$db, (array)\$items);");
|
||||
eval("\$items = array_merge((array)\$items, (array){$class}::\$db);");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1192,19 +1197,29 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
*/
|
||||
public function scaffoldFormFields() {
|
||||
$fields = new FieldSet();
|
||||
|
||||
$fields->push(new HeaderField($this->singular_name()));
|
||||
foreach($this->db() as $fieldName => $fieldType) {
|
||||
// @todo Pass localized title
|
||||
// commented out, to be less of a pain in the ass
|
||||
//$fields->addFieldToTab('Root.Main', $this->dbObject($fieldName)->scaffoldFormField());
|
||||
$fields->push($this->dbObject($fieldName)->scaffoldFormField());
|
||||
}
|
||||
|
||||
// @todo Add relation tabs
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the scaffold-generated relation fields to the given field set
|
||||
*/
|
||||
protected function addScaffoldRelationFields($fieldSet) {
|
||||
foreach($this->has_many() as $relationship => $component) {
|
||||
$relationshipFields = array_keys($this->searchableFields());
|
||||
$fieldSet->push(new ComplexTableField($this, $relationship, $component, $relationshipFields));
|
||||
}
|
||||
return $fieldSet;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Centerpiece of every data administration interface in Silverstripe,
|
||||
* which returns a {@link FieldSet} suitable for a {@link Form} object.
|
||||
@ -1228,7 +1243,12 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
* @return FieldSet
|
||||
*/
|
||||
public function getCMSFields() {
|
||||
return $this->scaffoldFormFields();
|
||||
$fields = $this->scaffoldFormFields();
|
||||
// If we don't have an ID, then relation fields don't work
|
||||
if($this->ID) {
|
||||
$this->addScaffoldRelationFields($fields);
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,9 +53,6 @@ class ComplexTableField extends TableListField {
|
||||
*/
|
||||
protected $permissions = array(
|
||||
"add",
|
||||
"edit",
|
||||
"show",
|
||||
"delete",
|
||||
//"export",
|
||||
);
|
||||
|
||||
@ -114,6 +111,7 @@ class ComplexTableField extends TableListField {
|
||||
|
||||
static $url_handlers = array(
|
||||
'item/$ID' => 'handleItem',
|
||||
'$Action!' => '$Action',
|
||||
);
|
||||
|
||||
function handleItem($request) {
|
||||
@ -387,6 +385,124 @@ JS;
|
||||
function setTemplatePopup($template) {
|
||||
$this->templatePopup = $template;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function getFieldsFor($childData) {
|
||||
// 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() );
|
||||
$childData->$parentIDName = $childData->ID;
|
||||
}
|
||||
|
||||
// If the fieldset is passed, use it
|
||||
if(is_a($this->detailFormFields,"Fieldset")) {
|
||||
$detailFields = $this->detailFormFields;
|
||||
|
||||
// Else use the formfields returned from the object via a string method call.
|
||||
} else {
|
||||
if(!is_string($this->detailFormFields)) $this->detailFormFields = "getCMSFields";
|
||||
$functioncall = $this->detailFormFields;
|
||||
if(!$childData->hasMethod($functioncall)) $functioncall = "getCMSFields";
|
||||
|
||||
$detailFields = $childData->$functioncall();
|
||||
}
|
||||
|
||||
// the ID field confuses the Controller-logic in finding the right view for ReferencedField
|
||||
$detailFields->removeByName('ID');
|
||||
|
||||
// only add childID if we're not adding a record
|
||||
if($childData->ID) {
|
||||
$detailFields->push(new HiddenField("ctf[childID]","",$childData->ID));
|
||||
}
|
||||
|
||||
// add a namespaced ID instead thats "converted" by saveComplexTableField()
|
||||
$detailFields->push(new HiddenField("ctf[ClassName]","",$this->sourceClass()));
|
||||
|
||||
if($this->getParentClass()) {
|
||||
$parentIdName = $this->getParentIdName($this->getParentClass(), $this->sourceClass());
|
||||
/*
|
||||
if(!$parentIdName) {
|
||||
user_error("ComplexTableField::DetailForm() Cannot automatically
|
||||
determine 'has-one'-relationship to parent class " . $this->ctf->getParentClass() . ",
|
||||
please use setParentClass() to set it manually",
|
||||
E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
if($parentIdName) {
|
||||
// add relational fields
|
||||
$detailFields->push(new HiddenField("ctf[parentClass]"," ",$this->getParentClass()));
|
||||
|
||||
if( $this->relationAutoSetting )
|
||||
$detailFields->push(new HiddenField("$parentIdName"," ",$childData->ID));
|
||||
}
|
||||
}
|
||||
|
||||
return $detailFields;
|
||||
}
|
||||
|
||||
function getValidatorFor($childData) {
|
||||
// if no custom validator is set, and there's on present on the object (e.g. Member), use it
|
||||
if(!isset($this->detailFormValidator) && $childData->hasMethod('getValidator')) {
|
||||
$this->detailFormValidator = $childData->getValidator();
|
||||
}
|
||||
return $this->detailFormValidator;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function add() {
|
||||
if(!$this->can('add')) return;
|
||||
|
||||
return $this->customise(array(
|
||||
'DetailForm' => $this->AddForm(),
|
||||
))->renderWith($this->templatePopup);
|
||||
}
|
||||
|
||||
function AddForm($childID = null) {
|
||||
$className = $this->sourceClass();
|
||||
$childData = new $className();
|
||||
|
||||
$fields = $this->getFieldsFor($childData);
|
||||
$validator = $this->getValidatorFor($childData);
|
||||
|
||||
$form = Object::create(
|
||||
$this->popupClass,
|
||||
$this, "AddForm",
|
||||
$fields, $validator, false, $childData);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the URL-Parameter "action_saveComplexTableField"
|
||||
* to provide a clue to the main controller if the main form has to be rendered,
|
||||
* even if there is no action relevant for the main controller (to provide the instance of ComplexTableField
|
||||
* which in turn saves the record.
|
||||
*
|
||||
* @see {Form::ReferencedField}).
|
||||
*/
|
||||
function saveComplexTableField($params) {
|
||||
$className = $this->sourceClass();
|
||||
$childData = new $className();
|
||||
|
||||
$this->saveInto($childData);
|
||||
$childData->write();
|
||||
|
||||
// if ajax-call in an iframe, update window
|
||||
if(Director::is_ajax()) {
|
||||
// Newly saved objects need their ID reflected in the reloaded form to avoid double saving
|
||||
$form = $this->controller->DetailForm();
|
||||
//$form->loadDataFrom($this->dataObject);
|
||||
FormResponse::update_dom_id($form->FormName(), $form->formHtmlContent(), true, 'update');
|
||||
return FormResponse::respond();
|
||||
} else {
|
||||
Director::redirectBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -463,21 +579,6 @@ class ComplexTableField_ItemRequest extends RequestHandlingData {
|
||||
echo $this->renderWith($this->ctf->templatePopup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Just a hook, processed in {DetailForm()}
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
function add() {
|
||||
if($this->Can('add') !== true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->methodName = "add";
|
||||
|
||||
echo $this->renderWith($this->templatePopup);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
@ -506,89 +607,46 @@ class ComplexTableField_ItemRequest extends RequestHandlingData {
|
||||
function DetailForm($childID = null) {
|
||||
$childData = $this->obj();
|
||||
|
||||
// If the fieldset is passed, use it, else use the formfields returned
|
||||
// from the object via a string method call.
|
||||
if(is_a($this->ctf->detailFormFields,"Fieldset")){
|
||||
$detailFields = $this->detailFormFields;
|
||||
|
||||
} else if( isset( $childData ) && is_string($this->ctf->detailFormFields)){
|
||||
$functioncall = $this->ctf->detailFormFields;
|
||||
if($childData->hasMethod($functioncall)){
|
||||
$detailFields = $childData->$functioncall();
|
||||
}
|
||||
|
||||
} elseif(! isset( $childData ) || $this->methodName == 'add') {
|
||||
$SNG_sourceClass = singleton($this->ctf->sourceClass());
|
||||
if($childData && is_numeric($childData->ID) && $this->ctf->getParentClass()) {
|
||||
// make sure the relation-link is existing, even if we just add the sourceClass
|
||||
// and didn't save it
|
||||
$parentIDName = $this->ctf->getParentIdName( $this->getParentClass(), $this->sourceClass() );
|
||||
$SNG_sourceClass->$parentIDName = $childData->ID;
|
||||
}
|
||||
$functioncall = $this->detailFormFields;
|
||||
if($SNG_sourceClass->hasMethod($functioncall)){
|
||||
$detailFields = $SNG_sourceClass->$functioncall();
|
||||
}
|
||||
else
|
||||
$detailFields = $SNG_sourceClass->getCMSFields();
|
||||
} else {
|
||||
$detailFields = $childData->getCMSFields();
|
||||
}
|
||||
|
||||
if($this->ctf->getParentClass()) {
|
||||
$parentIdName = $this->ctf->getParentIdName($this->ctf->getParentClass(), $this->ctf->sourceClass());
|
||||
/*
|
||||
if(!$parentIdName) {
|
||||
user_error("ComplexTableField::DetailForm() Cannot automatically
|
||||
determine 'has-one'-relationship to parent class " . $this->ctf->getParentClass() . ",
|
||||
please use setParentClass() to set it manually",
|
||||
E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
if($parentIdName) {
|
||||
// add relational fields
|
||||
$detailFields->push(new HiddenField("ctf[parentClass]"," ",$this->ctf->getParentClass()));
|
||||
|
||||
if( $this->relationAutoSetting )
|
||||
$detailFields->push(new HiddenField("$parentIdName"," ",$childData->ID));
|
||||
}
|
||||
}
|
||||
|
||||
// the ID field confuses the Controller-logic in finding the right view for ReferencedField
|
||||
$detailFields->removeByName('ID');
|
||||
|
||||
// only add childID if we're not adding a record
|
||||
if($this->methodName != 'add') {
|
||||
$detailFields->push(new HiddenField("ctf[childID]","",$childData->ID));
|
||||
}
|
||||
|
||||
// add a namespaced ID instead thats "converted" by saveComplexTableField()
|
||||
$detailFields->push(new HiddenField("ctf[ClassName]","",$this->ctf->sourceClass()));
|
||||
|
||||
$fields = $this->ctf->getFieldsFor($childData);
|
||||
$validator = $this->ctf->getValidatorFor($childData);
|
||||
$readonly = ($this->methodName == "show");
|
||||
|
||||
// if no custom validator is set, and there's on present on the object (e.g. Member), use it
|
||||
if(!isset($this->ctf->detailFormValidator) && singleton($this->ctf->sourceClass())->hasMethod('getValidator')) {
|
||||
$this->ctf->detailFormValidator = singleton($this->ctf->sourceClass())->getValidator();
|
||||
}
|
||||
$form = Object::create(
|
||||
$this->ctf->popupClass,
|
||||
$this, "DetailForm",
|
||||
$fields, $validator, $readonly, $childData);
|
||||
|
||||
$form = Object::create($this->ctf->popupClass,$this, "DetailForm", $detailFields, $this->ctf->detailFormValidator, $readonly, $childData);
|
||||
|
||||
if (is_numeric($childData->ID)) {
|
||||
if ($this->methodName == "show" || $this->methodName == "edit") {
|
||||
$form->loadDataFrom($childData);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->methodName == "show") {
|
||||
$form->makeReadonly();
|
||||
}
|
||||
if ($readonly) $form->makeReadonly();
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the URL-Parameter "action_saveComplexTableField"
|
||||
* to provide a clue to the main controller if the main form has to be rendered,
|
||||
* even if there is no action relevant for the main controller (to provide the instance of ComplexTableField
|
||||
* which in turn saves the record.
|
||||
*
|
||||
* @see {Form::ReferencedField}).
|
||||
*/
|
||||
function saveComplexTableField($data, $form, $request) {
|
||||
$form->saveInto($this->obj());
|
||||
$this->obj()->write();
|
||||
|
||||
// if ajax-call in an iframe, update window
|
||||
if(Director::is_ajax()) {
|
||||
// Newly saved objects need their ID reflected in the reloaded form to avoid double saving
|
||||
$form = $this->controller->DetailForm();
|
||||
//$form->loadDataFrom($this->dataObject);
|
||||
FormResponse::update_dom_id($form->FormName(), $form->formHtmlContent(), true, 'update');
|
||||
return FormResponse::respond();
|
||||
|
||||
} else {
|
||||
Director::redirectBack();
|
||||
}
|
||||
}
|
||||
|
||||
function PopupBaseLink() {
|
||||
$link = $this->FormAction() . "&action_callfieldmethod&fieldName={$this->Name()}";
|
||||
if(!strpos($link,'ctf[ID]')) {
|
||||
@ -790,7 +848,6 @@ class ComplexTableField_Popup extends Form {
|
||||
* WARNING: DO NOT CHANGE THE ORDER OF THESE JS FILES
|
||||
* Some have special requirements.
|
||||
*/
|
||||
Requirements::clear();
|
||||
//Requirements::css('cms/css/layout.css');
|
||||
Requirements::css('jsparty/tabstrip/tabstrip.css');
|
||||
Requirements::css('sapphire/css/Form.css');
|
||||
@ -812,7 +869,7 @@ class ComplexTableField_Popup extends Form {
|
||||
Requirements::javascript("sapphire/javascript/ComplexTableField_popup.js");
|
||||
|
||||
if($this->dataObject->hasMethod('getRequirementsForPopup')) {
|
||||
$this->data->getRequirementsForPopup();
|
||||
$this->dataObject->getRequirementsForPopup();
|
||||
}
|
||||
|
||||
$actions = new FieldSet();
|
||||
@ -833,31 +890,6 @@ class ComplexTableField_Popup extends Form {
|
||||
function ShowPagination() {
|
||||
return $this->controller->ShowPagination();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use the URL-Parameter "action_saveComplexTableField"
|
||||
* to provide a clue to the main controller if the main form has to be rendered,
|
||||
* even if there is no action relevant for the main controller (to provide the instance of ComplexTableField
|
||||
* which in turn saves the record.
|
||||
*
|
||||
* @see {Form::ReferencedField}).
|
||||
*/
|
||||
function saveComplexTableField($params) {
|
||||
$this->saveInto($this->dataObject);
|
||||
$this->dataObject->write();
|
||||
|
||||
// if ajax-call in an iframe, update window
|
||||
if(Director::is_ajax()) {
|
||||
// Newly saved objects need their ID reflected in the reloaded form to avoid double saving
|
||||
$form = $this->controller->DetailForm();
|
||||
//$form->loadDataFrom($this->dataObject);
|
||||
FormResponse::update_dom_id($form->FormName(), $form->formHtmlContent(), true, 'update');
|
||||
return FormResponse::respond();
|
||||
} else {
|
||||
Director::redirectBack();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
@ -182,11 +182,11 @@ class Form extends RequestHandlingData {
|
||||
|
||||
// First, try a handler method on the controller
|
||||
if($this->controller->hasMethod($funcName)) {
|
||||
return $this->controller->$funcName($vars, $this);
|
||||
return $this->controller->$funcName($vars, $this, $request);
|
||||
|
||||
// Otherwise, try a handler method on the form object
|
||||
} else {
|
||||
return $this->$funcName($vars, $this);
|
||||
return $this->$funcName($vars, $this, $request);
|
||||
}
|
||||
}
|
||||
|
||||
@ -456,7 +456,6 @@ class Form extends RequestHandlingData {
|
||||
*/
|
||||
function setFormMethod($method) {
|
||||
$this->formMethod = strtolower($method);
|
||||
if($this->formMethod == 'get') $this->fields->push(new HiddenField('executeForm', '', $this->name));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -466,20 +465,46 @@ class Form extends RequestHandlingData {
|
||||
* @return string
|
||||
*/
|
||||
function FormAction() {
|
||||
if($this->controller->hasMethod("FormObjectLink")) {
|
||||
if ($this->formActionPath) {
|
||||
return $this->formActionPath;
|
||||
} elseif($this->controller->hasMethod("FormObjectLink")) {
|
||||
return $this->controller->FormObjectLink($this->name);
|
||||
} else {
|
||||
$link = $this->controller->Link();
|
||||
if(substr($link,-1) != '/') $link .= '/';
|
||||
return $link . $this->name;
|
||||
return Controller::join_links($this->controller->Link(), $this->name);
|
||||
}
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
private $formActionPath = false;
|
||||
|
||||
/**
|
||||
* Set the form action attribute to a custom URL.
|
||||
*
|
||||
* Note: For "normal" forms, you shouldn't need to use this method. It is recommended only for situations where you have
|
||||
* two relatively distinct parts of the system trying to communicate via a form post.
|
||||
*/
|
||||
function setFormAction($path) {
|
||||
$this->formActionPath = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
private $htmlID = null;
|
||||
|
||||
/**
|
||||
* Returns the name of the form
|
||||
*/
|
||||
function FormName() {
|
||||
return $this->class . '_' . str_replace('.', '', $this->name);
|
||||
if($this->htmlID) return $this->htmlID;
|
||||
else return $this->class . '_' . str_replace('.','',$this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the HTML ID attribute of the form
|
||||
*/
|
||||
function setHTMLID($id) {
|
||||
$this->htmlID = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -920,7 +945,6 @@ class Form extends RequestHandlingData {
|
||||
*/
|
||||
function testSubmission($action, $data) {
|
||||
$data['action_' . $action] = true;
|
||||
$data['executeForm'] = $this->name;
|
||||
|
||||
return Director::test($this->FormAction(), $data, Controller::curr()->getSession());
|
||||
|
||||
|
@ -465,16 +465,6 @@ HTML;
|
||||
user_error('FormField::setExtraClass() is deprecated. Use FormField::addExtraClass() instead.', E_USER_NOTICE);
|
||||
$this->extraClasses[] = $extraClass;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This returns a link used to specify a controller in a form action.
|
||||
* This lets us use FormFields as controllers.
|
||||
*/
|
||||
function FormObjectLink($formName) {
|
||||
return $this->form->Controller()->Link() . "?executeController=" . $this->form->Name() . ".FieldMap." . $this->Name() . "&executeForm=" . $formName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
@ -72,8 +72,6 @@ ComplexTableField.prototype = {
|
||||
popupLink = _popupLink;
|
||||
table = _table;
|
||||
} else {
|
||||
alert(this.innerHTML);
|
||||
|
||||
// if clicked item is an input-element, don't trigger popup
|
||||
var el = Event.element(e);
|
||||
var input = Event.findElement(e,"input");
|
||||
|
@ -75,6 +75,12 @@ class SearchContext extends Object {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns scaffolded search fields for UI.
|
||||
*
|
||||
* @todo is this necessary in the SearchContext? - ModelAdmin could unwrap this and just use DataObject::scaffoldSearchFields
|
||||
* @return FieldSet
|
||||
*/
|
||||
public function getSearchFields() {
|
||||
return ($this->fields) ? $this->fields : singleton($this->modelClass)->scaffoldSearchFields();
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
* @package sapphire
|
||||
* @subpackage search
|
||||
*/
|
||||
class ExactMatchSearchFilter extends SearchFilter {
|
||||
class ExactMatchFilter extends SearchFilter {
|
||||
|
||||
}
|
||||
?>
|
@ -5,7 +5,7 @@
|
||||
* @package sapphire
|
||||
* @subpackage search
|
||||
*/
|
||||
class FulltextSearchFilter extends SearchFilter {
|
||||
class FulltextFilter extends SearchFilter {
|
||||
|
||||
}
|
||||
?>
|
@ -5,7 +5,7 @@
|
||||
* @package sapphire
|
||||
* @subpackage search
|
||||
*/
|
||||
class PartialMatchSearchFilter extends SearchFilter {
|
||||
class PartialMatchFilter extends SearchFilter {
|
||||
|
||||
}
|
||||
?>
|
13
search/filters/SubstringMatchFilter.php
Normal file
13
search/filters/SubstringMatchFilter.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* Uses a substring match against content in column rows.
|
||||
*
|
||||
* @package sapphire
|
||||
* @subpackage search
|
||||
*/
|
||||
class SubstringMatchFilter extends SearchFilter {
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -124,6 +124,20 @@ class DataObjectTest extends SapphireTest {
|
||||
$this->assertTrue($comment->Name == 'Joe');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test writing of database columns which don't correlate to a DBField,
|
||||
* e.g. all relation fields on has_one/has_many like "ParentID".
|
||||
*
|
||||
*/
|
||||
function testWritePropertyWithoutDBField() {
|
||||
$page = $this->objFromFixture('Page', 'page1');
|
||||
$page->ParentID = 99;
|
||||
$page->write();
|
||||
// reload the page from the database
|
||||
$savedPage = DataObject::get_by_id('Page', $page->ID);
|
||||
$this->assertTrue($savedPage->ParentID == 99);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test has many relationships
|
||||
* - Test getComponents() gets the ComponentSet of the other side of the relation
|
||||
|
18
tests/HTTPRequestTest.php
Normal file
18
tests/HTTPRequestTest.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
class HTTPRequestTest extends SapphireTest {
|
||||
static $fixture_file = null;
|
||||
|
||||
function testMatch() {
|
||||
$request = new HTTPRequest("GET", "admin/crm/add");
|
||||
|
||||
/* When a rule matches, but has no variables, array("_matched" => true) is returned. */
|
||||
$this->assertEquals(array("_matched" => true), $request->match('admin/crm', true));
|
||||
|
||||
/* Becasue we shifted admin/crm off the stack, just "add" should be remaining */
|
||||
$this->assertEquals("add", $request->remaining());
|
||||
|
||||
$this->assertEquals(array("_matched" => true), $request->match('add', true));
|
||||
}
|
||||
|
||||
}
|
35
tests/SearchContextTest.php
Normal file
35
tests/SearchContextTest.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
class SearchContextTest extends SapphireTest {
|
||||
static $fixture_file = 'sapphire/tests/SearchContextTest.yml';
|
||||
|
||||
function testResultSetFilterReturnsExpectedCount() {
|
||||
$person = singleton('PersonBubble');
|
||||
$context = $person->getDefaultSearchContext();
|
||||
|
||||
$results = $context->getResultSet(array('Name'=>''));
|
||||
$this->assertEquals(5, $results->Count());
|
||||
|
||||
$results = $context->getResultSet(array('EyeColor'=>'green'));
|
||||
$this->assertEquals(2, $results->Count());
|
||||
|
||||
$results = $context->getResultSet(array('EyeColor'=>'green', 'HairColor'=>'black'));
|
||||
$this->assertEquals(1, $results->Count());
|
||||
}
|
||||
|
||||
//function
|
||||
|
||||
}
|
||||
|
||||
class PersonBubble extends DataObject {
|
||||
|
||||
static $db = array(
|
||||
"Name" => "Text",
|
||||
"Email" => "Text",
|
||||
"HairColor" => "Text",
|
||||
"EyeColor" => "Text"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
?>
|
28
tests/SearchContextTest.yml
Normal file
28
tests/SearchContextTest.yml
Normal file
@ -0,0 +1,28 @@
|
||||
PersonBubble:
|
||||
person1:
|
||||
Name: James
|
||||
Email: james@example.com
|
||||
HairColor: brown
|
||||
EyeColor: brown
|
||||
person2:
|
||||
Name: John
|
||||
Email: john@example.com
|
||||
HairColor: blond
|
||||
EyeColor: blue
|
||||
person3:
|
||||
Name: Jane
|
||||
Email: jane@example.com
|
||||
HairColor: brown
|
||||
EyeColor: green
|
||||
person4:
|
||||
Name: Hemi
|
||||
Email: hemi@example.com
|
||||
HairColor: black
|
||||
EyeColor: brown
|
||||
person5:
|
||||
Name: Sara
|
||||
Email: sara@example.com
|
||||
HairColor: black
|
||||
EyeColor: green
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user