danielebarchiesi@4: $info) { danielebarchiesi@4: $result[$entity_type] = array( danielebarchiesi@4: 'label' => $info['label'], danielebarchiesi@4: 'class' => 'RestWSEntityResourceController', danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: return $result; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Returns a instance of a resource controller. danielebarchiesi@4: * danielebarchiesi@4: * @return RestWSResourceControllerInterface danielebarchiesi@4: * A resource controller object. danielebarchiesi@4: */ danielebarchiesi@4: function restws_resource_controller($name) { danielebarchiesi@4: $static = &drupal_static(__FUNCTION__); danielebarchiesi@4: if (!isset($static[$name])) { danielebarchiesi@4: $info = restws_get_resource_info(); danielebarchiesi@4: $static[$name] = isset($info[$name]) ? new $info[$name]['class']($name, $info[$name]) : FALSE; danielebarchiesi@4: } danielebarchiesi@4: return $static[$name]; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_restws_format_info(). danielebarchiesi@4: * danielebarchiesi@4: * Provides basic formats. danielebarchiesi@4: */ danielebarchiesi@4: function restws_restws_format_info() { danielebarchiesi@4: $result = array( danielebarchiesi@4: 'json' => array( danielebarchiesi@4: 'label' => t('JSON'), danielebarchiesi@4: 'class' => 'RestWSFormatJSON', danielebarchiesi@4: 'mime type' => 'application/json', danielebarchiesi@4: ), danielebarchiesi@4: 'xml' => array( danielebarchiesi@4: 'label' => t('XML'), danielebarchiesi@4: 'class' => 'RestWSFormatXML', danielebarchiesi@4: 'mime type' => 'application/xml', danielebarchiesi@4: ), danielebarchiesi@4: ); danielebarchiesi@4: if (module_exists('rdf')) { danielebarchiesi@4: $result['rdf'] = array( danielebarchiesi@4: 'label' => t('RDF'), danielebarchiesi@4: 'class' => 'RestWSFormatRDF', danielebarchiesi@4: 'mime type' => 'application/rdf+xml', danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: return $result; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Returns an instance of a format. danielebarchiesi@4: * danielebarchiesi@4: * @return RestWSFormatInterface danielebarchiesi@4: * A resource format object. danielebarchiesi@4: */ danielebarchiesi@4: function restws_format($name) { danielebarchiesi@4: $static = &drupal_static(__FUNCTION__); danielebarchiesi@4: if (!isset($static[$name])) { danielebarchiesi@4: $info = restws_get_format_info(); danielebarchiesi@4: $static[$name] = isset($info[$name]) ? new $info[$name]['class']($name, $info[$name]) : FALSE; danielebarchiesi@4: } danielebarchiesi@4: return $static[$name]; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Handles a request. danielebarchiesi@4: * danielebarchiesi@4: * @param string $op danielebarchiesi@4: * One of 'create', 'update', 'delete' or 'view'. danielebarchiesi@4: */ danielebarchiesi@4: function restws_handle_request($op, $format, $resource_name, $id = NULL, $payload = NULL) { danielebarchiesi@4: if ($resource = restws_resource_controller($resource_name)) { danielebarchiesi@4: // Allow other modules to change the web service request or react upon it. danielebarchiesi@4: $request = array( danielebarchiesi@4: 'op' => &$op, danielebarchiesi@4: 'format' => &$format, danielebarchiesi@4: 'resource' => &$resource, danielebarchiesi@4: 'id' => &$id, danielebarchiesi@4: 'payload' => &$payload, danielebarchiesi@4: ); danielebarchiesi@4: drupal_alter('restws_request', $request); danielebarchiesi@4: danielebarchiesi@4: // Since there is no access callback for query we need to use view. danielebarchiesi@4: $access_op = $op == 'query' ? 'view' : $op; danielebarchiesi@4: danielebarchiesi@4: if (user_access('access resource ' . $resource_name) && $resource->access($access_op, $id)) { danielebarchiesi@4: try { danielebarchiesi@4: $method = $op . 'Resource'; danielebarchiesi@4: if ($op == 'create') { danielebarchiesi@4: print $format->$method($resource, $payload); danielebarchiesi@4: drupal_add_http_header('Status', '201 Created'); danielebarchiesi@4: } danielebarchiesi@4: elseif ($op == 'query') { danielebarchiesi@4: if (!$resource instanceof RestWSQueryResourceControllerInterface) { danielebarchiesi@4: throw new RestWSException('Quering not available for this resources', 501); danielebarchiesi@4: } danielebarchiesi@4: print $format->$method($resource, $payload); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: print $format->$method($resource, $id, $payload); danielebarchiesi@4: } danielebarchiesi@4: drupal_add_http_header('Content-Type', $format->mimeType()); danielebarchiesi@4: } danielebarchiesi@4: catch (RestWSException $e) { danielebarchiesi@4: echo check_plain($e->getHTTPError()) . ': ' . check_plain($e->getMessage()); danielebarchiesi@4: drupal_add_http_header('Status', $e->getHTTPError()); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: echo '403 Forbidden'; danielebarchiesi@4: drupal_add_http_header('Status', '403 Forbidden'); danielebarchiesi@4: watchdog('access denied', check_plain($_GET['q']), NULL, WATCHDOG_WARNING); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: echo '404 Not Found'; danielebarchiesi@4: drupal_add_http_header('Status', '404 Not Found'); danielebarchiesi@4: } danielebarchiesi@4: drupal_page_footer(); danielebarchiesi@4: exit; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * An exception defining the HTTP error code and message. danielebarchiesi@4: */ danielebarchiesi@4: class RestWSException extends Exception { danielebarchiesi@4: danielebarchiesi@4: public function getHTTPError() { danielebarchiesi@4: $code = $this->getCode(); danielebarchiesi@4: switch ($code) { danielebarchiesi@4: case 403: danielebarchiesi@4: return '403 Forbidden'; danielebarchiesi@4: case 404: danielebarchiesi@4: return '404 Not Found'; danielebarchiesi@4: case 406: danielebarchiesi@4: return '406 Not Acceptable'; danielebarchiesi@4: case 412: danielebarchiesi@4: return '412 Precondition Failed'; danielebarchiesi@4: case 422: danielebarchiesi@4: return '422 Unprocessable Entity'; danielebarchiesi@4: default: danielebarchiesi@4: return '500 Internal Server Error'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_menu_alter(). danielebarchiesi@4: */ danielebarchiesi@4: function restws_menu_alter(&$items) { danielebarchiesi@4: foreach (restws_get_resource_info() as $resource => $info) { danielebarchiesi@4: // Resource full path (e.g. /node/% or /user/%) for accessing specific danielebarchiesi@4: // resources. danielebarchiesi@4: $menu_path = isset($info['menu_path']) ? $info['menu_path'] . '/%' : $resource . '/%'; danielebarchiesi@4: // Replace existing page callbacks with our own (e.g. node/%) danielebarchiesi@4: if (isset($items[$menu_path])) { danielebarchiesi@4: // Prepend the page callback and the resource to the page arguments. danielebarchiesi@4: // So we can re-use it on standard HTML page requests. danielebarchiesi@4: array_unshift($items[$menu_path]['page arguments'], $resource, $items[$menu_path]['page callback']); danielebarchiesi@4: $items[$menu_path]['page callback'] = 'restws_page_callback'; danielebarchiesi@4: } danielebarchiesi@4: // Also replace wildcard loaders (e.g. node/%node) danielebarchiesi@4: elseif (isset($items[$menu_path . $resource])) { danielebarchiesi@4: $menu_path = $menu_path . $resource; danielebarchiesi@4: array_unshift($items[$menu_path]['page arguments'], $resource, $items[$menu_path]['page callback']); danielebarchiesi@4: $items[$menu_path]['page callback'] = 'restws_page_callback'; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $items[$menu_path] = array( danielebarchiesi@4: 'page callback' => 'restws_page_callback', danielebarchiesi@4: 'page arguments' => array($resource), danielebarchiesi@4: 'access callback' => TRUE, danielebarchiesi@4: 'type' => MENU_CALLBACK, danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: // Resource base path (e.g. /node or /user) for creating resources. danielebarchiesi@4: $menu_path = isset($info['menu_path']) ? substr($menu_path, 0, strlen($menu_path) - 2) : $resource; danielebarchiesi@4: danielebarchiesi@4: if (isset($items[$menu_path])) { danielebarchiesi@4: // Prepend the page callback and the resource to the page arguments. danielebarchiesi@4: if (!isset($items[$menu_path]['page arguments'])) { danielebarchiesi@4: $items[$menu_path]['page arguments'] = array(); danielebarchiesi@4: } danielebarchiesi@4: array_unshift($items[$menu_path]['page arguments'], $resource, $items[$menu_path]['page callback']); danielebarchiesi@4: $items[$menu_path]['page callback'] = 'restws_page_callback'; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $items[$menu_path] = array( danielebarchiesi@4: 'page callback' => 'restws_page_callback', danielebarchiesi@4: 'page arguments' => array($resource), danielebarchiesi@4: 'access callback' => TRUE, danielebarchiesi@4: 'type' => MENU_CALLBACK, danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: // Querying menu paths. danielebarchiesi@4: foreach (array_keys(restws_get_format_info()) as $format) { danielebarchiesi@4: // Resource base path URLs with the suffixes (e.g. node.json or user.xml) danielebarchiesi@4: // for querying. danielebarchiesi@4: if (isset($items["$menu_path.$format"])) { danielebarchiesi@4: // Prepend the page callback and the resource to the page arguments. danielebarchiesi@4: if (!isset($items["$menu_path.$format"]['page arguments'])) { danielebarchiesi@4: $items["$menu_path.$format"]['page arguments'] = array(); danielebarchiesi@4: } danielebarchiesi@4: array_unshift($items["$menu_path.$format"]['page arguments'], $resource, $items["$menu_path.$format"]['page callback']); danielebarchiesi@4: $items["$menu_path.$format"]['page callback'] = 'restws_page_callback'; danielebarchiesi@4: danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $items["$menu_path.$format"] = array( danielebarchiesi@4: 'page callback' => 'restws_page_callback', danielebarchiesi@4: 'page arguments' => array($resource), danielebarchiesi@4: 'access callback' => TRUE, danielebarchiesi@4: 'type' => MENU_CALLBACK, danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Menu page callback. danielebarchiesi@4: */ danielebarchiesi@4: function restws_page_callback($resource, $page_callback = NULL) { danielebarchiesi@4: $id_arg = arg(1); danielebarchiesi@4: $resource_arg = arg(0); danielebarchiesi@4: $format = FALSE; danielebarchiesi@4: $id = NULL; danielebarchiesi@4: // Check for an appended .format string on GET requests only to avoid CSRF danielebarchiesi@4: // attacks on POST requests. danielebarchiesi@4: if ($_SERVER['REQUEST_METHOD'] == 'GET' && ($pos = strpos($id_arg, '.')) && $format_name = substr($id_arg, $pos + 1)) { danielebarchiesi@4: $id = substr($id_arg, 0, $pos); danielebarchiesi@4: $format = restws_format($format_name); danielebarchiesi@4: } danielebarchiesi@4: elseif ($_SERVER['REQUEST_METHOD'] == 'GET' && ($pos = strpos($resource_arg, '.')) && $format_name = substr($resource_arg, $pos + 1)) { danielebarchiesi@4: $format = restws_format($format_name); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $id = $id_arg; danielebarchiesi@4: switch ($_SERVER['REQUEST_METHOD']) { danielebarchiesi@4: case 'POST': danielebarchiesi@4: case 'PUT': danielebarchiesi@4: // Get format MIME type form HTTP Content type header. danielebarchiesi@4: $parts = explode(';', $_SERVER['CONTENT_TYPE'], 2); danielebarchiesi@4: $format = restws_format_mimetype($parts[0]); danielebarchiesi@4: break; danielebarchiesi@4: danielebarchiesi@4: case 'DELETE': danielebarchiesi@4: if (isset($_SERVER['HTTP_ACCEPT'])) { danielebarchiesi@4: $parts = explode(',', $_SERVER['HTTP_ACCEPT'], 2); danielebarchiesi@4: $format = restws_format_mimetype($parts[0]); danielebarchiesi@4: } danielebarchiesi@4: if (!$format) { danielebarchiesi@4: // We don't care about the format, just pick JSON. danielebarchiesi@4: $format = restws_format('json'); danielebarchiesi@4: } danielebarchiesi@4: break; danielebarchiesi@4: danielebarchiesi@4: default: danielebarchiesi@4: // Get the format MIME type form the HTTP Accept header. danielebarchiesi@4: // Ignore requests from web browsers that accept HTML. danielebarchiesi@4: if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'html') === FALSE) { danielebarchiesi@4: // Use the first MIME type. danielebarchiesi@4: $parts = explode(',', $_SERVER['HTTP_ACCEPT'], 2); danielebarchiesi@4: $format = restws_format_mimetype($parts[0]); danielebarchiesi@4: } danielebarchiesi@4: // Consumers should not use this URL if page caching is enabled. danielebarchiesi@4: // Drupal's page cache IDs are only determined by URL path, so this danielebarchiesi@4: // could poison the HTML page cache. A browser request to /node/1 could danielebarchiesi@4: // suddenly return JSON if the cache was primed with this RESTWS danielebarchiesi@4: // response. danielebarchiesi@4: if ($format && !isset($_COOKIE[session_name()]) && variable_get('cache')) { danielebarchiesi@4: // Redirect to the URL path containing the format name instead. danielebarchiesi@4: drupal_goto($_GET['q'] . '.' . $format->getName(), array(), 301); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if ($format) { danielebarchiesi@4: switch ($_SERVER['REQUEST_METHOD']) { danielebarchiesi@4: case 'POST': danielebarchiesi@4: $op = 'create'; danielebarchiesi@4: break; danielebarchiesi@4: danielebarchiesi@4: case 'PUT': danielebarchiesi@4: $op = 'update'; danielebarchiesi@4: break; danielebarchiesi@4: danielebarchiesi@4: case 'DELETE': danielebarchiesi@4: $op = 'delete'; danielebarchiesi@4: break; danielebarchiesi@4: danielebarchiesi@4: default: danielebarchiesi@4: if (!empty($id)) { danielebarchiesi@4: $op = 'view'; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $op = 'query'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // CSRF protection on write operations. danielebarchiesi@4: if (!in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD', 'OPTIONS', 'TRACE')) && !restws_csrf_validation()) { danielebarchiesi@4: echo '403 Access Denied: CSRF validation failed'; danielebarchiesi@4: drupal_add_http_header('Status', '403 Forbidden'); danielebarchiesi@4: drupal_page_footer(); danielebarchiesi@4: exit; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: $payload = file_get_contents('php://input'); danielebarchiesi@4: if ($file = variable_get('restws_debug_log')) { danielebarchiesi@4: $log = date(DATE_ISO8601) . "\n"; danielebarchiesi@4: $log .= 'Resource: ' . $resource . "\n"; danielebarchiesi@4: $log .= 'Operation: ' . $op . "\n"; danielebarchiesi@4: $log .= 'Format: ' . $format->mimeType() . "\n"; danielebarchiesi@4: $log .= 'Id: ' . $id . "\n"; danielebarchiesi@4: $log .= 'Payload: ' . $payload . "\n"; danielebarchiesi@4: $log .= "----------------------------------------------------------------\n"; danielebarchiesi@4: file_put_contents($file, $log, FILE_APPEND); danielebarchiesi@4: } danielebarchiesi@4: restws_handle_request($op, $format, $resource, $id, $payload); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // @todo: Determine human readable URIs and redirect, if there is no danielebarchiesi@4: // page callback. danielebarchiesi@4: if (isset($page_callback)) { danielebarchiesi@4: // Further page callback arguments have been appended to our arguments. danielebarchiesi@4: $args = func_get_args(); danielebarchiesi@4: return call_user_func_array($page_callback, array_slice($args, 2)); danielebarchiesi@4: } danielebarchiesi@4: echo '404 Not Found'; danielebarchiesi@4: drupal_add_http_header('Status', '404 Not Found'); danielebarchiesi@4: drupal_page_footer(); danielebarchiesi@4: exit; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Returns the URI used for the given resource. danielebarchiesi@4: * danielebarchiesi@4: * @param string $resource danielebarchiesi@4: * The resource for which the URI should be returned. danielebarchiesi@4: * @param int $id danielebarchiesi@4: * The resource ID or NULL if only the base path should be returned. danielebarchiesi@4: * @param array $options danielebarchiesi@4: * Optional array that is passed to url(). danielebarchiesi@4: */ danielebarchiesi@4: function restws_resource_uri($resource, $id = NULL, array $options = array()) { danielebarchiesi@4: $info = restws_get_resource_info($resource); danielebarchiesi@4: $basepath = isset($info['menu_path']) ? $info['menu_path'] : $resource; danielebarchiesi@4: $sub_path = isset($id) ? "/$id" : ''; danielebarchiesi@4: danielebarchiesi@4: // Avoid having the URLs aliased. danielebarchiesi@4: $base_options = array('absolute' => TRUE, 'alias' => TRUE); danielebarchiesi@4: $options += $base_options; danielebarchiesi@4: danielebarchiesi@4: return url($basepath . $sub_path, $options); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Returns the format instance for a given MIME type. danielebarchiesi@4: * danielebarchiesi@4: * @param string $mime danielebarchiesi@4: * The MIME type, e.g. 'application/json' or 'application/xml'. danielebarchiesi@4: * danielebarchiesi@4: * @return bool|RestWSFormatInterface danielebarchiesi@4: * The format controller or FALSE if the format was not found. danielebarchiesi@4: */ danielebarchiesi@4: function restws_format_mimetype($mime) { danielebarchiesi@4: foreach (restws_get_format_info() as $format_name => $info) { danielebarchiesi@4: if ($info['mime type'] == $mime) { danielebarchiesi@4: return restws_format($format_name); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return FALSE; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_permission(). danielebarchiesi@4: */ danielebarchiesi@4: function restws_permission() { danielebarchiesi@4: $permissions = array(); danielebarchiesi@4: // Create service access permissions per resource type. danielebarchiesi@4: foreach (restws_get_resource_info() as $type => $info) { danielebarchiesi@4: $permissions['access resource ' . $type] = array( danielebarchiesi@4: 'title' => t('Access the resource %resource', array('%resource' => $type)), danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: return $permissions; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_module_implements_alter(). danielebarchiesi@4: */ danielebarchiesi@4: function restws_module_implements_alter(&$implementations, $hook) { danielebarchiesi@4: // Make sure that restws runs last. danielebarchiesi@4: // @todo remove entity_info_alter once https://drupal.org/node/1780646 is fixed. danielebarchiesi@4: if ($hook == 'menu_alter' || $hook == 'entity_info_alter') { danielebarchiesi@4: $group = $implementations['restws']; danielebarchiesi@4: unset($implementations['restws']); danielebarchiesi@4: $implementations['restws'] = $group; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Return all available meta controls. danielebarchiesi@4: */ danielebarchiesi@4: function restws_meta_controls() { danielebarchiesi@4: return array( danielebarchiesi@4: 'sort' => 'sort', danielebarchiesi@4: 'direction' => 'direction', danielebarchiesi@4: 'page' => 'page', danielebarchiesi@4: 'limit' => 'limit', danielebarchiesi@4: 'full' => 'full', danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Ensures that a request with cookies has the required CSRF header set. danielebarchiesi@4: * danielebarchiesi@4: * @return bool danielebarchiesi@4: * TRUE if the request passed the CSRF protection, FALSE otherwise. danielebarchiesi@4: */ danielebarchiesi@4: function restws_csrf_validation() { danielebarchiesi@4: // This check only applies if the user was successfully authenticated and the danielebarchiesi@4: // request comes with a session cookie. danielebarchiesi@4: if (user_is_logged_in() && !empty($_COOKIE[session_name()])) { danielebarchiesi@4: return isset($_SERVER['HTTP_X_CSRF_TOKEN']) && drupal_valid_token($_SERVER['HTTP_X_CSRF_TOKEN'], 'restws'); danielebarchiesi@4: } danielebarchiesi@4: return TRUE; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_menu(). danielebarchiesi@4: */ danielebarchiesi@4: function restws_menu() { danielebarchiesi@4: $items['restws/session/token'] = array( danielebarchiesi@4: 'page callback' => 'restws_session_token', danielebarchiesi@4: // Only authenticated users are allowed to retrieve a session token. danielebarchiesi@4: 'access callback' => 'user_is_logged_in', danielebarchiesi@4: 'type' => MENU_CALLBACK, danielebarchiesi@4: ); danielebarchiesi@4: return $items; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Page callback: returns a session token for the currently active user. danielebarchiesi@4: */ danielebarchiesi@4: function restws_session_token() { danielebarchiesi@4: drupal_add_http_header('Content-Type', 'text/plain'); danielebarchiesi@4: print drupal_get_token('restws'); danielebarchiesi@4: drupal_exit(); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Access callback for the node entity. danielebarchiesi@4: * danielebarchiesi@4: * Replacement for entity_metadata_no_hook_node_access() because it does not danielebarchiesi@4: * work with the create operation. danielebarchiesi@4: * danielebarchiesi@4: * @todo Remove this once https://drupal.org/node/1780646 is fixed. danielebarchiesi@4: * danielebarchiesi@4: * @see restws_entity_info_alter() danielebarchiesi@4: * @see entity_metadata_no_hook_node_access() danielebarchiesi@4: */ danielebarchiesi@4: function restws_entity_node_access($op, $node = NULL, $account = NULL) { danielebarchiesi@4: // First deal with the case where a $node is provided. danielebarchiesi@4: if (isset($node)) { danielebarchiesi@4: // Ugly hack to handle field access, because entity_api does not distinguish danielebarchiesi@4: // between 'create' and 'update' permissions for fields. This should rather danielebarchiesi@4: // be fixed in EntityStructureWrapper::propertyAccess() (entity.wrapper.inc). danielebarchiesi@4: if ($op == 'update' && empty($node->nid)) { danielebarchiesi@4: $op = 'create'; danielebarchiesi@4: } danielebarchiesi@4: if ($op == 'create') { danielebarchiesi@4: if (isset($node->type)) { danielebarchiesi@4: return node_access($op, $node->type, $account); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: throw new EntityMalformedException('Permission to create a node was requested but no node type was given.'); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: // If a non-default revision is given, incorporate revision access. danielebarchiesi@4: $default_revision = node_load($node->nid); danielebarchiesi@4: if ($node->vid !== $default_revision->vid) { danielebarchiesi@4: return _node_revision_access($node, $op, $account); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: return node_access($op, $node, $account); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: // No node is provided. Check for access to all nodes. danielebarchiesi@4: if (user_access('bypass node access', $account)) { danielebarchiesi@4: return TRUE; danielebarchiesi@4: } danielebarchiesi@4: if (!user_access('access content', $account)) { danielebarchiesi@4: return FALSE; danielebarchiesi@4: } danielebarchiesi@4: if ($op == 'view' && node_access_view_all_nodes($account)) { danielebarchiesi@4: return TRUE; danielebarchiesi@4: } danielebarchiesi@4: return FALSE; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_entity_info_alter(). danielebarchiesi@4: * danielebarchiesi@4: * @todo Remove this once https://drupal.org/node/1780646 is fixed. danielebarchiesi@4: */ danielebarchiesi@4: function restws_entity_info_alter(&$info) { danielebarchiesi@4: // In order for this to work we have to make sure we run after entity_api. danielebarchiesi@4: // @see restws_module_implements_alter(). danielebarchiesi@4: $info['node']['access callback'] = 'restws_entity_node_access'; danielebarchiesi@4: }