Skip to content

Instantly share code, notes, and snippets.

@arturo-c
Created November 28, 2011 21:28
Show Gist options
  • Save arturo-c/1402157 to your computer and use it in GitHub Desktop.
Save arturo-c/1402157 to your computer and use it in GitHub Desktop.
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