diff --git a/includes/admin.form.inc b/includes/admin.form.inc index 05b4532..19bfb03 100644 --- a/includes/admin.form.inc +++ b/includes/admin.form.inc @@ -82,6 +82,44 @@ function islandora_pathauto_admin_settings(array $form, array &$form_state) { '#default_value' => variable_get('islandora_pathauto_objects_query_solr', '*:*'), ); + $form['multi_server'] = array( + '#type' => 'fieldset', + '#title' => t('Multiple server configuration'), + '#description' => t('When using multiple servers, one might want to trigger the alias generation on a different server when an object is ingested.'), + ); + $triggertype = variable_get('islandora_pathauto_trigger_type', NULL); + $triggerurl = variable_get('islandora_pathauto_trigger_url', NULL); + $triggersecret = variable_get('islandora_pathauto_trigger_secret', NULL); + $form['multi_server']['trigger_type'] = array( + '#type' => 'select', + '#title' => t('Trigger type'), + '#options' => array( + 0 => t('Disabled'), + 'receiving' => t('Trigger receiving server'), + 'sending' => t('Trigger sending server'), + ), + '#default_value' => $triggertype, + '#description' => t("Only enable for multi server configurations. For example: set the ingest server to 'trigger sending server' and the front-end server to 'trigger receiving server'.\nThe 'trigger sending server' will use the 'trigger URL' to send a trigger when an alias is created. The 'trigger receiving server' receives this trigger and creates an alias for the same object.\nDo not set both servers to 'trigger sending server' because this will create an endless loop."), + ); + $form['multi_server']['trigger_url'] = array( + '#type' => 'textfield', + '#title' => t('Trigger URL'), + '#description' => t('The base URL of the server to send a trigger to.'), + '#states' => array( + 'visible' => array(':input[name=trigger_type]' => array('value' => 'sending')), + ), + '#default_value' => $triggerurl, + ); + $form['multi_server']['trigger_secret'] = array( + '#type' => 'textfield', + '#title' => t('Trigger secret'), + '#description' => t('A secret that is shared between the sending and receiving server. Use the same secret for both sending and receiving servers.'), + '#states' => array( + 'visible' => array(':input[name=trigger_type]' => array(array('value' => 'sending'), array('value' => 'receiving'))), + ), + '#default_value' => $triggersecret, + ); + $form['use_cron'] = array( '#type' => 'checkbox', '#title' => t('Use cron to automatically add / update aliases.'), @@ -100,6 +138,38 @@ function islandora_pathauto_admin_settings(array $form, array &$form_state) { return $form; } +/** + * Validate handler for admin form. + */ +function islandora_pathauto_admin_settings_validate($form, &$form_state) { + $triggertype = $form_state['values']['trigger_type']; + if ($triggertype === 'receiving') { + if (empty($form_state['values']['trigger_secret'])) { + form_set_error('trigger_secret', t("Fill out a secret shared with the sending server.")); + } + } + if ($triggertype === 'sending') { + if (empty($form_state['values']['trigger_secret'])) { + form_set_error('trigger_secret', t("Fill out a secret shared with the receiving server.")); + } + if (empty($form_state['values']['trigger_url'])) { + form_set_error('trigger_url', t("Fill out the URL of the receiving server.")); + } + else { + $response = islandora_pathauto_trigger_creation_on_server(NULL, 'insert', $form_state['values']['trigger_url'], $form_state['values']['trigger_secret']); + if ($response === NULL) { + form_set_error('trigger_url', t("The URL provided is not a valid receiving server. Make sure the URL contains only protocol and domain.")); + } + elseif (isset($response['error'])) { + form_set_error('trigger_url', t("The URL provided gave the following error: %error", array('%error' => $response['error']))); + } + elseif (isset($response['success']) && !$response['success']) { + form_set_error('trigger_url', t("The URL provided is not valid and something unexpected happened")); + } + } + } +} + /** * Submit handler for admin form. */ @@ -109,6 +179,9 @@ function islandora_pathauto_admin_settings_submit($form, &$form_state) { variable_set('islandora_pathauto_use_cron', $form_state['values']['use_cron']); variable_set('islandora_pathauto_objects_query_method', $form_state['values']['objects_query_method']); variable_set('islandora_pathauto_objects_query_solr', $form_state['values']['objects_query_solr']); + variable_set('islandora_pathauto_trigger_type', $form_state['values']['trigger_type']); + variable_set('islandora_pathauto_trigger_url', $form_state['values']['trigger_url']); + variable_set('islandora_pathauto_trigger_secret', $form_state['values']['trigger_secret']); } /** diff --git a/islandora_pathauto.install b/islandora_pathauto.install index 140a5a5..40bc300 100644 --- a/islandora_pathauto.install +++ b/islandora_pathauto.install @@ -16,6 +16,9 @@ function islandora_pathauto_uninstall() { 'islandora_pathauto_selected_cmodels', 'islandora_pathauto_objects_query_method', 'islandora_pathauto_objects_query_solr', + 'islandora_pathauto_trigger_type', + 'islandora_pathauto_trigger_url', + 'islandora_pathauto_trigger_secret', ); array_walk($variables, 'variable_del'); } diff --git a/islandora_pathauto.module b/islandora_pathauto.module index 838db73..fbdd589 100644 --- a/islandora_pathauto.module +++ b/islandora_pathauto.module @@ -23,6 +23,12 @@ function islandora_pathauto_menu() { 'file' => 'includes/admin.form.inc', 'type' => MENU_NORMAL_ITEM, ); + $items['admin/islandora/tools/islandora-pathauto/trigger/%/%'] = array( + 'page callback' => 'islandora_pathauto_triggered_create_alias', + 'page arguments' => array(5, 6), + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); return $items; } @@ -96,6 +102,7 @@ function islandora_pathauto_pathauto($op) { * may be >1 if the object has multiple content models. */ function islandora_pathauto_create_alias($object, $op) { + module_load_include('inc', 'islandora', 'includes/utilities'); module_load_include('inc', 'pathauto'); $count = 0; if (islandora_namespace_accessible($object->id)) { @@ -114,6 +121,14 @@ function islandora_pathauto_create_alias($object, $op) { } } } + if (variable_get('islandora_pathauto_trigger_type', '') === 'sending' && in_array($op, array('insert', 'update'), TRUE)) { + $response = islandora_pathauto_trigger_creation_on_server($object, $op); + if ($response === NULL || isset($response['error'])) { + watchdog('islandora_pathauto', 'Unable to trigger creation of alias on remote server: @error', array( + '@error' => isset($response['error']) ? $response['error'] : 'Cannot read response', + ), WATCHDOG_ERROR); + } + } return $count; } @@ -593,3 +608,117 @@ function islandora_pathauto_cron() { function islandora_pathauto_pathauto_bulkupdate(&$context) { return islandora_pathauto_bulk_batch_process(FALSE, $context); } + +/** + * Fire a trigger to create aliases on other servers. + */ +function islandora_pathauto_trigger_creation_on_server($object, $op, $url = NULL, $secret = NULL) { + if ($url === NULL) { + $url = variable_get('islandora_pathauto_trigger_url', NULL); + } + if ($secret === NULL) { + $secret = variable_get('islandora_pathauto_trigger_secret', NULL); + } + if (isset($url, $secret)) { + $identifier = isset($object) ? $object->id : 'testing'; + $time = time(); + $url = rtrim($url, '/') . '/admin/islandora/tools/islandora-pathauto/trigger/' . $identifier . '/' . $op; + $hash = drupal_hash_base64("$secret $identifier $op $time"); + $options = array( + 'method' => 'POST', + 'data' => array('hash' => $hash, 'time' => $time), + ); + $result = drupal_http_request($url, $options); + + if (isset($result->data, $result->code) && json_decode($result->data) !== NULL) { + return json_decode($result->data, TRUE); + } + else { + return array('success' => FALSE, 'error' => 'Cannot decode data'); + } + } +} + +/** + * Handle a received trigger from an other server. + */ +function islandora_pathauto_triggered_create_alias($objectid, $op) { + $input = file_get_contents("php://input"); + parse_str($input, $vars); + $istest = ($objectid === 'testing'); + $issuccess = TRUE; + $error = NULL; + $message = NULL; + $status = 200; + + if (isset($vars['hash'], $vars['time'])) { + $receivedhash = $vars['hash']; + $receivedtime = $vars['time']; + $secret = variable_get('islandora_pathauto_trigger_secret', NULL); + + $hash = drupal_hash_base64("$secret $objectid $op $receivedtime"); + if ($hash !== $receivedhash) { + $issuccess = FALSE; + $error = 'Authentication failed'; + $status = 401; + } + } + else { + $issuccess = FALSE; + $error = 'Authentication required'; + $status = 401; + } + + if ($issuccess && ($op !== 'insert' && $op !== 'update' && $op !== 'bulkupdate')) { + $issuccess = FALSE; + $error = 'Forbidden'; + $status = 403; + } + if ($issuccess && variable_get('islandora_pathauto_trigger_type', '') !== 'receiving') { + $issuccess = FALSE; + $error = 'Forbidden'; + $status = 403; + } + if ($issuccess && !$istest) { + $object = islandora_object_load($objectid); + if ($object) { + $result = islandora_pathauto_create_alias($object, $op); + if ($result == 0) { + $orig_path = "islandora/object/$objectid"; + $alias = drupal_get_path_alias($orig_path); + if ($alias == $orig_path) { + $issuccess = FALSE; + $error = t('Could not !op alias for object !id.', array('!op' => $op, '!id' => $objectid)); + $status = 400; + } + } + elseif ($result > 0) { + if ($op === 'insert') { + $message = t('Created new alias %alias for object %id.', array('%id' => $objectid, '%alias' => $result)); + } + else { + $message = t('Updated alias %alias for object %id.', array('%id' => $objectid, '%alias' => $result)); + } + } + } + else { + $issuccess = FALSE; + $error = t('Object "!id" not found', array('!id' => $objectid)); + $status = 404; + } + } + $data = array('success' => $issuccess, 'test' => $istest); + if (isset($error)) { + $data['error'] = $error; + $data['success'] = FALSE; + } + elseif (isset($message)) { + $data['message'] = $message; + } + drupal_add_http_header('Status', $status); + drupal_add_http_header('Content-Type', 'application/json; utf-8'); + if ($data !== NULL) { + print drupal_json_encode($data); + } + exit(); +}