Created
November 28, 2011 21:28
-
-
Save arturo-c/1402157 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/CHANGELOG.txt b/CHANGELOG.txt | |
index 90aa405..cc2cb52 100644 | |
--- a/CHANGELOG.txt | |
+++ b/CHANGELOG.txt | |
@@ -2,6 +2,6 @@ Trying to remember to document major API changes here | |
- hook_services_authentication_info() no longer uses the 'alter_browse_form', 'alter_browse_form_validate' or 'alter_browse_form_submit' callbacks since the browser is gone. | |
-- service definitions are now divided into predefined classes (create, retrieve, update, delete, actions, targeted actions, relationships) and the available keys have all changed/been removed/been added to in a variety of ways since 2.x. | |
+- service definitions are now divided into predefined classes (create, retrieve, update, delete, actions, targeted_actions, relationships) and the available keys have all changed/been removed/been added to in a variety of ways since 2.x. | |
- hash marks are gone gone gone | |
\ No newline at end of file | |
diff --git a/auth/services_sessauth/services_sessauth.inc b/auth/services_sessauth/services_sessauth.inc | |
deleted file mode 100644 | |
index 9d00e86..0000000 | |
--- a/auth/services_sessauth/services_sessauth.inc | |
+++ /dev/null | |
@@ -1,24 +0,0 @@ | |
-<?php | |
- | |
-/** | |
- * @file | |
- * The implementation of the session authentication scheme | |
- */ | |
- | |
-function _services_sessauth_authenticate_call($settings, $method, &$args, $auth_args) { | |
- // Anyone passing in a specific session id to use, should do so in the | |
- // 'sessid' key. Otherwise, just use the current session (IE do nothing.) | |
- if (isset($auth_args['sessid'])) { | |
- // Add additonal processing for methods requiring session | |
- $session_backup = NULL; | |
- $sessid = $auth_args['sessid']; | |
- if (empty($sessid)) { | |
- return t('Invalid sessid.'); | |
- } | |
- $session_backup = services_session_load($sessid); | |
- } | |
-} | |
- | |
-function _services_sessauth_security_settings() { | |
- return array(); | |
-} | |
diff --git a/auth/services_sessauth/services_sessauth.info b/auth/services_sessauth/services_sessauth.info | |
deleted file mode 100644 | |
index c4e8be5..0000000 | |
--- a/auth/services_sessauth/services_sessauth.info | |
+++ /dev/null | |
@@ -1,5 +0,0 @@ | |
-name = Session Authentication | |
-description = Provides session authentication for the services module | |
-package = Services - authentication | |
-dependencies[] = services | |
-core = 6.x | |
\ No newline at end of file | |
diff --git a/auth/services_sessauth/services_sessauth.module b/auth/services_sessauth/services_sessauth.module | |
deleted file mode 100644 | |
index 835a0e6..0000000 | |
--- a/auth/services_sessauth/services_sessauth.module | |
+++ /dev/null | |
@@ -1,33 +0,0 @@ | |
-<?php | |
-/** | |
- * @file | |
- * Provides a key based validation system. | |
- */ | |
- | |
-/** | |
- * Implementation of hook_authentication_info(). | |
- * | |
- * @return array | |
- * The configuration array for the authentication scheme | |
- */ | |
-function services_sessauth_services_authentication_info() { | |
- return array( | |
- 'file' => 'services_sessauth.inc', | |
- 'title' => t('Session authentication'), | |
- 'description' => t('The default session based authentication'), | |
- 'authenticate_call' => '_services_sessauth_authenticate_call', | |
- 'security_settings' => '_services_sessauth_security_settings', | |
- 'controller_settings' => '_services_sessauth_security_settings', | |
- ); | |
-} | |
- | |
- | |
-// These don't currently do anything, need to conform to new cache keys | |
-// $cache_key = 'services:' . $endpoint_name . ':resources'; | |
-function services_sessauth_disable() { | |
- cache_clear_all('services:methods', 'cache'); | |
-} | |
- | |
-function services_sessauth_enable() { | |
- cache_clear_all('services:methods', 'cache'); | |
-} | |
diff --git a/plugins/export_ui/services_ctools_export_ui.class.php b/plugins/export_ui/services_ctools_export_ui.class.php | |
index fe4a51d..6a448ee 100644 | |
--- a/plugins/export_ui/services_ctools_export_ui.class.php | |
+++ b/plugins/export_ui/services_ctools_export_ui.class.php | |
@@ -23,6 +23,14 @@ class services_ctools_export_ui extends ctools_export_ui { | |
return drupal_get_form('services_edit_form_endpoint_authentication', $item); | |
} | |
+ /** | |
+ * Page callback for the server page. | |
+ */ | |
+ function server_page($js, $input, $item) { | |
+ drupal_set_title($this->get_page_title('server', $item)); | |
+ return drupal_get_form('services_edit_form_endpoint_server', $item); | |
+ } | |
+ | |
// Avoid standard submit of edit form by ctools. | |
function edit_save_form($form_state) { } | |
@@ -58,8 +66,7 @@ function services_edit_form_endpoint_authentication($form_state) { | |
$form['message'] = array( | |
'#type' => 'item', | |
'#title' => t('Authentication'), | |
- '#description' => t('No authentication modules are installed, standard ' . | |
- 'Drupal session based security will be used.'), | |
+ '#description' => t('No authentication modules are installed, all requests will be anonymous.'), | |
); | |
return $form; | |
} | |
@@ -67,8 +74,7 @@ function services_edit_form_endpoint_authentication($form_state) { | |
$form['message'] = array( | |
'#type' => 'item', | |
'#title' => t('Authentication'), | |
- '#description' => t('No authentication modules are enabled, standard ' . | |
- 'Drupal session based security will be used.'), | |
+ '#description' => t('No authentication modules are enabled, all requests will be anonymous.'), | |
); | |
return $form; | |
} | |
@@ -84,13 +90,13 @@ function services_edit_form_endpoint_authentication($form_state) { | |
'#tree' => TRUE, | |
); | |
$module_settings_form = services_auth_invoke($module, 'security_settings', $settings); | |
- if (!empty($module_settings_form) && $settings == $module || is_array($settings)) { | |
+ if (!empty($module_settings_form) && $module_settings_form !== TRUE && $settings == $module || is_array($settings)) { | |
$form[$module] += $module_settings_form; | |
} | |
else { | |
$form[$module]['message'] = array( | |
'#type' => 'item', | |
- '#value' => t('@module has no settings available.', array('@module' => $module)), | |
+ '#value' => t('@module has no settings available.', array('@module' => drupal_ucfirst($module))), | |
); | |
} | |
} | |
@@ -115,6 +121,79 @@ function services_edit_form_endpoint_authentication_submit($form, $form_state) { | |
services_endpoint_save($endpoint); | |
} | |
+function services_edit_form_endpoint_server($form, $form_state) { | |
+ $form = array(); | |
+ $endpoint = $form_state; | |
+ $servers = services_get_servers(); | |
+ | |
+ $server = !empty($servers[$endpoint->server]) ? $servers[$endpoint->server] : FALSE; | |
+ | |
+ $form['endpoint_object'] = array( | |
+ '#type' => 'value', | |
+ '#value' => $endpoint, | |
+ ); | |
+ | |
+ if (!$server) { | |
+ $form['message'] = array( | |
+ '#type' => 'item', | |
+ '#title' => t('Unknown server @name', array('@name' => $endpoint->server)), | |
+ '#description' => t('No server matching the one used in the endpoint.'), | |
+ ); | |
+ } | |
+ else if (empty($server['settings'])) { | |
+ $form['message'] = array( | |
+ '#type' => 'item', | |
+ '#title' => t('@name has no settings', array('@name' => $endpoint->server)), | |
+ '#description' => t("The server doesn't have any settings that needs to be configured."), | |
+ ); | |
+ } | |
+ else { | |
+ $definition = $server['settings']; | |
+ | |
+ $settings = isset($endpoint->server_settings[$endpoint->server]) ? $endpoint->server_settings[$endpoint->server] : array(); | |
+ | |
+ if (!empty($definition['file'])) { | |
+ call_user_func_array('module_load_include', $definition['file']); | |
+ } | |
+ | |
+ $form[$endpoint->server] = array( | |
+ '#type' => 'fieldset', | |
+ '#title' => $server['name'], | |
+ '#tree' => TRUE, | |
+ ); | |
+ call_user_func_array($definition['form'], array(&$form[$endpoint->server], $endpoint, $settings)); | |
+ | |
+ $form['submit'] = array( | |
+ '#type' => 'submit', | |
+ '#value' => 'Save', | |
+ ); | |
+ } | |
+ | |
+ return $form; | |
+} | |
+ | |
+function services_edit_form_endpoint_server_submit($form, $form_state) { | |
+ $endpoint = $form_state['values']['endpoint_object']; | |
+ $servers = services_get_servers(); | |
+ $definition = $servers[$endpoint->server]['settings']; | |
+ | |
+ $values = $form_state['values'][$endpoint->server]; | |
+ | |
+ // Allow the server to alter the submitted values before they're stored | |
+ // as settings. | |
+ if (!empty($definition['submit'])) { | |
+ if (!empty($definition['file'])) { | |
+ call_user_func_array('module_load_include', $definition['file']); | |
+ } | |
+ $values = call_user_func_array($definition['submit'], array($endpoint, &$values)); | |
+ } | |
+ | |
+ // Store the settings in the endpoint | |
+ $endpoint->server_settings[$endpoint->server] = $values; | |
+ services_endpoint_save($endpoint); | |
+ | |
+ drupal_set_message(t('Your server settings have been saved.')); | |
+} | |
/** | |
* services_edit_endpoint_resources function. | |
@@ -140,7 +219,7 @@ function services_edit_endpoint_resources($endpoint) { | |
* @param object $endpoint | |
* @return Form | |
*/ | |
-function services_edit_form_endpoint_resources($form, &$form_state) { | |
+function services_edit_form_endpoint_resources(&$form_state, $endpoint) { | |
module_load_include('resource_build.inc', 'services'); | |
$form = array(); | |
@@ -148,7 +227,6 @@ function services_edit_form_endpoint_resources($form, &$form_state) { | |
drupal_add_js('misc/tableselect.js'); | |
drupal_add_js(drupal_get_path('module', 'services') . '/js/services.admin.js'); | |
drupal_add_css(drupal_get_path('module', 'services') . '/css/services.admin.css'); | |
- $endpoint = $form_state; | |
$form['endpoint_object'] = array( | |
'#type' => 'value', | |
'#value' => $endpoint, | |
@@ -179,7 +257,7 @@ function services_edit_form_endpoint_resources($form, &$form_state) { | |
'#theme' => 'services_resource_table', | |
); | |
- $ignoreArray = array('actions', 'relationships', 'endpoint', 'name', 'file'); | |
+ $ignoreArray = array('actions', 'relationships', 'endpoint', 'name', 'file', 'targeted_actions'); | |
// Generate the list of methods arranged by resource. | |
foreach ($resources as $resource => $methods) { | |
$form['resources']['table'][$resource] = array( | |
@@ -258,7 +336,7 @@ function services_edit_form_endpoint_resources($form, &$form_state) { | |
* @param array $form_state | |
* @return void | |
*/ | |
-function services_edit_form_endpoint_resources_validate($form, $form_state) { | |
+function services_edit_form_endpoint_resources_validate($form, &$form_state) { | |
$input = $form_state['values']['endpoint_object']; | |
// Validate aliases. | |
@@ -280,7 +358,7 @@ function services_edit_form_endpoint_resources_validate($form, $form_state) { | |
* @param array $form_state | |
* @return void | |
*/ | |
-function services_edit_form_endpoint_resources_submit($form, $form_state) { | |
+function services_edit_form_endpoint_resources_submit($form, &$form_state) { | |
$endpoint = $form_state['values']['endpoint_object']; | |
$existing_resources = _services_build_resources(); | |
@@ -312,4 +390,4 @@ function services_edit_form_endpoint_resources_submit($form, $form_state) { | |
$endpoint->resources = $final_resource; | |
services_endpoint_save($endpoint); | |
drupal_set_message('Resources have been saved'); | |
-} | |
\ No newline at end of file | |
+} | |
diff --git a/plugins/export_ui/services_ctools_export_ui.inc b/plugins/export_ui/services_ctools_export_ui.inc | |
index cb33c63..d4ec02b 100644 | |
--- a/plugins/export_ui/services_ctools_export_ui.inc | |
+++ b/plugins/export_ui/services_ctools_export_ui.inc | |
@@ -17,7 +17,16 @@ $plugin = array( | |
'load arguments' => array('services_ctools_export_ui'), | |
'access arguments' => array('administer services'), | |
'type' => MENU_LOCAL_TASK, | |
- | |
+ ), | |
+ 'server' => array( | |
+ 'path' => 'list/%ctools_export_ui/server', | |
+ 'title' => 'Server', | |
+ 'page callback' => 'ctools_export_ui_switcher_page', | |
+ 'page arguments' => array('services_ctools_export_ui', 'server', 4), | |
+ 'load arguments' => array('services_ctools_export_ui'), | |
+ 'access arguments' => array('administer services'), | |
+ 'type' => MENU_LOCAL_TASK, | |
+ 'weight' => -1, | |
), | |
'authentication' => array( | |
'path' => 'list/%ctools_export_ui/authentication', | |
@@ -34,6 +43,7 @@ $plugin = array( | |
// Add our custom operations. | |
'allowed operations' => array( | |
'resources' => array('title' => t('Edit Resources')), | |
+ 'server' => array('title' => t('Edit Server')), | |
'authentication' => array('title' => t('Edit Authentication')), | |
), | |
'form' => array( | |
@@ -71,15 +81,6 @@ function services_ctools_export_ui_form(&$form, &$form_state) { | |
'#value' => $endpoint, | |
); | |
- $form['title'] = array( | |
- '#type' => 'textfield', | |
- '#size' => 24, | |
- '#maxlength' => 255, | |
- '#default_value' => $endpoint->title, | |
- '#title' => t('Endpoint title'), | |
- '#required' => TRUE, | |
- ); | |
- | |
$servers = services_get_servers(); | |
$server_opts = array( | |
'' => t('-- Select a server'), | |
@@ -138,17 +139,14 @@ function services_ctools_export_ui_form(&$form, &$form_state) { | |
'#options' => $auth_options, | |
'#default_value' => $default_values, | |
'#title' => t('Authentication'), | |
- '#description' => t('Choose which authentication schemes that should ' . | |
- 'be used with your endpoint. If no authentication method is selected ' . | |
- 'the standard Drupal session security is used.'), | |
+ '#description' => t('Choose which authentication schemes that should be used with your endpoint. If no authentication method is selected all requests will be done by an anonymous user.'), | |
); | |
} | |
else { | |
$form['authentication'] = array( | |
'#type' => 'item', | |
'#title' => t('Authentication'), | |
- '#description' => t('No authentication modules are installed, standard ' . | |
- 'Drupal session based security will be used.'), | |
+ '#description' => t('No authentication modules are installed, all requests will be done by an anonymous user.'), | |
); | |
} | |
@@ -182,10 +180,12 @@ function services_ctools_export_ui_form_validate(&$form, &$form_state) { | |
* Submit handler for endpoint. | |
*/ | |
function services_ctools_export_ui_form_submit(&$form, &$form_state) { | |
+ | |
+ variable_set('services_use_content_permissions', $form_state['values']['services_use_content_permissions']); | |
+ | |
$endpoint = $form_state['values']['endpoint_object']; | |
$endpoint->name = $form_state['values']['name']; | |
- $endpoint->title = $form_state['values']['title']; | |
$endpoint->server = $form_state['values']['server']; | |
$endpoint->path = $form_state['values']['path']; | |
$endpoint->debug = $form_state['values']['debug']; | |
diff --git a/resources/comment_resource.inc b/resources/comment_resource.inc | |
index 0270cb2..a7c5b52 100644 | |
--- a/resources/comment_resource.inc | |
+++ b/resources/comment_resource.inc | |
@@ -130,7 +130,7 @@ function _comment_resource_definition() { | |
'name' => 'nid', | |
'type' => 'int', | |
'description' => t('The node id to count all comments.'), | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'nid'), | |
'optional' => FALSE, | |
), | |
), | |
@@ -146,15 +146,15 @@ function _comment_resource_definition() { | |
'name' => 'nid', | |
'type' => 'int', | |
'description' => t('The node id to load comments for.'), | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'nid'), | |
'optional' => FALSE, | |
), | |
array( | |
- 'name' => 'since', | |
- 'type' => 'int', | |
- 'optional' => TRUE, | |
- 'description' => t('Timestamp to count from (defaults to time of last user acces to node).'), | |
- 'source' => 'data', | |
+ 'name' => 'since', | |
+ 'type' => 'int', | |
+ 'optional' => TRUE, | |
+ 'description' => t('Timestamp to count from (defaults to time of last user acces to node).'), | |
+ 'source' => array('data' => 'since'), | |
'optional' => TRUE, | |
'default value' => 0, | |
), | |
@@ -181,20 +181,22 @@ function _comment_resource_definition() { | |
* @param $fields | |
* The fields you want returned. | |
* @param $parameters | |
- * An array of fields and values used to build a sql WHERE clause indicating | |
- * what items should be deleted. | |
+ * An array containing fields and values used to build a sql WHERE clause | |
+ * indicating items to retrieve. | |
+ * @param $page_size | |
+ * Integer number of items to be returned. | |
* @return | |
* An array of comment objects. | |
* | |
* @see _node_resource_index() for more notes | |
**/ | |
-function _comment_resource_index($page, $fields, $parameters) { | |
+function _comment_resource_index($page, $fields, $parameters, $page_title) { | |
// Limit to published nodes if user doesn't have 'administer nodes' | |
// permissions. | |
if (!user_access('administer comments')) { | |
$parameters['status'] = 1; | |
} | |
- $query = services_resource_build_index_query('comments', 'timestamp DESC', $page, $fields, $parameters); | |
+ $query = services_resource_build_index_query('comments', 'c.timestamp DESC', $page, $fields, $parameters, 'c', 'cid', $page_title); | |
// Put together array of matching nodes to return. | |
$results = array(); | |
while ($comments = db_fetch_object($query)) { | |
@@ -214,6 +216,12 @@ function _comment_resource_index($page, $fields, $parameters) { | |
*/ | |
function _comment_resource_create($comment) { | |
+ // Adds backwards compatability with regression fixed in #1083242 | |
+ $comment = _services_arg_value($comment, 'comment'); | |
+ | |
+ if (empty($comment['nid'])) { | |
+ return services_error(t('A nid must be provided')); | |
+ } | |
// Setup form_state | |
$form_state = array(); | |
@@ -225,6 +233,11 @@ function _comment_resource_create($comment) { | |
'cid' => NULL, | |
); | |
+ if (!empty($comment['pid'])) { | |
+ $comment_empty['pid'] = $comment['pid']; | |
+ } | |
+ | |
+ | |
$ret = drupal_execute('comment_form', $form_state, $comment_empty); | |
if ($errors = form_get_errors()) { | |
@@ -264,6 +277,8 @@ function _comment_resource_retrieve($cid) { | |
* Unique identifier for the comment (cid) or FALSE if there was a problem. | |
*/ | |
function _comment_resource_update($cid, $comment) { | |
+ // Adds backwards compatability with regression fixed in #1083242 | |
+ $comment = _services_arg_value($comment, 'data'); | |
$comment['cid'] = $cid; | |
$old_comment = (array) _comment_load($cid); | |
@@ -344,6 +359,9 @@ function _comment_resource_count_new($nid, $since = 0) { | |
* Access check callback for comment controllers. | |
*/ | |
function _comment_resource_access($op = 'view', $args = array()) { | |
+ // Add backwards compatability with regression fixed in #1083242 | |
+ $args = _services_access_value($args, array('comment', 'data')); | |
+ | |
if ($op == 'view' && !isset($args[0])) { | |
return user_access('access comments'); | |
} | |
diff --git a/resources/file_resource.inc b/resources/file_resource.inc | |
index 0b0a96b..0d30512 100644 | |
--- a/resources/file_resource.inc | |
+++ b/resources/file_resource.inc | |
@@ -13,8 +13,8 @@ | |
function _file_resource_definition() { | |
return array( | |
'file' => array( | |
- 'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/file_resource'), | |
'create' => array( | |
+ 'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/file_resource'), | |
'help' => 'Creates a file', | |
'callback' => '_file_resource_create', | |
'access callback' => '_file_resource_access', | |
@@ -31,6 +31,7 @@ function _file_resource_definition() { | |
), | |
), | |
'retrieve' => array( | |
+ 'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/file_resource'), | |
'help' => 'Retrieves a file', | |
'callback' => '_file_resource_retrieve', | |
'access callback' => '_file_resource_access', | |
@@ -48,13 +49,14 @@ function _file_resource_definition() { | |
'name' => 'file_contents', | |
'type' => 'int', | |
'description' => t('To return file contents or not.'), | |
- 'source' => 'data', | |
+ 'source' => array('param' => 'file_contents'), | |
'optional' => FALSE, | |
'default value' => TRUE, | |
), | |
), | |
), | |
'delete' => array( | |
+ 'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/file_resource'), | |
'help' => 'Deletes a file', | |
'callback' => '_file_resource_delete', | |
'access callback' => '_file_resource_access', | |
@@ -98,6 +100,14 @@ function _file_resource_definition() { | |
'default value' => array(), | |
'source' => array('param' => 'parameters'), | |
), | |
+ array( | |
+ 'name' => 'pagesize', | |
+ 'optional' => TRUE, | |
+ 'type' => 'init', | |
+ 'description' => 'Number of records to get per page.', | |
+ 'default value' => variable_get('services_file_index_page_size', 20), | |
+ 'source' => array('param' => 'pagesize'), | |
+ ), | |
), | |
'access callback' => '_file_resource_access', | |
'access arguments' => array('index'), | |
@@ -122,17 +132,18 @@ function _file_resource_definition() { | |
* @param $fields | |
* The fields you want returned. | |
* @param $parameters | |
- * An array of fields and values used to build a sql WHERE clause indicating | |
- * what items should be deleted. | |
+ * An array containing fields and values used to build a sql WHERE clause | |
+ * indicating items to retrieve. | |
+ * @param $page_size | |
+ * Integer number of items to be returned. | |
* @return | |
* An array of file objects. | |
* | |
* @see _node_resource_index() for more notes | |
**/ | |
-function _file_resource_index($page, $fields, $parameters) { | |
- services_resource_build_index_query('files', 'timestamp DESC', $page, $fields, $parameters); | |
+function _file_resource_index($page, $fields, $parameters, $page_size) { | |
- $query = services_resource_build_index_query('files', 'timestamp', $page, $fields, $parameters); | |
+ $query = services_resource_build_index_query('files', 'f.timestamp', $page, $fields, $parameters, 'f', 'fid', $page_size); | |
// Put together array of matching nodes to return. | |
$results = array(); | |
while ($comments = db_fetch_object($query)) { | |
@@ -152,6 +163,8 @@ function _file_resource_index($page, $fields, $parameters) { | |
* Unique identifier for the file (fid) or errors if there was a problem. | |
*/ | |
function _file_resource_create($file) { | |
+ $file = _services_arg_value($file, 'file'); | |
+ | |
global $user; | |
// If the file data is empty then bail. | |
@@ -191,6 +204,14 @@ function _file_resource_create($file) { | |
// Get the directory name for the location of the file: | |
$dir = dirname($file['filepath']); | |
// Build the destination folder tree if it doesn't already exists. | |
+ // @see http://drupal.org/node/180970 | |
+ $dir_array = explode('/', $dir); | |
+ $file_check_directory_array = array(); | |
+ foreach ($dir_array as $dir_element) { | |
+ $file_check_directory_array[] = $dir_element; | |
+ $dir_path_element = implode('/', $file_check_directory_array); | |
+ file_check_directory($dir_path_element, FILE_CREATE_DIRECTORY); | |
+ } | |
if (!file_check_directory($dir, FILE_CREATE_DIRECTORY)) { | |
return services_error("Could not create destination directory for file. " . $dir); | |
} | |
@@ -221,7 +242,7 @@ function _file_resource_create($file) { | |
$file['timestamp'] = time(); | |
// If we made it this far it's safe to record this file in the database. | |
- drupal_write_record('files', $file, $update); | |
+ drupal_write_record('files', $file); | |
return array( | |
'fid' => $file['fid'], | |
@@ -237,16 +258,18 @@ function _file_resource_create($file) { | |
* @return | |
* The file | |
*/ | |
-function _file_resource_retrieve($fid, $file_contents = TRUE) { | |
+function _file_resource_retrieve($fid, $file_contents) { | |
if ($file = db_fetch_array(db_query('SELECT * FROM {files} WHERE fid = %d', $fid))) { | |
if ($file_contents) { | |
- $binaryfile = fopen($file['filepath'], 'rb'); | |
+ $absolute_file_path = $_SERVER['DOCUMENT_ROOT'] . '/' . file_create_path($file['filepath']); | |
+ $binaryfile = fopen($absolute_file_path, 'rb'); | |
if ($binaryfile === FALSE) { | |
services_error(t('Cannot open file with ID %fid.', array('%fid' => $fid))); | |
} | |
$file['file'] = base64_encode(fread($binaryfile, filesize($file['filepath']))); | |
fclose($binaryfile); | |
} | |
+ $file['uri'] = services_resource_uri(array('file', $file['fid'])); | |
return $file; | |
} | |
return services_error(t('There is no file with the given ID.')); | |
@@ -313,6 +336,8 @@ function _file_resource_delete($fid) { | |
* Access check callback for file controllers. | |
*/ | |
function _file_resource_access($op = 'view', $args = array()) { | |
+ // Adds backwards compatability with regression fixed in #1083242 | |
+ $args[0] = _services_access_value($args[0], 'file'); | |
global $user; | |
if (user_access('administer files')) { | |
return TRUE; | |
diff --git a/resources/node_resource.inc b/resources/node_resource.inc | |
index d6c5786..1ae1445 100644 | |
--- a/resources/node_resource.inc | |
+++ b/resources/node_resource.inc | |
@@ -101,32 +101,40 @@ function _node_resource_definition() { | |
'default value' => array(), | |
'source' => array('param' => 'parameters'), | |
), | |
+ array( | |
+ 'name' => 'pagesize', | |
+ 'optional' => TRUE, | |
+ 'type' => 'init', | |
+ 'description' => 'Number of records to get per page.', | |
+ 'default value' => variable_get('services_node_index_page_size', 20), | |
+ 'source' => array('param' => 'pagesize'), | |
+ ), | |
), | |
'access arguments' => array('access content'), | |
), | |
'relationships' => array( | |
- 'nodeFiles' => array( | |
- 'help' => t('This method returns the files on a given node.'), | |
+ 'files' => array( | |
'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/node_resource'), | |
+ 'help' => t('This method returns files associated with a node.'), | |
'access callback' => '_node_resource_access', | |
'access arguments' => array('view'), | |
'access arguments append' => TRUE, | |
'callback' => '_node_resource_load_node_files', | |
'args' => array( | |
array( | |
- 'name' => 'nid', | |
- 'type' => 'int', | |
- 'description' => t('The node id to load files for.'), | |
- 'source' => 'data', | |
- 'optional' => FALSE, | |
+ 'name' => 'nid', | |
+ 'optional' => FALSE, | |
+ 'source' => array('path' => 0), | |
+ 'type' => 'int', | |
+ 'description' => 'The nid of the node whose files we are getting', | |
), | |
array( | |
- 'name' => 'file_contents', | |
- 'type' => 'int', | |
+ 'name' => 'file_contents', | |
+ 'type' => 'int', | |
'description' => t('To return file contents or not.'), | |
- 'source' => 'data', | |
- 'optional' => FALSE, | |
- 'default value' => TRUE, | |
+ 'source' => array('path' => 2), | |
+ 'optional' => TRUE, | |
+ 'default value' => false, | |
), | |
), | |
), | |
@@ -245,10 +253,19 @@ function _node_resource_retrieve($nid) { | |
* @see drupal_execute() | |
*/ | |
function _node_resource_create($node) { | |
- if (!isset($node['type'])) { | |
- return services_error('Missing node type', 406); | |
+ // Adds backwards compatability with regression fixed in #1083242 | |
+ $node = _services_arg_value($node, 'node'); | |
+ | |
+ if (!isset($node['name'])) { | |
+ // Assign username to the node from $user created at auth step. | |
+ global $user; | |
+ $node['name'] = $user->name; | |
} | |
+ // Validate the node. If there is validation error Exception will be thrown | |
+ // so code below won't be executed. | |
+ _node_resource_validate_type($node); | |
+ | |
// Load the required includes for drupal_execute | |
module_load_include('inc', 'node', 'node.pages'); | |
$nid = NULL; | |
@@ -282,6 +299,25 @@ function _node_resource_create($node) { | |
return $node; | |
} | |
+/* | |
+ * Helper function to validate node type information. | |
+ * | |
+ * @param $node | |
+ * Array representing the attributes a node edit form would submit. | |
+ */ | |
+function _node_resource_validate_type($node) { | |
+ if (!isset($node['type'])) { | |
+ return services_error(t('Missing node type'), 406); | |
+ } | |
+ // Wanted to return a gracefull error instead of a blank nid, this should | |
+ // allow for that. | |
+ $types = node_get_types(); | |
+ $node_type = $node['type']; | |
+ if (!isset($types[$node_type])) { | |
+ return services_error(t('Node type @type does not exist.', array('@type' => $node_type)), 406); | |
+ } | |
+} | |
+ | |
/** | |
* Updates a new node based on submitted values. | |
* | |
@@ -299,6 +335,13 @@ function _node_resource_create($node) { | |
* @see drupal_execute() | |
*/ | |
function _node_resource_update($nid, $node) { | |
+ // Adds backwards compatability with regression fixed in #1083242 | |
+ $node = _services_arg_value($node, 'node'); | |
+ | |
+ // Validate the node. If there is validation error Exception will be thrown | |
+ // so code below won't be executed. | |
+ _node_resource_validate_type($node); | |
+ | |
$node['nid'] = $nid; | |
$old_node = node_load($nid); | |
@@ -356,10 +399,10 @@ function _node_resource_delete($nid) { | |
* Page number of results to return (in pages of 20). | |
* @param $fields | |
* The fields you want returned. | |
- * @param $parameters | |
- * An array of fields and values used to build a sql WHERE clause indicating | |
- * what items should be deleted. | |
- * @return | |
+ * An array containing fields and values used to build a sql WHERE clause | |
+ * indicating items to retrieve. | |
+ * @param $page_size | |
+ * Integer number of items to be returned. | |
* An array of node objects. | |
* | |
* @todo | |
@@ -368,13 +411,13 @@ function _node_resource_delete($nid) { | |
* - Is there an easier syntax we can define which can make the urls | |
* for index requests more straightforward? | |
*/ | |
-function _node_resource_index($page, $fields, $parameters) { | |
+function _node_resource_index($page, $fields, $parameters, $page_size) { | |
// Limit to published nodes if user doesn't have 'administer nodes' | |
// permissions. | |
if (!user_access('administer nodes')) { | |
$parameters['status'] = 1; | |
} | |
- $query = services_resource_build_index_query('node', 'sticky DESC, created DESC', $page, $fields, $parameters); | |
+ $query = services_resource_build_index_query('node', 'n.sticky DESC, n.created DESC', $page, $fields, $parameters, 'n', 'nid', $page_size); | |
// Put together array of matching nodes to return. | |
$results = array(); | |
while ($node = db_fetch_object($query)) { | |
@@ -394,7 +437,7 @@ function _node_resource_index($page, $fields, $parameters) { | |
* @return | |
* Array. A list of all files from the given node | |
*/ | |
-function _node_resource_load_node_files($nid, $include_file_contents = TRUE) { | |
+function _node_resource_load_node_files($nid, $include_file_contents) { | |
module_load_include('inc', 'services', 'resources/file_resource'); | |
$node = node_load($nid); | |
if (!isset($node->files)) { | |
@@ -407,21 +450,7 @@ function _node_resource_load_node_files($nid, $include_file_contents = TRUE) { | |
if (!$file->list) { | |
continue; | |
} | |
- $return[$file->fid] = array( | |
- 'filename' => $file->filename, | |
- 'uid' => $file->uid, | |
- 'filemime' => $file->filemime, | |
- 'filesize' => $file->filesize, | |
- 'status' => $file->status, | |
- 'timestamp' => $file->timestamp | |
- ); | |
- // If to add content of the file. | |
- if ($file_include_contents) { | |
- $filepath = file_create_path($file->filepath); | |
- $binaryfile = fopen($filepath, 'rb'); | |
- $return[$file->fid]['file'] = base64_encode(fread($binaryfile, filesize($filepath))); | |
- fclose($binaryfile); | |
- } | |
+ $return[$file->fid] = _file_resource_retrieve($file->fid, $include_file_contents); | |
} | |
return $return; | |
@@ -439,6 +468,8 @@ function _node_resource_load_node_files($nid, $include_file_contents = TRUE) { | |
* @see node_access() | |
*/ | |
function _node_resource_access($op = 'view', $args = array()) { | |
+ // Adds backwards compatability with regression fixed in #1083242 | |
+ $args[0] = _services_access_value($args[0], 'node'); | |
// Make sure we have an object or this all fails, some servers can | |
// mess up the types. | |
if (is_array($args[0])) { | |
@@ -461,4 +492,4 @@ function _node_resource_access($op = 'view', $args = array()) { | |
else { | |
return services_error('Node id: '. $args[0]->nid .' could not be found', 404); | |
} | |
-} | |
\ No newline at end of file | |
+} | |
diff --git a/resources/system_resource.inc b/resources/system_resource.inc | |
index 6d626f5..9c769bd 100644 | |
--- a/resources/system_resource.inc | |
+++ b/resources/system_resource.inc | |
@@ -24,14 +24,14 @@ function _system_resource_definition() { | |
array( | |
'name' => 'name', | |
'optional' => TRUE, | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'name'), | |
'description' => t('The name of the variable to return.'), | |
'type' => 'string', | |
), | |
array( | |
'name' => 'default', | |
'optional' => FALSE, | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'default'), | |
'description' => t('The default value to use if this variable has never been set.'), | |
'type' => 'string', | |
), | |
@@ -47,14 +47,14 @@ function _system_resource_definition() { | |
array( | |
'name' => 'name', | |
'optional' => FALSE, | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'name'), | |
'description' => t('The name of the variable to set.'), | |
'type' => 'string', | |
), | |
array( | |
'name' => 'value', | |
'optional' => FALSE, | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'value'), | |
'description' => t('The value to set.'), | |
'type' => 'string', | |
), | |
@@ -70,7 +70,7 @@ function _system_resource_definition() { | |
array( | |
'name' => 'name', | |
'optional' => FALSE, | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'name'), | |
'description' => t('The name of the variable to delete.'), | |
'type' => 'string', | |
), | |
@@ -92,6 +92,9 @@ function _system_resource_connect() { | |
$return = new stdClass(); | |
$return->sessid = session_id(); | |
+ | |
+ services_remove_user_data($user); | |
+ | |
$return->user = $user; | |
return $return; | |
diff --git a/resources/taxonomy_resource.inc b/resources/taxonomy_resource.inc | |
index 00b93e0..d96e2b3 100644 | |
--- a/resources/taxonomy_resource.inc | |
+++ b/resources/taxonomy_resource.inc | |
@@ -108,6 +108,14 @@ function _taxonomy_resource_definition() { | |
'default value' => array(), | |
'source' => array('param' => 'parameters'), | |
), | |
+ array( | |
+ 'name' => 'pagesize', | |
+ 'optional' => TRUE, | |
+ 'type' => 'init', | |
+ 'description' => 'Number of records to get per page.', | |
+ 'default value' => variable_get('services_taxonomy_term_index_page_size', 20), | |
+ 'source' => array('param' => 'pagesize'), | |
+ ), | |
), | |
'access arguments' => array('access content'), | |
), | |
@@ -124,14 +132,14 @@ function _taxonomy_resource_definition() { | |
'name' => 'tids', | |
'type' => 'string', | |
'description' => t('The vocabulary ids to retrieve, separated by comma.'), | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'tids'), | |
'optional' => FALSE, | |
), | |
array( | |
'name' => 'fields', | |
'type' => 'string', | |
'description' => t('The fields to retrieve, separated by comma.'), | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'fields'), | |
'optional' => TRUE, | |
'default value' => array(), | |
), | |
@@ -139,7 +147,7 @@ function _taxonomy_resource_definition() { | |
'name' => 'operator', | |
'type' => 'string', | |
'description' => t('How to interpret multiple IDs in the array. Can be "or" or "and".'), | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'operator'), | |
'optional' => TRUE, | |
'default value' => 'or', | |
), | |
@@ -147,7 +155,7 @@ function _taxonomy_resource_definition() { | |
'name' => 'depth', | |
'type' => 'int', | |
'description' => t('How many levels deep to traverse the taxonomy tree. Can be a nonnegative integer or "all".'), | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'depth'), | |
'optional' => TRUE, | |
'default value' => 0, | |
), | |
@@ -155,7 +163,7 @@ function _taxonomy_resource_definition() { | |
'name' => 'pager', | |
'type' => 'int', | |
'description' => t('Whether the nodes are to be used with a pager (the case on most Drupal pages) or not (in an XML feed, for example).'), | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'pager'), | |
'optional' => TRUE, | |
'default value' => TRUE, | |
), | |
@@ -163,7 +171,7 @@ function _taxonomy_resource_definition() { | |
'name' => 'order', | |
'type' => 'string', | |
'description' => t('The order clause for the query that retrieve the nodes.'), | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'order'), | |
'optional' => TRUE, | |
'default value' => 'n.sticky DESC, n.created DESC', | |
), | |
@@ -270,6 +278,14 @@ function _taxonomy_resource_definition() { | |
'default value' => array(), | |
'source' => array('param' => 'parameters'), | |
), | |
+ array( | |
+ 'name' => 'pagesize', | |
+ 'optional' => TRUE, | |
+ 'type' => 'init', | |
+ 'description' => 'Number of records to get per page.', | |
+ 'default value' => variable_get('services_taxonomy_vocabulary_index_page_size', 20), | |
+ 'source' => array('param' => 'pagesize'), | |
+ ), | |
), | |
'access arguments' => array('access content'), | |
), | |
@@ -286,24 +302,24 @@ function _taxonomy_resource_definition() { | |
'name' => 'vid', | |
'type' => 'int', | |
'description' => t('The vocabulary id to retrieve.'), | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'vid'), | |
'optional' => FALSE, | |
), | |
array( | |
- 'name' => 'parent', | |
- 'type' => 'int', | |
- 'description' => t('The term ID under which to generate the tree. If 0, generate the tree for the entire vocabulary.'), | |
- 'source' => 'data', | |
- 'default value' => 0, | |
- 'optional' => TRUE, | |
+ 'name' => 'parent', | |
+ 'type' => 'int', | |
+ 'description' => t('The term ID under which to generate the tree. If 0, generate the tree for the entire vocabulary.'), | |
+ 'source' => array('data' => 'parent'), | |
+ 'default value' => 0, | |
+ 'optional' => TRUE, | |
), | |
array( | |
- 'name' => 'maxdepth', | |
- 'type' => 'int', | |
- 'description' => t('The number of levels of the tree to return. Leave NULL to return all levels.'), | |
- 'source' => 'data', | |
- 'optional' => TRUE, | |
- 'default value' => NULL, | |
+ 'name' => 'maxdepth', | |
+ 'type' => 'int', | |
+ 'description' => t('The number of levels of the tree to return. Leave NULL to return all levels.'), | |
+ 'source' => array('data' => 'maxdepth'), | |
+ 'default value' => NULL, | |
+ 'optional' => TRUE, | |
), | |
), | |
), | |
@@ -326,15 +342,17 @@ function _taxonomy_resource_definition() { | |
* @param $fields | |
* The fields you want returned. | |
* @param $parameters | |
- * An array of fields and values used to build a sql WHERE clause indicating | |
- * what items should be returned. | |
+ * An array containing fields and values used to build a sql WHERE clause | |
+ * indicating items to retrieve. | |
+ * @param $page_size | |
+ * Integer number of items to be returned. | |
* @return | |
* An array of term objects. | |
* | |
* @see _node_resource_index() for more notes | |
**/ | |
-function _taxonomy_term_resource_index($page, $fields, $parameters) { | |
- $query = services_resource_build_index_query('term_data', 'vid DESC, weight DESC, name DESC', $page, $fields, $parameters); | |
+function _taxonomy_term_resource_index($page, $fields, $parameters, $page_size) { | |
+ $query = services_resource_build_index_query('term_data', 't.vid DESC, t.weight DESC, t.name DESC', $page, $fields, $parameters, 't', 'tid', $page_size); | |
$results = array(); | |
while ($taxonomy = db_fetch_object($query)) { | |
@@ -358,8 +376,10 @@ function _taxonomy_term_resource_index($page, $fields, $parameters) { | |
* @param $fields | |
* The fields you want returned. | |
* @param $parameters | |
- * An array of fields and values used to build a sql WHERE clause indicating | |
- * what items should be returned. | |
+* An array containing fields and values used to build a sql WHERE clause | |
+ * indicating items to retrieve. | |
+ * @param $page_size | |
+ * Integer number of items to be returned. | |
* @return | |
* An array of vocabulary objects. | |
* | |
@@ -368,8 +388,8 @@ function _taxonomy_term_resource_index($page, $fields, $parameters) { | |
* | |
* @see _node_resource_index() for more notes | |
**/ | |
-function _taxonomy_vocabulary_resource_index($page, $fields, $parameters) { | |
- $query = services_resource_build_index_query('vocabulary', 'weight DESC, name DESC', $page, $fields, $parameters); | |
+function _taxonomy_vocabulary_resource_index($page, $fields, $parameters, $page_size) { | |
+ $query = services_resource_build_index_query('vocabulary', 'v.weight DESC, v.name DESC', $page, $fields, $parameters, 'v', 'vid', $page_size); | |
$results = array(); | |
while ($taxonomy = db_fetch_object($query)) { | |
@@ -403,6 +423,9 @@ function _taxonomy_term_resource_retrieve($tid) { | |
* @see taxonomy_save_term() | |
*/ | |
function _taxonomy_term_resource_create($term) { | |
+ // Adds backwards compatability with regression fixed in #1083242 | |
+ $term = _services_arg_value($term, 'term'); | |
+ | |
return taxonomy_save_term($term); | |
} | |
@@ -419,6 +442,9 @@ function _taxonomy_term_resource_create($term) { | |
* @see taxonomy_save_term() | |
*/ | |
function _taxonomy_term_resource_update($tid, $term) { | |
+ // Adds backwards compatability with regression fixed in #1083242 | |
+ $term = _services_arg_value($term, 'term'); | |
+ | |
$term['tid'] = $tid; | |
return _taxonomy_term_resource_create($term); | |
} | |
@@ -482,7 +508,10 @@ function _taxonomy_vocabulary_resource_retrieve($vid) { | |
* @see taxonomy_save_vocabulary() | |
*/ | |
function _taxonomy_vocabulary_resource_create($vocabulary) { | |
- return taxonomy_save_vocabulary($vocabulary); | |
+ // Adds backwards compatability with regression fixed in #1083242 | |
+ $vocabulary = _services_arg_value($vocabulary, 'vocabulary'); | |
+ taxonomy_save_vocabulary($vocabulary); | |
+ return $vocabulary; | |
} | |
/** | |
@@ -498,6 +527,9 @@ function _taxonomy_vocabulary_resource_create($vocabulary) { | |
* @see taxonomy_save_vocabulary() | |
*/ | |
function _taxonomy_vocabulary_resource_update($vid, $vocabulary) { | |
+ // Adds backwards compatability with regression fixed in #1083242 | |
+ $vocabulary = _services_arg_value($vocabulary, 'vocabulary'); | |
+ | |
$vocabulary['vid'] = $vid; | |
return _taxonomy_vocabulary_resource_create($vocabulary); | |
} | |
@@ -559,7 +591,7 @@ function taxonomy_service_select_nodes($tids, $fields, $operator, $depth, $pager | |
} | |
function _taxonomy_resource_access($op = 'view', $args = array()) { | |
- if (user_access('administer taxonomy')) { | |
+ if (user_access('administer taxonomy') || user_access('get taxonomy tree')) { | |
return TRUE; | |
} | |
// TODO - check perms of user to return ability to interact with terms / vocabulary | |
diff --git a/resources/user_resource.inc b/resources/user_resource.inc | |
index cd46157..f6cb764 100644 | |
--- a/resources/user_resource.inc | |
+++ b/resources/user_resource.inc | |
@@ -108,6 +108,14 @@ function _user_resource_definition() { | |
'default value' => NULL, | |
'source' => array('param' => 'parameters'), | |
), | |
+ array( | |
+ 'name' => 'pagesize', | |
+ 'optional' => TRUE, | |
+ 'type' => 'init', | |
+ 'description' => 'Number of records to get per page.', | |
+ 'default value' => variable_get('services_user_index_page_size', 20), | |
+ 'source' => array('param' => 'pagesize'), | |
+ ), | |
), | |
'access arguments' => array('access user profiles'), | |
'access arguments append' => FALSE, | |
@@ -121,14 +129,14 @@ function _user_resource_definition() { | |
'name' => 'username', | |
'type' => 'string', | |
'description' => 'A valid username', | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'username'), | |
'optional' => FALSE, | |
), | |
array( | |
'name' => 'password', | |
'type' => 'string', | |
'description' => 'A valid password', | |
- 'source' => 'data', | |
+ 'source' => array('data' => 'password'), | |
'optional' => FALSE, | |
), | |
), | |
@@ -182,6 +190,8 @@ function _user_resource_retrieve($uid) { | |
return services_error(t('There is no user with such ID.'), 404); | |
} | |
+ services_remove_user_data($account); | |
+ | |
// Everything went right. | |
return $account; | |
} | |
@@ -211,6 +221,9 @@ function _user_resource_retrieve($uid) { | |
* The user object of the newly created user. | |
*/ | |
function _user_resource_create($account) { | |
+ // Adds backwards compatability with regression fixed in #1083242 | |
+ $account = _services_arg_value($account, 'account'); | |
+ | |
// Load the required includes for saving profile information | |
// with drupal_execute(). | |
module_load_include('inc', 'user', 'user.pages'); | |
@@ -254,7 +267,11 @@ function _user_resource_create($account) { | |
* The modified user object. | |
*/ | |
function _user_resource_update($uid, $account) { | |
+ // Adds backwards compatability with regression fixed in #1083242 | |
+ $account = _services_arg_value($account, 'data'); | |
+ | |
$account['uid'] = $uid; | |
+ $user = user_load($uid); | |
// Load the required includes for saving profile information | |
// with drupal_execute(). | |
@@ -287,14 +304,22 @@ function _user_resource_update($uid, $account) { | |
$form_state['values']['op'] = 'Save'; | |
$form_state['values']['_category'] = $category; | |
$form_state['values']['_account'] = (object)$account; | |
+ if (isset($account['pass'])) { | |
+ $form_state['values']['pass'] = array( | |
+ 'pass1' => $account['pass'], | |
+ 'pass2' => $account['pass'], | |
+ ); | |
+ } | |
- $ret = drupal_execute('user_profile_form', $form_state, (object)$account, $category); | |
+ $ret = drupal_execute('user_profile_form', $form_state, $user, $category); | |
// Error if needed. | |
if ($errors = form_get_errors()) { | |
return services_error(implode(" ", $errors), 406, array('form_errors' => $errors)); | |
} | |
+ services_remove_user_data($account); | |
+ | |
return $account; | |
} | |
@@ -347,6 +372,9 @@ function _user_resource_login($username, $password) { | |
$return = new stdClass(); | |
$return->sessid = session_id(); | |
$return->session_name = session_name(); | |
+ | |
+ services_remove_user_data($user); | |
+ | |
$return->user = $user; | |
return $return; | |
@@ -370,7 +398,8 @@ function _user_resource_logout() { | |
// Destroy the current session: | |
session_destroy(); | |
- module_invoke_all('user', 'logout', NULL, $user); | |
+ $null = NULL; // Only variables can be passed by reference workaround | |
+ user_module_invoke('logout', $null, $user ); | |
// Load the anonymous user | |
$user = drupal_anonymous_user(); | |
@@ -393,18 +422,20 @@ function _user_resource_logout() { | |
* @param $fields | |
* The fields you want returned. | |
* @param $parameters | |
- * An array of fields and values used to build a sql WHERE clause indicating | |
- * what items should be deleted. | |
+ * An array containing fields and values used to build a sql WHERE clause | |
+ * indicating items to retrieve. | |
+ * @param $page_size | |
+ * Integer number of items to be returned. | |
* @return | |
* An array of user objects. | |
* | |
* @see _node_resource_index() for more notes | |
*/ | |
-function _user_resource_index($page, $fields, $parameters) { | |
+function _user_resource_index($page, $fields, $parameters, $page_size) { | |
if (!user_access('administer users')) { | |
$parameters['active'] = 1; | |
} | |
- $query = services_resource_build_index_query('users', 'created DESC', $page, $fields, $parameters); | |
+ $query = services_resource_build_index_query('users', 'u.created DESC', $page, $fields, $parameters, 'u', 'uid', $page_size); | |
$results = array(); | |
while ($user = db_fetch_object($query)) { | |
@@ -417,6 +448,9 @@ function _user_resource_index($page, $fields, $parameters) { | |
* Access check callback for user resource. | |
*/ | |
function _user_resource_access($op = 'view', $args = array()) { | |
+ // Adds backwards compatability with regression fixed in #1083242 | |
+ $args[0] = _services_access_value($args[0], array('account', 'data')); | |
+ | |
global $user; | |
switch ($op) { | |
case 'view': | |
diff --git a/servers/rest_server/includes/RESTServer.inc b/servers/rest_server/includes/RESTServer.inc | |
index f1ab017..896f9e7 100755 | |
--- a/servers/rest_server/includes/RESTServer.inc | |
+++ b/servers/rest_server/includes/RESTServer.inc | |
@@ -53,6 +53,15 @@ class RESTServer { | |
} | |
$endpoint = services_get_server_info('endpoint', ''); | |
+ | |
+ $endpoint_definition = services_endpoint_load($endpoint); | |
+ | |
+ // Get the server settings from the endpoint. | |
+ $this->settings = !empty($endpoint_definition->server_settings['rest_server']) ? $endpoint_definition->server_settings['rest_server'] : array(); | |
+ // Normalize the settings so that we get the expected structure | |
+ // and sensible defaults. | |
+ $this->settings = rest_server_setup_settings($this->settings); | |
+ | |
$resources = services_get_resources($endpoint); | |
$controller = FALSE; | |
if (!empty($resource_name) && isset($resources[$resource_name])) { | |
@@ -62,13 +71,17 @@ class RESTServer { | |
$controller = $this->resolveController($resource, $method, $path); | |
} | |
else { | |
- throw new ServicesException(t('Could not find resource @name', array( | |
- '@name' => $resource_name, | |
- )), 404); | |
+ //This will stop the 404 from happening when you request just the endpoint. | |
+ if($endpoint_definition->path == $resource_name) { | |
+ $response = t('Services Endpoint "@name" has been setup successfully.', array('@name' => $endpoint)); | |
+ drupal_alter('services_endpoint_response', $response); | |
+ return $response; | |
+ } | |
+ return services_error(t('Could not find resource @name.', array('@name' => $resource_name)), 404); | |
} | |
if (!$controller) { | |
- throw new ServicesException('Could not find the controller', 404); | |
+ return services_error(t('Could not find the controller.'), 404); | |
} | |
// Parse the request data | |
@@ -83,7 +96,6 @@ class RESTServer { | |
// Negotiate response format based on accept-headers if we | |
// don't have a response format | |
if (empty($response_format)) { | |
- module_load_include('php', 'rest_server', 'lib/mimeparse'); | |
$mime_candidates = array(); | |
$mime_map = array(); | |
@@ -98,21 +110,16 @@ class RESTServer { | |
} | |
// Get the best matching format, default to json | |
+ $response_format = 'json'; | |
if (isset($_SERVER['HTTP_ACCEPT'])) { | |
- $mime = new Mimeparse(); | |
+ $mime = $this->mimeParse(); | |
$mime_type = $mime->best_match($mime_candidates, $_SERVER['HTTP_ACCEPT']); | |
- } | |
- | |
- if (isset($mime_type) && $mime_type) { | |
$response_format = $mime_map[$mime_type]; | |
} | |
- else { | |
- $response_format = 'json'; | |
- } | |
} | |
// Check if we support the response format and determine the mime type | |
- if (empty($mime_type) && !empty($response_format) && isset($formats[$response_format])) { | |
+ if (empty($mime_type) && isset($formats[$response_format])) { | |
$formatter = $formats[$response_format]; | |
if (!isset($formatter['model']) || $this->supportedControllerModel($controller, $formatter)) { | |
$mime_type = $formatter['mime types'][0]; | |
@@ -120,7 +127,7 @@ class RESTServer { | |
} | |
if (empty($response_format) || empty($mime_type)) { | |
- throw new ServicesException('Unknown or unsupported response format', 406); | |
+ return services_error(t('Unknown or unsupported response format.'), 406); | |
} | |
// Give the model (if any) a opportunity to alter the arguments. | |
@@ -152,7 +159,7 @@ class RESTServer { | |
$result = services_controller_execute($controller, $arguments); | |
} catch (ServicesException $e) { | |
$errors = $this->handleException($e); | |
- drupal_alter('rest_server_exectue_errors', $errors, $controller, $arguments); | |
+ drupal_alter('rest_server_execute_errors', $errors, $controller, $arguments); | |
$result = $errors; | |
} | |
$formatter = $formats[$response_format]; | |
@@ -218,16 +225,15 @@ class RESTServer { | |
} | |
} | |
else { | |
- if (isset($sources[$info['source']][$info['name']])) { | |
- $arguments[$i] = $sources[$info['source']][$info['name']]; | |
- } | |
+ if (isset($sources[$info['source']])) { | |
+ $arguments[$i] = $sources[$info['source']]; | |
+ } | |
} | |
// Convert to array if argument expected to be array. | |
if ($info['type'] == 'array' && isset($arguments[$i])) { | |
$arguments[$i] = (array)$arguments[$i]; | |
} | |
} | |
- | |
// When argument isn't set, insert default value if provided or | |
// throw a exception if the argument isn't optional. | |
if (!isset($arguments[$i])) { | |
@@ -249,15 +255,11 @@ class RESTServer { | |
} | |
private function parseRequest($method, $controller) { | |
- if (($method == 'POST' || $method == 'PUT') && isset($_SERVER['CONTENT_TYPE'])) { | |
- $type = self::parseContentHeader($_SERVER['CONTENT_TYPE']); | |
- } | |
+ | |
switch ($method) { | |
case 'POST': | |
- if (isset($type['value']) && $type['value'] == 'application/x-www-form-urlencoded') { | |
- return $_POST; | |
- } | |
case 'PUT': | |
+ $type = self::parseContentHeader($_SERVER['CONTENT_TYPE']); | |
// Get the mime type for the request, default to form-urlencoded | |
if (isset($type['value'])) { | |
$mime = $type['value']; | |
@@ -267,11 +269,9 @@ class RESTServer { | |
} | |
// Get the parser for the mime type | |
- $parser = $this->requestParsers($mime, $controller); | |
+ $parser = $this->requestParser($mime, $controller); | |
if (!$parser) { | |
- throw new ServicesException(t('Unsupported request content type !mime', array( | |
- '!mime' => $mime, | |
- )), 406); | |
+ return services_error(t('Unsupported request content type @mime', array('@mime' => $mime)), 406); | |
} | |
// Read the raw input stream | |
@@ -359,7 +359,7 @@ class RESTServer { | |
$errors = array_merge($errors, call_user_func_array($function, $args)); | |
} | |
if (!empty($errors)) { | |
- throw new ServicesException(t('Errors while validating the file'), 406, $errors); | |
+ return services_error(t('Errors while validating the file - @errors', array('@errors' => implode(" ", $errors))), 406); | |
} | |
drupal_write_record('files', $file); | |
@@ -400,32 +400,32 @@ class RESTServer { | |
return $view->render(); | |
} | |
- private function requestParsers($mime=NULL, $controller=NULL) { | |
- static $parsers; | |
- | |
- if ($mime && $controller && !empty($controller['rest request parsers'])) { | |
+ /** | |
+ * Get best match parser for $controller based on $mime type. | |
+ */ | |
+ private function requestParser($mime, $controller = NULL) { | |
+ // Check if the controller has declared support for parsing the mime type. | |
+ if ($controller && !empty($controller['rest request parsers'])) { | |
$parser = $this->matchParser($mime, $controller['rest request parsers']); | |
if ($parser) { | |
return $parser; | |
} | |
} | |
- if (!$parsers) { | |
- $parsers = array( | |
- 'application/x-www-form-urlencoded' => 'RESTServer::parseURLEncoded', | |
- 'application/x-yaml' => 'RESTServer::parseYAML', | |
- 'application/json' => 'RESTServer::parseJSON', | |
- 'application/vnd.php.serialized' => 'RESTServer::parsePHP', | |
- ); | |
- drupal_alter('rest_server_request_parsers', $parsers); | |
- } | |
- | |
- if ($mime) { | |
- return $this->matchParser($mime, $parsers); | |
+ $parsers = rest_server_request_parsers(); | |
+ // Remove parsers that have been disabled for this endpoint. | |
+ foreach (array_keys($parsers) as $key) { | |
+ if (!$this->settings['parsers'][$key]) { | |
+ unset($parsers[$key]); | |
+ } | |
} | |
- return $parsers; | |
+ return $this->matchParser($mime, $parsers); | |
} | |
- | |
+ /** | |
+ * Create a instance of the Mimeparse utility class. | |
+ * | |
+ * @return Mimeparse | |
+ */ | |
private function mimeParse() { | |
static $mimeparse; | |
if (!$mimeparse) { | |
@@ -438,9 +438,7 @@ class RESTServer { | |
private function matchParser($mime, $parsers) { | |
$mimeparse = $this->mimeParse(); | |
$mime_type = $mimeparse->best_match(array_keys($parsers), $mime); | |
- if ($mime_type) { | |
- return $parsers[$mime_type]; | |
- } | |
+ return ($mime_type) ? $parsers[$mime_type] : FALSE; | |
} | |
public static function parseURLEncoded($handle) { | |
@@ -451,6 +449,10 @@ class RESTServer { | |
public static function parsePHP($handle) { | |
return unserialize(self::contentFromStream($handle)); | |
} | |
+ | |
+ public static function parseFile($handle) { | |
+ return self::contentFromStream($handle); | |
+ } | |
public static function parseJSON($handle) { | |
return json_decode(self::contentFromStream($handle), TRUE); | |
@@ -462,52 +464,18 @@ class RESTServer { | |
} | |
private function responseFormatters($format=NULL) { | |
- static $formatters; | |
- | |
- if (!$formatters) { | |
- $formatters = array( | |
- 'xml' => array( | |
- 'mime types' => array('application/xml', 'text/xml'), | |
- 'view' => 'RESTServerViewBuiltIn', | |
- 'view arguments' => array('format' => 'xml'), | |
- ), | |
- 'json' => array( | |
- 'mime types' => array('application/json'), | |
- 'view' => 'RESTServerViewBuiltIn', | |
- 'view arguments' => array('format' => 'json'), | |
- ), | |
- 'jsonp' => array( | |
- 'mime types' => array('text/javascript', 'application/javascript'), | |
- 'view' => 'RESTServerViewBuiltIn', | |
- 'view arguments' => array('format' => 'jsonp'), | |
- ), | |
- 'php' => array( | |
- 'mime types' => array('application/vnd.php.serialized'), | |
- 'view' => 'RESTServerViewBuiltIn', | |
- 'view arguments' => array('format' => 'php'), | |
- ), | |
- 'yaml' => array( | |
- 'mime types' => array('text/plain', 'application/x-yaml', 'text/yaml'), | |
- 'view' => 'RESTServerViewBuiltIn', | |
- 'view arguments' => array('format' => 'yaml'), | |
- ), | |
- 'bencode' => array( | |
- 'mime types' => array('application/x-bencode'), | |
- 'view' => 'RESTServerViewBuiltIn', | |
- 'view arguments' => array('format' => 'bencode'), | |
- ), | |
- 'rss' => array( | |
- 'model' => 'ResourceFeedModel', | |
- 'mime types' => array('text/xml'), | |
- 'view' => 'RssFormatView', | |
- ), | |
- ); | |
- drupal_alter('rest_server_response_formatters', $formatters); | |
- } | |
+ $formatters = rest_server_response_formatters(); | |
+ // Remove formatters that have been disabled for this endpoint. | |
+ foreach (array_keys($formatters) as $key) { | |
+ if (!$this->settings['formatters'][$key]) { | |
+ unset($formatters[$key]); | |
+ } | |
+ } | |
if ($format) { | |
return isset($formatters[$format]) ? $formatters[$format] : FALSE; | |
} | |
+ | |
return $formatters; | |
} | |
@@ -530,15 +498,21 @@ class RESTServer { | |
function handleException($e){ | |
$code = $e->getCode(); | |
switch ($code) { | |
+ case 204: | |
+ drupal_set_header('HTTP/1.0 204 No Content: ' . $e->getMessage()); | |
+ break; | |
+ case 304: | |
+ drupal_set_header('HTTP/1.0 304 Not Modified: ' . $e->getMessage()); | |
+ break; | |
case 401: | |
drupal_set_header('HTTP/1.0 401 Unauthorized: ' . $e->getMessage()); | |
- break; | |
+ break; | |
case 404: | |
drupal_set_header('HTTP/1.0 404 Not found: ' . $e->getMessage()); | |
- break; | |
+ break; | |
case 406: | |
drupal_set_header('HTTP/1.0 406 Not Acceptable: ' . $e->getMessage()); | |
- break; | |
+ break; | |
default: | |
if ($code >= 400 && $code < 600) { | |
drupal_set_header('HTTP/1.0 ' . $code . ' ' . $e->getMessage()); | |
@@ -546,12 +520,13 @@ class RESTServer { | |
else { | |
drupal_set_header('HTTP/1.0 500 An error occurred: (' . $code . ') ' . $e->getMessage()); | |
} | |
- break; | |
+ break; | |
} | |
if(method_exists($e,'getData')){ | |
return $e->getData(); | |
} | |
} | |
+ | |
private function resolveController($resource, $method, $path) { | |
$pc = count($path); | |
// Use the index handler for all empty path request, except on POST | |
@@ -589,7 +564,7 @@ class RESTServer { | |
} | |
// Detect action requests targeted at specific resources | |
elseif ($pc >= 2 && $method == 'POST') { | |
- $action = $resource['targeted actions'][$path[1]]; | |
+ $action = $resource['targeted_actions'][$path[1]]; | |
return $action; | |
} | |
} | |
diff --git a/servers/rest_server/rest_server.api.php b/servers/rest_server/rest_server.api.php | |
index 7e0217f..23b1d26 100644 | |
--- a/servers/rest_server/rest_server.api.php | |
+++ b/servers/rest_server/rest_server.api.php | |
@@ -11,22 +11,51 @@ | |
*/ | |
/** | |
- * ... | |
+ * Triggered when the REST server request a list of available request parsers. | |
* | |
+ * @param array $parsers | |
+ * An associative array of parser callbacks keyed by mime-type. | |
+ * @return void | |
*/ | |
-function hook_rest_server_request_parsers_alter() { | |
- return array( | |
- 'name' => 'REST', | |
- ); | |
+function hook_rest_server_request_parsers_alter(&$parsers) { | |
+ $parsers['application/json'] = 'RESTServer::parseJSON'; | |
+ unset($parsers['application/x-www-form-urlencoded']); | |
} | |
/** | |
- * ... | |
+ * Triggered when the REST server request a list of supported response formats. | |
* | |
+ * @param array $formatters | |
+ * An associative array of formatter info arrays keyed by type extension. The | |
+ * formatter info specifies an array of 'mime types' that corresponds to the | |
+ * output format; a 'view' class that is a subclass of RESTServerView; and | |
+ * 'view arguments' that should be passed to the view when it is created; | |
+ * a 'model' can also be specified which the controller then must declare | |
+ * support for to be able to serve data in that format. | |
+ * @return void | |
*/ | |
-function hook_rest_server_response_formatters_alter() { | |
- return array( | |
- 'name' => 'REST', | |
+function hook_rest_server_response_formatters_alter(&$formatters) { | |
+ /* | |
+ * Sample modifications of the formatters array. Both yaml and | |
+ * rss are formats that already are supported, so the changes are | |
+ * nonsensical but illustrates the proper use of this hook. | |
+ */ | |
+ | |
+ // Add a Yaml response format. | |
+ $formatters['yaml'] = array( | |
+ 'mime types' => array('text/plain', 'application/x-yaml', 'text/yaml'), | |
+ 'view' => 'RESTServerViewBuiltIn', | |
+ 'view arguments' => array('format' => 'yaml'), | |
); | |
+ | |
+ // Add a Rss response format. | |
+ $formatters['rss'] = array( | |
+ 'model' => 'ResourceFeedModel', | |
+ 'mime types' => array('text/xml'), | |
+ 'view' => 'RssFormatView', | |
+ ); | |
+ | |
+ // Remove the jsonp response format. | |
+ unset($formatters['jsonp']); | |
} | |
diff --git a/servers/rest_server/rest_server.inc b/servers/rest_server/rest_server.inc | |
index 462a8ac..b12f5a2 100644 | |
--- a/servers/rest_server/rest_server.inc | |
+++ b/servers/rest_server/rest_server.inc | |
@@ -2,9 +2,94 @@ | |
/** | |
* @file | |
- * Autoload classes. | |
+ * Autoload classes and server settings. | |
*/ | |
+/** | |
+ * REST server settings form. Generates the form fragment for configuring the REST server | |
+ * for an endpoint. | |
+ * | |
+ * @param array $form | |
+ * The form fragment from services that we should add our fields to. | |
+ * @param object $endpoint | |
+ * The endpoint that we're configuring the REST server for. | |
+ * @param array $settings | |
+ * The current settings. | |
+ * @return void | |
+ */ | |
+function _rest_server_settings(&$form, $endpoint, $settings) { | |
+ $settings = rest_server_setup_settings($settings); | |
+ | |
+ $form['formatters'] = array( | |
+ '#type' => 'checkboxes', | |
+ '#title' => t('Response formatters'), | |
+ '#required' => TRUE, | |
+ '#description' => t('Select the response formats you want to enable for the rest server.'), | |
+ ) + _rest_server_settings_checkboxes_attributes($settings['formatters']); | |
+ | |
+ $form['parsers'] = array( | |
+ '#type' => 'checkboxes', | |
+ '#title' => t('Request parsing'), | |
+ '#required' => TRUE, | |
+ '#description' => t('Select the request parser types you want to enable for the rest server.'), | |
+ ) + _rest_server_settings_checkboxes_attributes($settings['parsers']); | |
+} | |
+ | |
+/** | |
+ * Utility function that creates attributes for a checkboxes-type form | |
+ * element from a rest server settings array. | |
+ * | |
+ * @param array $settings | |
+ * @return array | |
+ */ | |
+function _rest_server_settings_checkboxes_attributes($settings) { | |
+ $keys = array_keys($settings); | |
+ $options = array_combine($keys, $keys); | |
+ $default = array(); | |
+ foreach ($settings as $key => $enabled) { | |
+ if ($enabled) { | |
+ $default[] = $key; | |
+ } | |
+ } | |
+ ksort($options); | |
+ return array( | |
+ '#options' => $options, | |
+ '#default_value' => $default, | |
+ ); | |
+} | |
+ | |
+/** | |
+ * Submit handler for the services REST server settings form. | |
+ * | |
+ * @param object $endpoint | |
+ * The endpoint that's being configured. | |
+ * @param array $values | |
+ * The partial form-state from services. | |
+ * @return array | |
+ * The settings for the REST server in this endpoint. | |
+ */ | |
+function _rest_server_settings_submit($endpoint, $values) { | |
+ $values['formatters'] = array_map('_rest_server_settings_not_zero', $values['formatters']); | |
+ $values['parsers'] = array_map('_rest_server_settings_not_zero', $values['parsers']); | |
+ return $values; | |
+} | |
+ | |
+/** | |
+ * Utility function intended for use with array_map to change everything that | |
+ * isn't === 0 into TRUE. | |
+ * | |
+ * @param string $value | |
+ * The value to map. | |
+ * @return bool | |
+ * FALSE if the $value is === 0 otherwise TRUE is returned. | |
+ */ | |
+function _rest_server_settings_not_zero($value) { | |
+ return $value !== 0; | |
+} | |
+ | |
+/** | |
+ * Actual implementation of hook_autoload_info(). | |
+ */ | |
function _rest_server_autoload_info() { | |
return array( | |
'RESTServer' => array( | |
diff --git a/servers/rest_server/rest_server.module b/servers/rest_server/rest_server.module | |
index d7e4669..100d022 100755 | |
--- a/servers/rest_server/rest_server.module | |
+++ b/servers/rest_server/rest_server.module | |
@@ -1,9 +1,17 @@ | |
<?php | |
+/** | |
+ * Implements hook_server_info(). | |
+ */ | |
function rest_server_server_info() { | |
return array( | |
'name' => 'REST', | |
'path' => 'rest', | |
+ 'settings' => array( | |
+ 'file' => array('inc', 'rest_server'), | |
+ 'form' => '_rest_server_settings', | |
+ 'submit' => '_rest_server_settings_submit', | |
+ ), | |
); | |
} | |
@@ -20,17 +28,17 @@ function rest_server_enable() { | |
autoload_flush_caches(); | |
} | |
+/** | |
+ * Starting point of the REST server. | |
+ * | |
+ * @return type | |
+ */ | |
function rest_server_server() { | |
$endpoint_path = services_get_server_info('endpoint_path', 'services/rest'); | |
- $canonical_path = trim(substr($_GET['q'], drupal_strlen($endpoint_path)), '/'); | |
- $canonical_path = explode('/', $_GET['q']); | |
- $endpoint_path_count = count(explode('/', $endpoint_path)); | |
- for($x=0; $x<$endpoint_path_count;$x++) { | |
- array_shift($canonical_path); | |
- } | |
- $canonical_path = implode('/', $canonical_path); | |
+ | |
+ $canonical_path = str_replace($endpoint_path . '/', '', $_GET['q']); | |
if (empty($canonical_path)) { | |
- return ''; | |
+ return; | |
} | |
try { | |
@@ -43,9 +51,142 @@ function rest_server_server() { | |
} | |
/** | |
- * Implementation of hook_services_resources_alter(). | |
+ * Builds a list of request parsers that are available to the RESTServer. | |
+ * | |
+ * @return array | |
+ * An associative array of parser callbacks keyed by mime-type. | |
+ */ | |
+function rest_server_request_parsers() { | |
+ static $parsers = NULL; | |
+ if (!$parsers) { | |
+ $parsers = array( | |
+ 'application/x-www-form-urlencoded' => 'RESTServer::parseURLEncoded', | |
+ 'application/x-yaml' => 'RESTServer::parseYAML', | |
+ 'application/json' => 'RESTServer::parseJSON', | |
+ 'application/vnd.php.serialized' => 'RESTServer::parsePHP', | |
+ 'multipart/form-data' => 'RESTServer::parseFile', | |
+ ); | |
+ drupal_alter('rest_server_request_parsers', $parsers); | |
+ } | |
+ return $parsers; | |
+} | |
+ | |
+/** | |
+ * Builds a list of response formatters that are available to the RESTServer. | |
+ * | |
+ * @return array | |
+ * An associative array of formatter info arrays keyed by type extension. The | |
+ * formatter info specifies an array of 'mime types' that corresponds to the | |
+ * output format; a 'view' class that is a subclass of RESTServerView; and | |
+ * 'view arguments' that should be passed to the view when it is created; | |
+ * a 'model' can also be specified which the controller then must declare | |
+ * support for to be able to serve data in that format. | |
+ */ | |
+function rest_server_response_formatters() { | |
+ static $formatters = NULL; | |
+ if (!$formatters) { | |
+ $formatters = array( | |
+ 'xml' => array( | |
+ 'mime types' => array('application/xml', 'text/xml'), | |
+ 'view' => 'RESTServerViewBuiltIn', | |
+ 'view arguments' => array('format' => 'xml'), | |
+ ), | |
+ 'json' => array( | |
+ 'mime types' => array('application/json'), | |
+ 'view' => 'RESTServerViewBuiltIn', | |
+ 'view arguments' => array('format' => 'json'), | |
+ ), | |
+ 'jsonp' => array( | |
+ 'mime types' => array('text/javascript', 'application/javascript'), | |
+ 'view' => 'RESTServerViewBuiltIn', | |
+ 'view arguments' => array('format' => 'jsonp'), | |
+ ), | |
+ 'php' => array( | |
+ 'mime types' => array('application/vnd.php.serialized'), | |
+ 'view' => 'RESTServerViewBuiltIn', | |
+ 'view arguments' => array('format' => 'php'), | |
+ ), | |
+ 'yaml' => array( | |
+ 'mime types' => array('text/plain', 'application/x-yaml', 'text/yaml'), | |
+ 'view' => 'RESTServerViewBuiltIn', | |
+ 'view arguments' => array('format' => 'yaml'), | |
+ ), | |
+ 'bencode' => array( | |
+ 'mime types' => array('application/x-bencode'), | |
+ 'view' => 'RESTServerViewBuiltIn', | |
+ 'view arguments' => array('format' => 'bencode'), | |
+ ), | |
+ 'rss' => array( | |
+ 'model' => 'ResourceFeedModel', | |
+ 'mime types' => array('text/xml'), | |
+ 'view' => 'RssFormatView', | |
+ ), | |
+ ); | |
+ drupal_alter('rest_server_response_formatters', $formatters); | |
+ } | |
+ return $formatters; | |
+} | |
+ | |
+/** | |
+ * Set up settings for a rest server endpoint, fills the settings | |
+ * array with defaults. This is done to ensure that the default state | |
+ * is consistent between what's shown by default in the settings form | |
+ * and used by default by the REST server if it hasn't been configured. | |
+ * | |
+ * @param array $settings | |
+ * @return array | |
+ * The standardized settings array. | |
+ */ | |
+function rest_server_setup_settings($settings = array()) { | |
+ // Apply defaults | |
+ $settings = $settings + array( | |
+ 'formatters' => array('jsonp' => FALSE), | |
+ 'parsers' => array('application/x-www-form-urlencoded' => FALSE), | |
+ ); | |
+ | |
+ // Get all available parsers and formatters. | |
+ $parsers = rest_server_request_parsers(); | |
+ $formatters = rest_server_response_formatters(); | |
+ | |
+ _rest_server_add_default_and_remove_unknown($settings['parsers'], array_keys($parsers), TRUE); | |
+ _rest_server_add_default_and_remove_unknown($settings['formatters'], array_keys($formatters), TRUE); | |
+ | |
+ return $settings; | |
+} | |
+ | |
+/** | |
+ * Utility function set set up an array with default values for a set | |
+ * of keys and remove all entries that does not match a key in the set. | |
+ * | |
+ * @param array $array | |
+ * The array to modify. | |
+ * @param array $keys | |
+ * An array of keys. | |
+ * @param mixed $default | |
+ * A default value. | |
+ * @return void | |
+ */ | |
+function _rest_server_add_default_and_remove_unknown(&$array, $keys, $default) { | |
+ // Add default values to all keys that do not | |
+ // exist in $array but exist in $keys. | |
+ foreach ($keys as $k) { | |
+ if (!isset($array[$k])) { | |
+ $array[$k] = $default; | |
+ } | |
+ } | |
+ // Unset all values that key exist in $array | |
+ // but does not exist in $keys. | |
+ foreach (array_keys($array) as $key) { | |
+ if (!in_array($key, $keys)) { | |
+ unset($array[$key]); | |
+ } | |
+ } | |
+} | |
+ | |
+/** | |
+ * Implements hook_services_resources_alter(). | |
*/ | |
-function rest_server_services_resources_alter($resources, $endpoint) { | |
+function rest_server_services_resources_alter(&$resources, $endpoint) { | |
// Set the default models for the retrieve and index controllers in the node | |
// resource if they are not already set. | |
if (isset($resources['node'])) { | |
diff --git a/servers/xmlrpc_server/xmlrpc_server.module b/servers/xmlrpc_server/xmlrpc_server.module | |
index bc7f90e..8f56b97 100644 | |
--- a/servers/xmlrpc_server/xmlrpc_server.module | |
+++ b/servers/xmlrpc_server/xmlrpc_server.module | |
@@ -27,7 +27,6 @@ function xmlrpc_server_server() { | |
require_once './includes/xmlrpc.inc'; | |
require_once './includes/xmlrpcs.inc'; | |
- // | |
return xmlrpc_server(xmlrpc_server_xmlrpc()); | |
} | |
@@ -66,20 +65,37 @@ function xmlrpc_server_call_wrapper() { | |
$controller = services_controller_get($method_name, $endpoint); | |
try { | |
- return services_controller_execute($controller, $args); | |
- } | |
+ // Add in default arguments if arguments still dont exist. | |
+ if (isset($controller['args']) && is_array($controller['args'])) { | |
+ foreach ($controller['args'] as $index => $arg) { | |
+ if ($arg['optional'] && isset($arg['default value']) && !isset($args[$index])) { | |
+ $args[$index] = $arg['default value']; | |
+ } | |
+ elseif ($arg['optional'] == FALSE && !isset($args[$index])) { | |
+ return services_error(t('Missing required argument @arg', array('@arg' => $arg['name'])), 401); | |
+ } | |
+ } | |
+ } | |
+ return services_controller_execute($controller, $args); | |
+ } | |
catch (Exception $e) { | |
$code = $e->getCode(); | |
switch($code) { | |
+ case 204: | |
+ drupal_set_header('HTTP/1.0 204 No Content: ' . $e->getMessage()); | |
+ break; | |
+ case 304: | |
+ drupal_set_header('HTTP/1.0 304 Not Modified: ' . $e->getMessage()); | |
+ break; | |
case 401: | |
drupal_set_header('HTTP/1.0 401 Unauthorized: ' . $e->getMessage()); | |
- break; | |
+ break; | |
case 404: | |
drupal_set_header('HTTP/1.0 404 Not found: ' . $e->getMessage()); | |
- break; | |
+ break; | |
case 406: | |
drupal_set_header('HTTP/1.0 406 Not Acceptable: ' . $e->getMessage()); | |
- break; | |
+ break; | |
default: | |
if ($code >= 400 && $code < 600) { | |
drupal_set_header('HTTP/1.0 ' . $code . ' ' . $e->getMessage()); | |
@@ -87,7 +103,7 @@ function xmlrpc_server_call_wrapper() { | |
else { | |
drupal_set_header('HTTP/1.0 500 An error occurred: (' . $code . ') ' . $e->getMessage()); | |
} | |
- break; | |
+ break; | |
} | |
} | |
} | |
diff --git a/services.install b/services.install | |
index 31ccc19..fe3da28 100644 | |
--- a/services.install | |
+++ b/services.install | |
@@ -26,12 +26,6 @@ function services_schema() { | |
'length' => 255, | |
'not null' => TRUE, | |
), | |
- 'title' => array( | |
- 'description' => 'The title of the endpoint.', | |
- 'type' => 'varchar', | |
- 'length' => 255, | |
- 'not null' => TRUE, | |
- ), | |
'server' => array( | |
'description' => 'The name of the server used in this endpoint.', | |
'type' => 'varchar', | |
@@ -52,6 +46,13 @@ function services_schema() { | |
'serialize' => TRUE, | |
'object default' => array(), | |
), | |
+ 'server_settings' => array( | |
+ 'description' => 'The server settings for the endpoint.', | |
+ 'type' => 'blob', | |
+ 'size' => 'big', | |
+ 'not null' => TRUE, | |
+ 'serialize' => TRUE | |
+ ), | |
'resources' => array( | |
'description' => 'Information about the resources exposed in this endpoint.', | |
'type' => 'text', | |
@@ -87,7 +88,46 @@ function services_schema() { | |
return $schema; | |
} | |
+ /** | |
+ * Implements hook_requirements(). | |
+ */ | |
+function services_requirements($phase) { | |
+ $requirements = array(); | |
+ $t = get_t(); | |
+ // Warn users of the possible threat. | |
+ if ($phase == 'runtime') { | |
+ //Pull endpoints that do not have services authentication enabled | |
+ $result = db_query("SELECT * FROM {services_endpoint} AS se WHERE se.authentication NOT LIKE '%s'", '%services%'); | |
+ $items = array(); | |
+ $has_endpoint = FALSE; | |
+ while ($endpoint = db_fetch_object($result)) { | |
+ $has_endpoint = TRUE; | |
+ $items[] = l($endpoint->name, 'admin/build/services/list/'. $endpoint->name); | |
+ } | |
+ // theme the endpoints list | |
+ $endpoints = ''; | |
+ if (!empty($items)) { | |
+ $endpoints = theme('item_list', array('items' => $items)); | |
+ } | |
+ // Only display the list if we have at least one endpoint without services authentication. | |
+ if (count($items)) { | |
+ $requirements['services'] = array( | |
+ 'description' => $t('Services authentication mechanism has not been enabled for the following endpoints. Requests to these endpoints will always be anonymous.'), | |
+ 'severity' => REQUIREMENT_ERROR, | |
+ 'value' => $endpoints, | |
+ 'title' => 'Services Authentication Mechanism', | |
+ ); | |
+ } else { | |
+ $requirements['services'] = array( | |
+ 'severity' => REQUIREMENT_OK, | |
+ 'value' => 'Enabled for all Endpoints', | |
+ 'title' => 'Services Authentication Mechanism', | |
+ ); | |
+ } | |
+ } | |
+ return $requirements; | |
+} | |
/** | |
* Implementation of hook_install(). | |
*/ | |
@@ -266,24 +306,27 @@ function services_update_6302() { | |
} | |
/** | |
- * Implements hook_requirements(). | |
- * | |
- * Check version of ctools. | |
+ * Update 6303 adds the possibility to configure server settings on a per-endpoint basis. | |
+ * and sets upgrades all new servers to have at least services session enabled. | |
*/ | |
-function services_requirements($phase) { | |
- $t = get_t(); | |
- $requirements = array(); | |
- $ctools_info = db_fetch_array(db_query('SELECT info FROM {system} WHERE name = "%s"', 'ctools')); | |
- $ctools_info = unserialize($ctools_info['info']); | |
- // Check whether version of ctools is minimum alpha3 | |
- // as we need patch applied from http://drupal.org/node/933946 | |
- if ($ctools_info['datestamp'] < 1299715446) { | |
- $requirements['ctools'] = array( | |
- 'title' => 'CTools', | |
- 'value' => $ctools_info['version'], | |
- 'severity' => REQUIREMENT_ERROR, | |
- 'description' => $t('Please update CTools module to latest stable version (minimum 6.x-dev March 09).'), | |
- ); | |
- } | |
- return $requirements; | |
+function services_update_6303() { | |
+ $ret = array(); | |
+ // Add the new server settings field. | |
+ $new_field = array( | |
+ 'description' => 'The server settings for the endpoint.', | |
+ 'type' => 'blob', | |
+ 'size' => 'big', | |
+ 'not null' => TRUE, | |
+ 'serialize' => TRUE | |
+ ); | |
+ db_add_field($ret, 'services_endpoint', 'server_settings', $new_field); | |
+ return $ret; | |
+} | |
+/** | |
+ * Update 6304 removes title functionality as it is no longer used. | |
+ */ | |
+function services_update_6304() { | |
+ $ret = array(); | |
+ db_drop_field($ret, 'services_endpoint', 'title'); | |
+ return $ret; | |
} | |
\ No newline at end of file | |
diff --git a/services.module b/services.module | |
index 46e4ead..91062dd 100644 | |
--- a/services.module | |
+++ b/services.module | |
@@ -35,6 +35,7 @@ function services_help($path, $arg) { | |
function services_perm() { | |
return array( | |
'administer services', | |
+ 'get taxonomy tree', | |
// File resource permissions | |
'get any binary files', | |
'get own binary files', | |
@@ -164,6 +165,7 @@ function services_endpoint_callback($endpoint_name) { | |
'endpoint_path' => $endpoint->path, | |
'debug' => $endpoint->debug, | |
'drupal_path' => getcwd(), | |
+ 'settings' => $endpoint->server_settings[$server], | |
)); | |
if ($endpoint->debug) { | |
watchdog('services', 'Server info main object: <pre>@info</pre>', array('@info' => print_r(services_server_info_object(), TRUE)), WATCHDOG_DEBUG); | |
@@ -204,11 +206,6 @@ function services_endpoint_load($name) { | |
if (isset($result[$name])) { | |
return $result[$name]; | |
} | |
- $result = db_query("SELECT * FROM {services_endpoint} WHERE name = '%s'", $name); | |
- $matches = array(); | |
- while ($endpoint = db_fetch_object($result)) { | |
- return $endpoint; | |
- } | |
return FALSE; | |
} | |
@@ -220,14 +217,6 @@ function services_endpoint_load($name) { | |
*/ | |
function services_endpoint_load_all() { | |
ctools_include('export'); | |
- if (!ctools_export_load_object('services_endpoint')) { | |
- $result = db_query("SELECT * FROM {services_endpoint}"); | |
- $matches = array(); | |
- while ($endpoint = db_fetch_object($result)) { | |
- $matches[] = $endpoint; | |
- } | |
- return $matches; | |
- } | |
return ctools_export_load_object('services_endpoint'); | |
} | |
@@ -241,6 +230,7 @@ function services_endpoint_save($endpoint) { | |
$endpoint = $endpoint['build_info']['args'][0]; | |
} | |
ctools_export_crud_save('services_endpoint', $endpoint); | |
+ ctools_export_load_object_reset('services_endpoint'); | |
menu_rebuild(); | |
cache_clear_all('services:' . $endpoint->name . ':', 'cache', TRUE); | |
} | |
@@ -252,6 +242,7 @@ function services_endpoint_save($endpoint) { | |
*/ | |
function services_endpoint_delete($endpoint) { | |
ctools_export_crud_delete('services_endpoint', $endpoint); | |
+ ctools_export_load_object_reset('services_endpoint'); | |
menu_rebuild(); | |
cache_clear_all('services:' . $endpoint->name . ':', 'cache', TRUE); | |
} | |
@@ -263,8 +254,7 @@ function services_endpoint_delete($endpoint) { | |
*/ | |
function services_endpoint_export($endpoint, $indent = '') { | |
ctools_include('export'); | |
- $output = ctools_export_object('services_endpoint', $endpoint, $indent); | |
- return $output; | |
+ return ctools_export_object('services_endpoint', $endpoint, $indent); | |
} | |
@@ -298,7 +288,28 @@ function services_get_resources($endpoint_name = '') { | |
function services_services_resources() { | |
module_load_include('resource_build.inc', 'services'); | |
// Return resources representing legacy services | |
- return array_merge(_services_core_resources(), _services_legacy_services_as_resources()); | |
+ return _services_core_resources(); | |
+} | |
+ | |
+ /** | |
+ * Implementation of hook_services_authentication(). | |
+ */ | |
+function services_services_authentication_info() { | |
+ return array( | |
+ 'title' => t('Session authentication'), | |
+ 'description' => t("Uses Drupal's built in sessions to authenticate."), | |
+ 'authenticate_call' => '_services_sessions_authenticate_call', | |
+ ); | |
+} | |
+ | |
+/** | |
+ * Authenticates a call using Drupal's built in sessions | |
+ * | |
+ * @return void | |
+ */ | |
+function _services_sessions_authenticate_call() { | |
+ global $user; | |
+ $user = services_get_server_info('original_user'); | |
} | |
/** | |
@@ -314,7 +325,7 @@ function services_services_resources() { | |
*/ | |
function services_controllers_list($endpoint) { | |
$controllers = array(); | |
- $ops = array('actions', 'relationships', 'targeted actions'); | |
+ $ops = array('actions', 'relationships', 'targeted_actions'); | |
$resources = services_get_resources($endpoint); | |
foreach ($resources as $resource_name => $res) { | |
// Get all basic operations | |
@@ -348,7 +359,7 @@ function services_controllers_list($endpoint) { | |
*/ | |
function services_controller_get($name, $endpoint) { | |
list($resource_name, $method) = explode('.', $name); | |
- $ops = array('actions', 'relationships', 'targeted actions'); | |
+ $ops = array('actions', 'relationships', 'targeted_actions'); | |
$resources = services_get_resources($endpoint); | |
if (isset($resources[$resource_name])) { | |
@@ -379,7 +390,7 @@ function services_controller_get($name, $endpoint) { | |
*/ | |
function services_resources_as_procedures($resource) { | |
static $controllers = array('create', 'retrieve', 'update', 'delete', 'index'); | |
- static $subcontrollers = array('actions', 'relationships', 'targeted actions'); | |
+ static $subcontrollers = array('actions', 'relationships', 'targeted_actions'); | |
$methods = array(); | |
@@ -405,7 +416,7 @@ function services_resources_as_procedures($resource) { | |
* | |
* @param $resource | |
* The resource being converted (node, user, etc.) | |
- * @param $name | |
+ * @param $name | |
* The method name (retrieve, create, etc.) | |
* @param $controller | |
* Associative array defining the method's properties and callbacks. | |
@@ -431,10 +442,21 @@ function _services_resource_controller_as_procedure($resource, $name, $controlle | |
* Array fields to return. | |
* @param $parameter | |
* Array parameters to add to the index query. | |
+ * @param $page_size | |
+ * Integer number of items to be returned. | |
*/ | |
-function services_resource_build_index_query($schema, $order, $page, $fields, $parameters = array()) { | |
+function services_resource_build_index_query($schema, $order, $page, $fields, $parameters = array(), $primary_table, $primary_field, $page_size) { | |
$where = array(); | |
$fields = db_escape_string($fields); | |
+ | |
+ // need to append table prefix | |
+ if ($fields_array = explode(',', $fields)) { | |
+ foreach ($fields_array as &$field) { | |
+ $field = $primary_table . '.' . trim($field); | |
+ } | |
+ $fields = implode(',', $fields_array); | |
+ } | |
+ | |
$schema = db_escape_string($schema); | |
$table = $schema; | |
$schema = drupal_get_schema($schema); | |
@@ -442,7 +464,7 @@ function services_resource_build_index_query($schema, $order, $page, $fields, $p | |
// db_query(). | |
if(is_array($parameters)) { | |
foreach ($parameters as $field => $value) { | |
- $where[] = $field . ' = ' . db_type_placeholder($schema['fields'][$field]['type']); | |
+ $where[] = $primary_table . '.' . $field . ' = ' . db_type_placeholder($schema['fields'][$field]['type']); | |
} | |
} | |
@@ -450,9 +472,9 @@ function services_resource_build_index_query($schema, $order, $page, $fields, $p | |
$where = !empty($where) ? ' WHERE '. implode(' AND ', $where) : ''; | |
// Run through db_rewrite_sql to make sure proper access checks are applied. | |
- $sql = "SELECT $fields FROM {$table} $where ORDER BY $order"; | |
- $sql = db_rewrite_sql($sql); | |
- $result = db_query_range($sql, $parameters, $page * 20, 20); | |
+ $sql = "SELECT $fields FROM {{$table}} AS $primary_table $where ORDER BY $order"; | |
+ $sql = db_rewrite_sql($sql, $primary_table, $primary_field); | |
+ $result = db_query_range($sql, $parameters, $page * $page_size, $page_size); | |
return $result; | |
} | |
@@ -472,9 +494,23 @@ function services_resource_build_index_list($results, $type, $field) { | |
foreach ($results as $result) { | |
if ($uri = services_resource_uri(array($type, $result->{$field}))) { | |
$result->uri = $uri; | |
+ if ($type == 'user') { | |
+ services_remove_user_data($result); | |
+ } | |
} | |
$items[] = $result; | |
} | |
return $items; | |
-} | |
\ No newline at end of file | |
+} | |
+ | |
+/** | |
+ * Helper function to remove data from the user object. | |
+ * | |
+ * @param $account | |
+ * Object user object. | |
+ */ | |
+function services_remove_user_data(&$account) { | |
+ // Remove the users password from the account object. | |
+ unset($account->pass); | |
+} | |
diff --git a/services.resource_build.inc b/services.resource_build.inc | |
index 0a25baf..94b737e 100644 | |
--- a/services.resource_build.inc | |
+++ b/services.resource_build.inc | |
@@ -34,7 +34,7 @@ function _services_build_resources($endpoint_name = '') { | |
// Process the resources, and collect all controllers in the process | |
$controllers = array(); | |
foreach ($resources as $name => &$resource) { | |
- $controllers = _services_process_resource($name, $resource, $controllers); | |
+ _services_process_resource($name, $resource, $controllers); | |
} | |
// Make sure that we got a access callback for all resources | |
@@ -50,6 +50,9 @@ function _services_build_resources($endpoint_name = '') { | |
} | |
} | |
drupal_alter('services_resources_controller_post_processing', $controllers, $endpoint); | |
+ | |
+ // This hook is deprecated and will be removed in next versions of services. | |
+ // Use hook_services_resources_alter instead. | |
drupal_alter('services_resources_post_processing', $resources, $endpoint); | |
// Do some endpoint-dependent processing | |
@@ -60,9 +63,7 @@ function _services_build_resources($endpoint_name = '') { | |
} | |
// Apply any aliases from endpoint | |
- if (!empty($endpoint)) { | |
- $aliased = array(); | |
- } | |
+ $aliased = array(); | |
foreach ($resources as $key => $def) { | |
if (!empty($def['endpoint']['alias'])) { | |
$aliased[$def['endpoint']['alias']] = $def; | |
@@ -110,7 +111,7 @@ function _services_apply_endpoint(&$resources, $endpoint, $strict = TRUE) { | |
} | |
} | |
- $classes = array('targeted actions', 'actions', 'relationships'); | |
+ $classes = array('targeted_actions', 'actions', 'relationships'); | |
foreach ($classes as $class) { | |
if (!empty($resource[$class])) { | |
foreach ($resource[$class] as $op => $def) { | |
@@ -142,38 +143,27 @@ function _services_apply_endpoint(&$resources, $endpoint, $strict = TRUE) { | |
* An that will be fillew with all the controllers for the resource. | |
* @return void | |
*/ | |
-function _services_process_resource($name, &$resource, $controllers) { | |
+function _services_process_resource($name, &$resource, &$controllers) { | |
$resource['name'] = $name; | |
- $keys = array('retrieve', 'create', 'update', 'delete'); | |
- foreach ($keys as $key) { | |
- if (isset($resource[$key])) { | |
- $controllers[$name . '/' . $key] = &$resource[$key]; | |
+ // CRUD operations. | |
+ foreach (array('retrieve', 'create', 'update', 'delete', 'index') as $op) { | |
+ if (isset($resource[$op])) { | |
+ $controllers[$name . '/' . $op] = &$resource[$op]; | |
} | |
} | |
- if (isset($resource['index'])) { | |
- $controllers[$name . '/index'] = &$resource['index']; | |
- } | |
- | |
- if (isset($resource['relationships'])) { | |
- foreach ($resource['relationships'] as $relname => $rel) { | |
- $controllers[$name . '/relationship/' . $relname] = &$resource['relationships'][$relname]; | |
+ // Actions and relationships. | |
+ foreach (array('relationships', 'actions', 'targeted_actions') as $class) { | |
+ if (!isset($resource[$class])) { | |
+ continue; | |
} | |
- } | |
- if (isset($resource['actions'])) { | |
- foreach ($resource['actions'] as $actname => $act) { | |
- $controllers[$name . '/action/' . $actname] = &$resource['actions'][$actname]; | |
+ foreach (array_keys($resource[$class]) as $action_name) { | |
+ $class_singular = trim($class, 's'); | |
+ $controllers[$name . '/' . $class_singular . '/' . $action_name] = &$resource[$class][$action_name]; | |
} | |
} | |
- | |
- if (isset($resource['targeted actions'])) { | |
- foreach ($resource['targeted actions'] as $actname => $act) { | |
- $controllers[$name . '/targeted_action/' . $actname] = &$resource['actions'][$actname]; | |
- } | |
- } | |
- return $controllers; | |
} | |
/** | |
@@ -200,32 +190,3 @@ function _services_core_resources() { | |
return $resources; | |
} | |
- | |
-/** | |
- * Invokes all hook_service and translates them to actions on resources. | |
- * | |
- * @return array | |
- */ | |
-function _services_legacy_services_as_resources() { | |
- $services = $methods = module_invoke_all('service'); | |
- $resources = array(); | |
- | |
- foreach ($services as $service) { | |
- $signature = preg_split('/\./', $service['method']); | |
- | |
- $controller = $service; | |
- $controller['args'] = array(); | |
- | |
- if (!empty($service['args'])) { | |
- foreach ($service['args'] as $arg) { | |
- $arg['source'] = array( | |
- 'data' => $arg['name'], | |
- ); | |
- $controller['args'][] = $arg; | |
- } | |
- } | |
- | |
- $resources['service_' . $signature[0]]['actions'][$signature[1]] = $controller; | |
- } | |
- return $resources; | |
-} | |
\ No newline at end of file | |
diff --git a/services.runtime.inc b/services.runtime.inc | |
index 643547c..5c4f1bf 100644 | |
--- a/services.runtime.inc | |
+++ b/services.runtime.inc | |
@@ -94,7 +94,8 @@ class ServicesArgumentException extends ServicesException { | |
* services-specific authentication checks. Access checks will always be | |
* made. | |
*/ | |
-function services_controller_execute($controller, $args = array(), $auth_args = array(), $options = array()) { | |
+function services_controller_execute($controller, $args = array(), $options = array()) { | |
+ global $user; | |
// Check for missing arguments. | |
$server_info = services_server_info_object(); | |
if ($server_info->debug) { | |
@@ -102,24 +103,31 @@ function services_controller_execute($controller, $args = array(), $auth_args = | |
watchdog('services', 'Passed arguments: <pre>@arguments</pre>', array('@arguments' => print_r($args, TRUE)), WATCHDOG_DEBUG); | |
} | |
- // Check authentication | |
+ $original_user = $user; | |
+ $old_state = session_save_session(); | |
+ session_save_session(FALSE); | |
+ $user = drupal_anonymous_user(); | |
+ | |
+ services_set_server_info('original_user', $original_user); | |
+ | |
+ // Check authentication. | |
if (!isset($options['skip_authentication']) || !$options['skip_authentication']) { | |
$endpoint_name = services_get_server_info('endpoint'); | |
$endpoint = services_endpoint_load($endpoint_name); | |
foreach ($endpoint->authentication as $auth_module => $settings) { | |
- if (isset($settings) && $auth_error = services_auth_invoke($auth_module, 'authenticate_call', $settings, $controller, $args, $auth_args)) { | |
+ if (isset($settings) && $auth_error = services_auth_invoke($auth_module, 'authenticate_call', $settings, $controller, $args)) { | |
return services_error($auth_error, 401); | |
} | |
} | |
} | |
- // Load the proper file | |
+ // Load the proper file. | |
if (!empty($controller['file']) && $file = $controller['file']) { | |
module_load_include($file['type'], $file['module'], (isset($file['name']) ? $file['name'] : NULL)); | |
} | |
- // Construct access arguments array | |
+ // Construct access arguments array. | |
if (isset($controller['access arguments'])) { | |
$access_arguments = $controller['access arguments']; | |
if (isset($controller['access arguments append']) && $controller['access arguments append']) { | |
@@ -131,7 +139,13 @@ function services_controller_execute($controller, $args = array(), $auth_args = | |
$access_arguments = $args; | |
} | |
- // Call default or custom access callback | |
+ // Load the proper file for the access callback. | |
+ if (!empty($controller['access callback file']) && $access_cb_file = $controller['access callback file']) { | |
+ $access_cb_file_name = isset($access_cb_file['name']) ? $access_cb_file['name'] : NULL; | |
+ module_load_include($access_cb_file['type'], $access_cb_file['module'], $access_cb_file_name); | |
+ } | |
+ | |
+ // Call default or custom access callback. | |
if (call_user_func_array($controller['access callback'], $access_arguments) != TRUE) { | |
global $user; | |
return services_error(t('Access denied for user !uid "@user"', array( | |
@@ -148,27 +162,34 @@ function services_controller_execute($controller, $args = array(), $auth_args = | |
chdir($drupal_path); | |
} | |
- // Check if the arguments should be preprocessed | |
+ // Check if the arguments should be preprocessed. | |
if (!empty($controller['endpoint']['preprocess'])) { | |
foreach ($controller['endpoint']['preprocess'] as $callable) { | |
call_user_func_array($callable, array(&$args, &$controller)); | |
} | |
} | |
- // Execute the controller callback | |
+ // Execute the controller callback. | |
$result = call_user_func_array($controller['callback'], $args); | |
if (isset($server_root) && $server_root) { | |
chdir($server_root); | |
} | |
- // Check if the result should be post-processed | |
+ // Check if the result should be post-processed. | |
if (!empty($controller['endpoint']['postprocess'])) { | |
foreach ($controller['endpoint']['postprocess'] as $callable) { | |
$result = call_user_func_array($callable, array($args, $controller, $result)); | |
} | |
} | |
+ if (session_save_session($user) === FALSE) { | |
+ $user = $original_user; | |
+ session_save_session($old_state); | |
+ } | |
+ if ($server_info->debug) { | |
+ watchdog('services', 'results: <pre>@results</pre>', array('@results' => print_r($result, TRUE)), WATCHDOG_DEBUG); | |
+ } | |
return $result; | |
} | |
@@ -243,6 +264,12 @@ function services_auth_invoke($module, $method, &$arg1 = NULL, &$arg2 = NULL, &$ | |
* Returns the formatted resource uri, or NULL if no formatter has been registered. | |
*/ | |
function services_resource_uri($path) { | |
+ //We need to use the alias if it exists. | |
+ $endpoint_name = services_get_server_info('endpoint'); | |
+ $endpoint = services_endpoint_load($endpoint_name); | |
+ if (!empty($path[0]) && !empty($endpoint->resources[$path[0]]['alias'])) { | |
+ $path[0] = $endpoint->resources[$path[0]]['alias']; | |
+ } | |
$formatter = services_get_server_info('resource_uri_formatter'); | |
if ($formatter) { | |
return call_user_func($formatter, $path); | |
@@ -422,3 +449,48 @@ function services_session_unload($backup) { | |
session_save_session(TRUE); | |
} | |
+ | |
+ | |
+/** | |
+ * Extract arguments for a services method callback, preserving backwards compatibility with #1083242. | |
+ * | |
+ * @param array $data | |
+ * original argument passed to a resource method callback | |
+ * @param string $field | |
+ * name of the field where arguments should be checked for | |
+ * @return array | |
+ */ | |
+ | |
+// Adds backwards compatability with regression fixed in #1083242 | |
+function _services_arg_value($data, $field) { | |
+ if (isset($data[$field]) && count($data) == 1 && is_array($data[$field])) { | |
+ return $data[$field]; | |
+ } | |
+ return $data; | |
+} | |
+ | |
+ | |
+/** | |
+ * Extract arguments for a services method access callback, preserving backwards compatibility with #1083242. | |
+ * | |
+ * @param string $data | |
+ * original argument passed to a resource method callback | |
+ * @param mixed $fields | |
+ * name of the field(s) where arguments should be checked for, either as a string or as an array of strings | |
+ * @return array | |
+ */ | |
+ | |
+// Adds backwards compatability with regression fixed in #1083242 | |
+function _services_access_value($data, $fields) { | |
+ | |
+ if (!is_array($fields)) { | |
+ $fields = array($fields); | |
+ } | |
+ | |
+ foreach ($fields as $field) { | |
+ if (is_array($data) && isset($data[$field]) && count($data) == 1) { | |
+ return $data[$field]; | |
+ } | |
+ } | |
+ return $data; | |
+} | |
diff --git a/services.servers.api.php b/services.servers.api.php | |
index dff762b..f5978ef 100644 | |
--- a/services.servers.api.php | |
+++ b/services.servers.api.php | |
@@ -17,10 +17,17 @@ | |
* An associative array with the following keys. | |
* | |
* - name: The display name of this server. | |
+ * - settings: an assoc array containing settings information per endpoint that this server is enabled. | |
*/ | |
function hook_server_info() { | |
return array( | |
'name' => 'REST', | |
+ 'path' => 'rest', | |
+ 'settings' => array( | |
+ 'file' => array('inc', 'rest_server'), | |
+ 'form' => '_rest_server_settings', | |
+ 'submit' => '_rest_server_settings_submit', | |
+ ), | |
); | |
} | |
@@ -32,5 +39,16 @@ function hook_server_info() { | |
* servers. | |
*/ | |
function hook_server() { | |
- | |
+ $endpoint_path = services_get_server_info('endpoint_path', 'services/rest'); | |
+ $canonical_path = trim(drupal_substr($_GET['q'], drupal_strlen($endpoint_path)), '/'); | |
+ $canonical_path = explode('/', $_GET['q']); | |
+ $endpoint_path_count = count(explode('/', $endpoint_path)); | |
+ for ($x = 0; $x < $endpoint_path_count; $x++) { | |
+ array_shift($canonical_path); | |
+ } | |
+ $canonical_path = implode('/', $canonical_path); | |
+ if (empty($canonical_path)) { | |
+ return ''; | |
+ } | |
+ //Handle server based on $canonical_path | |
} | |
\ No newline at end of file | |
diff --git a/services.services.api.php b/services.services.api.php | |
index 784b93c..a1393fb 100644 | |
--- a/services.services.api.php | |
+++ b/services.services.api.php | |
@@ -18,7 +18,7 @@ | |
* @return | |
* An associative array which defines available resources. | |
* | |
- * The associative array which defines services has six possible top | |
+ * The associative array which defines services has seven possible top | |
* level keys: | |
* | |
* - create | |
@@ -26,10 +26,12 @@ | |
* - update | |
* - delete | |
* - actions | |
- * - targeted actions | |
+ * - targeted_actions | |
+ * - relationships | |
+ * | |
* | |
* The first four (the CRUD functions) define the indvidual service | |
- * callbacks for each function. However 'actions' and 'targeted actions' | |
+ * callbacks for each function. However 'actions' and 'targeted_actions' | |
* can contain multiple callbacks. | |
* | |
* For those familiar with Services 2.x, these callbacks are created | |
@@ -39,9 +41,16 @@ | |
* - help: Text describing what this callback does. | |
* - callback: The name of a function to call when this resource is | |
* requested. | |
+ * - file: an array describing the file which contains the callback | |
+ * function | |
* - access callback: The name of a function to call to check whether | |
* the requesting user has permission to access this resource. If not | |
* specified, this defaults to 'user_access'. | |
+ * - access callback file: an array describing the file which contains the | |
+ * access callback function. This attribute only needs to be supplied if | |
+ * the method callback and the access callback are defined in different | |
+ * files, for example when a method callback is overridden using | |
+ * hook_services_resources_alter but the access callback is not | |
* - access arguments: The arguments to pass to the access callback. | |
* - access arguments append: A boolean indicating whether the resource's | |
* arguments should be appended to the access arguments. This can be useful | |
@@ -62,57 +71,172 @@ | |
* - default value: this is a value that will be passed to the method for this particular argument if no argument value is passed | |
*/ | |
function hook_services_resources() { | |
- return array( | |
- 'user' => array( | |
- 'file' => array('type' => 'inc', 'module' => 'user_resource'), | |
+$node_resource = array( | |
+ 'node' => array( | |
'retrieve' => array( | |
- 'help' => 'Retrieves a user', | |
- 'callback' => '_user_resource_retrieve', | |
- 'access arguments' => array('access user profiles'), // this is probably not enough, doesn't block things like pass and email | |
- 'access arguments append' => TRUE, | |
+ 'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/node_resource'), | |
+ 'callback' => '_node_resource_retrieve', | |
'args' => array( | |
array( | |
- 'name' => 'uid', | |
+ 'name' => 'nid', | |
+ 'optional' => FALSE, | |
+ 'source' => array('path' => 0), | |
'type' => 'int', | |
- 'description' => 'The uid of the user to retrieve.', | |
- 'source' => array('path' => '0'), | |
+ 'description' => 'The nid of the node to get', | |
+ ), | |
+ ), | |
+ 'access callback' => '_node_resource_access', | |
+ 'access callback file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/node_resource'), | |
+ 'access arguments' => array('view'), | |
+ 'access arguments append' => TRUE, | |
+ ), | |
+ 'create' => array( | |
+ 'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/node_resource'), | |
+ 'callback' => '_node_resource_create', | |
+ 'args' => array( | |
+ array( | |
+ 'name' => 'node', | |
'optional' => FALSE, | |
- 'default value' => NULL, | |
+ 'source' => 'data', | |
+ 'description' => 'The node data to create', | |
+ 'type' => 'array', | |
), | |
), | |
+ 'access callback' => '_node_resource_access', | |
+ 'access arguments' => array('create'), | |
+ 'access arguments append' => TRUE, | |
), | |
- ), | |
- | |
- 'actions' => array( | |
- 'login' => array( | |
- 'help' => 'Login a user for a new session', | |
- 'callback' => '_user_resource_login', | |
+ 'update' => array( | |
+ 'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/node_resource'), | |
+ 'callback' => '_node_resource_update', | |
'args' => array( | |
array( | |
- 'name' => 'username', | |
- 'type' => 'string', | |
- 'description' => 'A valid username', | |
- 'source' => array('data'), | |
+ 'name' => 'nid', | |
'optional' => FALSE, | |
+ 'source' => array('path' => 0), | |
+ 'type' => 'int', | |
+ 'description' => 'The nid of the node to get', | |
), | |
array( | |
- 'name' => 'password', | |
- 'type' => 'string', | |
- 'description' => 'A valid password', | |
- 'source' => array('data'), | |
+ 'name' => 'node', | |
'optional' => FALSE, | |
+ 'source' => 'data', | |
+ 'description' => 'The node data to update', | |
+ 'type' => 'array', | |
), | |
), | |
+ 'access callback' => '_node_resource_access', | |
+ 'access arguments' => array('update'), | |
+ 'access arguments append' => TRUE, | |
), | |
- | |
- 'logout' => array( | |
- 'help' => 'Logout a user session', | |
- 'callback' => '_user_resource_logout', | |
+ 'delete' => array( | |
+ 'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/node_resource'), | |
+ 'callback' => '_node_resource_delete', | |
+ 'args' => array( | |
+ array( | |
+ 'name' => 'nid', | |
+ 'optional' => FALSE, | |
+ 'source' => array('path' => 0), | |
+ 'type' => 'int', | |
+ ), | |
+ ), | |
+ 'access callback' => '_node_resource_access', | |
+ 'access arguments' => array('delete'), | |
+ 'access arguments append' => TRUE, | |
+ ), | |
+ 'index' => array( | |
+ 'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/node_resource'), | |
+ 'callback' => '_node_resource_index', | |
'args' => array( | |
array( | |
+ 'name' => 'page', | |
+ 'optional' => TRUE, | |
+ 'type' => 'int', | |
+ 'description' => 'The zero-based index of the page to get, defaults to 0.', | |
+ 'default value' => 0, | |
+ 'source' => array('param' => 'page'), | |
+ ), | |
+ array( | |
+ 'name' => 'fields', | |
+ 'optional' => TRUE, | |
+ 'type' => 'string', | |
+ 'description' => 'The fields to get.', | |
+ 'default value' => '*', | |
+ 'source' => array('param' => 'fields'), | |
+ ), | |
+ array( | |
+ 'name' => 'parameters', | |
+ 'optional' => TRUE, | |
+ 'type' => 'array', | |
+ 'description' => 'Parameters array', | |
+ 'default value' => array(), | |
+ 'source' => array('param' => 'parameters'), | |
+ ), | |
+ ), | |
+ 'access arguments' => array('access content'), | |
+ ), | |
+ 'relationships' => array( | |
+ 'files' => array( | |
+ 'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/node_resource'), | |
+ 'help' => t('This method returns files associated with a node.'), | |
+ 'access callback' => '_node_resource_access', | |
+ 'access arguments' => array('view'), | |
+ 'access arguments append' => TRUE, | |
+ 'callback' => '_node_resource_load_node_files', | |
+ 'args' => array( | |
+ array( | |
+ 'name' => 'nid', | |
+ 'optional' => FALSE, | |
+ 'source' => array('path' => 0), | |
+ 'type' => 'int', | |
+ 'description' => 'The nid of the node whose files we are getting', | |
+ ), | |
+ array( | |
+ 'name' => 'file_contents', | |
+ 'type' => 'int', | |
+ 'description' => t('To return file contents or not.'), | |
+ 'source' => array('path' => 2), | |
+ 'optional' => TRUE, | |
+ 'default value' => TRUE, | |
+ ), | |
), | |
), | |
), | |
), | |
); | |
+ if (module_exists('comment')) { | |
+ $comments = array( | |
+ 'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/node_resource'), | |
+ 'help' => t('This method returns the number of new comments on a given node.'), | |
+ 'access callback' => 'user_access', | |
+ 'access arguments' => array('access comments'), | |
+ 'access arguments append' => FALSE, | |
+ 'callback' => '_node_resource_load_node_comments', | |
+ 'args' => array( | |
+ array( | |
+ 'name' => 'nid', | |
+ 'type' => 'int', | |
+ 'description' => t('The node id to load comments for.'), | |
+ 'source' => array('path' => 0), | |
+ 'optional' => FALSE, | |
+ ), | |
+ array( | |
+ 'name' => 'count', | |
+ 'type' => 'int', | |
+ 'description' => t('Number of comments to load.'), | |
+ 'source' => array('param' => 'count'), | |
+ 'optional' => TRUE, | |
+ ), | |
+ array( | |
+ 'name' => 'offset', | |
+ 'type' => 'int', | |
+ 'description' => t('If count is set to non-zero value, you can pass also non-zero value for start. For example to get comments from 5 to 15, pass count=10 and start=5.'), | |
+ 'source' => array('param' => 'offset'), | |
+ 'optional' => TRUE, | |
+ ), | |
+ ), | |
+ ); | |
+ $node_resource['node']['relationships']['comments'] = $comments; | |
+ } | |
+ return $node_resource; | |
} | |
diff --git a/tests/functional/ServicesEndpointTests.test b/tests/functional/ServicesEndpointTests.test | |
index 3e3dba6..b3344fd 100644 | |
--- a/tests/functional/ServicesEndpointTests.test | |
+++ b/tests/functional/ServicesEndpointTests.test | |
@@ -23,7 +23,6 @@ class ServicesEndpointTests extends ServicesWebTestCase { | |
'ctools', | |
'services', | |
'rest_server', | |
- 'services_sessauth', | |
'inputstream' | |
); | |
} | |
@@ -53,7 +52,7 @@ class ServicesEndpointTests extends ServicesWebTestCase { | |
$this->privilegedUser = $this->drupalCreateUser(array( | |
'administer services', | |
'administer nodes', | |
- 'administer site configuration', | |
+ 'administer site configuration', | |
)); | |
$this->drupalLogin($this->privilegedUser); | |
$this->drupalPost('admin/build/services/add', $edit, t('Save')) ; | |
@@ -83,34 +82,6 @@ class ServicesEndpointTests extends ServicesWebTestCase { | |
$this->assertText('Path to endpoint field is required.', | |
t('Endpoint path missing error message.')) ; | |
- $this->assertFieldByName('title', $edit['title'], | |
- t('Title field remains.')) ; | |
- $this->assertFieldByName('server', 'rest_server', | |
- t('Server is rest server')) ; | |
- $this->assertFieldChecked('edit-services-use-content-permissions', | |
- t('Storage use content permission is checked.')) ; | |
- } | |
- | |
- /** | |
- * Test missing title for endpoint causes an error. | |
- */ | |
- public function testMissingTitle() { | |
- $edit = $this->populateEndpointFAPI() ; | |
- unset($edit['title']) ; | |
- // Create and log in our privileged user. | |
- $this->privilegedUser = $this->drupalCreateUser(array( | |
- 'administer services', | |
- 'administer nodes', | |
- 'administer site configuration', | |
- )); | |
- $this->drupalLogin($this->privilegedUser); | |
- $this->drupalPost('admin/build/services/add', $edit, t('Save')) ; | |
- $this->assertResponse('200', 'expected 200'); | |
- | |
- $this->assertText('Endpoint title field is required.', | |
- t('Endpoint title missing error message.')) ; | |
- $this->assertFieldByName('name', $edit['name'], | |
- t('Name field remains.')) ; | |
$this->assertFieldByName('server', 'rest_server', | |
t('Server is rest server')) ; | |
$this->assertFieldChecked('edit-services-use-content-permissions', | |
@@ -127,7 +98,7 @@ class ServicesEndpointTests extends ServicesWebTestCase { | |
$this->privilegedUser = $this->drupalCreateUser(array( | |
'administer services', | |
'administer nodes', | |
- 'administer site configuration', | |
+ 'administer site configuration', | |
)); | |
$this->drupalLogin($this->privilegedUser); | |
$this->drupalPost('admin/build/services/add', $edit, | |
@@ -138,8 +109,6 @@ class ServicesEndpointTests extends ServicesWebTestCase { | |
t('Server missing error message.')); | |
$this->assertFieldByName('name', $edit['name'], | |
t('Name field remains.')); | |
- $this->assertFieldByName('title', $edit['title'], | |
- t('Title field remains.')); | |
$this->assertFieldChecked('edit-services-use-content-permissions', | |
t('Storage use content permission is checked.')) ; | |
} | |
@@ -159,7 +128,6 @@ class ServicesEndpointTests extends ServicesWebTestCase { | |
public function populateEndpointFAPI() { | |
return array( | |
'name' => 'machinename', | |
- 'title' => $this->randomName(20), | |
'path' => $this->randomName(10), | |
'server' => 'rest_server', | |
'services_use_content_permissions' => TRUE, | |
@@ -172,11 +140,9 @@ class ServicesEndpointTests extends ServicesWebTestCase { | |
$endpoint->disabled = FALSE; /* Edit this to true to make a default endpoint disabled initially */ | |
$endpoint->api_version = 3; | |
$endpoint->name = $edit['name']; | |
- $endpoint->title = $edit['title']; | |
$endpoint->server = $edit['server']; | |
$endpoint->path = $edit['path']; | |
$endpoint->authentication = array( | |
- 'services_sessauth' => array(), | |
); | |
$endpoint->resources = array( | |
'node' => array( | |
@@ -326,8 +292,11 @@ class ServicesEndpointTests extends ServicesWebTestCase { | |
'enabled' => 1, | |
), | |
), | |
- 'actions' => array( | |
- 'nodeFiles' => array( | |
+ 'relationships' => array( | |
+ 'files' => array( | |
+ 'enabled' => 1, | |
+ ), | |
+ 'comments' => array( | |
'enabled' => 1, | |
), | |
), | |
@@ -345,12 +314,7 @@ class ServicesEndpointTests extends ServicesWebTestCase { | |
$endpoint->status = 1; | |
services_endpoint_save($endpoint); | |
$endpoint = services_endpoint_load($endpoint->name); | |
- if ($endpoint->name == $edit['name']) { | |
- $this->pass('Endpoint successfully created'); | |
- } | |
- else { | |
- $this->fail('Endpoint creation failed'); | |
- } | |
+ $this->assertEqual($endpoint->name, $edit['name'], t('Endpoint successfully created')); | |
$this->servicesGet($endpoint->path); | |
return $endpoint; | |
} | |
diff --git a/tests/functional/ServicesParserTests.test b/tests/functional/ServicesParserTests.test | |
new file mode 100644 | |
index 0000000..6e7214d | |
--- /dev/null | |
+++ b/tests/functional/ServicesParserTests.test | |
@@ -0,0 +1,104 @@ | |
+<?php | |
+ | |
+/** | |
+ * @file | |
+ * Call the endpoint tests when no authentication is being used. | |
+ * | |
+ */ | |
+ | |
+/** | |
+ * Run test cases for the endpoint with no authentication turned on. | |
+ * | |
+ */ | |
+class ServicesParserTests extends ServicesWebTestCase { | |
+ // Class variables | |
+ protected $privilegedUser = NULL ; | |
+ // Endpoint details. | |
+ protected $endpoint = NULL; | |
+ | |
+ /** | |
+ * Implementation of setUp(). | |
+ */ | |
+ public function setUp() { | |
+ parent::setUp( | |
+ 'autoload', | |
+ 'ctools', | |
+ 'services', | |
+ 'rest_server' | |
+ ); | |
+ // Set up endpoint with disabled 'application/x-www-form-urlencoded' parser. | |
+ $edit = $this->populateEndpointFAPI() ; | |
+ $endpoint = new stdClass; | |
+ $endpoint->disabled = FALSE; | |
+ $endpoint->api_version = 3; | |
+ $endpoint->name = $edit['name']; | |
+ $endpoint->title = $edit['title']; | |
+ $endpoint->server = $edit['server']; | |
+ $endpoint->path = $edit['path']; | |
+ $endpoint->authentication = array( | |
+ 'services' => 'services', | |
+ ); | |
+ $endpoint->server_settings = array( | |
+ 'rest_server' => array( | |
+ 'formatters' => array( | |
+ 'php' => TRUE, | |
+ ), | |
+ 'parsers' => array( | |
+ 'application/x-yaml' => TRUE, | |
+ 'application/json' => TRUE, | |
+ 'application/vnd.php.serialized' => TRUE, | |
+ 'application/plist' => TRUE, | |
+ 'application/plist+xml' => TRUE, | |
+ 'application/x-www-form-urlencoded' => FALSE, | |
+ ), | |
+ ), | |
+ ); | |
+ $endpoint->resources = array( | |
+ 'user' => array( | |
+ 'actions' => array( | |
+ 'login' => array( | |
+ 'enabled' => 1, | |
+ ), | |
+ 'logout' => array( | |
+ 'enabled' => 1, | |
+ ), | |
+ ), | |
+ ), | |
+ ); | |
+ $endpoint->debug = 1; | |
+ $endpoint->export_type = FALSE; | |
+ services_endpoint_save($endpoint); | |
+ $endpoint = services_endpoint_load($endpoint->name); | |
+ $this->assertTrue($endpoint->name == $edit['name'], t('Endpoint successfully created')); | |
+ $this->endpoint = $endpoint; | |
+ } | |
+ | |
+ /** | |
+ * Implementation of getInfo(). | |
+ */ | |
+ public static function getInfo() { | |
+ return array( | |
+ 'name' => t('Parser'), | |
+ 'description' => t('Test the Parser functionality.'), | |
+ 'group' => t('Services'), | |
+ ); | |
+ } | |
+ | |
+ /** | |
+ * Testing parser functionality. | |
+ */ | |
+ public function testParser() { | |
+ $account = $this->drupalCreateUser(); | |
+ | |
+ // Logout first. | |
+ $this->drupalLogout(); | |
+ | |
+ // Try to login. By default servicesPost uses | |
+ // 'application/x-www-form-urlencoded' type. So it should be refused. | |
+ $response = $this->servicesPost($this->endpoint->path . '/user/login', array('username' => $account->name, 'password' => $account->pass_raw)); | |
+ | |
+ $this->assertTrue(strpos($response['header'], '406 Not Acceptable: Unsupported request content type application/x-www-form-urlencoded') !== FALSE, | |
+ t('Do not accept application/x-www-form-urlencoded if disabled.'), 'Parser'); | |
+ } | |
+ | |
+} | |
\ No newline at end of file | |
diff --git a/tests/functional/ServicesResourceCommentTests.test b/tests/functional/ServicesResourceCommentTests.test | |
index c2e093b..552fbe3 100644 | |
--- a/tests/functional/ServicesResourceCommentTests.test | |
+++ b/tests/functional/ServicesResourceCommentTests.test | |
@@ -28,7 +28,6 @@ class ServicesResourceCommentTests extends ServicesWebtestCase { | |
'ctools', | |
'services', | |
'rest_server', | |
- 'services_sessauth', | |
'inputstream' | |
); | |
// Set up endpoint. | |
@@ -66,7 +65,7 @@ class ServicesResourceCommentTests extends ServicesWebtestCase { | |
'subject' => $this->randomString(), | |
'comment' => $this->randomString(), | |
); | |
- $response = $this->servicesPost($path . '/comment', array('comment' => $comment)); | |
+ $response = $this->servicesPost($path . '/comment', $comment); | |
$cid = $response['body']['cid']; | |
$comment['cid'] = $cid; | |
@@ -85,10 +84,54 @@ class ServicesResourceCommentTests extends ServicesWebtestCase { | |
// Full HTML format. | |
'format' => 2, | |
); | |
+ $response_array = $this->servicesPost($this->endpoint->path . '/comment', $comment); | |
+ | |
+ $new_comment = _comment_load($response_array['body']['cid']); | |
+ $this->assertNotEqual($new_comment->format, $comment['format'], t('Full HTML format has not been applied.'), 'CommentResource: Create'); | |
+ } | |
+ | |
+ /** | |
+ * Test create comment (Legacy). | |
+ * | |
+ * TODO: To be removed in future version. | |
+ * @see http://drupal.org/node/1083242 | |
+ */ | |
+ function testCommentCreateLegacy() { | |
+ $path = $this->endpoint->path; | |
+ | |
+ // Create node with commenting. | |
+ $settings = array('comment' => COMMENT_NODE_READ_WRITE); | |
+ $node = $this->drupalCreateNode($settings); | |
+ | |
+ $comment = array( | |
+ 'uid' => $this->privileged_user->uid, | |
+ 'nid' => $node->nid, | |
+ 'subject' => $this->randomString(), | |
+ 'comment' => $this->randomString(), | |
+ ); | |
+ $response = $this->servicesPost($path . '/comment', array('comment' => $comment)); | |
+ | |
+ $cid = $response['body']['cid']; | |
+ $comment['cid'] = $cid; | |
+ | |
+ $comment_load = (array)_comment_load($cid); | |
+ $comment_intersect = array_intersect_assoc($comment_load, $comment); | |
+ | |
+ $this->assertEqual($comment, $comment_intersect, t('Comment created properly.'), 'CommentResource: Create (Legacy)'); | |
+ | |
+ // Try to create node with not allowed filter. | |
+ $comment = array( | |
+ 'uid' => $this->privileged_user->uid, | |
+ 'nid' => $node->nid, | |
+ 'subject' => $this->randomString(), | |
+ 'comment' => $this->randomString(), | |
+ // Full HTML format. | |
+ 'format' => 2, | |
+ ); | |
$response_array = $this->servicesPost($this->endpoint->path . '/comment', array('comment' => $comment)); | |
$new_comment = _comment_load($response_array['body']['cid']); | |
- $this->assertNotEqual($new_comment->format, $comment->format, t('Full HTML format has not been applied.'), 'CommentResource: Create'); | |
+ $this->assertNotEqual($new_comment->format, $comment['format'], t('Full HTML format has not been applied.'), 'CommentResource: Create (Legacy)'); | |
} | |
/** | |
@@ -102,10 +145,12 @@ class ServicesResourceCommentTests extends ServicesWebtestCase { | |
$node = $this->drupalCreateNode($settings); | |
$comment = array( | |
+ 'cid' => NULL, | |
'uid' => $this->privileged_user->uid, | |
'nid' => $node->nid, | |
'subject' => $this->randomString(), | |
'comment' => $this->randomString(), | |
+ 'format' => 1, | |
); | |
$cid = comment_save((array) $comment); | |
@@ -131,20 +176,22 @@ class ServicesResourceCommentTests extends ServicesWebtestCase { | |
$node = $this->drupalCreateNode($settings); | |
$comment = array( | |
+ 'cid' => NULL, | |
'uid' => $this->privileged_user->uid, | |
'nid' => $node->nid, | |
'subject' => $this->randomString(), | |
'comment' => $this->randomString(), | |
+ 'format' => 1, | |
); | |
- $cid = comment_save((array) $comment); | |
+ $cid = comment_save($comment); | |
$comment['cid'] = $cid; | |
$comment_update = $comment; | |
$comment_update['subject'] = $this->randomString(); | |
$comment_update['comment'] = $this->randomString(); | |
- $response = $this->servicesPut($path . '/comment/' . $cid, array('data' => $comment_update)); | |
+ $response = $this->servicesPut($path . '/comment/' . $cid, $comment_update); | |
$comment_load = (array)_comment_load($cid); | |
@@ -154,9 +201,12 @@ class ServicesResourceCommentTests extends ServicesWebtestCase { | |
} | |
/** | |
- * Test delete method. | |
+ * Test update method (Legacy). | |
+ * | |
+ * TODO: To be removed in future version. | |
+ * @see http://drupal.org/node/1083242 | |
*/ | |
- function testCommentDelete() { | |
+ function testCommentUpdateLegacy() { | |
$path = $this->endpoint->path; | |
// Create node with commenting. | |
@@ -164,74 +214,57 @@ class ServicesResourceCommentTests extends ServicesWebtestCase { | |
$node = $this->drupalCreateNode($settings); | |
$comment = array( | |
+ 'cid' => NULL, | |
'uid' => $this->privileged_user->uid, | |
'nid' => $node->nid, | |
'subject' => $this->randomString(), | |
'comment' => $this->randomString(), | |
+ 'format' => 1, | |
); | |
$cid = comment_save((array) $comment); | |
$comment['cid'] = $cid; | |
- $response = $this->servicesDelete($path . '/comment/' . $cid); | |
+ $comment_update = $comment; | |
+ $comment_update['subject'] = $this->randomString(); | |
+ $comment_update['comment'] = $this->randomString(); | |
- $comment_load = _comment_load($cid); | |
+ $response = $this->servicesPut($path . '/comment/' . $cid, array('data' => $comment_update)); | |
- $this->assertTrue(empty($comment_load), t('Comment deleted properly.'), 'CommentResource: Delete'); | |
+ $comment_load = (array)_comment_load($cid); | |
+ | |
+ $comment_intersect = array_intersect_assoc($comment_load, $comment_update); | |
+ | |
+ $this->assertEqual($comment_update, $comment_intersect, t('Comment updated properly.'), 'CommentResource: Update (legacy)'); | |
} | |
/** | |
- * Test loadNodeComments method. | |
+ * Test delete method. | |
*/ | |
- function testCommentLoadNodeComments() { | |
+ function testCommentDelete() { | |
$path = $this->endpoint->path; | |
// Create node with commenting. | |
$settings = array('comment' => COMMENT_NODE_READ_WRITE); | |
$node = $this->drupalCreateNode($settings); | |
- $nid = $node->nid; | |
- | |
- // Generate 15 comments for node. | |
- $comments = array(); | |
- for ($i = 0; $i < 15; $i++) { | |
- $comment = array( | |
- 'uid' => $this->privileged_user->uid, | |
- 'nid' => $nid, | |
- 'subject' => $this->randomString(), | |
- 'comment' => $this->randomString(), | |
- ); | |
- | |
- $cid = comment_save((array) $comment); | |
- | |
- $comments[] = _comment_load($cid); | |
- } | |
- $comments = array_reverse($comments); | |
- // Generate some comments for another node. | |
- $settings = array('comment' => COMMENT_NODE_READ_WRITE); | |
- $node2 = $this->drupalCreateNode($settings); | |
- for ($i = 0; $i < 5; $i++) { | |
- $comment = array( | |
- 'uid' => $this->privileged_user->uid, | |
- 'nid' => $node2->nid, | |
- 'subject' => $this->randomString(), | |
- 'comment' => $this->randomString(), | |
- ); | |
+ $comment = array( | |
+ 'cid' => NULL, | |
+ 'uid' => $this->privileged_user->uid, | |
+ 'nid' => $node->nid, | |
+ 'subject' => $this->randomString(), | |
+ 'comment' => $this->randomString(), | |
+ 'format' => 1, | |
+ ); | |
- $cid = comment_save((array) $comment); | |
- } | |
+ $cid = comment_save((array) $comment); | |
+ $comment['cid'] = $cid; | |
- // Load all comments of the first node. | |
- $response = $this->servicesPost($path . '/comment/loadNodeComments', array('nid' => $nid)); | |
- $this->assertEqual(array_slice($comments, 0, 10), $response['body'], t('Received all 15 comments.'), 'CommentResource: loadNodeComments'); | |
+ $response = $this->servicesDelete($path . '/comment/' . $cid); | |
- // Load only 5 comments of the first node. | |
- $response = $this->servicesPost($path . '/comment/loadNodeComments', array('nid' => $nid, 'count' => 5)); | |
- $this->assertEqual(array_slice($comments, 0, 5), $response['body'], t('Received last 5 comments.'), 'CommentResource: loadNodeComments'); | |
+ $comment_load = _comment_load($cid); | |
- // Load only 5 comments of the first node starting from fifth comment. | |
- $response = $this->servicesPost($path . '/comment/loadNodeComments', array('nid' => $nid, 'count' => 5, 'start' => 5)); | |
- $this->assertEqual(array_slice($comments, 5, 5), $response['body'], t('Received 5 comments starting from fifth comment.'), 'CommentResource: loadNodeComments'); | |
+ $this->assertTrue(empty($comment_load), t('Comment deleted properly.'), 'CommentResource: Delete'); | |
} | |
/** | |
@@ -244,10 +277,12 @@ class ServicesResourceCommentTests extends ServicesWebtestCase { | |
$node = $this->drupalCreateNode($settings); | |
for ($i = 0; $i < 5; $i++) { | |
$comment = array( | |
+ 'cid' => NULL, | |
'uid' => $this->privileged_user->uid, | |
'nid' => $node->nid, | |
'subject' => $this->randomString(), | |
'comment' => $this->randomString(), | |
+ 'format' => 1, | |
); | |
$cid = comment_save((array) $comment); | |
} | |
@@ -266,10 +301,12 @@ class ServicesResourceCommentTests extends ServicesWebtestCase { | |
$node = $this->drupalCreateNode($settings); | |
for ($i = 0; $i < 5; $i++) { | |
$comment = array( | |
+ 'cid' => NULL, | |
'uid' => $this->privileged_user->uid, | |
'nid' => $node->nid, | |
'subject' => $this->randomString(), | |
'comment' => $this->randomString(), | |
+ 'format' => 1, | |
); | |
$cid = comment_save((array) $comment); | |
$comments[] = _comment_load($cid); | |
diff --git a/tests/functional/ServicesResourceFileTests.test b/tests/functional/ServicesResourceFileTests.test | |
index f2e750f..51bcdc4 100644 | |
--- a/tests/functional/ServicesResourceFileTests.test | |
+++ b/tests/functional/ServicesResourceFileTests.test | |
@@ -29,7 +29,6 @@ class ServicesResourceFileTests extends ServicesWebTestCase { | |
'ctools', | |
'services', | |
'rest_server', | |
- 'services_sessauth', | |
'inputstream' | |
); | |
// Set up endpoint. | |
@@ -45,12 +44,6 @@ class ServicesResourceFileTests extends ServicesWebTestCase { | |
$this->drupalLogin($this->privileged_user); | |
// Get a test file. | |
$this->testfiles = $this->drupalGetTestFiles('text'); | |
- | |
- // Create directory structure in sandbox. This should be done because | |
- // file_check_directory() will not do that for us as it should. | |
- // @see http://drupal.org/node/180970 | |
- $directory = file_directory_path() . '/sites/default/files/simpletest'; | |
- @mkdir($directory, 0775, TRUE); | |
} | |
/** | |
@@ -71,13 +64,13 @@ class ServicesResourceFileTests extends ServicesWebTestCase { | |
// Create file argument with data. | |
$file = array( | |
'filesize' => filesize($this->testfiles[0]->filename), | |
- 'filename' => $this->testfiles[0]->filename, | |
+ 'filename' => str_replace('/simpletest/', '/' . $this->randomName() . '/' . $this->randomName() . '/', $this->testfiles[0]->filename), | |
'file' => base64_encode(file_get_contents($this->testfiles[0]->filename)), | |
'uid' => $this->privileged_user->uid, | |
); | |
// Create file with call. | |
- $result = $this->servicesPost($this->endpoint->path . '/file', array('file' => $file)); | |
+ $result = $this->servicesPost($this->endpoint->path . '/file', $file); | |
$this->assertEqual($result['code'], 200, t('File created.'), 'FileResource: Create'); | |
// Load file and assert that it exists. | |
@@ -88,7 +81,7 @@ class ServicesResourceFileTests extends ServicesWebTestCase { | |
// Lets create another file with the same details | |
// and ensure name and path are changed automatically. | |
- $result = $this->servicesPost($this->endpoint->path . '/file', array('file' => $file)); | |
+ $result = $this->servicesPost($this->endpoint->path . '/file', $file); | |
$dbresult = db_query('SELECT * FROM {files} WHERE fid = %d', $result['body']['fid']); | |
$file1 = db_fetch_object($dbresult); | |
$file2 = db_fetch_object($dbresult); | |
@@ -97,6 +90,41 @@ class ServicesResourceFileTests extends ServicesWebTestCase { | |
} | |
/** | |
+ * Test create method (Legacy). | |
+ * | |
+ * TODO: To be removed in future version. | |
+ * @see http://drupal.org/node/1083242 | |
+ */ | |
+ public function testResourceFileCreateLegacy() { | |
+ // Create file argument with data. | |
+ $file = array( | |
+ 'filesize' => filesize($this->testfiles[0]->filename), | |
+ 'filename' => $this->testfiles[0]->filename, | |
+ 'file' => base64_encode(file_get_contents($this->testfiles[0]->filename)), | |
+ 'uid' => $this->privileged_user->uid, | |
+ ); | |
+ | |
+ // Create file with call. | |
+ $result = $this->servicesPost($this->endpoint->path . '/file', array('file' => $file)); | |
+ $this->assertEqual($result['code'], 200, t('File created.'), 'FileResource: Create (Legacy)'); | |
+ | |
+ // Load file and assert that it exists. | |
+ //$file_load = file_load($result['body']['fid']); | |
+ $dbresult = db_query('SELECT * FROM {files} WHERE fid = %d', $result['body']['fid']); | |
+ $file_load = db_fetch_object($dbresult); | |
+ $this->assertTrue(is_file($file_load->filepath), t('New file saved to disk.'), 'FileResource: Create (Legacy)'); | |
+ | |
+ // Lets create another file with the same details | |
+ // and ensure name and path are changed automatically. | |
+ $result = $this->servicesPost($this->endpoint->path . '/file', array('file' => $file)); | |
+ $dbresult = db_query('SELECT * FROM {files} WHERE fid = %d', $result['body']['fid']); | |
+ $file1 = db_fetch_object($dbresult); | |
+ $file2 = db_fetch_object($dbresult); | |
+ $this->assertTrue($file1->filename != $file2->filename && $file1->filepath != $file2->filepath, | |
+ t('Two identical files created end up with different names.'), 'FileResource: Create (Legacy)'); | |
+ } | |
+ | |
+ /** | |
* Test retrieve method. | |
*/ | |
public function testResourceFileRetrieve() { | |
@@ -137,7 +165,7 @@ class ServicesResourceFileTests extends ServicesWebTestCase { | |
*/ | |
public function uploadTestFile($file = NULL) { | |
if (empty($file)) { | |
- $file = next($this->testfiles); | |
+ $file = current($this->testfiles); | |
} | |
$testfile = array( | |
'fid' => NULL, | |
@@ -152,7 +180,17 @@ class ServicesResourceFileTests extends ServicesWebTestCase { | |
$source = $testfile['filepath']; | |
$destination = file_directory_path() . '/' . $testfile['filepath']; | |
$dirname = dirname($destination); | |
- file_check_directory($dirname, FILE_CREATE_DIRECTORY); | |
+ | |
+ // Build the destination folder tree if it doesn't already exists. | |
+ // @see http://drupal.org/node/180970 | |
+ $dir_array = explode('/', $dirname); | |
+ $file_check_directory_array = array(); | |
+ foreach ($dir_array as $dir_element) { | |
+ $file_check_directory_array[] = $dir_element; | |
+ $dir_path_element = implode('/', $file_check_directory_array); | |
+ file_check_directory($dir_path_element, FILE_CREATE_DIRECTORY); | |
+ } | |
+ | |
file_copy($source, $destination); | |
drupal_write_record('files', $testfile); | |
diff --git a/tests/functional/ServicesResourceNodeTests.test b/tests/functional/ServicesResourceNodeTests.test | |
index 76275f2..4d11e10 100644 | |
--- a/tests/functional/ServicesResourceNodeTests.test | |
+++ b/tests/functional/ServicesResourceNodeTests.test | |
@@ -28,7 +28,6 @@ class ServicesResourceNodetests extends ServicesWebtestCase { | |
'ctools', | |
'services', | |
'rest_server', | |
- 'services_sessauth', | |
'inputstream', | |
'upload', | |
'comment' | |
@@ -98,7 +97,7 @@ class ServicesResourceNodetests extends ServicesWebtestCase { | |
'name' => $this->privilegedUser->name, | |
); | |
- $responseArray = $this->servicesPost($this->endpoint->path . '/node', array('node' => $node)); | |
+ $responseArray = $this->servicesPost($this->endpoint->path . '/node', $node); | |
$nodeResourceCreateReturn = $responseArray['body']; | |
$this->assertTrue(isset($nodeResourceCreateReturn['nid']), t('Node was successfully created'), 'NodeResource: Create'); | |
@@ -110,6 +109,37 @@ class ServicesResourceNodetests extends ServicesWebtestCase { | |
} | |
/** | |
+ * Testing node_resource Create (Legacy). | |
+ * | |
+ * TODO: To be removed in future version. | |
+ * @see http://drupal.org/node/1083242 | |
+ */ | |
+ public function testEndpointResourceNodeCreateLegacy() { | |
+ // Create and log in our privileged user. | |
+ $this->privilegedUser = $this->drupalCreateUser(array( | |
+ 'administer services', | |
+ 'administer nodes', | |
+ )); | |
+ $this->drupalLogin($this->privilegedUser); | |
+ $node = array( | |
+ 'title' => 'testing', | |
+ 'body' => 'bodytest', | |
+ 'type' => 'story', | |
+ 'name' => $this->privilegedUser->name, | |
+ ); | |
+ | |
+ $responseArray = $this->servicesPost($this->endpoint->path . '/node', array('node' => $node)); | |
+ $nodeResourceCreateReturn = $responseArray['body']; | |
+ | |
+ $this->assertTrue(isset($nodeResourceCreateReturn['nid']), t('Node was successfully created'), 'NodeResource: Create (Legacy)'); | |
+ if (isset($nodeResourceCreateReturn['nid'])) { | |
+ $newNode = node_load($nodeResourceCreateReturn['nid']); | |
+ $this->assertTrue($newNode->title = $node['title'], t('Title was the same'), 'NodeResource: Create (Legacy)'); | |
+ $this->assertTrue($newNode->body = $node['body'], t('Body was the same'), 'NodeResource: Create (Legacy)'); | |
+ } | |
+ } | |
+ | |
+ /** | |
* testing node_resource Created make ure it fails with no perms | |
*/ | |
public function testEndpointResourceNodeCreateFail() { | |
@@ -125,7 +155,7 @@ class ServicesResourceNodetests extends ServicesWebtestCase { | |
'name' => $this->privilegedUser->name, | |
); | |
- $responseArray = $this->servicesPost($this->endpoint->path . '/node', array('node' => $node)); | |
+ $responseArray = $this->servicesPost($this->endpoint->path . '/node', $node); | |
$this->assertTrue($responseArray['code'] == 401, t('User with not sufficient permissions cannot create node'), 'NodeResource: Create'); | |
} | |
@@ -148,11 +178,9 @@ class ServicesResourceNodetests extends ServicesWebtestCase { | |
'type' => 'page', | |
); | |
- $responseArray = $this->servicesPost($this->endpoint->path . '/node', array('node' => $node)); | |
+ $response_array = $this->servicesPost($this->endpoint->path . '/node', $node); | |
- $nodeResourceUpdateReturn = $responseArray['body']; | |
- $nodeAfterUpdate = node_load($nodeResourceUpdateReturn); | |
- $this->assertTrue(strpos($responseArray['status'], 'Title field is required.'), t('Node was not created without title. '), 'NodeResource: Created'); | |
+ $this->assertEqual($response_array['body']['form_errors']['title'], t('Title field is required.'), t('Node was not created without title. '), 'NodeResource: Created'); | |
} | |
/** | |
@@ -195,7 +223,7 @@ class ServicesResourceNodetests extends ServicesWebtestCase { | |
)), | |
); | |
- $response = $this->servicesPost($this->endpoint->path . '/node', array('node' => $node)); | |
+ $response = $this->servicesPost($this->endpoint->path . '/node', $node); | |
// Get number of attached taxonomy terms from this node. We do it manually | |
// as we cannot get terms on node_load as as empty array is statically | |
@@ -219,7 +247,6 @@ class ServicesResourceNodetests extends ServicesWebtestCase { | |
$node = $this->drupalCreateNode($settings); | |
$nid = $node->nid; | |
- $this->pass(print_r($node, 1)); | |
// Generate 15 comments for node. | |
$comments = array(); | |
for ($i = 0; $i < 15; $i++) { | |
@@ -230,7 +257,7 @@ class ServicesResourceNodetests extends ServicesWebtestCase { | |
'comment' => $this->randomString(), | |
); | |
- $cid = comment_save((array) $comment); | |
+ $cid = comment_save($comment); | |
$comments[] = _comment_load($cid); | |
} | |
@@ -247,13 +274,12 @@ class ServicesResourceNodetests extends ServicesWebtestCase { | |
'comment' => $this->randomString(), | |
); | |
- $cid = comment_save((array) $comment); | |
+ $cid = comment_save($comment); | |
} | |
// Load all comments of the first node. | |
$response = $this->servicesGet($path . '/node/'. $nid .'/comments'); | |
- $this->pass($response); | |
$this->assertEqual($comments, $response['body'], t('Received all 15 comments.'), 'NodeResource: comments'); | |
// Load only 5 comments of the first node. | |
@@ -265,9 +291,9 @@ class ServicesResourceNodetests extends ServicesWebtestCase { | |
} | |
/** | |
- * Test nodeFiles action. | |
+ * Test files relationship. | |
*/ | |
- public function testNodeFiles() { | |
+ public function testNodeRelationshipFiles() { | |
$this->privileged_user = $this->drupalCreateUser(array( | |
'get own binary files', | |
'save file information', | |
@@ -299,13 +325,14 @@ class ServicesResourceNodetests extends ServicesWebtestCase { | |
)); | |
$node = $this->drupalCreateNode($settings); | |
- $result = $this->servicesPost($this->endpoint->path . '/node/nodeFiles', array('nid' => $node->nid)); | |
+ $result = $this->servicesGet($this->endpoint->path . '/node/' . $node->nid . '/files'); | |
$this->assertTrue(isset($result['body'][$testfile1['fid']]) && isset($result['body'][$testfile2['fid']]) && !isset($result['body'][$testfile3['fid']]), | |
t('Attached files listed.'), 'FileResource: nodeFiles'); | |
} | |
+ | |
/** | |
- * testing node_resource Update | |
- */ | |
+ * Testing node_resource Update | |
+ */ | |
public function testEndpointResourceNodeUpdate() { | |
// Create and log in our privileged user. | |
$this->privilegedUser = $this->drupalCreateUser(array( | |
@@ -321,13 +348,50 @@ class ServicesResourceNodetests extends ServicesWebtestCase { | |
'name' => $this->privilegedUser->name, | |
); | |
- $responseArray = $this->servicesPut($this->endpoint->path . '/node/' . $node->nid, array('node' => $node_update)); | |
+ $responseArray = $this->servicesPut($this->endpoint->path . '/node/' . $node->nid, $node_update); | |
$nodeAfterUpdate = node_load($responseArray['body']['nid']); | |
$this->assertTrue(isset($nodeAfterUpdate->nid), t('Node was successfully updated'), 'NodeResource: Updated'); | |
if (isset($nodeAfterUpdate->nid)) { | |
$this->assertTrue($nodeAfterUpdate->title == $node_update['title'], t('Title was the same'), 'NodeResource: Update'); | |
$this->assertTrue($nodeAfterUpdate->body == $node_update['body'], t('Body was the same'), 'NodeResource: Update'); | |
} | |
+ | |
+ // Try to update the node without node type. | |
+ $node_update_no_type = $node_update; | |
+ unset($node_update_no_type['type']); | |
+ $node_update_no_type['title'] = $this->randomName(); | |
+ $responseArray = $this->servicesPut($this->endpoint->path . '/node/' . $node->nid, $node_update_no_type); | |
+ $this->assertNotEqual($responseArray['code'], 200, t('Can\'t update node without specifying node type'), 'NodeResource: Update'); | |
+ } | |
+ | |
+ /** | |
+ * Testing node_resource Update (Legacy). | |
+ * | |
+ * TODO: To be removed in future version. | |
+ * @see http://drupal.org/node/1083242 | |
+ */ | |
+ public function testEndpointResourceNodeUpdateLegacy() { | |
+ // Create and log in our privileged user. | |
+ $this->privilegedUser = $this->drupalCreateUser(array( | |
+ 'administer services', | |
+ 'administer nodes', | |
+ )); | |
+ $this->drupalLogin($this->privilegedUser); | |
+ $node = $this->drupalCreateNode(); | |
+ $node_update = array( | |
+ 'title' => 'testing', | |
+ 'body' => 'bodytest', | |
+ 'type' => 'story', | |
+ 'name' => $this->privilegedUser->name, | |
+ ); | |
+ | |
+ $responseArray = $this->servicesPut($this->endpoint->path . '/node/' . $node->nid, array('node' => $node_update)); | |
+ $nodeAfterUpdate = node_load($responseArray['body']['nid']); | |
+ $this->assertTrue(isset($nodeAfterUpdate->nid), t('Node was successfully updated'), 'NodeResource: Updated (Legacy)'); | |
+ if (isset($nodeAfterUpdate->nid)) { | |
+ $this->assertTrue($nodeAfterUpdate->title == $node_update['title'], t('Title was the same'), 'NodeResource: Update (Legacy)'); | |
+ $this->assertTrue($nodeAfterUpdate->body == $node_update['body'], t('Body was the same'), 'NodeResource: Update (Legacy)'); | |
+ } | |
} | |
/** | |
@@ -352,7 +416,7 @@ class ServicesResourceNodetests extends ServicesWebtestCase { | |
'body' => 'bodytest', | |
'type' => 'story', | |
); | |
- $responseArray = $this->servicesPut($this->endpoint->path . '/node/' . $node->nid, array('node' => $node_update)); | |
+ $responseArray = $this->servicesPut($this->endpoint->path . '/node/' . $node->nid, $node_update); | |
$this->assertTrue(strpos($responseArray['status'], 'Access denied for user'), t('Updating the node failed without needed permissions. This is good!'), 'NodeResource: Update'); | |
} | |
@@ -375,7 +439,7 @@ class ServicesResourceNodetests extends ServicesWebtestCase { | |
'type' => 'story', | |
); | |
- $responseArray = $this->servicesPut($this->endpoint->path . '/node/' . $node->nid, array('node' => $node_update)); | |
+ $responseArray = $this->servicesPut($this->endpoint->path . '/node/' . $node->nid, $node_update); | |
$this->assertTrue(strpos($responseArray['status'], 'Title field is required.'), t('Node was not updated without title.'), 'NodeResource: Update'); | |
} | |
diff --git a/tests/functional/ServicesResourceSystemTests.test b/tests/functional/ServicesResourceSystemTests.test | |
index c836c03..625e58e 100644 | |
--- a/tests/functional/ServicesResourceSystemTests.test | |
+++ b/tests/functional/ServicesResourceSystemTests.test | |
@@ -27,7 +27,6 @@ class ServicesResourceSystemTests extends ServicesWebtestCase { | |
'ctools', | |
'services', | |
'rest_server', | |
- 'services_sessauth', | |
'inputstream' | |
); | |
// Set up endpoint. | |
diff --git a/tests/functional/ServicesResourceTaxonomyTests.test b/tests/functional/ServicesResourceTaxonomyTests.test | |
index 1bbf87e..2d2b97d 100644 | |
--- a/tests/functional/ServicesResourceTaxonomyTests.test | |
+++ b/tests/functional/ServicesResourceTaxonomyTests.test | |
@@ -27,7 +27,6 @@ class ServicesResourceTaxonomyTests extends ServicesWebtestCase { | |
'ctools', | |
'services', | |
'rest_server', | |
- 'services_sessauth', | |
'inputstream' | |
); | |
// Set up endpoint. | |
@@ -67,7 +66,7 @@ class ServicesResourceTaxonomyTests extends ServicesWebtestCase { | |
'weight' => 0, | |
); | |
- $response = $this->servicesPost($path . '/taxonomy_vocabulary', array('vocabulary' => $vocabulary)); | |
+ $response = $this->servicesPost($path . '/taxonomy_vocabulary', $vocabulary); | |
$vid = db_result(db_query('SELECT vid FROM {vocabulary} WHERE name = "%s"', $vocabulary['name'])); | |
@@ -78,6 +77,36 @@ class ServicesResourceTaxonomyTests extends ServicesWebtestCase { | |
} | |
/** | |
+ * Test taxonomy vocabulary create method (Legacy). | |
+ * | |
+ * TODO: To be removed in future version. | |
+ * @see http://drupal.org/node/1083242 | |
+ */ | |
+ function testVocabularyCreateLegacy() { | |
+ $path = $this->endpoint->path; | |
+ | |
+ $vocabulary = array( | |
+ 'name' => $this->randomName(), | |
+ 'description' => $this->randomString(), | |
+ 'help' => $this->randomString(), | |
+ 'relations' => 1, | |
+ 'hierarchy' => 1, | |
+ 'multiple' => 1, | |
+ 'required' => 0, | |
+ 'module' => 'services', | |
+ 'weight' => 0, | |
+ ); | |
+ | |
+ $response = $this->servicesPost($path . '/taxonomy_vocabulary', array('vocabulary' => $vocabulary)); | |
+ | |
+ $vid = db_result(db_query('SELECT vid FROM {vocabulary} WHERE name = "%s"', $vocabulary['name'])); | |
+ | |
+ $vocabulary_load = (array)taxonomy_vocabulary_load($vid); | |
+ $vocabulary_intersect = array_intersect_assoc($response['body'], $vocabulary_load); | |
+ $this->assertEqual($vocabulary['name'], $response['body']['name'], t('Taxonomy vocabulary created properly.'), 'TaxonomyVocabularyResource: Create (Legacy)'); | |
+ } | |
+ | |
+ /** | |
* Test taxonomy vocabulry retrieve method. | |
*/ | |
function testVocabularyRetrieve() { | |
@@ -106,7 +135,7 @@ class ServicesResourceTaxonomyTests extends ServicesWebtestCase { | |
$vocabulary['name'] = $this->randomName(); | |
$vocabulary['description'] = $this->randomString(); | |
- $response = $this->servicesPUT($path . '/taxonomy_vocabulary/' . $vid, array('vocabulary' => $vocabulary)); | |
+ $response = $this->servicesPUT($path . '/taxonomy_vocabulary/' . $vid, $vocabulary); | |
$vocabulary_load = (array)taxonomy_vocabulary_load($vid, TRUE); | |
@@ -115,6 +144,29 @@ class ServicesResourceTaxonomyTests extends ServicesWebtestCase { | |
} | |
/** | |
+ * Test taxonomy vocabulary update (Legacy). | |
+ * | |
+ * TODO: To be removed in future version. | |
+ * @see http://drupal.org/node/1083242 | |
+ */ | |
+ function testVocabularyUpdateLegacy() { | |
+ $path = $this->endpoint->path; | |
+ | |
+ $vocabulary = $this->createVocabulary(); | |
+ $vid = $vocabulary['vid']; | |
+ | |
+ $vocabulary['name'] = $this->randomName(); | |
+ $vocabulary['description'] = $this->randomString(); | |
+ | |
+ $response = $this->servicesPUT($path . '/taxonomy_vocabulary/' . $vid, array('vocabulary' => $vocabulary)); | |
+ | |
+ $vocabulary_load = (array)taxonomy_vocabulary_load($vid, TRUE); | |
+ | |
+ $vocabulary_intersect = array_intersect_assoc($vocabulary, $vocabulary_load); | |
+ $this->assertEqual($vocabulary, $vocabulary_intersect, t('Taxonomy vocabulary updated properly.'), 'TaxonomyVocabularyResource: Update (Legacy)'); | |
+ } | |
+ | |
+ /** | |
* Test taxonomy vocabulary delete method. | |
*/ | |
function testVocabularyDelete() { | |
@@ -198,7 +250,7 @@ class ServicesResourceTaxonomyTests extends ServicesWebtestCase { | |
'parent' => NULL, | |
); | |
- $response = $this->servicesPost($path . '/taxonomy_term', array('term' => $term)); | |
+ $response = $this->servicesPost($path . '/taxonomy_term', $term); | |
// Load term by name. | |
$term_by_name = (array)current(taxonomy_get_term_by_name($term['name'])); | |
@@ -212,6 +264,38 @@ class ServicesResourceTaxonomyTests extends ServicesWebtestCase { | |
} | |
/** | |
+ * Test taxonomy term create method (Legacy). | |
+ * | |
+ * TODO: To be removed in future version. | |
+ * @see http://drupal.org/node/1083242 | |
+ */ | |
+ function testTermCreateLegacy() { | |
+ $path = $this->endpoint->path; | |
+ | |
+ $vocabulary = $this->createVocabulary(); | |
+ | |
+ $term = array( | |
+ 'vid' => $vocabulary['vid'], | |
+ 'name' => $this->randomName(), | |
+ 'description' => $this->randomString(), | |
+ 'weight' => rand(0, 100), | |
+ 'parent' => NULL, | |
+ ); | |
+ | |
+ $response = $this->servicesPost($path . '/taxonomy_term', array('term' => $term)); | |
+ | |
+ // Load term by name. | |
+ $term_by_name = (array)current(taxonomy_get_term_by_name($term['name'])); | |
+ $term_intersect = array_intersect_assoc($term, $term_by_name); | |
+ | |
+ // As term_intersect will not have parent, we unset this property. | |
+ $term_data = $term; | |
+ unset($term_data['parent']); | |
+ | |
+ $this->assertEqual($term_data, $term_intersect, t('Taxonomy term created properly.'), 'TaxonomyTermResource: Create (Legacy)'); | |
+ } | |
+ | |
+ /** | |
* Test taxonomy term retrieve method. | |
*/ | |
function testTermRetrieve() { | |
@@ -241,7 +325,7 @@ class ServicesResourceTaxonomyTests extends ServicesWebtestCase { | |
'weight' => rand(0, 100), | |
); | |
- $this->servicesPut($path . '/taxonomy_term/' . $term['tid'], array('term' => $term_update_data)); | |
+ $this->servicesPut($path . '/taxonomy_term/' . $term['tid'], $term_update_data); | |
$term_update = (array)current(taxonomy_get_term_by_name($term_update_data['name'])); | |
@@ -250,6 +334,32 @@ class ServicesResourceTaxonomyTests extends ServicesWebtestCase { | |
} | |
/** | |
+ * Test taxonomy term update method (Legacy). | |
+ * | |
+ * TODO: To be removed in future version. | |
+ * @see http://drupal.org/node/1083242 | |
+ */ | |
+ function testTermUpdateLegacy() { | |
+ $path = $this->endpoint->path; | |
+ | |
+ $vocabulary = $this->createVocabulary(); | |
+ $term = $this->createTerm($vocabulary['vid']); | |
+ | |
+ $term_update_data = array( | |
+ 'name' => $this->randomName(), | |
+ 'description' => $this->randomString(), | |
+ 'weight' => rand(0, 100), | |
+ ); | |
+ | |
+ $this->servicesPut($path . '/taxonomy_term/' . $term['tid'], array('term' => $term_update_data)); | |
+ | |
+ $term_update = (array)current(taxonomy_get_term_by_name($term_update_data['name'])); | |
+ | |
+ // Ensure that terms have different names but same tid. | |
+ $this->assertTrue(($term['tid'] == $term_update['tid']) && ($term['name'] != $term_update['name']), t('Taxonomy term updated properly.'), 'TaxonomyTermResource: Update (Legacy)'); | |
+ } | |
+ | |
+ /** | |
* Test taxonomy term delete method. | |
*/ | |
function testTermDelete() { | |
diff --git a/tests/functional/ServicesResourceUserTests.test b/tests/functional/ServicesResourceUserTests.test | |
index a800a3f..5cb3545 100644 | |
--- a/tests/functional/ServicesResourceUserTests.test | |
+++ b/tests/functional/ServicesResourceUserTests.test | |
@@ -27,7 +27,6 @@ class ServicesResourceUsertests extends ServicesWebtestCase { | |
'ctools', | |
'services', | |
'rest_server', | |
- 'services_sessauth', | |
'inputstream' | |
); | |
// Set up endpoint. | |
@@ -61,7 +60,7 @@ class ServicesResourceUsertests extends ServicesWebtestCase { | |
$user['pass'] = user_password(); | |
$user['status'] = 1; | |
- $response = $this->servicesPost($this->endpoint->path . '/user', array('account' => $user)); | |
+ $response = $this->servicesPost($this->endpoint->path . '/user', $user); | |
$account = $response['body']; | |
$this->assertTrue(!empty($account['uid']), t('User has been create successfully.'), 'UserResource: Create'); | |
@@ -74,11 +73,42 @@ class ServicesResourceUsertests extends ServicesWebtestCase { | |
$user['name'] = $this->randomName(); | |
$user['pass'] = user_password(); | |
$user['status'] = 1; | |
- $response = $this->servicesPost($this->endpoint->path . '/user', array('account' => $user)); | |
+ $response = $this->servicesPost($this->endpoint->path . '/user', $user); | |
$this->assertTrue(strpos($response['status'], 'E-mail address field is required') !== FALSE, t('It is not possible to create user without email.'), 'UserResource: Create'); | |
} | |
/** | |
+ * Test create method (Legacy). | |
+ * | |
+ * TODO: To be removed in future version. | |
+ * @see http://drupal.org/node/1083242 | |
+ */ | |
+ function testCreateUserLegacy() { | |
+ // Create user. | |
+ $user = array(); | |
+ $user['name'] = $this->randomName(); | |
+ $user['mail'] = $user['name'] . '@example.com'; | |
+ $user['pass'] = user_password(); | |
+ $user['status'] = 1; | |
+ | |
+ $response = $this->servicesPost($this->endpoint->path . '/user', array('account' => $user)); | |
+ $account = $response['body']; | |
+ $this->assertTrue(!empty($account['uid']), t('User has been create successfully.'), 'UserResource: Create (Legacy)'); | |
+ | |
+ // Load user. | |
+ $user_load = user_load($account['uid']); | |
+ $this->assertTrue(!empty($user_load), t('Newly created user has been loaded successfully.'), 'UserResource: Create (Legacy)'); | |
+ | |
+ // Try to create user without email. | |
+ $user = array(); | |
+ $user['name'] = $this->randomName(); | |
+ $user['pass'] = user_password(); | |
+ $user['status'] = 1; | |
+ $response = $this->servicesPost($this->endpoint->path . '/user', array('account' => $user)); | |
+ $this->assertTrue(strpos($response['status'], 'E-mail address field is required') !== FALSE, t('It is not possible to create user without email.'), 'UserResource: Create (Legacy)'); | |
+ } | |
+ | |
+ /** | |
* Test register method. | |
* | |
* Register user, load user. | |
@@ -91,8 +121,8 @@ class ServicesResourceUsertests extends ServicesWebtestCase { | |
$user['pass'] = user_password(); | |
$user['status'] = 1; | |
- $response = $this->servicesPost($this->endpoint->path . '/user/register', array('account' => $user)); | |
- | |
+ $response = $this->servicesPost($this->endpoint->path . '/user/register', $user); | |
+ | |
//Verify logged in users cannnot create accounts | |
$code = $response['code']; | |
$this->assertEqual($code, '401', t('Verify permission denied 401'), 'UserResource: Create'); | |
@@ -100,7 +130,7 @@ class ServicesResourceUsertests extends ServicesWebtestCase { | |
//Verify logged out state can create users | |
$this->drupalLogout(); | |
- $response = $this->servicesPost($this->endpoint->path . '/user/register', array('account' => $user)); | |
+ $response = $this->servicesPost($this->endpoint->path . '/user/register', $user); | |
$account = $response['body']; | |
$this->assertTrue(!empty($account['uid']), t('User has been create successfully.'), 'UserResource: Create'); | |
@@ -112,6 +142,40 @@ class ServicesResourceUsertests extends ServicesWebtestCase { | |
} | |
/** | |
+ * Test register method (Legacy). | |
+ * | |
+ * TODO: To be removed in future version. | |
+ * @see http://drupal.org/node/1083242 | |
+ */ | |
+ function testRegisterUserLegacy() { | |
+ // Create user. | |
+ $user = array(); | |
+ $user['name'] = $this->randomName(); | |
+ $user['mail'] = $user['name'] . '@example.com'; | |
+ $user['pass'] = user_password(); | |
+ $user['status'] = 1; | |
+ | |
+ $response = $this->servicesPost($this->endpoint->path . '/user/register', array('account' => $user)); | |
+ | |
+ //Verify logged in users cannnot create accounts | |
+ $code = $response['code']; | |
+ $this->assertEqual($code, '401', t('Verify permission denied 401'), 'UserResource: Create (Legacy)'); | |
+ | |
+ //Verify logged out state can create users | |
+ $this->drupalLogout(); | |
+ | |
+ $response = $this->servicesPost($this->endpoint->path . '/user/register', array('account' => $user)); | |
+ $account = $response['body']; | |
+ | |
+ $this->assertTrue(!empty($account['uid']), t('User has been create successfully.'), 'UserResource: Create (Legacy)'); | |
+ | |
+ // Load user. | |
+ $user_load = user_load($account['uid']); | |
+ $this->assertTrue(!empty($user_load), t('Newly created user has been loaded successfully.'), 'UserResource: Create (Legacy)'); | |
+ $this->drupalLogin($this->privileged_user); | |
+ } | |
+ | |
+ /** | |
* Test retrieve method. | |
*/ | |
function testRetrieveUser() { | |
@@ -141,13 +205,37 @@ class ServicesResourceUsertests extends ServicesWebtestCase { | |
'roles' => $account->roles, | |
'mail' => $this->randomName() . '@example.com', | |
); | |
- $response = $this->servicesPut($this->endpoint->path . '/user/' . $account->uid, array('data' => $updated_account)); | |
+ $response = $this->servicesPut($this->endpoint->path . '/user/' . $account->uid, $updated_account); | |
$user_load = user_load($account->uid); | |
$this->assertEqual($updated_account['mail'], $user_load->mail, t('User details have been updated successfully'), 'UserResource: Update'); | |
} | |
/** | |
+ * Test update method (Legacy). | |
+ * | |
+ * TODO: To be removed in future version. | |
+ * @see http://drupal.org/node/1083242 | |
+ */ | |
+ function testUpdateUserLegacy() { | |
+ // Create user. | |
+ $account = $this->drupalCreateUser(); | |
+ | |
+ // Update mail of the user. Note: roles is required attribute as update | |
+ // method does drupal_execute of user_profile_form form. | |
+ $updated_account = array( | |
+ 'uid' => $account->uid, | |
+ 'name' => $account->name, | |
+ 'roles' => $account->roles, | |
+ 'mail' => $this->randomName() . '@example.com', | |
+ ); | |
+ $response = $this->servicesPut($this->endpoint->path . '/user/' . $account->uid, array('data' => $updated_account)); | |
+ | |
+ $user_load = user_load($account->uid); | |
+ $this->assertEqual($updated_account['mail'], $user_load->mail, t('User details have been updated successfully'), 'UserResource: Update (Legacy)'); | |
+ } | |
+ | |
+ /** | |
* Test delete method. | |
*/ | |
function testDeleteUser() { | |
@@ -198,7 +286,7 @@ class ServicesResourceUsertests extends ServicesWebtestCase { | |
// Try to login with wrong credentials. | |
$response = $this->servicesPost($this->endpoint->path . '/user/login', array('username' => $account->name, 'password' => $this->randomString())); | |
$this->assertTrue(strpos($response['status'], 'Wrong username or password') !== FALSE, t('User cannot login with wrong username / password.'), 'UserResource: Login'); | |
- | |
+ | |
//Try missing param | |
$response = $this->servicesPost($this->endpoint->path . '/user/login', array('user' => $account->name, 'password' => $this->randomString())); | |
$this->assertTrue(strpos($response['status'], 'Missing required argument username') !== FALSE, t('Found missing requirment'), 'UserResource: Login'); | |
diff --git a/tests/functional/ServicesWebTestCase.php b/tests/functional/ServicesWebTestCase.php | |
index 70f012f..b6547ea 100644 | |
--- a/tests/functional/ServicesWebTestCase.php | |
+++ b/tests/functional/ServicesWebTestCase.php | |
@@ -156,7 +156,7 @@ class ServicesWebTestCase extends DrupalWebTestCase { | |
} | |
public function saveNewEndpoint() { | |
- $edit = $this->populateEndpointFAPI() ; | |
+ $edit = $this->populateEndpointFAPI(); | |
$endpoint = new stdClass; | |
$endpoint->disabled = FALSE; /* Edit this to true to make a default endpoint disabled initially */ | |
$endpoint->api_version = 3; | |
@@ -165,11 +165,33 @@ class ServicesWebTestCase extends DrupalWebTestCase { | |
$endpoint->server = $edit['server']; | |
$endpoint->path = $edit['path']; | |
$endpoint->authentication = array( | |
- 'services_sessauth' => array(), | |
+ 'services' => 'services', | |
+ ); | |
+ $endpoint->server_settings = array( | |
+ 'rest_server' => array( | |
+ 'formatters' => array( | |
+ 'json' => TRUE, | |
+ 'bencode' => TRUE, | |
+ 'rss' => TRUE, | |
+ 'plist' => TRUE, | |
+ 'xmlplist' => TRUE, | |
+ 'php' => TRUE, | |
+ 'yaml' => TRUE, | |
+ 'jsonp' => FALSE, | |
+ 'xml' => FALSE, | |
+ ), | |
+ 'parsers' => array( | |
+ 'application/x-yaml' => TRUE, | |
+ 'application/json' => TRUE, | |
+ 'application/vnd.php.serialized' => TRUE, | |
+ 'application/plist' => TRUE, | |
+ 'application/plist+xml' => TRUE, | |
+ 'application/x-www-form-urlencoded' => TRUE, | |
+ ), | |
+ ), | |
); | |
$endpoint->resources = array( | |
- 'node' => array( | |
- 'alias' => '', | |
+ 'comment' => array( | |
'operations' => array( | |
'create' => array( | |
'enabled' => 1, | |
@@ -187,39 +209,37 @@ class ServicesWebTestCase extends DrupalWebTestCase { | |
'enabled' => 1, | |
), | |
), | |
- 'relationships' => array( | |
- 'nodefiles' => array( | |
+ 'actions' => array( | |
+ 'countAll' => array( | |
'enabled' => 1, | |
), | |
- 'comments' => array( | |
+ 'countNew' => array( | |
'enabled' => 1, | |
), | |
), | |
), | |
- 'system' => array( | |
- 'alias' => '', | |
- 'actions' => array( | |
- 'connect' => array( | |
+ 'file' => array( | |
+ 'operations' => array( | |
+ 'create' => array( | |
'enabled' => 1, | |
), | |
- 'get_variable' => array( | |
+ 'retrieve' => array( | |
'enabled' => 1, | |
), | |
- 'set_variable' => array( | |
+ 'delete' => array( | |
'enabled' => 1, | |
), | |
- 'del_variable' => array( | |
+ 'index' => array( | |
'enabled' => 1, | |
), | |
), | |
), | |
- 'taxonomy_term' => array( | |
- 'alias' => '', | |
+ 'node' => array( | |
'operations' => array( | |
- 'create' => array( | |
+ 'retrieve' => array( | |
'enabled' => 1, | |
), | |
- 'retrieve' => array( | |
+ 'create' => array( | |
'enabled' => 1, | |
), | |
'update' => array( | |
@@ -228,42 +248,41 @@ class ServicesWebTestCase extends DrupalWebTestCase { | |
'delete' => array( | |
'enabled' => 1, | |
), | |
- ), | |
- 'actions' => array( | |
- 'selectNodes' => array( | |
+ 'index' => array( | |
'enabled' => 1, | |
), | |
), | |
- ), | |
- 'taxonomy_vocabulary' => array( | |
- 'alias' => '', | |
- 'operations' => array( | |
- 'create' => array( | |
+ 'relationships' => array( | |
+ 'files' => array( | |
'enabled' => 1, | |
), | |
- 'retrieve' => array( | |
+ 'comments' => array( | |
'enabled' => 1, | |
), | |
- 'update' => array( | |
+ ), | |
+ ), | |
+ 'system' => array( | |
+ 'actions' => array( | |
+ 'connect' => array( | |
'enabled' => 1, | |
), | |
- 'delete' => array( | |
+ 'get_variable' => array( | |
'enabled' => 1, | |
), | |
- ), | |
- 'actions' => array( | |
- 'getTree' => array( | |
+ 'set_variable' => array( | |
+ 'enabled' => 1, | |
+ ), | |
+ 'del_variable' => array( | |
'enabled' => 1, | |
), | |
), | |
), | |
- 'user' => array( | |
- 'alias' => '', | |
+ 'taxonomy_term' => array( | |
'operations' => array( | |
- 'create' => array( | |
+ 'retrieve' => array( | |
'enabled' => 1, | |
), | |
- 'retrieve' => array( | |
+ 'create' => array( | |
'enabled' => 1, | |
), | |
'update' => array( | |
@@ -277,24 +296,17 @@ class ServicesWebTestCase extends DrupalWebTestCase { | |
), | |
), | |
'actions' => array( | |
- 'login' => array( | |
- 'enabled' => 1, | |
- ), | |
- 'logout' => array( | |
- 'enabled' => 1, | |
- ), | |
- 'register' => array( | |
+ 'selectNodes' => array( | |
'enabled' => 1, | |
), | |
), | |
), | |
- 'comment' => array( | |
- 'alias' => '', | |
+ 'taxonomy_vocabulary' => array( | |
'operations' => array( | |
- 'create' => array( | |
+ 'retrieve' => array( | |
'enabled' => 1, | |
), | |
- 'retrieve' => array( | |
+ 'create' => array( | |
'enabled' => 1, | |
), | |
'update' => array( | |
@@ -303,41 +315,48 @@ class ServicesWebTestCase extends DrupalWebTestCase { | |
'delete' => array( | |
'enabled' => 1, | |
), | |
- ), | |
- 'actions' => array( | |
- 'countAll' => array( | |
+ 'index' => array( | |
'enabled' => 1, | |
), | |
- 'countNew' => array( | |
+ ), | |
+ 'actions' => array( | |
+ 'getTree' => array( | |
'enabled' => 1, | |
), | |
), | |
), | |
- 'file' => array( | |
- 'alias' => '', | |
+ 'user' => array( | |
'operations' => array( | |
+ 'retrieve' => array( | |
+ 'enabled' => 1, | |
+ ), | |
'create' => array( | |
'enabled' => 1, | |
), | |
- 'retrieve' => array( | |
+ 'update' => array( | |
'enabled' => 1, | |
), | |
'delete' => array( | |
'enabled' => 1, | |
), | |
- ), | |
- ), | |
- 'echo' => array( | |
- 'alias' => '', | |
- 'operations' => array( | |
'index' => array( | |
'enabled' => 1, | |
), | |
), | |
+ 'actions' => array( | |
+ 'login' => array( | |
+ 'enabled' => 1, | |
+ ), | |
+ 'logout' => array( | |
+ 'enabled' => 1, | |
+ ), | |
+ 'register' => array( | |
+ 'enabled' => 1, | |
+ ), | |
+ ), | |
), | |
); | |
$endpoint->debug = 1; | |
- $endpoint->status = 1; | |
$endpoint->export_type = FALSE; | |
services_endpoint_save($endpoint); | |
$endpoint = services_endpoint_load($endpoint->name); | |
diff --git a/tests/unit/TestServicesModule.test b/tests/unit/TestServicesModule.test | |
index 1da24e2..9061dd7 100644 | |
--- a/tests/unit/TestServicesModule.test | |
+++ b/tests/unit/TestServicesModule.test | |
@@ -29,6 +29,7 @@ class ServicesModuleTests extends DrupalWebTestCase { | |
*/ | |
public function setUp() { | |
parent::setUp( | |
+ 'autoload', | |
'ctools', | |
'services', | |
'rest_server' | |
@@ -64,8 +65,8 @@ class ServicesModuleTests extends DrupalWebTestCase { | |
$message = t('services_perm should return an array') ; | |
$this->assertTrue(gettype($resultArray)=='array', $message) ; | |
- $message = t('There should be 6 permission types') ; | |
- $this->assertEqual(count($resultArray), 6, $message) ; | |
+ $message = t('There should be 7 permission types') ; | |
+ $this->assertEqual(count($resultArray), 7, $message) ; | |
$permission = 'administer services' ; | |
$this->helperPermExists($resultArray, $permission) ; | |
@@ -110,38 +111,43 @@ class ServicesModuleTests extends DrupalWebTestCase { | |
*/ | |
public function testEndpointMenu() { | |
// Create the endpoint. | |
- $endpointSettings = array( | |
- 'name' => 'mchnname', | |
- 'title' => $this->randomName(20), | |
- 'path' => $this->randomName(10), | |
+ $endpoint_settings = array( | |
+ 'name' => strtolower($this->randomName(10)), | |
+ 'path' => strtolower($this->randomName(10)), | |
'server' => 'rest_server', | |
'services_use_content_permissions' => TRUE, | |
); | |
- | |
- $this->drupalPost('admin/build/services/add', $endpointSettings, | |
+ $this->drupalPost('admin/build/services/add', $endpoint_settings, | |
t('Save')); | |
- cache_clear_all(); | |
- menu_rebuild(); | |
- $this->assertResponse('200', t('Failed to create endpoint.')); | |
+ $this->assertResponse('200', t('Endpoint created successfully.')); | |
+ | |
+ // Enable node resource index method. | |
+ $resource_settings = array( | |
+ 'node-index' => TRUE, | |
+ ); | |
+ $this->drupalPost('admin/build/services/list/' . $endpoint_settings['name'] . '/resources', | |
+ $resource_settings, t('Save')); | |
+ $this->assertResponse('200', t('Node resource index method enabled successfully.')); | |
// Check path. | |
- $this->drupalGet($endpointSettings['path']); | |
- $this->assertResponse('200', t('Failed to access endpoint menu path.')); | |
+ $this->drupalGet($endpoint_settings['path'] . '/node'); | |
+ $this->assertResponse('200', t('Accessed endpoint menu path node index method.')); | |
+ | |
+ // After accessing node resource we got logged out. So we login again. | |
+ $this->drupalLogin($this->privilegedUser); | |
// Check edit. | |
- $this->drupalGet('admin/build/services/' . $endpointSettings['name'] | |
- . '/edit'); | |
- $this->assertResponse('200', t('Failed to access endpoint edit path.')) ; | |
+ $this->drupalGet('admin/build/services/list/' . $endpoint_settings['name']); | |
+ $this->assertTitle(t('Edit endpoint !name | Drupal', array('!name' => $endpoint_settings['name'])), t('Accessed endpoint edit path.')); | |
// Check export. | |
- $this->drupalGet('admin/build/services/' . $endpointSettings['name'] | |
+ $this->drupalGet('admin/build/services/list/' . $endpoint_settings['name'] | |
. '/export'); | |
- $this->assertResponse('200', t('Failed to access endpoint export path.')) ; | |
+ $this->assertTitle(t('Export endpoint !name | Drupal', array('!name' => $endpoint_settings['name'])), t('Accessed endpoint export path.')); | |
// Check delete. | |
- $this->drupalGet('admin/build/services/' . $endpointSettings['name'] | |
- . '/delete'); | |
- $this->assertResponse('200', t('Failed to access endpoint delete path.')) ; | |
+ $this->drupalPost('admin/build/services/list/' . $endpoint_settings['name'], array(), t('Delete')); | |
+ $this->assertTitle(t('Are you sure you want to delete !name? | Drupal', array('!name' => $endpoint_settings['name'])), t('Can delete endpoint.')); | |
} | |
@@ -190,7 +196,6 @@ class ServicesModuleTests extends DrupalWebTestCase { | |
$string = 'New Service object should have property '; | |
$this->assertTrue(property_exists($results, 'eid'), t($string . 'eid.')); | |
$this->assertTrue(property_exists($results, 'name'), t($string . 'name.')); | |
- $this->assertTrue(property_exists($results, 'title'), t($string . 'title.')); | |
$this->assertTrue(property_exists($results, 'server'), t($string . 'server.')); | |
$this->assertTrue(property_exists($results, 'path' ), t($string . 'path.')); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment