diff --git CHANGELOG.txt CHANGELOG.txt index 3bb6df1..505fea3 100644 --- CHANGELOG.txt +++ CHANGELOG.txt @@ -2,6 +2,8 @@ $Id$ 6.x-1.x-RC4 release =================== +- [#609448] by rfay: Added Feature: Add customer reviews and ratings to Amazon information retrieved. +- [#556414] by rfay, stratosgear: Provide Amazon.com price or lowest price or both. - [#697138] by robertDouglass: Added Drupal alter $params before searching in amazon_search so that modules can customize search. - [#288577] by rfay, reprise: Add 13-digit ISBN/EAN support to the admin test diff --git README.txt README.txt index 89c6b04..abd980d 100644 --- README.txt +++ README.txt @@ -1,22 +1,37 @@ Amazon.module API Documentation =============================== -The Amazon package gives Drupal based web sites access to the core features of the Amazon Product Marketing API. The package provides several components: +The Amazon package gives Drupal based web sites access to the core features of +the Amazon Product Marketing API. The package provides several components: -1) A core API module that communicates with Amazon's web services and adds support for retrieving, displaying, and storing basic product information in Drupal as well as associating Amazon products with content nodes. -2) A field type (and an accompanying set of formatters) for CCK that allows Amazon products to be explicitly added to any node type. -3) An 'Amazon Media' module that adds support for Amazon's extended product information for several common product types (Books, Music, DVDs, and Software). -4) An 'Amazon Search' module that allows developers to conduct API-driven searches of the Amazon product database. This module also allows users to search Amazon's product database from Drupal's Advanced Search page. +1) A core API module that communicates with Amazon's web services and adds + support for retrieving, displaying, and storing basic product information + in Drupal as well as associating Amazon products with content nodes. +2) A field type (and an accompanying set of formatters) for CCK that allows + Amazon products to be explicitly added to any node type. +3) An 'Amazon Media' module that adds support for Amazon's extended product + information for several common product types (Books, Music, DVDs, and + Software). +4) An 'Amazon Search' module that allows developers to conduct API-driven + searches of the Amazon product database. This module also allows users to + search Amazon's product database from Drupal's Advanced Search page. -Most users and site builders will want to enable the basic Amazon API module, as well as the Amazon Field and Amazon Media modules. These provide the functionality that the majority of simple sites need. +Most users and site builders will want to enable the basic Amazon API module, +as well as the Amazon Field and Amazon Media modules. These provide the +functionality that the majority of simple sites need. -For more information, visit the project page at http://drupal.org/project/amazon and -the project documentation at http://drupal.org/node/595464. +For more information, visit the project page at http://drupal.org/project/amazon +and the project documentation at http://drupal.org/node/595464. Amazon API conventions ====================== - All element keys are lowercased for consistency -- The Amazon ItemAttributes collection is merged into the top level of the Drupal entity for simplicity. -- Information outside the 'common' ItemAttributes, Product Images, and Artist/Author/Etc. data must be handled by external third-party modules. -- Authors, Directors, and other creator information is stored in the generic $item['participants'] array. It's a crummy name, but until I come up with something better it'll have to do. A separate array for each participant type is also created. \ No newline at end of file +- The Amazon ItemAttributes collection is merged into the top level of the + Drupal entity for simplicity. +- Information outside the 'common' ItemAttributes, Product Images, and + Artist/Author/Etc. data must be handled by external third-party modules. +- Authors, Directors, and other creator information is stored in the generic + $item['participants'] array. It's a crummy name, but until I come up with + something better it'll have to do. A separate array for each participant type + is also created. \ No newline at end of file diff --git amazon.admin.inc amazon.admin.inc index 86653c5..ca1cb70 100644 --- amazon.admin.inc +++ amazon.admin.inc @@ -79,6 +79,7 @@ function amazon_storage_settings_form() { 'creators' => t('Book authors, film actors, etc.'), 'images' => t('Product images'), 'editorial_reviews' => t('Editorial reviews'), + 'customer_reviews' => t('Customer reviews'), ), ); diff --git amazon.install amazon.install index 8d8b28a..0518d15 100644 --- amazon.install +++ amazon.install @@ -66,6 +66,12 @@ function amazon_schema() { 'listpriceamount' => array('type' => 'numeric'), 'listpricecurrencycode' => array('type' => 'varchar', 'length' => 32), 'listpriceformattedprice' => array('type' => 'varchar', 'length' => 32), + 'lowestpriceamount' => array('type' => 'numeric'), + 'lowestpricecurrencycode' => array('type' => 'varchar', 'length' => 32), + 'lowestpriceformattedprice' => array('type' => 'varchar', 'length' => 32), + 'amazonpriceamount' => array('type' => 'numeric'), + 'amazonpricecurrencycode' => array('type' => 'varchar', 'length' => 32), + 'amazonpriceformattedprice' => array('type' => 'varchar', 'length' => 32), 'productgroup' => array('type' => 'varchar', 'length' => 255), 'producttypename' => array('type' => 'varchar', 'length' => 255), 'invalid_asin' => array('type' => 'int', 'default' => 0), @@ -105,9 +111,19 @@ function amazon_schema() { 'source' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE), 'content' => array('type' => 'text'), ), - 'primary key' => array('asin', 'source'), + 'indexes' => array('asin' => array('asin')), ); + $schema['amazon_item_customer_review'] = array( + 'fields' => array( + 'asin' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE), + 'rating' => array('type' => 'numeric'), + 'date' => array('type' => 'varchar', 'length' => 11), + 'summary' => array('type' => 'text'), + 'content' => array('type' => 'text'), + ), + 'indexes' => array('asin' => array('asin')), + ); $schema['amazon_item_node'] = array( 'fields' => array( @@ -255,11 +271,47 @@ function amazon_update_6008() { * It looks like nid should be the correct portion of the key, since there * is no vid coming in. In fact... There should be no vid. * - * @return unknown_type */ function amazon_update_6009() { $ret = array(); db_drop_primary_key($ret, 'amazon_item_node'); db_add_primary_key($ret, 'amazon_item_node', array('nid', 'asin', 'module')); return $ret; -} \ No newline at end of file +} + + +/** + * Change key on editorial reviews, since it shouldn't be primary. + */ +function amazon_update_6010() { + $ret = array(); + db_drop_primary_key($ret, 'amazon_item_editorial_review'); + db_add_index($ret, 'amazon_item_editorial_review', 'amazon_item_editorial_review', array('asin')); + return $ret; +} + +/** + * Add Customer Reviews table so we can handle that. + * @return unknown_type + */ +function amazon_update_6011() { + $schema = amazon_schema(); + if (!db_table_exists('amazon_item_customer_review')) { + db_create_table($ret, 'amazon_item_customer_review', $schema['amazon_item_customer_review']); + } + return $ret; +} + +/** + * Add new elements to the amazon_item table: Lowest price, etc. + * @return unknown_type + */ +function amazon_update_6012() { + db_add_field($ret, 'amazon_item', 'lowestpriceamount', array('type' => 'numeric')); + db_add_field($ret, 'amazon_item', 'lowestpricecurrencycode', array('type' => 'numeric')); + db_add_field($ret, 'amazon_item', 'lowestpriceformattedprice', array('type' => 'varchar', 'length' => 32)); + db_add_field($ret, 'amazon_item', 'amazonpriceamount', array('type' => 'numeric')); + db_add_field($ret, 'amazon_item', 'amazonpricecurrencycode', array('type' => 'numeric')); + db_add_field($ret, 'amazon_item', 'amazonpriceformattedprice', array('type' => 'varchar', 'length' => 32)); + return $ret; +} diff --git amazon.module amazon.module index 2c0f0c2..a0a0db5 100644 --- amazon.module +++ amazon.module @@ -118,6 +118,7 @@ function amazon_preprocess_amazon_item(&$variables) { $variables['type'] = _amazon_clean_type($item['producttypename']); $variables['detailpageurl'] = check_url($item['detailpageurl']); $variables['editorialreview'] = !empty($item['editorialreviews']) ? check_markup($item['editorialreviews'][0]['content']) : ''; + $variables['customerreview'] = !empty($item['customerreviews']) ? check_markup($item['customerreviews'][0]['content']) : ''; $variables['invalid_asin'] = !empty($item['invalid_asin']) ? 1 : 0; if (!empty($variables['theatricalreleasedate'])) { @@ -438,6 +439,12 @@ function amazon_item_clean_xml($xml) { // Pull the absolute basic information Amazon keeps at the top level // of the XML tree, cast to string, and move on. $item['asin'] = (string)$xml->ASIN; + if (empty($item['isbn'])) { + $item['isbn'] = (string)$xml->ItemAttributes->ISBN; + } + if (empty($item['ean'])) { + $item['ean'] = (string)$xml->ItemAttributes->EAN; + } $item['salesrank'] = (string)$xml->SalesRank; $item['detailpageurl'] = (string)$xml->DetailPageURL; @@ -446,6 +453,18 @@ function amazon_item_clean_xml($xml) { $item['listpricecurrencycode'] = (string)$xml->ItemAttributes->ListPrice->CurrencyCode; $item['listpriceformattedprice'] = (string)$xml->ItemAttributes->ListPrice->FormattedPrice; } + if (!empty($xml->OfferSummary->LowestNewPrice)) { + $item['lowestpriceamount'] = (string)$xml->OfferSummary->LowestNewPrice->Amount; + $item['lowestpricecurrencycode'] = (string)$xml->OfferSummary->LowestNewPrice->CurrencyCode; + $item['lowestpriceformattedprice'] = (string)$xml->OfferSummary->LowestNewPrice->FormattedPrice; + } + // Note that this one assumes we've searched with Merchant = Amazon. + // Otherwise we can do an xpath search looking for the actual amazon listing. + if (!empty($xml->Offers->Offer[0]->OfferListing->Price) && (string)$xml->Offers->Offer[0]->Merchant->MerchantId == 'ATVPDKIKX0DER') { + $item['amazonpriceamount'] = (string)$xml->Offers->Offer[0]->OfferListing->Price->Amount; + $item['amazonpricecurrencycode'] = (string)$xml->Offers->Offer[0]->OfferListing->Price->CurrencyCode; + $item['amazonpriceformattedprice'] = (string)$xml->Offers->Offer[0]->OfferListing->Price->FormattedPrice; + } $participant_types = split(',', AMAZON_PARTICIPANT_TYPES); @@ -493,6 +512,18 @@ function amazon_item_clean_xml($xml) { } } + // And the customer reviews. + if (isset($xml->CustomerReviews)) { + foreach($xml->CustomerReviews->Review as $data) { + $item['customerreviews'][] = array( + 'rating' => (string)$data->Rating, + 'date' => (string)$data->Date, + 'summary' => (string)$data->Summary, + 'content' => (string)$data->Content, + ); + } + } + // Give other modules an opportunity to pull out other bits of Amazon data // that would otherwise be ignored. We can't use module_invoke_all, as it // would lose the reference. @@ -553,6 +584,15 @@ function amazon_item_insert($item) { } } } + // Save the customer reviews if they exist. + if (in_array('customer_reviews', variable_get('amazon_core_data', array('creators', 'images')))) { + if (isset($item['customerreviews'])) { + foreach($item['customerreviews'] as $data) { + $review = array('asin' => $item['asin'], 'rating' => $data['rating'], 'date' => $data['date'], 'summary' => $data['summary'], 'content' => $data['content']); + drupal_write_record('amazon_item_customer_review', $review); + } + } + } module_invoke_all('amazon_item_insert', $item); } @@ -569,6 +609,7 @@ function amazon_item_delete($asin) { db_query("DELETE FROM {amazon_item_participant} WHERE asin = '%s'", $asin); db_query("DELETE FROM {amazon_item_image} WHERE asin = '%s'", $asin); db_query("DELETE FROM {amazon_item_editorial_review} WHERE asin = '%s'", $asin); + db_query("DELETE FROM {amazon_item_customer_review} WHERE asin = '%s'", $asin); } /** @@ -741,18 +782,22 @@ function amazon_cron() { function amazon_token_list($type = 'all') { if ($type == 'amazon_item' || $type == 'all') { - $tokens['amazon_item']['asin'] = t('Product ID'); - $tokens['amazon_item']['detailpageurl'] = t('The detail page URL for the item'); - $tokens['amazon_item']['salesrank'] = t("The product's sales rank"); - $tokens['amazon_item']['brand'] = t("The product's brand"); - $tokens['amazon_item']['publisher'] = t("The product's publisher"); - $tokens['amazon_item']['manufacturer'] = t("The product's manufacturer"); - $tokens['amazon_item']['studio'] = t("The product's studio"); - $tokens['amazon_item']['label'] = t("The product's label"); - $tokens['amazon_item']['binding'] = t("The product's binding type"); - $tokens['amazon_item']['releasedate'] = t("The product's release date"); - $tokens['amazon_item']['listprice'] = t("The product's list price"); - $tokens['amazon_item']['producttype'] = t("The product's Amazon product type"); + $tokens['amazon_item']['asin'] = t('Product ID'); + $values['amazon_item']['isbn'] = t('The 10-digit ISBN (International Standard Book Number)'); + $values['amazon_item']['ean'] = t('The EAN or 13-digit ISBN'); + $tokens['amazon_item']['detailpageurl'] = t('The detail page URL for the item'); + $tokens['amazon_item']['salesrank'] = t("The product's sales rank"); + $tokens['amazon_item']['brand'] = t("The product's brand"); + $tokens['amazon_item']['publisher'] = t("The product's publisher"); + $tokens['amazon_item']['manufacturer'] = t("The product's manufacturer"); + $tokens['amazon_item']['studio'] = t("The product's studio"); + $tokens['amazon_item']['label'] = t("The product's label"); + $tokens['amazon_item']['binding'] = t("The product's binding type"); + $tokens['amazon_item']['releasedate'] = t("The product's release date"); + $tokens['amazon_item']['listprice'] = t("The product's list price"); + $tokens['amazon_item']['producttype'] = t("The product's Amazon product type"); + $values['amazon_item']['lowestprice'] = t('The lowest price currently offered by any merchant at Amazon'); + $values['amazon_item']['amazonprice'] = t('Current price offered by Amazon'); return $tokens; } } @@ -760,18 +805,22 @@ function amazon_token_list($type = 'all') { function amazon_token_values($type, $object = NULL, $options = array()) { if ($type == 'amazon_item' || $type == 'all') { $item = (array)$object; - $values['asin'] = check_plain($item['asin']); - $values['detailpageurl'] = check_url($item['detailpageurl']); - $values['salesrank'] = check_plain($item['salesrank']); - $values['brand'] = check_plain($item['brand']); - $values['publisher'] = check_plain($item['publisher']); - $values['manufacturer'] = check_plain($item['manufacturer']); - $values['studio'] = check_plain($item['studio']); - $values['label'] = check_plain($item['label']); - $values['binding'] = check_plain($item['binding']); - $values['releasedate'] = check_plain($item['releasedate']); - $values['listprice'] = check_plain($item['listpriceformattedprice']); - $values['producttype'] = check_plain($item['producttype']); + $values['asin'] = check_plain($item['asin']); + $values['isbn'] = check_plain($item['isbn']); + $values['ean'] = check_plain($item['ean']); + $values['detailpageurl'] = check_url($item['detailpageurl']); + $values['salesrank'] = check_plain($item['salesrank']); + $values['brand'] = check_plain($item['brand']); + $values['publisher'] = check_plain($item['publisher']); + $values['manufacturer'] = check_plain($item['manufacturer']); + $values['studio'] = check_plain($item['studio']); + $values['label'] = check_plain($item['label']); + $values['binding'] = check_plain($item['binding']); + $values['releasedate'] = check_plain($item['releasedate']); + $values['listprice'] = check_plain($item['listpriceformattedprice']); + $values['producttype'] = check_plain($item['producttype']); + $values['lowestprice'] = check_plain($item['lowestpriceformattedprice']); + $values['amazonprice'] = check_plain($item['amazonpriceformattedprice']); return $values; } } diff --git amazon_filter/amazon_filter.module amazon_filter/amazon_filter.module index 292c30b..afbcfa5 100644 --- amazon_filter/amazon_filter.module +++ amazon_filter/amazon_filter.module @@ -13,8 +13,10 @@ function amazon_filter_filter_tips($delta, $format, $long = FALSE) { return t('Get Amazon product data using [amazon ASIN selector], for example, [amazon 0399155341 thumbnail], [amazon 0399155341 full], or [amazon 0399155341 inline]. In addition, you can grab various data items from the item description using selectors like - author, title, asin, detailpageurl, salesrank, publisher, manufacturer, studio, + author, title, asin, isbn, ean, detailpageurl, salesrank, publisher, manufacturer, studio, label, binding, listpriceamount, listpricecurrencycode, listpriceformattedprice, + lowestpriceamount, lowestpricecurrencycode, lowestpriceformattedprice, + amazonpriceamount, amazonpricecurrencycode, amazonpriceformattedprice, productgroup, producttypename, invalid_asin, deweydecimalnumber, edition, numberofpages, publicationyear, type, releaseyear, publicationyear, smallimage, smallimageurl, smallimageheight, smallimagewidth, mediumimage, mediumimageurl, mediumimageheight, mediumimagewidth, diff --git amazon_media/amazon_media.install amazon_media/amazon_media.install index bbe0608..7471556 100644 --- amazon_media/amazon_media.install +++ amazon_media/amazon_media.install @@ -9,6 +9,8 @@ function amazon_media_schema() { $schema['amazon_book'] = array( 'fields' => array( 'asin' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE), + 'ean' => array('type' => 'varchar', 'length' => 20, 'not null' => TRUE), + 'isbn' => array('type' => 'varchar', 'length' => 20, 'not null' => TRUE), 'deweydecimalnumber' => array('type' => 'varchar', 'length' => 64), 'edition' => array('type' => 'int'), 'numberofpages' => array('type' => 'int'), @@ -71,3 +73,13 @@ function amazon_media_update_6001() { return $ret; } + +/** + * Add the isbn and ean fields to a book item. + */ +function amazon_media_update_6002() { + $ret = array(); + db_add_field($ret, 'amazon_book', 'isbn', array('type' => 'varchar', 'length' => 20)); + db_add_field($ret, 'amazon_book', 'ean', array('type' => 'varchar', 'length' => 20)); + return $ret; +} \ No newline at end of file diff --git amazon_media/amazon_media.views.inc amazon_media/amazon_media.views.inc index 0f1bbc7..09a5acf 100644 --- amazon_media/amazon_media.views.inc +++ amazon_media/amazon_media.views.inc @@ -28,7 +28,9 @@ function amazon_media_views_data() { ), ); - _amazon_make_simple_text_field($data, 'amazon_book', 'deweydecimalnumber', 'Dewey decimal number'); + _amazon_make_simple_text_field($data, 'amazon_book', 'deweydecimalnumber', 'Dewey decimal number', 'Dewey decimal number (used by libraries)'); + _amazon_make_simple_text_field($data, 'amazon_book', 'isbn', 'ISBN number (10-digit)', 'Library of Congress ISBN number (10-digit)'); + _amazon_make_simple_text_field($data, 'amazon_book', 'ean', 'EAN (13-digit ISBN number)', 'The newer, longer 13-digit EAN or 13-digit ISBN number'); _amazon_make_simple_number_field($data, 'amazon_book', 'edition', 'Edition', 'The edition of an Amazon book product.'); _amazon_make_simple_number_field($data, 'amazon_book', 'numberofpages', 'Pages', 'The number of pages in an Amazon book product.'); _amazon_make_simple_date_field($data, 'amazon_book', 'publicationdate', 'Publication date', 'The publication date of the item'); diff --git includes/amazon.views.inc includes/amazon.views.inc index eff1edd..e972cd0 100644 --- includes/amazon.views.inc +++ includes/amazon.views.inc @@ -130,9 +130,16 @@ function amazon_views_data() { _amazon_make_simple_text_field($data, 'amazon_item', 'producttypename', 'Product type name', 'The Amazon internal product-type code for the product.'); _amazon_make_simple_date_field($data, 'amazon_item', 'releasedate', 'Release date', 'The release date of the product.'); - _amazon_make_simple_text_field($data, 'amazon_item', 'listpriceformattedprice', 'List price', 'The current list price of the item. (Unformatted)'); + _amazon_make_simple_number_field($data, 'amazon_item', 'listpriceamount', 'List price (numeric)', 'The current sales ranking of the product on Amazon.com.'); + _amazon_make_simple_text_field($data, 'amazon_item', 'listpriceformattedprice', 'List price (formatted)', 'The current list price of the item.'); + _amazon_make_simple_number_field($data, 'amazon_item', 'lowestpriceamount', 'Lowest price (numeric)', 'The current lowest price offered on Amazon.'); + _amazon_make_simple_text_field($data, 'amazon_item', 'lowestpriceformattedprice', 'Lowest price (formatted)', 'The lowest available price.'); + _amazon_make_simple_number_field($data, 'amazon_item', 'amazonpriceamount', 'Amazon Price (numeric)', "Amazon's current price for the item"); + _amazon_make_simple_text_field($data, 'amazon_item', 'amazonpriceformattedprice', 'Amazon price (formatted)', "Amazon's current price for the item."); _amazon_make_simple_boolean_field($data, 'amazon_item', 'invalid_asin', 'Invalid ASIN', 'If nonzero, the ASIN is invalid or discontinued by Amazon'); unset($data['amazon_item']['listpriceformattedprice']['argument']); + unset($data['amazon_item']['lowestformattedprice']['argument']); + unset($data['amazon_item']['amazonpriceformattedprice']['argument']); // Define the base group of this table. Fields that don't // have a group defined will go into this field by default. @@ -193,6 +200,31 @@ function amazon_views_data() { ), ); + + $data['amazon_item_editorial_review']['table']['group'] = t('Amazon'); + $data['amazon_item_editorial_review']['table']['join'] = array( + // editorial review links to amazon_item directly via asin. + 'amazon_item' => array( + 'left_field' => 'asin', + 'field' => 'asin', + ), + ); + _amazon_make_simple_text_field($data, 'amazon_item_editorial_review', 'source', 'Editorial review source', 'The source of this editorial review. Since there may be more than one review for each product, your view will produce one row per review.'); + _amazon_make_simple_text_field($data, 'amazon_item_editorial_review', 'content', 'Editorial content', 'Content of an editorial review. Since there may be more than one review for each product, your view will produce one row per review.'); + + $data['amazon_item_customer_review']['table']['group'] = t('Amazon'); + $data['amazon_item_customer_review']['table']['join'] = array( + // customer review links to amazon_item directly via asin. + 'amazon_item' => array( + 'left_field' => 'asin', + 'field' => 'asin', + ), + ); + _amazon_make_simple_number_field($data, 'amazon_item_customer_review', 'rating', 'Customer Rating', "Numeric rating of this product. Since there is normally more than one review for each product, your view will produce one row per review."); + _amazon_make_simple_date_field($data, 'amazon_item_customer_review', 'date', 'Customer Rating Date', 'Date the product was rated. Since there is normally more than one review for each product, your view will produce one row per review.'); + _amazon_make_simple_text_field($data, 'amazon_item_customer_review', 'summary', 'Customer Review Summary', 'Summary of the review. Since there is normally more than one review for each product, your view will produce one row per review.'); + _amazon_make_simple_text_field($data, 'amazon_item_customer_review', 'content', 'Customer Review Content', 'Customer review content. Since there is normally more than one review for each product, your view will produce one row per review.'); + return $data; }