mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02: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@60209 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
a599df309c
commit
016cff2093
@ -32,10 +32,7 @@ Director::addRules(10, array(
|
||||
));
|
||||
|
||||
Director::addRules(1, array(
|
||||
'$URLSegment/$Action/$ID/$OtherID' => array(
|
||||
'_PopTokeniser' => 1,
|
||||
'Controller' => 'ModelAsController',
|
||||
),
|
||||
'$URLSegment//$Action/$ID/$OtherID' => 'ModelAsController',
|
||||
));
|
||||
|
||||
/**
|
||||
|
42
api/DataFormatter.php
Normal file
42
api/DataFormatter.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A DataFormatter object handles transformation of data from Sapphire model objects to a particular output format, and vice versa.
|
||||
* This is most commonly used in developing RESTful APIs.
|
||||
*/
|
||||
|
||||
abstract class DataFormatter extends Object {
|
||||
/**
|
||||
* Get a DataFormatter object suitable for handling the given file extension
|
||||
*/
|
||||
static function for_extension($extension) {
|
||||
$classes = ClassInfo::subclassesFor("DataFormatter");
|
||||
array_shift($classes);
|
||||
|
||||
foreach($classes as $class) {
|
||||
$formatter = singleton($class);
|
||||
if(in_array($extension, $formatter->supportedExtensions())) {
|
||||
return $formatter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of the extensions that this data formatter supports
|
||||
*/
|
||||
abstract function supportedExtensions();
|
||||
|
||||
|
||||
/**
|
||||
* Convert a single data object to this format. Return a string.
|
||||
* @todo Add parameters for things like selecting output columns
|
||||
*/
|
||||
abstract function convertDataObject(DataObjectInterface $do);
|
||||
|
||||
/**
|
||||
* Convert a data object set to this format. Return a string.
|
||||
* @todo Add parameters for things like selecting output columns
|
||||
*/
|
||||
abstract function convertDataObjectSet(DataObjectSet $set);
|
||||
|
||||
}
|
81
api/JSONDataFormatter.php
Normal file
81
api/JSONDataFormatter.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
class JSONDataFormatter extends DataFormatter {
|
||||
/**
|
||||
* @todo pass this from the API to the data formatter somehow
|
||||
*/
|
||||
static $api_base = "api/v1/";
|
||||
|
||||
public function supportedExtensions() {
|
||||
return array('json', 'js');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an XML representation of the given {@link DataObject}.
|
||||
*
|
||||
* @param DataObject $obj
|
||||
* @param $includeHeader Include <?xml ...?> header (Default: true)
|
||||
* @return String XML
|
||||
*/
|
||||
public function convertDataObject(DataObjectInterface $obj) {
|
||||
$className = $obj->class;
|
||||
$id = $obj->ID;
|
||||
|
||||
$json = "{\n className : \"$className\",\n";
|
||||
$dbFields = array_merge($obj->databaseFields(), array('ID'=>'Int'));
|
||||
foreach($dbFields as $fieldName => $fieldType) {
|
||||
if(is_object($obj->$fieldName)) {
|
||||
$jsonParts[] = "$fieldName : " . $obj->$fieldName->toJSON();
|
||||
} else {
|
||||
$jsonParts[] = "$fieldName : \"" . Convert::raw2js($obj->$fieldName) . "\"";
|
||||
}
|
||||
}
|
||||
|
||||
foreach($obj->has_one() as $relName => $relClass) {
|
||||
$fieldName = $relName . 'ID';
|
||||
if($obj->$fieldName) {
|
||||
$href = Director::absoluteURL(self::$api_base . "$relClass/" . $obj->$fieldName);
|
||||
} else {
|
||||
$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName");
|
||||
}
|
||||
$jsonParts[] = "$relName : { className : \"$relClass\", href : \"$href.json\", id : \"{$obj->$fieldName}\" }";
|
||||
}
|
||||
|
||||
foreach($obj->has_many() as $relName => $relClass) {
|
||||
$jsonInnerParts = array();
|
||||
$items = $obj->$relName();
|
||||
foreach($items as $item) {
|
||||
//$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName/$item->ID");
|
||||
$href = Director::absoluteURL(self::$api_base . "$relClass/$item->ID");
|
||||
$jsonInnerParts[] = "{ className : \"$relClass\", href : \"$href.json\", id : \"{$obj->$fieldName}\" }";
|
||||
}
|
||||
$jsonParts[] = "$relName : [\n " . implode(",\n ", $jsonInnerParts) . " \n ]";
|
||||
}
|
||||
|
||||
foreach($obj->many_many() as $relName => $relClass) {
|
||||
$jsonInnerParts = array();
|
||||
$items = $obj->$relName();
|
||||
foreach($items as $item) {
|
||||
//$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName/$item->ID");
|
||||
$href = Director::absoluteURL(self::$api_base . "$relClass/$item->ID");
|
||||
$jsonInnerParts[] = " { className : \"$relClass\", href : \"$href.json\", id : \"{$obj->$fieldName}\" }";
|
||||
}
|
||||
$jsonParts[] = "$relName : [\n " . implode(",\n ", $jsonInnerParts) . "\n ]";
|
||||
}
|
||||
|
||||
return "{\n " . implode(",\n ", $jsonParts) . "\n}"; }
|
||||
|
||||
/**
|
||||
* Generate an XML representation of the given {@link DataObjectSet}.
|
||||
*
|
||||
* @param DataObjectSet $set
|
||||
* @return String XML
|
||||
*/
|
||||
public function convertDataObjectSet(DataObjectSet $set) {
|
||||
$jsonParts = array();
|
||||
foreach($set as $item) {
|
||||
if($item->canView()) $jsonParts[] = $this->convertDataObject($item);
|
||||
}
|
||||
return "[\n" . implode(",\n", $jsonParts) . "\n]";
|
||||
}
|
||||
}
|
@ -71,16 +71,19 @@ class RestfulServer extends Controller {
|
||||
'html' => 'text/html',
|
||||
);
|
||||
$contentType = isset($contentMap[$extension]) ? $contentMap[$extension] : 'text/xml';
|
||||
|
||||
|
||||
if(!$extension) $extension = "xml";
|
||||
$formatter = DataFormatter::for_extension($extension); //$this->dataFormatterFromMime($contentType);
|
||||
|
||||
switch($requestMethod) {
|
||||
case 'GET':
|
||||
return $this->getHandler($className, $id, $relation, $contentType);
|
||||
return $this->getHandler($className, $id, $relation, $formatter);
|
||||
|
||||
case 'PUT':
|
||||
return $this->putHandler($className, $id, $relation, $contentType);
|
||||
return $this->putHandler($className, $id, $relation, $formatter);
|
||||
|
||||
case 'DELETE':
|
||||
return $this->deleteHandler($className, $id, $relation, $contentType);
|
||||
return $this->deleteHandler($className, $id, $relation, $formatter);
|
||||
|
||||
case 'POST':
|
||||
}
|
||||
@ -118,7 +121,7 @@ class RestfulServer extends Controller {
|
||||
* @param String $contentType
|
||||
* @return String The serialized representation of the requested object(s) - usually XML or JSON.
|
||||
*/
|
||||
protected function getHandler($className, $id, $relation, $contentType) {
|
||||
protected function getHandler($className, $id, $relation, $formatter) {
|
||||
if($id) {
|
||||
$obj = DataObject::get_by_id($className, $id);
|
||||
if(!$obj) {
|
||||
@ -142,176 +145,10 @@ class RestfulServer extends Controller {
|
||||
return $this->permissionFailure();
|
||||
}
|
||||
}
|
||||
|
||||
// TO DO - inspect that Accept header as well. $_GET['accept'] can still be checked, as it's handy for debugging
|
||||
switch($contentType) {
|
||||
case "text/xml":
|
||||
$this->getResponse()->addHeader("Content-type", "text/xml");
|
||||
if($obj instanceof DataObjectSet) return $this->dataObjectSetAsXML($obj);
|
||||
else return $this->dataObjectAsXML($obj);
|
||||
|
||||
case "text/json":
|
||||
//$this->getResponse()->addHeader("Content-type", "text/json");
|
||||
if($obj instanceof DataObjectSet) return $this->dataObjectSetAsJSON($obj);
|
||||
else return $this->dataObjectAsJSON($obj);
|
||||
|
||||
case "text/html":
|
||||
case "application/xhtml+xml":
|
||||
if($obj instanceof DataObjectSet) return $this->dataObjectSetAsXHTML($obj);
|
||||
else return $this->dataObjectAsXHTML($obj);
|
||||
}
|
||||
if($obj instanceof DataObjectSet) return $formatter->convertDataObjectSet($obj);
|
||||
else return $formatter->convertDataObject($obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an XML representation of the given {@link DataObject}.
|
||||
*
|
||||
* @param DataObject $obj
|
||||
* @param $includeHeader Include <?xml ...?> header (Default: true)
|
||||
* @return String XML
|
||||
*/
|
||||
public function dataObjectAsXML(DataObject $obj, $includeHeader = true) {
|
||||
$className = $obj->class;
|
||||
$id = $obj->ID;
|
||||
$objHref = Director::absoluteURL(self::$api_base . "$obj->class/$obj->ID");
|
||||
|
||||
$json = "";
|
||||
if($includeHeader) $json .= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
$json .= "<$className href=\"$objHref.xml\">\n";
|
||||
$dbFields = array_merge($obj->databaseFields(), array('ID'=>'Int'));
|
||||
foreach($dbFields as $fieldName => $fieldType) {
|
||||
if(is_object($obj->$fieldName)) {
|
||||
$json .= $obj->$fieldName->toXML();
|
||||
} else {
|
||||
$json .= "<$fieldName>" . Convert::raw2xml($obj->$fieldName) . "</$fieldName>\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach($obj->has_one() as $relName => $relClass) {
|
||||
$fieldName = $relName . 'ID';
|
||||
if($obj->$fieldName) {
|
||||
$href = Director::absoluteURL(self::$api_base . "$relClass/" . $obj->$fieldName);
|
||||
} else {
|
||||
$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName");
|
||||
}
|
||||
$json .= "<$relName linktype=\"has_one\" href=\"$href.xml\" id=\"{$obj->$fieldName}\" />\n";
|
||||
}
|
||||
|
||||
foreach($obj->has_many() as $relName => $relClass) {
|
||||
$json .= "<$relName linktype=\"has_many\" href=\"$objHref/$relName.xml\">\n";
|
||||
$items = $obj->$relName();
|
||||
foreach($items as $item) {
|
||||
//$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName/$item->ID");
|
||||
$href = Director::absoluteURL(self::$api_base . "$relClass/$item->ID");
|
||||
$json .= "<$relClass href=\"$href.xml\" id=\"{$item->ID}\" />\n";
|
||||
}
|
||||
$json .= "</$relName>\n";
|
||||
}
|
||||
|
||||
foreach($obj->many_many() as $relName => $relClass) {
|
||||
$json .= "<$relName linktype=\"many_many\" href=\"$objHref/$relName.xml\">\n";
|
||||
$items = $obj->$relName();
|
||||
foreach($items as $item) {
|
||||
$href = Director::absoluteURL(self::$api_base . "$relClass/$item->ID");
|
||||
$json .= "<$relClass href=\"$href.xml\" id=\"{$item->ID}\" />\n";
|
||||
}
|
||||
$json .= "</$relName>\n";
|
||||
}
|
||||
|
||||
$json .= "</$className>";
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an XML representation of the given {@link DataObjectSet}.
|
||||
*
|
||||
* @param DataObjectSet $set
|
||||
* @return String XML
|
||||
*/
|
||||
public function dataObjectSetAsXML(DataObjectSet $set) {
|
||||
$className = $set->class;
|
||||
|
||||
$xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<$className>\n";
|
||||
foreach($set as $item) {
|
||||
if($item->canView()) $xml .= $this->dataObjectAsXML($item, false);
|
||||
}
|
||||
$xml .= "</$className>";
|
||||
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an JSON representation of the given {@link DataObject}.
|
||||
*
|
||||
* @see http://json.org
|
||||
*
|
||||
* @param DataObject $obj
|
||||
* @return String JSON
|
||||
*/
|
||||
public function dataObjectAsJSON(DataObject $obj) {
|
||||
$className = $obj->class;
|
||||
$id = $obj->ID;
|
||||
|
||||
$json = "{\n className : \"$className\",\n";
|
||||
$dbFields = array_merge($obj->databaseFields(), array('ID'=>'Int'));
|
||||
foreach($dbFields as $fieldName => $fieldType) {
|
||||
if(is_object($obj->$fieldName)) {
|
||||
$jsonParts[] = "$fieldName : " . $obj->$fieldName->toJSON();
|
||||
} else {
|
||||
$jsonParts[] = "$fieldName : \"" . Convert::raw2js($obj->$fieldName) . "\"";
|
||||
}
|
||||
}
|
||||
|
||||
foreach($obj->has_one() as $relName => $relClass) {
|
||||
$fieldName = $relName . 'ID';
|
||||
if($obj->$fieldName) {
|
||||
$href = Director::absoluteURL(self::$api_base . "$relClass/" . $obj->$fieldName);
|
||||
} else {
|
||||
$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName");
|
||||
}
|
||||
$jsonParts[] = "$relName : { className : \"$relClass\", href : \"$href.json\", id : \"{$obj->$fieldName}\" }";
|
||||
}
|
||||
|
||||
foreach($obj->has_many() as $relName => $relClass) {
|
||||
$jsonInnerParts = array();
|
||||
$items = $obj->$relName();
|
||||
foreach($items as $item) {
|
||||
//$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName/$item->ID");
|
||||
$href = Director::absoluteURL(self::$api_base . "$relClass/$item->ID");
|
||||
$jsonInnerParts[] = "{ className : \"$relClass\", href : \"$href.json\", id : \"{$obj->$fieldName}\" }";
|
||||
}
|
||||
$jsonParts[] = "$relName : [\n " . implode(",\n ", $jsonInnerParts) . " \n ]";
|
||||
}
|
||||
|
||||
foreach($obj->many_many() as $relName => $relClass) {
|
||||
$jsonInnerParts = array();
|
||||
$items = $obj->$relName();
|
||||
foreach($items as $item) {
|
||||
//$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName/$item->ID");
|
||||
$href = Director::absoluteURL(self::$api_base . "$relClass/$item->ID");
|
||||
$jsonInnerParts[] = " { className : \"$relClass\", href : \"$href.json\", id : \"{$obj->$fieldName}\" }";
|
||||
}
|
||||
$jsonParts[] = "$relName : [\n " . implode(",\n ", $jsonInnerParts) . "\n ]";
|
||||
}
|
||||
|
||||
return "{\n " . implode(",\n ", $jsonParts) . "\n}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an JSON representation of the given {@link DataObjectSet}.
|
||||
*
|
||||
* @param DataObjectSet $set
|
||||
* @return String JSON
|
||||
*/
|
||||
public function dataObjectSetAsJSON(DataObjectSet $set) {
|
||||
$jsonParts = array();
|
||||
foreach($set as $item) {
|
||||
if($item->canView()) $jsonParts[] = $this->dataObjectAsJSON($item);
|
||||
}
|
||||
return "[\n" . implode(",\n", $jsonParts) . "\n]";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handler for object delete
|
||||
@ -324,7 +161,6 @@ class RestfulServer extends Controller {
|
||||
} else {
|
||||
return $this->permissionFailure();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
96
api/XMLDataFormatter.php
Normal file
96
api/XMLDataFormatter.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
class XMLDataFormatter extends DataFormatter {
|
||||
/**
|
||||
* @todo pass this from the API to the data formatter somehow
|
||||
*/
|
||||
static $api_base = "api/v1/";
|
||||
|
||||
public function supportedExtensions() {
|
||||
return array('xml');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an XML representation of the given {@link DataObject}.
|
||||
*
|
||||
* @param DataObject $obj
|
||||
* @param $includeHeader Include <?xml ...?> header (Default: true)
|
||||
* @return String XML
|
||||
*/
|
||||
public function convertDataObject(DataObjectInterface $obj) {
|
||||
Controller::curr()->getResponse()->addHeader("Content-type", "text/xml");
|
||||
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" . $this->convertDataObjectWithoutHeader($obj);
|
||||
}
|
||||
|
||||
|
||||
public function convertDataObjectWithoutHeader(DataObject $obj) {
|
||||
$className = $obj->class;
|
||||
$id = $obj->ID;
|
||||
$objHref = Director::absoluteURL(self::$api_base . "$obj->class/$obj->ID");
|
||||
|
||||
$json = "<$className href=\"$objHref.xml\">\n";
|
||||
$dbFields = array_merge($obj->databaseFields(), array('ID'=>'Int'));
|
||||
foreach($dbFields as $fieldName => $fieldType) {
|
||||
if(is_object($obj->$fieldName)) {
|
||||
$json .= $obj->$fieldName->toXML();
|
||||
} else {
|
||||
$json .= "<$fieldName>" . Convert::raw2xml($obj->$fieldName) . "</$fieldName>\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach($obj->has_one() as $relName => $relClass) {
|
||||
$fieldName = $relName . 'ID';
|
||||
if($obj->$fieldName) {
|
||||
$href = Director::absoluteURL(self::$api_base . "$relClass/" . $obj->$fieldName);
|
||||
} else {
|
||||
$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName");
|
||||
}
|
||||
$json .= "<$relName linktype=\"has_one\" href=\"$href.xml\" id=\"{$obj->$fieldName}\" />\n";
|
||||
}
|
||||
|
||||
foreach($obj->has_many() as $relName => $relClass) {
|
||||
$json .= "<$relName linktype=\"has_many\" href=\"$objHref/$relName.xml\">\n";
|
||||
$items = $obj->$relName();
|
||||
foreach($items as $item) {
|
||||
//$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName/$item->ID");
|
||||
$href = Director::absoluteURL(self::$api_base . "$relClass/$item->ID");
|
||||
$json .= "<$relClass href=\"$href.xml\" id=\"{$item->ID}\" />\n";
|
||||
}
|
||||
$json .= "</$relName>\n";
|
||||
}
|
||||
|
||||
foreach($obj->many_many() as $relName => $relClass) {
|
||||
$json .= "<$relName linktype=\"many_many\" href=\"$objHref/$relName.xml\">\n";
|
||||
$items = $obj->$relName();
|
||||
foreach($items as $item) {
|
||||
$href = Director::absoluteURL(self::$api_base . "$relClass/$item->ID");
|
||||
$json .= "<$relClass href=\"$href.xml\" id=\"{$item->ID}\" />\n";
|
||||
}
|
||||
$json .= "</$relName>\n";
|
||||
}
|
||||
|
||||
$json .= "</$className>";
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an XML representation of the given {@link DataObjectSet}.
|
||||
*
|
||||
* @param DataObjectSet $set
|
||||
* @return String XML
|
||||
*/
|
||||
public function convertDataObjectSet(DataObjectSet $set) {
|
||||
Controller::curr()->getResponse()->addHeader("Content-type", "text/xml");
|
||||
$className = $set->class;
|
||||
|
||||
$xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<$className>\n";
|
||||
foreach($set as $item) {
|
||||
if($item->canView()) $xml .= $this->convertDataObjectWithoutHeader($item);
|
||||
}
|
||||
$xml .= "</$className>";
|
||||
|
||||
return $xml;
|
||||
}
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
#!/usr/bin/php5
|
||||
<?php
|
||||
|
||||
if(isset($_SERVER['HTTP_HOST'])) {
|
||||
echo "cli-script.php can't be run from a web request, you have to run it on the command-line.";
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* File similar to main.php designed for command-line scripts
|
||||
@ -98,7 +102,7 @@ require_once(MANIFEST_FILE);
|
||||
|
||||
if(isset($_GET['debugmanifest'])) Debug::show(file_get_contents(MANIFEST_FILE));
|
||||
|
||||
if(!isset(Director::$environment_type)) Director::set_environment_type($envType);
|
||||
if(!isset(Director::$environment_type) && $envType) Director::set_environment_type($envType);
|
||||
|
||||
// Load error handlers
|
||||
Debug::loadErrorHandlers();
|
||||
|
@ -62,6 +62,13 @@ class ArrayData extends ViewableData {
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is pretty crude, but it helps diagnose error situations
|
||||
*/
|
||||
function forTemplate() {
|
||||
return var_export($this->array, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -109,6 +109,7 @@ class SSViewer extends Object {
|
||||
*/
|
||||
public function dontRewriteHashlinks() {
|
||||
$this->rewriteHashlinks = false;
|
||||
self::$options['rewriteHashlinks'] = false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -577,7 +577,8 @@ class Director {
|
||||
*/
|
||||
static function set_environment_type($et) {
|
||||
if($et != 'dev' && $et != 'test' && $et != 'live') {
|
||||
user_error("Director::set_environment_type passed '$et'. It should be passed dev, test, or live");
|
||||
Debug::backtrace();
|
||||
user_error("Director::set_environment_type passed '$et'. It should be passed dev, test, or live", E_USER_WARNING);
|
||||
} else {
|
||||
self::$environment_type = $et;
|
||||
}
|
||||
|
@ -1206,7 +1206,7 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
$model = singleton($component);
|
||||
$records = DataObject::get($component);
|
||||
$collect = ($model->hasMethod('customSelectOption')) ? 'customSelectOption' : current($model->summary_fields());
|
||||
$options = $records->filter_map('ID', $collect);
|
||||
$options = $records ? $records->filter_map('ID', $collect) : array();
|
||||
$fields->push(new DropdownField($relationship.'ID', $relationship, $options));
|
||||
}
|
||||
return $fields;
|
||||
@ -1216,9 +1216,21 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
* 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->searchable_fields());
|
||||
$fieldSet->push(new ComplexTableField($this, $relationship, $component, $relationshipFields));
|
||||
|
||||
if($this->has_many()) {
|
||||
// Refactor the fields that we have been given into a tab, "Main", in a tabset
|
||||
$oldFields = $fieldSet;
|
||||
$fieldSet = new FieldSet(
|
||||
new TabSet("Root", new Tab("Main"))
|
||||
);
|
||||
foreach($oldFields as $field) $fieldSet->addFieldToTab("Root.Main", $field);
|
||||
|
||||
// Add each relation as a separate tab
|
||||
foreach($this->has_many() as $relationship => $component) {
|
||||
$relationshipFields = singleton($component)->summary_fields();
|
||||
$foreignKey = $this->getComponentJoinField($relationship);
|
||||
$fieldSet->addFieldToTab("Root.$relationship", new ComplexTableField($this, $relationship, $component, $relationshipFields, "getCMSFields", "$foreignKey = $this->ID"));
|
||||
}
|
||||
}
|
||||
return $fieldSet;
|
||||
}
|
||||
@ -1249,7 +1261,7 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
$fields = $this->scaffoldFormFields();
|
||||
// If we don't have an ID, then relation fields don't work
|
||||
if($this->ID) {
|
||||
$this->addScaffoldRelationFields($fields);
|
||||
$fields = $this->addScaffoldRelationFields($fields);
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
@ -2085,7 +2097,7 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
public function searchable_fields() {
|
||||
$fields = $this->stat('searchable_fields');
|
||||
if (!$fields) {
|
||||
$fields = array_fill_keys($this->summary_fields(), 'TextField');
|
||||
$fields = array_fill_keys(array_keys($this->summary_fields()), 'TextField');
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
@ -2101,11 +2113,19 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
$fields = $this->stat('summary_fields');
|
||||
if (!$fields) {
|
||||
$fields = array();
|
||||
if ($this->hasField('Name')) $fields[] = 'Name';
|
||||
if ($this->hasField('Title')) $fields[] = 'Title';
|
||||
if ($this->hasField('Description')) $fields[] = 'Description';
|
||||
if ($this->hasField('Firstname')) $fields[] = 'Firstname';
|
||||
if ($this->hasField('Name')) $fields['Name'] = 'Name';
|
||||
if ($this->hasField('Title')) $fields['Title'] = 'Title';
|
||||
if ($this->hasField('Description')) $fields['Description'] = 'Description';
|
||||
if ($this->hasField('Firstname')) $fields['Firstname'] = 'Firstname';
|
||||
}
|
||||
|
||||
// Final fail-over, just list all the fields :-S
|
||||
if(!$fields) {
|
||||
foreach(array_keys($this->db()) as $field) {
|
||||
$fields[$field] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
@ -2128,7 +2148,7 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
} else {
|
||||
if (is_array($type)) {
|
||||
$filter = current($type);
|
||||
$filters[$name] = new $filter();
|
||||
$filters[$name] = new $filter($name);
|
||||
} else {
|
||||
if (is_subclass_of($type, 'SearchFilter')) {
|
||||
$filters[$name] = new $type($name);
|
||||
|
@ -75,7 +75,7 @@ class DatabaseAdmin extends Controller {
|
||||
* Updates the database schema, creating tables & fields as necessary.
|
||||
*/
|
||||
function build() {
|
||||
if(Director::isLive() && Security::database_is_ready() && (!Member::currentUser() || !Member::currentUser()->isAdmin())) {
|
||||
if(Director::isLive() && Security::database_is_ready() && !Director::is_cli() && (!Member::currentUser() || !Member::currentUser()->isAdmin())) {
|
||||
Security::permissionFailure($this,
|
||||
"This page is secured and you need administrator rights to access it. " .
|
||||
"Enter your credentials below and we will send you right along.");
|
||||
|
@ -68,6 +68,7 @@ class SQLQuery extends Object {
|
||||
|
||||
/**
|
||||
* Construct a new SQLQuery.
|
||||
*
|
||||
* @param array $select An array of fields to select.
|
||||
* @param array $from An array of join clauses. The first one should be just the table name.
|
||||
* @param array $where An array of filters, to be inserted into the WHERE clause.
|
||||
@ -76,7 +77,7 @@ class SQLQuery extends Object {
|
||||
* @param array $having An array of having clauses.
|
||||
* @param string $limit A LIMIT clause.
|
||||
*/
|
||||
function __construct($select = array(), $from = array(), $where = "", $orderby = "", $groupby = "", $having = "", $limit = "") {
|
||||
function __construct($select = "*", $from = array(), $where = "", $orderby = "", $groupby = "", $having = "", $limit = "") {
|
||||
if($select) $this->select = is_array($select) ? $select : array($select);
|
||||
if($from) $this->from = is_array($from) ? $from : array(str_replace('`','',$from) => $from);
|
||||
if($where) $this->where = is_array($where) ? $where : array($where);
|
||||
@ -87,7 +88,76 @@ class SQLQuery extends Object {
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify the list of columns to be selected by the query.
|
||||
*
|
||||
* <code>
|
||||
* // pass fields to select as single parameter array
|
||||
* $query->select(array("Col1","Col2"))->from("MyTable");
|
||||
*
|
||||
* // pass fields to select as multiple parameters
|
||||
* $query->select("Col1", "Col2")->from("MyTable");
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $fields
|
||||
* @return SQLQuery
|
||||
*/
|
||||
public function select($fields) {
|
||||
if (func_num_args() > 1) {
|
||||
$this->select = func_get_args();
|
||||
} else {
|
||||
$this->select = is_array($fields) ? $fields : array($fields);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the target table to select from.
|
||||
*
|
||||
* <code>
|
||||
* $query->from("MyTable"); // SELECT * FROM MyTable
|
||||
* </code>
|
||||
*
|
||||
* @param string $table
|
||||
* @return SQLQuery
|
||||
*/
|
||||
public function from($table) {
|
||||
$this->from[] = $table;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a predicate filter to the where clause.
|
||||
*
|
||||
* Accepts a variable length of arguments, which represent
|
||||
* different ways of formatting a predicate in a where clause:
|
||||
*
|
||||
* <code>
|
||||
* // the entire predicate as a single string
|
||||
* $query->where("Column = 'Value'");
|
||||
*
|
||||
* // an exact match predicate with a key value pair
|
||||
* $query->where("Column", "Value");
|
||||
*
|
||||
* // a predicate with user defined operator
|
||||
* $query->where("Column", "!=", "Value");
|
||||
* </code>
|
||||
*
|
||||
*/
|
||||
public function where() {
|
||||
$args = func_get_args();
|
||||
if (func_num_args() == 3) {
|
||||
$filter = "{$args[0]} {$args[1]} '{$args[2]}'";
|
||||
} elseif (func_num_args() == 2) {
|
||||
$filter = "{$args[0]} = '{$args[1]}'";
|
||||
} else {
|
||||
$filter = $args[0];
|
||||
}
|
||||
$this->where[] = $filter;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the disjunctive operator 'OR' to join filter expressions in the WHERE clause.
|
||||
*/
|
||||
@ -147,19 +217,21 @@ class SQLQuery extends Object {
|
||||
* @return string
|
||||
*/
|
||||
function getFilter() {
|
||||
return implode(") {$this->connective} (" , $this->where);
|
||||
return ($this->where) ? implode(") {$this->connective} (" , $this->where) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the SQL statement for this query.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function sql() {
|
||||
if (!$this->from) return '';
|
||||
$distinct = $this->distinct ? "DISTINCT " : "";
|
||||
if($this->select) {
|
||||
if($this->delete) {
|
||||
$text = "DELETE ";
|
||||
} else if($this->select) {
|
||||
$text = "SELECT $distinct" . implode(", ", $this->select);
|
||||
} else {
|
||||
if($this->delete) $text = "DELETE ";
|
||||
}
|
||||
$text .= " FROM " . implode(" ", $this->from);
|
||||
|
||||
@ -172,6 +244,15 @@ class SQLQuery extends Object {
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the generated SQL string for this query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function __toString() {
|
||||
return $this->sql();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute this query.
|
||||
* @return Query
|
||||
|
@ -53,6 +53,9 @@ class ComplexTableField extends TableListField {
|
||||
*/
|
||||
protected $permissions = array(
|
||||
"add",
|
||||
"edit",
|
||||
"show",
|
||||
"delete",
|
||||
//"export",
|
||||
);
|
||||
|
||||
@ -213,7 +216,6 @@ JS;
|
||||
}
|
||||
|
||||
$this->sourceItems = DataObject::get($this->sourceClass, $this->sourceFilter, $sort, $this->sourceJoin, $limitClause);
|
||||
|
||||
$this->unpagedSourceItems = DataObject::get($this->sourceClass, $this->sourceFilter, $sort, $this->sourceJoin);
|
||||
|
||||
$this->totalCount = ($this->unpagedSourceItems) ? $this->unpagedSourceItems->TotalItems() : null;
|
||||
@ -436,8 +438,11 @@ JS;
|
||||
// add relational fields
|
||||
$detailFields->push(new HiddenField("ctf[parentClass]"," ",$this->getParentClass()));
|
||||
|
||||
if( $this->relationAutoSetting )
|
||||
$detailFields->push(new HiddenField("$parentIdName"," ",$childData->ID));
|
||||
if( $this->relationAutoSetting ) {
|
||||
// Hack for model admin: model admin will have included a dropdown for the relation itself
|
||||
$detailFields->removeByName($parentIdName);
|
||||
$detailFields->push(new HiddenField("$parentIdName"," ",$this->sourceID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -485,18 +490,17 @@ JS;
|
||||
*
|
||||
* @see {Form::ReferencedField}).
|
||||
*/
|
||||
function saveComplexTableField($params) {
|
||||
function saveComplexTableField($data, $form, $params) {
|
||||
$className = $this->sourceClass();
|
||||
$childData = new $className();
|
||||
|
||||
$this->saveInto($childData);
|
||||
$form->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);
|
||||
$childRequestHandler = new ComplexTableField_ItemRequest($this, $childData->ID);
|
||||
$form = $childRequestHandler->DetailForm();
|
||||
FormResponse::update_dom_id($form->FormName(), $form->formHtmlContent(), true, 'update');
|
||||
return FormResponse::respond();
|
||||
} else {
|
||||
@ -542,18 +546,20 @@ class ComplexTableField_ItemRequest extends RequestHandlingData {
|
||||
}
|
||||
|
||||
$this->methodName = "show";
|
||||
/*
|
||||
$this->sourceItems = $this->ctg->sourceItems();
|
||||
|
||||
$this->pageSize = 1;
|
||||
|
||||
if(isset($_REQUEST['ctf'][$this->Name()]['start']) && is_numeric($_REQUEST['ctf'][$this->Name()]['start'])) {
|
||||
$this->unpagedSourceItems->setPageLimits($_REQUEST['ctf'][$this->Name()]['start'], $this->pageSize, $this->totalCount);
|
||||
}
|
||||
*/
|
||||
|
||||
echo $this->renderWith($this->ctf->templatePopup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a 1-element data object set that can be used for pagination.
|
||||
*/
|
||||
/* this doesn't actually work :-(
|
||||
function Paginator() {
|
||||
$paginatingSet = new DataObjectSet(array($this->dataObj()));
|
||||
$start = isset($_REQUEST['ctf']['start']) ? $_REQUEST['ctf']['start'] : 0;
|
||||
$paginatingSet->setPageLimits($start, 1, $this->ctf->TotalCount());
|
||||
return $paginatingSet;
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Just a hook, processed in {DetailForm()}
|
||||
@ -566,25 +572,23 @@ class ComplexTableField_ItemRequest extends RequestHandlingData {
|
||||
}
|
||||
|
||||
$this->methodName = "edit";
|
||||
/*
|
||||
$this->sourceItems = $this->sourceItems();
|
||||
|
||||
$this->pageSize = 1;
|
||||
|
||||
if(is_numeric($_REQUEST['ctf']['start'])) {
|
||||
$this->unpagedSourceItems->setPageLimits($_REQUEST['ctf']['start'], $this->pageSize, $this->totalCount);
|
||||
}
|
||||
*/
|
||||
|
||||
echo $this->renderWith($this->ctf->templatePopup);
|
||||
}
|
||||
|
||||
function delete() {
|
||||
if($this->ctf->Can('delete') !== true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->dataObj()->delete();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Return the data object being manipulated
|
||||
*/
|
||||
function obj() {
|
||||
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
|
||||
@ -605,7 +609,7 @@ class ComplexTableField_ItemRequest extends RequestHandlingData {
|
||||
* @param int $childID
|
||||
*/
|
||||
function DetailForm($childID = null) {
|
||||
$childData = $this->obj();
|
||||
$childData = $this->dataObj();
|
||||
|
||||
$fields = $this->ctf->getFieldsFor($childData);
|
||||
$validator = $this->ctf->getValidatorFor($childData);
|
||||
@ -631,13 +635,13 @@ class ComplexTableField_ItemRequest extends RequestHandlingData {
|
||||
* @see {Form::ReferencedField}).
|
||||
*/
|
||||
function saveComplexTableField($data, $form, $request) {
|
||||
$form->saveInto($this->obj());
|
||||
$this->obj()->write();
|
||||
$form->saveInto($this->dataObj());
|
||||
$this->dataObj()->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 = $this->DetailForm();
|
||||
//$form->loadDataFrom($this->dataObject);
|
||||
FormResponse::update_dom_id($form->FormName(), $form->formHtmlContent(), true, 'update');
|
||||
return FormResponse::respond();
|
||||
@ -647,58 +651,52 @@ class ComplexTableField_ItemRequest extends RequestHandlingData {
|
||||
}
|
||||
}
|
||||
|
||||
function PopupBaseLink() {
|
||||
$link = $this->FormAction() . "&action_callfieldmethod&fieldName={$this->Name()}";
|
||||
if(!strpos($link,'ctf[ID]')) {
|
||||
$link = str_replace('&','&',HTTP::setGetVar('ctf[ID]',$this->sourceID(),$link));
|
||||
}
|
||||
return $link;
|
||||
}
|
||||
|
||||
function PopupCurrentItem() {
|
||||
return $_REQUEST['ctf']['start']+1;
|
||||
}
|
||||
|
||||
|
||||
function PopupFirstLink() {
|
||||
if(!is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == 0) {
|
||||
$this->ctf->LinkToItem();
|
||||
|
||||
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$item = $this->unpagedSourceItems->First();
|
||||
$start = 0;
|
||||
return Convert::raw2att($this->PopupBaseLink() . "&methodName={$_REQUEST['methodName']}&ctf[childID]={$item->ID}&ctf[start]={$start}");
|
||||
return Controller::join_links($this->Link(), "$this->methodName?ctf[start]={$start}");
|
||||
}
|
||||
|
||||
function PopupLastLink() {
|
||||
if(!is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == $this->totalCount-1) {
|
||||
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == $this->totalCount-1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$item = $this->unpagedSourceItems->Last();
|
||||
$start = $this->totalCount - 1;
|
||||
return Convert::raw2att($this->PopupBaseLink() . "&methodName={$_REQUEST['methodName']}&ctf[childID]={$item->ID}&ctf[start]={$start}");
|
||||
return Controller::join_links($this->Link(), "$this->methodName?ctf[start]={$start}");
|
||||
}
|
||||
|
||||
function PopupNextLink() {
|
||||
if(!is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == $this->totalCount-1) {
|
||||
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == $this->totalCount-1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$item = $this->unpagedSourceItems->getIterator()->getOffset($_REQUEST['ctf']['start'] + 1);
|
||||
|
||||
$start = $_REQUEST['ctf']['start'] + 1;
|
||||
return Convert::raw2att($this->PopupBaseLink() . "&methodName={$_REQUEST['methodName']}&ctf[childID]={$item->ID}&ctf[start]={$start}");
|
||||
return Controller::join_links($this->Link(), "$this->methodName?ctf[start]={$start}");
|
||||
}
|
||||
|
||||
function PopupPrevLink() {
|
||||
if(!is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == 0) {
|
||||
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$item = $this->unpagedSourceItems->getIterator()->getOffset($_REQUEST['ctf']['start'] - 1);
|
||||
|
||||
$start = $_REQUEST['ctf']['start'] - 1;
|
||||
return Convert::raw2att($this->PopupBaseLink() . "&methodName={$_REQUEST['methodName']}&ctf[childID]={$item->ID}&ctf[start]={$start}");
|
||||
return Controller::join_links($this->Link(), "$this->methodName?ctf[start]={$start}");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -722,7 +720,7 @@ class ComplexTableField_ItemRequest extends RequestHandlingData {
|
||||
for($i = $offset;$i <= $offset + $this->pageSize && $i <= $this->totalCount;$i++) {
|
||||
$start = $i - 1;
|
||||
$item = $this->unpagedSourceItems->getIterator()->getOffset($i-1);
|
||||
$links['link'] = Convert::raw2att($this->PopupBaseLink() . "&methodName={$_REQUEST['methodName']}&ctf[childID]={$item->ID}&ctf[start]={$start}");
|
||||
$links['link'] = Controller::join_links($this->Link() . "$this->methodName?ctf[start]={$start}");
|
||||
$links['number'] = $i;
|
||||
$links['active'] = $i == $currentItem ? false : true;
|
||||
$result->push(new ArrayData($links));
|
||||
@ -730,6 +728,9 @@ class ComplexTableField_ItemRequest extends RequestHandlingData {
|
||||
return $result;
|
||||
}
|
||||
|
||||
function ShowPagination() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -886,10 +887,6 @@ class ComplexTableField_Popup extends Form {
|
||||
function FieldHolder() {
|
||||
return $this->renderWith('ComplexTableField_Form');
|
||||
}
|
||||
|
||||
function ShowPagination() {
|
||||
return $this->controller->ShowPagination();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
@ -32,6 +32,7 @@ class FieldSet extends DataObjectSet {
|
||||
if($field->hasData()) {
|
||||
$name = $field->Name();
|
||||
if(isset($list[$name])) {
|
||||
$errSuffix = "";
|
||||
if($this->form) $errSuffix = " in your '{$this->form->class}' form called '" . $this->form->Name() . "'";
|
||||
else $errSuffix = '';
|
||||
user_error("collateDataFields() I noticed that a field called '$name' appears twice$errSuffix.", E_USER_ERROR);
|
||||
|
@ -813,6 +813,15 @@ class Form extends RequestHandlingData {
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a rendered version of this form, suitable for ajax post-back.
|
||||
* It triggers slightly different behaviour, such as disabling the rewriting of # links
|
||||
*/
|
||||
function forAjaxTemplate() {
|
||||
$view = new SSViewer("Form");
|
||||
return $view->dontRewriteHashlinks()->process($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an HTML rendition of this form, without the <form> tag itself.
|
||||
* Attaches 3 extra hidden files, _form_action, _form_name, _form_method, and _form_enctype. These are
|
||||
|
@ -222,7 +222,7 @@ JS
|
||||
|
||||
// sorting links (only if we have a form to refresh with)
|
||||
if($this->form) {
|
||||
$sortLink = $this->BaseLink();
|
||||
$sortLink = $this->Link();
|
||||
$sortLink = HTTP::setGetVar("ctf[{$this->Name()}][sort]", $fieldName, $sortLink);
|
||||
if(isset($_REQUEST['ctf'][$this->Name()]['dir'])) {
|
||||
$XML_sort = (isset($_REQUEST['ctf'][$this->Name()]['dir'])) ? Convert::raw2xml($_REQUEST['ctf'][$this->Name()]['dir']) : null;
|
||||
@ -644,7 +644,7 @@ JS
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->BaseLink() . "&ctf[{$this->Name()}][start]={$start}{$this->filterString()}";
|
||||
return $this->Link() . "/ajax_refresh?ctf[{$this->Name()}][start]={$start}{$this->filterString()}";
|
||||
}
|
||||
|
||||
function PrevLink() {
|
||||
@ -656,7 +656,7 @@ JS
|
||||
|
||||
$start = ($_REQUEST['ctf'][$this->Name()]['start'] - $this->pageSize < 0) ? 0 : $_REQUEST['ctf'][$this->Name()]['start'] - $this->pageSize;
|
||||
|
||||
return $this->BaseLink() . "&ctf[{$this->Name()}][start]=$start{$this->filterString()}";
|
||||
return $this->Link() . "/ajax_refresh?ctf[{$this->Name()}][start]=$start{$this->filterString()}";
|
||||
}
|
||||
|
||||
function NextLink() {
|
||||
@ -665,7 +665,7 @@ JS
|
||||
if($currentStart >= $start-1) {
|
||||
return null;
|
||||
}
|
||||
return $this->BaseLink() . "&ctf[{$this->Name()}][start]={$start}{$this->filterString()}";
|
||||
return $this->Link() . "/ajax_refresh?ctf[{$this->Name()}][start]={$start}{$this->filterString()}";
|
||||
}
|
||||
|
||||
function LastLink() {
|
||||
@ -678,7 +678,7 @@ JS
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->BaseLink() . "&ctf[{$this->Name()}][start]=$start{$this->filterString()}";
|
||||
return $this->Link() . "/ajax_refresh?ctf[{$this->Name()}][start]=$start{$this->filterString()}";
|
||||
}
|
||||
|
||||
function FirstItem() {
|
||||
@ -815,7 +815,7 @@ JS
|
||||
* We need to instanciate this button manually as a normal button has no means of adding inline onclick-behaviour.
|
||||
*/
|
||||
function ExportLink() {
|
||||
return Director::absoluteURL($this->FormAction()) . "&action_callfieldmethod&fieldName={$this->Name()}&methodName=export";
|
||||
return Controller::join_links($this->Link(), 'export');
|
||||
}
|
||||
|
||||
function printall() {
|
||||
@ -832,7 +832,7 @@ JS
|
||||
}
|
||||
|
||||
function PrintLink() {
|
||||
$link = Director::absoluteURL($this->FormAction()) . "&action_callfieldmethod&fieldName={$this->Name()}&methodName=printall";
|
||||
$link = Controller::join_links($this->Link(), 'printall');
|
||||
if(isset($_REQUEST['ctf'][$this->Name()]['sort'])) {
|
||||
$link = HTTP::setGetVar("ctf[{$this->Name()}][sort]",Convert::raw2xml($_REQUEST['ctf'][$this->Name()]['sort']), $link);
|
||||
}
|
||||
@ -913,50 +913,12 @@ JS
|
||||
function setTemplate($template) {
|
||||
$this->template = $template;
|
||||
}
|
||||
|
||||
function BaseLink() {
|
||||
return $this->FormAction() . "&action_callfieldmethod&fieldName={$this->Name()}&ctf[ID]={$this->sourceID()}&methodName=ajax_refresh&SecurityID=" . Session::get('SecurityID');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action of the surrounding form - needed to maintain context on subsequent calls.
|
||||
* It is only needed to embed this field into a form if you want to use more than "display-functionality".
|
||||
* We try to mirror the existing GET-properties to achieve the same application-state.
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
function FormAction() {
|
||||
$params = $_GET;
|
||||
|
||||
// we don't want this to be overriding our new actions
|
||||
unset($params['executeForm']);
|
||||
unset($params['fieldName']);
|
||||
unset($params['url']);
|
||||
unset($params['methodName']);
|
||||
unset($params['forcehtml']);
|
||||
// TODO Refactor
|
||||
unset($params['ctf']);
|
||||
$params['ctf'][$this->Name()]['search'] = (isset($_REQUEST['ctf'][$this->Name()]['search'])) ? $_REQUEST['ctf'][$this->Name()]['search'] : null;
|
||||
$params['SecurityID'] = Session::get('SecurityID');
|
||||
|
||||
// unset all actions (otherwise they override action_callfieldmethod)
|
||||
foreach($params as $paramKey => $paramVal) {
|
||||
if(strpos($paramKey, 'action_') === 0) {
|
||||
unset($params[$paramKey]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
$link = $this->form->FormAction();
|
||||
$link .= (!strpos($link,'?')) ? '?' : '&';
|
||||
$link .= urldecode (http_build_query($params));
|
||||
} catch(Exception $e) {
|
||||
user_error('Please embed this field into a form if you want to use actions such as "add", "edit" or "delete"', E_USER_ERROR);
|
||||
}
|
||||
return $link;
|
||||
}
|
||||
|
||||
function BaseLink() {
|
||||
user_error("TableListField::BaseLink() deprecated, use Link() instead", E_USER_NOTICE);
|
||||
return $this->Link();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Int
|
||||
*/
|
||||
@ -1069,11 +1031,15 @@ class TableListField_Item extends ViewableData {
|
||||
}
|
||||
|
||||
function BaseLink() {
|
||||
return $this->parent->FormAction() . "&action_callfieldmethod&fieldName={$this->parent->Name()}&ctf[childID]={$this->item->ID}";
|
||||
user_error("TableListField_Item::BaseLink() deprecated, use Link() instead", E_USER_NOTICE);
|
||||
return $this->Link() . '/ajax_refresh';
|
||||
}
|
||||
function Link() {
|
||||
return Controller::join_links($this->parent->Link() . "item/" . $this->item->ID);
|
||||
}
|
||||
|
||||
function DeleteLink() {
|
||||
return $this->BaseLink() . "&methodName=delete";
|
||||
return Controller::join_links($this->Link(), "delete");
|
||||
}
|
||||
|
||||
function MarkingCheckbox() {
|
||||
|
@ -92,7 +92,7 @@ ComplexTableField.prototype = {
|
||||
var table = Event.findElement(e,"table");
|
||||
if(Event.element(e).nodeName == "IMG") {
|
||||
link = Event.findElement(e,"a");
|
||||
popupLink = link.href+"&ajax=1";
|
||||
popupLink = link.href+"?ajax=1";
|
||||
} else {
|
||||
el = Event.findElement(e,"tr");
|
||||
var link = $$("a",el)[0];
|
||||
@ -112,16 +112,13 @@ ComplexTableField.prototype = {
|
||||
|
||||
GB_OpenerObj = this;
|
||||
// use same url to refresh the table after saving the popup, but use a generic rendering method
|
||||
GB_RefreshLink = popupLink;
|
||||
GB_RefreshLink = GB_RefreshLink.replace(/(methodName=)[^&]*/,"$1ajax_refresh");
|
||||
// dont include pagination index
|
||||
GB_RefreshLink = GB_RefreshLink.replace(/ctf\[start\][^&]*/,"");
|
||||
GB_RefreshLink += '&forcehtml=1';
|
||||
GB_RefreshLink = this.getAttribute('href');
|
||||
|
||||
if(this.GB_Caption) {
|
||||
var title = this.GB_Caption;
|
||||
} else {
|
||||
type = popupLink.match(/methodName=([^&]*)/);
|
||||
// Getting the title from the URL is pretty ugly, but it works for now
|
||||
type = popupLink.match(/[0-9]+\/([^\/?&]*)([?&]|$)/);
|
||||
var title = (type && type[1]) ? type[1].ucfirst() : "";
|
||||
}
|
||||
|
||||
|
@ -4,11 +4,12 @@ ComplexTableFieldPopupForm.prototype = {
|
||||
errorMessage: "Error talking to server",
|
||||
|
||||
initialize: function() {
|
||||
Behaviour.register({
|
||||
"form#ComplexTableField_Popup_DetailForm .Actions input.action": {
|
||||
onclick: this.submitForm.bind(this)
|
||||
}
|
||||
});
|
||||
var rules = {};
|
||||
rules["#" + this.id + " .Actions input.action"] = {
|
||||
'onclick' : this.submitForm.bind(this)
|
||||
};
|
||||
|
||||
Behaviour.register(rules);
|
||||
},
|
||||
|
||||
loadNewPage : function(content) {
|
||||
@ -33,7 +34,6 @@ ComplexTableFieldPopupForm.prototype = {
|
||||
submitButton.disabled = true;
|
||||
Element.addClassName(submitButton,'loading');
|
||||
}
|
||||
|
||||
new parent.parent.Ajax.Request(
|
||||
theForm.getAttribute("action"),
|
||||
{
|
||||
@ -58,7 +58,6 @@ ComplexTableFieldPopupForm.prototype = {
|
||||
|
||||
// don't update when validation is present and failed
|
||||
if(!this.validate || (this.validate && !hasHadFormError())) {
|
||||
alert("GB:" + parent.parent.GB_RefreshLink);
|
||||
new parent.parent.Ajax.Request(
|
||||
parent.parent.GB_RefreshLink,
|
||||
{
|
||||
@ -113,8 +112,9 @@ ComplexTableFieldPopupForm.prototype = {
|
||||
}
|
||||
|
||||
// causes IE6 to go nuts
|
||||
//this.GB_hide();
|
||||
this.GB_hide();
|
||||
|
||||
}
|
||||
}
|
||||
ComplexTableFieldPopupForm.applyTo('form#ComplexTableField_Popup_DetailForm');
|
||||
ComplexTableFieldPopupForm.applyTo('#ComplexTableField_Popup_DetailForm');
|
||||
ComplexTableFieldPopupForm.applyTo('#ComplexTableField_Popup_AddForm');
|
40
sake
Executable file
40
sake
Executable file
@ -0,0 +1,40 @@
|
||||
# Check for an argument
|
||||
if [ $1 = "" ]; then
|
||||
echo "Sapphire Sake
|
||||
|
||||
Usage: $0 (command-url) (params)
|
||||
Executes a Sapphire command"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Special case for "sake installsake"
|
||||
if [ "$1" = "installsake" ]; then
|
||||
echo "Installing sake to /usr/bin..."
|
||||
cp $0 /usr/bin
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Find the PHP binary
|
||||
for candidatephp in php5 php; do
|
||||
if [ -f `which $candidatephp` ]; then
|
||||
php=`which $candidatephp`
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$php" = "" ]; then
|
||||
echo "Can't find any php binary"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ -d ./sapphire ]; then
|
||||
$php ./sapphire/cli-script.php $1 $2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -f ./cli-script.php ]; then
|
||||
$php ./cli-script.php $1 $2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Can't find ./sapphire/cli-script.php or ./cli-script.php"
|
@ -91,7 +91,8 @@ class SearchContext extends Object {
|
||||
foreach($searchParams as $key => $value) {
|
||||
$filter = $this->getFilter($key);
|
||||
if ($filter) {
|
||||
$query->where[] = $filter->apply($value);
|
||||
$filter->setValue($value);
|
||||
$filter->apply($query);
|
||||
}
|
||||
}
|
||||
return $query;
|
||||
@ -108,6 +109,7 @@ class SearchContext extends Object {
|
||||
* @return DataObjectSet
|
||||
*/
|
||||
public function getResults($searchParams, $start = false, $limit = false) {
|
||||
$searchParams = array_filter($searchParams, array($this,'clearEmptySearchFields'));
|
||||
$query = $this->getQuery($searchParams, $start, $limit);
|
||||
//
|
||||
// use if a raw SQL query is needed
|
||||
@ -121,6 +123,17 @@ class SearchContext extends Object {
|
||||
return DataObject::get($this->modelClass, $query->getFilter(), "", "", $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback map function to filter fields with empty values from
|
||||
* being included in the search expression.
|
||||
*
|
||||
* @param unknown_type $value
|
||||
* @return boolean
|
||||
*/
|
||||
function clearEmptySearchFields($value) {
|
||||
return ($value != '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo documentation
|
||||
* @todo implementation
|
||||
@ -138,8 +151,15 @@ class SearchContext extends Object {
|
||||
}
|
||||
}
|
||||
$query->where = $conditions;
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for the filter attached to a named field.
|
||||
*
|
||||
* @param string $name
|
||||
* @return SearchFilter
|
||||
*/
|
||||
public function getFilter($name) {
|
||||
if (isset($this->filters[$name])) {
|
||||
return $this->filters[$name];
|
||||
@ -148,29 +168,42 @@ class SearchContext extends Object {
|
||||
}
|
||||
}
|
||||
|
||||
public function getFields() {
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
public function setFields($fields) {
|
||||
$this->fields = $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the map of filters in the current search context.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFilters() {
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
public function setFilters($filters) {
|
||||
$this->filters = $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of searchable fields in the current search context.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFields() {
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
function clearEmptySearchFields($value) {
|
||||
return ($value != '');
|
||||
/**
|
||||
* Apply a list of searchable fields to the current search context.
|
||||
*
|
||||
* @param array $fields
|
||||
*/
|
||||
public function setFields($fields) {
|
||||
$this->fields = $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Placeholder, until I figure out the rest of the SQLQuery stuff
|
||||
* and link the $searchable_fields array to the SearchContext
|
||||
*
|
||||
* @deprecated in favor of getResults
|
||||
*/
|
||||
public function getResultSet($fields) {
|
||||
$filter = "";
|
||||
|
@ -15,8 +15,8 @@ class ExactMatchFilter extends SearchFilter {
|
||||
*
|
||||
* @return unknown
|
||||
*/
|
||||
public function apply($value) {
|
||||
return "{$this->name}='$value'";
|
||||
public function apply(SQLQuery $query) {
|
||||
return $query->where("{$this->name} = '{$this->value}'");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,15 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* Matches FULLTEXT indices.
|
||||
* Filters by full-text matching on the given field.
|
||||
*
|
||||
* Full-text indexes are only available with MyISAM tables. The following column types are
|
||||
* supported:
|
||||
* - Char
|
||||
* - Varchar
|
||||
* - Text
|
||||
*
|
||||
* To enable full-text matching on fields, you also need to add an index to the
|
||||
* database table, using the {$indexes} hash in your DataObject subclass:
|
||||
*
|
||||
* <code>
|
||||
* static $indexes = array(
|
||||
* 'SearchFields' => 'fulltext(Name, Title, Description)'
|
||||
* );
|
||||
* </code>
|
||||
*
|
||||
* @package sapphire
|
||||
* @subpackage search
|
||||
*/
|
||||
class FulltextFilter extends SearchFilter {
|
||||
|
||||
public function apply($value) {
|
||||
public function apply(SQLQuery $query) {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
?>
|
@ -12,8 +12,8 @@
|
||||
*/
|
||||
class NegationFilter extends SearchFilter {
|
||||
|
||||
public function apply($value) {
|
||||
return "{$this->name} != '$value'";
|
||||
public function apply(SQLQuery $query) {
|
||||
return $query->where("{$this->name} != '{$this->value}'");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,8 +7,8 @@
|
||||
*/
|
||||
class PartialMatchFilter extends SearchFilter {
|
||||
|
||||
public function apply($value) {
|
||||
return "{$this->name} LIKE '%$value%'";
|
||||
public function apply(SQLQuery $query) {
|
||||
return $query->where("{$this->name} LIKE '%{$this->value}%'");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,12 +8,23 @@
|
||||
abstract class SearchFilter extends Object {
|
||||
|
||||
protected $name;
|
||||
protected $value;
|
||||
|
||||
function __construct($name) {
|
||||
function __construct($name, $value = false) {
|
||||
$this->name = $name;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
abstract public function apply($value);
|
||||
public function setValue($value) {
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply filter criteria to a SQL query.
|
||||
*
|
||||
* @param SQLQuery $query
|
||||
*/
|
||||
abstract public function apply(SQLQuery $query);
|
||||
|
||||
}
|
||||
?>
|
16
search/filters/SubstringFilter.php
Normal file
16
search/filters/SubstringFilter.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* Uses a substring match against content in column rows.
|
||||
*
|
||||
* @package sapphire
|
||||
* @subpackage search
|
||||
*/
|
||||
class SubstringFilter extends SearchFilter {
|
||||
|
||||
public function apply(SQLQuery $query) {
|
||||
return $query->where("LOCATE({$this->name}, $value)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -1,16 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Uses a substring match against content in column rows.
|
||||
*
|
||||
* @package sapphire
|
||||
* @subpackage search
|
||||
*/
|
||||
class SubstringMatchFilter extends SearchFilter {
|
||||
|
||||
public function apply($value) {
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -24,7 +24,7 @@ class BasicAuth extends Object {
|
||||
*/
|
||||
static function requireLogin($realm, $permissionCode) {
|
||||
if(self::$disabled) return true;
|
||||
if(!Security::database_is_ready()) return true;
|
||||
if(!Security::database_is_ready() || Director::is_cli()) return true;
|
||||
|
||||
|
||||
if(isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div id="$id" class="$CSSClasses field">
|
||||
<div id="$id" class="$CSSClasses field" href="$Link">
|
||||
<div class="middleColumn">
|
||||
<% include TableListField_PageControls %>
|
||||
<table class="data">
|
||||
|
@ -7,36 +7,34 @@
|
||||
<div class="right $PopupClasses">
|
||||
$DetailForm
|
||||
</div>
|
||||
<% if IsAddMode %>
|
||||
<% else %>
|
||||
<% if ShowPagination %>
|
||||
<table id="ComplexTableField_Pagination">
|
||||
<tr>
|
||||
<% if PopupPrevLink %>
|
||||
<td id="ComplexTableField_Pagination_Previous">
|
||||
<a href="$PopupPrevLink"><img src="cms/images/pagination/record-prev.png" /><% _t('PREVIOUS', 'Previous') %></a>
|
||||
</td>
|
||||
<% end_if %>
|
||||
<% if TotalCount == 1 %>
|
||||
<% else %>
|
||||
<td>
|
||||
<% control Pagination %>
|
||||
<% if active %>
|
||||
<a href="$link">$number</a>
|
||||
<% else %>
|
||||
<span>$number</span>
|
||||
<% end_if %>
|
||||
<% end_control %>
|
||||
</td>
|
||||
<% end_if %>
|
||||
<% if PopupNextLink %>
|
||||
<td id="ComplexTableField_Pagination_Next">
|
||||
<a href="$PopupNextLink"><% _t('NEXT', 'Next') %><img src="cms/images/pagination/record-next.png" /></a>
|
||||
</td>
|
||||
<% end_if %>
|
||||
</tr>
|
||||
</table>
|
||||
<% end_if %>
|
||||
|
||||
<% if ShowPagination %>
|
||||
<table id="ComplexTableField_Pagination">
|
||||
<tr>
|
||||
<% if Paginator.PrevLink %>
|
||||
<td id="ComplexTableField_Pagination_Previous">
|
||||
<a href="$Paginator.PrevLink"><img src="cms/images/pagination/record-prev.png" /><% _t('PREVIOUS', 'Previous') %></a>
|
||||
</td>
|
||||
<% end_if %>
|
||||
<% if xdsfdsf %>
|
||||
<% else %>
|
||||
<td>
|
||||
<% control Paginator.Pages %>
|
||||
<% if active %>
|
||||
<a href="$link">$number</a>
|
||||
<% else %>
|
||||
<span>$number</span>
|
||||
<% end_if %>
|
||||
<% end_control %>
|
||||
</td>
|
||||
<% end_if %>
|
||||
<% if Paginator.NextLink %>
|
||||
<td id="ComplexTableField_Pagination_Next">
|
||||
<a href="$Paginator.NextLink"><% _t('NEXT', 'Next') %><img src="cms/images/pagination/record-next.png" /></a>
|
||||
</td>
|
||||
<% end_if %>
|
||||
</tr>
|
||||
</table>
|
||||
<% end_if %>
|
||||
</body>
|
||||
</html>
|
||||
|
72
tests/SQLQueryTest.php
Normal file
72
tests/SQLQueryTest.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
class SQLQueryTest extends SapphireTest {
|
||||
|
||||
static $fixture_file = null;
|
||||
|
||||
function testEmptyQueryReturnsNothing() {
|
||||
$query = new SQLQuery();
|
||||
$this->assertEquals('', $query->sql());
|
||||
}
|
||||
|
||||
function testSelectFromBasicTable() {
|
||||
$query = new SQLQuery();
|
||||
$query->from[] = "MyTable";
|
||||
$this->assertEquals("SELECT * FROM MyTable", $query->sql());
|
||||
$query->from[] = "MyJoin";
|
||||
$this->assertEquals("SELECT * FROM MyTable, MyJoin", $query->sql());
|
||||
}
|
||||
|
||||
function testSelectFromUserSpecifiedFields() {
|
||||
$query = new SQLQuery();
|
||||
$query->select = array("Name", "Title", "Description");
|
||||
$query->from[] = "MyTable";
|
||||
$this->assertEquals("SELECT Name, Title, Description FROM MyTable", $query->sql());
|
||||
}
|
||||
|
||||
function testSelectWithWhereClauseFilter() {
|
||||
$query = new SQLQuery();
|
||||
$query->select = array("Name","Meta");
|
||||
$query->from[] = "MyTable";
|
||||
$query->where[] = "Name = 'Name'";
|
||||
$query->where[] = "Meta = 'Test'";
|
||||
$this->assertEquals("SELECT Name, Meta FROM MyTable WHERE (Name = 'Name') AND (Meta = 'Test')", $query->sql());
|
||||
}
|
||||
|
||||
function testSelectWithConstructorParameters() {
|
||||
$query = new SQLQuery(array("Foo", "Bar"), "FooBarTable");
|
||||
$this->assertEquals("SELECT Foo, Bar FROM FooBarTable", $query->sql());
|
||||
$query = new SQLQuery(array("Foo", "Bar"), "FooBarTable", array("Foo = 'Boo'"));
|
||||
$this->assertEquals("SELECT Foo, Bar FROM FooBarTable WHERE (Foo = 'Boo')", $query->sql());
|
||||
}
|
||||
|
||||
function testSelectWithChainedMethods() {
|
||||
$query = new SQLQuery();
|
||||
$query->select("Name","Meta")->from("MyTable")->where("Name", "Name")->where("Meta", "Test");
|
||||
$this->assertEquals("SELECT Name, Meta FROM MyTable WHERE (Name = 'Name') AND (Meta = 'Test')", $query->sql());
|
||||
}
|
||||
|
||||
function testSelectWithChainedFilterParameters() {
|
||||
$query = new SQLQuery();
|
||||
$query->select(array("Name","Meta"))->from("MyTable");
|
||||
$query->where("Name = 'Name'")->where("Meta","Test")->where("Beta", "!=", "Gamma");
|
||||
$this->assertEquals("SELECT Name, Meta FROM MyTable WHERE (Name = 'Name') AND (Meta = 'Test') AND (Beta != 'Gamma')", $query->sql());
|
||||
}
|
||||
|
||||
function testSelectWithPredicateFilters() {
|
||||
$query = new SQLQuery();
|
||||
$query->select(array("Name"))->from("MyTable");
|
||||
$match = new ExactMatchFilter("Name", "Value");
|
||||
$match->apply($query);
|
||||
$match = new PartialMatchFilter("Meta", "Value");
|
||||
$match->apply($query);
|
||||
$this->assertEquals("SELECT Name FROM MyTable WHERE (Name = 'Value') AND (Meta LIKE '%Value%')", $query->sql());
|
||||
}
|
||||
|
||||
function testSelectWithLimitClause() {
|
||||
// not implemented
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -7,15 +7,14 @@ class SearchContextTest extends SapphireTest {
|
||||
$person = singleton('SearchContextTest_Person');
|
||||
$context = $person->getDefaultSearchContext();
|
||||
|
||||
$results = $context->getResultSet(array('Name'=>''));
|
||||
$results = $context->getResults(array('Name'=>''));
|
||||
$this->assertEquals(5, $results->Count());
|
||||
|
||||
$results = $context->getResultSet(array('EyeColor'=>'green'));
|
||||
$results = $context->getResults(array('EyeColor'=>'green'));
|
||||
$this->assertEquals(2, $results->Count());
|
||||
|
||||
$results = $context->getResultSet(array('EyeColor'=>'green', 'HairColor'=>'black'));
|
||||
$results = $context->getResults(array('EyeColor'=>'green', 'HairColor'=>'black'));
|
||||
$this->assertEquals(1, $results->Count());
|
||||
|
||||
}
|
||||
|
||||
function testSummaryIncludesDefaultFieldsIfNotDefined() {
|
||||
@ -58,26 +57,26 @@ class SearchContextTest extends SapphireTest {
|
||||
}
|
||||
|
||||
function testUserDefinedFiltersAppearInSearchContext() {
|
||||
//$company = singleton('SearchContextTest_Company');
|
||||
//$context = $company->getDefaultSearchContext();
|
||||
$company = singleton('SearchContextTest_Company');
|
||||
$context = $company->getDefaultSearchContext();
|
||||
|
||||
/*$this->assertEquals(
|
||||
$this->assertEquals(
|
||||
array(
|
||||
"Name" => new PartialMatchFilter("Name"),
|
||||
"Industry" => new ExactMatchFilter("Industry"),
|
||||
"AnnualProfit" => new PartialMatchFilter("AnnualProfit")
|
||||
),
|
||||
$context->getFilters()
|
||||
);*/
|
||||
);
|
||||
}
|
||||
|
||||
function testRelationshipObjectsLinkedInSearch() {
|
||||
//$project = singleton('SearchContextTest_Project');
|
||||
//$context = $project->getDefaultSearchContext();
|
||||
$project = singleton('SearchContextTest_Project');
|
||||
$context = $project->getDefaultSearchContext();
|
||||
|
||||
//$query = array("Name"=>"Blog Website");
|
||||
$query = array("Name"=>"Blog Website");
|
||||
|
||||
//$results = $context->getQuery($query);
|
||||
$results = $context->getQuery($query);
|
||||
}
|
||||
|
||||
function testCanGenerateQueryUsingAllFilterTypes() {
|
||||
@ -194,13 +193,15 @@ class SearchContextTest_AllFilterTypes extends DataObject implements TestOnly {
|
||||
"ExactMatch" => "Text",
|
||||
"PartialMatch" => "Text",
|
||||
"Negation" => "Text",
|
||||
"HiddenValue" => "Text"
|
||||
"SubstringMatch" => "Text",
|
||||
"HiddenValue" => "Text",
|
||||
);
|
||||
|
||||
static $searchable_fields = array(
|
||||
"ExactMatch" => "ExactMatchFilter",
|
||||
"PartialMatch" => "PartialMatchFilter",
|
||||
"Negation" => "NegationFilter"
|
||||
"Negation" => "NegationFilter",
|
||||
"SubstringMatch" => "SubstringFilter"
|
||||
);
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user