Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : May 4, 2026

CVE-2024-13362: Freemius <= 2.10.1 – Reflected DOM-Based Cross-Site Scripting via url Parameter (geo-mashup)

Plugin geo-mashup
Severity Medium (CVSS 6.1)
CWE 79
Vulnerable Version 1.13.15
Patched Version 1.13.16
Disclosed April 29, 2026

Analysis Overview

Atomic Edge analysis of CVE-2024-13362:

This vulnerability is a reflected DOM-based Cross-Site Scripting (XSS) issue found in the Freemius SDK and its bundled plugins or themes for WordPress, specifically within the `url` parameter handling. The flaw affects multiple plugins using Freemius versions <= 2.10.1, including Geo Mashup as shown in this diff. CVSS score 6.1 indicates medium severity, allowing unauthenticated attackers to inject arbitrary web scripts into pages that execute when a user clicks a crafted link.

The root cause is insufficient input sanitization and output escaping of the `url` parameter in the Freemius module. While the provided diff primarily shows version bumps and new template files for Geo Mashup, the reflected XSS issue originates from Freemius SDK's `start.php` or similar file where the `url` parameter is processed. The parameter is often passed via GET requests in admin-ajax or Freemius-specific actions, then reflected into the page without proper encoding. This allows DOM-based injection where the payload executes in the user's browser context.

An attacker can exploit this by crafting a malicious link containing a `url` parameter with JavaScript code, such as `?url=javascript:alert(document.cookie)`. The victim must click this link, which could be delivered via phishing emails, social engineering, or embedded in other sites. The lack of authentication requirements means any site visitor can trigger the exploitation if tricked.

The patch in this diff updates version numbers to 1.13.16 and adds new template files, but the critical XSS fix likely resides in the Freemius SDK update (from 2.10.1 to a safe version) which properly escapes the `url` parameter using functions like `esc_url()` or `htmlspecialchars()`. Before the patch, the parameter was echoed without sanitization; after, it is filtered to allow only safe URL schemes and encode special characters, preventing script execution.

If exploited, this vulnerability enables an attacker to execute arbitrary JavaScript in the victim's session. Impact includes session hijacking (stealing cookies), redirecting users to malicious sites, performing actions as the logged-in user (e.g., creating admin accounts if the victim has privileges), or defacing the affected page. The DOM-based nature means the payload can manipulate the page content and interact with sensitive data displayed.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/geo-mashup/geo-mashup.php
+++ b/geo-mashup/geo-mashup.php
@@ -3,7 +3,7 @@
 Plugin Name: Geo Mashup
 Plugin URI: https://wordpress.org/plugins/geo-mashup/
 Description: Save location for posts and pages, or even users and comments. Display these locations on Google, Leaflet, and OSM maps. Make WordPress into your GeoCMS.
-Version: 1.13.15
+Version: 1.13.16
 Author: Dylan Kuhn
 Text Domain: GeoMashup
 Domain Path: /lang
@@ -256,7 +256,7 @@
 		define('GEO_MASHUP_DIRECTORY', dirname( GEO_MASHUP_PLUGIN_NAME ) );
 		define('GEO_MASHUP_URL_PATH', trim( plugin_dir_url( __FILE__ ), '/' ) );
 		define('GEO_MASHUP_MAX_ZOOM', 20);
-		define('GEO_MASHUP_VERSION', '1.13.15');
+		define('GEO_MASHUP_VERSION', '1.13.16');
 		define('GEO_MASHUP_DB_VERSION', '1.3');
 	}

--- a/geo-mashup/trunk/default-templates/comment.php
+++ b/geo-mashup/trunk/default-templates/comment.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * This is the default template for the comment info window in Geo Mashup maps.
+ *
+ * Don't modify this file! It will be overwritten by upgrades.
+ *
+ * Instead, copy this file to "geo-mashup-comment.php" in your theme directory,
+ * or "comment.php" in the Geo Mashup Custom plugin directory, if you have that
+ * installed. Those files take precedence over this one.
+ *
+ * For styling of the info window, see map-style-default.css.
+ *
+ * @package GeoMashup
+ */
+?>
+<div class="locationinfo comment-location-info">
+<?php if ( GeoMashupQuery::have_comments() ) : ?>
+
+	<?php GeoMashupQuery::list_comments( 'callback=geo_mashup_comment_default' ); ?>
+
+<?php else : ?>
+
+	<h2 class="center">Not Found</h2>
+	<p class="center">Sorry, but you are looking for something that isn't here.</p>
+
+<?php endif; ?>
+
+</div>
+<?php
+/**
+ * Template callback for GeoMashupQuery::list_comments()
+ *
+ * Use the newer form of template, where the individual comment template goes in
+ * a function that matches the callback argument to list_comments
+ *
+ * @since 1.3
+ * @access public
+ * @package GeoMashup
+ *
+ * @param object $comment The comment to display
+ * @param array $args Arguments from wp_list_comments
+ * @param mixed $depth Nested depth
+ */
+function geo_mashup_comment_default( $comment, $args, $depth ) {
+	// Enable the WordPress comment functions
+	GeoMashupQuery::set_the_comment( $comment );
+	// From here to the closing curly brace should look like a familiar template
+?>
+	<div id="div-comment-<?php comment_ID() ?>" class="<?php comment_class(''); ?>">
+		<div class="comment-author vcard">
+		<?php printf(__('<cite class="fn">%s</cite> <span class="says">says:</span>'), get_comment_author_link()) ?>
+		</div>
+		<div class="comment-meta commentmetadata">
+			<a href="<?php echo esc_html( get_comment_link( $comment->comment_ID ) ) ?>"><?php printf(__('%1$s at %2$s'), get_comment_date(),  get_comment_time()) ?></a>
+		</div>
+		<?php comment_text() ?>
+
+	</div>
+<?php } ?>
--- a/geo-mashup/trunk/default-templates/full-post.php
+++ b/geo-mashup/trunk/default-templates/full-post.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * This is the default template for full post display of a clicked marker
+ * in a Geo Mashup map.
+ *
+ * Don't modify this file! It will be overwritten by upgrades.
+ *
+ * Instead, copy this file to "full-post.php" in your geo-mashup-custom directory,
+ * or "geo-mashup-full-post.php" in your theme directory. Those files will
+ * take precedence over this one.
+ *
+ * @package GeoMashup
+ */
+?>
+<?php if (have_posts()) : ?>
+
+	<?php while (have_posts()) : the_post(); ?>
+
+		<h2><a href="<?php the_permalink() ?>" title="<?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
+		<p class="meta"><span class="blogdate"><?php echo get_the_date(); ?></span> <?php the_category( ', ' ) ?></p>
+		<?php if ( function_exists( 'has_post_thumbnail') and has_post_thumbnail() ) : ?>
+		<?php the_post_thumbnail(); ?>
+		<?php endif; ?>
+
+		<div class="storycontent">
+			<?php the_content(); ?>
+		</div>
+
+	<?php endwhile; ?>
+
+<?php else : ?>
+
+	<h2 class="center">Not Found</h2>
+	<p class="center">Sorry, but you are looking for something that isn't here.</p>
+
+<?php endif; ?>
--- a/geo-mashup/trunk/default-templates/info-window-max.php
+++ b/geo-mashup/trunk/default-templates/info-window-max.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * This is the default template for the maximized info window display of a clicked marker
+ * in a Geo Mashup map.
+ *
+ * Don't modify this file! It will be overwritten by upgrades.
+ *
+ * Instead, copy this file to "info-window-max.php" in your geo-mashup-custom directory,
+ * or "geo-mashup-info-window-max.php" in your theme directory. Those files will
+ * take precedence over this one.
+ *
+ * @package GeoMashup
+ */
+
+// Avoid nested maps
+add_filter( 'the_content', array( 'GeoMashupQuery', 'strip_map_shortcodes' ), 1, 9 );
+?>
+<div class="info-window-max">
+<?php if (have_posts()) : ?>
+
+	<?php while (have_posts()) : the_post(); ?>
+
+		<h2><a href="<?php the_permalink() ?>" title="<?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
+		<p class="meta"><span class="blogdate"><?php echo get_the_date(); ?></span> <?php the_category( ', ' ) ?></p>
+		<?php if ( function_exists( 'has_post_thumbnail') and has_post_thumbnail() ) : ?>
+		<?php the_post_thumbnail(); ?>
+		<?php endif; ?>
+
+		<div class="storycontent">
+			<?php the_content(); ?>
+		</div>
+
+	<?php endwhile; ?>
+
+<?php else : ?>
+
+	<h2 class="center">Not Found</h2>
+	<p class="center">Sorry, but you are looking for something that isn't here.</p>
+
+<?php endif; ?>
+</div>
--- a/geo-mashup/trunk/default-templates/info-window.php
+++ b/geo-mashup/trunk/default-templates/info-window.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * This is the default template for the info window in Geo Mashup maps.
+ *
+ * Don't modify this file! It will be overwritten by upgrades.
+ *
+ * Instead, copy this file to "geo-mashup-info-window.php" in your theme directory,
+ * or info-window.php in the Geo Mashup Custom plugin directory, if you have that
+ * installed. Those files take precedence over this one.
+ *
+ * For styling of the info window, see map-style-default.css.
+ *
+ * @package GeoMashup
+ */
+
+add_filter( 'post_thumbnail_size', [ 'GeoMashupQuery', 'post_thumbnail_size' ]);
+
+// A potentially heavy-handed way to remove shortcode-like content
+add_filter( 'the_excerpt', array( 'GeoMashupQuery', 'strip_brackets' ) );
+?>
+<div class="locationinfo post-location-info">
+<?php if (have_posts()) : ?>
+
+	<?php while (have_posts()) : the_post(); ?>
+
+		<h2><a href="<?php the_permalink() ?>" title="<?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
+		<p class="meta"><span class="blogdate"><?php echo get_the_date(); ?></span> <?php the_category( ', ' ) ?></p>
+		<?php if ( function_exists( 'has_post_thumbnail') and has_post_thumbnail() ) : ?>
+		<?php the_post_thumbnail(); ?>
+		<?php endif; ?>
+
+		<?php if ($wp_query->post_count == 1) : ?>
+			<div class="storycontent">
+				<?php the_excerpt(); ?>
+			</div>
+		<?php endif; ?>
+
+	<?php endwhile; ?>
+
+<?php else : ?>
+
+	<h2 class="center">Not Found</h2>
+	<p class="center">Sorry, but you are looking for something that isn't here.</p>
+
+<?php endif; ?>
+
+</div>
--- a/geo-mashup/trunk/default-templates/map-frame.php
+++ b/geo-mashup/trunk/default-templates/map-frame.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * This is the default template for map frames.
+ *
+ * Don't modify this file! It will be overwritten by upgrades.
+ *
+ * Instead, copy this file to "geo-mashup-map-frame.php" in your theme directory,
+ * or map-frame.php in the Geo Mashup Custom plugin directory, if you have that
+ * installed. Those files take precedence over this one.
+ *
+ * For styling of map elements, see map-style-default.dev.css.
+ *
+ * Map properties include height, width, and name (see the style tag for example).
+ *
+ * Individual registered scripts can be added with code like
+ *
+ * <code>GeoMashupRenderMap::enqueue_script( 'colorbox' );</code>
+ *
+ * Or include all queued resources by replacing
+ *
+ * <code>GeoMashupRenderMap::head();</code>
+ *
+ * with
+ *
+ * <code>wp_head();</code>
+ *
+ * @package GeoMashup
+ */
+?>
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+	<head>
+		<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
+	<title>Geo Mashup Map</title>
+		<?php GeoMashupRenderMap::head(); ?>
+
+		<style type="text/css">
+			v:* { behavior:url(#default#VML); }
+			#geo-mashup {
+				width:100%;
+				height:100%;
+				<?php if ( GeoMashupRenderMap::map_property( 'background_color' ) ) : ?>
+				background-color: <?php echo esc_attr( GeoMashupRenderMap::map_property( 'background_color' ) ); ?>;
+				<?php endif; ?>
+			}
+		</style>
+	</head>
+	<body>
+	<div id="geo-mashup" class="<?php echo esc_attr( GeoMashupRenderMap::map_property( 'name' ) ); ?>">
+		<noscript>
+			<p><?php _e( 'This map requires JavaScript. You may have to enable it in your browser's settings.', 'GeoMashup' ); ?></p>
+		</noscript>
+	</div>
+	<?php echo GeoMashupRenderMap::map_script( 'geo-mashup' ); ?>
+	</body>
+</html>
--- a/geo-mashup/trunk/default-templates/nearby-list.php
+++ b/geo-mashup/trunk/default-templates/nearby-list.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Default Geo Mashup Search nearby posts template.
+ *
+ * THIS FILE WILL BE OVERWRITTEN BY AUTOMATIC UPGRADES
+ * See the geo-mashup-search.php plugin file for license
+ *
+ * Copy this to a file named geo-mashup-nearby-list.php in your active theme folder
+ * to customize. For bonus points delete this message in the copy!
+ *
+ * Variables in scope:
+ * $geo_mashup_search  object   The managing search object
+ * $search_text        string   The search text entered in the form
+ * $radius             int      The search radius
+ * $units              string   'mi' or 'km' or 'nm'
+ * $object_name        string   'post' or 'user' or 'comment'
+ * $near_location      array    The location searched, including 'lat' and 'lng'
+ * $distance_factor    float    The multiplier to convert the radius to kilometers
+ * $approximate_zoom   int      A guess at a zoom level that will include all results
+ *
+ * Methods of $geo_mashup_search mimic WordPress Loop functions have_posts()
+ * and the_post() (see http://codex.wordpress.org/The_Loop). This makes post
+ * template functions like the_title() work as expected. For distance:
+ *
+ * $geo_mashup_search->the_distance();
+ *
+ * will echo the distance with units. Its output can be modified:
+ *
+ * $geo_mashup_search->the_distance( 'decimal_places=1&append_units=0&echo=0' );
+ */
+?>
+<?php if ( $geo_mashup_search->have_posts() ) : ?>
+
+	<aside>
+	<?php if ($object_name == 'post'): ?>
+		<h1><?php _e( 'Posts Nearby', 'GeoMashup' ); ?></h1>
+	<?php elseif ($object_name == 'user'):?>
+		<h1><?php _e( 'Users Nearby', 'GeoMashup' ); ?></h1>
+	<?php endif; ?>
+	<ul class="geo-mashup-nearby-posts">
+
+		<?php if ($object_name == 'post'):?>
+
+			<?php while ( $geo_mashup_search->have_posts() ) : $geo_mashup_search->the_post(); ?>
+				<li>
+					<a href="<?php the_permalink(); ?>" title=""><?php the_title(); ?></a>
+					<span class="distance"><?php $geo_mashup_search->the_distance(); ?></span>
+				</li>
+			<?php endwhile; ?>
+
+		<?php elseif ($object_name == 'user'):?>
+
+			<?php while ( $geo_mashup_search->have_posts() ) : $user=$geo_mashup_search->get_userdata(); ?>
+				<li>
+					<?php echo $user->first_name.' '.$user->last_name;?> aka <?php echo $user->user_nicename?>
+					<span class="distance"><?php $geo_mashup_search->the_distance(); ?></span>
+				</li>
+			<?php endwhile; ?>
+
+		<?php endif; ?>
+
+	</ul>
+	</aside>
+
+<?php endif; ?>
--- a/geo-mashup/trunk/default-templates/search-form.php
+++ b/geo-mashup/trunk/default-templates/search-form.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * Default Geo Mashup Search widget form template.
+ *
+ * THIS FILE WILL BE OVERWRITTEN BY AUTOMATIC UPGRADES
+ * See the geo-mashup-search.php plugin file for license
+ *
+ * Copy this to a file named geo-mashup-search-form.php in your active theme folder
+ * to customize. For bonus points delete this message in the copy!
+ *
+ * Variables in scope:
+ * $widget     object      The widget generating this form
+ * $widget_id	string		The unique widget identifier
+ * $instance   array       The widget instance data
+ * $action_url string      The URL of the page chosen to display results
+ * $categories array       Terms objects to include in the category menu, if any.
+ * $radii      array       Radius distances to include in the radius menu, if any.
+ */
+?>
+<form class="geo-mashup-search-form" method="post" action="<?php echo $action_url; ?>">
+	<input name="results_page_id" type="hidden" value="<?php echo esc_attr( $instance['results_page_id'] ); ?>" />
+	<input name="units" type="hidden" value="<?php echo esc_attr( $instance['units'] ); ?>" />
+	<div class="Location clear">
+		<label for="<?php echo $widget_id; ?>-input"><?php _e('Location','GeoMashup' ) ?>:</label>
+		<input id="<?php echo $widget_id; ?>-input" class="geo-mashup-search-input" name="location_text" type="search"
+			placeholder="<?php if ( !empty( $_POST['location_text'] ) ) { echo esc_attr( $_POST['location_text'] ); } else if ( !empty( $instance['default_search_text'] ) ) {echo esc_attr( $instance['default_search_text'] );} ?>"
+			value=""/>
+	</div>
+	<div class="object clear">
+	<?php
+	if($instance['object_name'] === 'user' ||  $instance['object_name'] === 'comment') : ?>
+		<input name="object_name" type="hidden" value="<?php echo esc_attr( $instance['object_name'] ); ?>" />
+	</div><!-- .object -->
+	<?php else : ?>
+		<input name="object_name" type="hidden" value="post" />
+		<input name="map_post_type" type="hidden" value="<?php echo esc_attr( $instance['object_name'] ); ?>" />
+		</div><!-- .object -->
+
+		<?php if ( !empty( $categories ) ) : ?>
+			<?php if ( $categories === 'all' ) : ?>
+			<div class="taxonomy clear">
+				<input name="taxonomy" type="hidden" value="<?php echo $widget->get_default_value($instance, 'taxonomy', 'category');	?>" />
+				<input name="map_terms" type="hidden" value="<?php echo $categories; ?>" />
+			</div><!-- .taxonomy -->
+			<?php else: // $taxonomy_terms ?>
+			<div class="taxonomy clear">
+				<input name="taxonomy" type="hidden" value="<?php echo $widget->get_default_value($instance, 'taxonomy', 'category');	?>" />
+				<label for="<?php echo $widget_id; ?>-categories"><?php _e( 'Category', 'GeoMashup' ); ?>:</label>
+					<select id="<?php echo $widget_id; ?>-categories" name="map_terms">
+					<?php foreach ( $categories as $term ) : ?>
+						<option value="<?php echo $term->term_id; ?>"<?php
+							if ( $widget->get_default_value( $_POST, 'map_terms' ) == $term->term_id ) echo ' selected="selected"';	?>>
+							<?php echo $term->name; ?>
+						</option>
+					<?php endforeach; ?>
+					</select>
+			</div><!-- .taxonomy -->
+			<?php endif; ?>
+		<?php endif; // $taxonomy_terms ?>
+
+	<?php endif; ?>
+	<?php if ( !empty( $radii ) ) : ?>
+	<div class="radius clear">
+		<label for="<?php echo $widget_id; ?>-radius"><?php echo ucfirst(__( 'within', 'GeoMashup' )); ?>:</label>
+		<select id="<?php echo $widget_id; ?>-radius" name="radius">
+		<?php foreach ( $radii as $radius ) : ?>
+			<option value="<?php echo $radius; ?>"<?php
+				if ( $widget->get_default_value( $_POST, 'radius' ) == $radius )
+						echo ' selected="selected"';
+			?>><?php echo $radius; ?> <?php echo esc_html( $instance['units'] ); ?></option>
+		<?php endforeach; ?>
+		</select>
+	</div>
+	<?php endif; // Radius ?>
+	<div class="submit">
+		<input id="<?php echo $widget_id; ?>-submit" name="geo_mashup_search_submit" type="submit" value="<?php _e( 'Search', 'GeoMashup' ); ?>" />
+		<?php if ( !empty( $instance['find_me_button'] ) ) : ?>
+			<input name="geolocation" type="hidden" value="" />
+			<button id="<?php echo $widget_id; ?>-find-me" class="geo-mashup-search-find-me" style="display:none;"><?php echo $instance['find_me_button']; ?></button>
+		<?php endif; ?>
+	</div>
+</form>
 No newline at end of file
--- a/geo-mashup/trunk/default-templates/search-results.php
+++ b/geo-mashup/trunk/default-templates/search-results.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Default Geo Mashup Search results template.
+ *
+ * THIS FILE WILL BE OVERWRITTEN BY AUTOMATIC UPGRADES
+ * See the geo-mashup-search.php plugin file for license
+ *
+ * Copy this to a file named geo-mashup-search-results.php in your active theme folder
+ * to customize. For bonus points delete this message in the copy!
+ *
+ * Variables in scope:
+ * @var $geo_mashup_search  object   The managing search object
+ * @var $result_count       int      The number of objects found
+ * @var $search_text        string   The search text entered in the form
+ * @var $radius             int      The search radius
+ * @var $units              string   'mi' or 'km' or 'nm'
+ * @var $object_name        string   'post' or 'user' or 'comment'
+ * @var $near_location      array    The location searched, including 'lat' and 'lng'
+ * @var $distance_factor    float    The multiplier to convert the radius to kilometers
+ * @var $approximate_zoom   int      A guess at a zoom level that will include all results
+ *
+ * Methods of $geo_mashup_search mimic WordPress Loop functions have_posts()
+ * and the_post() (see http://codex.wordpress.org/The_Loop). This makes post
+ * template functions like the_title() work as expected. For distance:
+ *
+ * $geo_mashup_search->the_distance();
+ *
+ * will echo the distance with units. Its output can be modified:
+ *
+ * $geo_mashup_search->the_distance( 'decimal_places=1&append_units=0&echo=0' );
+ */
+?>
+<div id="geo-mashup-search-results">
+
+	<h2><?php printf( __( 'Search results near "%s"', 'GeoMashup' ), esc_html( $search_text ) ); ?></h2>
+
+	<?php if ( $geo_mashup_search->have_posts() ) : ?>
+
+	<?php echo GeoMashup::map( array(
+		'name' => 'search-results-map',
+		'search_text' => $search_text,
+		'object_ids' => $geo_mashup_search->get_the_ID_list(),
+		'center_lat' => $near_location['lat'],
+		'center_lng' => $near_location['lng'],
+		'search_lat' => $near_location['lat'],
+		'search_lng' => $near_location['lng'],
+		'map_content' => 'global',
+		'object_name'=> $object_name,
+		'zoom' => 	$approximate_zoom + 1 // Adjust to taste
+		) ); ?>
+
+		<?php if ($object_name == 'post'):?>
+
+			<?php while ( $geo_mashup_search->have_posts() ) : $geo_mashup_search->the_post(); ?>
+					<div class="search-result">
+						<h3><a href="<?php the_permalink(); ?>" title=""><?php the_title(); ?></a></h3>
+						<p><?php the_excerpt(); ?></p>
+						<p>
+					<?php _e( 'Distance', 'GeoMashup' ); ?>:
+					<?php $geo_mashup_search->the_distance(); ?>
+				</p>
+			</div>
+			<?php endwhile; ?>
+		<?php elseif ($object_name == 'user'):?>
+
+			<?php while ( $geo_mashup_search->have_posts() ) : $user=$geo_mashup_search->get_userdata(); ?>
+					<div class="search-result">
+						<h3><?php echo $user->first_name.' '.$user->last_name;?> aka <?php echo $user->user_nicename?></h3>
+						<p>
+					<?php _e( 'Distance', 'GeoMashup' ); ?>:
+					<?php $geo_mashup_search->the_distance(); ?>
+				</p>
+			</div>
+			<?php endwhile; ?>
+		<?php endif;?>
+	<?php else : ?>
+
+				<p><?php _e( 'No results found.', 'GeoMashup' ); ?></p>
+
+	<?php endif; ?>
+</div>
--- a/geo-mashup/trunk/default-templates/user.php
+++ b/geo-mashup/trunk/default-templates/user.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * This is the default template for the User info window in Geo Mashup maps.
+ *
+ * Don't modify this file! It will be overwritten by upgrades.
+ *
+ * Instead, copy this file to "geo-mashup-user.php" in your theme directory,
+ * or "user.php" in the Geo Mashup Custom plugin directory, if you have that
+ * installed. Those files take precedence over this one.
+ *
+ * For styling of the info window, see map-style-default.css.
+ *
+ * @package GeoMashup
+ */
+?>
+<div class="locationinfo user-location-info">
+<?php if ( GeoMashupQuery::have_users() ) : ?>
+
+<?php GeoMashupQuery::list_users( 'callback=geo_mashup_user_default_template' ); ?>
+
+<?php else : ?>
+
+	<h2 class="center">Not Found</h2>
+	<p class="center">Sorry, but you are looking for something that isn't here.</p>
+
+<?php endif; ?>
+</div>
+<?php
+/**
+ * Template callback for GeoMashupQuery::list_users()
+ *
+ * @since 1.3
+ * @package GeoMashup
+ * @param object $user The user to display.
+ */
+function geo_mashup_user_default_template( $user ) {
+	GeoMashupQuery::set_the_user( $user );
+?>
+<div id="div-user-<?php echo esc_attr( $user->ID ); ?>" class="vcard">
+	<div class="fn">
+		<span class="type"><?php _e( 'Display Name' ); ?></span>: <span class="value"><?php echo esc_attr( $user->display_name ); ?></span>
+	</div>
+	<?php echo GeoMashup::location_info( 'fields=locality_name,admin_code&format=<div class="adr"><span class="locality">%s</span>, <span class="region">%s</span></div>' ); ?>
+	<?php if ( isset( $user->user_url ) && strlen( $user->user_url ) > 7 ) : ?>
+	<div class="url">
+		<span class="type"><?php _e( 'Website' ); ?></span>: <a class="value" href="<?php echo esc_attr( $user->user_url ); ?>"><?php echo esc_attr( $user->user_url ); ?></a>
+	</div>
+	<?php endif; ?>
+</div>
+<?php } ?>
+
--- a/geo-mashup/trunk/edit-form.php
+++ b/geo-mashup/trunk/edit-form.php
@@ -0,0 +1,237 @@
+<?php
+/**
+ * A function wrapper for the location editor HTML.
+ *
+ * @package GeoMashup
+ */
+
+/**
+ * Print the Geo Mashup location editor HTML for an object.
+ *
+ * Goals for this interface are to make it usable for any kind of locatable
+ * object, to be usable without javascript, functional on the front end or admin,
+ * and eventually adaptable to editing multiple locations for an object.
+ *
+ * It's assumed this will go inside an existing form for editing the object,
+ * such as the WordPress admin post edit form.
+ *
+ * @since 1.2
+ * @see geo-mashup-ui-managers.php
+ * @see geo-mashup-location-editor.js
+ * @uses edit-form.css
+ * @access public
+ *
+ * @param string $object_name The type of object, e.g. 'post', 'user', etc.
+ * @param string $object_id The ID of the object being edited.
+ * @param string $ui_manager Optionally the name of UI Manager class to use for AJAX operations.
+ */
+function geo_mashup_edit_form( $object_name, $object_id, $ui_manager = '' ) {
+	global $geo_mashup_options;
+
+	$help_class = 'geo-mashup-js';
+	$add_input_style = 'style="display:none;"';
+	$update_input_style = $delete_input_style = '';
+	$coordinate_string = '';
+
+	// Load any existing location for the object
+	$location = GeoMashupDB::get_object_location( $object_name, $object_id );
+	if ( empty( $location ) ) {
+		$location = GeoMashupDB::blank_object_location();
+		$help_class = '';
+		$add_input_style = '';
+		$update_input_style = $delete_input_style = 'style="display:none;"';
+	} else {
+		$coordinate_string = $location->lat . ',' . $location->lng;
+	}
+
+	$post_location_name = $location->saved_name;
+	$kml_url = '';
+
+	// Set a Geo date default when needed & possible
+	$date_missing = ( empty( $location->geo_date ) || '0000-00-00 00:00:00' == $location->geo_date );
+	if ( 'post' == $object_name) {
+		if ( $date_missing ) {
+
+			// Geo date defaults to post date
+			$post = get_post( $object_id );
+			$location->geo_date = $post->post_date;
+			if ( !empty( $location->id ) ) {
+				GeoMashupDB::set_object_location( $object_name, $object_id, $location->id, false, $location->geo_date );
+			}
+
+		}
+
+		// For posts, look for a KML attachment
+		$kml_urls = GeoMashup::get_kml_attachment_urls( $object_id );
+		if (count($kml_urls)>0) {
+			$kml_url = array_pop($kml_urls);
+		}
+
+	} else if ( 'user' == $object_name && $date_missing ) {
+
+		// Geo date defaults to registration date
+		$user = get_userdata( $object_id );
+		$location->geo_date = $user->user_registered;
+		if ( !empty( $location->id ) ) {
+			GeoMashupDB::set_object_location( $object_name, $object_id, $location->id, false, $location->geo_date );
+		}
+
+	} else if ( 'comment' == $object_name && $date_missing ) {
+
+		// Geo date defaults to comment date
+		$comment = get_comment( $object_id );
+		$location->geo_date = $comment->comment_date;
+		if ( !empty( $location->id ) ) {
+			GeoMashupDB::set_object_location( $object_name, $object_id, $location->id, false, $location->geo_date );
+		}
+
+	}
+	if ( empty( $location->geo_date ) ) {
+		$location_datetime = mktime();
+	} else {
+		$location_datetime = strtotime( $location->geo_date );
+	}
+	$location_date = date( 'M j, Y', $location_datetime );
+	$location_hour = date( 'G', $location_datetime );
+	$location_minute = date( 'i', $location_datetime );
+
+	// Load saved locations
+	$saved_locations = GeoMashupDB::get_saved_locations( );
+	$saved_location_options = array();
+	if ( ! empty( $saved_locations ) ) {
+		foreach ( $saved_locations as $saved_location ) {
+			$escaped_name = str_replace( array( "rn", "r", "n" ), '', $saved_location->saved_name );
+			if ( $saved_location->id != $location->id )
+				$selected = '';
+			else
+				$selected = ' selected="selected"';
+			$saved_location_options[] = '<option value="' . esc_attr( $saved_location->id . '|' . $saved_location->lat . '|' .
+				$saved_location->lng . '|' . $saved_location->address ) . '"' . $selected . '>' . esc_html( $escaped_name ) . '</option>';
+		}
+	}
+	$saved_location_options = implode( '', $saved_location_options );
+
+	$nonce = wp_create_nonce('geo-mashup-edit');
+
+	$static_maps_base_url = 'http://maps.google.com/maps/api/staticmap?key=' .
+		$geo_mashup_options->get( 'overall', 'googlev3_key' );
+?>
+	<div id="geo_mashup_location_editor">
+	<div id="geo_mashup_ajax_message" class="geo-mashup-js ui-state-highlight"></div>
+	<input id="geo_mashup_nonce" name="geo_mashup_nonce" type="hidden" value="<?php echo $nonce; ?>" />
+	<input id="geo_mashup_changed" name="geo_mashup_changed" type="hidden" value="" />
+	<?php ob_start(); ?>
+	<table id="geo-mashup-location-table">
+		<thead class="ui-widget-header">
+		<tr>
+			<th><?php _e( 'Address', 'GeoMashup' ); ?></th>
+			<th><?php _e( 'Saved Name', 'GeoMashup' ); ?></th>
+			<th><?php _e( 'Geo Date', 'GeoMashup' ); ?></th>
+		</tr>
+		</thead>
+		<tbody class="ui-widget-content">
+		<tr id="geo_mashup_display" class="geo-mashup-display-row">
+			<td class="geo-mashup-info">
+				<div class="geo-mashup-address"><?php echo esc_html( $location->address ); ?></div>
+				<div class="geo-mashup-coordinates"><?php echo esc_attr( $coordinate_string ); ?></div>
+			</td>
+			<td id="geo_mashup_saved_name_ui">
+				<input id="geo_mashup_location_name" name="geo_mashup_location_name" size="50" type="text" value="<?php echo esc_attr( $post_location_name ); ?>" />
+			</td>
+			<td id="geo_mashup_date_ui">
+				<input id="geo_mashup_date" name="geo_mashup_date" type="text" size="20" value="<?php echo esc_attr( $location_date ); ?>" /><br />
+				@
+				<input id="geo_mashup_hour" name="geo_mashup_hour" type="text" size="2" maxlength="2" value="<?php echo esc_attr( $location_hour ); ?>" />
+				:
+				<input id="geo_mashup_minute" name="geo_mashup_minute" type="text" size="2" maxlength="2" value="<?php echo esc_attr( $location_minute ); ?>" />
+			</td>
+			<td id="geo_mashup_ajax_buttons">
+			</td>
+
+		</tr>
+		</tbody>
+	</table>
+	<?php $location_table_html = ob_get_clean(); ?>
+	<?php ob_start(); ?>
+	<div id="geo_mashup_map" class="geo-mashup-js">
+		<?php _e('Loading Google map. Check Geo Mashup options if the map fails to load.', 'GeoMashup'); ?>
+	</div>
+	<?php if ( ! empty( $location->id ) ) : ?>
+	<noscript>
+		<div id="geo_mashup_static_map">
+			<img src="<?php echo $static_maps_base_url; ?>&size=400x300&zoom=4&markers=size:small|color:green|<?php echo esc_attr( $location->lat . ',' . $location->lng ); ?>"
+				alt="<?php _e( 'Location Map Image', 'GeoMashup' ); ?>" />
+		</div>
+	</noscript>
+	<?php endif; ?>
+	<?php $map_html = ob_get_clean(); ?>
+	<?php ob_start(); ?>
+	<label for="geo_mashup_search"><?php _e('Find a new location:', 'GeoMashup'); ?>
+	<input	id="geo_mashup_search" name="geo_mashup_search" type="text" size="35" />
+	</label>
+
+	<?php _e( 'or select from', 'GeoMashup' ); ?>
+	<select id="geo_mashup_select" name="geo_mashup_select">
+		<option value=""><?php _e('[Saved Locations]','GeoMashup'); ?></option>
+		<?php echo $saved_location_options; ?>
+	</select>
+	<?php $search_html = ob_get_clean(); ?>
+
+	<?php echo empty( $location->id ) ? $search_html . $map_html . $location_table_html : $location_table_html . $map_html . $search_html; ?>
+
+	<input id="geo_mashup_ui_manager" name="geo_mashup_ui_manager" type="hidden" value="<?php echo $ui_manager; ?>" />
+	<input id="geo_mashup_object_id" name="geo_mashup_object_id" type="hidden" value="<?php echo $object_id; ?>" />
+	<input id="geo_mashup_no_js" name="geo_mashup_no_js" type="hidden" value="true" />
+	<input id="geo_mashup_location_id" name="geo_mashup_location_id" type="hidden" value="<?php echo esc_attr( $location->id ); ?>" />
+	<input id="geo_mashup_location" name="geo_mashup_location" type="hidden" value="<?php echo esc_attr( $coordinate_string ); ?>" />
+	<input id="geo_mashup_geoname" name="geo_mashup_geoname" type="hidden" value="<?php echo esc_attr( $location->geoname ); ?>" />
+	<input id="geo_mashup_address" name="geo_mashup_address" type="hidden" value="<?php echo esc_attr( $location->address ); ?>" />
+	<input id="geo_mashup_postal_code" name="geo_mashup_postal_code" type="hidden" value="<?php echo esc_attr( $location->postal_code ); ?>" />
+	<input id="geo_mashup_country_code" name="geo_mashup_country_code" type="hidden" value="<?php echo esc_attr( $location->country_code ); ?>" />
+	<input id="geo_mashup_admin_code" name="geo_mashup_admin_code" type="hidden" value="<?php echo esc_attr( $location->admin_code ); ?>" />
+	<input id="geo_mashup_admin_name" name="geo_mashup_admin_name" type="hidden" value="" />
+	<input id="geo_mashup_kml_url" name="geo_mashup_kml_url" type="hidden" value="<?php echo $kml_url; ?>" />
+	<input id="geo_mashup_sub_admin_code" name="geo_mashup_sub_admin_code" type="hidden" value="<?php echo esc_attr( $location->sub_admin_code ); ?>" />
+	<input id="geo_mashup_sub_admin_name" name="geo_mashup_sub_admin_name" type="hidden" value="" />
+	<input id="geo_mashup_locality_name" name="geo_mashup_locality_name" type="hidden" value="<?php echo esc_attr( $location->locality_name ); ?>" />
+	<div id="geo_mashup_submit" class="submit">
+		<input id="geo_mashup_add_location" name="geo_mashup_add_location" type="submit" <?php echo $add_input_style; ?> value="<?php _e( 'Add Location', 'GeoMashup' ); ?>" />
+		<input id="geo_mashup_delete_location" name="geo_mashup_delete_location" type="submit" <?php echo $delete_input_style; ?> value="<?php _e( 'Delete', 'GeoMashup' ); ?>" />
+		<input id="geo_mashup_update_location" name="geo_mashup_update_location" type="submit" <?php echo $update_input_style; ?> value="<?php _e( 'Save', 'GeoMashup' ); ?>" />
+	</div>
+	<div id="geo-mashup-inline-help-link-wrap" class="geo-mashup-js">
+		<a href="#geo-mashup-inline-help" id="geo-mashup-inline-help-link"><?php _e('help', 'GeoMashup'); ?><span class="ui-icon ui-icon-triangle-1-s"></span></a>
+	</div>
+	<div id="geo-mashup-inline-help" class="<?php echo $help_class; ?> ui-widget-content">
+		<p><?php _e( '<em>Saved Name</em> is an optional name you may use to add entries to the Saved Locations menu.', 'GeoMashup' ); ?></p>
+		<p><?php _e( '<em>Geo Date</em> associates a date (most formats work) and time with a location. Leave the default value if uncertain.', 'GeoMashup' ); ?></p>
+		<div class="geo-mashup-js">
+			<p><?php _e('Put a green pin at a new location. There are many ways to do it:', 'GeoMashup'); ?></p>
+			<ul>
+				<li><?php _e('Search for a location name.', 'GeoMashup'); ?></li>
+				<li><?php _e('For multiple search results, mouse over pins to see location names, and click a result pin to select that location.', 'GeoMashup'); ?></li>
+				<li><?php _e('Search for a decimal latitude and longitude separated by a comma, like <em>40.123,-105.456</em>. Seven decimal places are stored. Negative latitude is used for the southern hemisphere, and negative longitude for the western hemisphere.', 'GeoMashup'); ?></li>
+				<li><?php _e('Search for a street address, like <em>123 main st, anytown, acity</em>.', 'GeoMashup'); ?></li>
+				<li><?php _e('Click on the location. Zoom in if necessary so you can refine the location by dragging it or clicking a new location.', 'GeoMashup'); ?></li>
+			</ul>
+			<p><?php _e('To execute a search, type search text into the Find Location box and hit the enter key. If you type a name next to "Save As", the location will be saved under that name and added to the Saved Locations dropdown list.', 'GeoMashup'); ?></p>
+			<p><?php _e('To remove the location (green pin), clear the search box and hit the enter key.', 'GeoMashup'); ?></p>
+			<p><?php _e('When you are satisfied with the location, save or update.', 'GeoMashup'); ?></p>
+		</div>
+		<noscript>
+			<div>
+				<p><?php _e( 'To add or update location choose a saved location, or find a new location using one of these formats:', 'GeoMashup' ); ?></p>
+				<ul>
+					<li><?php _e('A place name like <em>Yellowstone National Park</em>', 'GeoMashup'); ?></li>
+					<li><?php _e('A decimal latitude and longitude, like <em>40.123,-105.456</em>.', 'GeoMashup'); ?></li>
+					<li><?php _e('A full or partial street address, like <em>123 main st, anytown, acity 12345 USA</em>.', 'GeoMashup'); ?></li>
+				</ul>
+				<p><?php _e( 'When you save or update, the closest match available will be saved as the location.', 'GeoMashup' ); ?></p>
+			</div>
+		</noscript>
+
+	</div>
+	</div><!-- id="geo_mashup_location_editor" -->
+<?php
+}
+?>
--- a/geo-mashup/trunk/freemius.php
+++ b/geo-mashup/trunk/freemius.php
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * Freemius integration
+ * @since 1.9.1
+ * @since 1.10  Made instantiable.
+ */
+class GeoMashupFreemius {
+	/**
+	 * @since 1.10.0
+	 * @var string
+	 */
+	protected static $init_data_option_name = 'geo_mashup_freemius_init';
+
+	/**
+	 * @since 1.10.0
+	 * @var array
+	 */
+	protected $init_data;
+
+	/**
+	 * @since 1.10.0
+	 * @var Freemius
+	 */
+	protected $freemius;
+
+
+	/**
+	 * Instantiate a Freemius integration object.
+	 * @since 1.10.0
+	 */
+	public function __construct() {
+	}
+
+	/**
+	 * @since 1.10.0
+	 * @return bool
+	 */
+	public function is_loaded() {
+		return isset( $this->freemius );
+	}
+
+	/**
+	 * Load Freemius integrations.
+	 * @since 1.9.1
+	 */
+	public function load() {
+
+		if ( $this->is_loaded() ) {
+			return;
+		}
+
+		include_once( GEO_MASHUP_DIR_PATH . '/vendor/freemius/wordpress-sdk/start.php' );
+
+		$defaults = array(
+			'id' => '472',
+			'slug' => 'geo-mashup',
+			'type' => 'plugin',
+			'public_key' => 'pk_c28784eaec74e8b93e422064f2f99',
+			'is_premium' => false,
+			'has_premium_version' => false,
+			'has_addons' => false,
+			'has_paid_plans' => true,
+			'navigation' => 'tabs',
+			'menu' => array(
+				'slug' => 'geo-mashup/geo-mashup.php',
+				'contact' => false,
+				'support' => false,
+				'parent' => array(
+					'slug' => 'options-general.php',
+				),
+			),
+		);
+
+		$this->init_data = defined( 'GEO_MASHUP_FREEMIUS_INIT' ) ? unserialize( GEO_MASHUP_FREEMIUS_INIT ) : array();
+
+		$this->init_data = array_replace_recursive( $defaults, $this->init_data );
+
+		$this->init_data = array_replace_recursive( $this->init_data, get_option( self::$init_data_option_name, array() ) );
+
+		$this->freemius = fs_dynamic_init( $this->init_data );
+
+		$this->freemius->add_action( 'after_license_change', array( $this, 'after_license_change' ) );
+
+		$this->freemius->add_action( 'after_account_delete', array( $this, 'after_account_delete' ) );
+
+		$this->freemius->add_action( 'after_uninstall', array( $this, 'uninstall' ) );
+	}
+
+	/**
+	 * Track premium status when license changes.
+	 *
+	 * @since 1.10.0
+	 * @param string $event The Freemius license event.
+	 */
+	public function after_license_change( $event ) {
+		$is_paying = ! in_array( $event, array( 'cancelled', 'expired', 'trial_expired' ) );
+		$this->update_init_data( $is_paying );
+	}
+
+	/**
+	 * Track premium status when account is deleted.
+	 *
+	 * @since 1.10.0
+	 */
+	public function after_account_delete() {
+		$this->update_init_data( false );
+	}
+
+	/**
+	 * @since 1.9.1
+	 */
+	public function uninstall() {
+		delete_option( self::$init_data_option_name );
+		include_once( GEO_MASHUP_DIR_PATH . '/uninstaller.php' );
+		$uninstaller = new GeoMashupUninstaller();
+		$uninstaller->geo_mashup_uninstall_options();
+	}
+
+	/**
+	 * @since 1.10.0
+	 * @param bool $is_paying
+	 */
+	public function update_init_data( $is_paying = false ) {
+		$init_data = array(
+			'menu' => array(
+				'contact' => $is_paying,
+			),
+		);
+		update_option( self::$init_data_option_name, $init_data, false );
+	}
+
+}
--- a/geo-mashup/trunk/geo-mashup-db.php
+++ b/geo-mashup/trunk/geo-mashup-db.php
@@ -0,0 +1,2187 @@
+<?php
+/**
+ * Geo Mashup Data Access
+ *
+ * @package GeoMashup
+ */
+
+// Init at load time - just adds hooks
+GeoMashupDB::init();
+
+/**
+ * Static class to provide a namespace for Geo Mashup data functions.
+ *
+ * @since 1.2
+ * @package GeoMashup
+ */
+class GeoMashupDB {
+	/**
+	 * Current installed database version.
+	 *
+	 * @since 1.4
+	 * @var string
+	 */
+	private static $installed_version = null;
+	/**
+	 * Flag for objects that have geodata fields copied to.
+	 * Key $meta_type-$object_id, value true.
+	 *
+	 * @since 1.4
+	 * @var array
+	 */
+	private static $copied_to_geodata = array();
+	/**
+	 * Meta keys used to store geodata
+	 *
+	 * @since 1.4
+	 * @var array
+	 */
+	private static $geodata_keys = array( 'geo_latitude', 'geo_longitude', 'geo_address' );
+	/**
+	 * The last geocode error, or empty if no error.
+	 * @var WP_Error
+	 */
+	public static $geocode_error = array();
+
+	/**
+	 * WordPress action to set up data-related WordPress hooks.
+	 *
+	 * @since 1.4
+	 */
+	public static function init() {
+		global $geo_mashup_options;
+
+		// Enable the geo_mashup_query var
+		add_filter( 'query_vars', array( 'GeoMashupDB', 'query_vars' ) );
+		add_filter( 'posts_fields', array( 'GeoMashupDB', 'posts_fields' ), 10, 2 );
+		add_filter( 'posts_join', array( 'GeoMashupDB', 'posts_join' ), 10, 2 );
+		add_filter( 'posts_where', array( 'GeoMashupDB', 'posts_where' ), 10, 2 );
+		add_action( 'parse_query', array( 'GeoMashupDB', 'parse_query' ) );
+
+		// Some caching plugins don't implement this
+		if ( function_exists( 'wp_cache_add_global_groups' ) )
+			wp_cache_add_global_groups( array( 'geo_mashup_object_locations', 'geo_mashup_locations' ) );
+
+		// Avoid orphans
+		add_action( 'delete_post', array( 'GeoMashupDB', 'delete_post' ) );
+		add_action( 'delete_comment', array( 'GeoMashupDB', 'delete_comment' ) );
+		add_action( 'delete_user', array( 'GeoMashupDB', 'delete_user' ) );
+
+		if ( 'true' == $geo_mashup_options->get( 'overall', 'copy_geodata' ) or '' != $geo_mashup_options->get( 'overall', 'import_custom_field' ) )
+			self::add_geodata_sync_hooks();
+	}
+
+	/**
+	 * Add hooks to synchronize Geo Mashup ojbect locations with WordPress geodata.
+	 *
+	 * @since 1.4
+	 */
+	public static function add_geodata_sync_hooks() {
+		add_filter( 'update_post_metadata', array( 'GeoMashupDB', 'filter_update_post_metadata' ), 10, 5 );
+		add_action( 'added_post_meta', array( 'GeoMashupDB', 'action_added_post_meta' ), 10, 4 );
+		add_action( 'updated_post_meta', array( 'GeoMashupDB', 'action_added_post_meta' ), 10, 4 );
+		add_filter( 'update_user_metadata', array( 'GeoMashupDB', 'filter_update_user_metadata' ), 10, 5 );
+		add_action( 'added_user_meta', array( 'GeoMashupDB', 'action_added_user_meta' ), 10, 4 );
+		add_action( 'updated_user_meta', array( 'GeoMashupDB', 'action_added_user_meta' ), 10, 4 );
+		add_filter( 'update_comment_metadata', array( 'GeoMashupDB', 'filter_update_comment_metadata' ), 10, 5 );
+		add_action( 'added_comment_meta', array( 'GeoMashupDB', 'action_added_comment_meta' ), 10, 4 );
+		add_action( 'updated_comment_meta', array( 'GeoMashupDB', 'action_added_comment_meta' ), 10, 4 );
+		// AJAX calls use a slightly different hook - triksy!
+		add_action( 'added_postmeta', array( 'GeoMashupDB', 'action_added_post_meta' ), 10, 4 );
+		add_action( 'updated_postmeta', array( 'GeoMashupDB', 'action_added_post_meta' ), 10, 4 );
+
+		add_action( 'geo_mashup_added_object_location', array( 'GeoMashupDB', 'copy_to_geodata' ), 10, 4 );
+		add_action( 'geo_mashup_updated_object_location', array( 'GeoMashupDB', 'copy_to_geodata' ), 10, 4 );
+	}
+
+	/**
+	 * Remove hooks to synchronize Geo Mashup ojbect locations with WordPress geodata.
+	 *
+	 * @since 1.4
+	 */
+	public static function remove_geodata_sync_hooks() {
+		remove_filter( 'update_post_metadata', array( 'GeoMashupDB', 'filter_update_post_metadata' ), 10, 5 );
+		remove_action( 'added_post_meta', array( 'GeoMashupDB', 'action_added_post_meta' ), 10, 4 );
+		remove_action( 'updated_post_meta', array( 'GeoMashupDB', 'action_added_post_meta' ), 10, 4 );
+		remove_filter( 'update_user_metadata', array( 'GeoMashupDB', 'filter_update_user_metadata' ), 10, 5 );
+		remove_action( 'added_user_meta', array( 'GeoMashupDB', 'action_added_user_meta' ), 10, 4 );
+		remove_action( 'updated_user_meta', array( 'GeoMashupDB', 'action_added_user_meta' ), 10, 4 );
+		remove_filter( 'update_comment_metadata', array( 'GeoMashupDB', 'filter_update_comment_metadata' ), 10, 5 );
+		remove_action( 'added_comment_meta', array( 'GeoMashupDB', 'action_added_comment_meta' ), 10, 4 );
+		remove_action( 'updated_comment_meta', array( 'GeoMashupDB', 'action_added_comment_meta' ), 10, 4 );
+		// AJAX calls use a slightly different hook - triksy!
+		remove_action( 'added_postmeta', array( 'GeoMashupDB', 'action_added_post_meta' ), 10, 4 );
+		remove_action( 'updated_postmeta', array( 'GeoMashupDB', 'action_added_post_meta' ), 10, 4 );
+
+		remove_action( 'geo_mashup_added_object_location', array( 'GeoMashupDB', 'copy_to_geodata' ), 10, 4 );
+		remove_action( 'geo_mashup_updated_object_location', array( 'GeoMashupDB', 'copy_to_geodata' ), 10, 4 );
+	}
+
+	/**
+	 * WordPress action to update Geo Mashup post location when geodata custom fields are updated.
+	 *
+	 * @since 1.4
+	 */
+	public static function action_added_post_meta( $meta_id, $post_id, $meta_key, $meta_value ) {
+		self::copy_from_geodata( 'post', $meta_id, $post_id, $meta_key, $meta_value );
+	}
+
+	/**
+	 * WordPress action to update Geo Mashup user location when geodata custom fields are updated.
+	 *
+	 * @since 1.4
+	 */
+	public static function action_added_user_meta( $meta_id, $user_id, $meta_key, $meta_value ) {
+		self::copy_from_geodata( 'user', $meta_id, $user_id, $meta_key, $meta_value );
+	}
+
+	/**
+	 * WordPress action to update Geo Mashup comment location when geodata custom fields are updated.
+	 *
+	 * @since 1.4
+	 */
+	public static function action_added_comment_meta( $meta_id, $comment_id, $meta_key, $meta_value ) {
+		self::copy_from_geodata( 'comment', $meta_id, $comment_id, $meta_key, $meta_value );
+	}
+
+	/**
+	 * WordPress filter to prevent updates to geodata fields we've already updated.
+	 *
+	 * @since 1.4
+	 */
+	public static function filter_update_post_metadata( $ok, $object_id, $meta_key, $meta_value, $prev_value ) {
+		if ( !in_array( $meta_key, self::$geodata_keys ) )
+			return $ok;
+		if ( isset( self::$copied_to_geodata['post-' . $object_id] ) )
+			return false;
+		else
+			return $ok;
+	}
+
+	/**
+	 * WordPress filter to prevent updates to geodata fields we've already updated.
+	 *
+	 * @since 1.4
+	 */
+	public static function filter_update_user_metadata( $ok, $object_id, $meta_key, $meta_value, $prev_value ) {
+		if ( !in_array( $meta_key, self::$geodata_keys ) )
+			return $ok;
+		if ( isset( self::$copied_to_geodata['user-' . $object_id] ) )
+			return false;
+		else
+			return $ok;
+	}
+
+	/**
+	 * WordPress filter to prevent updates to geodata fields we've already updated.
+	 *
+	 * @since 1.4
+	 */
+	public static function filter_update_comment_metadata( $ok, $object_id, $meta_key, $meta_value, $prev_value ) {
+		if ( !in_array( $meta_key, self::$geodata_keys ) )
+			return $ok;
+		if ( isset( self::$copied_to_geodata['comment-' . $object_id] ) )
+			return false;
+		else
+			return $ok;
+	}
+
+	/**
+	 * Create a Geo Mashup object location from WordPress geodata.
+	 *
+	 * @since 1.4
+	 * @see http://codex.wordpress.org/Geodata
+	 */
+	private static function copy_from_geodata( $meta_type, $meta_id, $object_id, $meta_key, $meta_value ) {
+		global $geo_mashup_options, $wpdb;
+
+		// Do nothing if meta_key is not a known location field
+		$location_keys = array();
+		$is_copy_geodata_on = ( 'true' === $geo_mashup_options->get( 'overall', 'copy_geodata' ) );
+		$copy_imported_geodata = false;
+		if ( $is_copy_geodata_on ) {
+			$location_keys = array_merge( $location_keys, array( 'geo_latitude', 'geo_longitude', 'geo_lat_lng' ) );
+		}
+		$import_custom_keys = preg_split( '/s*,s*/', trim( $geo_mashup_options->get( 'overall', 'import_custom_field' ) ) );
+		$location_keys = array_merge( $location_keys, $import_custom_keys );
+		if ( ! in_array( $meta_key, $location_keys ) )
+			return;
+
+		$existing_location = self::get_object_location( $meta_type, $object_id );
+
+		$location = array();
+
+		// Do nothing unless both latitude and longitude exist for the object
+		if ( 'geo_lat_lng' == $meta_key ) {
+
+			$lat_lng = preg_split( '/s*[, ]s*/', trim( $meta_value ) );
+			if ( 2 != count( $lat_lng ) ) {
+				return;
+			}
+			$location['lat'] = $lat_lng[0];
+			$location['lng'] = $lat_lng[1];
+
+		} else if ( 'geo_latitude' == $meta_key ) {
+
+			$location['lat'] = $meta_value;
+			$lng = get_metadata( $meta_type, $object_id, 'geo_longitude', true );
+			if ( empty( $lng ) )
+				return;
+			$location['lng'] = $lng;
+
+		} else if ( 'geo_longitude' == $meta_key ) {
+
+			$location['lng'] = $meta_value;
+			$lat = get_metadata( $meta_type, $object_id, 'geo_latitude', true );
+			if ( empty( $lat ) )
+				return;
+			$location['lat'] = $lat;
+
+		} else if ( in_array( $meta_key, $import_custom_keys ) ) {
+
+			$lat_lng = preg_split( '/s*[, ]s*/', trim( $meta_value ) );
+			if ( count( $lat_lng ) == 2 and is_numeric( $lat_lng[0] ) and is_numeric( $lat_lng[1] ) ) {
+				$location['lat'] = $lat_lng[0];
+				$location['lng'] = $lat_lng[1];
+			} else if ( !empty( $meta_value ) ) {
+				$geocode_values = array();
+				foreach( $import_custom_keys as $import_custom_key ) {
+					if ( $meta_key == $import_custom_key ) {
+						$geocode_values[] = $meta_value;
+					} else {
+
+						// All keys must have a value - do nothing if not
+						if ( !metadata_exists( $meta_type, $object_id, $import_custom_key ) )
+							return;
+
+						$geocode_values[] = get_metadata( $meta_type, $object_id, $import_custom_key, true );
+
+					}
+				}
+				$location = self::blank_location( ARRAY_A );
+				self::geocode( implode( ',', $geocode_values ), $location );
+				if ( self::$geocode_error ) {
+					update_metadata( $meta_type, $object_id, 'geocoding_error', self::$geocode_error->get_error_message() );
+					return;
+				}
+				if ( $is_copy_geodata_on ) {
+					$copy_imported_geodata = true;
+				}
+			}
+		}
+
+		// Do nothing if the location already exists
+		if ( !empty( $existing_location ) ) {
+			$epsilon = 0.00001;
+			if ( abs( $location['lat'] - $existing_location->lat ) < $epsilon and abs( $location['lng'] - $existing_location->lng ) < $epsilon )
+				return;
+		}
+
+		// Save the location, attempt reverse geocoding
+		self::remove_geodata_sync_hooks();
+		// Use geo date if it exists
+		$geo_date = get_metadata( $meta_type, $object_id, 'geo_date', true );
+		$location_id = self::set_object_location( $meta_type, $object_id, $location, null, $geo_date );
+		if ( $copy_imported_geodata ) {
+			self::copy_to_geodata( $meta_type, $object_id, $geo_date, $location_id );
+		}
+		self::add_geodata_sync_hooks();
+	}
+
+	/**
+	 * Update object geodata if needed.
+	 *
+	 * @since 1.4
+	 *
+	 * @param string $meta_type 'post','user','comment'
+	 * @param int $object_id
+	 * @param string $geo_date
+	 * @param int $location_id The location to copy from.
+	 */
+	public static function copy_to_geodata( $meta_type, $object_id, $geo_date, $location_id ) {
+
+		$geo_latitude = get_metadata( $meta_type, $object_id, 'geo_latitude', true );
+		$geo_longitude = get_metadata( $meta_type, $object_id, 'geo_longitude', true );
+		$existing_object_location = self::get_object_location( $meta_type, $object_id );
+
+		// Do nothing if the geodata already exists
+		if ( $geo_latitude and $geo_longitude ) {
+			$epsilon = 0.00001;
+			if ( abs( $geo_latitude - $existing_object_location->lat ) < $epsilon and abs( $geo_longitude - $existing_object_location->lng ) < $epsilon )
+				return;
+		}
+
+		self::remove_geodata_sync_hooks();
+		update_metadata( $meta_type, $object_id, 'geo_latitude', $existing_object_location->lat );
+		update_metadata( $meta_type, $object_id, 'geo_longitude', $existing_object_location->lng );
+		update_metadata( $meta_type, $object_id, 'geo_address', $existing_object_location->address );
+		update_metadata( $meta_type, $object_id, 'geo_date', $existing_object_location->geo_date );
+		self::$copied_to_geodata[$meta_type . '-' . $object_id] = true;
+		self::add_geodata_sync_hooks();
+	}
+
+	/**
+	 * Set the installed database version.
+	 *
+	 * @since 1.4
+	 *
+	 * @param string $new_version
+	 */
+	private static function set_installed_version( $new_version ) {
+		self::$installed_version = $new_version;
+		update_option( 'geo_mashup_db_version', $new_version );
+	}
+
+	/**
+	 * Get the installed database version.
+	 *
+	 * @since 1.2
+	 *
+	 * @return string The installed database version.
+	 */
+	public static function installed_version() {
+
+		if ( is_null( self::$installed_version ) ) {
+			self::$installed_version = get_option( 'geo_mashup_db_version' );
+		}
+		return self::$installed_version;
+	}
+
+	/**
+	 * Get or set storage information for an object name.
+	 *
+	 * Potentially you could add storage information for a new kind of object:
+	 * <code>
+	 * GeoMashupDB::object_storage( 'foo', array(
+	 * 	'table' => $wpdb->prefix . 'foos',
+	 * 	'id_column' => 'foo_id',
+	 * 	'label_column' => 'foo_display_name',
+	 * 	'sort' => 'foo_order ASC' )
+	 * );
+	 * </code>
+	 * Would add the necessary information for a custom table of foo objects. By convention the
+	 * object name is the singular form of the table name without a prefix.
+	 *
+	 * @since 1.3
+	 *
+	 * @param string $object_name A type of object to be stored, default is 'post', 'user', and 'comment'.
+	 * @param array $new_storage If provided, adds or replaces the storage information for the object name.
+	 * @return array|bool The storage information array, or false if not found.
+	 */
+	public static function object_storage( $object_name, $new_storage = null ) {
+		global $wpdb;
+		static $objects = null;
+
+		if ( is_null( $objects ) ) {
+			$objects = array(
+				'post' => array(
+					'table' => $wpdb->posts,
+					'id_column' => 'ID',
+					'label_column' => 'post_title',
+					'date_column' => 'post_date',
+					'sort' => 'post_status ASC, geo_date DESC' ),
+				'user' => array(
+					'table' => $wpdb->users,
+					'id_column' => 'ID',
+					'label_column' => 'display_name',
+					'date_column' => 'user_registered',
+			 		'sort' => 'display_name ASC' ),
+				'comment' => array(
+					'table' => $wpdb->comments,
+					'id_column' => 'comment_ID',
+					'label_column' => 'comment_author',
+					'date_column' => 'comment_date',
+			 		'sort' => 'comment_date DESC'	)
+			);
+		}
+
+		if ( !empty( $new_storage ) ) {
+			$objects[$object_name] = $new_storage;
+		}
+		return ( isset( $objects[$object_name] ) ) ? $objects[$object_name] : false;
+	}
+
+	/**
+	 * Return a conventional object name given a table name.
+	 *
+	 * @since 1.7
+	 *
+	 * @param string $table
+	 * @return string
+	 */
+	public static function table_to_object_name( $table ) {
+		global $wpdb;
+		$object_name = str_replace( $wpdb->prefix, '', $table );
+		if ( 's' === substr( $object_name, -1 ) ) {
+			$object_name = substr( $object_name, 0, strlen( $object_name ) - 1 );
+		}
+		return $object_name;
+	}
+
+	/**
+	 * Toggle joining of WordPress queries with Geo Mashup tables.
+	 *
+	 * Use the public wrapper GeoMashup::join_post_queries()
+	 *
+	 * @since 1.3
+	 * @deprecated Use the geo_mashup_query query var
+	 *
+	 * @param bool $new_value If provided, replaces the current active state.
+	 * @return bool The current state.
+	 */
+	public static function join_post_queries( $new_value = null) {
+		static $active = null;
+
+		if ( is_bool( $new_value ) ) {
+			_deprecated_function( __METHOD__, '1.7', 'the geo_mashup_query query var' );
+			$active = $new_value;
+		}
+
+		return $active;
+	}
+
+	/**
+	 * WordPress filter to add Geo Mashup public query variables.
+	 *
+	 * query_vars {@link http://codex.wordpress.org/Plugin_API/Filter_Reference filter}
+	 * called by Wordpress.
+	 *
+	 * @since 1.3
+	 */
+	public static function query_vars( $public_query_vars ) {
+		if ( self::join_post_queries() ) {
+			$public_query_vars[] = 'geo_mashup_date';
+			$public_query_vars[] = 'geo_mashup_saved_name';
+			$public_query_vars[] = 'geo_mashup_country_code';
+			$public_query_vars[] = 'geo_mashup_postal_code';
+			$public_query_vars[] = 'geo_mashup_admin_code';
+			$public_query_vars[] = 'geo_mashup_locality';
+		}
+		return $public_query_vars;
+	}
+
+	/**
+	 * Set or get custom data associated with a WP_Query object.
+	 *
+	 * @since 1.7
+	 *
+	 * @param WP_Query $query
+	 * @param string $key Optional - return all data for the query if missing.
+	 * @param mixed $value Optional - set or overwrite data for the key if present.
+	 * @return mixed Extension data if present.
+	 */
+	private static function query_extension( $query, $key = null, $value = null ) {
+		static $extensions = array();
+
+		$hash = spl_object_hash( $query );
+
+		if ( is_null( $key ) )
+			return $extensions;
+
+		if ( !isset( $extensions[$hash] ) )
+			$extensions[$hash] = array();
+
+		if ( !is_null( $value ) )
+			$extensions[$hash][$key] = $value;
+		else if ( !isset( $extensions[$hash][$key] ) )
+			return null;
+
+		return $extensions[$hash][$key];
+	}
+
+	/**
+	 * WordPress action to capture custom orderby field before it is removed.
+	 *
+	 * parse_query {@link http://codex.wordpress.org/Plugin_API/Action_Reference action}
+	 * called by WordPress.
+	 *
+	 * @since 1.3
+	 * @access private
+	 * @static
+	 */
+	public static function parse_query( $query ) {
+		global $wpdb;
+
+		if ( !self::join_post_queries() )
+			return;
+
+		// Check for geo mashup fields in the orderby before they are removed as invalid
+		switch ( $query->query_vars['orderby'] ) {
+			case 'geo_mashup_date':
+				self::query_extension( $query, 'orderby', $wpdb->prefix . 'geo_mashup_location_relationships.geo_date' );
+				break;
+
+			case 'geo_mashup_locality':
+				self::query_extension( $query, 'orderby', $wpdb->prefix . 'geo_mashup_locations.locality_name' );
+				break;
+
+			case 'geo_mashup_saved_name':
+				self::query_extension( $query, 'orderby', $wpdb->prefix . 'geo_mashup_locations.saved_name' );
+				break;
+
+			case 'geo_mashup_country_code':
+				self::query_extension( $query, 'orderby', $wpdb->prefix . 'geo_mashup_locations.country_code' );
+				break;
+
+			case 'geo_mashup_admin_code':
+				self::query_extension( $query, 'orderby', $wpdb->prefix . 'geo_mashup_locations.admin_code' );
+				break;
+
+			case 'geo_mashup_postal_code':
+				self::query_extension( $query, 'orderby', $wpdb->prefix . 'geo_mashup_locations.postal_code' );
+				break;
+		}
+	}
+
+	/**
+	 * WordPress filter to add Geo Mashup fields to WordPress post queries.
+	 *
+	 * posts_fields {@link http://codex.wordpress.org/Plugin_API/Filter_Reference filter}
+	 * called by WordPress.
+	 *
+	 * @since 1.3
+	 */
+	public static function posts_fields( $fields, $query ) {
+		global $wpdb;
+
+		if ( self::join_post_queries() ) {
+			$fields .= ',' . $wpdb->prefix . 'geo_mashup_location_relationships.geo_date' .
+				',' . $wpdb->prefix . 'geo_mashup_locations.*';
+		}
+
+		return $fields;
+	}
+
+	/**
+	 * WordPress filter to join Geo Mashup tables to WordPress post queries.
+	 *
+	 * posts_join {@link http://codex.wordpress.org/Plugin_API/Filter_Reference filter}
+	 * called by WordPress.
+	 *
+	 * @since 1.3
+	 */
+	public static function posts_join( $join, $query ) {
+		global $wpdb;
+
+		if ( self::join_post_queries() ) {
+			$gmlr = $wpdb->prefix . 'geo_mashup_location_relationships';
+			$gml = $wpdb->prefix . 'geo_mashup_locations';
+			$join .= " INNER JOIN $gmlr ON ($gmlr.object_name = 'post' AND $gmlr.object_id = $wpdb->posts.ID)" .
+				" INNER JOIN $gml ON ($gml.id = $gmlr.location_id) ";
+		}
+
+		return $join;
+	}
+
+	/**
+	 * WordPress filter to incorporate geo mashup query vars in WordPress post queries.
+	 *
+	 * posts_where {@link http://codex.wordpress.org/Plugin_API/Filter_Reference filter}
+	 * called by WordPress.
+	 *
+	 * @since 1.3
+	 */
+	public static function posts_where( $where, $query ) {
+		global $wpdb;
+
+		if ( !self::join_post_queries() )
+			return $where;
+
+		$gmlr = $wpdb->prefix . 'geo_mashup_location_relationships';
+		$gml = $wpdb->prefix . 'geo_mashup_locations';
+		$geo_date = get_query_var( 'geo_mashup_date' );
+		if ( $geo_date ) {
+			$where .= $wpdb->prepare( " AND $gmlr.geo_date = %s ", $geo_date );
+		}
+		$saved_name = get_query_var( 'geo_mashup_saved_name' );
+		if ( $saved_name ) {
+			$where .= $wpdb->prepare( " AND $gml.saved_name = %s ", $saved_name );
+		}
+		$locality = get_query_var( 'geo_mashup_locality' );
+		if ( $locality ) {
+			$where .= $wpdb->prepare( " AND $gml.locality_name = %s ", $locality );
+		}
+		$country_code = get_query_var( 'geo_mashup_country_code' );
+		if ( $country_code ) {
+			$where .= $wpdb->prepare( " AND $gml.country_code = %s ", $country_code );
+		}
+		$admin_code = get_query_var( 'geo_mashup_admin_code' );
+		if ( $admin_code ) {
+			$where .= $wpdb->prepare( " AND $gml.admin_code = %s ", $admin_code );
+		}
+		$postal_code = get_query_var( 'geo_mashup_postal_code' );
+		if ( $postal_code ) {
+			$where .= $wpdb->prepare( " AND $gml.postal_code = %s ", $postal_code );
+		}
+
+		return $where;
+	}
+
+	/**
+	 * Append to the activation log.
+	 *
+	 * Add a message and optionally write the activation log.
+	 * Needs to be written before the end of the request or it will not be saved.
+	 *
+	 * @since 1.4
+	 *
+	 * @param string $message The message to append.
+	 * @param boolean $write Whether to save the log.
+	 * @return string The current log.
+	 */
+	public static function activation_log( $message = null, $write = false ) {
+		static $log = null;
+
+		if ( is_null( $log ) ) {
+			$log = get_option( 'geo_mashup_activation_log' );
+		}
+		if ( ! is_null( $message ) ) {
+			$log .= "n" . $message;
+		}
+		if ( $write ) {
+			update_option( 'geo_mashup_activation_log', $log );
+		}
+		return $log;
+	}
+
+	/**
+	 * Install or update Geo Mashup tables.
+	 *
+	 * @uses GeoMashupDB::activation_log()
+	 * @since 1.2
+	 */
+	public static function install() {
+		global $wpdb, $geo_mashup_options;
+
+		self::activation_log( date( 'r' ) . ' ' . __( 'Activating Geo Mashup', 'GeoMashup' ) );
+		$location_table_name = $wpdb->prefix . 'geo_mashup_locations';
+		$relationships_table_name = $wpdb->prefix . 'geo_mashup_location_relationships';
+		$administrative_names_table_name = $wpdb->prefix . 'geo_mashup_administrative_names';
+
+		$charset_collate = '';
+
+		if ( ! empty($wpdb-

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School