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@60228 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
8fd1a33d84
commit
0a8f2a67f6
@ -162,12 +162,12 @@ class RestfulServer extends Controller {
|
||||
}
|
||||
|
||||
} else {
|
||||
$obj = $this->search($className, $this->request->getVars(), $sort, $limit);
|
||||
// show empty serialized result when no records are present
|
||||
if(!$obj) $obj = new DataObjectSet();
|
||||
if(!singleton($className)->stat('api_access')) {
|
||||
return $this->permissionFailure();
|
||||
}
|
||||
$obj = $this->search($className, $this->request->getVars(), $sort, $limit);
|
||||
// show empty serialized result when no records are present
|
||||
if(!$obj) $obj = new DataObjectSet();
|
||||
}
|
||||
|
||||
if($obj instanceof DataObjectSet) return $formatter->convertDataObjectSet($obj);
|
||||
@ -187,9 +187,13 @@ class RestfulServer extends Controller {
|
||||
* @return DataObjectSet
|
||||
*/
|
||||
protected function search($className, $params = null, $sort = null, $limit = null, $existingQuery = null) {
|
||||
$searchContext = singleton($className)->getDefaultSearchContext();
|
||||
if(singleton($className)->hasMethod('getRestfulSearchContext')) {
|
||||
$searchContext = singleton($className)->{'getRestfulSearchContext'}();
|
||||
} else {
|
||||
$searchContext = singleton($className)->getDefaultSearchContext();
|
||||
}
|
||||
$query = $searchContext->getQuery($params, $sort, $limit, $existingQuery);
|
||||
|
||||
|
||||
return singleton($className)->buildDataObjectSet($query->execute());
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,10 @@ if( preg_match( '/(test\.totallydigital\.co\.nz|dev\.totallydigital\.co\.nz\/tes
|
||||
} else {
|
||||
echo "Error: could not determine server configuration {$_SERVER['SCRIPT_FILENAME']}\n";
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
// set request method (doesn't allow POST through cli)
|
||||
$_SERVER['REQUEST_METHOD'] = "GET";
|
||||
|
||||
$baseURL = dirname( dirname( $_SERVER['SCRIPT_NAME'] ) );
|
||||
|
||||
|
@ -1081,8 +1081,9 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
if ($this->many_many()) {
|
||||
foreach($this->many_many() as $relationship => $component) {
|
||||
$relationshipFields = singleton($component)->summary_fields();
|
||||
$filterWhere = $this->getManyManyFilter($relationship, $component);
|
||||
$filterJoin = $this->getManyManyJoin($relationship, $component);
|
||||
$tableField = new ComplexTableField($this, $relationship, $component, $relationshipFields, "getCMSFields", '', '', $filterJoin);
|
||||
$tableField = new ComplexTableField($this, $relationship, $component, $relationshipFields, "getCMSFields", $filterWhere, '', $filterJoin);
|
||||
$tableField->popupClass = "ScaffoldingComplexTableField_Popup";
|
||||
$fieldSet->addFieldToTab("Root.$relationship", $tableField);
|
||||
}
|
||||
@ -1111,6 +1112,12 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
return "LEFT JOIN `$table` ON (`$componentField` = `$componentClass`.`ID` AND `$parentField` = '{$this->ID}')";
|
||||
}
|
||||
}
|
||||
|
||||
function getManyManyFilter($componentName, $baseTable) {
|
||||
list($parentClass, $componentClass, $parentField, $componentField, $table) = $this->many_many($componentName);
|
||||
|
||||
return "`$table`.`$parentField` = '{$this->ID}'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the class of a one-to-one component. If $component is null, return all of the one-to-one components and their classes.
|
||||
|
@ -135,12 +135,17 @@ class Debug {
|
||||
if(!Director::isLive()) {
|
||||
$caller = Debug::caller();
|
||||
$file = basename($caller['file']);
|
||||
echo "<p style=\"background-color: white; color: black; width: 95%; margin: 0.5em; padding: 0.3em; border: 1px #CCC solid\">\n";
|
||||
if($showHeader) echo "<b>Debug (line $caller[line] of $file):</b>\n ";
|
||||
echo Convert::raw2xml(trim($message)) . "</p>\n";
|
||||
if(Director::is_cli()) {
|
||||
if($showHeader) echo "Debug (line $caller[line] of $file):\n ";
|
||||
echo trim($message) . "\n";
|
||||
} else {
|
||||
echo "<p style=\"background-color: white; color: black; width: 95%; margin: 0.5em; padding: 0.3em; border: 1px #CCC solid\">\n";
|
||||
if($showHeader) echo "<b>Debug (line $caller[line] of $file):</b>\n ";
|
||||
echo Convert::raw2xml(trim($message)) . "</p>\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load an error handler
|
||||
*
|
||||
@ -208,20 +213,7 @@ class Debug {
|
||||
echo "<p>Line <strong>$errline</strong> in <strong>$errfile</strong></p>";
|
||||
echo '</div>';
|
||||
echo '<div class="trace"><h3>Source</h3>';
|
||||
$lines = file($errfile);
|
||||
$offset = $errline-10;
|
||||
$lines = array_slice($lines, $offset, 16);
|
||||
echo '<pre>';
|
||||
$offset++;
|
||||
foreach($lines as $line) {
|
||||
$line = htmlentities($line);
|
||||
if ($offset == $errline) {
|
||||
echo "<span>$offset</span> <span class=\"error\">$line</span>";
|
||||
} else {
|
||||
echo "<span>$offset</span> $line";
|
||||
}
|
||||
$offset++;
|
||||
}
|
||||
Debug::showLines($errfile, $errline);
|
||||
echo '</pre><h3>Trace</h3>';
|
||||
Debug::backtrace();
|
||||
echo '</div>';
|
||||
@ -229,6 +221,24 @@ class Debug {
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
static function showLines($errfile, $errline) {
|
||||
$lines = file($errfile);
|
||||
$offset = $errline-10;
|
||||
$lines = array_slice($lines, $offset, 16);
|
||||
echo '<pre>';
|
||||
$offset++;
|
||||
foreach($lines as $line) {
|
||||
$line = htmlentities($line);
|
||||
if ($offset == $errline) {
|
||||
echo "<span>$offset</span> <span class=\"error\">$line</span>";
|
||||
} else {
|
||||
echo "<span>$offset</span> $line";
|
||||
}
|
||||
$offset++;
|
||||
}
|
||||
echo '</pre>';
|
||||
}
|
||||
|
||||
static function emailError($emailAddress, $errno, $errstr, $errfile, $errline, $errcontext, $errorType = "Error") {
|
||||
if(strtolower($errorType) == 'warning') {
|
||||
|
286
dev/SapphireTestReporter.php
Normal file
286
dev/SapphireTestReporter.php
Normal file
@ -0,0 +1,286 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Gathers details about PHPUnit2 test suites as they
|
||||
* are been executed. This does not actually format any output
|
||||
* but simply gathers extended information about the overall
|
||||
* results of all suites & their tests for use elsewhere.
|
||||
*
|
||||
* Changelog:
|
||||
* 0.6 First created [David Spurr]
|
||||
* 0.7 Added fix to getTestException provided [Glen Ogilvie]
|
||||
*
|
||||
* @version 0.7 2006-03-12
|
||||
* @author David Spurr
|
||||
*/
|
||||
require_once 'PHPUnit/Framework/TestResult.php';
|
||||
require_once 'PHPUnit/Framework/TestListener.php';
|
||||
|
||||
/**#@+
|
||||
* @var int
|
||||
*/
|
||||
/**
|
||||
* Failure test status constant
|
||||
*/
|
||||
define('TEST_FAILURE', -1);
|
||||
/**
|
||||
* Error test status constant
|
||||
*/
|
||||
define('TEST_ERROR', 0);
|
||||
/**
|
||||
* Success test status constant
|
||||
*/
|
||||
define('TEST_SUCCESS', 1);
|
||||
/**
|
||||
* Incomplete test status constant
|
||||
*/
|
||||
define('TEST_INCOMPLETE', 2);
|
||||
/**#@-*/
|
||||
|
||||
class SapphireTestReporter implements PHPUnit_Framework_TestListener {
|
||||
/**
|
||||
* Holds array of suites and total number of tests run
|
||||
* @var array
|
||||
*/
|
||||
private $suiteResults;
|
||||
/**
|
||||
* Holds data of current suite that is been run
|
||||
* @var array
|
||||
*/
|
||||
private $currentSuite;
|
||||
/**
|
||||
* Holds data of current test that is been run
|
||||
* @var array
|
||||
*/
|
||||
private $currentTest;
|
||||
/**
|
||||
* Whether PEAR Benchmark_Timer is available for timing
|
||||
* @var boolean
|
||||
*/
|
||||
private $hasTimer;
|
||||
/**
|
||||
* Holds the PEAR Benchmark_Timer object
|
||||
* @var obj Benchmark_Timer
|
||||
*/
|
||||
private $timer;
|
||||
|
||||
/**
|
||||
* Constructor, checks to see availability of PEAR Benchmark_Timer and
|
||||
* sets up basic properties
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function __construct() {
|
||||
@include_once 'Benchmark/Timer.php';
|
||||
if(class_exists('Benchmark_Timer')) {
|
||||
$this->timer = new Benchmark_Timer();
|
||||
$this->hasTimer = true;
|
||||
} else {
|
||||
$this->hasTimer = false;
|
||||
}
|
||||
|
||||
$this->suiteResults = array(
|
||||
'suites' => array(), // array of suites run
|
||||
'hasTimer' => $this->hasTimer, // availability of PEAR Benchmark_Timer
|
||||
'totalTests' => 0 // total number of tests run
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the suite results
|
||||
*
|
||||
* @access public
|
||||
* @return array Suite results
|
||||
*/
|
||||
public function getSuiteResults() {
|
||||
return $this->suiteResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the container for result details of the current test suite when
|
||||
* each suite is first run
|
||||
*
|
||||
* @access public
|
||||
* @param obj PHPUnit2_Framework_TestSuite, the suite that is been run
|
||||
* @return void
|
||||
*/
|
||||
public function startTestSuite( PHPUnit_Framework_TestSuite $suite) {
|
||||
if(strlen($suite->getName())) {
|
||||
$this->currentSuite = array(
|
||||
'suite' => $suite, // the test suite
|
||||
'tests' => array(), // the tests in the suite
|
||||
'errors' => 0, // number of tests with errors
|
||||
'failures' => 0, // number of tests which failed
|
||||
'incomplete' => 0); // number of tests that were not completed correctly
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the container for result details of the current test when each
|
||||
* test is first run
|
||||
*
|
||||
* @access public
|
||||
* @param obj PHPUnit_Framework_Test, the test that is being run
|
||||
* @return void
|
||||
*/
|
||||
public function startTest(PHPUnit_Framework_Test $test) {
|
||||
if($test instanceof PHPUnit_Framework_TestCase) {
|
||||
$this->currentTest = array(
|
||||
'name' => preg_replace('(\(.*\))', '', $test->toString()), // the name of the test (without the suite name)
|
||||
'timeElapsed' => 0, // execution time of the test
|
||||
'status' => TEST_SUCCESS, // status of the test execution
|
||||
'message' => '', // user message of test result
|
||||
'exception' => NULL, // original caught exception thrown by test upon failure/error
|
||||
'uid' => md5(microtime()) // a unique ID for this test (used for identification purposes in results)
|
||||
);
|
||||
if($this->hasTimer) $this->timer->start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the failure detail to the current test and increases the failure
|
||||
* count for the current suite
|
||||
*
|
||||
* @access public
|
||||
* @param obj PHPUnit_Framework_Test, current test that is being run
|
||||
* @param obj PHPUnit_Framework_AssertationFailedError, PHPUnit error
|
||||
* @return void
|
||||
*/
|
||||
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) {
|
||||
$this->currentSuite['failures']++;
|
||||
$this->currentTest['status'] = TEST_FAILURE;
|
||||
$this->currentTest['message'] = $e->toString();
|
||||
$this->currentTest['exception'] = $this->getTestException($test, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the error detail to the current test and increases the error
|
||||
* count for the current suite
|
||||
*
|
||||
* @access public
|
||||
* @param obj PHPUnit_Framework_Test, current test that is being run
|
||||
* @param obj PHPUnit_Framework_AssertationFailedError, PHPUnit error
|
||||
* @return void
|
||||
*/
|
||||
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) {
|
||||
$this->currentSuite['errors']++;
|
||||
$this->currentTest['status'] = TEST_ERROR;
|
||||
$this->currentTest['message'] = $e->getMessage();
|
||||
$this->currentTest['exception'] = $this->getTestException($test, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the test incomplete detail to the current test and increases the incomplete
|
||||
* count for the current suite
|
||||
*
|
||||
* @access public
|
||||
* @param obj PHPUnit_Framework_Test, current test that is being run
|
||||
* @param obj PHPUnit_Framework_AssertationFailedError, PHPUnit error
|
||||
* @return void
|
||||
*/
|
||||
public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) {
|
||||
$this->currentSuite['incomplete']++;
|
||||
$this->currentTest['status'] = TEST_INCOMPLETE;
|
||||
$this->currentTest['message'] = $e->toString();
|
||||
$this->currentTest['exception'] = $this->getTestException($test, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Not used
|
||||
*
|
||||
* @param PHPUnit_Framework_Test $test
|
||||
* @param unknown_type $time
|
||||
*/
|
||||
public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) {
|
||||
// not implemented
|
||||
}
|
||||
|
||||
/**
|
||||
* Upon completion of a test, records the execution time (if available) and adds the test to
|
||||
* the tests performed in the current suite.
|
||||
*
|
||||
* @access public
|
||||
* @param obj PHPUnit_Framework_Test, current test that is being run
|
||||
* @return void
|
||||
*/
|
||||
public function endTest( PHPUnit_Framework_Test $test, $time) {
|
||||
if($this->hasTimer) {
|
||||
$this->timer->stop();
|
||||
$this->currentTest['timeElapsed'] = $this->timer->timeElapsed();
|
||||
}
|
||||
array_push($this->currentSuite['tests'], $this->currentTest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upon completion of a test suite adds the suite to the suties performed
|
||||
*
|
||||
* @acces public
|
||||
* @param obj PHPUnit_Framework_TestSuite, current suite that is being run
|
||||
* @return void
|
||||
*/
|
||||
public function endTestSuite( PHPUnit_Framework_TestSuite $suite) {
|
||||
if(strlen($suite->getName())) {
|
||||
array_push($this->suiteResults['suites'], $this->currentSuite);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trys to get the original exception thrown by the test on failure/error
|
||||
* to enable us to give a bit more detail about the failure/error
|
||||
*
|
||||
* @access private
|
||||
* @param obj PHPUnit_Framework_Test, current test that is being run
|
||||
* @param obj PHPUnit_Framework_AssertationFailedError, PHPUnit error
|
||||
* @return array
|
||||
*/
|
||||
private function getTestException(PHPUnit_Framework_Test $test, Exception $e) {
|
||||
// get the name of the testFile from the test
|
||||
$testName = ereg_replace('(.*)\((.*[^)])\)', '\\2', $test->toString());
|
||||
$trace = $e->getTrace();
|
||||
// loop through the exception trace to find the original exception
|
||||
for($i = 0; $i < count($trace); $i++) {
|
||||
|
||||
if(array_key_exists('file', $trace[$i])) {
|
||||
if(stristr($trace[$i]['file'], $testName.'.php') != false) return $trace[$i];
|
||||
}
|
||||
if(array_key_exists('file:protected', $trace[$i])) {
|
||||
if(stristr($trace[$i]['file:protected'], $testName.'.php') != false) return $trace[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display error bar if it exists
|
||||
*/
|
||||
public function writeResults() {
|
||||
$passCount = 0;
|
||||
$failCount = 0;
|
||||
$testCount = 0;
|
||||
$errorCount = 0;
|
||||
|
||||
foreach($this->suiteResults['suites'] as $suite) {
|
||||
foreach($suite['tests'] as $test) {
|
||||
$testCount++;
|
||||
($test['status'] == 1) ? $passCount++ : $failCount++;
|
||||
if ($test['status'] == -1) {
|
||||
echo "<div class=\"failure\"><span>⊗ ". $this->testNameToPhrase($test['name']) ."</span><br>";
|
||||
echo "<pre>".htmlentities($test['message'])."</pre><br>";
|
||||
echo "<code>In line {$test['exception']['line']} of {$test['exception']['file']}</code></div>";
|
||||
}
|
||||
}
|
||||
}
|
||||
$result = ($failCount > 0) ? 'fail' : 'pass';
|
||||
echo "<div class=\"$result\">";
|
||||
echo "<h2><span>$testCount</span> tests run: <span>$passCount</span> passes, <span>$failCount</span> fails, and <span>0</span> exceptions</h2>";
|
||||
echo "</div>";
|
||||
|
||||
}
|
||||
|
||||
private function testNameToPhrase($name) {
|
||||
return ucfirst(preg_replace("/([a-z])([A-Z])/", "$1 $2", $name));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -31,8 +31,8 @@ class TestRunner extends Controller {
|
||||
private static $default_reporter;
|
||||
|
||||
static $url_handlers = array(
|
||||
'' => 'index',
|
||||
'$TestCase' => 'only'
|
||||
'' => 'browse',
|
||||
'$TestCase' => 'only',
|
||||
);
|
||||
|
||||
/**
|
||||
@ -53,7 +53,7 @@ class TestRunner extends Controller {
|
||||
/**
|
||||
* Run all test classes
|
||||
*/
|
||||
function index() {
|
||||
function all() {
|
||||
if(hasPhpUnit()) {
|
||||
$tests = ClassInfo::subclassesFor('SapphireTest');
|
||||
array_shift($tests);
|
||||
@ -65,6 +65,16 @@ class TestRunner extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Browse all enabled test cases in the environment
|
||||
*/
|
||||
function browse() {
|
||||
$tests = ClassInfo::subclassesFor('SapphireTest');
|
||||
foreach ($tests as $test) {
|
||||
echo "<h3><a href=\"$test\">$test</a></h3>";
|
||||
}
|
||||
}
|
||||
|
||||
function coverage() {
|
||||
if(hasPhpUnit()) {
|
||||
ManifestBuilder::includeEverything();
|
||||
@ -86,7 +96,7 @@ class TestRunner extends Controller {
|
||||
if(class_exists($className)) {
|
||||
$this->runTests(array($className));
|
||||
} else {
|
||||
echo "Class '$className' not found";
|
||||
if ($className == 'all') $this->all();
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,11 +104,15 @@ class TestRunner extends Controller {
|
||||
if(!Director::is_cli()) {
|
||||
self::$default_reporter->writeHeader();
|
||||
echo '<div class="info">';
|
||||
echo "<h1>Sapphire PHPUnit Test Runner</h1>";
|
||||
echo "<p>Using the following subclasses of SapphireTest for testing: " . implode(", ", $classList) . "</p>";
|
||||
if (count($classList) > 1) {
|
||||
echo "<h1>Sapphire Tests</h1>";
|
||||
echo "<p>Running test cases: " . implode(", ", $classList) . "</p>";
|
||||
} else {
|
||||
echo "<h1>{$classList[0]}</h1>";
|
||||
echo "<p>Running test case:</p>";
|
||||
}
|
||||
echo "</div>";
|
||||
echo '<div class="trace">';
|
||||
echo "<pre>";
|
||||
} else {
|
||||
echo "Sapphire PHPUnit Test Runner\n";
|
||||
echo "Using the following subclasses of SapphireTest for testing: " . implode(", ", $classList) . "\n\n";
|
||||
@ -114,18 +128,23 @@ class TestRunner extends Controller {
|
||||
$suite->addTest(new PHPUnit_Framework_TestSuite($className));
|
||||
}
|
||||
|
||||
$reporter = new SapphireTestReporter();
|
||||
$results = new PHPUnit_Framework_TestResult();
|
||||
$results->addListener($reporter);
|
||||
|
||||
/*, array("reportDirectory" => "/Users/sminnee/phpunit-report")*/
|
||||
if($coverage) {
|
||||
$testResult = PHPUnit_TextUI_TestRunner::run($suite, array("reportDirectory" => "../assets/coverage-report"));
|
||||
} else {
|
||||
$testResult = PHPUnit_TextUI_TestRunner::run($suite);
|
||||
}
|
||||
|
||||
if($coverage) {
|
||||
$suite->run($results);
|
||||
//$testResult = PHPUnit_TextUI_TestRunner::run($suite, array("reportDirectory" => "../assets/coverage-report"));
|
||||
$coverageURL = Director::absoluteURL('assets/coverage-report');
|
||||
echo "<p><a href=\"$coverageURL\">Coverage report available here</a></p>";
|
||||
} else {
|
||||
$suite->run($results);
|
||||
//$testResult = PHPUnit_TextUI_TestRunner::run($suite);
|
||||
}
|
||||
|
||||
$reporter->writeResults();
|
||||
|
||||
if(!Director::is_cli()) echo '</div>';
|
||||
|
||||
// Put the error handlers back
|
||||
|
@ -894,6 +894,10 @@ class ComplexTableField_Popup extends Form {
|
||||
class ScaffoldingComplexTableField_Popup extends Form {
|
||||
protected $sourceClass;
|
||||
protected $dataObject;
|
||||
|
||||
public static $allowed_actions = array(
|
||||
'filter', 'record', 'httpSubmission', 'handleAction'
|
||||
);
|
||||
|
||||
function __construct($controller, $name, $fields, $validator, $readonly, $dataObject) {
|
||||
$this->dataObject = $dataObject;
|
||||
@ -945,12 +949,76 @@ class ScaffoldingComplexTableField_Popup extends Form {
|
||||
$saveAction->addExtraClass('save');
|
||||
}
|
||||
|
||||
$fields->push(new HiddenField("ComplexTableField_Path", Director::absoluteBaseURL()));
|
||||
|
||||
parent::__construct($controller, $name, $fields, $actions, $validator);
|
||||
}
|
||||
|
||||
function FieldHolder() {
|
||||
return $this->renderWith('ComplexTableField_Form');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle a generic action passed in by the URL mapping.
|
||||
*
|
||||
* @param HTTPRequest $request
|
||||
*/
|
||||
public function handleAction($request) {
|
||||
$action = str_replace("-","_",$request->param('Action'));
|
||||
if(!$this->action) $this->action = 'index';
|
||||
|
||||
if($this->checkAccessAction($action)) {
|
||||
if($this->hasMethod($action)) {
|
||||
$result = $this->$action($request);
|
||||
|
||||
// Method returns an array, that is used to customise the object before rendering with a template
|
||||
if(is_array($result)) {
|
||||
return $this->getViewer($action)->process($this->customise($result));
|
||||
|
||||
// Method returns a string / object, in which case we just return that
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// There is no method, in which case we just render this object using a (possibly alternate) template
|
||||
} else {
|
||||
return $this->getViewer($action)->process($this);
|
||||
}
|
||||
} else {
|
||||
return $this->httpError(403, "Action '$action' isn't allowed on class $this->class");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to render results for an autocomplete filter.
|
||||
*
|
||||
* @param HTTPRequest $request
|
||||
* @return void
|
||||
*/
|
||||
function filter($request) {
|
||||
//$model = singleton($this->modelClass);
|
||||
$context = $this->dataObject->getDefaultSearchContext();
|
||||
$value = $request->getVar('q');
|
||||
$results = $context->getResults(array("Name"=>$value));
|
||||
header("Content-Type: text/plain");
|
||||
foreach($results as $result) {
|
||||
echo $result->Name . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to populate edit box with a single data object via Ajax query
|
||||
*/
|
||||
function record($request) {
|
||||
$type = $request->getVar('type');
|
||||
$value = $request->getVar('value');
|
||||
if ($type && $value) {
|
||||
$record = DataObject::get_one($this->dataObject->class, "$type = '$value'");
|
||||
header("Content-Type: text/plain");
|
||||
echo json_encode(array("record"=>$record->toMap()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -118,6 +118,7 @@ class Form extends RequestHandlingData {
|
||||
|
||||
static $url_handlers = array(
|
||||
'field/$FieldName!' => 'handleField',
|
||||
'$Action!' => 'handleAction',
|
||||
'POST ' => 'httpSubmission',
|
||||
'GET ' => 'httpSubmission',
|
||||
);
|
||||
|
@ -1,5 +1,24 @@
|
||||
window.onload = function() {
|
||||
|
||||
resourcePath = jQuery('form').attr('action');
|
||||
|
||||
jQuery("fieldset input:first").attr('autocomplete', 'off').autocomplete({list: ["mark rickerby", "maxwell sparks"]});
|
||||
|
||||
jQuery("fieldset input:first").bind('activate.autocomplete', function(e){
|
||||
|
||||
type = jQuery("fieldset input:first").attr('name');
|
||||
value = jQuery("fieldset input:first").val();
|
||||
|
||||
jQuery.getJSON(resourcePath + '/record', {'type':type, 'value':value}, function(data) {
|
||||
jQuery('form input').each(function(i, elm){
|
||||
if(elm.name in data.record) {
|
||||
val = data.record[elm.name];
|
||||
if (val != null) elm.setAttribute('value', val);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
};
|
2
sake
2
sake
@ -15,7 +15,7 @@ if [ "$1" = "installsake" ]; then
|
||||
fi
|
||||
|
||||
# Find the PHP binary
|
||||
for candidatephp in php5 php; do
|
||||
for candidatephp in "php5 php"; do
|
||||
if [ -f `which $candidatephp` ]; then
|
||||
php=`which $candidatephp`
|
||||
break
|
||||
|
@ -125,11 +125,10 @@ class SearchContext extends Object {
|
||||
|
||||
if($existingQuery) {
|
||||
$query = $existingQuery;
|
||||
$query->select = array_merge($query->select,$fields);
|
||||
} else {
|
||||
$query = $model->buildSQL();
|
||||
$query->select($fields);
|
||||
}
|
||||
$query->select = array_merge($query->select,$fields);
|
||||
|
||||
$SQL_limit = Convert::raw2sql($limit);
|
||||
$query->limit($SQL_limit);
|
||||
|
29
search/filters/CollectionFilter.php
Normal file
29
search/filters/CollectionFilter.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* Checks if a value is in a given set.
|
||||
* SQL syntax used: Column IN ('val1','val2')
|
||||
*
|
||||
* @todo Add negation (NOT IN)6
|
||||
*
|
||||
* @author Silverstripe Ltd., Ingo Schommer (<firstname>@silverstripe.com)
|
||||
*/
|
||||
class CollectionFilter extends SearchFilter {
|
||||
|
||||
public function apply(SQLQuery $query) {
|
||||
$query = $this->applyRelation($query);
|
||||
$values = explode(',',$this->value);
|
||||
if(!$values) return false;
|
||||
|
||||
for($i=0; $i<count($values); $i++) {
|
||||
if(!is_numeric($values[$i])) {
|
||||
// @todo Fix string replacement to only replace leading and tailing quotes
|
||||
$values[$i] = str_replace("'", '', $values[$i]);
|
||||
$values[$i] = Convert::raw2sql($values[$i]);
|
||||
}
|
||||
}
|
||||
$SQL_valueStr = "'" . implode("','", $values) . "'";
|
||||
return $query->where("{$this->getName()} IN ({$SQL_valueStr})");
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
@ -99,7 +99,8 @@ class SearchContextTest extends SapphireTest {
|
||||
$params = array(
|
||||
"ExactMatch" => "Match Me Exactly",
|
||||
"PartialMatch" => "partially",
|
||||
"Negation" => "undisclosed"
|
||||
"Negation" => "undisclosed",
|
||||
"CollectionMatch" => "ExistingCollectionValue,NonExistingCollectionValue,4,Inline'Quotes'",
|
||||
);
|
||||
|
||||
$results = $context->getResults($params);
|
||||
@ -209,13 +210,15 @@ class SearchContextTest_AllFilterTypes extends DataObject implements TestOnly {
|
||||
"Negation" => "Text",
|
||||
"SubstringMatch" => "Text",
|
||||
"HiddenValue" => "Text",
|
||||
"CollectionMatch" => "Text",
|
||||
);
|
||||
|
||||
static $searchable_fields = array(
|
||||
"ExactMatch" => "ExactMatchFilter",
|
||||
"PartialMatch" => "PartialMatchFilter",
|
||||
"Negation" => "NegationFilter",
|
||||
"SubstringMatch" => "SubstringFilter"
|
||||
"SubstringMatch" => "SubstringFilter",
|
||||
"CollectionMatch" => "CollectionFilter",
|
||||
);
|
||||
|
||||
}
|
||||
|
@ -61,4 +61,5 @@ SearchContextTest_AllFilterTypes:
|
||||
ExactMatch: Match me exactly
|
||||
PartialMatch: Match me partially
|
||||
Negation: Shouldnt match me
|
||||
HiddenValue: Filtered value
|
||||
HiddenValue: Filtered value
|
||||
CollectionMatch: ExistingCollectionValue
|
Loading…
Reference in New Issue
Block a user