Set options. Version: 1.0 Author: Martin Kliehm Author URI: http://learningtheworld.eu/ Copyright 2007 by Martin Kliehm The success and error icons were provided by http://www.famfamfam.com/lab/icons/silk/ The AJAX loading icon was provided by http://www.ajaxload.info This program is free software; you can redistribute it and/or modify it under the terms of the FreeBSD License: http://www.freebsd.org/copyright/freebsd-license.html This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the FreeBSD License for more details. There's no code in here to edit. Everything can be edited through the admin interface, the CSS, XSLT, and translation files. However, if you DO edit this file, make sure to save it as UTF-8. */ /** * AJAX request: get required WordPress variables and start action */ if ( isset( $_GET['ajax'] ) ) { require_once('../../../wp-config.php'); require_once('../../../wp-admin/includes/admin.php'); // I18N if ( defined( 'WPLANG' ) && '' != constant( 'WPLANG' ) ) load_plugin_textdomain( 'amtap', 'wp-content/plugins/amtap/languages' ); // Test access key status AMTAP::test_status(); } /** * I18N: load translation (MO) files */ if ( defined( 'WPLANG' ) && '' != constant( 'WPLANG' ) ) load_plugin_textdomain( 'amtap', 'wp-content/plugins/amtap/languages' ); /** * Constants */ define( 'AMTAP_PLUGINPATH', ABSPATH . 'wp-content/plugins/amtap/' ); // Plugin path define( 'AMTAP_CACHEPATH', ABSPATH . 'wp-content/cache/' ); // Cache path /** * Default options */ add_option( 'amtap_xml_maxage', 60 * 60 ); // Default maximum age of cached HTML: 1 hour add_option( 'amtap_associate_default', AMTAP::get_locale( true ) ); // Default locale add_option( 'amtap_ip2country', 'true' ); // Default for IP to Country is true add_option( 'amtap_donation', 'false' ); // Default for using the author's Associate Tags is false add_option( 'amtap_item_response_group', 'Images,Medium,Offers,Reviews' ); // Default reponse group for ItemLookup requests add_option( 'amtap_debug', 'false' ); // Debug options are disabled by default add_filter( 'the_content', array( 'AMTAP', 'get_tags_from_content' ), -10 ); // add content filtering /** * Amazon Machine Tag Associate Plugin (AMTAP) Class * @author Martin Kliehm * @version 1.0 * @package WordPress * @since 2.3 */ class AMTAP { /** * init() - Initialize the plugin */ function init() { // Add the plugin to the admin menu add_action( 'admin_menu', array( 'AMTAP', 'config_page' ) ); // Add the plugin CSS to the admin and blog pages add_action( 'admin_head', array( 'AMTAP', 'admin_css' ) ); add_action( 'wp_head', array( 'AMTAP', 'blog_css' ) ); // Add the plugin AJAX to the admin page add_action( 'admin_footer', array( 'AMTAP', 'admin_js' ) ); } /** * config_page() - Add WordPress action to show the admin configuration page */ function config_page() { if ( function_exists( 'add_submenu_page' ) ) { add_options_page( __( 'Amazon Machine Tags Configuration', 'amtap' ), __( 'Amazon Tags', 'amtap' ), 8, 'amtap', array( 'AMTAP', 'admin_page' ) ); } } /** * admin_page() - Show the admin page */ function admin_page() { // Test access key status if ( $sMessage = AMTAP::test_status() ) $aMessage = explode( ';;', $sMessage ); /** * Include admin GUI */ require_once( 'amtap-admin.inc.php' ); } /** * verify_key() - AWS access key verification * @param string $AWSAccessKeyId * @return string $sKeyStatus */ function verify_key( $AWSAccessKeyId ) { $sRequest = AMTAP::build_rest_url( $AWSAccessKeyId, true ); $sKeyStatus = AMTAP::transform( $sRequest, '', 'amtap-key-verification.xsl', 'amtap-aws-key-verification' ); // Set key status switch( $sKeyStatus ) { case 'true' : $sKeyStatus = 'valid'; break; case 'failed' : case false : $sKeyStatus = 'failed'; break; case 'nodirectory' : $sKeyStatus = 'no_directory'; break; case 'nocache' : $sKeyStatus = 'cache_not_writable'; break; default : $sKeyStatus = 'invalid'; break; } return $sKeyStatus; } /** * get_items() - Get and print items for a post * * Implement in the sidebar like AMTAP::get_items() * @param array $aTags * @return string $sResult */ function get_items( $aTags = '' ) { // Return if this is a category, archive, home, or search page if ( is_category() || is_archive() || is_search() || is_home() || !AMTAP::cached_key_status() ) return false; // Get AWS Access Key $AWSAccessKeyId = get_option( 'aws_access_key_id' ); // Get response group $sResponseGroup = get_option( 'amtap_item_response_group' ); // Set XSLT file $sXsl = ( $aTags ) ? 'amtap-html-content.xsl' : 'amtap-html-sidebar.xsl'; // Get top level domain (locale) if override is allowed, or default $sTld = ( get_option( 'amtap_ip2country' ) == 'true' ) ? AMTAP::get_locale() : get_option( 'amtap_associate_default' ); $sAssociateTag = get_option( 'amtap_associate_id_' . $sTld ); // Associate Tag is empty and donate option is set if ( $sAssociateTag == '' && get_option( 'amtap_donation' ) == 'true' ) $sAssociateTag = AMTAP::get_author_tag( $sTld ); // Get machine tags $aFilteredTags = AMTAP::get_machine_tags( $aTags ); if ( $aFilteredTags ) { // Set tags $sItemId = implode( ',', $aFilteredTags ); // Build REST URL $sRequest = AMTAP::build_rest_url( $AWSAccessKeyId, false, $sTld, 'ItemLookup', $sResponseGroup, $sItemId, $sAssociateTag ); // XSL Transformation $sResult = AMTAP::transform( $sRequest, $sTld, $sXsl ); // Replace stuff and return the result if the request was successful if ( $sResult && $sResult != 'failed' && !strpos( $sResult, 'is not a valid value for AWSAccessKeyId' ) ) { // Clean result $sResult = AMTAP::clean_result( $sResult, $sTld ); // Return (X)HTML code if ( $aTags ) { // Return result in content return $sResult; } else { // Print result to sidebar echo $sResult; // Print the request URL if debugging is enabled if ( get_option( 'amtap_debug' ) == 'true' ) echo "\n" . '' . "\n"; } } else { return false; } } else { return false; } } /** * cached_key_status() - Check content of the cached key verification * * The key is automatically verified when it is entered and saved on the * admin page. If there's no saved file or the daved file doesn't contain * 'true', any request to the AWS API is futile * @return boolean true|false */ function cached_key_status() { $sCachefile = AMTAP_CACHEPATH . 'amtap-aws-key-verification.txt'; if ( file_exists( $sCachefile ) ) { // get cached key status $sKeyStatus = file_get_contents( $sCachefile ); if ( $sKeyStatus == 'true' ) return true; } return false; } /** * build_rest_url() - Build Amazon API REST URL * @param string $AWSAccessKeyId * @param boolean $bValidateKey * @param string $sTld * @param string $sOperation * @param string $sResponseGroup * @param string $sItemId * @param string $sAssociateTag * @return string $sRequest */ function build_rest_url( $AWSAccessKeyId, $bValidateKey = false, $sTld = 'us', $sOperation = 'Help', $sResponseGroup = 'Help', $sItemId = '', $sAssociateTag = '' ) { // Amazon ECS URI $sAmazonUri = 'http://xml-' . $sTld . '.amznxslt.com/onca/xml'; // Amazon Web Services params $aAwsParams = array( 'Service' => 'AWSECommerceService', 'Version' => '2007-06-13', 'AWSAccessKeyId' => $AWSAccessKeyId, 'Operation' => $sOperation, 'ResponseGroup' => $sResponseGroup ); // Assign more default values for a validation request if ( $bValidateKey ) { $aAwsParams['About'] = 'ItemIds'; $aAwsParams['HelpType'] = 'ResponseGroup'; } // Assign values for an item request if ( $sAssociateTag ) $aAwsParams['AssociateTag'] = $sAssociateTag; if ( $sItemId ) $aAwsParams['ItemId'] = $sItemId; // Construct key/value pars foreach( $aAwsParams as $key => $value ) { $aAwsPairs[] = urlencode( $key ) . '=' . urlencode( $value ); } // Return URL $sRequest = $sAmazonUri . '?' . implode( $aAwsPairs, '&' ); return $sRequest; } /** * transform() - Query the Amazon API, return HTML code or an AJAX text string * @global object $wp_query * @param string $sRequest * @param string $sTld * @param string $sXsl * @param string $sCacheFilename * @return string $result */ function transform( $sRequest = '', $sTld = 'us', $sXsl = 'amtap-html-sidebar.xsl', $sCacheFilename = 'amtap-aws-items' ) { // Get post ID global $wp_query; $iPostId = $wp_query->post->ID; // Get request URI if ( !$sRequest ) $sRequest = AMTAP::build_rest_url( get_option( 'aws_access_key_id' ), true ); $sRequest .= '&Style=' . get_bloginfo( 'wpurl' ) . '/wp-content/plugins/amtap/' . $sXsl . '&ContentType='; $sRequest .= ( $sCacheFilename == 'amtap-aws-items' ) ? 'text/html' : 'text/plain'; // Get content // Load cache if it exists $sCachefile = ( $sCacheFilename == 'amtap-aws-items' ) ? $sCacheFilename . '-for-post-' . $iPostId . '-' . $sTld . '.html' : $sCacheFilename . '.txt'; $sCachefile = AMTAP_CACHEPATH . $sCachefile; if ( file_exists( $sCachefile ) ) { $result = $oCache = file_get_contents( $sCachefile ); $iCacheAge = time() - filectime( $sCachefile ); $bCacheOutdated = ( $iCacheAge < time() - get_option( 'amtap_xml_maxage' ) ); } // Get fresh content by REST request if cached XML is too old or doesn't exist if ( !$oCache || $bCacheOutdated || $sCacheFilename == 'amtap-aws-key-verification' ) { // Try to get fresh results with cURL first if ( function_exists( 'curl_init' ) ) { $oCurlHandle = curl_init(); curl_setopt( $oCurlHandle, CURLOPT_URL, $sRequest); curl_setopt( $oCurlHandle, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt( $oCurlHandle, CURLOPT_RETURNTRANSFER, true); curl_setopt( $oCurlHandle, CURLOPT_FAILONERROR, true ); // Get fresh content $oFresh = curl_exec( $oCurlHandle ); // Set result to FALSE if HTTP return code is in error $iHttpCode = curl_getinfo( $oCurlHandle, CURLINFO_HTTP_CODE ); curl_close( $oCurlHandle ); } else { // Try alternate method $oFresh = file_get_contents( $sRequest ); } $result = ( $iHttpCode < 400 ) ? $oFresh : 'failed'; } // Save file to cache if ( $oFresh != 'failed' ) { if ( !isset( $_GET['ajax'] ) ) { if ( !is_dir( AMTAP_CACHEPATH ) ) { // directory doesn't exist $result = 'nodirectory'; } else if ( !( $oFileHandle = fopen( $sCachefile, 'w' ) ) ) { // directory is not writable $result = 'nocache'; } else { // save file to cache fwrite( $oFileHandle, $oFresh ); fclose( $oFileHandle ); } } } else if ( !$oCache ) { // Both retrieving methods have failed and there's no cached version die ( 'AMTAP: cannot retrieve content from Amazon Web Services or cache' ); } return $result; } /** * clean_result() - Clean XHTML result * @param string $sResult * @param string $sTld * @return string $sResult */ function clean_result( $sResult, $sTld ) { // Cut XML declaration from result if xsl:omit-xml-declaration failed $sResult = preg_replace( '/\<\?xml .*\?\>/', '', $sResult ); // Replace "EUR" with euro character $sResult = preg_replace( '/EUR /', '€ ', $sResult ); // Replace pages with localized content $aPages = array( 'de' => ' Seiten,', 'jp' => 'ページ,' ); if ( $sTld == 'de' || $sTld == 'fr' || $sTld == 'jp' ) { if ( $sTld != 'fr' ) $sResult = preg_replace( '/ pages,/', $aPages[ $sTld ], $sResult ); $sResult = preg_replace( '/(lang=")(\w{2})(")/', "$1" . $sTld . "$3", $sResult ); } return $sResult; } /** * test_status() - Triggers key validation, returns status messages */ function test_status() { // options page was submitted or AJAX request was sent $is_ajax = isset( $_GET['ajax'] ); if ( isset( $_GET['updated'] ) || $is_ajax ) { // check AWS Access Key $key = ( $is_ajax ) ? wp_specialchars( strip_tags( $_GET['AWSAccessKeyId'] ) ) : get_option( 'aws_access_key_id' ); if ( empty( $key ) ) { $sKeyStatus = 'empty'; $ms[] = ( $is_ajax ) ? 'ajax_key_empty' : 'new_key_empty'; } else { $sKeyStatus = AMTAP::verify_key( $key ); } // Set status messages if ( $sKeyStatus == 'valid' ) { $ms[] = 'new_key_valid'; if ( $is_ajax ) update_option( 'aws_access_key_id', $key ); } else if ( $sKeyStatus == 'invalid' ) { $ms[] = 'new_key_invalid'; } else if ( $sKeyStatus == 'failed' ) { $ms[] = 'no_connection'; } else if ( $sKeyStatus == 'no_directory' ) { $ms[] = 'no_directory'; } else if ( $sKeyStatus == 'cache_not_writable' ) { $ms[] = 'cache_not_writeable'; } } // Status message texts $messages = array( 'new_key_valid' => array( 'status' => 'updated', 'text' => __( 'Your access key has been verified.', 'amtap' ) ), 'new_key_empty' => array( 'status' => 'error', 'text' => __( 'Your access key has been cleared. However, a valid key is required to access Amazon Web Services.', 'amtap' ) ), 'ajax_key_empty' => array( 'status' => 'error', 'text' => __( 'The access key is empty. However, a valid key is required to access Amazon Web Services.', 'amtap' ) ), 'new_key_invalid' => array( 'status' => 'error', 'text' => __( 'The access key you entered is invalid. Please double-check it.', 'amtap' ) ), 'no_connection' => array( 'status' => 'error', 'text' => __( 'No connection to Amazon Web Services. Please retry later.', 'amtap' ) ), 'no_directory' => array( 'status' => 'error', 'text' => __( 'Cache directory does not exist. Please create wp-content/cache/', 'amtap' ) ), 'cache_not_writable' => array( 'status' => 'error', 'test' => __( "Cannot open $sCachefile. Check directory permissions.", 'amtap' ) ) ); // Print or return status messages if ( count( $ms ) ) { foreach ( $ms as $m ) { $sMessage = $messages[$m]['status'].';;'.$messages[$m]['text']; if ( $is_ajax ) { // Return to AJAX echo $sMessage; } else { // Return to PHP return $sMessage; } } } else { return false; } } /** * get_the_tags() - Return an array of post tags * * Checks different ways to access implemented tags and returns them * as an array or FALSE if there are no tags * @global object $wpdb * @global object $wp_query * @global object $post * @return array|boolean $aTags|false */ function get_the_tags() { global $wpdb, $wp_query, $post; $aTags = array(); // Get tags from the WP 2.3 core function if ( function_exists( get_the_tags ) ) { $aWpTags = get_the_tags(); if ( $aWpTags ) { foreach( $aWpTags as $tag ) { $aTags[] = $tag->name; } } } // Try to get it somewhere else if ( !count( $aTags ) ) { if ( function_exists( get_bunny_tags ) ) { // Bunny Tags // Try to get the tags from the tags field $aTags = get_bunny_tags( ' ', 'tags' ); // Try to get the tags from the keywords field if ( !count( $aTags ) ) $aTags = get_bunny_tags( ', ', 'keywords' ); } else if ( function_exists( get_the_post_keywords ) ) { // Jerome's Keywords $aTags = explode( ',', get_the_post_keywords() ); } } // Return the array or false if ( count( $aTags ) ) { return $aTags; } else { return false; } } /** * get_machine_tags() - Return an array of filtered machine tags * * Searches for tags matching the machine tag structure with "isbn", "ean", or "asin" as predicate * @return array|boolean $aFilteredTags|false */ function get_machine_tags( $aTags = '' ) { // Get tags if ( !$aTags ) $aTags = AMTAP::get_the_tags(); $aFilteredTags = array(); if ( $aTags ) { foreach ( $aTags as $sTag ) { // Search for tags matching the machine tag structure with "isbn", "ean", or "asin" as predicate if ( preg_match( '/(\w+):(\w+)=(\w+)/i', $sTag, $matches ) && ( strpos( $matches[2], 'isbn' ) !== false || strpos( $matches[2], 'ean' ) !== false || strpos( $matches[2], 'asin' ) !== false ) ) { $aFilteredTags[] = $matches[3]; } } } // Return the array or false if ( count( $aFilteredTags ) ) { return $aFilteredTags; } else { return false; } } /** * get_tags_from_content() - Get tags from WordPress content * * Searches for a flag in the form [amtap book:isbn=1234567890] * @param string $sContent * @return string $sContent flags replaced by items */ function get_tags_from_content( $sContent ) { if ( strpos( $sContent, '[amtap' ) !== false ) { // Get all tags preg_match_all( '/\[amtap\s([^\]]*)\]/six', $sContent, $aMatches ); foreach ( $aMatches[0] as $sFlag ) { // Add slashes for the regex $aFlags[] = '/\\' . $sFlag . '/'; } // Get items from Amazon $sItems = AMTAP::get_items( $aMatches[1] ); // Remove line breaks to prevent the automatic inclusion of
elements $sItems = preg_replace( '/(\015\012)|(\015)|(\012)/', '', $sItems); // Match anything within a division preg_match_all( '/]*>(.*?)<\/div>/ix', $sItems, $aItems, null ); // Replace flags with items $sContent = preg_replace( $aFlags, $aItems[0], $sContent ); } return $sContent; } /** * get_locale() - Get locale by IP * @param boolean $bInit * @return string $sLocale */ function get_locale( $bInit = false ) { // Set array of Amazon locales $aLocales = array( 'ca', 'de', 'fr', 'jp', 'uk', 'us' ); // Set EU country codes $aEurope = array( 'ad', 'al', 'at', 'ba', 'be', 'bg', 'by', 'ch', 'cz', 'de', 'dk', 'ee', 'es', 'fi', 'fr', 'gr', 'hr', 'hu', 'ie', 'it', 'kz', 'li', 'lu', 'lv', 'mc', 'md', 'me', 'mk', 'mt', 'nl', 'no', 'pl', 'pt', 'ro', 'rs', 'ru', 'se', 'si', 'sk', 'sm', 'tr', 'ua', 'va', 'uk', 'yu' ); if ( function_exists( 'wp_ip2c_getCountryCode2' ) ) { // Get 2 letter country code from IP to Country DB $sCountrycode = wp_ip2c_getCountryCode2( 0, $_SERVER[ 'REMOTE_ADDR' ] ); if ( $sCountrycode == 'gb' ) $sCountrycode = 'uk'; // Compare country codes with locales foreach ( $aLocales as $sLocale ) { // Country code is exact match if ( $sLocale == $sCountrycode ) return $sLocale; } // no exact match, but country is within Europe foreach ( $aEurope as $sCountry ) { // Country is within the EU if ( $sCountry == $sCountrycode ) { // Match languages $sLang = AMTAP::get_language(); return ( $sLang == 'en' ) ? 'uk' : $sLang; } } } // no match: return default or US if default is not set return ( $bInit ) ? 'us' : get_option( 'amtap_associate_default' ); } /** * get_language() - Get HTTP header accept languages */ function get_language() { // European Amazon languages $aLang = array( 'de', 'en', 'fr' ); $aUserLang = array(); // Get browser languages from HTTP accept header if ( array_key_exists( 'HTTP_ACCEPT_LANGUAGE', $_SERVER ) && $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) { $aAcceptLang = explode( ',', $_SERVER['HTTP_ACCEPT_LANGUAGE'] ); foreach ( $aAcceptLang as $sAcceptLang ) { $aTempLang = explode( ';', $sAcceptLang ); $aTempLangBase = explode( '-', trim( $aTempLang[0] ) ); $aUserLang[] = trim( $aTempLangBase[0] ); } } foreach ( $aUserLang as $sUserLang ) { foreach ( $aLang as $sLang ) { // Compare European Amazon languages with browser accept languages if ( $sUserLang == $sLang ) return $sLang; } } // No match or no HTTP accept header: return default return 'en'; } /** * get_author_tag() - Donate unused Amazon credits to the author * * If the blog owner has chosen to donate Amazon karma for locales where he is * not an Amazon Associate, return the author's Associate Tag * * @param string $sTld * @return string $aAuthorTags[ $sTld ] */ function get_author_tag( $sTld ) { $aAuthorTags = array( 'ca' => 'leartheworl04-20', 'de' => 'leartheworl-21', 'fr' => 'leartheworl0d-21', 'jp' => 'leartheworl-22', 'uk' => 'leartheworl0c-21', 'us' => 'leartheworl-20' ); return $aAuthorTags[ $sTld ]; } /** * sort_locales() - Sort locales by locale name * * Sorts (translated) locales alphabetically on the admin page * * @param array $aLocales * @return array $aSortedLocales */ function sort_locales( $aLocales ) { $aHash = array(); foreach ( $aLocales as $key => $value ) { $aHash[ $aLocales[ $key ][ 'locale' ] ] = $value; $aHash[ $aLocales[ $key ][ 'locale' ] ][ 'code' ] = $key; } // Sort by key ksort( $aHash ); // Construct the output array $aSortedLocales = array(); foreach ( $aHash as $aLocale ) { $aSortedLocales[] = $aLocale; } return $aSortedLocales; } /** * admin_css() - Add CSS to the admin interface */ function admin_css() { echo '' . "\n"; } /** * admin_js() - Add the YUI AJAX library and local JavaScript to the admin interface * @return string sLoadingMessage Translated JavaScript variable */ function admin_js() { echo ' '; } /** * blog_css() - Add CSS to the blog interface */ function blog_css() { echo '' . "\n"; } } // initialize the plugin add_action('init', array( 'AMTAP', 'init' ) ); ?>