annotate sites/all/modules/restws/restws.module @ 9:830c812b520f

added smtp module
author root <root@paio.local>
date Mon, 28 Oct 2013 15:34:27 +0000
parents ce11bbd8f642
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.
danielebarchiesi@4 6 */
danielebarchiesi@4 7
danielebarchiesi@4 8 /**
danielebarchiesi@4 9 * Returns info about all defined resources.
danielebarchiesi@4 10 *
danielebarchiesi@4 11 * @param string $resource
danielebarchiesi@4 12 * By default null, else the info for the given resource will be returned.
danielebarchiesi@4 13 */
danielebarchiesi@4 14 function restws_get_resource_info($resource = NULL) {
danielebarchiesi@4 15 $info = &drupal_static(__FUNCTION__);
danielebarchiesi@4 16 if (!isset($info)) {
danielebarchiesi@4 17 $info = module_invoke_all('restws_resource_info');
danielebarchiesi@4 18 drupal_alter('restws_resource_info', $info);
danielebarchiesi@4 19 }
danielebarchiesi@4 20 if (!empty($resource)) {
danielebarchiesi@4 21 return $info[$resource];
danielebarchiesi@4 22 }
danielebarchiesi@4 23 return $info;
danielebarchiesi@4 24 }
danielebarchiesi@4 25
danielebarchiesi@4 26 /**
danielebarchiesi@4 27 * Returns info about all defined formats.
danielebarchiesi@4 28 */
danielebarchiesi@4 29 function restws_get_format_info() {
danielebarchiesi@4 30 $info = &drupal_static(__FUNCTION__);
danielebarchiesi@4 31 if (!isset($info)) {
danielebarchiesi@4 32 $info = module_invoke_all('restws_format_info');
danielebarchiesi@4 33 drupal_alter('restws_format_info', $info);
danielebarchiesi@4 34 }
danielebarchiesi@4 35 return $info;
danielebarchiesi@4 36 }
danielebarchiesi@4 37
danielebarchiesi@4 38 /**
danielebarchiesi@4 39 * Implements hook_restws_resource_info().
danielebarchiesi@4 40 *
danielebarchiesi@4 41 * Provides resources for all entity types.
danielebarchiesi@4 42 */
danielebarchiesi@4 43 function restws_restws_resource_info() {
danielebarchiesi@4 44 foreach (entity_get_info() as $entity_type => $info) {
danielebarchiesi@4 45 $result[$entity_type] = array(
danielebarchiesi@4 46 'label' => $info['label'],
danielebarchiesi@4 47 'class' => 'RestWSEntityResourceController',
danielebarchiesi@4 48 );
danielebarchiesi@4 49 }
danielebarchiesi@4 50 return $result;
danielebarchiesi@4 51 }
danielebarchiesi@4 52
danielebarchiesi@4 53 /**
danielebarchiesi@4 54 * Returns a instance of a resource controller.
danielebarchiesi@4 55 *
danielebarchiesi@4 56 * @return RestWSResourceControllerInterface
danielebarchiesi@4 57 * A resource controller object.
danielebarchiesi@4 58 */
danielebarchiesi@4 59 function restws_resource_controller($name) {
danielebarchiesi@4 60 $static = &drupal_static(__FUNCTION__);
danielebarchiesi@4 61 if (!isset($static[$name])) {
danielebarchiesi@4 62 $info = restws_get_resource_info();
danielebarchiesi@4 63 $static[$name] = isset($info[$name]) ? new $info[$name]['class']($name, $info[$name]) : FALSE;
danielebarchiesi@4 64 }
danielebarchiesi@4 65 return $static[$name];
danielebarchiesi@4 66 }
danielebarchiesi@4 67
danielebarchiesi@4 68 /**
danielebarchiesi@4 69 * Implements hook_restws_format_info().
danielebarchiesi@4 70 *
danielebarchiesi@4 71 * Provides basic formats.
danielebarchiesi@4 72 */
danielebarchiesi@4 73 function restws_restws_format_info() {
danielebarchiesi@4 74 $result = array(
danielebarchiesi@4 75 'json' => array(
danielebarchiesi@4 76 'label' => t('JSON'),
danielebarchiesi@4 77 'class' => 'RestWSFormatJSON',
danielebarchiesi@4 78 'mime type' => 'application/json',
danielebarchiesi@4 79 ),
danielebarchiesi@4 80 'xml' => array(
danielebarchiesi@4 81 'label' => t('XML'),
danielebarchiesi@4 82 'class' => 'RestWSFormatXML',
danielebarchiesi@4 83 'mime type' => 'application/xml',
danielebarchiesi@4 84 ),
danielebarchiesi@4 85 );
danielebarchiesi@4 86 if (module_exists('rdf')) {
danielebarchiesi@4 87 $result['rdf'] = array(
danielebarchiesi@4 88 'label' => t('RDF'),
danielebarchiesi@4 89 'class' => 'RestWSFormatRDF',
danielebarchiesi@4 90 'mime type' => 'application/rdf+xml',
danielebarchiesi@4 91 );
danielebarchiesi@4 92 }
danielebarchiesi@4 93 return $result;
danielebarchiesi@4 94 }
danielebarchiesi@4 95
danielebarchiesi@4 96 /**
danielebarchiesi@4 97 * Returns an instance of a format.
danielebarchiesi@4 98 *
danielebarchiesi@4 99 * @return RestWSFormatInterface
danielebarchiesi@4 100 * A resource format object.
danielebarchiesi@4 101 */
danielebarchiesi@4 102 function restws_format($name) {
danielebarchiesi@4 103 $static = &drupal_static(__FUNCTION__);
danielebarchiesi@4 104 if (!isset($static[$name])) {
danielebarchiesi@4 105 $info = restws_get_format_info();
danielebarchiesi@4 106 $static[$name] = isset($info[$name]) ? new $info[$name]['class']($name, $info[$name]) : FALSE;
danielebarchiesi@4 107 }
danielebarchiesi@4 108 return $static[$name];
danielebarchiesi@4 109 }
danielebarchiesi@4 110
danielebarchiesi@4 111 /**
danielebarchiesi@4 112 * Handles a request.
danielebarchiesi@4 113 *
danielebarchiesi@4 114 * @param string $op
danielebarchiesi@4 115 * One of 'create', 'update', 'delete' or 'view'.
danielebarchiesi@4 116 */
danielebarchiesi@4 117 function restws_handle_request($op, $format, $resource_name, $id = NULL, $payload = NULL) {
danielebarchiesi@4 118 if ($resource = restws_resource_controller($resource_name)) {
danielebarchiesi@4 119 // Allow other modules to change the web service request or react upon it.
danielebarchiesi@4 120 $request = array(
danielebarchiesi@4 121 'op' => &$op,
danielebarchiesi@4 122 'format' => &$format,
danielebarchiesi@4 123 'resource' => &$resource,
danielebarchiesi@4 124 'id' => &$id,
danielebarchiesi@4 125 'payload' => &$payload,
danielebarchiesi@4 126 );
danielebarchiesi@4 127 drupal_alter('restws_request', $request);
danielebarchiesi@4 128
danielebarchiesi@4 129 // Since there is no access callback for query we need to use view.
danielebarchiesi@4 130 $access_op = $op == 'query' ? 'view' : $op;
danielebarchiesi@4 131
danielebarchiesi@4 132 if (user_access('access resource ' . $resource_name) && $resource->access($access_op, $id)) {
danielebarchiesi@4 133 try {
danielebarchiesi@4 134 $method = $op . 'Resource';
danielebarchiesi@4 135 if ($op == 'create') {
danielebarchiesi@4 136 print $format->$method($resource, $payload);
danielebarchiesi@4 137 drupal_add_http_header('Status', '201 Created');
danielebarchiesi@4 138 }
danielebarchiesi@4 139 elseif ($op == 'query') {
danielebarchiesi@4 140 if (!$resource instanceof RestWSQueryResourceControllerInterface) {
danielebarchiesi@4 141 throw new RestWSException('Quering not available for this resources', 501);
danielebarchiesi@4 142 }
danielebarchiesi@4 143 print $format->$method($resource, $payload);
danielebarchiesi@4 144 }
danielebarchiesi@4 145 else {
danielebarchiesi@4 146 print $format->$method($resource, $id, $payload);
danielebarchiesi@4 147 }
danielebarchiesi@4 148 drupal_add_http_header('Content-Type', $format->mimeType());
danielebarchiesi@4 149 }
danielebarchiesi@4 150 catch (RestWSException $e) {
danielebarchiesi@4 151 echo check_plain($e->getHTTPError()) . ': ' . check_plain($e->getMessage());
danielebarchiesi@4 152 drupal_add_http_header('Status', $e->getHTTPError());
danielebarchiesi@4 153 }
danielebarchiesi@4 154 }
danielebarchiesi@4 155 else {
danielebarchiesi@4 156 echo '403 Forbidden';
danielebarchiesi@4 157 drupal_add_http_header('Status', '403 Forbidden');
danielebarchiesi@4 158 watchdog('access denied', check_plain($_GET['q']), NULL, WATCHDOG_WARNING);
danielebarchiesi@4 159 }
danielebarchiesi@4 160 }
danielebarchiesi@4 161 else {
danielebarchiesi@4 162 echo '404 Not Found';
danielebarchiesi@4 163 drupal_add_http_header('Status', '404 Not Found');
danielebarchiesi@4 164 }
danielebarchiesi@4 165 drupal_page_footer();
danielebarchiesi@4 166 exit;
danielebarchiesi@4 167 }
danielebarchiesi@4 168
danielebarchiesi@4 169 /**
danielebarchiesi@4 170 * An exception defining the HTTP error code and message.
danielebarchiesi@4 171 */
danielebarchiesi@4 172 class RestWSException extends Exception {
danielebarchiesi@4 173
danielebarchiesi@4 174 public function getHTTPError() {
danielebarchiesi@4 175 $code = $this->getCode();
danielebarchiesi@4 176 switch ($code) {
danielebarchiesi@4 177 case 403:
danielebarchiesi@4 178 return '403 Forbidden';
danielebarchiesi@4 179 case 404:
danielebarchiesi@4 180 return '404 Not Found';
danielebarchiesi@4 181 case 406:
danielebarchiesi@4 182 return '406 Not Acceptable';
danielebarchiesi@4 183 case 412:
danielebarchiesi@4 184 return '412 Precondition Failed';
danielebarchiesi@4 185 case 422:
danielebarchiesi@4 186 return '422 Unprocessable Entity';
danielebarchiesi@4 187 default:
danielebarchiesi@4 188 return '500 Internal Server Error';
danielebarchiesi@4 189 }
danielebarchiesi@4 190 }
danielebarchiesi@4 191 }
danielebarchiesi@4 192
danielebarchiesi@4 193 /**
danielebarchiesi@4 194 * Implements hook_menu_alter().
danielebarchiesi@4 195 */
danielebarchiesi@4 196 function restws_menu_alter(&$items) {
danielebarchiesi@4 197 foreach (restws_get_resource_info() as $resource => $info) {
danielebarchiesi@4 198 // Resource full path (e.g. /node/% or /user/%) for accessing specific
danielebarchiesi@4 199 // resources.
danielebarchiesi@4 200 $menu_path = isset($info['menu_path']) ? $info['menu_path'] . '/%' : $resource . '/%';
danielebarchiesi@4 201 // Replace existing page callbacks with our own (e.g. node/%)
danielebarchiesi@4 202 if (isset($items[$menu_path])) {
danielebarchiesi@4 203 // Prepend the page callback and the resource to the page arguments.
danielebarchiesi@4 204 // So we can re-use it on standard HTML page requests.
danielebarchiesi@4 205 array_unshift($items[$menu_path]['page arguments'], $resource, $items[$menu_path]['page callback']);
danielebarchiesi@4 206 $items[$menu_path]['page callback'] = 'restws_page_callback';
danielebarchiesi@4 207 }
danielebarchiesi@4 208 // Also replace wildcard loaders (e.g. node/%node)
danielebarchiesi@4 209 elseif (isset($items[$menu_path . $resource])) {
danielebarchiesi@4 210 $menu_path = $menu_path . $resource;
danielebarchiesi@4 211 array_unshift($items[$menu_path]['page arguments'], $resource, $items[$menu_path]['page callback']);
danielebarchiesi@4 212 $items[$menu_path]['page callback'] = 'restws_page_callback';
danielebarchiesi@4 213 }
danielebarchiesi@4 214 else {
danielebarchiesi@4 215 $items[$menu_path] = array(
danielebarchiesi@4 216 'page callback' => 'restws_page_callback',
danielebarchiesi@4 217 'page arguments' => array($resource),
danielebarchiesi@4 218 'access callback' => TRUE,
danielebarchiesi@4 219 'type' => MENU_CALLBACK,
danielebarchiesi@4 220 );
danielebarchiesi@4 221 }
danielebarchiesi@4 222 // Resource base path (e.g. /node or /user) for creating resources.
danielebarchiesi@4 223 $menu_path = isset($info['menu_path']) ? substr($menu_path, 0, strlen($menu_path) - 2) : $resource;
danielebarchiesi@4 224
danielebarchiesi@4 225 if (isset($items[$menu_path])) {
danielebarchiesi@4 226 // Prepend the page callback and the resource to the page arguments.
danielebarchiesi@4 227 if (!isset($items[$menu_path]['page arguments'])) {
danielebarchiesi@4 228 $items[$menu_path]['page arguments'] = array();
danielebarchiesi@4 229 }
danielebarchiesi@4 230 array_unshift($items[$menu_path]['page arguments'], $resource, $items[$menu_path]['page callback']);
danielebarchiesi@4 231 $items[$menu_path]['page callback'] = 'restws_page_callback';
danielebarchiesi@4 232 }
danielebarchiesi@4 233 else {
danielebarchiesi@4 234 $items[$menu_path] = array(
danielebarchiesi@4 235 'page callback' => 'restws_page_callback',
danielebarchiesi@4 236 'page arguments' => array($resource),
danielebarchiesi@4 237 'access callback' => TRUE,
danielebarchiesi@4 238 'type' => MENU_CALLBACK,
danielebarchiesi@4 239 );
danielebarchiesi@4 240 }
danielebarchiesi@4 241 // Querying menu paths.
danielebarchiesi@4 242 foreach (array_keys(restws_get_format_info()) as $format) {
danielebarchiesi@4 243 // Resource base path URLs with the suffixes (e.g. node.json or user.xml)
danielebarchiesi@4 244 // for querying.
danielebarchiesi@4 245 if (isset($items["$menu_path.$format"])) {
danielebarchiesi@4 246 // Prepend the page callback and the resource to the page arguments.
danielebarchiesi@4 247 if (!isset($items["$menu_path.$format"]['page arguments'])) {
danielebarchiesi@4 248 $items["$menu_path.$format"]['page arguments'] = array();
danielebarchiesi@4 249 }
danielebarchiesi@4 250 array_unshift($items["$menu_path.$format"]['page arguments'], $resource, $items["$menu_path.$format"]['page callback']);
danielebarchiesi@4 251 $items["$menu_path.$format"]['page callback'] = 'restws_page_callback';
danielebarchiesi@4 252
danielebarchiesi@4 253 }
danielebarchiesi@4 254 else {
danielebarchiesi@4 255 $items["$menu_path.$format"] = array(
danielebarchiesi@4 256 'page callback' => 'restws_page_callback',
danielebarchiesi@4 257 'page arguments' => array($resource),
danielebarchiesi@4 258 'access callback' => TRUE,
danielebarchiesi@4 259 'type' => MENU_CALLBACK,
danielebarchiesi@4 260 );
danielebarchiesi@4 261 }
danielebarchiesi@4 262 }
danielebarchiesi@4 263 }
danielebarchiesi@4 264 }
danielebarchiesi@4 265
danielebarchiesi@4 266 /**
danielebarchiesi@4 267 * Menu page callback.
danielebarchiesi@4 268 */
danielebarchiesi@4 269 function restws_page_callback($resource, $page_callback = NULL) {
danielebarchiesi@4 270 $id_arg = arg(1);
danielebarchiesi@4 271 $resource_arg = arg(0);
danielebarchiesi@4 272 $format = FALSE;
danielebarchiesi@4 273 $id = NULL;
danielebarchiesi@4 274 // Check for an appended .format string on GET requests only to avoid CSRF
danielebarchiesi@4 275 // attacks on POST requests.
danielebarchiesi@4 276 if ($_SERVER['REQUEST_METHOD'] == 'GET' && ($pos = strpos($id_arg, '.')) && $format_name = substr($id_arg, $pos + 1)) {
danielebarchiesi@4 277 $id = substr($id_arg, 0, $pos);
danielebarchiesi@4 278 $format = restws_format($format_name);
danielebarchiesi@4 279 }
danielebarchiesi@4 280 elseif ($_SERVER['REQUEST_METHOD'] == 'GET' && ($pos = strpos($resource_arg, '.')) && $format_name = substr($resource_arg, $pos + 1)) {
danielebarchiesi@4 281 $format = restws_format($format_name);
danielebarchiesi@4 282 }
danielebarchiesi@4 283 else {
danielebarchiesi@4 284 $id = $id_arg;
danielebarchiesi@4 285 switch ($_SERVER['REQUEST_METHOD']) {
danielebarchiesi@4 286 case 'POST':
danielebarchiesi@4 287 case 'PUT':
danielebarchiesi@4 288 // Get format MIME type form HTTP Content type header.
danielebarchiesi@4 289 $parts = explode(';', $_SERVER['CONTENT_TYPE'], 2);
danielebarchiesi@4 290 $format = restws_format_mimetype($parts[0]);
danielebarchiesi@4 291 break;
danielebarchiesi@4 292
danielebarchiesi@4 293 case 'DELETE':
danielebarchiesi@4 294 if (isset($_SERVER['HTTP_ACCEPT'])) {
danielebarchiesi@4 295 $parts = explode(',', $_SERVER['HTTP_ACCEPT'], 2);
danielebarchiesi@4 296 $format = restws_format_mimetype($parts[0]);
danielebarchiesi@4 297 }
danielebarchiesi@4 298 if (!$format) {
danielebarchiesi@4 299 // We don't care about the format, just pick JSON.
danielebarchiesi@4 300 $format = restws_format('json');
danielebarchiesi@4 301 }
danielebarchiesi@4 302 break;
danielebarchiesi@4 303
danielebarchiesi@4 304 default:
danielebarchiesi@4 305 // Get the format MIME type form the HTTP Accept header.
danielebarchiesi@4 306 // Ignore requests from web browsers that accept HTML.
danielebarchiesi@4 307 if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'html') === FALSE) {
danielebarchiesi@4 308 // Use the first MIME type.
danielebarchiesi@4 309 $parts = explode(',', $_SERVER['HTTP_ACCEPT'], 2);
danielebarchiesi@4 310 $format = restws_format_mimetype($parts[0]);
danielebarchiesi@4 311 }
danielebarchiesi@4 312 // Consumers should not use this URL if page caching is enabled.
danielebarchiesi@4 313 // Drupal's page cache IDs are only determined by URL path, so this
danielebarchiesi@4 314 // could poison the HTML page cache. A browser request to /node/1 could
danielebarchiesi@4 315 // suddenly return JSON if the cache was primed with this RESTWS
danielebarchiesi@4 316 // response.
danielebarchiesi@4 317 if ($format && !isset($_COOKIE[session_name()]) && variable_get('cache')) {
danielebarchiesi@4 318 // Redirect to the URL path containing the format name instead.
danielebarchiesi@4 319 drupal_goto($_GET['q'] . '.' . $format->getName(), array(), 301);
danielebarchiesi@4 320 }
danielebarchiesi@4 321 }
danielebarchiesi@4 322 }
danielebarchiesi@4 323 if ($format) {
danielebarchiesi@4 324 switch ($_SERVER['REQUEST_METHOD']) {
danielebarchiesi@4 325 case 'POST':
danielebarchiesi@4 326 $op = 'create';
danielebarchiesi@4 327 break;
danielebarchiesi@4 328
danielebarchiesi@4 329 case 'PUT':
danielebarchiesi@4 330 $op = 'update';
danielebarchiesi@4 331 break;
danielebarchiesi@4 332
danielebarchiesi@4 333 case 'DELETE':
danielebarchiesi@4 334 $op = 'delete';
danielebarchiesi@4 335 break;
danielebarchiesi@4 336
danielebarchiesi@4 337 default:
danielebarchiesi@4 338 if (!empty($id)) {
danielebarchiesi@4 339 $op = 'view';
danielebarchiesi@4 340 }
danielebarchiesi@4 341 else {
danielebarchiesi@4 342 $op = 'query';
danielebarchiesi@4 343 }
danielebarchiesi@4 344 }
danielebarchiesi@4 345
danielebarchiesi@4 346 // CSRF protection on write operations.
danielebarchiesi@4 347 if (!in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD', 'OPTIONS', 'TRACE')) && !restws_csrf_validation()) {
danielebarchiesi@4 348 echo '403 Access Denied: CSRF validation failed';
danielebarchiesi@4 349 drupal_add_http_header('Status', '403 Forbidden');
danielebarchiesi@4 350 drupal_page_footer();
danielebarchiesi@4 351 exit;
danielebarchiesi@4 352 }
danielebarchiesi@4 353
danielebarchiesi@4 354 $payload = file_get_contents('php://input');
danielebarchiesi@4 355 if ($file = variable_get('restws_debug_log')) {
danielebarchiesi@4 356 $log = date(DATE_ISO8601) . "\n";
danielebarchiesi@4 357 $log .= 'Resource: ' . $resource . "\n";
danielebarchiesi@4 358 $log .= 'Operation: ' . $op . "\n";
danielebarchiesi@4 359 $log .= 'Format: ' . $format->mimeType() . "\n";
danielebarchiesi@4 360 $log .= 'Id: ' . $id . "\n";
danielebarchiesi@4 361 $log .= 'Payload: ' . $payload . "\n";
danielebarchiesi@4 362 $log .= "----------------------------------------------------------------\n";
danielebarchiesi@4 363 file_put_contents($file, $log, FILE_APPEND);
danielebarchiesi@4 364 }
danielebarchiesi@4 365 restws_handle_request($op, $format, $resource, $id, $payload);
danielebarchiesi@4 366 }
danielebarchiesi@4 367
danielebarchiesi@4 368 // @todo: Determine human readable URIs and redirect, if there is no
danielebarchiesi@4 369 // page callback.
danielebarchiesi@4 370 if (isset($page_callback)) {
danielebarchiesi@4 371 // Further page callback arguments have been appended to our arguments.
danielebarchiesi@4 372 $args = func_get_args();
danielebarchiesi@4 373 return call_user_func_array($page_callback, array_slice($args, 2));
danielebarchiesi@4 374 }
danielebarchiesi@4 375 echo '404 Not Found';
danielebarchiesi@4 376 drupal_add_http_header('Status', '404 Not Found');
danielebarchiesi@4 377 drupal_page_footer();
danielebarchiesi@4 378 exit;
danielebarchiesi@4 379 }
danielebarchiesi@4 380
danielebarchiesi@4 381 /**
danielebarchiesi@4 382 * Returns the URI used for the given resource.
danielebarchiesi@4 383 *
danielebarchiesi@4 384 * @param string $resource
danielebarchiesi@4 385 * The resource for which the URI should be returned.
danielebarchiesi@4 386 * @param int $id
danielebarchiesi@4 387 * The resource ID or NULL if only the base path should be returned.
danielebarchiesi@4 388 * @param array $options
danielebarchiesi@4 389 * Optional array that is passed to url().
danielebarchiesi@4 390 */
danielebarchiesi@4 391 function restws_resource_uri($resource, $id = NULL, array $options = array()) {
danielebarchiesi@4 392 $info = restws_get_resource_info($resource);
danielebarchiesi@4 393 $basepath = isset($info['menu_path']) ? $info['menu_path'] : $resource;
danielebarchiesi@4 394 $sub_path = isset($id) ? "/$id" : '';
danielebarchiesi@4 395
danielebarchiesi@4 396 // Avoid having the URLs aliased.
danielebarchiesi@4 397 $base_options = array('absolute' => TRUE, 'alias' => TRUE);
danielebarchiesi@4 398 $options += $base_options;
danielebarchiesi@4 399
danielebarchiesi@4 400 return url($basepath . $sub_path, $options);
danielebarchiesi@4 401 }
danielebarchiesi@4 402
danielebarchiesi@4 403 /**
danielebarchiesi@4 404 * Returns the format instance for a given MIME type.
danielebarchiesi@4 405 *
danielebarchiesi@4 406 * @param string $mime
danielebarchiesi@4 407 * The MIME type, e.g. 'application/json' or 'application/xml'.
danielebarchiesi@4 408 *
danielebarchiesi@4 409 * @return bool|RestWSFormatInterface
danielebarchiesi@4 410 * The format controller or FALSE if the format was not found.
danielebarchiesi@4 411 */
danielebarchiesi@4 412 function restws_format_mimetype($mime) {
danielebarchiesi@4 413 foreach (restws_get_format_info() as $format_name => $info) {
danielebarchiesi@4 414 if ($info['mime type'] == $mime) {
danielebarchiesi@4 415 return restws_format($format_name);
danielebarchiesi@4 416 }
danielebarchiesi@4 417 }
danielebarchiesi@4 418 return FALSE;
danielebarchiesi@4 419 }
danielebarchiesi@4 420
danielebarchiesi@4 421 /**
danielebarchiesi@4 422 * Implements hook_permission().
danielebarchiesi@4 423 */
danielebarchiesi@4 424 function restws_permission() {
danielebarchiesi@4 425 $permissions = array();
danielebarchiesi@4 426 // Create service access permissions per resource type.
danielebarchiesi@4 427 foreach (restws_get_resource_info() as $type => $info) {
danielebarchiesi@4 428 $permissions['access resource ' . $type] = array(
danielebarchiesi@4 429 'title' => t('Access the resource %resource', array('%resource' => $type)),
danielebarchiesi@4 430 );
danielebarchiesi@4 431 }
danielebarchiesi@4 432 return $permissions;
danielebarchiesi@4 433 }
danielebarchiesi@4 434
danielebarchiesi@4 435 /**
danielebarchiesi@4 436 * Implements hook_module_implements_alter().
danielebarchiesi@4 437 */
danielebarchiesi@4 438 function restws_module_implements_alter(&$implementations, $hook) {
danielebarchiesi@4 439 // Make sure that restws runs last.
danielebarchiesi@4 440 // @todo remove entity_info_alter once https://drupal.org/node/1780646 is fixed.
danielebarchiesi@4 441 if ($hook == 'menu_alter' || $hook == 'entity_info_alter') {
danielebarchiesi@4 442 $group = $implementations['restws'];
danielebarchiesi@4 443 unset($implementations['restws']);
danielebarchiesi@4 444 $implementations['restws'] = $group;
danielebarchiesi@4 445 }
danielebarchiesi@4 446 }
danielebarchiesi@4 447
danielebarchiesi@4 448 /**
danielebarchiesi@4 449 * Return all available meta controls.
danielebarchiesi@4 450 */
danielebarchiesi@4 451 function restws_meta_controls() {
danielebarchiesi@4 452 return array(
danielebarchiesi@4 453 'sort' => 'sort',
danielebarchiesi@4 454 'direction' => 'direction',
danielebarchiesi@4 455 'page' => 'page',
danielebarchiesi@4 456 'limit' => 'limit',
danielebarchiesi@4 457 'full' => 'full',
danielebarchiesi@4 458 );
danielebarchiesi@4 459 }
danielebarchiesi@4 460
danielebarchiesi@4 461 /**
danielebarchiesi@4 462 * Ensures that a request with cookies has the required CSRF header set.
danielebarchiesi@4 463 *
danielebarchiesi@4 464 * @return bool
danielebarchiesi@4 465 * TRUE if the request passed the CSRF protection, FALSE otherwise.
danielebarchiesi@4 466 */
danielebarchiesi@4 467 function restws_csrf_validation() {
danielebarchiesi@4 468 // This check only applies if the user was successfully authenticated and the
danielebarchiesi@4 469 // request comes with a session cookie.
danielebarchiesi@4 470 if (user_is_logged_in() && !empty($_COOKIE[session_name()])) {
danielebarchiesi@4 471 return isset($_SERVER['HTTP_X_CSRF_TOKEN']) && drupal_valid_token($_SERVER['HTTP_X_CSRF_TOKEN'], 'restws');
danielebarchiesi@4 472 }
danielebarchiesi@4 473 return TRUE;
danielebarchiesi@4 474 }
danielebarchiesi@4 475
danielebarchiesi@4 476 /**
danielebarchiesi@4 477 * Implements hook_menu().
danielebarchiesi@4 478 */
danielebarchiesi@4 479 function restws_menu() {
danielebarchiesi@4 480 $items['restws/session/token'] = array(
danielebarchiesi@4 481 'page callback' => 'restws_session_token',
danielebarchiesi@4 482 // Only authenticated users are allowed to retrieve a session token.
danielebarchiesi@4 483 'access callback' => 'user_is_logged_in',
danielebarchiesi@4 484 'type' => MENU_CALLBACK,
danielebarchiesi@4 485 );
danielebarchiesi@4 486 return $items;
danielebarchiesi@4 487 }
danielebarchiesi@4 488
danielebarchiesi@4 489 /**
danielebarchiesi@4 490 * Page callback: returns a session token for the currently active user.
danielebarchiesi@4 491 */
danielebarchiesi@4 492 function restws_session_token() {
danielebarchiesi@4 493 drupal_add_http_header('Content-Type', 'text/plain');
danielebarchiesi@4 494 print drupal_get_token('restws');
danielebarchiesi@4 495 drupal_exit();
danielebarchiesi@4 496 }
danielebarchiesi@4 497
danielebarchiesi@4 498 /**
danielebarchiesi@4 499 * Access callback for the node entity.
danielebarchiesi@4 500 *
danielebarchiesi@4 501 * Replacement for entity_metadata_no_hook_node_access() because it does not
danielebarchiesi@4 502 * work with the create operation.
danielebarchiesi@4 503 *
danielebarchiesi@4 504 * @todo Remove this once https://drupal.org/node/1780646 is fixed.
danielebarchiesi@4 505 *
danielebarchiesi@4 506 * @see restws_entity_info_alter()
danielebarchiesi@4 507 * @see entity_metadata_no_hook_node_access()
danielebarchiesi@4 508 */
danielebarchiesi@4 509 function restws_entity_node_access($op, $node = NULL, $account = NULL) {
danielebarchiesi@4 510 // First deal with the case where a $node is provided.
danielebarchiesi@4 511 if (isset($node)) {
danielebarchiesi@4 512 // Ugly hack to handle field access, because entity_api does not distinguish
danielebarchiesi@4 513 // between 'create' and 'update' permissions for fields. This should rather
danielebarchiesi@4 514 // be fixed in EntityStructureWrapper::propertyAccess() (entity.wrapper.inc).
danielebarchiesi@4 515 if ($op == 'update' && empty($node->nid)) {
danielebarchiesi@4 516 $op = 'create';
danielebarchiesi@4 517 }
danielebarchiesi@4 518 if ($op == 'create') {
danielebarchiesi@4 519 if (isset($node->type)) {
danielebarchiesi@4 520 return node_access($op, $node->type, $account);
danielebarchiesi@4 521 }
danielebarchiesi@4 522 else {
danielebarchiesi@4 523 throw new EntityMalformedException('Permission to create a node was requested but no node type was given.');
danielebarchiesi@4 524 }
danielebarchiesi@4 525 }
danielebarchiesi@4 526 // If a non-default revision is given, incorporate revision access.
danielebarchiesi@4 527 $default_revision = node_load($node->nid);
danielebarchiesi@4 528 if ($node->vid !== $default_revision->vid) {
danielebarchiesi@4 529 return _node_revision_access($node, $op, $account);
danielebarchiesi@4 530 }
danielebarchiesi@4 531 else {
danielebarchiesi@4 532 return node_access($op, $node, $account);
danielebarchiesi@4 533 }
danielebarchiesi@4 534 }
danielebarchiesi@4 535 // No node is provided. Check for access to all nodes.
danielebarchiesi@4 536 if (user_access('bypass node access', $account)) {
danielebarchiesi@4 537 return TRUE;
danielebarchiesi@4 538 }
danielebarchiesi@4 539 if (!user_access('access content', $account)) {
danielebarchiesi@4 540 return FALSE;
danielebarchiesi@4 541 }
danielebarchiesi@4 542 if ($op == 'view' && node_access_view_all_nodes($account)) {
danielebarchiesi@4 543 return TRUE;
danielebarchiesi@4 544 }
danielebarchiesi@4 545 return FALSE;
danielebarchiesi@4 546 }
danielebarchiesi@4 547
danielebarchiesi@4 548 /**
danielebarchiesi@4 549 * Implements hook_entity_info_alter().
danielebarchiesi@4 550 *
danielebarchiesi@4 551 * @todo Remove this once https://drupal.org/node/1780646 is fixed.
danielebarchiesi@4 552 */
danielebarchiesi@4 553 function restws_entity_info_alter(&$info) {
danielebarchiesi@4 554 // In order for this to work we have to make sure we run after entity_api.
danielebarchiesi@4 555 // @see restws_module_implements_alter().
danielebarchiesi@4 556 $info['node']['access callback'] = 'restws_entity_node_access';
danielebarchiesi@4 557 }