asin.module in Amazon Product Advertisement API 7
Same filename and directory in other branches
Defines a field type for referencing an Amazon product.
File
asin/asin.moduleView 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_details' => array(
'label' => t('Thumbnail with details'),
'field types' => array(
'asin',
),
),
'asin_thumbnail' => array(
'label' => t('Thumbnail image'),
'field types' => array(
'asin',
),
),
'asin_medium' => array(
'label' => t('Medium image'),
'field types' => array(
'asin',
),
),
'asin_large' => array(
'label' => t('Large image'),
'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();
foreach ($items as $delta => $value) {
$asin = trim($value['asin']);
if (!empty($asin)) {
if ($display['type'] == 'asin_plain') {
$element[$delta] = array(
'#markup' => $asin,
);
}
else {
$lookup = amazon_item_lookup($asin);
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);
}
$element[$delta] = array(
'#markup' => theme($theme_function, array(
'item' => $item,
'style' => $style,
)),
);
}
}
}
}
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'];
}
}
if ($widget['type'] == 'asin_autocomplete') {
$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',
),
);
if (isset($settings['widget_settings']['locale'])) {
$form['widget_settings']['productgroup'] = array(
'#title' => t('Search Index'),
'#type' => 'select',
'#options' => $cache['locales'][$settings['widget_settings']['locale']]['search_indexes'],
'#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;
}
/**
* Widget validation function.
*
* 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) {
$asin = trim(amazon_convert_to_asin($item['asin']));
if (!empty($asin) && is_numeric($delta)) {
$results = amazon_item_lookup(array(
$asin,
));
if (empty($results)) {
form_set_error("{$field_name}][{$langcode}][{$delta}][asin", t('No Amazon product with the ASIN "%id" could be located.', array(
'%id' => $asin,
)));
}
else {
$item['asin'] = (string) key($results);
}
}
}
}
/**
* Autocomplete callback for the asin_autocomplete widget.
*/
function asin_autocomplete_callback($entity_type, $bundle, $field_name, $string = '') {
$field = field_info_field($field_name);
$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,
);
$results = amazon_http_request('ItemSearch', $parameters, $instance['widget']['settings']['widget_settings']['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);
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);
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']);
}
Functions
Name | 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 | Widget validation function. |
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_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. |