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@60234 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
7d2415c656
commit
c1440e0b02
@ -23,10 +23,11 @@ class JSONDataFormatter extends DataFormatter {
|
||||
|
||||
$json = "{\n className : \"$className\",\n";
|
||||
foreach($this->getFieldsForObj($obj) as $fieldName => $fieldType) {
|
||||
if(is_subclass_of($obj->$fieldName, 'Object') && $obj->hasMethod('toJSON')) {
|
||||
$jsonParts[] = "$fieldName : " . $obj->$fieldName->toJSON();
|
||||
$fieldValue = $obj->$fieldName;
|
||||
if(is_object($fieldValue) && is_subclass_of($fieldValue, 'Object') && $fieldValue->hasMethod('toJSON')) {
|
||||
$jsonParts[] = "$fieldName : " . $fieldValue->toJSON();
|
||||
} else {
|
||||
$jsonParts[] = "$fieldName : \"" . Convert::raw2json($obj->$fieldName) . "\"";
|
||||
$jsonParts[] = "$fieldName : " . Convert::raw2json($fieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,34 @@ class RestfulServer extends Controller {
|
||||
|
||||
protected static $api_base = "api/v1/";
|
||||
|
||||
/**
|
||||
* If no extension is given in the request, resolve to this extension
|
||||
* (and subsequently the {@link self::$default_mimetype}.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $default_extension = "xml";
|
||||
|
||||
/**
|
||||
* If no extension is given, resolve the request to this mimetype.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $default_mimetype = "text/xml";
|
||||
|
||||
/**
|
||||
* Maps common extensions to their mimetype representations.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $mimetype_map = array(
|
||||
'xml' => 'text/xml',
|
||||
'json' => 'text/json',
|
||||
'js' => 'text/json',
|
||||
'xhtml' => 'text/html',
|
||||
'html' => 'text/html',
|
||||
);
|
||||
|
||||
/*
|
||||
function handleItem($request) {
|
||||
return new RestfulServer_Item(DataObject::get_by_id($request->param("ClassName"), $request->param("ID")));
|
||||
@ -62,39 +90,22 @@ class RestfulServer extends Controller {
|
||||
*/
|
||||
function index() {
|
||||
ContentNegotiator::disable();
|
||||
|
||||
$requestMethod = $_SERVER['REQUEST_METHOD'];
|
||||
if(!isset($this->urlParams['ClassName'])) return $this->notFound();
|
||||
$className = $this->urlParams['ClassName'];
|
||||
$id = (isset($this->urlParams['ID'])) ? $this->urlParams['ID'] : null;
|
||||
$relation = (isset($this->urlParams['Relation'])) ? $this->urlParams['Relation'] : null;
|
||||
|
||||
$extension = $this->request->getExtension();
|
||||
|
||||
// Determine mime-type from extension
|
||||
$contentMap = array(
|
||||
'xml' => 'text/xml',
|
||||
'json' => 'text/json',
|
||||
'js' => 'text/json',
|
||||
'xhtml' => 'text/html',
|
||||
'html' => 'text/html',
|
||||
);
|
||||
$contentType = isset($contentMap[$extension]) ? $contentMap[$extension] : 'text/xml';
|
||||
|
||||
if(!$extension) $extension = "xml";
|
||||
$formatter = DataFormatter::for_extension($extension); //$this->dataFormatterFromMime($contentType);
|
||||
if($customFields = $this->request->getVar('fields')) $formatter->setCustomFields(explode(',',$customFields));
|
||||
$relationDepth = $this->request->getVar('relationdepth');
|
||||
if(is_numeric($relationDepth)) $formatter->relationDepth = (int)$relationDepth;
|
||||
|
||||
switch($requestMethod) {
|
||||
case 'GET':
|
||||
return $this->getHandler($className, $id, $relation, $formatter);
|
||||
return $this->getHandler($className, $id, $relation);
|
||||
|
||||
case 'PUT':
|
||||
return $this->putHandler($className, $id, $relation, $formatter);
|
||||
return $this->putHandler($className, $id, $relation);
|
||||
|
||||
case 'DELETE':
|
||||
return $this->deleteHandler($className, $id, $relation, $formatter);
|
||||
return $this->deleteHandler($className, $id, $relation);
|
||||
|
||||
case 'POST':
|
||||
}
|
||||
@ -129,10 +140,9 @@ class RestfulServer extends Controller {
|
||||
* @param String $className
|
||||
* @param Int $id
|
||||
* @param String $relation
|
||||
* @param String $contentType
|
||||
* @return String The serialized representation of the requested object(s) - usually XML or JSON.
|
||||
*/
|
||||
protected function getHandler($className, $id, $relation, $formatter) {
|
||||
protected function getHandler($className, $id, $relation) {
|
||||
$sort = array(
|
||||
'sort' => $this->request->getVar('sort'),
|
||||
'dir' => $this->request->getVar('dir')
|
||||
@ -142,6 +152,8 @@ class RestfulServer extends Controller {
|
||||
'limit' => $this->request->getVar('limit')
|
||||
);
|
||||
|
||||
$formatter = $this->getDataFormatter();
|
||||
|
||||
if($id) {
|
||||
$obj = DataObject::get_by_id($className, $id);
|
||||
if(!$obj) {
|
||||
@ -208,6 +220,21 @@ class RestfulServer extends Controller {
|
||||
return singleton($className)->buildDataObjectSet($query->execute());
|
||||
}
|
||||
|
||||
protected function getDataFormatter() {
|
||||
$extension = $this->request->getExtension();
|
||||
|
||||
// Determine mime-type from extension
|
||||
$contentType = isset(self::$mimetype_map[$extension]) ? self::$mimetype_map[$extension] : self::$default_mimetype;
|
||||
|
||||
if(!$extension) $extension = self::$default_extension;
|
||||
$formatter = DataFormatter::for_extension($extension); //$this->dataFormatterFromMime($contentType);
|
||||
if($customFields = $this->request->getVar('fields')) $formatter->setCustomFields(explode(',',$customFields));
|
||||
$relationDepth = $this->request->getVar('relationdepth');
|
||||
if(is_numeric($relationDepth)) $formatter->relationDepth = (int)$relationDepth;
|
||||
|
||||
return $formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for object delete
|
||||
*/
|
||||
|
@ -30,10 +30,11 @@ class XMLDataFormatter extends DataFormatter {
|
||||
|
||||
$json = "<$className href=\"$objHref.xml\">\n";
|
||||
foreach($this->getFieldsForObj($obj) as $fieldName => $fieldType) {
|
||||
if(is_subclass_of($obj->$fieldName, 'Object') && $obj->hasMethod('toXML')) {
|
||||
$json .= $obj->$fieldName->toXML();
|
||||
$fieldValue = $obj->$fieldName;
|
||||
if(is_object($fieldValue) && is_subclass_of($fieldValue, 'Object') && $fieldValue->hasMethod('toXML')) {
|
||||
$json .= $fieldValue->toXML();
|
||||
} else {
|
||||
$json .= "<$fieldName>" . Convert::raw2xml($obj->$fieldName) . "</$fieldName>\n";
|
||||
$json .= "<$fieldName>" . Convert::raw2xml($fieldValue) . "</$fieldName>\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1310,16 +1310,11 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
public function scaffoldSearchFields() {
|
||||
$fields = new FieldSet();
|
||||
foreach($this->searchable_fields() as $fieldName => $fieldType) {
|
||||
if (is_int($fieldName)) $fieldName = $fieldType;
|
||||
$field = $this->relObject($fieldName)->scaffoldSearchField();
|
||||
if (strstr($fieldName, '.')) {
|
||||
$field->setName(str_replace('.', '__', $fieldName));
|
||||
$parts = explode('.', $fieldName);
|
||||
//$label = $parts[count($parts)-2] . $parts[count($parts)-1];
|
||||
$field->setTitle($this->toLabel($parts[count($parts)-2]));
|
||||
} else {
|
||||
$field->setTitle($this->toLabel($fieldName));
|
||||
}
|
||||
$field->setTitle($this->searchable_fields_labels($fieldName));
|
||||
$fields->push($field);
|
||||
}
|
||||
return $fields;
|
||||
@ -1646,7 +1641,7 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
* @return boolean
|
||||
*/
|
||||
public function canView($member = null) {
|
||||
return true;
|
||||
return Permission::check('ADMIN');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1654,7 +1649,7 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
* @return boolean
|
||||
*/
|
||||
public function canEdit($member = null) {
|
||||
return true;
|
||||
return Permission::check('ADMIN');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1662,7 +1657,7 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
* @return boolean
|
||||
*/
|
||||
public function canDelete($member = null) {
|
||||
return true;
|
||||
return Permission::check('ADMIN');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1672,7 +1667,7 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
* @return boolean
|
||||
*/
|
||||
public function canCreate($member = null) {
|
||||
return true;
|
||||
return Permission::check('ADMIN');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2290,10 +2285,65 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
$fields = $this->stat('searchable_fields');
|
||||
if (!$fields) {
|
||||
$fields = array_fill_keys(array_keys($this->summaryFields()), 'TextField');
|
||||
} else {
|
||||
// rewrite array, if it is using shorthand syntax
|
||||
$rewrite = array();
|
||||
foreach($fields as $name => $type) {
|
||||
if (is_int($name)) $rewrite[$type] = 'TextField';
|
||||
else $rewrite[$name] = $type;
|
||||
}
|
||||
$fields = $rewrite;
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any user defined searchable fields labels that
|
||||
* exist. Allows overriding of default field names in the form
|
||||
* interface actually presented to the user.
|
||||
*
|
||||
* The reason for keeping this separate from searchable_fields,
|
||||
* which would be a logical place for this functionality, is to
|
||||
* avoid bloating and complicating the configuration array. Currently
|
||||
* much of this system is based on sensible defaults, and this property
|
||||
* would generally only be set in the case of more complex relationships
|
||||
* between data object being required in the search interface.
|
||||
*
|
||||
* Generates labels based on name of the field itself, if no static property exists.
|
||||
*
|
||||
* @todo fix bad code
|
||||
*
|
||||
* @param $fieldName name of the field to retrieve
|
||||
* @return array of all element labels if no argument given
|
||||
* @return string of label if field
|
||||
*/
|
||||
public function searchable_fields_labels($fieldName=false) {
|
||||
$labels = $this->stat('searchable_fields_labels');
|
||||
if (is_array($labels)) {
|
||||
if ($fieldName) {
|
||||
if (isset($labels[$fieldName])) {
|
||||
return $labels[$fieldName];
|
||||
}
|
||||
} else {
|
||||
return $labels;
|
||||
}
|
||||
} else {
|
||||
$fields = array_keys($this->searchable_fields());
|
||||
$labels = array_combine($fields, $fields);
|
||||
if ($fieldName) {
|
||||
if (strstr($fieldName, '.')) {
|
||||
$parts = explode('.', $fieldName);
|
||||
$label = $parts[count($parts)-2] . ' ' . $parts[count($parts)-1];
|
||||
return $this->toLabel($label);
|
||||
} else {
|
||||
return $this->toLabel($fieldName);
|
||||
}
|
||||
} else {
|
||||
return $labels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default summary fields for this object.
|
||||
*
|
||||
@ -2353,7 +2403,7 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
if (is_subclass_of($type, 'SearchFilter')) {
|
||||
$filters[$name] = new $type($name);
|
||||
} else {
|
||||
$filters[$name] = $this->relObject($name)->defaultSearchFilter();
|
||||
$filters[$name] = $this->relObject($name)->defaultSearchFilter($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2540,6 +2590,12 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
*/
|
||||
public static $searchable_fields = null;
|
||||
|
||||
/**
|
||||
* User defined labels for searchable_fields, used to override
|
||||
* default display in the search form.
|
||||
*/
|
||||
public static $searchable_fields_labels = null;
|
||||
|
||||
/**
|
||||
* Provides a default list of fields to be used by a 'summary'
|
||||
* view of this object.
|
||||
|
@ -142,7 +142,16 @@ class SQLQuery extends Object {
|
||||
*/
|
||||
public function leftJoin($table, $onPredicate) {
|
||||
$this->from[] = "LEFT JOIN $table ON $onPredicate";
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an INNER JOIN criteria to the FROM clause.
|
||||
*
|
||||
* @return SQLQuery This instance
|
||||
*/
|
||||
public function innerJoin($table, $onPredicate) {
|
||||
$this->from[] = "INNER JOIN $table ON $onPredicate";
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -217,12 +217,13 @@ abstract class DBField extends ViewableData {
|
||||
* @todo documentation
|
||||
*
|
||||
* @todo figure out how we pass configuration parameters to
|
||||
* search filters
|
||||
* search filters (note: parameter hack now in place to pass in the required full path - using $this->name won't work)
|
||||
*
|
||||
* @return SearchFilter
|
||||
*/
|
||||
public function defaultSearchFilter() {
|
||||
return new PartialMatchFilter($this->name);
|
||||
public function defaultSearchFilter($name = false) {
|
||||
$name = ($name) ? $name : $this->name;
|
||||
return new PartialMatchFilter($name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -110,12 +110,13 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
$parsedItems[] = $this->parseFixtureVal($item);
|
||||
}
|
||||
$obj->write();
|
||||
if($obj->many_many($fieldName)) {
|
||||
$obj->getManyManyComponents($fieldName)->setByIDList($parsedItems);
|
||||
} else {
|
||||
if($obj->has_many($fieldName)) {
|
||||
$obj->getComponents($fieldName)->setByIDList($parsedItems);
|
||||
} elseif($obj->many_many($fieldName)) {
|
||||
$obj->getManyManyComponents($fieldName)->setByIDList($parsedItems);
|
||||
}
|
||||
|
||||
} elseif($obj->has_one($fieldName)) {
|
||||
$obj->{$fieldName . 'ID'} = $this->parseFixtureVal($fieldVal);
|
||||
} else {
|
||||
$obj->$fieldName = $this->parseFixtureVal($fieldVal);
|
||||
}
|
||||
|
@ -1,4 +1,11 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package sapphire
|
||||
* @subpackage misc
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Routines for DNS to country resolution
|
||||
*
|
||||
@ -278,7 +285,7 @@ class Geoip extends Object {
|
||||
static function ip2country($address, $codeOnly = false) {
|
||||
|
||||
// Detect internal networks - this is us, so we're NZ
|
||||
if(substr($address,0,7)=="192.168" || substr($address,0,4)=="127." || $address == "::1") {
|
||||
if(substr($address,0,7)=="192.168" || substr($address,0,4)=="127.") {
|
||||
$code = "NZ";
|
||||
} else {
|
||||
$cmd = "geoiplookup ".escapeshellarg($address);
|
@ -136,7 +136,6 @@ class SearchContext extends Object {
|
||||
$SQL_sort = (!empty($sort)) ? Convert::raw2sql($sort) : singleton($this->modelClass)->stat('default_sort');
|
||||
$query->orderby($SQL_sort);
|
||||
|
||||
|
||||
foreach($searchParams as $key => $value) {
|
||||
if ($value != '0') {
|
||||
$key = str_replace('__', '.', $key);
|
||||
@ -148,7 +147,6 @@ class SearchContext extends Object {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
@ -164,6 +162,7 @@ class SearchContext extends Object {
|
||||
*/
|
||||
public function getResults($searchParams, $sort = false, $limit = false) {
|
||||
$searchParams = array_filter($searchParams, array($this,'clearEmptySearchFields'));
|
||||
|
||||
$query = $this->getQuery($searchParams, $sort, $limit);
|
||||
|
||||
// use if a raw SQL query is needed
|
||||
@ -188,26 +187,6 @@ class SearchContext extends Object {
|
||||
return ($value != '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo documentation
|
||||
* @todo implementation
|
||||
*
|
||||
* @param array $searchFilters
|
||||
* @param SQLQuery $query
|
||||
*/
|
||||
protected function processFilters(SQLQuery $query, $searchParams) {
|
||||
/*$conditions = array();
|
||||
foreach($this->filters as $field => $filter) {
|
||||
if (strstr($field, '.')) {
|
||||
$path = explode('.', $field);
|
||||
} else {
|
||||
$conditions[] = $filter->apply($searchParams[$field]);
|
||||
}
|
||||
}
|
||||
$query->where = $conditions;
|
||||
return $query;*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for the filter attached to a named field.
|
||||
*
|
||||
@ -231,6 +210,11 @@ class SearchContext extends Object {
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite the current search context filter map.
|
||||
*
|
||||
* @param array $filters
|
||||
*/
|
||||
public function setFilters($filters) {
|
||||
$this->filters = $filters;
|
||||
}
|
||||
|
@ -21,6 +21,9 @@ class Group extends DataObject {
|
||||
static $has_one = array(
|
||||
"Parent" => "SiteTree",
|
||||
);
|
||||
static $has_many = array(
|
||||
"Permissions" => "Permission",
|
||||
);
|
||||
static $many_many = array(
|
||||
"Members" => "Member",
|
||||
);
|
||||
|
@ -108,8 +108,8 @@ class Permission extends DataObject {
|
||||
|
||||
/**
|
||||
* Check that the given member has the given permission
|
||||
* @param int memberID The ID of the member to check. Leave blank for the
|
||||
* current member
|
||||
* @param int|Member memberID The ID of the member to check. Leave blank for the current member.
|
||||
* Alternatively you can use a member object.
|
||||
* @param string|array $code Code of the permission to check
|
||||
* @param string $arg Optional argument (e.g. a permissions for a specific
|
||||
* page)
|
||||
@ -120,8 +120,9 @@ class Permission extends DataObject {
|
||||
* disabled, TRUE will be returned if the permission does
|
||||
* not exist at all.
|
||||
*/
|
||||
public static function checkMember($memberID, $code, $arg = "any", $strict = true) {
|
||||
public static function checkMember($member, $code, $arg = "any", $strict = true) {
|
||||
$perms_list = self::get_declared_permissions_list();
|
||||
$memberID = (is_object($member)) ? $member->ID : $member;
|
||||
|
||||
if(self::$declared_permissions && is_array($perms_list) &&
|
||||
!in_array($code, $perms_list)) {
|
||||
@ -146,7 +147,7 @@ class Permission extends DataObject {
|
||||
if(is_numeric($arg)) {
|
||||
$argClause = "AND Arg IN (-1, $arg) ";
|
||||
} else {
|
||||
use_error("Permission::checkMember: bad arg '$arg'",
|
||||
user_error("Permission::checkMember: bad arg '$arg'",
|
||||
E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user