GeoMashup
[ class tree: GeoMashup ] [ index: GeoMashup ] [ all elements ]

Source for file geo-mashup.php

Documentation is available at geo-mashup.php

  1. <?php 
  2. /*
  3. Plugin Name: Geo Mashup
  4. Plugin URI: http://code.google.com/p/wordpress-geo-mashup/ 
  5. Description: Save location for posts and pages, or even users and comments. Display these locations on Google maps. Make WordPress into your GeoCMS.
  6. Version: 1.4beta5
  7. Author: Dylan Kuhn
  8. Author URI: http://www.cyberhobo.net/
  9. Minimum WordPress Version Required: 3.0
  10. License: GPL2+
  11. */
  12.  
  13. /*  Copyright 2011  Dylan Kuhn  (email : cyberhobo@cyberhobo.net)
  14.  
  15.     This program is free software; you can redistribute it and/or modify
  16.     it under the terms of the GNU General Public License, version 2 or later, as
  17.     published by the Free Software Foundation.
  18.  
  19.     This program is distributed in the hope that it will be useful,
  20.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  21.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22.     GNU General Public License for more details.
  23.  
  24.     You should have received a copy of the GNU General Public License
  25.     along with this program; if not, write to the Free Software
  26.     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  27. */
  28.  
  29. /**
  30.  * The main Geo Mashup plugin file loaded by WordPress.
  31.  *
  32.  * @package GeoMashup
  33.  */
  34.  
  35. if ( !class_exists( 'GeoMashup' ) ) {
  36. /**
  37.  * The Geo Mashup static class.
  38.  *
  39.  * Used primarily for namespace, with methods called using the scope operator,
  40.  * like echo GeoMashup::map();
  41.  *
  42.  * @package GeoMashup
  43.  * @since 1.0
  44.  * @access public
  45.  * @static
  46.  */
  47. class GeoMashup {
  48.     /**
  49.      * Whether to add the click-to-load map script.
  50.      *
  51.      * @since 1.4
  52.      */
  53.     private static $add_loader_script = false;
  54.  
  55.     /**
  56.      * Load Geo Mashup.
  57.      * 
  58.      * Initializations that can be done before init().
  59.      *
  60.      * @since 1.2
  61.      */
  62.     public static function load() {
  63.         GeoMashup::load_constants();
  64.         load_plugin_textdomain( 'GeoMashup', '', path_join( GEO_MASHUP_DIRECTORY, 'lang' ) );
  65.  
  66.         GeoMashup::load_dependencies();
  67.         GeoMashup::load_hooks();
  68.     }
  69.  
  70.     /**
  71.      *    Test to see if the current request is for the Geo Mashup options page.
  72.      *
  73.      * @since 1.4
  74.      *
  75.      * @return bool Whether this is a an options page request.
  76.      */
  77.     public static function is_options_page() {
  78.         // We may need this before $pagenow is set, but maybe this method won't always work?
  79.         return ( is_admin() and isset($_GET['page']) and GEO_MASHUP_PLUGIN_NAME === $_GET['page'] );
  80.     }
  81.  
  82.     /**
  83.      * WordPress init action.
  84.      *
  85.      * init {@link http://codex.wordpress.org/Plugin_API/Action_Reference#Advanced_Actions action},
  86.      * called by WordPress.
  87.      *
  88.      * @since 1.2
  89.      */
  90.     public static function init() {
  91.         GeoMashup::load_styles();
  92.         GeoMashup::load_scripts();
  93.     }
  94.  
  95.     /**
  96.      * Load relevant dependencies.
  97.      * 
  98.      * @since 1.2
  99.      */
  100.     private static function load_dependencies() {
  101.         include_once( GEO_MASHUP_DIR_PATH . '/geo-mashup-options.php' );
  102.         include_once( GEO_MASHUP_DIR_PATH . '/geo-mashup-db.php' );
  103.         include_once( GEO_MASHUP_DIR_PATH . '/geo-mashup-ui-managers.php' );
  104.     if ( !is_admin() ) {
  105.             include_once( GEO_MASHUP_DIR_PATH . '/shortcodes.php');
  106.         }
  107.     }
  108.  
  109.     /**
  110.      * Load relevant hooks.
  111.      * 
  112.      * @since 1.2
  113.      */
  114.     private static function load_hooks() {
  115.         global $geo_mashup_options;
  116.  
  117.         add_action( 'init', array( __CLASS__, 'init' ) );
  118.  
  119.         add_action( 'plugins_loaded', array( __CLASS__, 'dependent_init' ), -1 );
  120.         add_action( 'wp_ajax_geo_mashup_query', array( __CLASS__, 'geo_query') );
  121.         add_action( 'wp_ajax_nopriv_geo_mashup_query', array( __CLASS__, 'geo_query') );
  122.         add_action( 'wp_ajax_geo_mashup_kml_attachments', array( __CLASS__, 'ajax_kml_attachments') );
  123.         add_action( 'wp_ajax_nopriv_geo_mashup_kml_attachments', array( __CLASS__, 'ajax_kml_attachments') );
  124.         add_action( 'wp_ajax_geo_mashup_suggest_custom_keys', array( 'GeoMashupDB', 'post_meta_key_suggest' ) );
  125.  
  126.         if (is_admin()) {
  127.  
  128.             register_activation_hook( __FILE__, array( __CLASS__, 'activation_hook' ) );
  129.  
  130.             // To add Geo Mashup settings page
  131.             add_action('admin_menu', array(__CLASS__, 'admin_menu'));
  132.  
  133.             // To make important announcements
  134.             add_action( 'admin_notices', array( __CLASS__, 'admin_notices' ) );
  135.  
  136.             // To add plugin listing links
  137.             add_filter( 'plugin_action_links', array( __CLASS__, 'plugin_action_links' ), 10, 2 );
  138.             add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 );
  139.  
  140.         } else {
  141.  
  142.             // This is a non-admin request
  143.  
  144.             if ($geo_mashup_options->get('overall','add_category_links') == 'true') {
  145.                 // To add map links to a category list - flaky, requires non-empty category description
  146.                 add_filter('list_cats', array(__CLASS__, 'list_cats'), 10, 2);
  147.             }
  148.  
  149.             // To output location meta tags in the page head
  150.             add_action('wp_head', array(__CLASS__, 'wp_head'));
  151.  
  152.             // To add footer output (like scripts)
  153.             add_action( 'wp_footer', array( __CLASS__, 'wp_footer' ) );
  154.  
  155.             // To allow shortcodes in the text widget
  156.             if ( ! has_filter( 'widget_text', 'do_shortcode' ) ) {
  157.                 add_filter( 'widget_text', 'do_shortcode', 11 );
  158.             }
  159.  
  160.             // To add the GeoRSS namespace to feeds (not available for RSS 0.92)
  161.             add_action('rss2_ns', array(__CLASS__, 'rss_ns'));
  162.             add_action('atom_ns', array(__CLASS__, 'rss_ns'));
  163.  
  164.             // To add GeoRSS location to feeds
  165.             add_action('rss2_item', array(__CLASS__, 'rss_item'));
  166.             add_action('atom_entry', array(__CLASS__, 'rss_item'));
  167.  
  168.             // To add custom renderings
  169.             add_filter( 'query_vars', array( __CLASS__, 'query_vars' ) );
  170.             add_action( 'template_redirect', array( __CLASS__, 'template_redirect' ) );
  171.  
  172.         }
  173.     }
  174.  
  175.     /**
  176.      * Define Geo Mashup constants.
  177.      * 
  178.      * @since 1.2
  179.      */
  180.     private static function load_constants() {
  181.         define('GEO_MASHUP_PLUGIN_NAME', plugin_basename(__FILE__));
  182.         define('GEO_MASHUP_DIR_PATH', dirname( __FILE__ ));
  183.         define('GEO_MASHUP_DIRECTORY', dirname( GEO_MASHUP_PLUGIN_NAME ) );
  184.         define('GEO_MASHUP_URL_PATH', trim( plugin_dir_url( __FILE__ ), '/' ) );
  185.         define('GEO_MASHUP_MAX_ZOOM', 20);
  186.         // Make numeric versions: -.02 for alpha, -.01 for beta
  187.         define('GEO_MASHUP_VERSION', '1.3.99.5');
  188.         define('GEO_MASHUP_DB_VERSION', '1.3');
  189.     }
  190.  
  191.     /**
  192.      * Load relevant scripts.
  193.      * 
  194.      * @since 1.2
  195.      */
  196.     private static function load_scripts() {
  197.         if ( self::is_options_page() )
  198.             wp_enqueue_script( 'jquery-ui-tabs' );
  199.     }
  200.  
  201.     /**
  202.      * Load relevant styles.
  203.      * 
  204.      * @since 1.2
  205.      */
  206.     private static function load_styles() {
  207.         if ( self::is_options_page() ) {
  208.             self::register_style( 'jquery-smoothness', 'css/jquery-ui.1.7.smoothness.css', array(), GEO_MASHUP_VERSION, 'screen' );
  209.             wp_enqueue_style( 'jquery-smoothness' );
  210.             wp_enqueue_script( 'suggest' );
  211.         }
  212.     }
  213.  
  214.     /**
  215.      * WordPress hook to perform activation tasks.
  216.      * 
  217.      * @since 1.4
  218.      */
  219.     public static function activation_hook() {
  220.         global $geo_mashup_options;
  221.         GeoMashupDB::install();
  222.         if ( '' == $geo_mashup_options->get( 'overall', 'version' ) and '' != $geo_mashup_options->get( 'overall', 'google_key' ) ) {
  223.             // Upgrading from a pre-1.4 version - don't set the default provider to Google v3
  224.             $geo_mashup_options->set_valid_options(
  225.                 array(
  226.                     'overall' => array(
  227.                         'map_api' => 'google',
  228.                         'version' => GEO_MASHUP_VERSION
  229.                     )
  230.                 )
  231.             );
  232.             $geo_mashup_options->save();
  233.         }
  234.     }
  235.  
  236.     /**
  237.      * WordPress action to supply an init action for plugins that would like to use Geo Mashup APIs.
  238.      * 
  239.      * @since 1.4
  240.      * @uses do_action() geo_mashup_init Fired when Geo Mashup is loaded and ready.
  241.      */
  242.     public static function dependent_init() {
  243.         do_action( 'geo_mashup_init' );
  244.     }
  245.  
  246.     /**
  247.      * Register the Geo Mashup script appropriate for the request.
  248.      *
  249.      * @since 1.4
  250.      * 
  251.      * @param string $handle Global tag for the script.
  252.      * @param string $src Path to the script from the root directory of Geo Mashup.
  253.      * @param array $deps Array of dependency handles.
  254.      * @param string $ver Script version.
  255.      * @param bool $in_footer Whether the script can be loaded in the footer.
  256.      */
  257.     public static function register_script( $handle, $src, $deps = array(), $ver = false, $in_footer = false ) {
  258.         // Use the .dev version if SCRIPT_DEBUG is set or there is no minified version
  259.         if ( ( defined( 'SCRIPT_DEBUG' ) and SCRIPT_DEBUG ) or !is_readable( path_join( GEO_MASHUP_DIR_PATH, $src ) ) )
  260.             $src = preg_replace( '/(\.\w*)$/', '.dev$1', $src );
  261.         wp_register_script( $handle, plugins_url( $src, __FILE__ ), $deps, $ver, $in_footer );
  262.     }
  263.  
  264.     /**
  265.      * Register the Geo Mashup style appropriate for the request.
  266.      *
  267.      * @since 1.4
  268.      *
  269.      * @param string $handle Global tag for the style.
  270.      * @param string $src Path to the stylesheet from the root directory of Geo Mashup.
  271.      * @param array $deps Array of dependency handles.
  272.      * @param string $ver Script version.
  273.      * @param bool $media Stylesheet media target.
  274.      */
  275.     public static function register_style( $handle, $src, $deps = array(), $ver = false, $media = 'all' ) {
  276.         // Use the .dev version if SCRIPT_DEBUG is set or there is no minified version
  277.         if ( ( defined( 'SCRIPT_DEBUG' ) and SCRIPT_DEBUG ) or !is_readable( path_join( GEO_MASHUP_DIR_PATH, $src ) ) )
  278.             $src = preg_replace( '/(\.\w*)$/', '.dev$1', $src );
  279.  
  280.         wp_register_style( $handle, plugins_url( $src, __FILE__ ), $deps, $ver, $media );
  281.     }
  282.  
  283.     /**
  284.      * WordPress action to add things like scripts to the footer.
  285.      * 
  286.      * @since 1.4
  287.      */
  288.     public static function wp_footer() {
  289.         if ( self::$add_loader_script ) {
  290.             self::register_script( 'geo-mashup-loader', 'js/loader.js', array(), GEO_MASHUP_VERSION, true );
  291.             wp_print_scripts( 'geo-mashup-loader' );
  292.         }
  293.     }
  294.  
  295.     /**
  296.      * WordPress filter to add Geo Mashup query variables.
  297.      *
  298.      * @since 1.3
  299.      */
  300.     public static function query_vars( $public_query_vars ) {
  301.         $public_query_vars[] = 'geo_mashup_content';
  302.         return $public_query_vars;
  303.     }
  304.  
  305.     /**
  306.      *    Locate a Geo Mashup template.
  307.      * 
  308.      * Geo Mashup looks for templates given a certain base name. Given a base
  309.      * name of 'info-window', it will return the first of:
  310.      *     'geo-mashup-info-window.php' in the active theme directory
  311.      *     'info-window.php' in the geo-mashup-custom plugin directory
  312.      *     'default-templates/info-window.php' in the geo-mashup plugin directory
  313.      *
  314.      * @since 1.4
  315.      * 
  316.      * @param string $template_base The base name of the template.
  317.      * @return string The file path of the template found.
  318.      */
  319.     public static function locate_template( $template_base ) {
  320.         global $geo_mashup_custom;
  321.         $template = locate_template( array("geo-mashup-$template_base.php") );
  322.         if ( empty( $template ) and isset( $geo_mashup_custom ) and $geo_mashup_custom->file_url( $template_base . '.php' ) ) {
  323.             $template = path_join( $geo_mashup_custom->dir_path, $template_base . '.php' );
  324.         }
  325.         if ( empty( $template ) or !is_readable( $template ) ) {
  326.             $template = path_join( GEO_MASHUP_DIR_PATH, "default-templates/$template_base.php" );
  327.         }
  328.         if ( empty( $template ) or !is_readable( $template ) ) {
  329.             // If all else fails, just use the default info window template
  330.             $template = path_join( GEO_MASHUP_DIR_PATH, 'default-templates/info-window.php' );
  331.         }
  332.         return $template;
  333.     }
  334.  
  335.     /**
  336.      * WordPress action to deliver templated Geo Mashup content.
  337.      *
  338.      * @since 1.3
  339.      *
  340.      * @uses geo_query
  341.      * @uses render_map
  342.      */
  343.     public static function template_redirect() {
  344.         $geo_mashup_content = get_query_var( 'geo_mashup_content' );
  345.         if ( ! empty( $geo_mashup_content ) ) {
  346.  
  347.             // The parameter's purpose is to get us here, we can remove it now
  348.             unset( $_GET['geo_mashup_content'] );
  349.  
  350.             // Call the function corresponding to the content request
  351.             // This provides some security, as only implemented methods will be executed
  352.             $method = str_replace( '-', '_', $geo_mashup_content );
  353.             call_user_func( array( __CLASS__, $method ) );
  354.             exit();
  355.         }
  356.     }
  357.  
  358.     /**
  359.      * Process an AJAX geo query.
  360.      *
  361.      * @since 1.3
  362.      * @uses geo-query.php
  363.      */
  364.     public static function geo_query() {
  365.         require_once( 'geo-query.php' );
  366.         exit();
  367.     }
  368.  
  369.     /**
  370.      * Process an iframe map request.
  371.      *
  372.      * @since 1.3
  373.      * @uses render-map.php
  374.      */
  375.     private static function render_map() {
  376.         require_once( 'render-map.php' );
  377.         GeoMashupRenderMap::render_map();
  378.         exit();
  379.     }
  380.  
  381.     /**
  382.      * WordPress action to perform an ajax edit operation and echo results.
  383.      *
  384.      * @since 1.3
  385.      */
  386.     public static function ajax_edit() {
  387.         check_ajax_referer( 'geo-mashup-edit', 'geo_mashup_nonce' );
  388.         unset( $_GET['_wpnonce'] );
  389.  
  390.         $status = array( 'request' => 'ajax-edit', 'code' => 200 );
  391.         if ( isset( $_POST['geo_mashup_object_id'] ) ) {
  392.             $status['object_id'] = $_POST['geo_mashup_object_id'];
  393.         } else {
  394.             $status['code'] = 400;
  395.             $status['message'] = __( 'No object id posted.', 'GeoMashup' );
  396.             $status['object_id'] = '?';
  397.         }
  398.  
  399.         /** @todo add an option for a user capability check here? */
  400.  
  401.         if ( 200 == $status['code'] and ! empty( $_POST['geo_mashup_ui_manager'] ) ) {
  402.             $ui_manager = GeoMashupUIManager::get_instance( $_POST['geo_mashup_ui_manager'] );
  403.             $result = $ui_manager->save_posted_object_location( $status['object_id'] );
  404.             if ( is_wp_error( $result ) ) {
  405.                 $status['code'] = 500;
  406.                 $status['message'] = $result->get_error_message();
  407.             }
  408.         }
  409.  
  410.         if ( 200 == $status['code'] ) {
  411.             if ( ! empty( $_REQUEST['geo_mashup_update_location'] ) ) {
  412.                 $status['message'] = __( 'Location updated.', 'GeoMashup' );
  413.             } else if ( ! empty( $_REQUEST['geo_mashup_delete_location'] ) ) {
  414.                 $status['message'] = __( 'Location deleted.', 'GeoMashup' );
  415.             } else if ( ! empty( $_REQUEST['geo_mashup_add_location'] ) ) {
  416.                 $status['message'] = __( 'Location added.', 'GeoMashup' );
  417.             }
  418.         } 
  419.  
  420.         echo json_encode( array( 'status' => $status ) );
  421.         exit();
  422.     }
  423.  
  424.     /**
  425.      * Toggle limiting of query_posts to located posts only,
  426.      * with Geo Mashup query extensions.
  427.      *
  428.      * When enabled, only posts with locations will be returned from
  429.      * WordPress query_posts() and related functions. Also adds Geo Mashup
  430.      * public query variables.
  431.      *
  432.      * Caution - what if a future Geo Mashup incorporates multiple locations per object?
  433.      *
  434.      * @since 1.3
  435.      *
  436.      * @param bool $yes_or_no Whether to activate the join or not.
  437.      */
  438.     public static function join_post_queries( $yes_or_no ) {
  439.         GeoMashupDB::join_post_queries( $yes_or_no );
  440.     }
  441.  
  442.     /**
  443.      * Helper to turn a string of key-value pairs into an associative array.
  444.      *
  445.      * @since 1.0
  446.      *
  447.      * @param string $glue1 Pair separator.
  448.      * @param string $glue2 Key/value separator.
  449.      * @param string $str String to explode.
  450.      * @return array The associative array.
  451.      */
  452.     public static function explode_assoc($glue1, $glue2, $str) {
  453.         $array2=explode($glue2, $str);
  454.         foreach($array2 as  $val) {
  455.             $pos=strpos($val,$glue1);
  456.             $key=substr($val,0,$pos);
  457.             $array3[$key] =substr($val,$pos+1,strlen($val));
  458.         }
  459.         return $array3;
  460.     }
  461.  
  462.     /**
  463.      * Helper to turn an associative array into a string of key-value pairs.
  464.      * 
  465.      * @since 1.0
  466.      *
  467.      * @param string $inner_glue Key/value separator.
  468.      * @param string $outer_glue Pair separator.
  469.      * @param array $array Array to implode.
  470.      * @param mixed $skip_empty Whether to include empty values in output.
  471.      * @param mixed $urlencoded Whetern to URL encode the output.
  472.      * @return string The imploded string.
  473.      */
  474.     public static function implode_assoc($inner_glue, $outer_glue, $array, $skip_empty=false, $urlencoded=false) {
  475.     $output = array();
  476.         foreach($array as $key=>$item) {
  477.             if (!$skip_empty || isset($item)) {
  478.                 if ($urlencoded)
  479.                     $output[] = preg_replace('/\s/', ' ', $key.$inner_glue.urlencode($item));
  480.                 else
  481.                     $output[] = preg_replace('/\s/', ' ', $key.$inner_glue.$item);
  482.             }
  483.         }
  484.         return implode($outer_glue, $output);
  485.     }
  486.  
  487.     /**
  488.      * Guess the best language code for the current context.
  489.      * 
  490.      * Takes some plugins and common practices into account.
  491.      * 
  492.      * @since 1.4
  493.      * 
  494.      * @return string Language code.
  495.      */
  496.     public static function get_language_code() {
  497.         $language_code = '';
  498.         if ( isset( $_GET['lang'] ) ) {
  499.             // A language override technique is to use this querystring parameter
  500.             $language_code = $_GET['lang'];
  501.         } else if ( function_exists( 'qtrans_getLanguage' ) ) {
  502.             // qTranslate integration
  503.             $language_code = qtrans_getLanguage();
  504.         } else if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
  505.             // WPML integration
  506.             $language_code = ICL_LANGUAGE_CODE;
  507.         } else {
  508.             $language_code = get_locale();
  509.         }
  510.         return $language_code;
  511.     }
  512.  
  513.     /**
  514.      * Get an array of URLs of KML or KMZ attachments for a post.
  515.      * 
  516.      * @since 1.1
  517.      *
  518.      * @param id $post_id 
  519.      * @return array Array of URL strings.
  520.      */
  521.     public static function get_kml_attachment_urls($post_id) {
  522.         if ( empty( $post_id ) ) {
  523.             return array();
  524.         }
  525.         $args = array(
  526.             'post_type' => 'attachment',
  527.             'numberposts' => null,
  528.             'post_status' => null,
  529.             'post_mime_type' => array(
  530.                 'application/vnd.google-earth.kml+xml',
  531.                 'application/vnd.google-earth.kmz',
  532.                 'application/octet-stream'
  533.             ),
  534.             'post_parent' => $post_id
  535.             ); 
  536.         $attachments = get_posts($args);
  537.         $urls = array();
  538.         if ($attachments) {
  539.             foreach ($attachments as $attachment) {
  540.                 $url = wp_get_attachment_url( $attachment->ID ); 
  541.                 // Backwards compatibility: include KML attachments with the incorrect octet-stream mime type
  542.                 if ( 'application/octet-stream' != $attachment->post_mime_type or 'kml' == substr( $url, -3 ) ) {
  543.                     array_push( $urls, $url );
  544.                 }
  545.             }
  546.         }
  547.         return $urls;
  548.     }
  549.  
  550.     /**
  551.      * Echo a JSONP array of URLs of KML or KMZ attachments for posts.
  552.      *
  553.      * since 1.4
  554.      */
  555.     public static function ajax_kml_attachments() {
  556.         $urls = array(); 
  557.         if ( !empty( $_REQUEST['post_ids'] ) ) {
  558.             $post_ids = array_map( 'intval', explode( ',', $_REQUEST['post_ids'] ) );
  559.             foreach( $post_ids as $post_id ) {
  560.                 $urls = array_merge( $urls, self::get_kml_attachment_urls( $post_id ) );
  561.             }
  562.         }
  563.         $json = json_encode( $urls );
  564.         if ( isset( $_REQUEST['callback'] ) )
  565.             $json = $_REQUEST['callback'] . '(' . $json . ')';
  566.         echo $json;
  567.         exit();
  568.     }
  569.  
  570. /**
  571.  * WordPress action to add relevant geo meta tags to the document head.
  572.  *
  573.  * wp_head {@link http://codex.wordpress.org/Plugin_API/Action_Reference#TemplateActions action},
  574.  * called by WordPress.
  575.  *
  576.  * @since 1.0
  577.  */
  578. public static function wp_head() {
  579.     global $wp_query;
  580.  
  581.     if (is_single())
  582.     {
  583.         $loc = GeoMashupDB::get_object_location( 'post', $wp_query->post->ID );
  584.         if (!empty($loc)) {
  585.             $title = esc_html(convert_chars(strip_tags(get_bloginfo('name'))." - ".$wp_query->post->post_title));
  586.             echo '<meta name="ICBM" content="' . esc_attr( $loc->lat . ', ' . $loc->lng ) . '" />' . "\n";
  587.             echo '<meta name="DC.title" content="' . esc_attr( $title ) . '" />' . "\n";
  588.             echo '<meta name="geo.position" content="' .  esc_attr( $loc->lat . ';' . $loc->lng ) . '" />' . "\n";
  589.         }
  590.     }
  591. }
  592.  
  593. /**
  594.  * Query object locations and return JSON.
  595.  *
  596.  * Offers customization per object location via a filter, geo_mashup_locations_json_object.
  597.  *
  598.      * @since 1.2
  599.      * @uses GeoMashupDB::get_object_locations()
  600.      * @uses apply_filters() the_title Filter post titles.
  601.      * @uses apply_filters() geo_mashup_locations_json_object Filter each location associative array before conversion to JSON.
  602.      *
  603.      * @param string|array$query_args Query variables for GeoMashupDB::get_object_locations().
  604.      * @param string $format (optional) 'JSON' (default) or ARRAY_A
  605.      * @return string Queried object locations JSON ( { "object" : [...] } ).
  606.      */
  607.     public static function get_locations_json( $query_args, $format = 'JSON' ) {
  608.         $default_args = array( 'object_name' => 'post' );
  609.         $query_args = wp_parse_args( $query_args, $default_args );
  610.         $json_objects = array();
  611.         $objects = GeoMashupDB::get_object_locations( $query_args );
  612.         if ( $objects ) {
  613.             foreach ($objects as $object) {
  614.                 $category_ids = array();
  615.                 $author_name = '';
  616.                 $attachments = array();
  617.                 if ( 'post' == $query_args['object_name'] ) {
  618.  
  619.                     // Filter the title
  620.                     $object->label = sanitize_text_field( apply_filters( 'the_title', $object->label, $object->object_id ) );
  621.  
  622.                     // Only know about post categories now, but could abstract to objects
  623.                     if ( !defined( 'GEO_MASHUP_DISABLE_CATEGORIES' ) )
  624.                         $category_ids = wp_get_object_terms( $object->object_id, 'category', array( 'fields' => 'ids' ) );
  625.  
  626.                     // Include post author
  627.                     $author = get_userdata( $object->post_author );
  628.                     if ( empty( $author ) ) {
  629.                         $author_name = '';
  630.                     } else {
  631.                         $author_name = $author->display_name;
  632.                     }
  633.                 }
  634.  
  635.                 $json_object = array(
  636.                     'object_name' => $query_args['object_name'],
  637.                     'object_id' => $object->object_id,
  638.                     // We should be able to use real UTF-8 characters in titles
  639.                     // Helps with the spelling-out of entities in tooltips
  640.                     'title' => html_entity_decode( $object->label, ENT_COMPAT, 'UTF-8' ),
  641.                     'lat' => $object->lat,
  642.                     'lng' => $object->lng,
  643.                     'author_name' => $author_name,
  644.                     'categories' => $category_ids
  645.                 );
  646.  
  647.                 // Allow companion plugins to add data
  648.                 $json_object = apply_filters( 'geo_mashup_locations_json_object', $json_object, $object );
  649.                 $json_objects[] = $json_object;
  650.             }
  651.         }
  652.         if ( ARRAY_A == $format ) 
  653.             return array( 'objects' => $json_objects );
  654.         else
  655.             return json_encode( array( 'objects' => $json_objects ) );
  656.     }
  657.  
  658.     /**
  659.      * Convert depricated attribute names.
  660.      *
  661.      * @since 1.3
  662.      * 
  663.      * @param array $atts Attributes to modify.
  664.      */
  665.     private static function convert_map_attributes( &$atts ) {
  666.         $attribute_conversions = array( 
  667.             'auto_open_info_window' => 'auto_info_open',
  668.             'open_post_id' => 'open_object_id'
  669.         );
  670.         foreach ( $attribute_conversions as $old_key => $new_key ) {
  671.             if ( isset( $atts[$old_key] ) ) {
  672.                 if ( ! isset( $atts[$new_key] ) ) {
  673.                     $atts[$new_key] = $atts[$old_key];
  674.                 }
  675.                 unset( $atts[$old_key] );
  676.             }
  677.         }
  678.     }
  679.  
  680.     /**
  681.      * Build the data for a javascript map.
  682.      *
  683.      * Parameters are used both to retrieve data and as options to
  684.      * eventually pass to the javascript.
  685.      *
  686.      * @since 1.4
  687.      * @uses GeoMashup::get_locations_json()
  688.      *
  689.      * @global array $geo_mashup_options 
  690.      * @global object $geo_mashup_custom 
  691.      * @param array $query Query parameters
  692.      * @return array Map data ready to be rendered.
  693.      */
  694.     public static function build_map_data( $query ) {
  695.         global $geo_mashup_options, $geo_mashup_custom;
  696.         $defaults = array(
  697.             'map_api' => $geo_mashup_options->get( 'overall', 'map_api' )
  698.         );
  699.         $query = wp_parse_args( $query, $defaults );
  700.         $object_id = isset( $query['object_id'] ) ? $query['object_id'] : 0;
  701.         unset( $query['object_id'] );
  702.  
  703.         $map_data = $query + array(
  704.             'ajaxurl' => admin_url( 'admin-ajax.php' ),
  705.             'siteurl' => home_url(), // qTranslate doesn't work with get_option( 'home' )
  706.             'url_path' => GEO_MASHUP_URL_PATH,
  707.             'template_url_path' => get_stylesheet_directory_uri()
  708.         );
  709.         if ( isset( $geo_mashup_custom ) ) {
  710.             $map_data['custom_url_path'] = $geo_mashup_custom->url_path;
  711.         }
  712.  
  713.         $map_content = ( isset( $query['map_content'] ) ) ? $query['map_content'] : null;
  714.         $object_name = ( isset( $query['object_name'] ) ) ? $query['object_name'] : 'post';
  715.  
  716.         if ( $map_content == 'single') {
  717.             $location = GeoMashupDB::get_object_location( $object_name, $object_id, ARRAY_A );
  718.             $options = $geo_mashup_options->get( 'single_map' );
  719.             if ( !empty( $location ) ) {
  720.                 $map_data['object_data'] = array( 'objects' => array( $location ) );
  721.                 $map_data['center_lat'] = $location['lat'];
  722.                 $map_data['center_lng'] = $location['lng'];
  723.             }
  724.             $map_data = array_merge ( $options, $map_data );
  725.             if ( 'post' == $object_name ) {
  726.                 $kml_urls = self::get_kml_attachment_urls( $object_id );
  727.                 if (count($kml_urls)>0) {
  728.                     $map_data['load_kml'] = array_pop( $kml_urls );
  729.                 }
  730.             }
  731.         } else {
  732.             // Map content is not single
  733.             $map_data['context_object_id'] = $object_id;
  734.  
  735.             if ( $map_content == 'contextual' ) {
  736.                 $options = $geo_mashup_options->get( 'context_map' );
  737.                 // If desired we could make these real options
  738.                 $options['auto_info_open'] = 'false';
  739.             } else {
  740.                 $options = $geo_mashup_options->get( 'global_map' );
  741.                 // Category options done during render
  742.                 unset( $options['category_color'] );
  743.                 unset( $options['category_line_zoom'] );
  744.                 if ( empty( $query['show_future'] ) )
  745.                     $query['show_future'] = $options['show_future'];
  746.                 if ( is_null( $map_content ) ) 
  747.                     $options['map_content'] = 'global';
  748.             }
  749.  
  750.             if ( isset( $options['add_google_bar'] ) and 'true' == $options['add_google_bar'] ) {
  751.                 $options['adsense_code'] = $geo_mashup_options->get( 'overall', 'adsense_code' );
  752.             }
  753.  
  754.             // We have a lot map control parameters that don't effect the locations query,
  755.             // but only the relevant ones are used
  756.             $map_data['object_data'] = self::get_locations_json( $query, ARRAY_A );
  757.  
  758.             // Incorporate parameters from the query and options
  759.             $map_data = array_merge( $query, $map_data );
  760.             $map_data = array_merge( $options, $map_data );
  761.         }
  762.         return $map_data;
  763.     }
  764.  
  765.     /**
  766.      * The map template tag.
  767.      *
  768.      * Returns HTML for a Google map. Must use with echo in a template: echo GeoMashup::map();.
  769.      *
  770.      * @since 1.0
  771.      * @link http://code.google.com/p/wordpress-geo-mashup/wiki/TagReference#Map tag parameter documentation
  772.      * @uses $_SERVER['QUERY_STRING'] The first global map on a page uses query string parameters like tag parameters.
  773.      * @staticvar $map_number Used to index maps per request.
  774.      *
  775.      * @param string|array$atts Template tag parameters.
  776.      * @return string The HTML for the requested map.
  777.      */
  778.     public static function map( $atts = null ) {
  779.         global $wp_query, $in_comment_loop, $geo_mashup_options;
  780.         static $map_number = 0;
  781.  
  782.         $map_number++;
  783.         $atts = wp_parse_args( $atts );
  784.         $static = (bool)( !empty( $atts['static'] ) and 'true' == $atts['static'] );
  785.         unset( $atts['static'] );
  786.         if ( empty( $atts['lang'] ) ) {
  787.             if ( function_exists( 'qtrans_getLanguage' ) ) {
  788.                 // qTranslate integration
  789.                 $atts['lang'] = qtrans_getLanguage();
  790.             } else if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
  791.                 // WPML integration
  792.                 $atts['lang'] = ICL_LANGUAGE_CODE;
  793.             }
  794.         }
  795.         $click_to_load_options = array( 'click_to_load', 'click_to_load_text' );
  796.  
  797.         self::convert_map_attributes( $atts );
  798.  
  799.         // Default query is for posts
  800.         $object_name = ( isset( $atts['object_name'] ) ) ? $atts['object_name'] : 'post';
  801.  
  802.         // Find the ID and location of the container object if it exists
  803.         if ( 'post' == $object_name and $wp_query->in_the_loop ) {
  804.  
  805.             $context_object_id = $wp_query->post->ID;
  806.  
  807.         } else if ( 'comment' == $object_name and $in_comment_loop ) {
  808.             
  809.             $context_object_id = get_comment_ID();
  810.  
  811.         } else if ( 'user' == $object_name and $wp_query->post ) {
  812.  
  813.             $context_object_id = $wp_query->post->post_author;
  814.  
  815.         }
  816.         if ( empty( $atts['object_id'] ) and ! empty( $context_object_id ) ) {
  817.             
  818.             $atts['object_id'] = $context_object_id;
  819.             $context_location = GeoMashupDB::get_object_location( $object_name, $context_object_id );
  820.  
  821.         }
  822.  
  823.         // Map content type isn't required, so resolve it
  824.         $map_content = isset( $atts['map_content'] ) ? $atts['map_content'] : null;
  825.  
  826.         if ( empty ( $map_content ) ) {
  827.  
  828.             if ( empty( $context_object_id ) ) {
  829.                 $map_content = 'contextual';
  830.             } else if ( empty( $context_location ) ) {
  831.                 // Not located, go global
  832.                 $map_content = 'global';
  833.             } else {
  834.                 // Located, go single
  835.                 $map_content = 'single';
  836.             }
  837.  
  838.         }
  839.  
  840.         switch ($map_content) {
  841.             case 'contextual':
  842.                 $atts['map_content'] = 'contextual';
  843.                 $atts += $geo_mashup_options->get( 'context_map', $click_to_load_options );
  844.                 $object_ids = array();
  845.                 if ( 'comment' == $object_name ) {
  846.                     $context_objects = $wp_query->comments;
  847.                 } else {
  848.                     $context_objects = $wp_query->posts;
  849.                 }
  850.                 if ( !is_array( $context_objects ) ) {
  851.                     return '<!-- ' . __( 'Geo Mashup found no objects to map in this context', 'GeoMashup' ) . '-->';
  852.                 }
  853.                 foreach ( $context_objects as $context_object ) {
  854.                     if ( 'post' == $object_name ) {
  855.                         $object_ids[] = $context_object->ID;
  856.                     } else if ( 'user' == $object_name ) {
  857.                         $object_ids[] = $context_object->post_author;
  858.                     } else if ( 'comment' == $object_name ) {
  859.                         $object_ids[] = $context_object->comment_ID;
  860.                     }
  861.                 }
  862.                 $atts['object_ids'] = implode( ',', $object_ids );
  863.                 break;
  864.  
  865.             case 'single':
  866.                 $atts['map_content'] = 'single';
  867.                 $atts += $geo_mashup_options->get( 'single_map', $click_to_load_options );
  868.                 if ( empty( $atts['object_id'] ) ) {
  869.                     return '<!-- ' . __( 'Geo Mashup found no current object to map', 'GeoMashup' ) . '-->';
  870.                 }
  871.                 $single_location = GeoMashupDB::get_object_location( $object_name, $atts['object_id'] );
  872.                 if ( empty( $single_location ) ) {
  873.                     return '<!-- ' . __( 'Geo Mashup omitted a map for an object with no location', 'GeoMashup' ) . '-->';
  874.                 }
  875.                 break;
  876.  
  877.             case 'global':
  878.                 if ( isset( $_GET['template'] ) and 'full-post' == $_GET['template'] ) {
  879.                     // Global maps tags in response to a full-post query can infinitely nest, prevent this
  880.                     return '<!-- ' . __( 'Geo Mashup map omitted to avoid nesting maps', 'GeoMashup' ) . '-->';
  881.                 }
  882.                 $atts['map_content'] = 'global';
  883.                 if ( isset($_SERVER['QUERY_STRING']) and 1 == $map_number ) {
  884.                     // The first global map on a page will make use of query string arguments
  885.                     $atts = wp_parse_args( $_SERVER['QUERY_STRING'], $atts );
  886.                 } 
  887.                 $atts += $geo_mashup_options->get( 'global_map', $click_to_load_options );
  888.                 // Don't query more than max_posts
  889.                 $max_posts = $geo_mashup_options->get( 'global', 'max_posts' );
  890.                 if ( empty( $atts['limit'] ) and !empty( $max_posts ) )
  891.                     $atts['limit'] = $max_posts;
  892.                 break;
  893.  
  894.             default:
  895.                 return '<div class="gm-map"><p>Unrecognized value for map_content: "'.$map_content.'".</p></div>';
  896.         }
  897.         
  898.         $click_to_load = $atts['click_to_load'];
  899.         unset( $atts['click_to_load'] );
  900.         $click_to_load_text = $atts['click_to_load_text'];
  901.         unset( $atts['click_to_load_text'] );
  902.         if ( !isset( $atts['name'] ) )
  903.             $atts['name'] = 'gm-map-' . $map_number;
  904.  
  905.         $map_data = self::build_map_data( $atts );
  906.         if ( empty( $map_data['object_data']['objects'] ) and !isset( $map_data['load_empty_map'] ) )
  907.             return '<!-- ' . __( 'Geo Mashup omitted a map with no located objects found.', 'GeoMashup' ) . '-->';
  908.         unset( $map_data['load_empty_map'] );
  909.  
  910.         $map_image = '';
  911.         if ( $static ) {
  912.             // Static maps have a limit of 50 markers: http://code.google.com/apis/maps/documentation/staticmaps/#Markers
  913.             $atts['limit'] = empty( $atts['limit'] ) ? 50 : $atts['limit'];
  914.  
  915.             if ( !empty( $map_data['object_data']['objects'] ) ) {
  916.                 $image_width = str_replace( '%', '', $map_data['width'] );
  917.                 $image_height = str_replace( '%', '', $map_data['height'] );
  918.                 $map_image = '<img src="http://maps.google.com/maps/api/staticmap?size='.$image_width.'x'.$image_height;
  919.                 if ( count( $map_data['object_data']['objects'] ) == 1) {
  920.                     $map_image .= '&amp;center=' . $map_data['object_data']['objects'][0]['lat'] . ',' .
  921.                         $map_data['object_data']['objects'][0]['lng'];
  922.                 }
  923.                 $map_image .= '&amp;sensor=false&amp;zoom=' . $map_data['zoom'] . '&amp;markers=size:small|color:red';
  924.                 foreach( $map_data['object_data']['objects'] as $location ) {
  925.                     // TODO: Try to use the correct color for the category? Draw category lines?
  926.                     $map_image .= '|' . $location['lat'] . ',' . $location['lng'];
  927.                 }
  928.                 $map_image .= '" alt="geo_mashup_map"';
  929.                 if ($click_to_load == 'true') {
  930.                     $map_image .= '" title="'.$click_to_load_text.'"';
  931.                 }
  932.                 $map_image .= ' />';
  933.             }
  934.         }
  935.  
  936.         $atts_md5 =  md5( serialize( $atts ) );
  937.         set_transient( 'gmm' . $atts_md5, $map_data, 20 );
  938.         set_transient( 'gmp' . $atts_md5, $atts, 60*60*24 );
  939.  
  940.         $iframe_src =  home_url( '?geo_mashup_content=render-map&amp;map_data_key=' . $atts_md5 );
  941.         if ( !empty( $atts['lang'] ) )
  942.             $iframe_src .= '&amp;lang=' . $atts['lang'];
  943.             
  944.         $content = "";
  945.  
  946.         if ($click_to_load == 'true') {
  947.             if ( is_feed() ) {
  948.                 $content .= "<a href=\"{$iframe_src}\">$click_to_load_text</a>";
  949.             } else {
  950.                 self::$add_loader_script = true;
  951.                 $style = "height:{$map_data['height']}px;width:{$map_data['width']}px;background-color:#ddd;".
  952.                     "background-image:url(".GEO_MASHUP_URL_PATH."/images/wp-gm-pale.png);".
  953.                     "background-repeat:no-repeat;background-position:center;cursor:pointer;";
  954.                 $content = "<div class=\"gm-map\" style=\"$style\" " .
  955.                     "onclick=\"GeoMashupLoader.addMapFrame(this,'$iframe_src','{$map_data['height']}','{$map_data['width']}','{$map_data['name']}')\">";
  956.                 if ( $static ) {
  957.                     // TODO: test whether click to load really works with a static map
  958.                     $content .= $map_image . '</div>';
  959.                 } else {
  960.                     $content .= "<p style=\"text-align:center;\">$click_to_load_text</p></div>";
  961.                 }
  962.             }
  963.         } else if ( $static ) {
  964.             $content = "<div class=\"gm-map\">$map_image</div>";
  965.         } else {
  966.             $content =  "<div class=\"gm-map\"><iframe name=\"{$map_data['name']}\" src=\"{$iframe_src}\" " .
  967.                 "height=\"{$map_data['height']}\" width=\"{$map_data['width']}\" marginheight=\"0\" marginwidth=\"0\" ".
  968.                 "scrolling=\"no\" frameborder=\"0\"></iframe></div>";
  969.         }
  970.         return $content;
  971.     }
  972.  
  973.     /**
  974.      * Full post template tag.
  975.      *
  976.      * Returns a placeholder where a related map should display the full post content
  977.      * of the currently selected marker.
  978.      *
  979.      * @since 1.1
  980.      * @link http://code.google.com/p/wordpress-geo-mashup/wiki/TagReference#Full_Post
  981.      * 
  982.      * @param string|array$args Template tag arguments.
  983.      * @return string Placeholder HTML.
  984.      */
  985.     public static function full_post($args = null) {
  986.         $args = wp_parse_args($args);
  987.         $for_map = 'gm';
  988.         if ( !empty( $args['for_map'] ) ) {
  989.             $for_map = $args['for_map'];
  990.         }
  991.         // It's nice if click-to-load works in the full post display
  992.         self::$add_loader_script = true;
  993.  
  994.         return '<div id="' . $for_map . '-post"></div>';
  995.     }
  996.  
  997.     /**
  998.      * Category name template tag.
  999.      *
  1000.      * If there is a map_cat parameter, return the name of that category.
  1001.      *
  1002.      * @since 1.1
  1003.      * @link http://code.google.com/p/wordpress-geo-mashup/wiki/TagReference#Category_Name
  1004.      * 
  1005.      * @param string|array$option_args Template tag arguments.
  1006.      * @return string Category name.
  1007.      */
  1008.     public static function category_name($option_args = null) {
  1009.         $category_name = '';
  1010.         if (is_string($option_args)) {
  1011.             $option_args = wp_parse_args($option_args);
  1012.         }
  1013.         if (is_page() && isset($_SERVER['QUERY_STRING'])) {
  1014.             $option_args = $option_args + self::explode_assoc('=','&amp;',$_SERVER['QUERY_STRING']);
  1015.         }
  1016.         if (isset($option_args['map_cat'])) {
  1017.             $category_name = get_cat_name($option_args['map_cat']);
  1018.         }
  1019.         return $category_name;
  1020.     }
  1021.  
  1022.     /**
  1023.      * Category legend template tag.
  1024.      *
  1025.      * Returns a placeholder where a related map should display a legend for the
  1026.      * categories of the displayed content.
  1027.      *
  1028.      * @since 1.1
  1029.      * @link http://code.google.com/p/wordpress-geo-mashup/wiki/TagReference#Category_Legend
  1030.      * 
  1031.      * @param string|array$args Template tag arguments.
  1032.      * @return string Placeholder HTML.
  1033.      */
  1034.     public static function category_legend($args = null) {
  1035.         $args = wp_parse_args($args);
  1036.         $for_map = 'gm-map-1';
  1037.         if ( !empty( $args['for_map'] ) ) {
  1038.             $for_map = $args['for_map'];
  1039.         }    
  1040.         return '<div id="' . $for_map . '-legend"></div>';
  1041.     }
  1042.  
  1043.     /**
  1044.      * If the option is set, add a map link to category lists.
  1045.      *
  1046.      * list_cats {@link http://codex.wordpress.org/Plugin_API/Filter_Reference#Category_Filters filter}
  1047.      * called by WordPress.
  1048.      *
  1049.      * @since 1.0
  1050.      */
  1051.     public static function list_cats($content, $category = null) {
  1052.         global $geo_mashup_options;
  1053.  
  1054.         if ( $category and 'category' == $category->taxonomy ) {
  1055.             $count = GeoMashupDB::category_located_post_count( $category->cat_ID );
  1056.             // Add map link only if there are geo-located posts to see
  1057.             if ($count) {
  1058.                 // This feature doesn't work unless there is a category description
  1059.                 if ( empty( $category->description ) ) {
  1060.                     return $content . $geo_mashup_options->get('overall', 'category_link_separator') . 
  1061.                         __( 'You must add a description to this category to use this Geo Mashup feature.', 'GeoMashup' );
  1062.                 }
  1063.                 $link = '';
  1064.                 $url = get_page_link($geo_mashup_options->get('overall', 'mashup_page'));
  1065.                 if (strstr($url,'?')) {
  1066.                     $url .= '&amp;';
  1067.                 } else {
  1068.                     $url .= '?';
  1069.                 }
  1070.                 $link = '<a href="'.$url.'map_cat='.$category->cat_ID.'&amp;zoom='.$geo_mashup_options->get('overall', 'category_zoom').
  1071.                     '" title="'.$geo_mashup_options->get('overall', 'category_link_text').'">';
  1072.                 return $content.'</a>'.$geo_mashup_options->get('overall', 'category_link_separator').$link.
  1073.                     $geo_mashup_options->get('overall', 'category_link_text');
  1074.             }
  1075.         }
  1076.         return $content;
  1077.     }
  1078.  
  1079.     /**
  1080.      * WordPress action to add the Geo Mashup Options settings admin page.
  1081.      *
  1082.      * admin_menu {@link http://codex.wordpress.org/Plugin_API/Action_Reference#Advanced_Actions action}
  1083.      * called by WordPress.
  1084.      *
  1085.      * @since 1.0
  1086.      */
  1087.     public static function admin_menu() {
  1088.         if (function_exists('add_options_page')) {
  1089.             add_options_page(__('Geo Mashup Options','GeoMashup'), __('Geo Mashup','GeoMashup'), 'manage_options', __FILE__, array( __CLASS__, 'options_page'));
  1090.         }
  1091.     }
  1092.  
  1093.     /**
  1094.      * WordPress action to display important messages in the admin.
  1095.      * 
  1096.      * admin_notices {@link http://codex.wordpress.org/Plugin_API/Action_Reference#Advanced_Actions action}
  1097.      * called by WordPress.
  1098.      *
  1099.      * @since 1.3
  1100.      */
  1101.     public static function admin_notices() {
  1102.         global $geo_mashup_options;
  1103.  
  1104.         $message = '';
  1105.         if ( ! self::is_options_page() ) {
  1106.             // We're not looking at the settings, but it may be important to do so
  1107.             $google_key = $geo_mashup_options->get( 'overall', 'google_key' );
  1108.             if ( empty( $google_key ) and 'google' == $geo_mashup_options->get( 'overall', 'map_api' ) and current_user_can( 'manage_options' ) ) {
  1109.                 $message = __( 'Geo Mashup requires a Google API key in the <a href="%s">settings</a> before it will work.', 'GeoMashup' );
  1110.                 $message = sprintf( $message, admin_url( 'options-general.php?page=' . GEO_MASHUP_PLUGIN_NAME ) );
  1111.             }
  1112.             if ( GEO_MASHUP_DB_VERSION != GeoMashupDB::installed_version() and current_user_can( 'manage_options' ) ) {
  1113.                 $message = __( 'Geo Mashup needs to upgrade its database, visit the <a href="%s">settings</a> to do it now.', 'GeoMashup' );
  1114.                 $message = sprintf( $message, admin_url( 'options-general.php?page=' . GEO_MASHUP_PLUGIN_NAME ) );
  1115.             }
  1116.         }
  1117.  
  1118.         if ( ! empty( $message ) ) {
  1119.             echo '<div class="error fade"><p>' . $message . '</p></div>';
  1120.         }
  1121.     }
  1122.  
  1123.     /**
  1124.      * WordPress action to add custom action links to the plugin listing.
  1125.      * 
  1126.      * plugin_action_links {@link http://codex.wordpress.org/Plugin_API/Filter_Reference#Advanced_WordPress_Filters filter},
  1127.      * called by WordPress.
  1128.      *
  1129.      * @since 1.3
  1130.      */
  1131.     public static function plugin_action_links( $links, $file ) {
  1132.         if ( GEO_MASHUP_PLUGIN_NAME == $file ) {
  1133.             $settings_link = '<a href="' . admin_url( 'options-general.php?page=' . GEO_MASHUP_PLUGIN_NAME ) .'">' .
  1134.                 __( 'Settings' ) . '</a>';
  1135.             array_unshift( $links, $settings_link );
  1136.         }
  1137.         return $links;
  1138.     }
  1139.  
  1140.     /**
  1141.      * WordPress action to add custom meta links to the plugin listing.
  1142.      * 
  1143.      * plugin_row_meta {@link http://codex.wordpress.org/Plugin_API/Filter_Reference#Advanced_WordPress_Filters filter},
  1144.      * called by WordPress.
  1145.      *
  1146.      * @since 1.3
  1147.      */
  1148.     public static function plugin_row_meta( $links, $file ) {
  1149.         if ( GEO_MASHUP_PLUGIN_NAME == $file ) {
  1150.             $links[] = '<a href="http://code.google.com/p/wordpress-geo-mashup/wiki/Documentation">' .
  1151.                 __( 'Documentation', 'GeoMashup' ) . '</a>';
  1152.             $links[] = '<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=11045324">' .
  1153.                 __( 'Donate', 'GeoMashup' ) . '</a>';
  1154.         }
  1155.         return $links;
  1156.     }
  1157.  
  1158.     /**
  1159.      * WordPress action to produce the Geo Mashup Options admin page.
  1160.      *
  1161.      * Called by the WordPress admin.
  1162.      * 
  1163.      * @since 1.0
  1164.      */
  1165.     public static function options_page() {
  1166.         include_once( path_join( GEO_MASHUP_DIR_PATH, 'options.php' ) );
  1167.         geo_mashup_options_page();
  1168.     }
  1169.  
  1170.     /**
  1171.      * Get the location of the current loop object, if any.
  1172.      *
  1173.      * @since 1.3
  1174.      *
  1175.      * @param string $output ARRAY_A | ARRAY_N | OBJECT
  1176.      * @return object|boolLocation object or false if none.
  1177.      */
  1178.     public static function current_location( $output = OBJECT ) {
  1179.         global $in_comment_loop, $in_user_loop, $user;
  1180.  
  1181.  
  1182.         $location = false;
  1183.  
  1184.         if ( $in_comment_loop ) {
  1185.             $object_name = 'comment';
  1186.             $object_id = get_comment_ID();
  1187.         } else if ( $in_user_loop ) {
  1188.             $object_name = 'user';
  1189.             $object_id = $user->ID;
  1190.         } else if ( in_the_loop() ) {
  1191.             $object_name = 'post';
  1192.             $object_id = get_the_ID();
  1193.         } else {
  1194.             $object_name = $object_id = '';
  1195.         }
  1196.             
  1197.         if ( $object_name && $object_id ) {
  1198.             $location = GeoMashupDB::get_object_location( $object_name, $object_id, $output );
  1199.         }
  1200.         return $location;
  1201.     }
  1202.  
  1203.     /** 
  1204.      * A template tag to insert location information.
  1205.      *
  1206.      * @since 1.3
  1207.      *
  1208.      * @param string|array$args Template tag arguments.
  1209.      * @return string The information requested, empty string if none.
  1210.      */
  1211.     public static function location_info( $args = '' ) {
  1212.         $defaults = array( 
  1213.             'fields' => 'address', 
  1214.             'separator' => ',', 
  1215.             'format' => '',
  1216.             'object_name' => null, 
  1217.             'object_id' => null );
  1218.         $args = wp_parse_args( $args, $defaults );
  1219.         extract( $args, EXTR_SKIP );
  1220.         $info = '';
  1221.  
  1222.         if ( $object_name && $object_id ) {
  1223.             $location = GeoMashupDB::get_object_location( $object_name, $object_id, ARRAY_A );
  1224.         } else {
  1225.             $location = self::current_location( ARRAY_A );
  1226.         }
  1227.  
  1228.         if ( !empty( $location ) ) {
  1229.             $fields = preg_split( '/\s*,\s*/', $fields );
  1230.             $values = array();
  1231.             foreach( $fields as $field ) {
  1232.                 if ( isset( $location[$field] ) ) {
  1233.                     array_push( $values, $location[$field] );
  1234.                 } else {
  1235.                     if ( 'country_name' == $field ) { 
  1236.                         array_push( $values, GeoMashupDB::get_administrative_name( $location['country_code'] ) );
  1237.                     } else if ( 'admin_name' == $field ) {
  1238.                         array_push( $values, GeoMashupDB::get_administrative_name( $location['country_code'], $location['admin_code'] ) );
  1239.                     } else {
  1240.                         array_push( $values, '' );
  1241.                     }
  1242.                 } 
  1243.             }
  1244.             if ( empty( $format ) ) {
  1245.                 $info = implode( $separator, $values );
  1246.             } else {
  1247.                 $info = vsprintf( $format, $values );
  1248.             }
  1249.         }
  1250.         return $info;
  1251.     }    
  1252.  
  1253.     /**
  1254.      * A template tag to insert a link to a post on the mashup.
  1255.      * 
  1256.      * @see show_on_map_link()
  1257.      */
  1258.     public static function post_link($option_args = '') {
  1259.         return self::show_on_map_link($option_args);
  1260.     }
  1261.  
  1262.     /**
  1263.      * A template tag to return an URL for the current location on the
  1264.      * global map page.
  1265.      *
  1266.      * @since 1.3
  1267.      *
  1268.      * @return string The URL, empty if no current location is found.
  1269.      */
  1270.     public static function show_on_map_link_url( $args = null ) {
  1271.         global $geo_mashup_options;
  1272.  
  1273.         $defaults = array( 'zoom' => '' );
  1274.         $args = wp_parse_args( $args, $defaults );
  1275.  
  1276.         $url = '';
  1277.         $location = self::current_location();
  1278.         if ( $location ) {
  1279.             $url = get_page_link($geo_mashup_options->get('overall', 'mashup_page'));
  1280.             if ( !$url ) {
  1281.                 return $url;
  1282.             }
  1283.             if ( strstr( $url, '?' ) ) {
  1284.                 $url .= '&amp;';
  1285.             } else {
  1286.                 $url .= '?';
  1287.             }
  1288.             $open = '';
  1289.             if ( $geo_mashup_options->get( 'global_map', 'auto_info_open' ) == 'true' ) {
  1290.                 $open = '&open_object_id=' . $location->object_id;
  1291.             }
  1292.             $zoom = '';
  1293.             if ( !empty( $args['zoom'] ) ) {
  1294.                 $zoom = '&zoom=' . urlencode( $args['zoom'] );
  1295.             }
  1296.             $url .= htmlentities("center_lat={$location->lat}&center_lng={$location->lng}$open$zoom");
  1297.         }
  1298.         return $url;
  1299.     }
  1300.  
  1301.     /**
  1302.      * A template tag to insert a link to the current location post on the
  1303.      * global map page.
  1304.      *
  1305.      * @since 1.3
  1306.      *
  1307.      * @return string The link HTML, empty if no current location is found.
  1308.      */
  1309.     public static function show_on_map_link( $args = null ) {
  1310.         $defaults = array( 'text' => __( 'Show on map', 'GeoMashup' ),
  1311.              'display' => false,
  1312.              'zoom' => '',
  1313.              'show_icon' => true );
  1314.         $options = wp_parse_args($args, $defaults);
  1315.         $link = '';
  1316.         $url = self::show_on_map_link_url( $args );
  1317.         if ( $url ) {
  1318.             $icon = '';
  1319.             if ($options['show_icon'] && strcmp( $options['show_icon'], 'false' ) != 0) {
  1320.                 $icon = '<img src="'.GEO_MASHUP_URL_PATH.
  1321.                     '/images/geotag_16.png" alt="'.__('Geotag Icon','GeoMashup').'"/>';
  1322.             }
  1323.             $link = '<a class="gm-link" href="'.$url.'">'.
  1324.                 $icon.' '.$options['text'].'</a>';
  1325.             if ($options['display']) {
  1326.                 echo $link;
  1327.             }
  1328.         }
  1329.         return $link;
  1330.     }
  1331.  
  1332.     /** 
  1333.      * Visible posts list template tag.
  1334.      *
  1335.      * Returns a placeholder where a related map should display a list
  1336.      * of the currently visible posts.
  1337.      *
  1338.      * @since 1.2
  1339.      * @link http://code.google.com/p/wordpress-geo-mashup/wiki/TagReference#Visible_Posts_List
  1340.      *
  1341.      * @param string|array$args Template tag arguments.
  1342.      * @return string Placeholder HTML.
  1343.      */
  1344.     public static function visible_posts_list($args = null) {
  1345.         $args = wp_parse_args($args);
  1346.  
  1347.         $list_html = '';
  1348.  
  1349.         $for_map = 'gm-map-1';
  1350.         if ( !empty( $args['for_map'] ) ) {
  1351.             $for_map = $args['for_map'];
  1352.         }    
  1353.         if ( !empty( $args['heading_text'] ) ) {
  1354.             $heading_div = '<div id="' . $for_map . '-visible-list-header" style="display:none;">';
  1355.             $heading_tags = '<h2>';
  1356.             if ( !empty( $args['heading_tags'] ) ) {
  1357.                 $heading_tags = $args['heading_tags'];
  1358.             }
  1359.             $list_html .= balanceTags( $heading_div . $heading_tags . $args['heading_text'], true );
  1360.         }
  1361.         $list_html .= '<div id="' . $for_map . '-visible-list"></div>';
  1362.         return $list_html;
  1363.     }
  1364.  
  1365.     /**
  1366.      * List located posts template tag.
  1367.      *
  1368.      * Returns an HTML list of all located posts.
  1369.      *
  1370.      * @since 1.1
  1371.      * @link http://code.google.com/p/wordpress-geo-mashup/wiki/TagReference#List_Located_Posts
  1372.      *
  1373.      * @param string|array$args Template tag arguments.
  1374.      * @return string List HTML.
  1375.      */
  1376.     public static function list_located_posts( $option_args = null ) {
  1377.         $option_args = wp_parse_args( $option_args );
  1378.         $option_args['object_name'] = 'post';
  1379.         $list_html = '<ul class="gm-index-posts">';
  1380.         $locs = GeoMashupDB::get_object_locations( $option_args );
  1381.         if ($locs) {
  1382.             foreach ($locs as $loc) {
  1383.                 $list_html .= '<li><a href="'.get_permalink($loc->object_id).'">'.
  1384.                     $loc->label."</a></li>\n";
  1385.             }
  1386.         }
  1387.         $list_html .= '</ul>';
  1388.         return $list_html;
  1389.     }
  1390.  
  1391.     /**
  1392.      * List located posts by area template tag.
  1393.      *
  1394.      * Returns an HTML list of all located posts by country and state. May try to look up
  1395.      * this information when absent.
  1396.      *
  1397.      * @since 1.2
  1398.      * @link http://code.google.com/p/wordpress-geo-mashup/wiki/TagReference#List_Located_Posts_By_Area
  1399.      *
  1400.      * @param string|array$args Template tag arguments.
  1401.      * @return string List HTML.
  1402.      */
  1403.     public static function list_located_posts_by_area( $args ) {
  1404.         $args = wp_parse_args( $args );
  1405.         $list_html = '<div class="gm-area-list">';
  1406.         $countries = GeoMashupDB::get_distinct_located_values( 'country_code', array( 'object_name' => 'post' ) );
  1407.         $country_count = count( $countries );
  1408.         $country_heading = '';
  1409.         foreach ( $countries as $country ) {
  1410.             if ( $country_count > 1 ) {
  1411.                 $country_name = GeoMashupDB::get_administrative_name( $country->country_code ); 
  1412.                 $country_name = $country_name ? $country_name : $country->country_code;
  1413.                 $country_heading = '<h3>' . $country_name . '</h3>';
  1414.             }
  1415.             $states = GeoMashupDB::get_distinct_located_values( 'admin_code', 
  1416.                 array( 'country_code' => $country->country_code, 'object_name' => 'post' ) );
  1417.             if ( empty( $states ) ) {
  1418.                 $states = array( (object) array( 'admin_code' => null ) );
  1419.             }
  1420.             foreach ($states as $state ) { 
  1421.                 $location_query = array( 
  1422.                     'object_name' => 'post',
  1423.                     'country_code' => $country->country_code,
  1424.                     'admin_code' => $state->admin_code,
  1425.                     'sort' => 'post_title'
  1426.                 );
  1427.                 $post_locations = GeoMashupDB::get_object_locations( $location_query );
  1428.                 if ( count( $post_locations ) > 0 ) {
  1429.                     if ( ! empty( $country_heading ) ) {
  1430.                         $list_html .= $country_heading;
  1431.                         $country_heading = '';
  1432.                     }
  1433.                     if ( null != $states[0]->admin_code ) {
  1434.                         $state_name = GeoMashupDB::get_administrative_name( $country->country_code, $state->admin_code );
  1435.                         $state_name = $state_name ? $state_name : $state->admin_code;
  1436.                         $list_html .= '<h4>' . $state_name . '</h4>';
  1437.                     }
  1438.                     $list_html .= '<ul class="gm-index-posts">';
  1439.                     foreach ( $post_locations as $post_location ) { 
  1440.                         $list_html .= '<li><a href="' . 
  1441.                             get_permalink( $post_location->object_id ) .
  1442.                             '">' .
  1443.                             $post_location->label .
  1444.                             '</a>';
  1445.                         if ( isset( $args['include_address'] ) && $args['include_address'] == 'true' ) {
  1446.                             $list_html .= '<p>' . $post_location->address . '</p>';
  1447.                         }
  1448.                         $list_html .= '</li>';
  1449.                     }
  1450.                     $list_html .= '</ul>';
  1451.                 }
  1452.             }
  1453.         }
  1454.         $list_html .= '</div>';
  1455.         return $list_html;
  1456.     }
  1457.  
  1458.     /**
  1459.      * Post coordinates template tag.
  1460.      *
  1461.      * Get the coordinates of the current post.
  1462.      *
  1463.      * @since 1.0
  1464.      * @link http://code.google.com/p/wordpress-geo-mashup/wiki/TagReference#Post_Coordinates
  1465.      * @deprecated 1.3 Use GeoMashup::current_location()
  1466.      *
  1467.      * @param string|array$places Maximum number of decimal places to use.
  1468.      * @return array Array containing 'lat' and 'lng' keys.
  1469.      */
  1470.     public static function post_coordinates($places = 10) {
  1471.         global $post;
  1472.  
  1473.         $location = GeoMashupDB::get_object_location( 'post', $post->ID );
  1474.         $coordinates = array();
  1475.         if ( !empty( $location ) ) {
  1476.             $lat = $location->lat;
  1477.             $lng = $location->lng;
  1478.             $lat_dec_pos = strpos($lat,'.');
  1479.             if ($lat_dec_pos !== false) {
  1480.                 $lat = substr($lat, 0, $lat_dec_pos+$places+1);
  1481.             }
  1482.             $lng_dec_pos = strpos($lng,'.');
  1483.             if ($lng_dec_pos !== false) {
  1484.                 $lng = substr($lng, 0, $lng_dec_pos+$places+1);
  1485.             }
  1486.             $coordinates['lat'] = $lat;
  1487.             $coordinates['lng'] = $lng;
  1488.         }
  1489.         return $coordinates;
  1490.     }
  1491.  
  1492.     /**
  1493.      * WordPress action to emit GeoRSS namespace.
  1494.      *
  1495.      * rss_ns {@link http://codex.wordpress.org/Plugin_API/Action_Reference#Feed_Actions action}
  1496.      * called by WordPress.
  1497.      *
  1498.      * @since 1.0
  1499.      */
  1500.     public static function rss_ns() {
  1501.         echo 'xmlns:georss="http://www.georss.org/georss" ';
  1502.     }
  1503.  
  1504.     /**
  1505.      * WordPress action to emit GeoRSS tags.
  1506.      *
  1507.      * rss_item {@link http://codex.wordpress.org/Plugin_API/Action_Reference#Feed_Actions action}
  1508.      * called by WordPress.
  1509.      *
  1510.      * @since 1.0
  1511.      */
  1512.     public static function rss_item() {
  1513.         global $wp_query;
  1514.  
  1515.         // Using Simple GeoRSS for now
  1516.         $location = GeoMashupDB::get_object_location( 'post', $wp_query->post->ID );
  1517.         if ( !empty( $location ) ) {
  1518.             echo '<georss:point>' . esc_html( $location->lat . ' ' . $location->lng ) . '</georss:point>';
  1519.         }
  1520.     }
  1521.  
  1522.     /**
  1523.      * Tabbed category index template tag.
  1524.      *
  1525.      * Returns a placeholder where a related map should display a list
  1526.      * of map objects by category, organized into HTML suited for presentation
  1527.      * as tabs.
  1528.      *
  1529.      * @since 1.2
  1530.      * @link http://code.google.com/p/wordpress-geo-mashup/wiki/TagReference#Visible_Posts_List
  1531.      *
  1532.      * @param string|array$args Template tag arguments.
  1533.      * @return string Placeholder HTML.
  1534.      */
  1535.     public static function tabbed_category_index( $args ) {
  1536.         $args = wp_parse_args($args);
  1537.  
  1538.         $for_map = 'gm-map-1';
  1539.         if ( !empty( $args['for_map'] ) ) {
  1540.             $for_map = $args['for_map'];
  1541.         }    
  1542.         
  1543.         return '<div id="' . $for_map . '-tabbed-index"></div>';
  1544.     }
  1545. } // class GeoMashup
  1546. } // class exists
  1547.  
  1548. ?>

Documentation generated on Sat, 09 Jul 2011 21:54:53 -0700 by phpDocumentor 1.4.3