You are here

asin.module in Amazon Product Advertisement API 7.2

Same filename and directory in other branches
  1. 6 asin/asin.module
  2. 7 asin/asin.module

Defines a field type for referencing an Amazon product.

File

asin/asin.module
View source
<?php

/**
 * @file
 * Defines a field type for referencing an Amazon product.
 */

/**
 * Implementation of hook_menu().
 */
function asin_menu() {
  $items = array();
  $items['asin/autocomplete/%/%/%'] = array(
    'page callback' => 'asin_autocomplete_callback',
    'page arguments' => array(
      2,
      3,
      4,
    ),
    'access arguments' => array(
      'access content',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implementation of hook_field_info().
 */
function asin_field_info() {
  return array(
    'asin' => array(
      'label' => t('Amazon item'),
      'description' => t('Store the id of a product listed on Amazon.com.'),
      'default_widget' => 'asin_text',
      'default_formatter' => 'asin_default',
      'default_token_formatter' => 'asin_plain',
    ),
  );
}

///**

// * @todo: This is the leftover from D6 hook_field. Seems that there's some
// * views stuff yet to be done?
// */

//function asin_field_settings($op, $field) {

//// TODO: Views data stuff

//      $data = content_views_field_views_data($field);
//      $db_info = content_database_info($field);
//      $table_alias = content_views_tablename($field);
//
//      // Filter: Add a 'many to one' filter.
//      $copy = $data[$table_alias][$field['field_name'] . '_asin'];
//      $copy['title'] = t('@label (!name) - Allowed values', array('@label' => $field['widget']['label'], '!name' => $field['field_name']));
//      $copy['filter']['handler'] = 'views_handler_filter_many_to_one';
//      unset($copy['field'], $copy['argument'], $copy['sort']);
//      $data[$table_alias][$field['field_name'] . '_value_many_to_one'] = $copy;
//      // Argument : swap the handler to the 'many to one' operator.
//      $data[$table_alias][$field['field_name'] . '_value']['argument']['handler'] = 'views_handler_argument_many_to_one';
//
//      // Add a relationship for related node.
//      $data[$table_alias][$field['field_name'] . '_asin']['relationship'] = array(
//        'base' => 'amazon_item',
//        'field' => $db_info['columns']['asin']['column'],
//        'handler' => 'views_handler_relationship',
//      );
//      return $data;

//}

/**
 * Trim spaces from the front/back of each ASIN in an array.
 *
 * @param $items
 *   Array of ASINS
 * @return
 *   Same array of ASINs, trimmed
 */
function _asin_trim_items(&$items) {
  foreach ($items as $delta => &$item) {
    $item = trim($item);
  }
}

/**
 * Implements of hook_field_is_empty().
 */
function asin_field_is_empty($item, $field) {
  if (empty($item['asin'])) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Implementation of hook_field_formatter_info().
 */
function asin_field_formatter_info() {
  return array(
    'asin_default' => array(
      'label' => t('Thumbnail with title'),
      'field types' => array(
        'asin',
      ),
    ),
    'asin_default_gallery' => array(
      'label' => t('Gallery with title'),
      'field types' => array(
        'asin',
      ),
    ),
    'asin_details' => array(
      'label' => t('Thumbnail with details'),
      'field types' => array(
        'asin',
      ),
    ),
    'asin_details_gallery' => array(
      'label' => t('Gallery with details'),
      'field types' => array(
        'asin',
      ),
    ),
    'asin_thumbnail' => array(
      'label' => t('Thumbnail image'),
      'field types' => array(
        'asin',
      ),
    ),
    'asin_thumbnail_gallery' => array(
      'label' => t('Thumbnail Gallery Image(s)'),
      'field types' => array(
        'asin',
      ),
    ),
    'asin_medium' => array(
      'label' => t('Medium image'),
      'field types' => array(
        'asin',
      ),
    ),
    'asin_medium_gallery' => array(
      'label' => t('Medium Gallery Image(s)'),
      'field types' => array(
        'asin',
      ),
    ),
    'asin_large' => array(
      'label' => t('Large image'),
      'field types' => array(
        'asin',
      ),
    ),
    'asin_large_gallery' => array(
      'label' => t('Large Gallery Image(s)'),
      'field types' => array(
        'asin',
      ),
    ),
    'asin_inline' => array(
      'label' => t('Link to item'),
      'field types' => array(
        'asin',
      ),
    ),
    'asin_plain' => array(
      'label' => t('ASIN as plain text'),
      'field types' => array(
        'asin',
      ),
    ),
  );
}

/**
 * Implements hook_field_formatter_view().
 */
function asin_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();
  if (isset($instance['widget']['settings']['widget_settings']['locale'])) {
    $locale = $instance['widget']['settings']['widget_settings']['locale'];
  }
  else {
    $locale = variable_get('amazon_default_locale');
  }
  foreach ($items as $delta => $value) {
    $asin = trim($value['asin']);
    if (!empty($asin)) {

      // If :: Check to see if the field format is set to "ASIN as plain text"
      // 1 - Return plain Amazon Product ASIN
      // 0 - Return themed Amazon Product Item
      if ($display['type'] == 'asin_plain') {
        $element[$delta] = array(
          '#markup' => $asin,
        );
      }
      else {

        // Lookup :: Search the amazon_item table or request Amazon API information
        $lookup = amazon_item_lookup($asin, FALSE, $locale);

        // If :: Check to see if Amazon Product was returned succesfully
        if (!empty($lookup) && ($item = $lookup[$asin])) {

          // // TODO: kill off amazon_inline_item. There's no reason for it to clutter the earth.
          $theme_function = $display['type'] == 'asin_inline' ? 'amazon_inline_item' : 'amazon_item';

          // Trim the 'asin_' prefix from the formatter machine name before
          // passing it to the theme function.
          if (strpos($display['type'], 'asin_') === 0) {
            $style = substr($display['type'], 5);
          }
          $elements = array(
            '#theme' => $theme_function,
            '#style' => $style,
            '#item' => $item,
          );
          $element[$delta] = array(
            '#markup' => drupal_render($elements),
          );
        }
      }
    }
  }
  return $element;
}

/**
 * Implementation of hook_field_widget_info().
 *
 */
function asin_field_widget_info() {
  return array(
    'asin_text' => array(
      'label' => t('Amazon ASIN Text field'),
      'field types' => array(
        'asin',
      ),
    ),
    'asin_autocomplete' => array(
      'label' => t('Product name autocomplete'),
      'field types' => array(
        'asin',
      ),
    ),
  );
}

/**
 * Implements hook_field_widget_settings_form().
 */
function asin_field_widget_settings_form($field, $instance) {
  $widget = $instance['widget'];
  $settings = $widget['settings'];
  $cache = amazon_data_cache();
  $locale_options = array(
    '' => '-- Select --',
  );
  foreach ($cache['locales'] as $locale => $data) {
    if (variable_get('amazon_locale_' . $locale . '_associate_id', '')) {
      $locale_options[$locale] = $data['name'];
    }
  }
  $form['widget_settings'] = array(
    '#prefix' => '<div id="asin-autocomplete-widget-settings">',
    '#suffix' => '</div>',
  );
  $form['widget_settings']['locale'] = array(
    '#title' => t('Amazon Locale'),
    '#type' => 'select',
    '#options' => $locale_options,
    '#default_value' => isset($settings['widget_settings']['locale']) ? $settings['widget_settings']['locale'] : '',
    '#required' => TRUE,
    '#ajax' => array(
      'wrapper' => 'asin-autocomplete-widget-settings',
      'callback' => 'asin_widget_locale_product_group',
      'method' => 'replace',
      'effect' => 'fade',
    ),
  );

  // Default value for the dropdown.
  $productgroup_options = array(
    'All' => 'All indexes',
  );
  if (isset($settings['widget_settings']['locale'])) {
    if (isset($cache['locales'][$settings['widget_settings']['locale']]['search_indexes'])) {

      // Populate list.
      $productgroup_options = $cache['locales'][$settings['widget_settings']['locale']]['search_indexes'];
    }
  }
  if ($instance['widget']['type'] == 'asin_autocomplete') {
    $form['widget_settings']['productgroup'] = array(
      '#title' => t('Search Index'),
      '#type' => 'select',
      '#options' => $productgroup_options,
      '#default_value' => isset($settings['widget_settings']['productgroup']) ? $settings['widget_settings']['productgroup'] : 'All',
    );
  }
  return $form;
}

/**
 * AJAX callback for widget settings form.
 */
function asin_widget_locale_product_group($form, &$form_state) {
  $cache = amazon_data_cache();
  $form['instance']['widget']['settings']['widget_settings']['productgroup']['#options'] = $cache['locales'][$form_state['values']['instance']['widget']['settings']['widget_settings']['locale']]['search_indexes'];
  return render($form['instance']['widget']['settings']['widget_settings']);
}

/**
 * Implements hook_field_widget_form().
 *
 */
function asin_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $base) {
  $element = $base;
  $element['asin'] = $base + array(
    '#type' => 'textfield',
    '#default_value' => !empty($items[$delta]['asin']) ? $items[$delta]['asin'] : '',
  );
  switch ($instance['widget']['type']) {
    case 'asin_text':
      $element['asin']['#size'] = 25;
      $element['asin']['#element_validate'] = array(
        'asin_field_widget_element_validate',
      );
      break;
    case 'asin_autocomplete':
      $element['asin']['#autocomplete_path'] = 'asin/autocomplete/' . $instance['entity_type'] . '/' . $instance['bundle'] . '/' . $field['field_name'];
      $element['asin']['#value_callback'] = 'asin_autocomplete_value';
      $element['asin']['#maxlength'] = 256;
      $element['asin']['#element_validate'][] = 'asin_autocomplete_validate';
      break;
  }
  $form_state['langcode'] = $langcode;
  return $element;
}

/**
 * Text callback for the asin_text widget.
 *
 * Checks to see if we can look up an ASIN, URL, or ISBN-13 using the
 * provided text. If we can, it's OK and we turn it into an ASIN.
 * Otherwise, flag as error.
 */
function asin_field_widget_element_validate($element, &$form_state) {
  $field_name = $element['#field_name'];
  $langcode = $form_state['langcode'];
  foreach ($form_state['values'][$field_name][$langcode] as $delta => &$item) {
    $locale = NULL;
    if (isset($form_state['field'][$field_name][LANGUAGE_NONE]['instance']['widget']['settings']['widget_settings']['locale'])) {
      $locale = $form_state['field'][$field_name][LANGUAGE_NONE]['instance']['widget']['settings']['widget_settings']['locale'];
    }
    $asin = trim(amazon_convert_to_asin($item['asin']));
    if (!empty($asin) && is_numeric($delta)) {
      $results = amazon_item_lookup(array(
        $asin,
      ), FALSE, $locale);
      if (empty($results)) {
        form_set_error("{$field_name}][{$langcode}][{$delta}][asin", t('No Amazon product with the ASIN "%id" could be located in %locale.', array(
          '%id' => $asin,
          '%locale' => $locale,
        )));
      }
      else {
        $item['asin'] = (string) key($results);
      }
    }
  }
}

/**
 * Autocomplete callback for the asin_autocomplete widget.
 */
function asin_autocomplete_callback($entity_type, $bundle, $field_name, $string = '') {
  $instance = field_info_instance($entity_type, $field_name, $bundle);
  $items = $matches = array();
  $cleanstring = trim(preg_replace('#[^\\p{L}\\p{N}]+#u', ' ', $string));

  // Search Amazon.
  $parameters = array(
    'ResponseGroup' => 'Small',
    'SearchIndex' => $instance['widget']['settings']['widget_settings']['productgroup'],
    'Keywords' => $cleanstring,
  );
  if (isset($instance['widget']['settings']['widget_settings']['locale'])) {
    $locale = $instance['widget']['settings']['widget_settings']['locale'];
  }
  else {
    $locale = variable_get('amazon_default_locale', 'US');
  }
  $results = amazon_http_request('ItemSearch', $parameters, $locale);

  // Process the results.
  foreach ($results->Items->Item as $xml) {
    $items[(string) $xml->ASIN] = (string) $xml->ItemAttributes->Title . ' (' . $xml->ItemAttributes->ProductGroup . ')';
  }

  // Create our response.
  foreach ($items as $asin => $title) {

    // Add a class wrapper for a few required CSS overrides.
    $matches[$title . ' [asin:' . $asin . ']'] = '<div class="reference-autocomplete">' . $title . '</div>';
  }
  drupal_json_output($matches);
}

/**
 * Validate the autocomplete widget.
 *
 * This corrects the value (we want just the ASIN, not the whole title).
 */
function asin_autocomplete_validate($element, &$form_state) {
  $value = $element['#value'];
  $asin = NULL;
  if (preg_match('!.*\\[asin\\:([a-zA-Z0-9]*)\\]!', $value, $matches)) {
    $asin = $matches[1];
  }
  else {
    $asin = $value;
  }
  form_set_value($element, $asin, $form_state);
}

/**
 * Implements hook_field_views_data().
 *
 * Through this hook we're given the chance to change the views schema
 * data for the asin field. The primary thing to be done is to add a join
 * on the ASIN type to the amazon_item views base stuff.
 */
function asin_field_views_data($field) {
  $data = field_views_field_default_views_data($field);
  foreach ($data as $table_name => $table_data) {
    foreach ($table_data as $field_name => $field_data) {

      // Check for fieldapi value fields.
      if (isset($field_data['filter']['field_name'])) {
        $data[$table_name][$field_name]['relationship'] = array(
          'handler' => 'views_handler_relationship',
          'base' => 'amazon_item',
          'base_field' => 'asin',
          'label' => t('ASIN from !field_name', array(
            '!field_name' => $field['field_name'],
          )),
        );
      }
    }
  }
  return $data;
}

/**
 * Implementation of hook_feeds_node_processor_targets_alter().
 */
function asin_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) {
  foreach (field_info_instances($entity_type, $bundle_name) as $name => $instance) {
    $info = field_info_field($name);
    if ($info['type'] == 'asin') {
      $targets[$name] = array(
        'name' => $bundle_name . ':' . $name,
        'callback' => 'asin_feeds_set_target',
        'description' => t('The %name field of the %bundle', array(
          '%name' => $name,
          '%bundle' => $bundle_name,
        )),
        'real_target' => $name,
      );
    }
  }
}

/**
 * Callback for mapping. Here is where the actual mapping happens.
 *
 * When the callback is invoked, $target contains the name of the field the
 * user has decided to map to and $value contains the value of the feed item
 * element the user has picked as a source.
 *
 * @see my_module_set_target() in feeds.api.php
 */
function asin_feeds_set_target($source, $entity, $target, $value) {
  if (!isset($value)) {
    return;
  }

  // Handle non-multiple value fields.
  if (!is_array($value)) {
    $value = array(
      $value,
    );
  }

  // Iterate over all values.
  $i = 0;
  $info = field_info_field($target);
  $field_name = $target;
  foreach ($value as $v) {
    $field[LANGUAGE_NONE][$i]['asin'] = $v;
    if ($info['cardinality'] == 1) {
      break;
    }
    $i++;
  }
  $entity->{$field_name} = $field;
}

/**
 * Implements hook_devel_generate().
 * Callback for populating ASIN fields with devel_generate module.
 *
 * Picks a random computer-related book.
 */
function asin_devel_generate($object, $field, $instance, $bundle) {
  if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_CUSTOM) {
    return devel_generate_multiple('_asin_devel_generate', $object, $field, $instance, $bundle);
  }
  else {
    return _asin_devel_generate($object, $field, $instance, $bundle);
  }
}

/**
 * Utility function that actually provides the values for asin_devel_generate().
 *
 * You can change the SearchIndex and Keywords used for the search by changing
 * the variables amazon_devel_generate_search_index and
 * amazon_devel_generate_keywords.
 */
function _asin_devel_generate($object, $field, $instance, $bundle) {
  $search_index = variable_get('amazon_devel_generate_search_index', 'All');
  $keywords_picker = explode(',', variable_get('asin_devel_generate_keywords', 'computer'));
  $keywords_picker = array_flip($keywords_picker);
  $asins = variable_get('amazon_devel_generate_asins', array());
  $amazon_item_page = variable_get('amazon_devel_generate_item_page', 1);
  $locale = variable_get('amazon_default_locale', 'US');
  if (empty($asins)) {
    $parameters = array(
      'ResponseGroup' => 'ItemAttributes,EditorialReview,Images',
      'SearchIndex' => $search_index,
      'Keywords' => array_rand($keywords_picker, 1),
      'ItemPage' => $amazon_item_page % 10,
    );
    $amazon_item_page++;
    $results = amazon_http_request('ItemSearch', $parameters, $locale);
    foreach ($results->Items->Item as $xml) {
      $item = amazon_item_clean_xml($xml);
      amazon_item_insert($item);
      $asins[] = $item['asin'];
    }
  }
  $field = array();

  //$field['asin'] = array_shift($asins);
  $asin = array_shift($asins);
  $field['asin'] = $asin;
  variable_set('amazon_devel_generate_asins', $asins);
  variable_set('amazon_devel_generate_item_page', $amazon_item_page);
  return $field;
}

/**
 * Allow selection of the SearchIndex and Keywords to be used
 */
function asin_form_devel_generate_content_form_alter(&$form, $form_state) {
  $form['asin_options'] = array(
    '#type' => 'fieldset',
    '#title' => t('Amazon ASIN Field Configuration'),
    '#expanded' => FALSE,
  );
  $form['asin_options']['asin_devel_generate_search_index'] = array(
    '#type' => 'select',
    '#title' => t('Amazon SearchIndex'),
    '#options' => drupal_map_assoc(array(
      'All',
      'Books',
      'DVD',
      'Electronics',
      'Kitchen',
      'Software',
    )),
    '#default_value' => variable_get('asin_devel_generate_search_index', 'All'),
  );
  $form['asin_options']['asin_devel_generate_keywords'] = array(
    '#type' => 'textfield',
    '#title' => t('Amazon.com search keywords for ASIN fields'),
    '#description' => t('Comma-separated list of keywords to be used in search to populate Amazon module asin fields'),
    '#default_value' => variable_get('asin_devel_generate_keywords', 'computers'),
  );
  $form['submit']['#weight'] = 99;
  array_unshift($form['#submit'], 'asin_devel_generate_set_values');
}
function asin_devel_generate_set_values($form, &$form_state) {
  variable_set('asin_devel_generate_keywords', $form_state['values']['asin_devel_generate_keywords']);
  variable_set('asin_devel_generate_search_index', $form_state['values']['asin_devel_generate_search_index']);
}

/**
 * Implements hook_preprocess_field().
 */
function asin_preprocess_field(&$variables, $hook) {

  // Add locale class to the Asin fields.
  if ($variables['element']['#field_type'] == 'asin') {
    $field_name = $variables['element']['#field_name'];
    $bundle = $variables['element']['#bundle'];
    $entity_type = $variables['element']['#entity_type'];
    $field_instance = field_info_instance($entity_type, $field_name, $bundle);
    if (isset($field_instance['widget']['settings']['widget_settings']['locale'])) {
      $locale = $field_instance['widget']['settings']['widget_settings']['locale'];
    }
    else {
      $locale = variable_get('amazon_default_locale');
    }
    $variables['classes_array'][] = 'locale-' . strtolower($locale);
  }
}

Functions

Namesort descending Description
asin_autocomplete_callback Autocomplete callback for the asin_autocomplete widget.
asin_autocomplete_validate Validate the autocomplete widget.
asin_devel_generate Implements hook_devel_generate(). Callback for populating ASIN fields with devel_generate module.
asin_devel_generate_set_values
asin_feeds_processor_targets_alter Implementation of hook_feeds_node_processor_targets_alter().
asin_feeds_set_target Callback for mapping. Here is where the actual mapping happens.
asin_field_formatter_info Implementation of hook_field_formatter_info().
asin_field_formatter_view Implements hook_field_formatter_view().
asin_field_info Implementation of hook_field_info().
asin_field_is_empty Implements of hook_field_is_empty().
asin_field_views_data Implements hook_field_views_data().
asin_field_widget_element_validate Text callback for the asin_text widget.
asin_field_widget_form Implements hook_field_widget_form().
asin_field_widget_info Implementation of hook_field_widget_info().
asin_field_widget_settings_form Implements hook_field_widget_settings_form().
asin_form_devel_generate_content_form_alter Allow selection of the SearchIndex and Keywords to be used
asin_menu Implementation of hook_menu().
asin_preprocess_field Implements hook_preprocess_field().
asin_widget_locale_product_group AJAX callback for widget settings form.
_asin_devel_generate Utility function that actually provides the values for asin_devel_generate().
_asin_trim_items Trim spaces from the front/back of each ASIN in an array.