diff --git a/code/ModelAdmin.php b/code/ModelAdmin.php
index 43b3b47a..dbdaa479 100644
--- a/code/ModelAdmin.php
+++ b/code/ModelAdmin.php
@@ -46,6 +46,16 @@ abstract class ModelAdmin extends LeftAndMain {
'handleItem'
);
+ /**
+ * @param string $collection_controller_class Override for controller class
+ */
+ protected static $collection_controller_class = "ModelAdmin_CollectionController";
+
+ /**
+ * @param string $collection_controller_class Override for controller class
+ */
+ protected static $record_controller_class = "ModelAdmin_RecordController";
+
/**
* Forward control to the default action handler
*/
@@ -129,7 +139,8 @@ abstract class ModelAdmin extends LeftAndMain {
* Base scaffolding method for returning a generic model instance.
*/
public function bindModelController($model, $request = null) {
- return new ModelAdmin_CollectionController($this, $model);
+ $class = $this->stat('collection_controller_class');
+ return new $class($this, $model);
}
/**
@@ -319,7 +330,8 @@ class ModelAdmin_CollectionController extends Controller {
* @return RecordController
*/
function handleID($request) {
- return new ModelAdmin_RecordController($this, $request);
+ $class = $this->parentController->stat('record_controller_class');
+ return new $class($this, $request);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/javascript/ModelAdmin.js b/javascript/ModelAdmin.js
index dd1478c1..de2f0f9d 100644
--- a/javascript/ModelAdmin.js
+++ b/javascript/ModelAdmin.js
@@ -75,9 +75,10 @@ jQuery(document).ready(function() {
/**
* attach generic action handler to all forms displayed in the #right panel
*/
- jQuery('#right .action').livequery('click', function(){
+ jQuery('#right #form_actions_right .action').livequery('click', function(){
form = jQuery('#right form');
- saveRecord(form.attr('action'), formData(form));
+ var formAction = form.attr('action') + '?' + jQuery(this).fieldSerialize();
+ saveRecord(formAction, formData(form));
return false;
});
diff --git a/tests/RestfulServerTest.php b/tests/RestfulServerTest.php
new file mode 100644
index 00000000..c8983a2f
--- /dev/null
+++ b/tests/RestfulServerTest.php
@@ -0,0 +1,216 @@
+assertEquals($response->getStatusCode(), 200);
+
+ $_SERVER['PHP_AUTH_USER'] = 'user@test.com';
+ $_SERVER['PHP_AUTH_PW'] = 'user';
+
+ // even with logged in user a GET with $api_access disabled should fail
+ $url = "/api/v1/RestfulServerTest_Page/1";
+ $response = Director::test($url, null, null, 'GET');
+ $this->assertEquals($response->getStatusCode(), 403);
+ }
+
+ /*
+ public function testAuthenticatedGET() {
+ // @todo create additional mock object with authenticated VIEW permissions
+ $url = "/api/v1/RestfulServerTest_Comment/1";
+ $response = Director::test($url, null, null, 'GET');
+ $this->assertEquals($response->getStatusCode(), 403);
+
+ $_SERVER['PHP_AUTH_USER'] = 'user@test.com';
+ $_SERVER['PHP_AUTH_PW'] = 'user';
+
+ $url = "/api/v1/RestfulServerTest_Comment/1";
+ $response = Director::test($url, null, null, 'GET');
+ $this->assertEquals($response->getStatusCode(), 200);
+ }
+ */
+
+ public function testAuthenticatedPUT() {
+ $url = "/api/v1/RestfulServerTest_Comment/1";
+ $data = array('Comment' => 'created');
+
+ $response = Director::test($url, $data, null, 'PUT');
+ $this->assertEquals($response->getStatusCode(), 403); // Permission failure
+
+ $_SERVER['PHP_AUTH_USER'] = 'editor@test.com';
+ $_SERVER['PHP_AUTH_PW'] = 'editor';
+ $response = Director::test($url, $data, null, 'PUT');
+ $this->assertEquals($response->getStatusCode(), 200); // Success
+ }
+
+ public function testPUTWithFormEncoded() {
+ $_SERVER['PHP_AUTH_USER'] = 'editor@test.com';
+ $_SERVER['PHP_AUTH_PW'] = 'editor';
+
+ $url = "/api/v1/RestfulServerTest_Comment/1";
+ $data = array('Comment' => 'updated');
+ $response = Director::test($url, $data, null, 'PUT');
+ $this->assertEquals($response->getStatusCode(), 200); // Success
+ // Assumption: XML is default output
+ $responseArr = Convert::xml2array($response->getBody());
+ $this->assertEquals($responseArr['ID'], 1);
+ $this->assertEquals($responseArr['Comment'], 'updated');
+ }
+
+ public function testPOSTWithFormEncoded() {
+ $_SERVER['PHP_AUTH_USER'] = 'editor@test.com';
+ $_SERVER['PHP_AUTH_PW'] = 'editor';
+
+ $url = "/api/v1/RestfulServerTest_Comment";
+ $data = array('Comment' => 'created');
+ $response = Director::test($url, $data, null, 'POST');
+ $this->assertEquals($response->getStatusCode(), 201); // Created
+ // Assumption: XML is default output
+ $responseArr = Convert::xml2array($response->getBody());
+ $this->assertEquals($responseArr['ID'], 2);
+ $this->assertEquals($responseArr['Comment'], 'created');
+ }
+
+ public function testPUTwithJSON() {
+ $_SERVER['PHP_AUTH_USER'] = 'editor@test.com';
+ $_SERVER['PHP_AUTH_PW'] = 'editor';
+
+ // by mimetype
+ $url = "/api/v1/RestfulServerTest_Comment/1";
+ $body = '{"Comment":"updated"}';
+ $response = Director::test($url, null, null, 'PUT', $body, array('Content-Type'=>'application/json'));
+ $this->assertEquals($response->getStatusCode(), 200); // Updated
+ $obj = Convert::json2obj($response->getBody());
+ $this->assertEquals($obj->ID, 1);
+ $this->assertEquals($obj->Comment, 'updated');
+
+ // by extension
+ $url = "/api/v1/RestfulServerTest_Comment/1.json";
+ $body = '{"Comment":"updated"}';
+ $response = Director::test($url, null, null, 'PUT', $body);
+ $this->assertEquals($response->getStatusCode(), 200); // Updated
+ $obj = Convert::json2obj($response->getBody());
+ $this->assertEquals($obj->ID, 1);
+ $this->assertEquals($obj->Comment, 'updated');
+ }
+
+ public function testPUTwithXML() {
+ $_SERVER['PHP_AUTH_USER'] = 'editor@test.com';
+ $_SERVER['PHP_AUTH_PW'] = 'editor';
+
+ // by mimetype
+ $url = "/api/v1/RestfulServerTest_Comment/1";
+ $body = 'updated';
+ $response = Director::test($url, null, null, 'PUT', $body, array('Content-Type'=>'text/xml'));
+ $this->assertEquals($response->getStatusCode(), 200); // Updated
+ $obj = Convert::xml2array($response->getBody());
+ $this->assertEquals($obj['ID'], 1);
+ $this->assertEquals($obj['Comment'], 'updated');
+
+ // by extension
+ $url = "/api/v1/RestfulServerTest_Comment/1.xml";
+ $body = 'updated';
+ $response = Director::test($url, null, null, 'PUT', $body);
+ $this->assertEquals($response->getStatusCode(), 200); // Updated
+ $obj = Convert::xml2array($response->getBody());
+ $this->assertEquals($obj['ID'], 1);
+ $this->assertEquals($obj['Comment'], 'updated');
+ }
+
+ public function testHTTPAcceptAndContentType() {
+ $url = "/api/v1/RestfulServerTest_Comment/1";
+
+ $headers = array('Accept' => 'application/json');
+ $response = Director::test($url, null, null, 'GET', null, $headers);
+ $this->assertEquals($response->getStatusCode(), 200); // Success
+ $obj = Convert::json2obj($response->getBody());
+ $this->assertEquals($obj->ID, 1);
+ $this->assertEquals($response->getHeader('Content-Type'), 'application/json');
+ }
+
+ public function testNotFound(){
+ $_SERVER['PHP_AUTH_USER'] = 'user@test.com';
+ $_SERVER['PHP_AUTH_PW'] = 'user';
+
+ $url = "/api/v1/RestfulServerTest_Comment/99";
+ $response = Director::test($url, null, null, 'GET');
+ $this->assertEquals($response->getStatusCode(), 404);
+ }
+
+ public function testMethodNotAllowed() {
+ $url = "/api/v1/RestfulServerTest_Comment/1";
+ $response = Director::test($url, null, null, 'UNKNOWNHTTPMETHOD');
+ $this->assertEquals($response->getStatusCode(), 405);
+ }
+
+ public function testUnsupportedMediaType() {
+ $url = "/api/v1/RestfulServerTest_Comment/1";
+ $data = "Comment||\/||updated"; // weird format
+ $headers = array('Content-Type' => 'text/weirdformat');
+ $response = Director::test($url, null, null, 'POST', $data, $headers);
+ $this->assertEquals($response->getStatusCode(), 415);
+ }
+
+}
+
+/**
+ * Everybody can view comments, logged in members in the "users" group can create comments,
+ * but only "editors" can edit or delete them.
+ *
+ */
+class RestfulServerTest_Comment extends DataObject implements PermissionProvider,TestOnly {
+
+ static $api_access = true;
+
+ static $db = array(
+ "Name" => "Varchar(255)",
+ "Comment" => "Text"
+ );
+
+ public function providePermissions(){
+ return array(
+ 'EDIT_Comment' => 'Edit Comment Objects',
+ 'CREATE_Comment' => 'Create Comment Objects',
+ 'DELETE_Comment' => 'Delete Comment Objects',
+ );
+ }
+
+ public function canView($member = null) {
+ return true;
+ }
+
+ public function canEdit($member = null) {
+ return Permission::checkMember($member, 'EDIT_Comment');
+ }
+
+ public function canDelete($member = null) {
+ return Permission::checkMember($member, 'DELETE_Comment');
+ }
+
+ public function canCreate($member = null) {
+ return Permission::checkMember($member, 'CREATE_Comment');
+ }
+
+}
+
+class RestfulServerTest_Page extends DataObject implements TestOnly {
+
+ static $api_access = false;
+
+ static $db = array(
+ 'Title' => 'Text',
+ 'Content' => 'HTMLText',
+ );
+}
+?>
\ No newline at end of file
diff --git a/tests/RestfulServerTest.yml b/tests/RestfulServerTest.yml
new file mode 100644
index 00000000..cdc371ad
--- /dev/null
+++ b/tests/RestfulServerTest.yml
@@ -0,0 +1,38 @@
+RestfulServerTest_Comment:
+ comment1:
+ Name: Joe
+ Comment: This is a test comment
+Member:
+ editor:
+ FirstName: Editor
+ Email: editor@test.com
+ Password: editor
+ user:
+ FirstName: User
+ Email: user@test.com
+ Password: user
+Group:
+ editorgroup:
+ Title: Editors
+ Code: editors
+ Members: =>Member.editor
+ usergroup:
+ Title: Users
+ Code: users
+ Members: =>Member.user
+Permission:
+ perm1:
+ Code: CREATE_Comment
+ Group: =>Group.usergroup
+ perm3:
+ Code: EDIT_Comment
+ Group: =>Group.editorgroup
+ perm4:
+ Code: DELETE_Comment
+ Group: =>Group.editorgroup
+ perm5:
+ Code: CREATE_Comment
+ Group: =>Group.editorgroup
+RestfulServerTest_Page:
+ page1:
+ Title: Testpage without API Access
\ No newline at end of file