diff --git a/README.txt b/README.txt index 25a0215..1d34279 100644 --- a/README.txt +++ b/README.txt @@ -4,11 +4,14 @@ This module expects an existing subscription with eloqua.com. If you do not have a subscription with eloqua.com at this point, please go to eloqua.com and review purchasing options if you wish to continue using this module. -This module will allow you to create webforms and link them to your existing -Eloqua forms. At present, this module requires you to still set up the form -in both Eloqua (using their form builder) and in Drupal (using webform). This -may change at a later date, but will at least provide a more Drupal-esque way -to create forms for use with Eloqua. +The Eloqua module will enable visitor tracking. For webform integration you will +want to enable the Eloqua webform module. + +The Eloqua webform module will allow you to create webforms and link them to +your existing Eloqua forms. At present, this module requires you to still set +up the form in both Eloqua (using their form builder) and in Drupal +(using webform). This may change at a later date, but will at least provide a +more Drupal-esque way to create forms for use with Eloqua. Requirements: ------------- diff --git a/eloqua.admin.inc b/eloqua.admin.inc index d949348..3c9697f 100644 --- a/eloqua.admin.inc +++ b/eloqua.admin.inc @@ -1,6 +1,7 @@ 'fieldset', '#title' => t('General Settings'), '#description' => t('General settings applicable to all Eloqua functionality.'), - // Site id. - ELOQUA_VARIABLE_SITE_ID => array( + 'eloqua_site_id' => array( '#type' => 'textfield', '#title' => t('Site Identifier'), '#description' => t('The Eloqua Site ID for this web site.'), '#size' => 20, '#maxlength' => 64, - '#default_value' => eloqua_get_site_id(), + '#default_value' => variable_get('eloqua_site_id', 0), '#required' => TRUE, ), - ELOQUA_SETTINGS_BATCH_SIZE => array( + 'batch_size' => array( '#type' => 'textfield', '#title' => t('Batch Size'), '#description' => t('The number of posts to process per cron run. Default=50'), '#size' => 5, '#maxlength' => 5, - '#default_value' => eloqua_get_batch_size(), + '#default_value' => (int) variable_get('batch_size', 50), ) ); return system_settings_form($form); diff --git a/eloqua.cron.inc b/eloqua.cron.inc deleted file mode 100644 index e7efb79..0000000 --- a/eloqua.cron.inc +++ /dev/null @@ -1,305 +0,0 @@ -data)) { - $post->data = array(); - } - - // Only pass along the accept-language header as a header. We pass the user - // agent information in the curl request below. - $original_headers = $post->data['user_headers']; - $headers = array( - 'Accept-Language' => array_key_exists('accept-language', $original_headers) ? $original_headers['accept-language'] : 'en', - ); - - // Fetch the post fields to send to Eloqua - $post_fields = _eloqua_cron_get_post_fields($post); - - // Create the Curl Request - $options = array(); - $options['post_fields'] = $post_fields; - $options['user_agent'] = array_key_exists('user-agent', $original_headers) ? $original_headers['user-agent'] : 'User Relay'; - $options['http_headers'] = $headers; - $ch = _eloqua_cron_get_curl_resource($options); - if (is_null($ch)) { - $message = t('Something went wrong with curl, unable to obtain handle. Aborting status updates.'); - $params = array(); - watchdog('eloqua', $message, $params, WATCHDOG_ERROR); - break; - } - try { - $data = curl_exec($ch); - $curl_status = curl_getinfo($ch); - $status_class = (int) floor($curl_status['http_code'] / 100); - $success = ($data) && ($status_class == 2 || $status_class == 3); - } catch (Exception $e) { - // PHP needs to implement finally - // No idea what went wrong, should just bail out of the process. - // However, log. - $message = t('Something went wrong with curl. Uncaught exception: !message'); - $params = array( - '!message' => $e->getMessage(), - ); - watchdog('eloqua', $message, $params, WATCHDOG_ERROR); - - // Throwing an error here will result in an uncaught exception. This, when it happens in - // cron, will result in the "Cron exceeding time limit" error, because it didn't end properly. - // The above will log the message to watchdog, and that's the extent of the notice that - // will be given. - // To mimic current behaviour, break will be used instead of continue. - // throw $e; - break; - } - curl_close($ch); - - // CURL - $post->data['server_post'][] = array( - 'timestamp' => time(), - 'response' => $data, - 'http_status' => $curl_status['http_code'], - ); - - // Update Post Data - if ($success) { - $post->{ELOQUA_POST_FIELD_STATUS} = ELOQUA_STATUS_UPLOADED; - } else { - $post->{ELOQUA_POST_FIELD_STATUS} = ELOQUA_STATUS_FAILED; - } - eloqua_post_update($post); - } -} - -/** - * Returns a configured Curl Resource for use - * @return resource - */ -function _eloqua_cron_get_curl_resource($options = array()) { - $ch = _eloqua_cron_get_curl_handle(); - if ($ch == NULL) { - return NULL; - } - - // Setting Options - $curl_opts = array( - CURLOPT_HEADER => 1, - CURLOPT_POST => 1, - CURLOPT_RETURNTRANSFER => 1, - CURLOPT_CONNECTTIMEOUT => 5, // TODO: TIMEOUT ADMIN SETTING - CURLOPT_TIMEOUT => 5, // TODO: TIMEOUT ADMIN SETTING - CURLOPT_URL => 'http://now.eloqua.com/e/f2.aspx', - ); - - $option_map = array( - 'post_fields' => CURLOPT_POSTFIELDS, - 'user_agent' => CURLOPT_USERAGENT, - 'http_headers' => CURLOPT_HTTPHEADER, - ); - foreach ($option_map as $key => $curl_key) { - if (array_key_exists($key, $options)) { - $curl_opts[$curl_key] = $options[$key]; - } - } - - $status = _eloqua_set_curl_opts($ch, $curl_opts); - $error_msg = _eloqua_set_curl_get_error($status); - if (!empty($error_msg)) { - // Failed setting an option - $message = t('Unable to set curl options. Skipping message. !options.'); - $params = array( - '!options' => '
' . $error_msg . '
', - ); - watchdog('eloqua', $message, $params, WATCHDOG_ERROR); - curl_close($ch); - $ch = NULL; - } - return $ch; -} - -/** - * Handle translating our post values into what eloqua wants in terms of structure. - * - * Dates are by default handled in YYYY-mm-dd - * Times are by default handled in HH:mm - * - * @param $tree - the post tree name => value pairs - * @param $posted_values - the post tree, could be name => value pairs or index => value pairs - * @param $result - the re-structured tree that Eloqua will leverage - * @return none - */ -function _eloqua_get_submission_data($tree, $posted_values, &$result) { - foreach ($tree as $name => $value) { - // we need to expand things in fieldsets - if (is_array($value) && !in_array($value, $posted_values)) { - _eloqua_get_submission_data($value, $posted_values, $result); - } - // we need to convert multi-value fields into strings - else if (is_array($value)) { - $result[$name] = implode(',', $value); - } - // everything else is just passed along - else { - $result[$name] = $value; - } - } -} - -/** - * Remaps the post fields to be used within Eloqua - * @param $post_fields array - * Data to post - * @param $post array - * Post data - * @return array - * $post_fields modified to have remapped keys where defined - */ -function _eloqua_cron_remap_post_fields($post_fields, $post) { - $result = array(); - $nid = $post->data['form_post']['details']['nid']; - if (empty($nid) || !is_numeric($nid)) { - // Something is wrong with this - return $post_fields; - } - $node = node_load($nid); - $components = $node->webform['components']; - $map = array(); - if (is_array($components)) { - foreach ($components as $component) { - if (!empty($component['extra']['eloqua']['key']) - && $component['extra']['eloqua']['key'] !== $component['form_key']) { - $map[$component['form_key']] = $component['extra']['eloqua']['key']; - } - } - } - - foreach ($post_fields as $key => $value) { - if (isset($map[$key])) { - $result[$map[$key]] = $value; - } else { - $result[$key] = $value; - } - } - - return $result; -} - -/** - * Returns the cURL error for a status returned from eloqua_curl_set_opts - * @TODO: Theme - * @param $status array - * @return string - * Html formatted message - */ -function _eloqua_set_curl_get_error($status) { - if (!is_array($status)) { - return FALSE; - } - $messages = array(); - $message = ''; - foreach ($status as $key => $data) { - if ($data['status']) { - continue; - } - $value = var_export($data['value'], TRUE); - $messages[] = "
  • $key - Error: {$data['message']}
    Value: $value
  • "; - } - if (!empty($messages)) { - $message = ''; - } - return $message; -} - -/** - * Sets curl options en mass, checking for errors - * @param $ch resource - * Curl Resource - * @param $options array - * Curl settings to apply to the resource - * @return array - * Status of all the set_opt calls - * - */ -function _eloqua_set_curl_opts(&$ch, $options = array()) { - if (!is_array($options)) { - return FALSE; - } - $result = array(); - foreach ($options as $key => $value) { - $status = curl_setopt($ch, $key, $value); - $status_msg = ''; - if (!$status) { - $status_msg = curl_error($ch); - } - $result[$key] = array( - 'status' => $status, - 'value' => $value, - 'message' => $status_msg, - ); - } - return $result; -} - -/** - * Returns the post fields for the request, modified for eloqua - * @param $post - * @return array - */ -function _eloqua_cron_get_post_fields($post) { - // TODO: Review newest webform for exact behaviour on whether submitted_tree exists. - $form_data = !empty($post->data['form_post']['submitted_tree']) ? $post->data['form_post']['submitted_tree'] : $post->data['form_post']['submitted']; - - $post_fields = array(); - // Merge any elq values into the post to be submitted - foreach ($post->data['form_post'] as $name => $value) { - if (substr($name, 0, 3) === 'elq') { - $post_fields[$name] = $value; - } - } - - // translates the data into a Eloqua-worthy format - _eloqua_get_submission_data($form_data, $post->data['form_post']['submitted'], $post_fields); - - // Remap any fields that can't be handled by webform - $post_fields = _eloqua_cron_remap_post_fields($post_fields, $post); - return $post_fields; -} diff --git a/eloqua.inc b/eloqua.inc deleted file mode 100644 index bc60e13..0000000 --- a/eloqua.inc +++ /dev/null @@ -1,615 +0,0 @@ - '
    ' . var_export($value, TRUE) . '
    ', - ); - watchdog(ELOQUA_WATCHDOG_CATEGORY, $message, $variables, WATCHDOG_NOTICE); - return FALSE; - } - variable_set(ELOQUA_VARIABLE_LAST_POST_TIME, $value); - return TRUE; -} - -/** - * Returns the Eloqua Site ID - * @return int - */ -function eloqua_get_site_id() { - $site_id = variable_get(ELOQUA_VARIABLE_SITE_ID, ELOQUA_VARIABLE_SITE_ID_DEFAULT); - return $site_id; -} - -/** - * Gets the update interval - * @return int - * Seconds - */ -function eloqua_get_update_time_interval() { - return variable_get(ELOQUA_VARIABLE_POST_INTERVAL, ELOQUA_VARIABLE_POST_INTERVAL_DEFAULT); -} - -/** - * Sets the update interval - * @param $seconds int - * Number of seconds to elapse between cron pushes - * @return boolean - * Successful Update - */ -function eloqua_set_update_time_interval($seconds) { - if (!is_numeric($seconds) - || $seconds < 0) { - $message = t('!method: Invalid value set for post time: !value'); - $variables = array( - '!method' => __METHOD__, - '!value' => '
    ' . var_export($seconds, TRUE) . '
    ', - ); - watchdog(ELOQUA_WATCHDOG_CATEGORY, $message, $variables, WATCHDOG_NOTICE); - return FALSE; - } - $seconds = (int) $seconds; - variable_set(ELOQUA_VARIABLE_POST_INTERVAL, $seconds); - return TRUE; -} - -//////////////////////////////////////////////////////////////////////////////// -// Webform Setting Database Access Functions - -/** - * Loads a webform settings from the database - * @hook eloqua_form_load ($webform) - * @param $nid int - * @return object - */ -function eloqua_webform_load($nid) { - // Get the raw result from the db - if (!is_numeric($nid)) { - $type = gettype($nid); - watchdog(ELOQUA_WATCHDOG_CATEGORY, t('Invalid argument sent to !module_name (!type).', array('!module_name' => __FUNCTION__, '!type' => $type)), array(), WATCHDOG_DEBUG); - return NULL; - } - - $webform = _eloqua_webform_load($nid); - if (empty($webform)) { - return NULL; - } - - // Send the object around to all of its friends - foreach (module_implements('eloqua_form_load') as $module_name) { - $method = $module_name . '_eloqua_form_load'; - $method($webform); - } - - return $webform; -} - -/** - * Creates a webform settings from the database - * @hook eloqua_form_create - * @param $webform object - * @return boolean - */ -function eloqua_webform_create($webform) { - if (!is_object($webform)) { - $type = gettype($webform); - watchdog(ELOQUA_WATCHDOG_CATEGORY, t('Invalid argument sent to !module_name (!type).', array('!module_name' => __FUNCTION__, '!type' => $type))); - return FALSE; - } - - $result = _eloqua_webform_create($webform); - if (!$result) { - // if we were unable to create the payment plan. Can't go on. - return FALSE; - } - - foreach (module_implements('eloqua_form_create') as $module_name) { - $method = $module_name . '_eloqua_form_create'; - $method($webform); - } - - _eloqua_webform_update($webform); - return $result; -} - -/** - * Updates a webform settings from the database - * @hook eloqua_form_update - * @param $webform object - * @return boolean - */ -function eloqua_webform_update($webform) { - if (!is_object($webform)) { - $type = gettype($webform); - watchdog(ELOQUA_WATCHDOG_CATEGORY, t('Invalid argument sent to !module_name (!type).', array('!module_name' => __FUNCTION__, '!type' => $type)), array(), WATCHDOG_DEBUG); - return FALSE; - } - - // Can't update the obvious invalid ppid of '0'... - if (empty($webform->nid)) { - return FALSE; - } - - foreach (module_implements('eloqua_form_update') as $module_name) { - $method = $module_name . '_eloqua_form_update'; - $method($webform); - } - - // Save the result to the database - $result = _eloqua_webform_update($webform); - return $result; -} - -/** - * Deletes a webform settings object from the database - * @param $nid integer - * @return boolean - */ -function eloqua_webform_delete($nid) { - if (!is_numeric($nid)) { - $type = gettype($nid); - watchdog(ELOQUA_WATCHDOG_CATEGORY, t('Invalid argument sent to !module_name (!type).', array('!module_name' => __FUNCTION__, '!type' => $type)), array(), WATCHDOG_DEBUG); - return FALSE; - } - - // Can't update the obvious invalid ppid of '0'... - if (empty($nid)) { - return FALSE; - } - - foreach (module_implements('eloqua_form_delete') as $module_name) { - $method = $module_name . '_eloqua_form_delete'; - $method($nid); - } - - $result = _eloqua_webform_delete($nid); - return $result; -} - -/** - * Loads the webform object from the database - * @param $nid int - * @return object - */ -function _eloqua_webform_load($nid) { - $result = db_select(ELOQUA_SCHEMA_WEBFORM) - ->fields(ELOQUA_SCHEMA_WEBFORM) - ->condition(ELOQUA_WEBFORM_FIELD_NODE_ID, $nid) - ->execute() - ->fetchAll(PDO::FETCH_OBJ); - $result = _eloqua_unserialize_data_column($result); - $result = array_shift($result); - return $result; -} - -/** - * Creates the webform object from the database - * @param $webform - * @return boolean - */ -function _eloqua_webform_create($webform) { - $index = array( - ELOQUA_WEBFORM_FIELD_NODE_ID => $webform->{ELOQUA_WEBFORM_FIELD_NODE_ID}, - ); - $fields = array( - ELOQUA_WEBFORM_FIELD_FORM_NAME => $webform->{ELOQUA_WEBFORM_FIELD_FORM_NAME}, - ELOQUA_WEBFORM_FIELD_ACTIVE => $webform->{ELOQUA_WEBFORM_FIELD_ACTIVE}, - ELOQUA_WEBFORM_FIELD_DATA => serialize($webform->{ELOQUA_WEBFORM_FIELD_DATA}), - ); - - $result = _eloqua_db_insert_update($index, $fields); - return $result; -} - -/** - * Updates the webform object from the database - * @param $webform object - * @return boolean - */ -function _eloqua_webform_update($webform) { - $fields = array( - ELOQUA_WEBFORM_FIELD_FORM_NAME => $webform->{ELOQUA_WEBFORM_FIELD_FORM_NAME}, - ELOQUA_WEBFORM_FIELD_ACTIVE => $webform->{ELOQUA_WEBFORM_FIELD_ACTIVE}, - ELOQUA_WEBFORM_FIELD_DATA => serialize($webform->{ELOQUA_WEBFORM_FIELD_DATA}), - ); - $result = db_update(ELOQUA_SCHEMA_WEBFORM) - ->fields($fields) - ->condition(ELOQUA_WEBFORM_FIELD_NODE_ID, $webform->{ELOQUA_WEBFORM_FIELD_NODE_ID}) - ->execute(); - - $result = (is_numeric($result)) ? ($result > 0) : FALSE; - return $result; -} - -/** - * Deletes a webform object from the database - * @param $nid int - * @return boolean - */ -function _eloqua_webform_delete($nid) { - $result = db_delete(ELOQUA_SCHEMA_WEBFORM) - ->condition(ELOQUA_WEBFORM_FIELD_NODE_ID, $nid) - ->execute(); - $result = (is_numeric($result)) ? ($result > 0) : FALSE; - return $result; -} - -/** - * Returns the cron processing batch size - * @return int - */ -function eloqua_get_batch_size() { - $batch_size = (int) variable_get(ELOQUA_SETTINGS_BATCH_SIZE, ELOQUA_SETTINGS_BATCH_SIZE_DEFAULT); - return $batch_size; -} - -//////////////////////////////////////////////////////////////////////////////// -// Completed Post Handling - - -function eloqua_post_get_batch() { - $result_set = db_select(ELOQUA_SCHEMA_SAVED_POSTS) - ->fields(ELOQUA_SCHEMA_SAVED_POSTS) - ->condition(ELOQUA_POST_FIELD_STATUS, ELOQUA_STATUS_NEW) - ->range(0, eloqua_get_batch_size()) - ->execute() - ->fetchAll(); - $result = _eloqua_unserialize_data_column($result_set); - return $result; -} - -/** - * Loads a post form the database - * @hook eloqua_post_load ($post) - * @param $post_id int - * @return object - */ -function eloqua_post_load($post_id) { - // Get the raw result from the db - if (!is_numeric($post_id)) { - $type = gettype($post_id); - watchdog(ELOQUA_WATCHDOG_CATEGORY, t('Invalid argument sent to !module_name (!type).', array('!module_name' => __FUNCTION__, '!type' => $type)), array(), WATCHDOG_DEBUG); - return NULL; - } - - $post = _eloqua_post_load($post_id); - if (empty($post)) { - return NULL; - } - - // Send the object around to all of its friends - foreach (module_implements('eloqua_post_load') as $module_name) { - $method = $module_name . '_eloqua_post_load'; - $method($post); - } - - return $post; -} - -/** - * Creates a post into the database - * @hook eloqua_post_create - * @param $post object - * @return int - * Post ID - */ -function eloqua_post_create($post) { - if (!is_object($post)) { - $type = gettype($post); - watchdog(ELOQUA_WATCHDOG_CATEGORY, t('Invalid argument sent to !module_name (!type).', array('!module_name' => __FUNCTION__, '!type' => $type))); - return FALSE; - } - - $result = _eloqua_post_create($post); - if (!$result) { - // if we were unable to create the payment plan. Can't go on. - return FALSE; - } - - $post->{ELOQUA_POST_FIELD_POST_ID} = $result; - - foreach (module_implements('eloqua_post_create') as $module_name) { - $method = $module_name . '_eloqua_post_create'; - $method($post); - } - - _eloqua_post_update($post); - return $result; -} - -/** - * Updates a post from the database - * @hook eloqua_post_update - * @param $post object - * @return boolean - */ -function eloqua_post_update($post) { - if (!is_object($post)) { - $type = gettype($post); - watchdog(ELOQUA_WATCHDOG_CATEGORY, t('Invalid argument sent to !module_name (!type).', array('!module_name' => __FUNCTION__, '!type' => $type)), array(), WATCHDOG_DEBUG); - return FALSE; - } - - // Can't update the obvious invalid ppid of '0'... - if (empty($post->{ELOQUA_POST_FIELD_POST_ID})) { - return FALSE; - } - - foreach (module_implements('eloqua_post_update') as $module_name) { - $method = $module_name . '_eloqua_post_update'; - $method($post); - } - - // Save the result to the database - $result = _eloqua_post_update($post); - return $result; -} - -/** - * Deletes a post object from the database - * @param $post_id integer - * @return boolean - */ -function eloqua_post_delete($post_id) { - if (!is_numeric($post_id)) { - $type = gettype($post_id); - watchdog(ELOQUA_WATCHDOG_CATEGORY, t('Invalid argument sent to !module_name (!type).', array('!module_name' => __FUNCTION__, '!type' => $type)), array(), WATCHDOG_DEBUG); - return FALSE; - } - - // Can't update the obvious invalid ppid of '0'... - if (empty($post_id)) { - return FALSE; - } - - foreach (module_implements('eloqua_post_delete') as $module_name) { - $method = $module_name . '_eloqua_post_delete'; - $method($post_id); - } - - $result = _eloqua_post_delete($post_id); - return $result; -} - -/** - * Loads the post object from the database - * @param $post_id int - * @return object - */ -function _eloqua_post_load($post_id) { - $result_set = db_select(ELOQUA_SCHEMA_SAVED_POSTS) - ->condition(ELOQUA_POST_FIELD_POST_ID, $post_id) - ->execute() - ->fetchAll(); - $result = _eloqua_unserialize_data_column($result_set); - $result = array_shift($result); - return $result; -} - -/** - * Creates the post object from the database - * @param $post - * @return int - * Newly created Post_id - */ -function _eloqua_post_create($post) { - $fields = array( - ELOQUA_POST_FIELD_FORM_ID => $post->{ELOQUA_POST_FIELD_FORM_ID}, - ELOQUA_POST_FIELD_POST_TIME => $post->{ELOQUA_POST_FIELD_POST_TIME}, - ELOQUA_POST_FIELD_STATUS => $post->{ELOQUA_POST_FIELD_STATUS}, - ELOQUA_POST_FIELD_DATA => serialize($post->{ELOQUA_POST_FIELD_DATA}), - ); - $result = db_insert(ELOQUA_SCHEMA_SAVED_POSTS) - ->fields($fields) - ->execute(); - // The result must be the post_id or we'll end up with duplicate records in - // the eloqua_saved_posts table. - if ($result === FALSE || $result === NULL) { - $result = false; - } - return $result; -} - -/** - * Updates the post object from the database - * @param $post object - * @return boolean - */ -function _eloqua_post_update($post) { - $fields = array( - ELOQUA_POST_FIELD_FORM_ID => $post->{ELOQUA_POST_FIELD_FORM_ID}, - ELOQUA_POST_FIELD_POST_TIME => $post->{ELOQUA_POST_FIELD_POST_TIME}, - ELOQUA_POST_FIELD_STATUS => $post->{ELOQUA_POST_FIELD_STATUS}, - ELOQUA_POST_FIELD_DATA => serialize($post->{ELOQUA_POST_FIELD_DATA}), - ); - $result = db_update(ELOQUA_SCHEMA_SAVED_POSTS) - ->fields($fields) - ->condition(ELOQUA_POST_FIELD_POST_ID, $post->{ELOQUA_POST_FIELD_POST_ID}) - ->execute(); - $result = (is_numeric($result)) ? ($result > 0) : FALSE; - return $result; -} - -/** - * Deletes a post object from the database - * @param $post_id int - * @return boolean - */ -function _eloqua_post_delete($post_id) { - $result = db_delete(ELOQUA_SCHEMA_SAVED_POSTS) - ->condition(ELOQUA_POST_FIELD_POST_ID, $post_id) - ->execute(); - $result = (is_numeric($result)) ? ($result > 0) : FALSE; - return $result; -} - -/** - * Goes through the result set and unserialises any 'data' columns - * @param $result_set - * @return array - */ -function _eloqua_unserialize_data_column($result_set) { - $result = array(); - if (!is_array($result_set)) { - $result_set = array(); - } - - foreach ($result_set as $row) { - $row->data = unserialize($row->data); - $result[] = $row; - } - - return $result; -} - -/** - * Query the Database and mimic a INSERT ... ON DUPLICATE KEY UPDATE - * - * Because there isn't a "Drupal" way to do a INSERT ... ON DUPLICATE KEY UPDATE using the - * abstraction layer they provided. - * - * Also, when using InnoDB tables, you cannot actually retrieve the last-insert-id reliably - * So, all in all, this just returns a TRUE/FALSE now, as opposed to the Drupal 6 Version - * @param $index - * @param $fields - * @return bool - */ -function _eloqua_db_insert_update($index, $fields) { - $insert_fields = $index + $fields; - $do_update = FALSE; - $result = FALSE; - try { - $result = db_insert(ELOQUA_SCHEMA_WEBFORM) - ->fields($insert_fields) - ->execute() - ; - // No longer able to return the last inserted Id. - if ($result !== FALSE) { - $result = TRUE; - } - } catch (PDOException $e) { - // Constraint Error - if ($e->getCode() == '23000') { - $do_update = TRUE; - } - } - - // Attempt Update - if ($do_update) { - $query = db_update(ELOQUA_SCHEMA_WEBFORM) - ->fields($fields); - foreach ($index as $key => $value) { - $query->condition($key, $value); - } - - try { - $result = $query->execute(); - } catch (PDOException $e) { - return FALSE; - } - - // Rows Updated - if (is_numeric($result)) { - $result = (bool) $result; - } else { - // Probably failed. - $result = FALSE; - } - } - return $result; -} - -// End of file diff --git a/eloqua.info b/eloqua.info index fd0a461..711118e 100644 --- a/eloqua.info +++ b/eloqua.info @@ -1,5 +1,5 @@ name = Eloqua Integration -description = Module to allow Eloqua integration for Drupal websites +description = Module to allow Eloqua tracking for Drupal websites package = "Eloqua" core = 7.x diff --git a/eloqua.install b/eloqua.install index ec183a2..a024e53 100644 --- a/eloqua.install +++ b/eloqua.install @@ -1,116 +1,19 @@ t('Webform Settings'), - 'primary key' => array(ELOQUA_POST_FIELD_POST_ID), - 'unique keys' => array(), - 'indexes' => array( - ELOQUA_SCHEMA_SAVED_POSTS . '_form_id' => array(ELOQUA_POST_FIELD_FORM_ID), - ), - 'fields' => array( - ELOQUA_POST_FIELD_FORM_ID => array( - // Associated Form Id - 'description' => 'Form ID that generated this post', - 'type' => 'varchar', - 'size' => 'normal', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - ELOQUA_POST_FIELD_POST_ID => array( - // Serial Number - 'type' => 'serial', - 'size' => 'normal', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - ELOQUA_POST_FIELD_STATUS => array( - // Form Posted Status - 'description' => 'Eloqua option name', - 'type' => 'varchar', - 'size' => 'normal', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - ELOQUA_POST_FIELD_POST_TIME => array( - // Post Time - 'description' => 'Node Id', - 'type' => 'int', - 'size' => 'normal', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - ELOQUA_POST_FIELD_DATA => array( - // Form Contents - 'description' => 'Extra data to be stored with the field', - 'type' => 'text', - 'size' => 'normal', /* 16KB in mySql */ - 'not null' => TRUE, - ), - ), - ); - - $schema[ELOQUA_SCHEMA_WEBFORM] = array( - 'description' => t('Webform Settings'), - 'primary key' => array(ELOQUA_WEBFORM_FIELD_NODE_ID), - 'unique keys' => array(), - 'indexes' => array( - ELOQUA_SCHEMA_WEBFORM . '_active' => array(ELOQUA_WEBFORM_FIELD_ACTIVE), - ), - 'fields' => array( - ELOQUA_WEBFORM_FIELD_NODE_ID => array( - // Webform Node Id - 'description' => 'Node Id', - 'type' => 'int', - 'size' => 'normal', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - ELOQUA_WEBFORM_FIELD_FORM_NAME => array( - // Eloqua Form Name - 'description' => 'Eloqua form name', - 'type' => 'varchar', - 'size' => 'normal', - 'length' => 64, - 'not null' => TRUE, - 'default' => '', - ), - ELOQUA_WEBFORM_FIELD_ACTIVE => array( - // Is this webform eloqua enabled - 'description' => 'Whether this form is eloqua active', - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - 'unsigned' => TRUE, - ), - ELOQUA_WEBFORM_FIELD_DATA => array( - 'description' => 'Extra data to be stored with the field', - 'type' => 'text', - 'size' => 'normal', /* 16KB in mySql */ - 'not null' => TRUE, - ), - ), - ); - return $schema; -} +/** + * @file + * + * Eloqua install, schema, uninstall and update hooks. + */ /** - * Implementation of hook_install(). + * Implements hook_install(). */ function eloqua_install() { } /** - * Implementation of hook_uninstall(). + * Implements hook_uninstall(). */ function eloqua_uninstall() { @@ -125,4 +28,13 @@ function eloqua_install_update_7000(&$sandbox) { drupal_set_message('You may remove the elqNow library from your sites folder. Libraries are no longer required to use this module.'); variable_del('eloqua_scripts_directory'); +} + +/** + * Enable eloqua_webform module + */ +function eloqua_install_update_7001(&$sandbox) { + $modules = array('eloqua_webform'); + + module_enable($modules, TRUE); } \ No newline at end of file diff --git a/eloqua.module b/eloqua.module index 876633f..b99427c 100644 --- a/eloqua.module +++ b/eloqua.module @@ -1,31 +1,26 @@ array('siteId' => check_plain(eloqua_get_site_id()))), 'setting'); + $site_id = check_plain(variable_get('eloqua_site_id', 0)); + + drupal_add_js(array('eloqua' => array('siteId' => $site_id)), 'setting'); drupal_add_js(drupal_get_path('module', 'eloqua') . '/eloqua.js'); } /** - * Implementation of hook_menu(). + * Implements hook_menu(). */ function eloqua_menu() { $items = array(); @@ -40,524 +35,3 @@ function eloqua_menu() { ); return $items; } - -function eloqua_cron() { - // Load the cron functionality - module_load_include('inc', 'eloqua', 'eloqua.cron'); - _eloqua_cron(); -} - -/** - * Returns the list of exempt component types - * @return array - */ -function _eloqua_webform_component_get_exempt_types() { - return array( - 'fieldset', - 'pagebreak', - ); -} - -/** - * Tests whether the given component type is valid for Eloqua Integration - * @param $test_type - * @return bool - */ -function _eloqua_is_valid_component_type($test_type) { - $invalid_types = _eloqua_webform_component_get_exempt_types(); - $result = in_array($test_type, $invalid_types); - $result = !$result; - return $result; -} - -/** - * Implementatation of hook_form_FORM_ID_alter(). - * This function is reponsible for adding the form-id mapping values to the Component add/edit page - * Note: this isn't used for the fieldset type. - */ -function eloqua_form_webform_component_edit_form_alter(&$form, $form_state) { - if (!array_key_exists('build_info', $form_state) - || !array_key_exists('args', $form_state['build_info']) - || !array_key_exists(1, $form_state['build_info']['args'])) { - return; - } - - $component = $form_state['build_info']['args'][1]; - - // Create the fieldset - $form['eloqua'] = array( - '#type' => 'fieldset', - '#title' => t('Eloqua'), - '#tree' => TRUE, - ); - - // Check the field type. Some do not make sense as having a value to post to Eloqua - $component_type = $component['type']; - if (!_eloqua_is_valid_component_type($component_type)) { - // todo: Get this working - $form['eloqua']['msg'] = array( - '#type' => 'markup', - '#value' => t('This component type is not compatible with the Eloqua Module.'), - ); - return; - } - - - $form['eloqua']['key'] = array( - '#type' => 'textfield', - '#default_value' => isset($component['extra']['eloqua']['key']) ? $component['extra']['eloqua']['key'] : '', - '#title' => t('Eloqua Field Name'), - '#description' => t('The eloqua field name if it contains symbols or characters not compatible with the defaults above. Leave empty to use the default above. Can not contain spaces.'), - '#weight' => 0, - ); - - $form['eloqua']['query_name'] = array( - '#type' => 'textfield', - '#default_value' => isset($component['extra']['eloqua']['query_name']) ? $component['extra']['eloqua']['query_name'] : '', - '#description' => t('If this value can be populated by a query parameter, the key that is to be used. Can only contain the characters a-z A-Z 0-9 and _.'), - '#title' => t('Request Parameter Key'), - ); - $form['#validate'][] = 'eloqua_form_webform_component_edit_form_validate'; -} - -/** - * Presave handler - * @param $component - * @return void - */ -function eloqua_webform_component_presave(&$component) { - if (array_key_exists('eloqua', $component) && isset($component['eloqua'])) { - $component['extra']['eloqua'] = $component['eloqua']; - } -} - -/** - * Validates the eloqua field - * @todo Make this less Western Centric. Get parameters should be able to accept all Unicode code-points - * @param $form - * @param $form_state - * @return unknown_type - */ -function eloqua_form_webform_component_edit_form_validate($form, $form_state) { - if (preg_match('![\x00-\x20 ]!', $form_state['values']['eloqua']['key'])) { - form_set_error('eloqua][key', t('The eloqua field key %field_key is invalid. Please do not include spaces.', array('%field_key' => $form_state['values']['eloqua']['key']))); - } - $values = $form_state['values']; - if (!empty($values['eloqua']['query_name'])) { - if (!preg_match('/^[a-zA-Z0-9_]+$/', $values['eloqua']['query_name'])) { - form_set_error('eloqua][query_name', t('The query name provided can only contain the characters a-z A-Z 0-9 and underscore (_).')); - } - } -} - -/** - * Implementation of hook_form_FORM_ID_alter(). - */ -function eloqua_form_webform_configure_form_alter(&$form, $form_state) { - $form['#submit'][] = 'eloqua_form_webform_configure_form_submit'; - $form['#validate'][] = 'eloqua_form_webform_configure_form_validate'; - // Load the form options from the database if available - $settings = new stdClass(); - - if (!empty($form['nid']['#value'])) { - $settings = eloqua_webform_load($form['nid']['#value']); - } - - // Apply default settings - if (!isset($settings->{ELOQUA_WEBFORM_FIELD_ACTIVE})) { - $settings->{ELOQUA_WEBFORM_FIELD_ACTIVE} = 0; - } - if (!isset($settings->{ELOQUA_WEBFORM_FIELD_FORM_NAME})) { - $settings->{ELOQUA_WEBFORM_FIELD_FORM_NAME} = ''; - } - if (!isset($settings->{ELOQUA_WEBFORM_FIELD_DATA})) { - $settings->{ELOQUA_WEBFORM_FIELD_DATA} = new stdClass(); - } - if (!isset($settings->{ELOQUA_WEBFORM_FIELD_DATA}->process_every_page)) { - $settings->{ELOQUA_WEBFORM_FIELD_DATA}->process_every_page = 0; - } - - $form['eloqua'] = array( - '#type' => 'fieldset', - '#description' => t('Eloqua Integration Options'), - '#title' => t('Eloqua'), - '#collapsible' => TRUE, - '#collapsed' => FALSE, - 'elq_enabled' => array( - '#type' => 'checkbox', - '#default_value' => $settings->{ELOQUA_WEBFORM_FIELD_ACTIVE}, - '#description' => t('Whether this form is eloqua enabled'), - '#title' => t('Enabled'), - ), - 'elq_form_name' => array( - '#type' => 'textfield', - '#default_value' => $settings->{ELOQUA_WEBFORM_FIELD_FORM_NAME}, - '#description' => t('The form name as it is defined in Eloqua'), - '#title' => t('Eloqua Form Name'), - ), - 'elq_process_every_page' => array( - '#type' => 'checkbox', - '#default_value' => $settings->{ELOQUA_WEBFORM_FIELD_DATA}->process_every_page, - '#description' => t('Process every page submit on a multi-page form, instead of one combined submit upon completion.'), - '#title' => t('Process subpages seperately'), - ), - ); -} - -/** - * Form Validation Function - * @param $form - * @param $form_state - * @return unknown_type - */ -function eloqua_form_webform_configure_form_validate($form, $form_state) { - $values = $form_state['values']; - if ($values['elq_enabled']) { - if (empty($values['elq_form_name'])) { - form_set_error('elq_form_name', t('The Eloqua Form Name must be set if the form is to be eloqua-enabled.' - . ' This can only contain the characters a-zA-Z and _')); - } - } -} - -/** - * Submit handler for webform_configure_form - * @param $form array - * @param $form_state array - */ -function eloqua_form_webform_configure_form_submit ($form, &$form_state) { - // On a new form - $nid = $form_state['values']['nid']; - $values = $form_state['values']; - $webform = new stdClass(); - $webform->{ELOQUA_WEBFORM_FIELD_NODE_ID} = $nid; - $webform->{ELOQUA_WEBFORM_FIELD_ACTIVE} = $values['elq_enabled']; - $webform->{ELOQUA_WEBFORM_FIELD_FORM_NAME} = $values['elq_form_name']; - $webform->{ELOQUA_WEBFORM_FIELD_DATA} = (object) array( - 'process_every_page' => $values['elq_process_every_page'], - ); - - eloqua_webform_create($webform); -} - -/** - * Implementation of hook_form_alter(). - */ -function eloqua_form_alter(&$form, $form_state, $form_id) { - // We only want to edit client versions of the webform. If it's anything else - // simply return. - if (strpos($form_id, 'webform_client_form') !== 0) { - return; - } - - if (!array_key_exists('#node', $form) || !is_object($form['#node']) || empty($form['#node']->nid)) { - return; - } - - $nid = $form['#node']->nid; - $form_settings = (array) eloqua_webform_load($nid); - - // Check to see if the form exists - if (empty($form_settings) || (!$form_settings[ELOQUA_WEBFORM_FIELD_ACTIVE])) { - return; - } - - // Check for QUERY parameter mappings to assign default values - _eloqua_form_url_map($form, $form['#node']->webform['components']); - - // Just shove everything into a container - $form['#submit'][] = 'eloqua_webform_submit'; - $form['eloqua'] = array(); - $process_every_page = - (is_object($form_settings[ELOQUA_SETTINGS_FIELD_DATA]) && isset($form_settings[ELOQUA_SETTINGS_FIELD_DATA]->process_every_page)) ? $form_settings[ELOQUA_SETTINGS_FIELD_DATA]->process_every_page : FALSE; - $form['eloqua']['process_every_page'] = array( - '#type' => 'value', - '#value' => $process_every_page, - ); - $form['eloqua']['elqFormName'] = _eloqua_form_hidden($form_settings[ELOQUA_WEBFORM_FIELD_FORM_NAME]); - $form['eloqua']['elqSiteId'] = _eloqua_form_hidden(eloqua_get_site_id()); - $form['eloqua']['elqDefaultTargetURL'] = _eloqua_form_hidden(''); - $form['eloqua']['elqPost'] = _eloqua_form_hidden(''); - $form['eloqua']['elqCustomerGUID'] = _eloqua_form_hidden(''); - // Drupal 7 doesn't automatically give hidden elements an #id - $form['eloqua']['elqCustomerGUID']['#attributes']['id'] = 'edit-elqCustomerGUID'; - $form['eloqua']['elqCookieWrite'] = _eloqua_form_hidden('0'); - - drupal_add_js('var timerId = null, timeout = 5;', 'inline'); - - $initScript = - 'function WaitUntilCustomerGUIDIsRetrieved() { - if (!!(timerId)) { - if (timeout == 0) { - return; - } - if (typeof this.GetElqCustomerGUID === \'function\') { - document.forms["'.check_plain($form_settings[ELOQUA_WEBFORM_FIELD_FORM_NAME]).'"].elements["elqCustomerGUID"].value = GetElqCustomerGUID(); - return; - } - timeout -= 1; - } - timerId = setTimeout("WaitUntilCustomerGUIDIsRetrieved()", 500); - return; - } - - window.onload = WaitUntilCustomerGUIDIsRetrieved; - _elqQ.push([\'elqGetCustomerGUID\']);'; - - drupal_add_js($initScript, 'inline'); - $user_headers = _eloqua_webform_get_user_headers(); - - $form['eloqua']['user_headers'] = array( - '#type' => 'hidden', - '#default_value' => serialize($user_headers), - ); - - // Hooks that don't need to load up all the checks to see if said form - // is eloqua_enabled - foreach (module_implements('eloqua_form_alter') as $module_name) { - $method = $module_name . '_eloqua_form_alter'; - $method($form, $form_state, $form_id); - } -} - -/** - * Returns the user headers filtered for types that shouldn't be needed - * This function currently is Apache module specific - * @return array - */ -function _eloqua_webform_get_user_headers() { - // Getting the request headers only exists in apache - if (!function_exists('apache_request_headers')) { - return array(); - } - - $headers = apache_request_headers(); - - // Check to see if the function succeeded or not - if (!$headers) { - return array(); - } - - $filter = array( - 'host', - 'cookie', - ); - - $result = array(); - foreach ($headers as $key => $value) { - if (!in_array(strtolower($key), $filter)) { - $result[strtolower($key)] = $value; - } - } - return $result; -} - -/** - * Submit handler for the webform - * @param $form array - * @param $form_state array - */ -function eloqua_webform_submit($form, $form_state) { - // Only process upon the final page submission, unless otherwise told - $process_every_page = $form_state['values']['process_every_page']; - if (!$process_every_page) { - if (!array_key_exists('webform_completed', $form_state) || !$form_state['webform_completed']) { - return; - } - } - - - $values = array(); - $values['form_post'] = $form_state['values']; - $values['user_headers'] = unserialize($form_state['values']['user_headers']); - // Check for serialization errors - if ($values['user_headers'] == FALSE) { - $values['user_headers'] = array(); - } - unset($values['form_post']['user_headers']); - - // Save the values and essential information to the database - $info = new stdClass(); - $info->{ELOQUA_POST_FIELD_FORM_ID} = $form_state['values']['form_id']; - $info->{ELOQUA_POST_FIELD_POST_TIME} = time(); - $info->{ELOQUA_POST_FIELD_STATUS} = ELOQUA_STATUS_NEW; - $info->{ELOQUA_POST_FIELD_DATA} = $values; - - $post_id = eloqua_post_create($info); - if (!$post_id) { - $message = t('Unable to save submitted entry.
    !data
    '); - $vars = array('!data' => var_export($info, TRUE)); - watchdog(ELOQUA_WATCHDOG_CATEGORY, $message, $vars, WATCHDOG_NOTICE); - } -} - -/** - * Convenience Function returning a fAPI compatible hidden field - * @param $value string - * @return array - * fAPI Form Element - */ -function _eloqua_form_hidden($value) { - return array( - '#type' => 'hidden', - '#default_value' => (string) $value, - ); -} - -/** - * Recursively builds the array tree that will need to be parsed to find values - * in the post. - * - * @param $components - the components array from webform - * @param $cid - the component id - * @return array - an array representing tree selections to find a value - */ -function eloqua_get_nesting($components, $cid) { - if ($components[$cid]['pid'] == 0) { - return array($components[$cid]['form_key']); - } else { - return array_merge(eloqua_get_nesting($components, $components[$cid]['pid']), array($components[$cid]['form_key'])); - } -} - -/** - * Maps the url parameters to the associated form fields - * @param $form - * @param $component_list - * The components must be keyed by cid - * @return void - */ -function _eloqua_form_url_map(&$form, $component_list) { - foreach ($component_list as $component) { - if (empty($component['extra']['eloqua']['query_name'])) { - continue; - } - - // Check whether the value is indeed in the query - $field_param_name = $component['extra']['eloqua']['query_name']; - if (!array_key_exists($field_param_name, $_REQUEST)) { - continue; - } - - // Determine nesting level - $path = _eloqua_form_url_get_component_path($component_list, $component); - - $target_element = &$form['submitted']; - $pass = TRUE; - foreach ($path as $level) { - if (array_key_exists($level, $target_element)) { - $target_element = & $target_element[$level]; - } else { - $pass = FALSE; - continue; - } - } - - // If we found the target form element, update the default value - if ($pass) { - $conversion_method = '_eloqua_form_url_map_' . $component['type']; - if (function_exists($conversion_method)) { - $conversion_method($_REQUEST[$field_param_name], $target_element); - } else { - $target_element['#default_value'] = $_REQUEST[$field_param_name]; - } - } - // Release the Reference - unset($target_element); - } -} - -/** - * Convert URLDate to form date - * The date must be in yyyy-mm-dd format - * @param $value - * @param $element - * @return void - */ -function _eloqua_form_url_map_date($value, &$element) { - $split = explode('-', $value); - // Valdiate format - if ((count($split) != 3) - || !is_numeric($split[0]) - || !is_numeric($split[1]) - || !is_numeric($split[2])) { - return; - } - - $year = (int) $split[0]; - $month = (int) $split[1]; - $day = (int) $split[2]; - // Valdiate value - if ($year < 0 - || $year > 9999 - || !checkdate($month, $day, $year)) { - return; - } - - // Update the default value. - // THe webform #process function expects a keyed array. - $element['#default_value'] = array( - 'year' => $year, - 'month' => $month, - 'day' => $day, - ); -} - -/** - * Convert url time to form time - * The time must be a 24 hour format - * @param $value - * @param $element - * @return void - */ -function _eloqua_form_url_map_time($value, &$element) { - $split = explode(':', $value); - // Validate the format - if ( - (count($split) != 2) - || !is_numeric($split[0]) - || !is_numeric($split[1])) { - return; - } - - $split[0] = (int) $split[0]; - $split[1] = (int) $split[1]; - // Validate the ranges - if ($split[0] > 23 - || $split[1] > 59 - || $split[1] < 0 - || $split[0] < 0) { - return; - } - - $hour = $split[0]; - $minute = $split[1]; - // If the form is 12-hr, convert - if (array_key_exists('ampm', $element)) { - $ampm = ($hour >= 12) ? 'pm' : 'am'; - $element['ampm']['#default_value'] = $ampm; - $hour = ($hour > 12 || $hour == 0) ? abs($hour - 12) : $hour; - } - $element['hour']['#default_value'] = $hour; - $element['minute']['#default_value'] = $minute; -} - -/** - * Returns the nesting keys for a component - * @param $component_list - * @param $target_component - * @return array - */ -function _eloqua_form_url_get_component_path($component_list, $target_component) { - $path = array($target_component['form_key']); - $current_component = $target_component; - - // Walk up the path - while ($current_component != NULL && $current_component['pid'] != 0) { - $current_component = $component_list[$current_component['pid']]; - array_unshift($path, $current_component['form_key']); - } - return $path; -} - -// END OF FILE diff --git a/eloqua_webform/eloqua_webform.cron.inc b/eloqua_webform/eloqua_webform.cron.inc new file mode 100644 index 0000000..72ef072 --- /dev/null +++ b/eloqua_webform/eloqua_webform.cron.inc @@ -0,0 +1,321 @@ +fields('eloqua_saved_posts') + ->condition('status', ELOQUA_STATUS_NEW) + ->range(0, (int) variable_get('batch_size', 50)) + ->execute() + ->fetchAll(); + $posts = _eloqua_unserialize_data_column($result_set); + } + + // If nothnig to do, or something funky happened, bail. + if (empty($posts) || !is_array($posts)) { + return; + } + + foreach ($posts as $post) { + if (!is_array($post->data)) { + $post->data = array(); + } + + // Only pass along the accept-language header as a header. We pass the user + // agent information in the curl request below. + $original_headers = $post->data['user_headers']; + $headers = array( + 'Accept-Language' => array_key_exists('accept-language', $original_headers) ? $original_headers['accept-language'] : 'en', + ); + + // Fetch the post fields to send to Eloqua + $post_fields = _eloqua_cron_get_post_fields($post); + + // Create the Curl Request + $options = array(); + $options['post_fields'] = $post_fields; + $options['user_agent'] = array_key_exists('user-agent', $original_headers) ? $original_headers['user-agent'] : 'User Relay'; + $options['http_headers'] = $headers; + $ch = _eloqua_cron_get_curl_resource($options); + if (is_null($ch)) { + $message = t('Something went wrong with curl, unable to obtain handle. Aborting status updates.'); + $params = array(); + watchdog('eloqua', $message, $params, WATCHDOG_ERROR); + break; + } + try { + $data = curl_exec($ch); + $curl_status = curl_getinfo($ch); + $status_class = (int) floor($curl_status['http_code'] / 100); + $success = ($data) && ($status_class == 2 || $status_class == 3); + } catch (Exception $e) { + // PHP needs to implement finally + // No idea what went wrong, should just bail out of the process. + // However, log. + $message = t('Something went wrong with curl. Uncaught exception: !message'); + $params = array( + '!message' => $e->getMessage(), + ); + watchdog('eloqua', $message, $params, WATCHDOG_ERROR); + + // Throwing an error here will result in an uncaught exception. This, when it happens in + // cron, will result in the "Cron exceeding time limit" error, because it didn't end properly. + // The above will log the message to watchdog, and that's the extent of the notice that + // will be given. + // To mimic current behaviour, break will be used instead of continue. + // throw $e; + break; + } + curl_close($ch); + + // CURL + $post->data['server_post'][] = array( + 'timestamp' => time(), + 'response' => $data, + 'http_status' => $curl_status['http_code'], + ); + + // Update Post Data + if ($success) { + $post->{'status'} = ELOQUA_STATUS_UPLOADED; + } else { + $post->{'status'} = ELOQUA_STATUS_FAILED; + } + eloqua_post_update($post); + } +} + +/** + * Returns a configured Curl Resource for use + * + * @return resource + */ +function _eloqua_cron_get_curl_resource($options = array()) { + $ch = curl_init(); + if (empty($ch)) { + $message = t('Unable to open curl. Skipping message.'); + $params = array(); + watchdog('eloqua', $message, $params, WATCHDOG_ERROR); + $ch = NULL; + } + + if ($ch == NULL) { + return NULL; + } + + // Setting Options + $curl_opts = array( + CURLOPT_HEADER => 1, + CURLOPT_POST => 1, + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_CONNECTTIMEOUT => 5, // TODO: TIMEOUT ADMIN SETTING + CURLOPT_TIMEOUT => 5, // TODO: TIMEOUT ADMIN SETTING + CURLOPT_URL => 'http://now.eloqua.com/e/f2.aspx', + ); + + $option_map = array( + 'post_fields' => CURLOPT_POSTFIELDS, + 'user_agent' => CURLOPT_USERAGENT, + 'http_headers' => CURLOPT_HTTPHEADER, + ); + foreach ($option_map as $key => $curl_key) { + if (array_key_exists($key, $options)) { + $curl_opts[$curl_key] = $options[$key]; + } + } + + $status = _eloqua_set_curl_opts($ch, $curl_opts); + $error_msg = _eloqua_set_curl_get_error($status); + if (!empty($error_msg)) { + // Failed setting an option + $message = t('Unable to set curl options. Skipping message. !options.'); + $params = array( + '!options' => '
    ' . $error_msg . '
    ', + ); + watchdog('eloqua', $message, $params, WATCHDOG_ERROR); + curl_close($ch); + $ch = NULL; + } + return $ch; +} + +/** + * Handle translating our post values into what eloqua wants in terms of structure. + * + * Dates are by default handled in YYYY-mm-dd + * Times are by default handled in HH:mm + * + * @param $tree - the post tree name => value pairs + * @param $posted_values - the post tree, could be name => value pairs or index => value pairs + * @param $result - the re-structured tree that Eloqua will leverage + * @return none + */ +function _eloqua_get_submission_data($tree, $posted_values, &$result) { + foreach ($tree as $name => $value) { + // we need to expand things in fieldsets + if (is_array($value) && !in_array($value, $posted_values)) { + _eloqua_get_submission_data($value, $posted_values, $result); + } + // we need to convert multi-value fields into strings + else if (is_array($value)) { + $result[$name] = implode(',', $value); + } + // everything else is just passed along + else { + $result[$name] = $value; + } + } +} + +/** + * Remaps the post fields to be used within Eloqua + * + * @param $post_fields array + * Data to post + * @param $post array + * Post data + * @return array + * $post_fields modified to have remapped keys where defined + */ +function _eloqua_cron_remap_post_fields($post_fields, $post) { + $result = array(); + $nid = $post->data['form_post']['details']['nid']; + if (empty($nid) || !is_numeric($nid)) { + // Something is wrong with this + return $post_fields; + } + $node = node_load($nid); + $components = $node->webform['components']; + $map = array(); + if (is_array($components)) { + foreach ($components as $component) { + if (!empty($component['extra']['eloqua']['key']) + && $component['extra']['eloqua']['key'] !== $component['form_key']) { + $map[$component['form_key']] = $component['extra']['eloqua']['key']; + } + } + } + + foreach ($post_fields as $key => $value) { + if (isset($map[$key])) { + $result[$map[$key]] = $value; + } else { + $result[$key] = $value; + } + } + + return $result; +} + +/** + * Returns the cURL error for a status returned from eloqua_curl_set_opts + * + * @TODO: Theme + * @param $status array + * @return string + * Html formatted message + */ +function _eloqua_set_curl_get_error($status) { + if (!is_array($status)) { + return FALSE; + } + $messages = array(); + $message = ''; + foreach ($status as $key => $data) { + if ($data['status']) { + continue; + } + $value = var_export($data['value'], TRUE); + $messages[] = "
  • $key - Error: {$data['message']}
    Value: $value
  • "; + } + if (!empty($messages)) { + $message = ''; + } + return $message; +} + +/** + * Sets curl options en mass, checking for errors + * + * @param $ch resource + * Curl Resource + * @param $options array + * Curl settings to apply to the resource + * @return array + * Status of all the set_opt calls + * + */ +function _eloqua_set_curl_opts(&$ch, $options = array()) { + if (!is_array($options)) { + return FALSE; + } + $result = array(); + foreach ($options as $key => $value) { + $status = curl_setopt($ch, $key, $value); + $status_msg = ''; + if (!$status) { + $status_msg = curl_error($ch); + } + $result[$key] = array( + 'status' => $status, + 'value' => $value, + 'message' => $status_msg, + ); + } + return $result; +} + +/** + * Returns the post fields for the request, modified for eloqua + * + * @param $post + * @return array + */ +function _eloqua_cron_get_post_fields($post) { + // TODO: Review newest webform for exact behaviour on whether submitted_tree exists. + $form_data = !empty($post->data['form_post']['submitted_tree']) ? $post->data['form_post']['submitted_tree'] : $post->data['form_post']['submitted']; + + $post_fields = array(); + // Merge any elq values into the post to be submitted + foreach ($post->data['form_post'] as $name => $value) { + if (substr($name, 0, 3) === 'elq') { + $post_fields[$name] = $value; + } + } + + // translates the data into a Eloqua-worthy format + _eloqua_get_submission_data($form_data, $post->data['form_post']['submitted'], $post_fields); + + // Remap any fields that can't be handled by webform + $post_fields = _eloqua_cron_remap_post_fields($post_fields, $post); + return $post_fields; +} diff --git a/eloqua_webform/eloqua_webform.inc b/eloqua_webform/eloqua_webform.inc new file mode 100644 index 0000000..a39be39 --- /dev/null +++ b/eloqua_webform/eloqua_webform.inc @@ -0,0 +1,417 @@ + __FUNCTION__, '!type' => $type)), array(), WATCHDOG_DEBUG); + return NULL; + } + + //Load webform + $result = db_select('eloqua_webform') + ->fields('eloqua_webform') + ->condition('nid', $nid) + ->execute() + ->fetchAll(PDO::FETCH_OBJ); + $result = _eloqua_unserialize_data_column($result); + $webform = array_shift($result); + + if (empty($webform)) { + return NULL; + } + + // Send the object around to all of its friends + foreach (module_implements('eloqua_form_load') as $module_name) { + $method = $module_name . '_eloqua_form_load'; + $method($webform); + } + + return $webform; +} + +/** + * Creates a webform settings from the database + * + * @hook eloqua_form_create + * @param $webform object + * @return boolean + */ +function eloqua_webform_create($webform) { + if (!is_object($webform)) { + $type = gettype($webform); + watchdog('eloqua', t('Invalid argument sent to !module_name (!type).', + array('!module_name' => __FUNCTION__, '!type' => $type))); + return FALSE; + } + + $index = array( + 'nid' => $webform->{'nid'}, + ); + $fields = array( + 'form_name' => $webform->{'form_name'}, + 'is_active' => $webform->{'is_active'}, + 'data' => serialize($webform->{'data'}), + ); + + $result = _eloqua_db_insert_update($index, $fields); + + if (!$result) { + // if we were unable to create the payment plan. Can't go on. + return FALSE; + } + + foreach (module_implements('eloqua_form_create') as $module_name) { + $method = $module_name . '_eloqua_form_create'; + $method($webform); + } + + _eloqua_webform_update($webform); + return $result; +} + +/** + * Updates a webform settings from the database + * + * @hook eloqua_form_update + * @param $webform object + * @return boolean + */ +function eloqua_webform_update($webform) { + if (!is_object($webform)) { + $type = gettype($webform); + watchdog('eloqua', t('Invalid argument sent to !module_name (!type).', + array('!module_name' => __FUNCTION__, '!type' => $type)), array(), WATCHDOG_DEBUG); + return FALSE; + } + + // Can't update the obvious invalid ppid of '0'... + if (empty($webform->nid)) { + return FALSE; + } + + foreach (module_implements('eloqua_form_update') as $module_name) { + $method = $module_name . '_eloqua_form_update'; + $method($webform); + } + + // Save the result to the database + $result = _eloqua_webform_update($webform); + return $result; +} + +/** + * Deletes a webform settings object from the database + * + * @param $nid integer + * @return boolean + */ +function eloqua_webform_delete($nid) { + if (!is_numeric($nid)) { + $type = gettype($nid); + watchdog('eloqua', t('Invalid argument sent to !module_name (!type).', + array('!module_name' => __FUNCTION__, '!type' => $type)), array(), WATCHDOG_DEBUG); + return FALSE; + } + + // Can't update the obvious invalid ppid of '0'... + if (empty($nid)) { + return FALSE; + } + + foreach (module_implements('eloqua_form_delete') as $module_name) { + $method = $module_name . '_eloqua_form_delete'; + $method($nid); + } + + $result = db_delete('eloqua_webform') + ->condition('nid', $nid) + ->execute(); + $result = (is_numeric($result)) ? ($result > 0) : FALSE; + + return $result; +} + +/** + * Updates the webform object from the database + * + * @param $webform object + * @return boolean + */ +function _eloqua_webform_update($webform) { + $fields = array( + 'form_name' => $webform->{'form_name'}, + 'is_active' => $webform->{'is_active'}, + 'data' => serialize($webform->{'data'}), + ); + $result = db_update('eloqua_webform') + ->fields($fields) + ->condition('nid', $webform->{'nid'}) + ->execute(); + + $result = (is_numeric($result)) ? ($result > 0) : FALSE; + return $result; +} + +/** + * Loads a post form the database + * + * @hook eloqua_post_load ($post) + * @param $post_id int + * @return object + */ +function eloqua_post_load($post_id) { + // Get the raw result from the db + if (!is_numeric($post_id)) { + $type = gettype($post_id); + watchdog('eloqua', t('Invalid argument sent to !module_name (!type).', + array('!module_name' => __FUNCTION__, '!type' => $type)), array(), WATCHDOG_DEBUG); + return NULL; + } + + $result_set = db_select('eloqua_saved_posts') + ->condition('post_id', $post_id) + ->execute() + ->fetchAll(); + $result = _eloqua_unserialize_data_column($result_set); + $post = array_shift($result); + + if (empty($post)) { + return NULL; + } + + // Send the object around to all of its friends + foreach (module_implements('eloqua_post_load') as $module_name) { + $method = $module_name . '_eloqua_post_load'; + $method($post); + } + + return $post; +} + +/** + * Creates a post into the database + * + * @hook eloqua_post_create + * @param $post object + * @return int + * Post ID + */ +function eloqua_post_create($post) { + if (!is_object($post)) { + $type = gettype($post); + watchdog('eloqua', t('Invalid argument sent to !module_name (!type).', + array('!module_name' => __FUNCTION__, '!type' => $type))); + return FALSE; + } + + $fields = array( + 'form_id' => $post->{'form_id'}, + 'post_time' => $post->{'post_time'}, + 'status' => $post->{'status'}, + 'data' => serialize($post->{'data'}), + ); + $result = db_insert('eloqua_saved_posts') + ->fields($fields) + ->execute(); + // The result must be the post_id or we'll end up with duplicate records in + // the eloqua_saved_posts table. + if ($result === FALSE || $result === NULL) { + $result = false; + } + + if (!$result) { + // if we were unable to create the payment plan. Can't go on. + return FALSE; + } + + $post->{'post_id'} = $result; + + foreach (module_implements('eloqua_post_create') as $module_name) { + $method = $module_name . '_eloqua_post_create'; + $method($post); + } + + _eloqua_post_update($post); + return $post; +} + +/** + * Updates a post from the database + * + * @hook eloqua_post_update + * @param $post object + * @return boolean + */ +function eloqua_post_update($post) { + if (!is_object($post)) { + $type = gettype($post); + watchdog('eloqua', t('Invalid argument sent to !module_name (!type).', + array('!module_name' => __FUNCTION__, '!type' => $type)), array(), WATCHDOG_DEBUG); + return FALSE; + } + + // Can't update the obvious invalid ppid of '0'... + if (empty($post->{'post_id'})) { + return FALSE; + } + + foreach (module_implements('eloqua_post_update') as $module_name) { + $method = $module_name . '_eloqua_post_update'; + $method($post); + } + + // Save the result to the database + $result = _eloqua_post_update($post); + return $result; +} + +/** + * Deletes a post object from the database + * + * @param $post_id integer + * @return boolean + */ +function eloqua_post_delete($post_id) { + if (!is_numeric($post_id)) { + $type = gettype($post_id); + watchdog('eloqua', t('Invalid argument sent to !module_name (!type).', + array('!module_name' => __FUNCTION__, '!type' => $type)), array(), WATCHDOG_DEBUG); + return FALSE; + } + + // Can't update the obvious invalid ppid of '0'... + if (empty($post_id)) { + return FALSE; + } + + foreach (module_implements('eloqua_post_delete') as $module_name) { + $method = $module_name . '_eloqua_post_delete'; + $method($post_id); + } + + $result = db_delete('eloqua_saved_posts') + ->condition('post_id', $post_id) + ->execute(); + $result = (is_numeric($result)) ? ($result > 0) : FALSE; + + return $result; +} + +/** + * Updates the post object from the database + * + * @param $post object + * @return boolean + */ +function _eloqua_post_update($post) { + $fields = array( + 'form_id' => $post->{'form_id'}, + 'post_time' => $post->{'post_time'}, + 'status' => $post->{'status'}, + 'data' => serialize($post->{'data'}), + ); + $result = db_update('eloqua_saved_posts') + ->fields($fields) + ->condition('post_id', $post->{'post_id'}) + ->execute(); + $result = (is_numeric($result)) ? ($result > 0) : FALSE; + return $result; +} + +/** + * Goes through the result set and unserialises any 'data' columns + * + * @param $result_set + * @return array + */ +function _eloqua_unserialize_data_column($result_set) { + $result = array(); + if (!is_array($result_set)) { + $result_set = array(); + } + + foreach ($result_set as $row) { + $row->data = unserialize($row->data); + $result[] = $row; + } + + return $result; +} + +/** + * Query the Database and mimic a INSERT ... ON DUPLICATE KEY UPDATE + * + * Because there isn't a "Drupal" way to do a INSERT ... ON DUPLICATE KEY UPDATE using the + * abstraction layer they provided. + * + * Also, when using InnoDB tables, you cannot actually retrieve the last-insert-id reliably + * So, all in all, this just returns a TRUE/FALSE now, as opposed to the Drupal 6 Version + * @param $index + * @param $fields + * @return bool + */ +function _eloqua_db_insert_update($index, $fields) { + $insert_fields = $index + $fields; + $do_update = FALSE; + $result = FALSE; + try { + $result = db_insert('eloqua_webform') + ->fields($insert_fields) + ->execute() + ; + // No longer able to return the last inserted Id. + if ($result !== FALSE) { + $result = TRUE; + } + } catch (PDOException $e) { + // Constraint Error + if ($e->getCode() == '23000') { + $do_update = TRUE; + } + } + + // Attempt Update + if ($do_update) { + $query = db_update('eloqua_webform') + ->fields($fields); + foreach ($index as $key => $value) { + $query->condition($key, $value); + } + + try { + $result = $query->execute(); + } catch (PDOException $e) { + return FALSE; + } + + // Rows Updated + if (is_numeric($result)) { + $result = (bool) $result; + } else { + // Probably failed. + $result = FALSE; + } + } + return $result; +} diff --git a/eloqua_webform/eloqua_webform.info b/eloqua_webform/eloqua_webform.info new file mode 100644 index 0000000..ce0c6d4 --- /dev/null +++ b/eloqua_webform/eloqua_webform.info @@ -0,0 +1,6 @@ +name = Eloqua Webform Integration +description = Module to allow Eloqua integration with Drupal webforms +package = "Eloqua" +core = 7.x +dependencies[] = eloqua +dependencies[] = webform \ No newline at end of file diff --git a/eloqua_webform/eloqua_webform.install b/eloqua_webform/eloqua_webform.install new file mode 100644 index 0000000..2d7d526 --- /dev/null +++ b/eloqua_webform/eloqua_webform.install @@ -0,0 +1,130 @@ + t('Webform Settings'), + 'primary key' => array('post_id'), + 'unique keys' => array(), + 'indexes' => array( + 'eloqua_saved_posts' . '_form_id' => array('form_id'), + ), + 'fields' => array( + 'form_id' => array( + // Associated Form Id + 'description' => 'Form ID that generated this post', + 'type' => 'varchar', + 'size' => 'normal', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + ), + 'post_id' => array( + // Serial Number + 'type' => 'serial', + 'size' => 'normal', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'status' => array( + // Form Posted Status + 'description' => 'Eloqua option name', + 'type' => 'varchar', + 'size' => 'normal', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + ), + 'post_time' => array( + // Post Time + 'description' => 'Node Id', + 'type' => 'int', + 'size' => 'normal', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'data' => array( + // Form Contents + 'description' => 'Extra data to be stored with the field', + 'type' => 'text', + 'size' => 'normal', /* 16KB in mySql */ + 'not null' => TRUE, + ), + ), + ); + + $schema['eloqua_webform'] = array( + 'description' => t('Webform Settings'), + 'primary key' => array('nid'), + 'unique keys' => array(), + 'indexes' => array( + 'eloqua_webform' . '_active' => array('is_active'), + ), + 'fields' => array( + 'nid' => array( + // Webform Node Id + 'description' => 'Node Id', + 'type' => 'int', + 'size' => 'normal', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'form_name' => array( + // Eloqua Form Name + 'description' => 'Eloqua form name', + 'type' => 'varchar', + 'size' => 'normal', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ), + 'is_active' => array( + // Is this webform eloqua enabled + 'description' => 'Whether this form is eloqua active', + 'type' => 'int', + 'size' => 'tiny', + 'not null' => TRUE, + 'default' => 0, + 'unsigned' => TRUE, + ), + 'data' => array( + 'description' => 'Extra data to be stored with the field', + 'type' => 'text', + 'size' => 'normal', /* 16KB in mySql */ + 'not null' => TRUE, + ), + ), + ); + return $schema; +} + +/** + * Implements hook_install(). + */ +function eloqua_webform_install() { + variable_set('eloqua_webform_process_submit', 0); +} + +/** + * Implements hook_uninstall(). + */ +function eloqua_webform_uninstall() { + +} + +/** + * Add new variable + */ +function eloqua_webform_update_7000() { + variable_set('eloqua_webform_process_submit', 0); +} \ No newline at end of file diff --git a/eloqua_webform/eloqua_webform.module b/eloqua_webform/eloqua_webform.module new file mode 100644 index 0000000..f9f8e8d --- /dev/null +++ b/eloqua_webform/eloqua_webform.module @@ -0,0 +1,492 @@ + 'Eloqua', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('eloqua_webform_configure_form', 1), + 'access callback' => 'node_access', + 'access arguments' => array('update', 1), + 'weight' => 1, + 'type' => MENU_LOCAL_TASK, + ); + return $items; +} + +/** + * Implements hook_cron() + */ +function eloqua_webform_cron() { + _eloqua_cron(); +} + +/** + * Implements hook_form_FORM_ID_alter(). + * This function is reponsible for adding the form-id mapping values to the Component add/edit page + * Note: this isn't used for the fieldset type. + */ +function eloqua_webform_form_webform_component_edit_form_alter(&$form, $form_state) { + if (!array_key_exists('build_info', $form_state) + || !array_key_exists('args', $form_state['build_info']) + || !array_key_exists(1, $form_state['build_info']['args'])) { + return; + } + + $component = $form_state['build_info']['args'][1]; + + // Create the fieldset + $form['eloqua'] = array( + '#type' => 'fieldset', + '#title' => t('Eloqua'), + '#tree' => TRUE, + ); + + // Check the field type. Some do not make sense as having a value to post to Eloqua + $component_type = $component['type']; + if (!in_array($component_type, array('fieldset', 'pagebreak'))) { + // todo: Get this working + $form['eloqua']['msg'] = array( + '#type' => 'markup', + '#value' => t('This component type is not compatible with the Eloqua Module.'), + ); + return; + } + + $form['eloqua']['key'] = array( + '#type' => 'textfield', + '#default_value' => isset($component['extra']['eloqua']['key']) ? $component['extra']['eloqua']['key'] : '', + '#title' => t('Eloqua Field Name'), + '#description' => t('The eloqua field name if it contains symbols or characters not compatible with the defaults above. Leave empty to use the default above. Can not contain spaces.'), + '#weight' => 0, + ); + + $form['eloqua']['query_name'] = array( + '#type' => 'textfield', + '#default_value' => isset($component['extra']['eloqua']['query_name']) ? $component['extra']['eloqua']['query_name'] : '', + '#description' => t('If this value can be populated by a query parameter, the key that is to be used. Can only contain the characters a-z A-Z 0-9 and _.'), + '#title' => t('Request Parameter Key'), + ); + + $form['#validate'][] = 'eloqua_webform_component_edit_form_validate'; +} + +/** + * Implements hook_webform_component_presave(). + * + * @param $component + * @return void + */ +function eloqua_webform_webform_component_presave(&$component) { + if (array_key_exists('eloqua', $component) && isset($component['eloqua'])) { + $component['extra']['eloqua'] = $component['eloqua']; + } +} + +/** + * Validates the eloqua field + * + * @todo Make this less Western Centric. Get parameters should be able to accept all Unicode code-points + * @param $form + * @param $form_state + * @return unknown_type + */ +function eloqua_webform_component_edit_form_validate($form, $form_state) { + if (preg_match('![\x00-\x20 ]!', $form_state['values']['eloqua']['key'])) { + form_set_error('eloqua][key', t('The eloqua field key %field_key is invalid. Please do not include spaces.', array('%field_key' => $form_state['values']['eloqua']['key']))); + } + $values = $form_state['values']; + if (!empty($values['eloqua']['query_name'])) { + if (!preg_match('/^[a-zA-Z0-9_]+$/', $values['eloqua']['query_name'])) { + form_set_error('eloqua][query_name', t('The query name provided can only contain the characters a-z A-Z 0-9 and underscore (_).')); + } + } +} + +/** + * Eloqua Webform Configure Form + * + * @see eloqua_webform_configure_form_submit() + * @see eloqua_webform_configure_form_validate() + */ +function eloqua_webform_configure_form($form, $form_state, $webform_node) { + // Load the form options from the database if available + $settings = new stdClass(); + + if (!empty($webform_node->nid)) { + $settings = eloqua_webform_load($webform_node->nid); + } + + // Apply default settings + if (!isset($settings->{'is_active'})) { + $settings->{'is_active'} = 0; + } + if (!isset($settings->{'form_name'})) { + $settings->{'form_name'} = ''; + } + if (!isset($settings->{'data'})) { + $settings->{'data'} = new stdClass(); + } + if (!isset($settings->{'data'}->process_every_page)) { + $settings->{'data'}->process_every_page = 0; + } + + $form['eloqua'] = array( + '#type' => 'fieldset', + '#description' => t('Eloqua Integration Options'), + '#title' => t('Eloqua'), + '#collapsible' => TRUE, + '#collapsed' => FALSE, + 'elq_enabled' => array( + '#type' => 'checkbox', + '#default_value' => $settings->{'is_active'}, + '#description' => t('Whether this form is eloqua enabled'), + '#title' => t('Enabled'), + ), + 'elq_form_name' => array( + '#type' => 'textfield', + '#default_value' => $settings->{'form_name'}, + '#description' => t('The form name as it is defined in Eloqua'), + '#title' => t('Eloqua Form Name'), + ), + 'elq_process_every_page' => array( + '#type' => 'checkbox', + '#default_value' => $settings->{'data'}->process_every_page, + '#description' => t('Process every page submit on a multi-page form, instead of one combined submit upon completion.'), + '#title' => t('Process subpages seperately'), + ), + ); + + $form['nid'] = array( + '#type' => 'hidden', + '#value' => $webform_node->nid, + ); + + $form['save'] = array( + '#type' => 'submit', + '#submit' => array('eloqua_webform_configure_form_submit'), + '#value' => t('Save'), + ); + + $form['#validate'][] = 'eloqua_webform_configure_form_validate'; + + return $form; +} + +/** + * Form Validation Function + * + * @param $form + * @param $form_state + * @return unknown_type + */ +function eloqua_webform_configure_form_validate($form, $form_state) { + $values = $form_state['values']; + if ($values['elq_enabled']) { + if (empty($values['elq_form_name'])) { + form_set_error('elq_form_name', t('The Eloqua Form Name must be set if the form is to be eloqua-enabled.' + . ' This can only contain the characters a-zA-Z and _')); + } + } +} + +/** + * Submit handler for webform_configure_form + * + * @param $form array + * @param $form_state array + */ +function eloqua_webform_configure_form_submit($form, &$form_state) { + // On a new form + $nid = $form_state['values']['nid']; + $values = $form_state['values']; + $webform = new stdClass(); + $webform->{'nid'} = $nid; + $webform->{'is_active'} = $values['elq_enabled']; + $webform->{'form_name'} = $values['elq_form_name']; + $webform->{'data'} = (object) array( + 'process_every_page' => $values['elq_process_every_page'], + ); + + eloqua_webform_create($webform); +} + +/** + * Implements hook_form_alter(). + */ +function eloqua_webform_form_alter(&$form, $form_state, $form_id) { + + // Add setting to allow for immediate posting + if ($form_id == 'eloqua_admin_form'){ + $form['webform'] = array( + '#type' => 'fieldset', + '#title' => t('Webform Settings'), + '#description' => t('Webform / Eloqua integration.'), + 'eloqua_webform_process_submit' => array( + '#type' => 'checkbox', + '#title' => 'Immediately process submissions?', + '#default_value' => variable_get('eloqua_webform_process_submit', ''), + ), + ); + } + + // We only want to edit client versions of the webform. If it's anything else + // simply return. + if (strpos($form_id, 'webform_client_form') !== 0) { + return; + } + + if (!array_key_exists('#node', $form) || !is_object($form['#node']) || empty($form['#node']->nid)) { + return; + } + + $nid = $form['#node']->nid; + $form_settings = (array) eloqua_webform_load($nid); + + // Check to see if the form exists + if (empty($form_settings) || (!$form_settings['is_active'])) { + return; + } + + // Check for QUERY parameter mappings to assign default values + _eloqua_form_url_map($form, $form['#node']->webform['components']); + + // Just shove everything into a container + $form['#submit'][] = 'eloqua_webform_submit'; + $form['eloqua'] = array(); + $process_every_page = + (is_object($form_settings[data]) && isset($form_settings[data]->process_every_page)) ? $form_settings[data]->process_every_page : FALSE; + $form['eloqua']['process_every_page'] = array( + '#type' => 'value', + '#value' => $process_every_page, + ); + $form['eloqua']['elqFormName'] = array( + '#type' => 'hidden', + '#default_value' => (string) $form_settings['form_name'], + ); + $form['eloqua']['elqSiteId'] = array( + '#type' => 'hidden', + '#default_value' => variable_get('eloqua_site_id', 0), + ); + $form['eloqua']['elqDefaultTargetURL'] = array( + '#type' => 'hidden', + '#default_value' => '', + ); + $form['eloqua']['elqPost'] = array( + '#type' => 'hidden', + '#default_value' => '', + ); + $form['eloqua']['elqCustomerGUID'] = array( + '#type' => 'hidden', + '#default_value' => '', + ); + // Drupal 7 doesn't automatically give hidden elements an #id + $form['eloqua']['elqCustomerGUID']['#attributes']['id'] = 'edit-elqCustomerGUID'; + $form['eloqua']['elqCookieWrite'] = array( + '#type' => 'hidden', + '#default_value' => 0, + ); + + drupal_add_js('var timerId = null, timeout = 5;', 'inline'); + + $initScript = + 'function WaitUntilCustomerGUIDIsRetrieved() { + if (!!(timerId)) { + if (timeout == 0) { + return; + } + if (typeof this.GetElqCustomerGUID === \'function\') { + document.forms["' . check_plain($form_settings['form_name']) . '"].elements["elqCustomerGUID"].value = GetElqCustomerGUID(); + return; + } + timeout -= 1; + } + timerId = setTimeout("WaitUntilCustomerGUIDIsRetrieved()", 500); + return; + } + + window.onload = WaitUntilCustomerGUIDIsRetrieved; + _elqQ.push([\'elqGetCustomerGUID\']);'; + + drupal_add_js($initScript, 'inline'); + $user_headers = _eloqua_webform_get_user_headers(); + + $form['eloqua']['user_headers'] = array( + '#type' => 'hidden', + '#default_value' => serialize($user_headers), + ); + + // Hooks that don't need to load up all the checks to see if said form + // is eloqua_enabled + foreach (module_implements('eloqua_form_alter') as $module_name) { + $method = $module_name . '_eloqua_form_alter'; + $method($form, $form_state, $form_id); + } +} + +/** + * Returns the user headers filtered for types that shouldn't be needed + * + * This function currently is Apache module specific + * @return array + */ +function _eloqua_webform_get_user_headers() { + // Getting the request headers only exists in apache + if (!function_exists('apache_request_headers')) { + return array(); + } + + $headers = apache_request_headers(); + + // Check to see if the function succeeded or not + if (!$headers) { + return array(); + } + + $filter = array( + 'host', + 'cookie', + ); + + $result = array(); + foreach ($headers as $key => $value) { + if (!in_array(strtolower($key), $filter)) { + $result[strtolower($key)] = $value; + } + } + return $result; +} + +/** + * Submit handler for the webform + * + * @param $form array + * @param $form_state array + */ +function eloqua_webform_submit($form, $form_state) { + // Only process upon the final page submission, unless otherwise told + $process_every_page = $form_state['values']['process_every_page']; + if (!$process_every_page) { + if (!array_key_exists('webform_completed', $form_state) || !$form_state['webform_completed']) { + return; + } + } + + $values = array(); + $values['form_post'] = $form_state['values']; + $values['user_headers'] = unserialize($form_state['values']['user_headers']); + // Check for serialization errors + if ($values['user_headers'] == FALSE) { + $values['user_headers'] = array(); + } + unset($values['form_post']['user_headers']); + + // Save the values and essential information to the database + $info = new stdClass(); + $info->{'form_id'} = $form_state['values']['form_id']; + $info->{'post_time'} = time(); + $info->{'status'} = ELOQUA_STATUS_NEW; + $info->{'data'} = $values; + + $post = eloqua_post_create($info); + + // Do we want to process immediately? + if (variable_get('eloqua_webform_process_submit', 0)){ + _eloqua_cron($post); + } + + if (isset($post->post_id)) { + $message = t('Unable to save submitted entry.
    !data
    '); + $vars = array('!data' => var_export($info, TRUE)); + watchdog('eloqua', $message, $vars, WATCHDOG_NOTICE); + } +} + +/** + * Maps the url parameters to the associated form fields + * + * @param $form + * @param $component_list + * The components must be keyed by cid + * @return void + */ +function _eloqua_form_url_map(&$form, $component_list) { + foreach ($component_list as $component) { + if (empty($component['extra']['eloqua']['query_name'])) { + continue; + } + + // Check whether the value is indeed in the query + $field_param_name = $component['extra']['eloqua']['query_name']; + if (!array_key_exists($field_param_name, $_REQUEST)) { + continue; + } + + // Determine nesting level + $path = _eloqua_form_url_get_component_path($component_list, $component); + + $target_element = &$form['submitted']; + $pass = TRUE; + foreach ($path as $level) { + if (array_key_exists($level, $target_element)) { + $target_element = & $target_element[$level]; + } else { + $pass = FALSE; + continue; + } + } + + // If we found the target form element, update the default value + if ($pass) { + $conversion_method = '_eloqua_form_url_map_' . $component['type']; + if (function_exists($conversion_method)) { + $conversion_method($_REQUEST[$field_param_name], $target_element); + } else { + $target_element['#default_value'] = $_REQUEST[$field_param_name]; + } + } + // Release the Reference + unset($target_element); + } +} + +/** + * Returns the nesting keys for a component + * + * @param $component_list + * @param $target_component + * @return array + */ +function _eloqua_form_url_get_component_path($component_list, $target_component) { + $path = array($target_component['form_key']); + $current_component = $target_component; + + // Walk up the path + while ($current_component != NULL && $current_component['pid'] != 0) { + $current_component = $component_list[$current_component['pid']]; + array_unshift($path, $current_component['form_key']); + } + return $path; +} \ No newline at end of file