View source
<?php
include_once __DIR__ . '/restful.entity.inc';
function restful_ctools_plugin_directory($module, $plugin) {
if ($module == 'restful') {
return 'plugins/' . $plugin;
}
}
function restful_plugin_process(&$plugin, $info) {
$plugin += array(
'description' => '',
);
$function = array(
'\\RestfulManager',
'pluginProcess' . preg_replace('/ /', '', ucwords(preg_replace('/_/', ' ', $info['type']))),
);
if (is_callable($function)) {
$plugin = call_user_func_array($function, array(
$plugin,
$info,
));
}
}
function restful_ctools_plugin_type() {
$plugins['authentication'] = $plugins['restful'] = $plugins['rate_limit'] = $plugins['formatter'] = array(
'classes' => array(
'class',
),
'process' => 'restful_plugin_process',
);
$plugins['restful']['child plugins'] = TRUE;
return $plugins;
}
function restful_get_restful_plugins() {
ctools_include('plugins');
return ctools_get_plugins('restful', 'restful');
}
function restful_get_authentication_plugins() {
ctools_include('plugins');
return ctools_get_plugins('restful', 'authentication');
}
function restful_get_rate_limit_plugins() {
ctools_include('plugins');
return ctools_get_plugins('restful', 'rate_limit');
}
function restful_get_formatter_plugins() {
ctools_include('plugins');
return ctools_get_plugins('restful', 'formatter');
}
function restful_get_authentication_plugin($plugin_name) {
ctools_include('plugins');
return ctools_get_plugins('restful', 'authentication', $plugin_name);
}
function restful_get_formatter_plugin($plugin_name) {
ctools_include('plugins');
return ctools_get_plugins('restful', 'formatter', $plugin_name);
}
function restful_get_rate_limit_plugin($plugin_name) {
ctools_include('plugins');
return ctools_get_plugins('restful', 'rate_limit', $plugin_name);
}
function restful_menu() {
$base_path = variable_get('restful_hook_menu_base_path', 'api');
$items = array();
foreach (restful_get_restful_plugins() as $plugin) {
if (!$plugin['hook_menu']) {
continue;
}
$item = array(
'title' => $plugin['name'],
'access callback' => 'restful_menu_access_callback',
'access arguments' => array(
$plugin['resource'],
),
'page callback' => 'restful_menu_process_callback',
'page arguments' => array(
$plugin['resource'],
),
'delivery callback' => 'restful_formatted_delivery',
'type' => MENU_CALLBACK,
);
if ($plugin['hook_menu'] && empty($plugin['menu_item'])) {
$item['access arguments'][] = 1;
$item['page arguments'][] = 1;
$items[$base_path . '/v' . $plugin['major_version'] . '.' . $plugin['minor_version'] . '/' . $plugin['resource']] = $item;
$items[$base_path . '/v' . $plugin['major_version'] . '/' . $plugin['resource']] = $item;
$item['access arguments'] = $item['page arguments'] = array(
1,
);
$items[$base_path . '/' . $plugin['resource']] = $item;
}
else {
$items[$plugin['menu_item']] = $item;
}
}
if (!empty($items[$base_path . '/session/token'])) {
$items[$base_path . '/session/token']['delivery callback'] = 'restful_unprepared_delivery';
}
if (!empty($items[$base_path . '/login'])) {
$items[$base_path . '/login']['access callback'] = 'user_is_anonymous';
}
$items['admin/config/services/restful'] = array(
'title' => 'RESTful',
'description' => 'Administer the RESTful module.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'restful_admin_settings',
),
'access arguments' => array(
'administer restful',
),
'file' => 'restful.admin.inc',
);
$items['admin/config/services/restful/restful'] = $items['admin/config/services/restful'];
$items['admin/config/services/restful/restful']['type'] = MENU_DEFAULT_LOCAL_TASK;
return $items;
}
function restful_permission() {
return array(
'administer restful' => array(
'title' => t('Administer the RESTful module'),
'description' => t('Access the administration pages for the RESTful module.'),
),
);
}
function restful_help($path, $arg) {
switch ($path) {
case 'admin/structure/block':
case 'admin/help#restful':
return '<p>' . t('This module is managed in GitHub. Please make sure to read the docs in the !link page for more help.', array(
'!link' => l('README.md', 'https://github.com/Gizra/restful/blob/7.x-1.x/README.md'),
)) . '</p>';
}
}
function restful_get_restful_handler($resource_name, $major_version = 1, $minor_version = 0) {
$cache =& drupal_static(__FUNCTION__);
$identifier = implode(':', array(
$major_version,
$resource_name,
$minor_version,
));
if (isset($cache[$identifier])) {
return $cache[$identifier];
}
$cache[$identifier] = NULL;
$valid_plugins = array();
foreach (restful_get_restful_plugins() as $plugin) {
if ($plugin['major_version'] != $major_version) {
continue;
}
if ($plugin['resource'] != $resource_name) {
continue;
}
if ($minor_version == $plugin['minor_version']) {
$valid_plugins[$plugin['minor_version']] = $plugin;
break;
}
if ($plugin['minor_version'] > $minor_version) {
continue;
}
$valid_plugins[$plugin['minor_version']] = $plugin;
}
if (!$valid_plugins) {
return;
}
ksort($valid_plugins);
$plugin = end($valid_plugins);
$cache[$identifier] = restful_get_restful_handler_by_name($plugin['name']);
return $cache[$identifier];
}
function restful_get_restful_handler_by_name($plugin_name) {
ctools_include('plugins');
$plugin = ctools_get_plugins('restful', 'restful', $plugin_name);
if (!($class = ctools_plugin_load_class('restful', 'restful', $plugin_name, 'class'))) {
throw new \RestfulServiceUnavailable(format_string('Restful plugin class (@plugin) was not found.', array(
'@plugin' => $plugin_name,
)));
}
$handler = new $class($plugin);
$auth_plugins = $plugin['authentication_types'] === TRUE ? array_keys(restful_get_authentication_plugins()) : $plugin['authentication_types'];
foreach ($auth_plugins as $auth_plugin_name) {
$auth_handler = restful_get_authentication_handler($auth_plugin_name);
$handler
->getAuthenticationManager()
->addAuthenticationProvider($auth_handler);
}
$handler
->getAuthenticationManager()
->setIsOptional($plugin['authentication_optional']);
return $handler;
}
function restful_get_authentication_handler($auth_plugin_name) {
$auth_plugin = restful_get_authentication_plugin($auth_plugin_name);
if (!($auth_class = ctools_plugin_get_class($auth_plugin, 'class'))) {
throw new \RestfulServiceUnavailable(format_string('Authentication plugin class (@plugin) was not found.', array(
'@plugin' => $auth_plugin_name,
)));
}
return new $auth_class($auth_plugin);
}
function restful_get_formatter_handler($formatter_plugin_name, $restful_handler) {
$formatter_plugin = restful_get_formatter_plugin($formatter_plugin_name);
if (!($formatter_class = ctools_plugin_get_class($formatter_plugin, 'class'))) {
throw new \RestfulServiceUnavailable(format_string('Formatter plugin class (@plugin) was not found.', array(
'@plugin' => $formatter_plugin_name,
)));
}
return new $formatter_class($formatter_plugin, $restful_handler);
}
function restful_get_restful_handler_for_path($path = NULL) {
$handlers =& drupal_static(__FUNCTION__);
$path = is_null($path) ? $_GET['q'] : $path;
if (isset($handlers[$path])) {
return $handlers[$path];
}
$router_item = \RestfulBase::getMenuItem($path);
if ($router_item['page_callback'] != 'restful_menu_process_callback') {
$handlers[$path] = FALSE;
return;
}
list($resource, ) = \RestfulBase::getPageArguments($path);
list($major_version, $minor_version) = \RestfulBase::getVersionFromRequest($path);
$handlers[$path] = restful_get_restful_handler($resource, $major_version, $minor_version);
return $handlers[$path];
}
function restful_menu_access_callback($resource_name, $version = NULL) {
if (!($versions = \RestfulBase::getVersionFromRequest())) {
return;
}
if (!($handler = restful_get_restful_handler($resource_name, $versions[0], $versions[1]))) {
return;
}
if (!\RestfulBase::isValidMethod($_SERVER['REQUEST_METHOD'], FALSE)) {
return;
}
$method = strtoupper($_SERVER['REQUEST_METHOD']);
if ($method == \RestfulInterface::POST && \RestfulManager::getRequestHttpHeader('X-HTTP-Method-Override')) {
$method = strtoupper(\RestfulManager::getRequestHttpHeader('X-HTTP-Method-Override'));
}
if (!\RestfulBase::isValidMethod($method, FALSE)) {
return;
}
$path = func_get_args();
array_shift($path);
if (preg_match('/^v\\d+(\\.\\d+)?$/', $version)) {
array_shift($path);
}
$path = implode('/', $path);
$request = restful_parse_request();
$handler
->setPath($path);
$handler
->setMethod($method);
$handler
->setRequest($request);
try {
$access = $handler
->access();
return $access;
} catch (Exception $e) {
return FALSE;
}
}
function restful_menu_process_callback($resource_name, $version = NULL) {
list($major_version, $minor_version) = \RestfulBase::getVersionFromRequest();
$handler = restful_get_restful_handler($resource_name, $major_version, $minor_version);
if (!$handler instanceof \RestfulDataProviderInterface) {
throw new \RestfulServiceUnavailable(format_string('The selected plugin (@plugin) does not implement \\RestfulDataProviderInterface.', array(
'@plugin' => $resource_name . ' v' . $major_version . '.' . $minor_version,
)));
}
$headers = $handler
->getHttpHeaders();
$additional_variations = empty($headers['Vary']) ? array(
'Accept',
) : array(
$headers['Vary'],
'Accept',
);
if (\RestfulManager::getRequestHttpHeader('X-API-Version')) {
$additional_variations[] = 'X-API-Version';
}
if ($additional_variations) {
$handler
->setHttpHeaders('Vary', implode(',', $additional_variations));
}
if ($allowed_origin = $handler
->getPluginKey('allow_origin')) {
$handler
->setHttpHeaders('Access-Control-Allow-Origin', $allowed_origin);
}
$method = strtoupper($_SERVER['REQUEST_METHOD']);
if ($method == \RestfulInterface::POST && \RestfulManager::getRequestHttpHeader('X-HTTP-Method-Override')) {
$method = \RestfulManager::getRequestHttpHeader('X-HTTP-Method-Override');
}
$method = strtolower($method);
$path = func_get_args();
array_shift($path);
if (preg_match('/^v\\d+(\\.\\d+)?$/', $version)) {
array_shift($path);
}
$path = implode('/', $path);
$request = restful_parse_request();
try {
if (!\RestfulBase::isValidMethod($method, FALSE)) {
throw new RestfulBadRequestException(format_string('Unsupported method @method.', array(
'@method' => $method,
)));
}
return $handler
->{$method}($path, $request);
} catch (RestfulException $e) {
$result = array(
'type' => $e
->getType(),
'title' => $e
->getMessage(),
'status' => $e
->getCode(),
'detail' => $e
->getDescription(),
);
if ($instance = $e
->getInstance()) {
$result['instance'] = $instance;
}
if ($errors = $e
->getFieldErrors()) {
$result['errors'] = $errors;
}
foreach ($e
->getHeaders() as $header_name => $header_value) {
drupal_add_http_header($header_name, $header_value);
}
if ($e
->getCode() < 500) {
watchdog('restful', $e
->getMessage());
}
else {
watchdog_exception('restful', $e);
}
} catch (Exception $e) {
$result = array(
'type' => 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1',
'title' => $e
->getMessage(),
'status' => 500,
);
watchdog_exception('restful', $e);
}
drupal_add_http_header('Status', $result['status']);
drupal_add_http_header('Content-Type', 'application/problem+json; charset=utf-8');
return $result;
}
function restful_parse_request() {
$request =& drupal_static(__FUNCTION__, NULL);
if ($request) {
return $request;
}
$method = strtoupper($_SERVER['REQUEST_METHOD']);
if ($method == \RestfulInterface::GET) {
$request = $_GET;
}
elseif ($method == \RestfulInterface::POST) {
$request = $_POST;
}
if (!$request && ($query_string = _restful_get_query_string_from_php_input())) {
if ($decoded_json = drupal_json_decode($query_string)) {
$request = $decoded_json;
}
else {
parse_str($query_string, $request);
}
}
$request['__application'] = array(
'rest_call' => TRUE,
'csrf_token' => \RestfulManager::getRequestHttpHeader('X-CSRF-Token'),
);
drupal_alter('restful_parse_request', $request);
return $request;
}
function restful_delivery($var = NULL, $method = 'format') {
if ($restful_handler = restful_get_restful_handler_for_path()) {
foreach ($restful_handler
->getHttpHeaders() as $key => $value) {
drupal_add_http_header($key, $value);
}
}
if (!isset($var)) {
return;
}
if (is_int($var)) {
_restful_get_json_from_menu_status($var);
drupal_add_http_header('Status', $var['status']);
drupal_add_http_header('Content-Type', 'application/problem+json; charset=utf-8');
}
if ($restful_handler && $restful_handler
->getMethod() == \RestfulInterface::OPTIONS) {
$method = 'render';
}
try {
$formatter_handler = \RestfulManager::outputFormat($restful_handler);
$output = $formatter_handler
->{$method}($var);
drupal_add_http_header('Content-Type', $formatter_handler
->getContentTypeHeader());
} catch (\RestfulException $e) {
drupal_add_http_header('Status', $e
->getCode());
echo $e
->getMessage();
return;
}
print $output;
\RestfulManager::pageFooter();
}
function restful_formatted_delivery($var = NULL) {
restful_delivery($var, 'format');
}
function restful_unprepared_delivery($var = NULL) {
restful_delivery($var, 'render');
}
function _restful_get_json_from_menu_status(&$var) {
switch ($var) {
case MENU_NOT_FOUND:
$class_name = 'RestfulNotFoundException';
$message = 'Invalid URL path.';
break;
case MENU_ACCESS_DENIED:
$class_name = 'RestfulForbiddenException';
$message = 'Access denied.';
break;
case MENU_SITE_OFFLINE:
$class_name = 'RestfulServiceUnavailable';
$message = 'Site is offline.';
break;
}
$e = new $class_name($message);
$var = array(
'type' => $e
->getType(),
'title' => $e
->getMessage(),
'status' => $e
->getCode(),
'detail' => $e
->getDescription(),
);
if ($instance = $e
->getInstance()) {
$var['instance'] = $instance;
}
if ($errors = $e
->getFieldErrors()) {
$var['errors'] = $errors;
}
}
function restful_page_delivery_callback_alter(&$callback) {
if (!variable_get('restful_hijack_api_pages', TRUE)) {
return;
}
$base_path = variable_get('restful_hook_menu_base_path', 'api');
if (strpos($_GET['q'], $base_path . '/') !== 0 && $_GET['q'] != $base_path) {
return;
}
if (menu_get_item()) {
return;
}
$callback = 'restful_deliver_menu_not_found';
}
function restful_deliver_menu_not_found($page_callback_result) {
restful_delivery(MENU_NOT_FOUND);
}
function restful_cron() {
\RestfulRateLimitManager::deleteExpired();
}
function restful_csrf_session_token() {
return array(
'X-CSRF-Token' => drupal_get_token(\RestfulInterface::TOKEN_VALUE),
);
}
function restful_date_time_format_element_validate($element, &$form_state) {
$value = $element['#value'];
try {
new \DateInterval($value);
} catch (\Exception $e) {
form_error($element, t('%name must be compatible with the !link.', array(
'%name' => $element['#title'],
'!link' => l(t('\\DateInterval format'), 'http://php.net/manual/en/class.dateinterval.php'),
)));
}
}
function restful_is_user_switched() {
$restful_switch_user =& drupal_static(__FUNCTION__, FALSE);
if (!$restful_switch_user) {
$restful_switch_user = TRUE;
return FALSE;
}
return TRUE;
}
function _restful_get_query_string_from_php_input() {
$query_string =& drupal_static(__FUNCTION__);
if (!isset($query_string)) {
$query_string = file_get_contents('php://input');
}
return $query_string;
}