annotate sites/all/modules/restws/restws.formats.inc @ 4:ce11bbd8f642

added modules
author danieleb <danielebarchiesi@me.com>
date Thu, 19 Sep 2013 10:38:44 +0100
parents
children
rev   line source
danielebarchiesi@4 1 <?php
danielebarchiesi@4 2
danielebarchiesi@4 3 /**
danielebarchiesi@4 4 * @file
danielebarchiesi@4 5 * RESTful web services module formats.
danielebarchiesi@4 6 */
danielebarchiesi@4 7
danielebarchiesi@4 8 /**
danielebarchiesi@4 9 * Interface implemented by formatter implementations for the http client.
danielebarchiesi@4 10 */
danielebarchiesi@4 11 interface RestWSFormatInterface {
danielebarchiesi@4 12
danielebarchiesi@4 13 /**
danielebarchiesi@4 14 * Gets the representation of a resource.
danielebarchiesi@4 15 *
danielebarchiesi@4 16 * @param RestWSResourceControllerInterface $resourceController
danielebarchiesi@4 17 * The controller used to retrieve the resource.
danielebarchiesi@4 18 * @param int|string $id
danielebarchiesi@4 19 * The id of the resource that should be returned.
danielebarchiesi@4 20 *
danielebarchiesi@4 21 * @return string
danielebarchiesi@4 22 * The representation of the resource.
danielebarchiesi@4 23 */
danielebarchiesi@4 24 public function viewResource($resourceController, $id);
danielebarchiesi@4 25
danielebarchiesi@4 26 /**
danielebarchiesi@4 27 * Create a resource.
danielebarchiesi@4 28 *
danielebarchiesi@4 29 * @param RestWSResourceControllerInterface $resourceController
danielebarchiesi@4 30 * The controller used to create the resource.
danielebarchiesi@4 31 * @param string $data
danielebarchiesi@4 32 * The representation of the resource.
danielebarchiesi@4 33 *
danielebarchiesi@4 34 * @return int|string
danielebarchiesi@4 35 * The id of the newly created resource.
danielebarchiesi@4 36 */
danielebarchiesi@4 37 public function createResource($resourceController, $data);
danielebarchiesi@4 38
danielebarchiesi@4 39 /**
danielebarchiesi@4 40 * Update a resource.
danielebarchiesi@4 41 *
danielebarchiesi@4 42 * @param RestWSResourceControllerInterface $resourceController
danielebarchiesi@4 43 * The controller used to update the resource.
danielebarchiesi@4 44 * @param int|string $id
danielebarchiesi@4 45 * The id of the resource that should be updated.
danielebarchiesi@4 46 * @param string $data
danielebarchiesi@4 47 * The representation of the resource.
danielebarchiesi@4 48 */
danielebarchiesi@4 49 public function updateResource($resourceController, $id, $data);
danielebarchiesi@4 50
danielebarchiesi@4 51 /**
danielebarchiesi@4 52 * Delete a resource.
danielebarchiesi@4 53 *
danielebarchiesi@4 54 * @param RestWSResourceControllerInterface $resourceController
danielebarchiesi@4 55 * The controller used to update the resource.
danielebarchiesi@4 56 * @param int|string $id
danielebarchiesi@4 57 * The id of the resource that should be deleted.
danielebarchiesi@4 58 */
danielebarchiesi@4 59 public function deleteResource($resourceController, $id);
danielebarchiesi@4 60
danielebarchiesi@4 61 /**
danielebarchiesi@4 62 * Query for a resource.
danielebarchiesi@4 63 *
danielebarchiesi@4 64 * If a format doesn't want to implement querying, then it should throw an
danielebarchiesi@4 65 * RestWSException with the 501 HTTP status code.
danielebarchiesi@4 66 *
danielebarchiesi@4 67 * @param RestWSResourceControllerInterface $resourceController
danielebarchiesi@4 68 * The controller used to query the resource.
danielebarchiesi@4 69 * @param array $payload
danielebarchiesi@4 70 * An optional way to pass the query parameters.
danielebarchiesi@4 71 *
danielebarchiesi@4 72 * @return string
danielebarchiesi@4 73 * The serialized representation of a list of resources.
danielebarchiesi@4 74 */
danielebarchiesi@4 75 public function queryResource($resourceController, $payload);
danielebarchiesi@4 76
danielebarchiesi@4 77
danielebarchiesi@4 78 /**
danielebarchiesi@4 79 * Returns the mime type of this format, e.g. 'application/json' or
danielebarchiesi@4 80 * 'application/xml'.
danielebarchiesi@4 81 */
danielebarchiesi@4 82 public function mimeType();
danielebarchiesi@4 83
danielebarchiesi@4 84 /**
danielebarchiesi@4 85 * Returns the short name of this format.
danielebarchiesi@4 86 *
danielebarchiesi@4 87 * @return string
danielebarchiesi@4 88 * The format name, example: "json".
danielebarchiesi@4 89 */
danielebarchiesi@4 90 public function getName();
danielebarchiesi@4 91 }
danielebarchiesi@4 92
danielebarchiesi@4 93 /**
danielebarchiesi@4 94 * A base for all simple formats that are just serializing/unserializing an
danielebarchiesi@4 95 * array of property values.
danielebarchiesi@4 96 */
danielebarchiesi@4 97 abstract class RestWSBaseFormat implements RestWSFormatInterface {
danielebarchiesi@4 98
danielebarchiesi@4 99 protected $formatName;
danielebarchiesi@4 100 protected $formatInfo;
danielebarchiesi@4 101
danielebarchiesi@4 102 public function __construct($name, $info) {
danielebarchiesi@4 103 $this->formatName = $name;
danielebarchiesi@4 104 $this->formatInfo = $info;
danielebarchiesi@4 105 }
danielebarchiesi@4 106
danielebarchiesi@4 107 /**
danielebarchiesi@4 108 * Gets the representation of a resource.
danielebarchiesi@4 109 */
danielebarchiesi@4 110 public function viewResource($resourceController, $id) {
danielebarchiesi@4 111 $values = self::getData($resourceController->wrapper($id));
danielebarchiesi@4 112 $function = __FUNCTION__;
danielebarchiesi@4 113 drupal_alter('restws_response', $values, $function, $this->formatName);
danielebarchiesi@4 114
danielebarchiesi@4 115 return $this->serialize($values);
danielebarchiesi@4 116 }
danielebarchiesi@4 117
danielebarchiesi@4 118 /**
danielebarchiesi@4 119 * Creates a new resource.
danielebarchiesi@4 120 */
danielebarchiesi@4 121 public function createResource($resourceController, $data) {
danielebarchiesi@4 122 $values = $this->unserialize($resourceController->propertyInfo(), $data);
danielebarchiesi@4 123 $id = $resourceController->create($values);
danielebarchiesi@4 124 $ref = self::getResourceReference($resourceController->resource(), $id);
danielebarchiesi@4 125 $function = __FUNCTION__;
danielebarchiesi@4 126 drupal_alter('restws_response', $ref, $function, $this->formatName);
danielebarchiesi@4 127
danielebarchiesi@4 128 return $this->serialize($ref);
danielebarchiesi@4 129 }
danielebarchiesi@4 130
danielebarchiesi@4 131 /**
danielebarchiesi@4 132 * Updates a resource.
danielebarchiesi@4 133 */
danielebarchiesi@4 134 public function updateResource($resourceController, $id, $data) {
danielebarchiesi@4 135 $values = $this->unserialize($resourceController->propertyInfo(), $data);
danielebarchiesi@4 136 $resourceController->update($id, $values);
danielebarchiesi@4 137 // Return an empty representation by default.
danielebarchiesi@4 138 $value = array();
danielebarchiesi@4 139 $function = __FUNCTION__;
danielebarchiesi@4 140 drupal_alter('restws_response', $value, $function, $this->formatName);
danielebarchiesi@4 141
danielebarchiesi@4 142 return $this->serialize($value);
danielebarchiesi@4 143 }
danielebarchiesi@4 144
danielebarchiesi@4 145 /**
danielebarchiesi@4 146 * Deletes a resource.
danielebarchiesi@4 147 */
danielebarchiesi@4 148 public function deleteResource($resourceController, $id) {
danielebarchiesi@4 149 $resourceController->delete($id);
danielebarchiesi@4 150 // Return an empty representation by default.
danielebarchiesi@4 151 $value = array();
danielebarchiesi@4 152 $function = __FUNCTION__;
danielebarchiesi@4 153 drupal_alter('restws_response', $value, $function, $this->formatName);
danielebarchiesi@4 154
danielebarchiesi@4 155 return $this->serialize($value);
danielebarchiesi@4 156 }
danielebarchiesi@4 157
danielebarchiesi@4 158 /**
danielebarchiesi@4 159 * Implements RestWSFormatInterface::queryResource().
danielebarchiesi@4 160 */
danielebarchiesi@4 161 public function queryResource($resourceController, $payload) {
danielebarchiesi@4 162 // Get the parameter from the URL.
danielebarchiesi@4 163 $parameters = drupal_get_query_parameters();
danielebarchiesi@4 164
danielebarchiesi@4 165 $rest_controls = restws_meta_controls();
danielebarchiesi@4 166 $properties = $resourceController->propertyInfo();
danielebarchiesi@4 167 $split_parameters = $this->splitParameters($properties, $parameters);
danielebarchiesi@4 168
danielebarchiesi@4 169 $values = $this->generateQueryURIs($resourceController, $parameters, $split_parameters['filters']);
danielebarchiesi@4 170
danielebarchiesi@4 171 $full = (isset($split_parameters['meta_controls'][$rest_controls['full']])) ? $split_parameters['meta_controls'][$rest_controls['full']] : 1;
danielebarchiesi@4 172
danielebarchiesi@4 173 $result = $resourceController->query($split_parameters['filters'], $split_parameters['meta_controls']);
danielebarchiesi@4 174 if ($full === '0') {
danielebarchiesi@4 175 foreach ($result as $id) {
danielebarchiesi@4 176 $values['list'][] = $this->getResourceReference($resourceController->resource(), $id);
danielebarchiesi@4 177 }
danielebarchiesi@4 178 }
danielebarchiesi@4 179 else {
danielebarchiesi@4 180 foreach ($result as $id) {
danielebarchiesi@4 181 $values['list'][] = self::getData($resourceController->wrapper($id));
danielebarchiesi@4 182 }
danielebarchiesi@4 183 }
danielebarchiesi@4 184
danielebarchiesi@4 185 $function = __FUNCTION__;
danielebarchiesi@4 186 drupal_alter('restws_response', $values, $function, $this->formatName);
danielebarchiesi@4 187
danielebarchiesi@4 188 return $this->serialize($values);
danielebarchiesi@4 189 }
danielebarchiesi@4 190
danielebarchiesi@4 191 public function mimeType() {
danielebarchiesi@4 192 return $this->formatInfo['mime type'];
danielebarchiesi@4 193 }
danielebarchiesi@4 194
danielebarchiesi@4 195 public function getName() {
danielebarchiesi@4 196 return $this->formatName;
danielebarchiesi@4 197 }
danielebarchiesi@4 198
danielebarchiesi@4 199 /**
danielebarchiesi@4 200 * Gets a simple PHP array using URI references for some wrapped data.
danielebarchiesi@4 201 *
danielebarchiesi@4 202 * This is the counter-part of self::getPropertyValues().
danielebarchiesi@4 203 */
danielebarchiesi@4 204 public static function getData($wrapper) {
danielebarchiesi@4 205 $data = array();
danielebarchiesi@4 206 $filtered = restws_property_access_filter($wrapper);
danielebarchiesi@4 207 foreach ($filtered as $name => $property) {
danielebarchiesi@4 208 try {
danielebarchiesi@4 209 if ($property instanceof EntityDrupalWrapper) {
danielebarchiesi@4 210 // For referenced entities only return the URI.
danielebarchiesi@4 211 if ($id = $property->getIdentifier()) {
danielebarchiesi@4 212 $data[$name] = self::getResourceReference($property->type(), $id);
danielebarchiesi@4 213 }
danielebarchiesi@4 214 }
danielebarchiesi@4 215 elseif ($property instanceof EntityValueWrapper) {
danielebarchiesi@4 216 $data[$name] = $property->value();
danielebarchiesi@4 217 }
danielebarchiesi@4 218 elseif ($property instanceof EntityListWrapper || $property instanceof EntityStructureWrapper) {
danielebarchiesi@4 219 $data[$name] = self::getData($property);
danielebarchiesi@4 220 }
danielebarchiesi@4 221 }
danielebarchiesi@4 222 catch (EntityMetadataWrapperException $e) {
danielebarchiesi@4 223 // A property causes problems - ignore that.
danielebarchiesi@4 224 }
danielebarchiesi@4 225 }
danielebarchiesi@4 226 return $data;
danielebarchiesi@4 227 }
danielebarchiesi@4 228
danielebarchiesi@4 229 public static function getResourceReference($resource, $id) {
danielebarchiesi@4 230 $return = array(
danielebarchiesi@4 231 'uri' => restws_resource_uri($resource, $id),
danielebarchiesi@4 232 'id' => $id,
danielebarchiesi@4 233 'resource' => $resource,
danielebarchiesi@4 234 );
danielebarchiesi@4 235 if (module_exists('uuid') && entity_get_info($resource)) {
danielebarchiesi@4 236 $ids = entity_get_uuid_by_id($resource, array($id));
danielebarchiesi@4 237 if ($id = reset($ids)) {
danielebarchiesi@4 238 $return['uuid'] = $id;
danielebarchiesi@4 239 }
danielebarchiesi@4 240 }
danielebarchiesi@4 241 return $return;
danielebarchiesi@4 242 }
danielebarchiesi@4 243
danielebarchiesi@4 244 /**
danielebarchiesi@4 245 * Transforms simple-array data values to valid entity property values.
danielebarchiesi@4 246 *
danielebarchiesi@4 247 * This is the counter-part of self::getData(), thus it converts resource
danielebarchiesi@4 248 * references to the required value(s).
danielebarchiesi@4 249 *
danielebarchiesi@4 250 * @param array $values
danielebarchiesi@4 251 * The array representation of the data values.
danielebarchiesi@4 252 * @param $property_info
danielebarchiesi@4 253 * The property info array of the entity type for which we are transforming
danielebarchiesi@4 254 * the values.
danielebarchiesi@4 255 */
danielebarchiesi@4 256 protected function getPropertyValues(array &$values, $property_info) {
danielebarchiesi@4 257 foreach ($values as $name => &$property_value) {
danielebarchiesi@4 258 if (isset($property_info[$name]) && $info = $property_info[$name]) {
danielebarchiesi@4 259
danielebarchiesi@4 260 // Check if there is a resource array and if the property has a type.
danielebarchiesi@4 261 if (is_array($property_value) && isset($info['type'])) {
danielebarchiesi@4 262
danielebarchiesi@4 263 // Check if the field is a list or a single value field.
danielebarchiesi@4 264 if (entity_property_list_extract_type($info['type'])) {
danielebarchiesi@4 265 // Check if the list values consist of structure wrappers.
danielebarchiesi@4 266 if (array_key_exists('property info', $info)) {
danielebarchiesi@4 267 foreach ($property_value as &$list_values) {
danielebarchiesi@4 268 $this->getPropertyValues($list_values, $info['property info']);
danielebarchiesi@4 269 }
danielebarchiesi@4 270 }
danielebarchiesi@4 271 else {
danielebarchiesi@4 272 $list_type = entity_property_list_extract_type($info['type']);
danielebarchiesi@4 273 foreach ($property_value as &$list_value) {
danielebarchiesi@4 274 $list_value = $this->getResourceReferenceValue($list_type, $list_value);
danielebarchiesi@4 275 }
danielebarchiesi@4 276 }
danielebarchiesi@4 277 }
danielebarchiesi@4 278 else {
danielebarchiesi@4 279 // Check if the property is a structure wrapper.
danielebarchiesi@4 280 if (array_key_exists('property info', $info)) {
danielebarchiesi@4 281 $this->getPropertyValues($property_value, $info['property info']);
danielebarchiesi@4 282 }
danielebarchiesi@4 283 else {
danielebarchiesi@4 284 $property_value = $this->getResourceReferenceValue($info['type'], $property_value);
danielebarchiesi@4 285 }
danielebarchiesi@4 286 }
danielebarchiesi@4 287 }
danielebarchiesi@4 288 }
danielebarchiesi@4 289 }
danielebarchiesi@4 290 }
danielebarchiesi@4 291
danielebarchiesi@4 292 /**
danielebarchiesi@4 293 * Gets the resource reference value.
danielebarchiesi@4 294 *
danielebarchiesi@4 295 * @param $type
danielebarchiesi@4 296 * The data type of the reference property.
danielebarchiesi@4 297 * @param array $reference
danielebarchiesi@4 298 * The input data specifying the resource reference in one supported way.
danielebarchiesi@4 299 *
danielebarchiesi@4 300 * @return mixed
danielebarchiesi@4 301 * The value to be set for the reference. Usually this is an entity or
danielebarchiesi@4 302 * resource id, but for generic entity references it's an
danielebarchiesi@4 303 * EntityDrupalWrapper.
danielebarchiesi@4 304 *
danielebarchiesi@4 305 * @see RestWSBaseFormat::getResourceReference()
danielebarchiesi@4 306 */
danielebarchiesi@4 307 protected function getResourceReferenceValue($type, array $reference) {
danielebarchiesi@4 308
danielebarchiesi@4 309 if (isset($reference['id']) && $type != 'entity') {
danielebarchiesi@4 310 return $reference['id'];
danielebarchiesi@4 311 }
danielebarchiesi@4 312 // Handle setting generic entity references, i.e. of type entity.
danielebarchiesi@4 313 elseif ($type == 'entity' && isset($reference['id']) && isset($reference['resource'])) {
danielebarchiesi@4 314 if (!entity_get_info($reference['resource'])) {
danielebarchiesi@4 315 throw new RestWSException('Invalid resource for entity reference given.', 406);
danielebarchiesi@4 316 }
danielebarchiesi@4 317 return entity_metadata_wrapper($reference['resource'], $reference['id']);
danielebarchiesi@4 318 }
danielebarchiesi@4 319 elseif (isset($reference['uri'])) {
danielebarchiesi@4 320 // @todo: Implement setting references by URI by parsing resource/id from
danielebarchiesi@4 321 // the URI.
danielebarchiesi@4 322 }
danielebarchiesi@4 323 elseif (isset($reference['uuid']) && module_exists('uuid') && $type != 'entity') {
danielebarchiesi@4 324 $ids = entity_get_id_by_uuid($type, array($reference['uuid']));
danielebarchiesi@4 325 if (!$ids) {
danielebarchiesi@4 326 throw new RestWSException('Invalid UUID for resource reference given.', 406);
danielebarchiesi@4 327 }
danielebarchiesi@4 328 return reset($ids);
danielebarchiesi@4 329 }
danielebarchiesi@4 330
danielebarchiesi@4 331 throw new RestWSException('Invalid value for resource reference given.', 406);
danielebarchiesi@4 332 }
danielebarchiesi@4 333
danielebarchiesi@4 334 /**
danielebarchiesi@4 335 * Splits a query parameter into two sub arrays containing the filters and
danielebarchiesi@4 336 * meta controls.
danielebarchiesi@4 337 *
danielebarchiesi@4 338 * @param array $properties
danielebarchiesi@4 339 * An array containing the properties of the resource.
danielebarchiesi@4 340 *
danielebarchiesi@4 341 * @param array $parameters
danielebarchiesi@4 342 * An array which contains filters and meta controls.
danielebarchiesi@4 343 *
danielebarchiesi@4 344 * @return array
danielebarchiesi@4 345 * An array containing two sub arrays, one for filters and one for meta
danielebarchiesi@4 346 * controls with corresponding keys.
danielebarchiesi@4 347 *
danielebarchiesi@4 348 * @throws RestWSException
danielebarchiesi@4 349 * If a filter isn't valid, the function will throw a RestWSException with
danielebarchiesi@4 350 * the 412 HTTP status code.
danielebarchiesi@4 351 */
danielebarchiesi@4 352 protected function splitParameters($properties, array $parameters) {
danielebarchiesi@4 353 $meta_controls = array();
danielebarchiesi@4 354 $rest_controls = restws_meta_controls();
danielebarchiesi@4 355 foreach ($parameters as $control_name => $property) {
danielebarchiesi@4 356 if (isset($rest_controls[$control_name])) {
danielebarchiesi@4 357 $meta_controls[$control_name] = $property;
danielebarchiesi@4 358 unset($parameters[$control_name]);
danielebarchiesi@4 359 }
danielebarchiesi@4 360 }
danielebarchiesi@4 361
danielebarchiesi@4 362 $filters = array();
danielebarchiesi@4 363 foreach ($parameters as $parameter => $value) {
danielebarchiesi@4 364 // Check if the property is prefixed.
danielebarchiesi@4 365 if (substr($parameter, 0, 9) == 'property_') {
danielebarchiesi@4 366 $parameter = substr($parameter, 9, strlen($parameter) - 9);
danielebarchiesi@4 367 }
danielebarchiesi@4 368
danielebarchiesi@4 369 // If the parameter doesn't exist, we can not filter for and need to
danielebarchiesi@4 370 // notify the client about it.
danielebarchiesi@4 371 if (!isset($properties[$parameter])) {
danielebarchiesi@4 372 throw new RestWSException('Not a valid filter: ' . $parameter, 412);
danielebarchiesi@4 373 }
danielebarchiesi@4 374 $filters[$parameter] = $value;
danielebarchiesi@4 375 }
danielebarchiesi@4 376 return array('meta_controls' => $meta_controls, 'filters' => $filters);
danielebarchiesi@4 377 }
danielebarchiesi@4 378
danielebarchiesi@4 379 /**
danielebarchiesi@4 380 * Generates all navigation links for querying.
danielebarchiesi@4 381 *
danielebarchiesi@4 382 * @param RestWSResourceControllerInterface $resourceController
danielebarchiesi@4 383 * The controller used to query the resource.
danielebarchiesi@4 384 *
danielebarchiesi@4 385 * @param array $parameters
danielebarchiesi@4 386 * The HTTP GET parameters for the query.
danielebarchiesi@4 387 *
danielebarchiesi@4 388 * @param array $filters
danielebarchiesi@4 389 * The filters for the query.
danielebarchiesi@4 390 *
danielebarchiesi@4 391 * @return array
danielebarchiesi@4 392 * An array containing all navigation links.
danielebarchiesi@4 393 *
danielebarchiesi@4 394 * @throws RestWSException
danielebarchiesi@4 395 * If the page is out of range the function will throw a new RestWSException
danielebarchiesi@4 396 * with HTTP status code 404.
danielebarchiesi@4 397 */
danielebarchiesi@4 398 protected function generateQueryURIs(RestWSResourceControllerInterface $resourceController, array $parameters, array $filters) {
danielebarchiesi@4 399 $rest_controls = restws_meta_controls();
danielebarchiesi@4 400
danielebarchiesi@4 401 $count = $resourceController->count($filters);
danielebarchiesi@4 402 $limit = isset($parameters[$rest_controls['limit']]) ? $parameters[$rest_controls['limit']] : NULL;
danielebarchiesi@4 403 $limit = $resourceController->limit($limit);
danielebarchiesi@4 404 $page = isset($parameters[$rest_controls['page']]) ? $parameters[$rest_controls['page']] : 0;
danielebarchiesi@4 405
danielebarchiesi@4 406 $last = ceil($count / $limit) - 1;
danielebarchiesi@4 407
danielebarchiesi@4 408 if ($page > $last || $page < 0) {
danielebarchiesi@4 409 throw new RestWSException('Page doesn\'t exist.', 404);
danielebarchiesi@4 410 }
danielebarchiesi@4 411
danielebarchiesi@4 412 $uris = array();
danielebarchiesi@4 413 $options = array(
danielebarchiesi@4 414 'query' => &$parameters,
danielebarchiesi@4 415 );
danielebarchiesi@4 416
danielebarchiesi@4 417 $uris['self'] = restws_resource_uri($resourceController->resource(), null, $options);
danielebarchiesi@4 418 $parameters['page'] = 0;
danielebarchiesi@4 419 $uris['first'] = restws_resource_uri($resourceController->resource(), null, $options);
danielebarchiesi@4 420 $parameters['page'] = $last;
danielebarchiesi@4 421 $uris['last'] = restws_resource_uri($resourceController->resource(), null, $options);
danielebarchiesi@4 422
danielebarchiesi@4 423
danielebarchiesi@4 424 if ($page != 0) {
danielebarchiesi@4 425 $parameters['page'] = $page - 1;
danielebarchiesi@4 426 $uris['prev'] = restws_resource_uri($resourceController->resource(), null, $options);
danielebarchiesi@4 427 }
danielebarchiesi@4 428
danielebarchiesi@4 429 if ($page != $last) {
danielebarchiesi@4 430 $parameters['page'] = $page + 1;
danielebarchiesi@4 431 $uris['next'] = restws_resource_uri($resourceController->resource(), null, $options);
danielebarchiesi@4 432 }
danielebarchiesi@4 433
danielebarchiesi@4 434 return $uris;
danielebarchiesi@4 435 }
danielebarchiesi@4 436 }
danielebarchiesi@4 437
danielebarchiesi@4 438 /**
danielebarchiesi@4 439 * Filters out properties where view access is not allowed for the current user.
danielebarchiesi@4 440 *
danielebarchiesi@4 441 * @param EntityMetadataWrapper $wrapper
danielebarchiesi@4 442 * EntityMetadataWrapper that should be checked.
danielebarchiesi@4 443 *
danielebarchiesi@4 444 * @return
danielebarchiesi@4 445 * An array of properties where access is allowed, keyed by their property
danielebarchiesi@4 446 * name.
danielebarchiesi@4 447 */
danielebarchiesi@4 448 function restws_property_access_filter($wrapper) {
danielebarchiesi@4 449 $filtered = array();
danielebarchiesi@4 450 foreach ($wrapper as $name => $property) {
danielebarchiesi@4 451 if ($property->access('view')) {
danielebarchiesi@4 452 $filtered[$name] = $property;
danielebarchiesi@4 453 }
danielebarchiesi@4 454 }
danielebarchiesi@4 455 return $filtered;
danielebarchiesi@4 456 }
danielebarchiesi@4 457
danielebarchiesi@4 458 /**
danielebarchiesi@4 459 * A formatter to format json.
danielebarchiesi@4 460 */
danielebarchiesi@4 461 class RestWSFormatJSON extends RestWSBaseFormat {
danielebarchiesi@4 462
danielebarchiesi@4 463 public function serialize($values) {
danielebarchiesi@4 464 return drupal_json_encode($values);
danielebarchiesi@4 465 }
danielebarchiesi@4 466
danielebarchiesi@4 467 public function unserialize($properties, $data) {
danielebarchiesi@4 468 $values = drupal_json_decode($data);
danielebarchiesi@4 469 $this->getPropertyValues($values, $properties);
danielebarchiesi@4 470 return $values;
danielebarchiesi@4 471 }
danielebarchiesi@4 472 }
danielebarchiesi@4 473
danielebarchiesi@4 474 /**
danielebarchiesi@4 475 * A formatter for XML.
danielebarchiesi@4 476 */
danielebarchiesi@4 477 class RestWSFormatXML extends RestWSBaseFormat {
danielebarchiesi@4 478
danielebarchiesi@4 479 /**
danielebarchiesi@4 480 * Gets the representation of a resource.
danielebarchiesi@4 481 */
danielebarchiesi@4 482 public function viewResource($resourceController, $id) {
danielebarchiesi@4 483 $xml = new DOMDocument('1.0', 'utf-8');
danielebarchiesi@4 484 $element = $xml->createElement($resourceController->resource());
danielebarchiesi@4 485 self::addToXML($xml, $element, $resourceController->wrapper($id));
danielebarchiesi@4 486 $xml->appendChild($element);
danielebarchiesi@4 487
danielebarchiesi@4 488 $function = __FUNCTION__;
danielebarchiesi@4 489 drupal_alter('restws_response', $xml, $function, $this->formatName);
danielebarchiesi@4 490
danielebarchiesi@4 491 return $xml->saveXML();
danielebarchiesi@4 492 }
danielebarchiesi@4 493
danielebarchiesi@4 494 /**
danielebarchiesi@4 495 * Creates a new resource.
danielebarchiesi@4 496 */
danielebarchiesi@4 497 public function createResource($resourceController, $data) {
danielebarchiesi@4 498 $values = $this->unserialize($resourceController->propertyInfo(), $data);
danielebarchiesi@4 499 $id = $resourceController->create($values);
danielebarchiesi@4 500
danielebarchiesi@4 501 $xml = new DOMDocument('1.0', 'utf-8');
danielebarchiesi@4 502 $element = $xml->createElement('uri');
danielebarchiesi@4 503 self::setXMLReference($element, $resourceController->resource(), $id);
danielebarchiesi@4 504 $xml->appendChild($element);
danielebarchiesi@4 505
danielebarchiesi@4 506 $function = __FUNCTION__;
danielebarchiesi@4 507 drupal_alter('restws_response', $xml, $function, $this->formatName);
danielebarchiesi@4 508
danielebarchiesi@4 509 return $xml->saveXML();
danielebarchiesi@4 510 }
danielebarchiesi@4 511
danielebarchiesi@4 512 /**
danielebarchiesi@4 513 * Overrides RestWSBaseFormat::queryResource().
danielebarchiesi@4 514 */
danielebarchiesi@4 515 public function queryResource($resourceController, $payload) {
danielebarchiesi@4 516 $xml = new DOMDocument('1.0', 'utf-8');
danielebarchiesi@4 517 $element = $xml->createElement('list');
danielebarchiesi@4 518
danielebarchiesi@4 519 $rest_controls = restws_meta_controls();
danielebarchiesi@4 520 $parameters = drupal_get_query_parameters();
danielebarchiesi@4 521 $properties = $resourceController->propertyInfo();
danielebarchiesi@4 522 $split_parameters = $this->splitParameters($properties, $parameters);
danielebarchiesi@4 523
danielebarchiesi@4 524 $links = $this->generateQueryURIs($resourceController, $parameters, $split_parameters['filters']);
danielebarchiesi@4 525
danielebarchiesi@4 526 foreach ($links as $rel => $link) {
danielebarchiesi@4 527 $item = $xml->createElement('link');
danielebarchiesi@4 528 $item->setAttribute('rel', $rel);
danielebarchiesi@4 529 $item->setAttribute('href', $link);
danielebarchiesi@4 530 $element->appendChild($item);
danielebarchiesi@4 531 }
danielebarchiesi@4 532
danielebarchiesi@4 533 $full = (isset($split_parameters['meta_controls'][$rest_controls['full']])) ? $split_parameters['meta_controls'][$rest_controls['full']] : 1;
danielebarchiesi@4 534
danielebarchiesi@4 535 $result = $resourceController->query($split_parameters['filters'], $split_parameters['meta_controls']);
danielebarchiesi@4 536
danielebarchiesi@4 537 if ($full === '0') {
danielebarchiesi@4 538 foreach ($result as $id) {
danielebarchiesi@4 539 $item = $xml->createElement($resourceController->resource());
danielebarchiesi@4 540 self::setXMLReference($item, $resourceController->resource(), $id);
danielebarchiesi@4 541 $element->appendChild($item);
danielebarchiesi@4 542 }
danielebarchiesi@4 543 }
danielebarchiesi@4 544 else {
danielebarchiesi@4 545 foreach ($result as $id) {
danielebarchiesi@4 546 $item = $xml->createElement($resourceController->resource());
danielebarchiesi@4 547 self::addToXML($xml, $item, $resourceController->wrapper($id));
danielebarchiesi@4 548 $element->appendChild($item);
danielebarchiesi@4 549 }
danielebarchiesi@4 550 }
danielebarchiesi@4 551
danielebarchiesi@4 552 $xml->appendChild($element);
danielebarchiesi@4 553
danielebarchiesi@4 554 $function = __FUNCTION__;
danielebarchiesi@4 555 drupal_alter('restws_response', $xml, $function, $this->formatName);
danielebarchiesi@4 556
danielebarchiesi@4 557 return $xml->saveXML();
danielebarchiesi@4 558 }
danielebarchiesi@4 559
danielebarchiesi@4 560 public function serialize($data) {
danielebarchiesi@4 561 // Return an empty XML document.
danielebarchiesi@4 562 $xml = new DOMDocument('1.0', 'utf-8');
danielebarchiesi@4 563 return $xml->saveXML();
danielebarchiesi@4 564 }
danielebarchiesi@4 565
danielebarchiesi@4 566 public function unserialize($properties, $data) {
danielebarchiesi@4 567 $xml = simplexml_load_string($data);
danielebarchiesi@4 568 return $this->xmlToArray($properties, $xml);
danielebarchiesi@4 569 }
danielebarchiesi@4 570
danielebarchiesi@4 571 /**
danielebarchiesi@4 572 * Turns the xml structure into an array of values.
danielebarchiesi@4 573 */
danielebarchiesi@4 574 public function xmlToArray($properties, SimpleXMLElement $xml, $listItemType = NULL) {
danielebarchiesi@4 575 foreach ($xml->children() as $name => $element) {
danielebarchiesi@4 576 // Check if we are processing an entity, an item from a list or a list.
danielebarchiesi@4 577 if ((isset($properties[$name]['type']) && (entity_property_list_extract_type($properties[$name]['type']) || entity_get_info($properties[$name]['type']))) || isset($listItemType)) {
danielebarchiesi@4 578 // If we are processing a list, then set the type of the list and save
danielebarchiesi@4 579 // the results into a a numeric array.
danielebarchiesi@4 580 if (isset($listItemType)) {
danielebarchiesi@4 581 $type = $listItemType;
danielebarchiesi@4 582 $result_pointer = &$result[];
danielebarchiesi@4 583 }
danielebarchiesi@4 584 else {
danielebarchiesi@4 585 $type = $properties[$name]['type'];
danielebarchiesi@4 586 $result_pointer = &$result[$name];
danielebarchiesi@4 587 }
danielebarchiesi@4 588
danielebarchiesi@4 589 // Check if the type is a list.
danielebarchiesi@4 590 if (entity_property_list_extract_type($type)) {
danielebarchiesi@4 591 $result_pointer = $this->xmlToArray($properties, $element, entity_property_list_extract_type($type));
danielebarchiesi@4 592 }
danielebarchiesi@4 593 else {
danielebarchiesi@4 594 $attributes = $element->attributes();
danielebarchiesi@4 595 $values['id'] = (string)$attributes['id'];
danielebarchiesi@4 596 $values['resource'] = (string)$attributes['resource'];
danielebarchiesi@4 597 $values['uri'] = $this->xmlToArray($properties, $element);
danielebarchiesi@4 598 $id = $this->getResourceReferenceValue($type, $values);
danielebarchiesi@4 599 // If an id could be extracted, then a resource array was send.
danielebarchiesi@4 600 if ($id !== FALSE) {
danielebarchiesi@4 601 $result_pointer = $id;
danielebarchiesi@4 602 }
danielebarchiesi@4 603 else {
danielebarchiesi@4 604 // If no ID could be extracted, then save the inner text content of
danielebarchiesi@4 605 // the node, which is saved in the $values['uri'].
danielebarchiesi@4 606 $result_pointer = $values['uri'];
danielebarchiesi@4 607 }
danielebarchiesi@4 608 }
danielebarchiesi@4 609 }
danielebarchiesi@4 610 else {
danielebarchiesi@4 611 $result[$name] = $this->xmlToArray($properties, $element);
danielebarchiesi@4 612 }
danielebarchiesi@4 613 foreach ($xml->attributes() as $attribute_name => $attribute_value) {
danielebarchiesi@4 614 $result[$attribute_name] = $attribute_value;
danielebarchiesi@4 615 }
danielebarchiesi@4 616 }
danielebarchiesi@4 617 if (!isset($result)) {
danielebarchiesi@4 618 $result = ($string = (string) $xml) ? $string : NULL;
danielebarchiesi@4 619 }
danielebarchiesi@4 620 return $result;
danielebarchiesi@4 621 }
danielebarchiesi@4 622
danielebarchiesi@4 623 /**
danielebarchiesi@4 624 * Adds the data of the given wrapper to the given XML element.
danielebarchiesi@4 625 */
danielebarchiesi@4 626 public static function addToXML(DOMDocument $doc, DOMNode $parent, $wrapper) {
danielebarchiesi@4 627 $filtered = restws_property_access_filter($wrapper);
danielebarchiesi@4 628 foreach ($filtered as $name => $property) {
danielebarchiesi@4 629 try {
danielebarchiesi@4 630 if ($property instanceof EntityDrupalWrapper) {
danielebarchiesi@4 631 // For referenced entities only return the URI.
danielebarchiesi@4 632 if ($id = $property->getIdentifier()) {
danielebarchiesi@4 633 $element = $doc->createElement(is_numeric($name) ? 'item' : $name);
danielebarchiesi@4 634 $parent->appendChild($element);
danielebarchiesi@4 635 self::setXMLReference($element, $property->type(), $id);
danielebarchiesi@4 636 }
danielebarchiesi@4 637 }
danielebarchiesi@4 638 elseif ($property instanceof EntityValueWrapper) {
danielebarchiesi@4 639 // Only primitive data types are allowed here. There might be complex
danielebarchiesi@4 640 // arrays/objects in EntityValueWrapper if no property information is
danielebarchiesi@4 641 // provided (example: the "data" property of commerce_price fields.
danielebarchiesi@4 642 if (is_scalar($property->value())) {
danielebarchiesi@4 643 $escaped = $doc->createTextNode($property->value());
danielebarchiesi@4 644 $element = $doc->createElement(is_numeric($name) ? 'item' : $name);
danielebarchiesi@4 645 $element->appendChild($escaped);
danielebarchiesi@4 646 $parent->appendChild($element);
danielebarchiesi@4 647 }
danielebarchiesi@4 648 }
danielebarchiesi@4 649 elseif ($property instanceof EntityListWrapper || $property instanceof EntityStructureWrapper) {
danielebarchiesi@4 650 $element = $doc->createElement(is_numeric($name) ? 'item' : $name);
danielebarchiesi@4 651 $parent->appendChild($element);
danielebarchiesi@4 652 self::addToXML($doc, $element, $property);
danielebarchiesi@4 653 }
danielebarchiesi@4 654 }
danielebarchiesi@4 655 catch (EntityMetadataWrapperException $e) {
danielebarchiesi@4 656 // A property causes problems - ignore that.
danielebarchiesi@4 657 }
danielebarchiesi@4 658 }
danielebarchiesi@4 659 }
danielebarchiesi@4 660
danielebarchiesi@4 661 public static function setXMLReference(DOMElement $node, $resource, $id) {
danielebarchiesi@4 662 $node->nodeValue = restws_resource_uri($resource, $id);
danielebarchiesi@4 663 $node->setAttribute('resource', $resource);
danielebarchiesi@4 664 $node->setAttribute('id', $id);
danielebarchiesi@4 665 }
danielebarchiesi@4 666 }
danielebarchiesi@4 667
danielebarchiesi@4 668 /**
danielebarchiesi@4 669 * A simple formatter for RDF. Requires the RDF module for the mapping.
danielebarchiesi@4 670 */
danielebarchiesi@4 671 class RestWSFormatRDF extends RestWSBaseFormat {
danielebarchiesi@4 672
danielebarchiesi@4 673 protected $namespaces;
danielebarchiesi@4 674
danielebarchiesi@4 675 public function __construct($name, $info) {
danielebarchiesi@4 676 parent::__construct($name, $info);
danielebarchiesi@4 677 $this->namespaces = rdf_get_namespaces();
danielebarchiesi@4 678 $this->namespaces['rdf'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
danielebarchiesi@4 679 }
danielebarchiesi@4 680
danielebarchiesi@4 681 /**
danielebarchiesi@4 682 * Gets the representation of a resource.
danielebarchiesi@4 683 */
danielebarchiesi@4 684 public function viewResource($resourceController, $id) {
danielebarchiesi@4 685 $xml = new DOMDocument('1.0', 'utf-8');
danielebarchiesi@4 686 $rdf_element = $xml->createElementNS($this->namespaces['rdf'], 'rdf:RDF');
danielebarchiesi@4 687 $xml->appendChild($rdf_element);
danielebarchiesi@4 688
danielebarchiesi@4 689 $element = $xml->createElementNS($this->namespaces['rdf'], 'rdf:Description');
danielebarchiesi@4 690 $element->setAttributeNS($this->namespaces['rdf'], 'rdf:about', restws_resource_uri($resourceController->resource(), $id));
danielebarchiesi@4 691
danielebarchiesi@4 692 // Add the RDF type of the resource if there is a mapping.
danielebarchiesi@4 693 $entity = $resourceController->read($id);
danielebarchiesi@4 694 if (!empty($entity->rdf_mapping['rdftype'])) {
danielebarchiesi@4 695 foreach ($entity->rdf_mapping['rdftype'] as $rdf_type) {
danielebarchiesi@4 696 $type_element = $xml->createElementNS($this->namespaces['rdf'], 'rdf:type');
danielebarchiesi@4 697 list($ns, $name) = explode(':', $rdf_type);
danielebarchiesi@4 698 $type_element->setAttributeNS($this->namespaces['rdf'], 'rdf:resource', $this->namespaces[$ns] . $name);
danielebarchiesi@4 699 $element->appendChild($type_element);
danielebarchiesi@4 700 }
danielebarchiesi@4 701 }
danielebarchiesi@4 702
danielebarchiesi@4 703 $this->addToXML($xml, $element, $resourceController->wrapper($id));
danielebarchiesi@4 704 $rdf_element->appendChild($element);
danielebarchiesi@4 705
danielebarchiesi@4 706 $function = __FUNCTION__;
danielebarchiesi@4 707 drupal_alter('restws_response', $xml, $function, $this->formatName);
danielebarchiesi@4 708
danielebarchiesi@4 709 return $xml->saveXML();
danielebarchiesi@4 710 }
danielebarchiesi@4 711
danielebarchiesi@4 712 public function createResource($resourceController, $data) {
danielebarchiesi@4 713 throw new RestWSException('Not implemented', 501);
danielebarchiesi@4 714 }
danielebarchiesi@4 715
danielebarchiesi@4 716 public function updateResource($resourceController, $id, $data) {
danielebarchiesi@4 717 throw new RestWSException('Not implemented', 501);
danielebarchiesi@4 718 }
danielebarchiesi@4 719
danielebarchiesi@4 720 public function queryResource($resourceController, $parameters) {
danielebarchiesi@4 721 throw new RestWSException('Not implemented', 501);
danielebarchiesi@4 722 }
danielebarchiesi@4 723
danielebarchiesi@4 724 /**
danielebarchiesi@4 725 * Adds the data of the given wrapper to the given XML element.
danielebarchiesi@4 726 */
danielebarchiesi@4 727 public function addToXML(DOMDocument $doc, DOMNode $parent, $wrapper) {
danielebarchiesi@4 728 $filtered = restws_property_access_filter($wrapper);
danielebarchiesi@4 729 foreach ($filtered as $name => $property) {
danielebarchiesi@4 730 try {
danielebarchiesi@4 731 if ($property instanceof EntityDrupalWrapper) {
danielebarchiesi@4 732 // For referenced entities only return the URI.
danielebarchiesi@4 733 if ($id = $property->getIdentifier()) {
danielebarchiesi@4 734 $element = $this->addRdfElement($doc, $wrapper, $name);
danielebarchiesi@4 735 $parent->appendChild($element);
danielebarchiesi@4 736 $this->addReference($doc, $element, $property->type(), $id);
danielebarchiesi@4 737 }
danielebarchiesi@4 738 }
danielebarchiesi@4 739 elseif ($property instanceof EntityValueWrapper) {
danielebarchiesi@4 740 $element = $this->addRdfElement($doc, $wrapper, $name);
danielebarchiesi@4 741 $parent->appendChild($element);
danielebarchiesi@4 742 $element->nodeValue = $property->value();
danielebarchiesi@4 743 }
danielebarchiesi@4 744 elseif ($property instanceof EntityListWrapper || $property instanceof EntityStructureWrapper) {
danielebarchiesi@4 745 $element = $this->addRdfElement($doc, $wrapper, $name);
danielebarchiesi@4 746 $parent->appendChild($element);
danielebarchiesi@4 747 $node = $doc->createElementNS($this->namespaces['rdf'], 'rdf:Description');
danielebarchiesi@4 748 $element->appendChild($node);
danielebarchiesi@4 749 $this->addToXML($doc, $node, $property);
danielebarchiesi@4 750 }
danielebarchiesi@4 751 }
danielebarchiesi@4 752 catch (EntityMetadataWrapperException $e) {
danielebarchiesi@4 753 // A property causes problems - ignore that.
danielebarchiesi@4 754 }
danielebarchiesi@4 755 }
danielebarchiesi@4 756 }
danielebarchiesi@4 757
danielebarchiesi@4 758 public function addReference(DomDocument $doc, DOMElement $node, $resource, $id) {
danielebarchiesi@4 759 $element = $doc->createElementNS($this->namespaces['rdf'], 'rdf:Description');
danielebarchiesi@4 760 $element->setAttributeNS($this->namespaces['rdf'], 'rdf:about', restws_resource_uri($resource, $id));
danielebarchiesi@4 761 $node->appendChild($element);
danielebarchiesi@4 762 }
danielebarchiesi@4 763
danielebarchiesi@4 764 /**
danielebarchiesi@4 765 * Adds an RDF element for the given property of the wrapper using the RDF
danielebarchiesi@4 766 * mapping.
danielebarchiesi@4 767 */
danielebarchiesi@4 768 public function addRdfElement(DOMDOcument $doc, EntityMetadataWrapper $wrapper, $name) {
danielebarchiesi@4 769 if ($wrapper instanceof EntityDrupalWrapper) {
danielebarchiesi@4 770 $entity = $wrapper->value();
danielebarchiesi@4 771 if (!empty($entity->rdf_mapping[$name])) {
danielebarchiesi@4 772 // Just make use of the first predicate for now.
danielebarchiesi@4 773 $predicate = reset($entity->rdf_mapping[$name]['predicates']);
danielebarchiesi@4 774 list($ns, $qname) = explode(':', $predicate);
danielebarchiesi@4 775 $element = $doc->createElementNS($this->namespaces[$ns], $predicate);
danielebarchiesi@4 776
danielebarchiesi@4 777 if (!empty($entity->rdf_mapping[$name]['datatype'])) {
danielebarchiesi@4 778 $element->setAttributeNS($this->namespaces['rdf'], 'rdf:datatype', $entity->rdf_mapping[$name]['datatype']);
danielebarchiesi@4 779 }
danielebarchiesi@4 780 }
danielebarchiesi@4 781 }
danielebarchiesi@4 782 if (!isset($element)) {
danielebarchiesi@4 783 // For other elements just use the site URL as namespace.
danielebarchiesi@4 784 $element = $doc->createElementNS(url('', array('absolute' => TRUE)), 'site:' . (is_numeric($name) ? 'item' : $name));
danielebarchiesi@4 785 }
danielebarchiesi@4 786 return $element;
danielebarchiesi@4 787 }
danielebarchiesi@4 788 }