You are here

restws.entity.inc in RESTful Web Services 7

Same filename and directory in other branches
  1. 7.2 restws.entity.inc

RESTful web services module integration for entities.

File

restws.entity.inc
View source
<?php

/**
 * @file
 * RESTful web services module integration for entities.
 */

/**
 * Specifies CRUD and access methods for resources.
 */
interface RestWSResourceControllerInterface {

  /**
   * Returns the property info for the given resource.
   *
   * @return array
   *   An array structured as hook_entity_property_info() is structured for an
   *   entity type.
   */
  public function propertyInfo();

  /**
   * Returns a metadata wrapper for the resource with the given id.
   *
   * @return EntityStructureWrapper
   *   Metadata wrapper of the resource.
   */
  public function wrapper($id);

  /**
   * Create a new resource.
   *
   * @param array $values
   *   Array of values for properties of the resource, keyed by property
   *   name. At least for all required properties values have to be given.
   *
   * @return int|string
   *   The id of the newly created resource.
   */
  public function create(array $values);

  /**
   * Returns an existing resource.
   *
   * @param int|string $id
   *   The id of the resource that should be returned.
   *
   * @return
   *   The internal representation of the resource.
   */
  public function read($id);

  /**
   * Update an existing resource.
   *
   * @param int|string $id
   *   The id of the resource that should be updated.
   * @param array $values
   *   An array of values for the properties to be updated, keyed by property
   *   name.
   */
  public function update($id, array $values);

  /**
   * Delete an existing resource.
   *
   * @param int|string $id
   *   The id of the resource that should be deleted.
   */
  public function delete($id);

  /**
   * Determines access for a given operation and resource.
   *
   * @param string $op
   *   Either 'create', 'view' (= read), 'update' or 'delete'.
   * @param int|string $id
   *   The id of the resource.
   *
   * @see entity_access()
   */
  public function access($op, $id);

  /**
   * Returns the name of the resource.
   */
  public function resource();

}

/**
 * Controller for entity-bases resources.
 */
class RestWSEntityResourceController implements RestWSResourceControllerInterface {
  protected $entityType, $entityInfo;
  public function __construct($name, $info) {
    $this->entityType = $name;
    $this->entityInfo = entity_get_info($name);
  }
  public function propertyInfo() {
    return entity_get_property_info($this->entityType);
  }
  public function wrapper($id) {
    return entity_metadata_wrapper($this->entityType, $id);
  }
  public function read($id) {
    return $this
      ->wrapper($id)
      ->value();
  }
  public function create(array $values) {

    // Make sure that bundle information is present on entities that have
    // bundles.
    $entity_info = entity_get_info($this->entityType);
    if (isset($entity_info['bundle keys'])) {
      foreach ($entity_info['bundle keys'] as $bundle_key) {
        if (!array_key_exists($bundle_key, $values)) {
          throw new RestWSException('Missing bundle: ' . $bundle_key, 406);
        }
      }
    }
    try {
      $wrapper = entity_property_values_create_entity($this->entityType, $values);

      // Get the ID and bundle property names.
      $entity_keys = array_intersect_key($entity_info['entity keys'], array(
        'id' => 1,
        'bundle' => 1,
      ));
      foreach (array_keys($values) as $name) {

        // Don't check access on entity keys for new entities. Otherwise,
        // property access checks will fail for, e.g., node type, which
        // requires the 'administer nodes' permission to set.
        // @see entity_metadata_node_entity_property_info().
        if (!in_array($name, $entity_keys)) {
          if (!$this
            ->checkPropertyAccess($wrapper, $name, $wrapper->{$name})) {
            throw new RestWSException(t('Not authorized to set property @p', array(
              '@p' => $name,
            )), 403);
          }
        }
      }
    } catch (EntityMetadataWrapperException $e) {
      throw new RestWSException($e
        ->getMessage(), 406);
    }
    $properties = $wrapper
      ->getPropertyInfo();
    $diff = array_diff_key($values, $properties);
    if (!empty($diff)) {
      throw new RestWSException('Unknown data properties: ' . implode(' ', array_keys($diff)) . '.', 406);
    }
    $wrapper
      ->save();
    return $wrapper
      ->getIdentifier();
  }
  public function update($id, array $values) {
    $wrapper = $this
      ->wrapper($id);
    $entity_info = $wrapper
      ->entityInfo();

    // Get the ID and bundle property names.
    $entity_keys = array_intersect_key($entity_info['entity keys'], array(
      'id' => 1,
      'bundle' => 1,
    ));
    try {
      foreach ($values as $name => $value) {
        if (in_array($name, $entity_keys)) {

          // We don't allow changing the entity ID or bundle.
          if ($wrapper->{$name}
            ->value() != $value) {
            throw new RestWSException('Unable to change ' . $name, 422);
          }
        }
        else {
          $wrapper->{$name}
            ->set($value);
          if (!$this
            ->checkPropertyAccess($wrapper, $name, $wrapper->{$name})) {
            throw new RestWSException(t('Not authorized to set property @p', array(
              '@p' => $name,
            )), 403);
          }
        }
      }
    } catch (EntityMetadataWrapperException $e) {
      throw new RestWSException($e
        ->getMessage(), 406);
    }
    $wrapper
      ->save();
  }
  public function delete($id) {
    entity_delete($this->entityType, $id);
  }
  public function access($op, $id) {
    return entity_access($op, $this->entityType, isset($id) ? $this
      ->wrapper($id)
      ->value() : NULL);
  }
  public function resource() {
    return $this->entityType;
  }

  /**
   * Helper method to check access on a property.
   *
   * @todo Remove this once Entity API properly handles text format access.
   *
   * @param EntityMetadataWrapper $entity
   *   The parent entity.
   * @param string $property_name
   *   The property name on the entity.
   * @param EntityMetadataWrapper $property
   *   The property whose access is to be checked.
   *
   * @return bool
   *   TRUE if the current user has access to set the property, FALSE otherwise.
   */
  protected function checkPropertyAccess($entity, $property_name, $property) {
    global $user;

    // Special case node author: we allow access if set to the current user.
    if ($entity
      ->type() == 'node' && $property_name == 'author' && $property
      ->raw() == $GLOBALS['user']->uid) {
      return TRUE;
    }
    elseif ($property
      ->type() == 'text_formatted' && $property->format
      ->value()) {
      $format = (object) array(
        'format' => $property->format
          ->value(),
      );
      if (!filter_access($format)) {
        return FALSE;
      }
    }
    return $property
      ->access('edit');
  }

}

Classes

Namesort descending Description
RestWSEntityResourceController Controller for entity-bases resources.

Interfaces

Namesort descending Description
RestWSResourceControllerInterface Specifies CRUD and access methods for resources.