(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@60330 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2008-08-11 03:03:52 +00:00
parent 395acde8d7
commit b6abd40783
17 changed files with 761 additions and 40 deletions

View File

@ -28,6 +28,7 @@ Director::addRules(10, array(
'' => 'RootURLController',
'sitemap.xml' => 'GoogleSitemap',
'api/v1' => 'RestfulServer',
'soap/v1' => 'SOAPModelAccess',
'dev' => 'DevelopmentAdmin'
));

247
api/SOAPModelAccess.php Executable file
View File

@ -0,0 +1,247 @@
<?
/**
* Basic SOAP Server to access and modify DataObject instances.
* You can enable SOAP access on a DataObject by setting {@link DataObject::$api_access} to true.
*
* Usage - Getting a record:
* <code>
* $c = new SoapClient('http://mysite.com/soap/v1/wsdl');
* echo $c->getXML("MyClassName", 99); // gets record #99 as xml
* </code>
*
* Usage - Updating a record:
* <code>
* $c = new SoapClient('http://mysite.com/soap/v1/wsdl');
* $data = array('MyProperty' => 'MyUpdatedValue');
* echo $c->putXML("MyClassName", 99, null, $data);
* </code>
*
* Usage - Creating a record:
* <code>
* $c = new SoapClient('http://mysite.com/soap/v1/wsdl');
* $data = array('MyProperty' => 'MyValue');
* echo $c->putXML("MyClassName", null, null, $data);
* </code>
*
* Usage - Creating a record:
* <code>
* $c = new SoapClient('http://mysite.com/soap/v1/wsdl');
* echo $c->deleteXML("MyClassName");
* </code>
*
* @todo Test relation methods
*
* @package sapphire
* @subpackage api
*/
class SOAPModelAccess extends SapphireSoapServer {
public static $methods = array(
'getXML' => array(
'class' => 'string',
'id' => 'int',
'relation' => 'string',
'_returns' => 'string',
),
'getJSON' => array(
'class' => 'string',
'id' => 'int',
'relation' => 'string',
'_returns' => 'string',
),
'putXML' => array(
'class' => 'string',
'id' => 'int',
'relation' => 'string',
'data' => 'string',
'username' => 'string',
'password' => 'string',
'_returns' => 'boolean',
),
'putJSON' => array(
'class' => 'string',
'id' => 'int',
'relation' => 'string',
'_returns' => 'boolean',
),
);
function Link() {
return "soap/v1/";
}
/**
* Used to emulate RESTful GET requests with XML data.
*
* @param string $class
* @param Number $id
* @param string $relation Relation name
* @return string
*/
function getXML($class, $id, $relation = false, $username = null, $password = null) {
$this->authenticate($username, $password);
$response = Director::test(
$this->buildRestfulURL($class, $id, $relation, 'xml'),
null,
null,
'GET'
);
return ($response->isError()) ? $this->getErrorMessage($response) : $response->getBody();
}
/**
* Used to emulate RESTful GET requests with JSON data.
*
* @param string $class
* @param Number $id
* @param string $relation Relation name
* @param string $username
* @param string $password
* @return string
*/
function getJSON($class, $id, $relation = false, $username = null, $password = null) {
$this->authenticate($username, $password);
$response = Director::test(
$this->buildRestfulURL($class, $id, $relation, 'json'),
null,
null,
'GET'
);
return ($response->isError()) ? $this->getErrorMessage($response) : $response->getBody();
}
/**
* Used to emulate RESTful POST and PUT requests with XML data.
*
* @param string $class
* @param Number $id
* @param string $relation Relation name
* @param array $data
* @param string $username
* @param string $password
* @return string
*/
function putXML($class, $id = false, $relation = false, $data, $username = null, $password = null) {
$this->authenticate($username, $password);
$response = Director::test(
$this->buildRestfulURL($class, $id, $relation, 'xml'),
$data,
null,
($id) ? 'PUT' : 'POST'
);
return ($response->isError()) ? $this->getErrorMessage($response) : $response->getBody();
}
/**
* Used to emulate RESTful POST and PUT requests with JSON data.
*
* @param string $class
* @param Number $id
* @param string $relation Relation name
* @param array $data
* @param string $username
* @param string $password
* @return string
*/
function putJSON($class = false, $id = false, $relation = false, $data, $username = null, $password = null) {
$this->authenticate($username, $password);
$response = Director::test(
$this->buildRestfulURL($class, $id, $relation, 'json'),
$data,
null,
($id) ? 'PUT' : 'POST'
);
return ($response->isError()) ? $this->getErrorMessage($response) : $response->getBody();
}
/**
* Used to emulate RESTful DELETE requests.
*
* @param string $class
* @param Number $id
* @param string $relation Relation name
* @param string $username
* @param string $password
* @return string
*/
function deleteXML($class, $id, $relation = false, $username = null, $password = null) {
$this->authenticate($username, $password);
$response = Director::test(
$this->buildRestfulURL($class, $id, $relation, 'xml'),
null,
null,
'DELETE'
);
return ($response->isError()) ? $this->getErrorMessage($response) : $response->getBody();
}
/**
* Used to emulate RESTful DELETE requests.
*
* @param string $class
* @param Number $id
* @param string $relation Relation name
* @param string $username
* @param string $password
* @return string
*/
function deleteJSON($class, $id, $relation = false, $username = null, $password = null) {
$this->authenticate($username, $password);
$response = Director::test(
$this->buildRestfulURL($class, $id, $relation, 'json'),
null,
null,
'DELETE'
);
return ($response->isError()) ? $this->getErrorMessage($response) : $response->getBody();
}
/**
* Faking an HTTP Basicauth login in the PHP environment
* that RestfulServer can pick up.
*
* @param string $username Username
* @param string $password Plaintext password
*/
protected function authenticate($username, $password) {
$_SERVER['PHP_AUTH_USER'] = $username;
$_SERVER['PHP_AUTH_PW'] = $password;
}
/**
* @param string $class
* @param Number $id
* @param string $relation
* @param string $extension
* @return string
*/
protected function buildRestfulURL($class, $id, $relation, $extension) {
$url = "api/v1/{$class}";
if($id) $url .= "/{$id}";
if($relation) $url .= "/{$relation}";
if($extension) $url .= "/.{$extension}";
return $url;
}
/**
* @param HTTPResponse $response
* @return string XML string containing the HTTP error message
*/
protected function getErrorMessage($response) {
return "<error type=\"authentication\" code=\"" . $response->getStatusCode() . "\">" . $response->getStatusDescription() . "</error>";
}
}
?>

View File

@ -9,6 +9,7 @@ class SapphireSoapServer extends Controller {
static $methods = array();
static $xsd_types = array(
'int' => 'xsd:int',
'boolean' => 'xsd:boolean',
'string' => 'xsd:string',
'binary' => 'xsd:base64Binary',
);
@ -20,7 +21,7 @@ class SapphireSoapServer extends Controller {
}
function getWSDLURL() {
return Director::absoluteBaseURLWithAuth() . $this->class . "/wsdl";
return Director::absoluteBaseURLWithAuth() . $this->Link() . "wsdl";
}
function Methods() {

46
api/SapphireSoapServer_wsdl.ss Executable file
View File

@ -0,0 +1,46 @@
<?xml version="1.0"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="{$ServiceURL}wsdl"
targetNamespace="{$ServiceURL}wsdl">
<% control Methods %>
<message name="{$Name}Request" targetNamespace="$CurrentPage.TargetNamespace">
<% control Arguments %>
<part name="$Name" type="$Type"/>
<% end_control %>
</message>
<message name="{$Name}Response" targetNamespace="$CurrentPage.TargetNamespace">
<part name="{$Name}Return" type="$ReturnType" />
</message>
<% end_control %>
<portType name="SapphireSOAP_methodsPortType">
<% control Methods %>
<operation name="$Name">
<input message="tns:{$Name}Request"/>
<output message="tns:{$Name}Response"/>
</operation>
<% end_control %>
</portType>
<binding name="SapphireSOAP_methodsBinding" type="tns:SapphireSOAP_methodsPortType">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<% control Methods %>
<operation name="$Name">
<soap:operation soapAction="$CurrentPage.ServiceURL?method=$Name" style="rpc"/>
<input>
<soap:body use="encoded" namespace="$CurrentPage.TargetNamespace" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output>
<soap:body use="encoded" namespace="$CurrentPage.TargetNamespace" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</operation>
<% end_control %>
</binding>
<service name="SapphireSOAP_methods">
<port name="SapphireSOAP_methodsPort" binding="tns:SapphireSOAP_methodsBinding">
<soap:address location="$CurrentPage.ServiceURL" />
</port>
</service>
</definitions>

View File

@ -164,6 +164,8 @@ class Director {
/**
* Handle an HTTP request, defined with a HTTPRequest object.
*
* @return HTTPResponse|string
*/
protected static function handleRequest(HTTPRequest $request, Session $session) {
krsort(Director::$rules);
@ -452,10 +454,12 @@ class Director {
* Returns the Absolute URL of the site root, embedding the current basic-auth credentials into the URL.
*/
static function absoluteBaseURLWithAuth() {
if($_SERVER['PHP_AUTH_USER'])
$login = "$_SERVER[PHP_AUTH_USER]:$_SERVER[PHP_AUTH_PW]@";
if($_SERVER['SSL']) $s = "s";
$s = "";
$login = "";
if(isset($_SERVER['PHP_AUTH_USER'])) $login = "$_SERVER[PHP_AUTH_USER]:$_SERVER[PHP_AUTH_PW]@";
if(isset($_SERVER['SSL']) && $_SERVER['SSL'] != 'Off') $s = "s";
return "http$s://" . $login . $_SERVER['HTTP_HOST'] . Director::baseURL();
}

View File

@ -79,6 +79,17 @@ class HTTPResponse extends Object {
function getStatusCode() {
return $this->statusCode;
}
/**
* @return string Description for a HTTP status code
*/
function getStatusDescription() {
if(isset(self::$status_codes[$this->statusCode])) {
return self::$status_codes[$this->statusCode];
} else {
return false;
}
}
/**
* Returns true if this HTTP response is in error
@ -148,7 +159,7 @@ class HTTPResponse extends Object {
function output() {
if(in_array($this->statusCode, self::$redirect_codes) && headers_sent($file, $line)) {
$url = $this->headers['Location'];
echo
echo
"<p>Redirecting to <a href=\"$url\" title=\"Please click this link if your browser does not redirect you\">$url... (output started on $file, line $line)</a></p>
<meta http-equiv=\"refresh\" content=\"1; url=$url\" />
<script type=\"text/javascript\">setTimeout('window.location.href = \"$url\"', 50);</script>";

View File

@ -68,6 +68,7 @@ class RequestHandlingData extends ViewableData {
* @param $params The parameters taken from the parsed URL of the parent url handler
* @param $request The {@link HTTPRequest} object that is reponsible for distributing URL parsing
* @uses HTTPRequest
* @return HTTPResponse|RequestHandlingData|string|array
*/
function handleRequest($request) {
$this->request = $request;

View File

@ -157,36 +157,31 @@ class ComponentSet extends DataObjectSet {
function setByIDList($idList) {
$has = array();
// Index current data
if($this->items) {
foreach( $this->items as $item )
$has[$item->ID] = true;
if($this->items) foreach($this->items as $item) {
$has[$item->ID] = true;
}
// Keep track of items to delete
if( isset( $has ) )
$itemsToDelete = $has;
$itemsToDelete = $has;
if($idList){
foreach($idList as $id) {
if( isset( $itemsToDelete ) )
$itemsToDelete[$id] = false;
if( ! isset( $has ) || ( $id && ! isset( $has[$id] ) ) )
$this->add($id);
}
// add items in the list
// $id is the database ID of the record
if($idList) foreach($idList as $id) {
$itemsToDelete[$id] = false;
if($id && !isset($has[$id])) $this->add($id);
}
// Delete any unmentionedItems
if( isset( $itemsToDelete ) ) {
foreach($itemsToDelete as $id => $actuallyDelete) {
if($actuallyDelete) $removeList[] = $id;
}
if( isset( $removeList ) )
$this->removeMany($removeList);
// delete items not in the list
$removeList = array();
foreach($itemsToDelete as $id => $actuallyDelete) {
if($actuallyDelete) $removeList[] = $id;
}
$this->removeMany($removeList);
}
/**
* Remove an item from this set.
*
* @param DataObject|string|int $item Item to remove, either as a DataObject or as the ID.
*/
function remove($item) {
@ -226,20 +221,19 @@ class ComponentSet extends DataObjectSet {
/**
* Remove many items from this set.
* @param array $itemList The items to remove.
Ü
* @param array $itemList The items to remove, as a numerical array with IDs or as a DataObjectSet
*/
function removeMany($itemList) {
if(!count($itemList)) return false;
if($this->type == '1-to-many') {
if(isset($itemList)) {
foreach($itemList as $item) $this->remove($item);
}
foreach($itemList as $item) $this->remove($item);
} else {
if(isset($itemList)) {
$itemCSV = implode(", ", $itemList);
$parentField = $this->ownerClass . 'ID';
$childField = ($this->childClass == $this->ownerClass) ? "ChildID" : ($this->childClass . 'ID');
DB::query("DELETE FROM `$this->tableName` WHERE $parentField = {$this->ownerObj->ID} AND $childField IN ($itemCSV)");
}
$itemCSV = implode(", ", $itemList);
$parentField = $this->ownerClass . 'ID';
$childField = ($this->childClass == $this->ownerClass) ? "ChildID" : ($this->childClass . 'ID');
DB::query("DELETE FROM `$this->tableName` WHERE $parentField = {$this->ownerObj->ID} AND $childField IN ($itemCSV)");
}
}

View File

@ -1803,7 +1803,7 @@ class DataObject extends ViewableData implements DataObjectInterface {
$object = $component->dbObject($fieldName);
if (!($object instanceof DBField)) {
if (!($object instanceof DBField) && !($object instanceof ComponentSet)) {
user_error("Unable to traverse to related object field [$fieldPath] on [$this->class]", E_USER_ERROR);
}
return $object;
@ -2057,7 +2057,7 @@ class DataObject extends ViewableData implements DataObjectInterface {
}
/**
* Flush the cached results for get_one()
* Flush the cached results for all relations (has_one, has_many, many_many)
*/
public function flushCache() {
if($this->class == 'DataObject') {
@ -2073,6 +2073,8 @@ class DataObject extends ViewableData implements DataObjectInterface {
// if(DataObject::$cache_get_one[$class]) foreach(DataObject::$cache_get_one[$class] as $obj) if($obj) $obj->destroy();
DataObject::$cache_get_one[$class] = null;
}
$this->componentCache = array();
}
/**
@ -2450,6 +2452,10 @@ class DataObject extends ViewableData implements DataObjectInterface {
$filters = array();
foreach($this->searchableFields() as $name => $spec) {
$filterClass = $spec['filter'];
// if $filterClass is not set a name of any subclass of SearchFilter than assing 'PartiailMatchFilter' to it
if (!is_subclass_of($filterClass, 'SearchFilter')) {
$filterClass = 'PartialMatchFilter';
}
$filters[$name] = new $filterClass($name);
}
return $filters;

View File

@ -141,7 +141,7 @@ class SQLQuery extends Object {
* @return SQLQuery This instance
*/
public function leftJoin($table, $onPredicate) {
$this->from[$table] = "LEFT JOIN $table ON $onPredicate";
$this->from[$table] = "LEFT JOIN `$table` ON $onPredicate";
return $this;
}

View File

@ -119,7 +119,11 @@ class CheckboxSetField extends OptionsetField {
$fieldname = $this->name ;
if($fieldname && $record && ($record->has_many($fieldname) || $record->many_many($fieldname))) {
$record->$fieldname()->setByIDList($this->value);
$idList = array();
foreach($this->value as $id => $bool) {
if($bool) $idList[] = $id;
}
$record->$fieldname()->setByIDList($idList);
} elseif($fieldname && $record) {
if($this->value) {
$this->value = str_replace(",", "{comma}", $this->value);
@ -130,6 +134,20 @@ class CheckboxSetField extends OptionsetField {
}
}
/**
* Return the CheckboxSetField value, as an array of the selected item keys
*/
function dataValue() {
if($this->value){
// Filter items to those who aren't 0
$filtered = array();
foreach($this->value as $item) if($item) $filtered[] = str_replace(",", "{comma}", $item);
return implode(",", $filtered);
} else {
return '';
}
}
/**
* Makes a pretty readonly field
*/

View File

@ -154,6 +154,94 @@ class DataObjectTest extends SapphireTest {
$this->assertTrue($comment->ParentID == $page->ID);
}
}
/**
* @todo Test removeMany() and addMany() on $many_many relationships
*/
function testManyManyRelationships() {
$player1 = $this->fixture->objFromFixture('DataObjectTest_Player', 'player1');
$player2 = $this->fixture->objFromFixture('DataObjectTest_Player', 'player2');
$team1 = $this->fixture->objFromFixture('DataObjectTest_Team', 'team1');
$team2 = $this->fixture->objFromFixture('DataObjectTest_Team', 'team2');
// Test adding single DataObject by reference
$player1->Teams()->add($team1);
$player1->flushCache();
$compareTeams = new ComponentSet($team1);
$this->assertEquals(
$player1->Teams()->column('ID'),
$compareTeams->column('ID'),
"Adding single record as DataObject to many_many"
);
// test removing single DataObject by reference
$player1->Teams()->remove($team1);
$player1->flushCache();
$compareTeams = new ComponentSet();
$this->assertEquals(
$player1->Teams()->column('ID'),
$compareTeams->column('ID'),
"Removing single record as DataObject from many_many"
);
// test adding single DataObject by ID
$player1->Teams()->add($team1->ID);
$player1->flushCache();
$compareTeams = new ComponentSet($team1);
$this->assertEquals(
$player1->Teams()->column('ID'),
$compareTeams->column('ID'),
"Adding single record as ID to many_many"
);
// test removing single DataObject by ID
$player1->Teams()->remove($team1->ID);
$player1->flushCache();
$compareTeams = new ComponentSet();
$this->assertEquals(
$player1->Teams()->column('ID'),
$compareTeams->column('ID'),
"Removing single record as ID from many_many"
);
}
/**
* @todo Extend type change tests (e.g. '0'==NULL)
*/
function testChangedFields() {
$page = $this->fixture->objFromFixture('Page', 'home');
$page->Title = 'Home-Changed';
$page->ShowInMenus = true;
$this->assertEquals(
$page->getChangedFields(false, 1),
array(
'Title' => array(
'before' => 'Home',
'after' => 'Home-Changed',
'level' => 2
),
'ShowInMenus' => array(
'before' => 1,
'after' => true,
'level' => 1
)
),
'Changed fields are correctly detected with strict type changes (level=1)'
);
$this->assertEquals(
$page->getChangedFields(false, 2),
array(
'Title' => array(
'before'=>'Home',
'after'=>'Home-Changed',
'level' => 2
)
),
'Changed fields are correctly detected while ignoring type changes (level=2)'
);
}
}
?>

View File

@ -23,3 +23,15 @@ Page:
page2:
Title: Second Page
DataObjectTest_Team:
team1:
Title: Team 1
team2:
Title: Team 2
DataObjectTest_Player:
player1:
FirstName: Player 1
player2:
FirstName: Player 2
Teams: =>DataObjectTest_Team.team1,=>DataObjectTest_Team.team2

96
tests/GroupTest.php Normal file
View File

@ -0,0 +1,96 @@
<?php
/**
* @package sapphire
* @subpackage testing
*/
class GroupTest extends FunctionalTest {
static $fixture_file = 'sapphire/tests/GroupTest.yml';
function testMemberGroupRelationForm() {
$adminGroup = $this->fixture->objFromFixture('Group', 'admingroup');
$parentGroup = $this->fixture->objFromFixture('Group', 'parentgroup');
$childGroup = $this->fixture->objFromFixture('Group', 'childgroup');
// Test single group relation through checkboxsetfield
$form = new GroupTest_MemberForm($this, 'Form');
$member = $this->fixture->objFromFixture('GroupTest_Member', 'admin');
$form->loadDataFrom($member);
$checkboxSetField = $form->Fields()->fieldByName('Groups');
$checkboxSetField->setValue(array(
$adminGroup->ID => $adminGroup->ID, // keep existing relation
$parentGroup->ID => $parentGroup->ID, // add new relation
));
$form->saveInto($member);
$updatedGroups = $member->Groups();
$controlGroups = new Member_GroupSet(
$adminGroup,
$parentGroup
);
$this->assertEquals(
$updatedGroups->Map('ID','ID'),
$controlGroups->Map('ID','ID'),
"Adding a toplevel group works"
);
// Test unsetting relationship
$form->loadDataFrom($member);
$checkboxSetField = $form->Fields()->fieldByName('Groups');
$checkboxSetField->setValue(array(
$adminGroup->ID => $adminGroup->ID, // keep existing relation
//$parentGroup->ID => $parentGroup->ID, // remove previously set relation
));
$form->saveInto($member);
$member->flushCache();
$updatedGroups = $member->Groups();
$controlGroups = new Member_GroupSet(
$adminGroup
);
$this->assertEquals(
$updatedGroups->Map('ID','ID'),
$controlGroups->Map('ID','ID'),
"Removing a previously added toplevel group works"
);
// Test adding child group
}
}
class GroupTest_Member extends Member implements TestOnly {
function getCMSFields() {
$groups = DataObject::get('Group');
$groupsMap = ($groups) ? $groups->toDropDownMap() : false;
$fields = new FieldSet(
new HiddenField('ID', 'ID'),
new CheckboxSetField(
'Groups',
'Groups',
$groupsMap
)
);
return $fields;
}
}
class GroupTest_MemberForm extends Form {
function __construct($controller, $name) {
$fields = singleton('GroupTest_Member')->getCMSFields();
$actions = new FieldSet(
new FormAction('doSave','save')
);
parent::__construct($controller, $name, $fields, $actions);
}
function doSave($data, $form) {
// done in testing methods
}
}
?>

21
tests/GroupTest.yml Normal file
View File

@ -0,0 +1,21 @@
Group:
admingroup:
Code: admingroup
parentgroup:
Code: parentgroup
childgroup:
Code: childgroup
Parent: =>Group.parentgroup
GroupTest_Member:
admin:
FirstName: Admin
Groups: =>Group.admingroup
parentgroupuser:
FirstName: Parent Group User
Groups: =>Group.parentgroup
childgroupuser:
FirstName: Child Group User
Groups: =>Group.childgroup
allgroupuser:
FirstName: All Group User
Groups: =>Group.admingroup,=>Group.parentgroup,=>Group.childgroup

View File

@ -0,0 +1,137 @@
<?php
/**
*
* @todo Test Relation getters
* @todo Test filter and limit through GET params
* @todo Test DELETE verb
*
* @package sapphire
* @subpackage testing
*/
class SoapModelAccessTest extends SapphireTest {
static $fixture_file = 'sapphire/tests/SoapModelAccessTest.yml';
/*
public function testApiAccess() {
$c = new SoapClient(Director::absoluteBaseURL() . 'soap/v1/wsdl');
$soapResponse = $c->getXML(
"SoapModelAccessTest_Comment",
1,
null,
null,
'editor@test.com',
'editor'
);
var_dump($soapResponse);
die();
$responseArr = Convert::xml2array($soapResponse);
$this->assertEquals($responseArr['ID'], 1);
$this->assertEquals($responseArr['Name'], 'Joe');
}
public function testAuthenticatedPUT() {
// test wrong details
$c = new SoapClient(Director::absoluteBaseURL() . 'soap/v1/wsdl');
$soapResponse = $c->getXML(
"SoapModelAccessTest_Comment",
1,
null,
array(
'Name' => 'Updated Name'
),
'editor@test.com',
'wrongpassword'
);
$this->assertEquals(
$soapResponse,
'<error type="authentication" code="403">Forbidden</error>'
);
// test correct details
$c = new SoapClient(Director::absoluteBaseURL() . 'soap/v1/wsdl');
$soapResponse = $c->getXML(
"SoapModelAccessTest_Comment",
1,
null,
array(
'Name' => 'Updated Name'
),
'editor@test.com',
'editor'
);
$responseArr = Convert::xml2array($soapResponse);
$this->assertEquals($responseArr['ID'], 1);
$this->assertEquals($responseArr['Name'], 'Updated Name');
}
public function testAuthenticatedPOST() {
$c = new SoapClient(Director::absoluteBaseURL() . 'soap/v1/wsdl');
$soapResponse = $c->getXML(
"SoapModelAccessTest_Comment",
null,
null,
array(
'Name' => 'Created Name'
),
'editor@test.com',
'editor'
);
$responseArr = Convert::xml2array($soapResponse);
$this->assertEquals($responseArr['Name'], 'Created Name');
}
*/
}
/**
* Everybody can view comments, logged in members in the "users" group can create comments,
* but only "editors" can edit or delete them.
*
*/
class SoapModelAccessTest_Comment extends DataObject implements PermissionProvider,TestOnly {
static $api_access = true;
static $db = array(
"Name" => "Varchar(255)",
"Comment" => "Text"
);
static $has_many = array();
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 SoapModelAccessTest_Page extends DataObject implements TestOnly {
static $api_access = false;
static $db = array(
'Title' => 'Text',
'Content' => 'HTMLText',
);
}
?>

View File

@ -0,0 +1,38 @@
SoapModelAccessTest_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
SoapModelAccessTest_Page:
page1:
Title: Testpage without API Access