/** * Default constructor. * * @param string $endpoint_name * Name of the endpoint. This is the path for the server, for e.g. "rest". */ public function __construct($endpoint_name) { module_load_include('inc', 'services', 'includes/services.runtime'); $this->endpoint_name = $endpoint_name; $endpoint = services_endpoint_load($this->endpoint_name); $server = $endpoint->server; services_set_server_info_from_array(array('module' => $server, 'endpoint' => $this->endpoint_name, 'endpoint_path' => $endpoint->path, 'debug' => $endpoint->debug, 'settings' => $endpoint->server_settings)); $this->endpoint_path = services_get_server_info('endpoint_path', 'services/rest'); $services_rest_server_factory = variable_get('services_rest_server_factory_class', 'ServicesRESTServerFactory'); $this->rest_server_factory = new $services_rest_server_factory(array('endpoint_path' => $this->endpoint_path)); }
/** * Acts on requests to the server defined in hook_server_info(). * * This is the main entry point to your server implementation. * Need to get some more description about the best way to implement * 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 }
/** * Retrieves information about the current user * * @param object $info ["data"] * * @return object * * @Access(callback='user_access', args={'access content'}, appendArgs=false) */ public static function roleUpdate($info) { $return = array('error' => TRUE); // Extract the info we need from post data $uid = $info->euid; $consumer = services_get_server_info('oauth_consumer'); // Get site ID $result = db_query("SELECT * FROM {conglomerate_source} WHERE oauth_consumer='%s' LIMIT 1", $consumer->key); if ($result !== FALSE) { $site = db_fetch_object($result); $sid = $site->nid; } if ($result !== FALSE && $info->roles != NULL) { // Delete old roles (if any) to avoid duplicates db_query("DELETE FROM {conglomerate_user_roles} WHERE uid=%d AND sid=%d", $uid, $sid); // If no roles were sent it means user is deleted. And also, the for loop won't be run. foreach ($info->roles as $role) { db_query("INSERT INTO {conglomerate_user_roles} VALUES(%d, '%s', %d)", $uid, $role, $sid); } $return = array('error' => FALSE); } return (object) $return; }
/** * Helper function that maps incoming data to the proper node attributes * * @param object $node * @param object $data * @return object */ private static function nodeWrite($node, $data) { $oauth_consumer = services_get_server_info('oauth_consumer'); $source = conglomerate_source_from_consumer($oauth_consumer); $attr = array('title' => array('required' => TRUE), 'text' => array('to' => 'body', 'required' => TRUE), 'position' => array('to' => 'simple_geo_position', 'required' => TRUE), 'tags' => array('required' => FALSE, 'adapt' => 'adaptTags'), 'metadata' => array('required' => FALSE, 'to' => 'conglomerate_metadata'), 'picture' => array('required' => FALSE, 'adapt' => 'adaptPicture'), 'large_picture' => array('required' => FALSE, 'adapt' => 'adaptLargePicture'), 'url' => array('required' => TRUE, 'adapt' => 'adaptUrl'), 'fulltext' => array('required' => FALSE, 'adapt' => 'adaptFulltext'), 'type' => array('required' => TRUE), 'language' => array('required' => TRUE), 'comments' => array('required' => FALSE)); switch ($data->type) { case 'event': $attr['starts'] = array('adapt' => 'adaptStartTime', 'required' => TRUE); break; case 'subpage': $attr['searchable'] = array('adapt' => 'adaptSearchable', 'required' => TRUE); break; } drupal_alter('conglomerate_node_write_attributes', $attr, $data, $source); // Transfer attributes from data foreach ($attr as $name => $info) { if (isset($data->{$name})) { $to = $name; if (!empty($info['to'])) { $to = $info['to']; } $node->{$to} = $data->{$name}; if (isset($info['adapt'])) { call_user_func('ConglomerateContentResource::' . $info['adapt'], $node); } } else { if ($info['required']) { return services_error("Missing attribute {$name}", 406); } } } // Add information about the conglomerate source $node->conglomerate_source = $source->sid; $the_language = $node->language; node_save($node); // Workaround to preserve language. TODO: Must find out what is happening to the language. if (!$node->language) { db_query("UPDATE {node} SET language='%s' WHERE nid = %d", array(':language' => $the_language, ':nid' => $node->nid)); } return (object) array('nid' => $node->nid, 'uri' => services_resource_uri(array('conglomerate-content', $node->nid)), 'url' => url('node/' . $node->nid, array('absolute' => TRUE))); }
public function handle() { //A method is required, no matter what if (empty($this->method_name)) { $this->error(JSONRPC_ERROR_REQUEST, t("The received JSON not a valid JSON-RPC Request")); } $endpoint = services_get_server_info('endpoint'); //Find the method $this->method = services_controller_get($this->method_name, $endpoint); $args = array(); if (!isset($this->method)) { // No method found is a fatal error $this->error(JSONRPC_ERROR_PROCEDURE_NOT_FOUND, t("Invalid method @method", array('@method' => $request))); } //If needed, check if parameters can be omitted $arg_count = count($this->method['args']); if (!isset($this->params)) { for ($i = 0; $i < $arg_count; $i++) { $arg = $this->method['#args'][$i]; if (!$arg['optional']) { if (empty($this->params)) { // We have required parameter, but we don't have any. if (is_array($this->params)) { // The request has probably been parsed correctly if params is an array, // just tell the client that we're missing parameters. $this->error(JSONRPC_ERROR_PARAMS, t("No parameters received, the method '@method' has required parameters.", array('@method' => $this->method_name))); } else { // If params isn't an array we probably have a syntax error in the json. // Tell the client that there was a error while parsing the json. // TODO: parse errors should be caught earlier $this->error(JSONRPC_ERROR_PARSE, t("No parameters received, the likely reason is malformed json, the method '@method' has required parameters.", array('@method' => $this->method_name))); } } } } } // Map parameters to arguments, the 1.1 draft is more generous than the 2.0 proposal when // it comes to parameter passing. 1.1-d allows mixed positional and named parameters while // 2.0-p forces the client to choose between the two. // // 2.0 proposal on parameters: http://groups.google.com/group/json-rpc/web/json-rpc-1-2-proposal#parameters-positional-and-named // 1.1 draft on parameters: http://json-rpc.org/wd/JSON-RPC-1-1-WD-20060807.html#NamedPositionalParameters if ($this->array_is_assoc($this->params)) { $this->args = array(); //Create a assoc array to look up indexes for parameter names $arg_dict = array(); for ($i = 0; $i < $arg_count; $i++) { $arg = $this->method['args'][$i]; $arg_dict[$arg['name']] = $i; } foreach ($this->params as $key => $value) { if ($this->major_version == 1 && preg_match('/^\\d+$/', $key)) { //A positional argument (only allowed in v1.1 calls) if ($key >= $arg_count) { //Index outside bounds $this->error(JSONRPC_ERROR_PARAMS, t("Positional parameter with a position outside the bounds (index: @index) received", array('@index' => $key))); } else { $this->args[intval($key)] = $value; } } else { //Associative key if (!isset($arg_dict[$key])) { //Unknown parameter $this->error(JSONRPC_ERROR_PARAMS, t("Unknown named parameter '@name' received", array('@name' => $key))); } else { $this->args[$arg_dict[$key]] = $value; } } } } else { //Non associative arrays can be mapped directly $param_count = count($this->params); if ($param_count > $arg_count) { $this->error(JSONRPC_ERROR_PARAMS, t("Too many arguments received, the method '@method' only takes '@num' argument(s)", array('@method' => $this->method_name, '@num' => $arg_count))); } $this->args = $this->params; } //Validate arguments for ($i = 0; $i < $arg_count; $i++) { $val = $this->args[$i]; $arg = $this->method['args'][$i]; if (isset($val)) { //If we have data if ($arg['type'] == 'struct' && is_array($val) && $this->array_is_assoc($val)) { $this->args[$i] = $val = (object) $val; } //Only array-type parameters accepts arrays if (is_array($val) && $arg['type'] != 'array' && !($this->is_assoc($val) && $arg['type'] == 'struct')) { $this->error_wrong_type($arg, 'array'); } else { if (($arg['type'] == 'int' || $arg['type'] == 'float') && !is_numeric($val)) { $this->error_wrong_type($arg, 'string'); } } } else { if (!$arg['optional']) { //Trigger error if a required parameter is missing $this->error(JSONRPC_ERROR_PARAMS, t("Argument '@name' is required but was not received", array('@name' => $arg['name']))); } } } // We are returning JSON, so tell the browser. drupal_set_header('Content-Type: application/json; charset=utf-8'); // Services assumes parameter positions to match the method callback's // function signature so we need to sort arguments by position (key) // before passing them to the method callback. The best solution here would // be to pad optional parameters using a #default key in the hook_service // method definitions instead of requiring all parameters to be present, as // we do now. // For reference: http://drupal.org/node/715044 ksort($this->args); //Call service method try { $result = services_controller_execute($this->method, $this->args); return $this->result($result); } catch (ServicesException $e) { $this->error(JSONRPC_ERROR_INTERNAL_ERROR, $e->getMessage(), $e->getData()); } catch (Exception $e) { $this->error(JSONRPC_ERROR_INTERNAL_ERROR, $e->getMessage()); } }