View source
<?php
define('SERVICES_CLIENT_DELMITER_ARRAY', '#>');
define('SERVICES_CLIENT_DELMITER_OBJECT', '#');
function services_client_menu() {
$items['admin/structure/services_client/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/structure/services_client/settings'] = array(
'title' => 'Settings',
'description' => 'Configure general client settings',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'services_client_settings',
),
'access arguments' => array(
'administer services client',
),
'file' => 'services_client.admin.inc',
'type' => MENU_LOCAL_TASK,
);
return $items;
}
function services_client_menu_alter(&$items) {
$items['admin/structure/services_client/connection']['type'] = MENU_LOCAL_TASK;
}
function services_client_ctools_plugin_api($owner, $api) {
if ($owner == 'services_client' && in_array($api, array(
'mapping',
'condition',
))) {
return array(
'version' => 1,
);
}
}
function services_client_ctools_plugin_directory($owner, $plugin_type) {
if ($owner == 'ctools' && $plugin_type == 'export_ui') {
return 'plugins/' . $plugin_type;
}
}
function services_client_get_existing_hooks($hook_id = NULL) {
ctools_include('export');
if ($hook_id) {
$hooks = ctools_export_crud_load('services_client_connection_hook', $hook_id);
}
else {
$hooks = ctools_export_crud_load_all('services_client_connection_hook');
}
$hooks_disabled = variable_get('default_services_client_connection_hook', array());
$conns_disabled = variable_get('default_services_client_connection', array());
foreach ($hooks as $hook_name => $hook_info) {
if (!empty($hooks_disabled[$hook_name])) {
unset($hooks[$hook_name]);
}
if (!empty($conns_disabled[$hook_info->conn_name])) {
unset($hooks[$hook_name]);
}
}
return $hooks;
}
function services_client_ctools_plugin_type() {
return array(
'condition' => array(
'cache' => FALSE,
'use hooks' => TRUE,
'classes' => array(
'handler',
),
),
'mapping' => array(
'cache' => FALSE,
'use hooks' => TRUE,
'classes' => array(
'handler',
),
),
);
}
function services_client_get_plugins($type, $select_box = FALSE, $supports_key = NULL) {
$output = array();
$plugins = ctools_get_plugins('services_client', $type);
drupal_alter('services_client_plugins', $plugins, $type);
$output = $plugins;
if ($select_box) {
$options = array();
foreach ($output as $key => $plugin) {
if (isset($supports_key) && !in_array($supports_key, $plugin['supports'])) {
continue;
}
$options[$key] = $plugin['name'];
}
$output = $options;
}
return $output;
}
function services_client_get_plugin($type, $name, $item, $config) {
$class = ctools_plugin_load_class('services_client', $type, $name, 'handler');
if ($class) {
return new $class($item, $config);
}
else {
throw new ServicesClientException(t('Missing class @name', array(
'@name' => $name,
)));
}
}
function services_client_services_client_mapping() {
module_load_include('inc', 'services_client', 'include/plugin_definition');
return _services_client_mapping();
}
function services_client_services_client_condition() {
module_load_include('inc', 'services_client', 'include/plugin_definition');
return _services_client_condition();
}
function services_client_theme() {
return array(
'services_client_admin_list' => array(
'render element' => 'element',
'file' => 'services_client.theme.inc',
),
);
}
function services_client_cron_queue_info() {
if (variable_get('services_client_use_queue', FALSE) && variable_get('services_client_process_queue_cron', FALSE)) {
$queues['services_client_sync'] = array(
'worker callback' => 'services_client_queue_sync',
'time' => 120,
);
return $queues;
}
else {
return array();
}
}
function services_client_permission() {
return array(
'administer services client' => array(
'title' => t('Administer the services client'),
'description' => t('Manage services client connections, hooks and mappings'),
),
'make services client requests' => array(
'title' => t('Perform services client requests'),
'description' => t('Grant permission to actually execute the services hooks'),
),
);
}
function services_client_services_connection_hook_exists($name, $element) {
$conn_name = $element['#machine_name']['conn_name'];
$query = 'SELECT 1 FROM {services_client_connection_hook} WHERE conn_name = :conn_name AND name = :name';
$result = db_query_range($query, 0, 1, array(
'conn_name' => $conn_name,
'name' => $name,
))
->fetchField();
return $result;
}
function services_client_get_client_hooks_list($params = array()) {
$ret = array();
if (!empty($params['hid']) && is_numeric($params['hid'])) {
$ret = db_query("SELECT * FROM {services_client_connection_hook} WHERE hid = ?", array(
$params['hid'],
));
}
else {
if (!empty($params['name']) && $params['name']) {
$ret = db_query("SELECT * FROM {services_client_connection_hook} WHERE name = ?", array(
$params['name'],
));
}
else {
if (!empty($params['conn_name']) && $params['conn_name']) {
$ret = db_query("SELECT * FROM {services_client_connection_hook} WHERE conn_name = ?", array(
$params['conn_name'],
));
}
else {
$ret = db_query("SELECT * FROM {services_client_connection_hook} WHERE 1");
}
}
}
if (!empty($ret)) {
if (!empty($params['num']) && $params['num'] == 'all') {
return $ret
->fetchAll();
}
else {
return $ret
->fetchAssoc();
}
}
else {
return FALSE;
}
}
function services_client_hook_load($hid) {
return services_client_get_client_hooks_list(array(
'hid' => $hid,
));
}
function services_client_node_insert($node) {
if (module_exists('reference_uuid')) {
reference_uuid_entity_load(array(
$node,
), 'node');
}
services_client_data_process($node, 'node_save');
}
function services_client_node_update($node) {
if (module_exists('reference_uuid')) {
reference_uuid_entity_load(array(
$node,
), 'node');
}
services_client_data_process($node, 'node_save');
}
function services_client_node_delete($node) {
$node->_services_client = array(
'origin' => $_SERVER['SERVER_NAME'],
);
services_client_data_process($node, 'node_delete');
}
function services_client_user_insert(&$edit, $account, $category) {
if (!empty($edit['_services_client_skip'])) {
unset($edit['_services_client_skip']);
return;
}
if (isset($edit['_services_client'])) {
$account->_services_client = $edit['_services_client'];
}
services_client_data_process($account, 'user_save');
}
function services_client_user_update(&$edit, $account, $category) {
if (!empty($edit['_services_client_skip'])) {
unset($edit['_services_client_skip']);
return;
}
if (isset($edit['_services_client'])) {
$account->_services_client = $edit['_services_client'];
}
services_client_data_process($account, 'user_save');
}
function services_client_webform_submission_insert($node, $submission) {
services_client_data_process($submission, 'webform_submission_save');
}
function services_client_data_process($src_data, $type, $name = NULL) {
ctools_include('export');
if (services_client_sync_exclude($src_data, services_client_get_data_type($type))) {
return TRUE;
}
if (services_client_queue_data($src_data, $type)) {
return;
}
$data_type = services_client_get_data_type($type);
drupal_alter('services_client_data', $src_data, $data_type);
$src_data->_services_client = isset($src_data->_services_client) ? $src_data->_services_client : array();
$src_data->_services_client['origin'] = services_client_get_id();
$src_data->_services_client['visted'] = isset($src_data->_services_client['visted']) ? $src_data->_services_client['visted'] : array();
unset($src_data->_services_client['queued']);
if (!in_array(services_client_get_id(), $src_data->_services_client['visted'])) {
$src_data->_services_client['visted'][] = services_client_get_id();
}
else {
watchdog('sc', "Possible loop in system. Type: @type, data: <pre>@data</pre>", array(
'@type' => $type,
'@data' => print_r($src_data, TRUE),
));
return FALSE;
}
$tasks = array();
$conns = array();
$hooks = array();
$hooks_all = array();
$hooks_all = services_client_get_existing_hooks();
if (!empty($name)) {
if (!empty($hooks_all[$name])) {
$hooks[] = array(
$hooks_all[$name],
);
}
}
else {
foreach ($hooks_all as $hook) {
if (isset($hook->hook) && $hook->hook == $type) {
$hooks[] = $hook;
}
}
}
foreach ($hooks as $hook) {
$conds = $hook->config['condition']['config'];
$mappings = $hook->config['mapping']['config'];
if (in_array($type, array(
'node_save',
'node_delete',
)) && count($conds) > 0) {
if ($conds['published'] != 'e' && $conds['published'] != $src_data->status) {
continue;
}
if ($conds['node_type'] != $src_data->type) {
continue;
}
}
$connection = services_client_connection_load($hook->conn_name);
if (isset($connection->services_client_id) && in_array($connection->services_client_id, $src_data->_services_client['visted'])) {
watchdog('sc', 'Loop prevention. Not sending type: @type to connection @conn. data: <pre>@data</pre>', array(
'@type' => $type,
'@data' => print_r($src_data, TRUE),
'@conn' => $connection->name,
));
continue;
}
$tasks[$hook->conn_name][] = $hook;
}
$results = array(
'success' => array(),
'errors' => array(),
);
if (count($tasks) > 0) {
foreach ($tasks as $hook_conn) {
foreach ($hook_conn as $task) {
$success = TRUE;
$result = array(
'data' => $src_data,
'type' => services_client_get_data_type($type),
'hook' => $type,
'task' => $task,
'entity_type' => services_client_get_data_type($type),
'entity_id' => services_client_get_data_id($src_data, $type),
);
module_invoke_all('sc_process_data', $result['entity_type'], $src_data, $task);
try {
services_client_make_call($src_data, $type, $task);
} catch (ServicesClientConnectionResponseException $e) {
$e
->log();
$success = FALSE;
$result += array(
'code' => $e
->getErrorCode(),
'message' => $e
->getErrorMessage(),
);
}
$results[$success ? 'success' : 'errors'][] = $result;
}
}
}
if (count($results['errors'])) {
services_client_process_errors($results['errors']);
}
return $results;
}
function services_client_scalar_result($result) {
if (is_array($result) && count($result) == 1 && isset($result[0])) {
return $result[0];
}
return $result;
}
function services_client_get_data_type($event) {
static $types = array(
'node_save' => 'node',
'node_delete' => 'node',
'user_save' => 'user',
);
return isset($types[$event]) ? $types[$event] : NULL;
}
function services_client_get_data_id($src_data, $event) {
$type = services_client_get_data_type($event);
if (entity_get_info($type)) {
$id = reset(entity_extract_ids($type, $src_data));
return $id;
}
return NULL;
}
function services_client_make_call($src_data, $type, $task) {
switch ($type) {
case 'node_save':
services_client_make_node_call($src_data, $task);
break;
case 'node_delete':
services_client_make_node_delete_call($src_data, $task);
break;
case 'user_save':
services_client_make_user_call($src_data, $task);
break;
case 'webform_submission_save':
$conds = unserialize($hook->hook_conditions);
if ($src_data->nid == $conds['webform_nid']) {
services_client_make_submission_to_node_call($src_data, $task);
}
break;
}
}
function services_client_make_user_call($user, $task) {
if (services_client_user_exclude($user)) {
return TRUE;
}
$client = services_client_connection_get($task->conn_name);
$user_data = new stdClass();
$conds = $task->config['condition']['config'];
$mapping = $task->config['mapping']['config'];
$fields = explode("\n", $mapping['field_mapping']);
$fields_empty = services_client_process_mapping_prepare_empty(isset($mapping['field_mapping_empty']) ? $mapping['field_mapping_empty'] : array());
$user_data = services_client_process_mapping($user, $fields, $fields_empty);
if (!empty($user->uuid)) {
$user_data->uuid = $user->uuid;
}
if (isset($user->_services_client)) {
$user_data->_services_client = $user->_services_client;
}
services_client_process_roles_mapping($user, $user_data, $mapping['user_sync_roles'], $client, $task->conn_name);
$data = (array) $user_data;
watchdog('sc_user', 'Sending user to %conn: <pre>@user</pre>', array(
'%conn' => $task->conn_name,
'@user' => print_r($data, TRUE),
));
$remote_name = isset($user->original->name) ? $user->original->name : $user->name;
if (variable_get('services_client_user_sync_byname', TRUE) && $remote_name) {
$result = $client
->index('user', 'uid,name', array(
'name' => $remote_name,
));
if (!empty($result)) {
$uid = $result[0]['uid'];
}
}
else {
$uid = services_client_scalar_result($client
->get('uuid', 'user', array(
'uuid' => $user->uuid,
)));
}
if (!empty($uid)) {
watchdog('sc_user', 'Got @uid for uuid @uuid', array(
'@uid' => $uid,
'@uuid' => $user->uuid,
));
$client
->update('user_raw', $uid, $data);
}
else {
$client
->create('user_raw', $data);
}
}
function services_client_make_node_call($node, $task) {
$client = services_client_connection_get($task->conn_name);
$conds = $task->config['condition']['config'];
$mapping = $task->config['mapping']['config'];
$fields = explode("\n", $mapping['field_mapping']);
$fields_empty = services_client_process_mapping_prepare_empty(isset($mapping['field_mapping_empty']) ? $mapping['field_mapping_empty'] : array());
$types = explode("\n", $mapping['node_type_mapping']);
$node_data = services_client_process_mapping($node, $fields, $fields_empty);
if (count($types) > 0) {
$node_data->type = services_client_type_override($types, $node->type);
}
$node_data->uuid = $node->uuid;
$account = user_load($node->uid);
$uid = services_client_scalar_result($client
->get('uuid', 'user', array(
'uuid' => $account->uuid,
)));
if (is_numeric($uid) && !empty($uid)) {
$node_data->uid = $uid;
}
if (isset($node->_services_client)) {
$node_data->_services_client = $node->_services_client;
}
$data = (array) $node_data;
watchdog('sc_node', 'Sending node to %conn: <pre>@node</pre>', array(
'%conn' => $task->conn_name,
'@node' => print_r($data, TRUE),
));
$nid = services_client_scalar_result($client
->get('uuid', 'node', array(
'uuid' => $node->uuid,
)));
if (is_numeric($nid) && !empty($nid)) {
watchdog('sc_node', 'Got @nid for uuid @uuid', array(
'@nid' => $nid,
'@uuid' => $node->uuid,
));
$client
->update('node_raw', $nid, $data);
}
else {
$client
->create('node_raw', $data);
}
}
function services_client_make_node_delete_call($node, $task) {
$client = services_client_connection_get($task->conn_name);
watchdog('sc_node', 'Deleting node @title - @nid from %conn', array(
'%conn' => $task->conn_name,
'@title' => $node->title,
'@nid' => $node->nid,
));
$nid = services_client_scalar_result($client
->get('uuid', 'node', array(
'uuid' => $node->uuid,
)));
if ($nid) {
$client
->delete('node', $nid);
watchdog('sc_node', 'Node @nid was deleted from remote site @name', array(
'@nid' => $node->nid,
'@name' => $task->conn_name,
));
}
else {
watchdog('sc_node', 'Node @nid was not found on remote site @name', array(
'@nid' => $node->nid,
'@name' => $task->conn_name,
));
}
}
function services_client_make_submission_to_node_call($submission, $task) {
$client = services_client_connection_get($task->conn_name);
$conds = $task->config['condition']['config'];
$mapping = $task->config['mapping']['config'];
$fields = explode("\n", $mapping['field_mapping']);
$types = explode("\n", $mapping['node_type_mapping']);
$fields_empty = services_client_process_mapping_prepare_empty(isset($mapping['field_mapping_empty']) ? $mapping['field_mapping_empty'] : '');
$file_field_name = $mapping['file_field_name'];
$submission_data = services_client_process_mapping($submission, $fields, $fields_empty);
if (count($types) > 0) {
$submission_data->type = services_client_type_override($types, 'webform');
}
$submission_data->revision = '';
$submission_data->language = LANGUAGE_NONE;
if (isset($submission->_services_client)) {
$submission_data->_services_client = $submission->_services_client;
}
$data = (array) $submission_data;
watchdog('sc_node', 'Sending node to %conn: <pre>@node</pre>', array(
'%conn' => $task->conn_name,
'@node' => print_r($data, TRUE),
));
$response = $client
->create('node_raw', $data);
$response_nid = $response['nid'];
if (count($submission->file_usage['added_fids'])) {
$file = file_load($submission->file_usage['added_fids'][0]);
$file_data = array(
'file' => base64_encode(file_get_contents($file->uri)),
'filename' => $file->filename,
);
$response = $client
->create('file', $file_data);
$data = array(
'field_name' => $file_field_name,
'fid' => $response['fid'],
);
$client
->targetAction('node_raw', $response_nid, 'attach_file', $data);
}
}
function services_client_process_mapping($src_data, $fields, $fields_empty = array()) {
$data_obj = new stdClass();
foreach ($fields as $field) {
$field = trim($field);
if (!empty($field)) {
list($server_name, $client_name) = explode('|', $field);
$data = NULL;
try {
if ($client_name) {
$data = services_client_raw_data_get($client_name, $src_data);
}
if ($server_name && (!empty($data) || $data === 0 || $data === "0" || $data === 0.0)) {
services_client_raw_data_set($server_name, $data_obj, $data);
}
} catch (ServicesClientDataNotFoundException $e) {
if (isset($fields_empty[$client_name])) {
$server_name = isset($fields_empty[$client_name]['destination']) ? $fields_empty[$client_name]['destination'] : $server_name;
services_client_raw_data_set($server_name, $data_obj, $fields_empty[$client_name]['value']);
}
}
}
}
return $data_obj;
}
function services_client_process_mapping_prepare_empty($empty) {
$output = array();
$rows = is_array($empty) ? $empty : explode("\n", trim($empty));
foreach ($rows as $row) {
$row = trim($row);
if (!empty($row)) {
$data = explode("|", $row);
$output[$data[0]] = array(
'destination' => isset($data[2]) ? $data[1] : NULL,
'value' => isset($data[2]) ? $data[2] : $data[1],
);
if ($output[$data[0]]['value'] == "NULL") {
$output[$data[0]]['value'] = NULL;
}
if ($output[$data[0]]['value'] == "ARRAY") {
$output[$data[0]]['value'] = array();
}
}
}
return $output;
}
function services_client_raw_data_get($path, $source) {
$parts = explode('#', trim($path));
$data = $source;
foreach ($parts as $part) {
if (substr($part, 0, 1) == '>') {
$name = substr($part, 1);
if (isset($data[$name])) {
$data = $data[$name];
}
else {
throw new ServicesClientDataNotFoundException();
}
}
else {
$name = $part;
if (isset($data->{$name})) {
$data = $data->{$name};
}
else {
throw new ServicesClientDataNotFoundException();
}
}
}
return $data;
}
function services_client_raw_data_set($path, &$destination, $set_data) {
$parts = explode('#', trim($path));
$data =& $destination;
foreach ($parts as $part) {
if (substr($part, 0, 1) == '>') {
$name = substr($part, 1);
if (!isset($data[$name])) {
$data[$name] = NULL;
}
$data =& $data[$name];
}
else {
$name = $part;
if (!isset($data->{$name})) {
$data->{$name} = NULL;
}
$data =& $data->{$name};
}
}
$data = $set_data;
}
function services_client_type_override($types, $node_type) {
$assigned_type = $node_type;
foreach ($types as $type) {
$mpair = explode('|', $type);
$mname = trim($mpair[0]);
$cname = trim($mpair[1]);
if ($node_type == $cname) {
$assigned_type = $mname;
break;
}
}
return $assigned_type;
}
function services_client_queue_data($src_data, $type) {
if (isset($src_data->_services_client)) {
if (variable_get('services_client_use_queue', TRUE) && empty($src_data->_services_client['queued']) && empty($src_data->_services_client['bypass_queue'])) {
$src_data->_services_client['queued'] = TRUE;
$queue = DrupalQueue::get('services_client_sync', TRUE);
return $queue
->createItem(array(
'src_data' => $src_data,
'type' => $type,
));
}
}
return FALSE;
}
function services_client_queue_sync($data) {
services_client_data_process($data['src_data'], $data['type']);
}
function services_client_process_roles_mapping($account, &$data, $mapping, &$client, $conn_name) {
$lines = explode("\n", trim($mapping));
$roles = array();
foreach ($lines as $line) {
if (!empty($line)) {
list($local, $remote) = explode("|", $line);
$roles[trim($local)] = trim($remote);
}
}
if (count($roles)) {
if (count(array_intersect($account->roles, array_keys($roles)))) {
$remote_roles = services_client_get_remote_roles($conn_name, $client);
$data->roles = isset($data->roles) ? $data->roles : array();
foreach ($roles as $local_role => $remote_role) {
if (in_array($local_role, $account->roles) && ($key = array_search($remote_role, $remote_roles))) {
$data->roles[$key] = $remote_role;
}
}
}
}
}
function services_client_get_remote_roles($conn, $client) {
$cid = 'services_client:remote_roles:' . $conn;
$roles = array();
if ($cache = cache_get($cid)) {
$roles = $cache->data;
}
else {
$roles = $client
->action('user', 'list_roles');
cache_set($cid, $roles, 'cache', time() + 60 * 60);
}
return $roles;
}
function services_client_user_exclude($account) {
$exclude = explode(',', trim(variable_get('services_client_exclude_users', '1')));
return in_array($account->uid, $exclude);
}
function services_client_process_errors($errors = array()) {
module_invoke_all('sc_process_errors', $errors);
}
function services_client_form_alter(&$form, $form_state, $form_id) {
if (($form_id == 'user_register_form' || $form_id == 'user_profile_form') && user_access('administer users')) {
$form['_services_client_skip'] = array(
'#type' => 'checkbox',
'#title' => t("Don't send update by services client"),
);
}
if ($form_id == 'ctools_export_ui_edit_item_form') {
if ($form_state['plugin']['schema'] == 'services_client_connection') {
$form['services_client_id'] = array(
'#type' => 'textfield',
'#title' => t('Remote client ID'),
'#default_value' => isset($form_state['item']->services_client_id) ? $form_state['item']->services_client_id : NULL,
'#description' => t('Enter ID of services client on remote endpoint.'),
'#size' => 50,
);
$form['#submit'][] = 'services_client_form_submit_connection';
}
}
}
function services_client_get_id() {
return variable_get('services_client_id', drupal_get_token('services_client'));
}
function services_client_form_submit_connection($form, &$form_state) {
if (isset($form_state['values']['services_client_id']) && !empty($form_state['values']['services_client_id'])) {
$form_state['item']->services_client_id = $form_state['values']['services_client_id'];
}
}
function services_client_services_client_connection_save($connection) {
if (!empty($connection->services_client_id)) {
db_merge('services_client_connection_id')
->key(array(
'name' => $connection->name,
))
->fields(array(
'services_client_id' => $connection->services_client_id,
))
->execute();
}
}
function services_client_services_client_connection_load($connection) {
if ($id = services_client_get_connection_id($connection->name)) {
$connection->services_client_id = $id;
}
}
function services_client_get_connection_id($name) {
$cache =& drupal_static(__FUNCTION__);
if (!isset($cache[$name])) {
$sql = "SELECT services_client_id FROM {services_client_connection_id} WHERE name = :name";
$cache[$name] = db_query($sql, array(
':name' => $name,
))
->fetchField();
}
return isset($cache[$name]) ? $cache[$name] : NULL;
}
function services_client_sync_exclude($object, $type) {
$hook = 'services_client_data_exclude';
foreach (module_implements($hook) as $module) {
$func = $module . '_' . $hook;
$result = $func($object, $type);
if ($result === TRUE) {
return TRUE;
}
}
}