init_error_messages(); $this->register_plugin( 'author-location', __FILE__ ); // Everything OK? $this->add_action( 'init', 'sanity_checks' ); // Add our scripts, CSS, etc in to the main blog viewing screens $this->add_action( 'wp_head', 'view_enqueuing', 0 ); $this->add_action( 'wp_head', 'info_window_html' ); // Show the new location panel in the user profile edit screen $this->add_action( 'edit_user_profile', 'location_dialog' ); $this->add_action( 'show_user_profile', 'location_dialog' ); // Grab values when POSTed $this->add_action( 'load-profile.php', 'load_profile' ); $this->add_action( 'load-user-edit.php', 'load_profile' ); // Add some JS vars into the page $this->add_action( 'admin_print_scripts-profile.php', 'js_paths', 5 ); $this->add_action( 'admin_print_scripts-user-edit.php', 'js_paths', 5 ); // Encode any errors or notices to the redirect URL (what a pain) $this->add_filter( 'wp_redirect', 'append_errors' ); // Append currently entered location data, for the user to correct (if necessary) $this->add_filter( 'wp_redirect', 'append_location_data' ); // Show any errors or notices $this->add_action( 'admin_notices' ); // Shortcode $this->add_shortcode ('map-authors', 'shortcode_map_authors'); // HTTP client stuff $this->user_agent = 'WordPress/' . $GLOBALS['wp_version']; $this->fetch_time_out = 5; // 5 second timeout $this->use_gzip = true; // Use gzip encoding to fetch remote files if supported // Utility class $this->utility = new AuthorLocationUtility(); } function sanity_checks() { // Check there's a constant defining the GOOGLE_MAPS_API_KEY if ( ! defined( 'GOOGLE_MAPS_API_KEY' ) ) { $this->errors[] = 'no_api_key_defined'; // Check the constant is sane } else if ( GOOGLE_MAPS_API_KEY == '' || GOOGLE_MAPS_API_KEY == 'your-key-goes-here' ) { $this->errors[] = 'api_key_looks_wrong'; } } function init_error_messages() { // Useful var $loc_anchor = '#al_your_location'; $place_anchor = "#al_country_place"; // User data validation messages $this->error_msgs[ 'no_lat_or_long' ] = sprintf( __( 'You must enter a latitude and a longitude for your location.' ), $loc_anchor ); $this->error_msgs[ 'no_place_name' ] = sprintf( __( 'Please enter a place name for your location.' ), $place_anchor ); $this->error_msgs[ 'no_country' ] = sprintf( __( 'Please enter a country for your location.' ), $place_anchor ); $this->error_msgs[ 'invalid_latitude' ] = sprintf( __( 'Please enter the latitude as a decimal number between -90 and +90' ), $loc_anchor ); $this->error_msgs[ 'invalid_longitude' ] = sprintf( __( 'Please enter the longitude as a decimal number between -180 and +180' ), $loc_anchor ); // API key messages $this->error_msgs[ 'no_api_key_defined' ] = __( 'Please add a line into your wp-config.php as follows (you can get a Google Maps API key from here):
define( "GOOGLE_MAPS_API_KEY", "your-key-goes-here" );.
No data can be saved until you have entered this key.' ); $this->error_msgs[ 'api_key_looks_wrong' ] = __( 'Please check your wp-config.php file, to ensure that your Google Maps API key is in correctly. It should look like this:
define( "GOOGLE_MAPS_API_KEY", "your-key-goes-here" );.
No data can be saved until you have entered this key correctly.' ); } function profile_enqueuing() { wp_enqueue_script( 'jquery' ); // Probably present, but let's be sure $google_maps_js = 'http://maps.google.com/maps?file=api&v=2&key=' . GOOGLE_MAPS_API_KEY; wp_enqueue_script( 'google_maps', $google_maps_js ); $get_gmaps_location_js = $this->url() . '/js/get-gmaps-location.js'; wp_enqueue_script( 'get_gmaps_location_js', $get_gmaps_location_js ); // Add the CSS $get_gmaps_location_css = $this->url() . '/css/get-gmaps-location.css'; wp_enqueue_style( 'get_gmaps_location_css', $get_gmaps_location_css ); } /** * Enqueue various CSS and JS scripts to show maps on the blog. * * @return void **/ function view_enqueuing() { wp_enqueue_script( 'jquery' ); // Probably present, but let's be sure $google_maps_js = 'http://maps.google.com/maps?file=api&v=2&key=' . GOOGLE_MAPS_API_KEY; wp_enqueue_script( 'google_maps', $google_maps_js ); $map_authors_js = $this->url() . '/js/map-authors.js'; wp_enqueue_script( 'map_authors_js', $map_authors_js ); // Add the CSS // $get_gmaps_location_css = $this->url() . '/css/get-gmaps-location.css'; // wp_enqueue_style( 'get_gmaps_location_css', $get_gmaps_location_css ); } /** * Render an HTML SCRIPT element containing some vars which are inconvenient * to get the the JS in another way. * * @return void **/ function js_paths() { $template_vars = array(); $template_vars[ 'plugin_path' ] = $this->url(); // Render the template $this->render_admin ( 'js-paths', $template_vars ); } /** * * * @return void **/ function info_window_html() { // Capture the info HTML template $html = $this->capture( 'map-authors-info-window-html', array() ); $html = $this->utility->strip_newlines( $html ); $escaped_html = $this->utility->escape_for_js( $html ); // Put that escaped HTML into a second template $template_vars = array(); $template_vars[ 'html' ] = $escaped_html; // Render the template $this->render( 'map-author-js', $template_vars ); } /** * Render some HTML for the location dialog (long and lat fields) * * @return void **/ function location_dialog() { $template_vars = array(); // Get the long and lat for the current user $profileuser = $this->get_profileuser(); // Merge location data from that which has been stored against the user, // and that which has been entered during this session. $saved = (array) get_usermeta( $profileuser->ID, 'author_location' ); $previous = $this->session_location_data(); $location_data = $saved; // Merge the data from this session over the previously saved data if ( $previous ) { $location_data = array_merge( $saved, $previous ); } $template_vars[ 'location_data' ] = $location_data; $template_vars[ 'saved' ] = $saved; // Render the template $this->render_admin ( 'location-dialog', $template_vars ); } function session_location_data() { $slashed = @ $_GET[ 'al_data' ]; if ( ! $slashed ) { return false; } // F'ing slashes $serialised = stripslashes( $slashed ); $location_data = unserialize( $serialised ); return $location_data; } function load_profile() { $this->set_location(); $this->profile_enqueuing(); } /** * Check for the POSTed variables for Longitude and Latitude when the user saves the profile, * and save them against the appropriate user. * * @return void **/ function set_location() { // First check that there's a dialog present $present = (bool) @ $_POST['al_present']; if ( ! $present ) return; // Better luck next time // OK. Good to go... get the other fields $lat = (float) @ $_POST['al_latitude']; $long = (float) @ $_POST['al_longitude']; $place_name = @ $_POST['al_place_name']; $country = @ $_POST['al_country']; // Bung it all in the location_data property array $this->location_data[ 'latitude' ] = $lat; $this->location_data[ 'longitude' ] = $long; $this->location_data[ 'place_name' ] = $place_name; $this->location_data[ 'country' ] = $country; // Validate if ( ! $this->location_data[ 'place_name' ] ) { $this->errors[] = 'no_place_name'; } if ( ! $this->location_data[ 'country' ] ) { $this->errors[] = 'no_country'; } // Both lat & long present? if ( ! $this->location_data[ 'latitude' ] && $this->location_data[ 'latitude' ] !== (float) 0 ) { $this->errors[] = 'no_lat_or_long'; } else if ( ! $this->location_data[ 'longitude' ] && $this->location_data[ 'longitude' ] !== (float) 0 ) { $this->errors[] = 'no_lat_or_long'; // Let's validate then. } else { $this->validate_latitude(); $this->validate_longitude(); } // Any errors? Don't save the values. if ( $this->errors ) { return; } // Set the usermeta $profileuser = $this->get_profileuser(); update_usermeta( $profileuser->ID, 'author_location', $this->location_data ); } /** * WP Filter * Append a serialised array of error codes to the redirect, so they are * displayed on the next page. * * @param string $location The URL string to be redirected to * @return string The amended URL string to be redirected to **/ function append_errors( $location ) { // If we've no errors, then just pass it straight through if ( ! $this->errors ) return $location; // Serialise and encode the errors array, and add to the location query string $serialised = serialize( $this->errors ); $encoded = urlencode( $serialised ); $location = add_query_arg( 'al_errors', $encoded, $location ); // We don't want the anchor element on the URL return $location; } /** * WP Filter * Append a serialised array of the currently entered location data * to the redirect, so they can be displayed on the next page. * * @param string $location The URL string to be redirected to * @return string The amended URL string to be redirected to **/ function append_location_data( $location ) { // If we've no errors, then just pass it straight through if ( ! $this->location_data ) return $location; // Serialise and encode the location data array, and add to the location query string $serialised = serialize( $this->location_data ); $encoded = urlencode( $serialised ); $location = add_query_arg( 'al_data', $encoded, $location ); return $location; } /** * Show any errors or messages in the standard style. Called on admin pages. * * @return void **/ function admin_notices() { // Any errors in the errors property array? foreach ( $this->errors AS & $error_code ) { $this->render_error( $this->error_msgs[ $error_code ] ); } // Get any errors from the GET params $slashed = @ $_GET[ 'al_errors' ]; if ( ! $slashed ) { return; } // F'ing slashes $serialised = stripslashes( $slashed ); $errors = (array) unserialize( $serialised ); if ( ! $errors ) return; // After all that, any errors? Display them. foreach ( $errors AS & $error_code ) { $this->render_error( $this->error_msgs[ $error_code ] ); } } /** * Validate a value as a latitude. Put any errors into the errors array * property. I believe a Latitude is a float, between -90 and +90 * * @return void **/ function validate_latitude() { // Both errors served by the same message if ( ! is_float( $this->location_data[ 'latitude' ] ) || $this->location_data[ 'latitude' ] > 90 || $this->location_data[ 'latitude' ] < -90 ) { $this->errors[] = 'invalid_latitude'; } } /** * Validate a value as a longitude. Put any errors into the errors array * property. I believe a Longitude is a float, between -180 and +180 * * @return void **/ function validate_longitude() { // Both errors served by the same message if ( ! is_float( $this->location_data[ 'longitude' ] ) || $this->location_data[ 'longitude' ] > 180 || $this->location_data[ 'longitude' ] < -180 ) { $this->errors[] = 'invalid_longitude'; } } /** * Get the user object for the currently edited profile. * * @return WP User object **/ private function get_profileuser() { $user_id = (int) @ $_REQUEST[ 'user_id' ]; if ( ! $user_id ) { $current_user = wp_get_current_user(); $user_id = $current_user->ID; } return get_user_to_edit( $user_id ); } function shortcode_map_authors( $atts, $content = '' ) { $defaults = array( 'include_protected_posts' => false ); extract( shortcode_atts( $defaults, $atts ) ); // Pass off to the template method to remain as DRY as possible return $this->list_authors( $includ_protected_posts, false ); } /** * A template method to print the published authors. Default HTML can be overriden * by adding a new template file into view/author-location/authors.php. * * @return void * @author Simon Wheatley **/ public function list_authors( $include_protected_posts, $echo = true ) { $template_vars = array(); $template_vars['authors'] = $this->get_published_authors( $include_protected_posts ); $template_vars['utility'] = & $this->utility; // Capture the HTML $output = $this->capture( 'map-authors', $template_vars ); if ( ! $echo ) { error_log( 'Not echoing' ); return $output; } error_log( 'Echoing' ); echo $output; } /** * A getter which returns an array of published authors as WP_User objects * * @return array An array of WP_User objects. * @author Simon Wheatley **/ protected function get_published_authors( $include_protected_posts ) { // Maybe this has already been done? if ( ! empty( $this->published_authors ) ) return $this->published_authors; // ...obviously not $author_ids = $this->published_author_ids( $include_protected_posts ); foreach ( $author_ids AS $author_id ) { $author = new ALoc_Author( $author_id ); $author->include_protected_posts = $this->include_protected_posts; $this->published_authors[] = $author; } // All ready. return $this->published_authors; } /** * A method to return the list of IDs for published authors. N.B. This means that * if an author has not published at least one post, they will not be shown. * * @return array WordPress User IDs for the authors who have been (in)active in the last 30 days. * @author Simon Wheatley **/ protected function published_author_ids( $include_protected_posts ) { // Maybe this has already been done? if ( ! empty( $this->author_ids ) ) return $this->author_ids; // ...obviously not global $wpdb; // SWTODO: This does NOT cope with posts being marked "private", which is different to password protecting posts $unprepared_sql = "SELECT DISTINCT post_author FROM $wpdb->posts "; $unprepared_sql .= "WHERE post_status = 'publish' AND post_type = 'post' "; // It strikes me that post_password might be NULL or the empty string, best check both if ( ! $include_protected_posts ) $unprepared_sql .= "AND ( post_password IS NULL OR post_password = '' ) "; $unprepared_sql .= "ORDER BY post_date_gmt DESC "; $sql = $wpdb->prepare( $unprepared_sql ); $this->published_author_ids = $wpdb->get_col( $sql ); return $this->published_author_ids; } } /** * Instantiate the plugin * * @global **/ $AuthorLocation = new AuthorLocation(); /** * A template tag function which wraps the list_authors method from the * AuthorLocations class for namespace convenience. * * @param string $args optional A string of URL GET alike variables which are parsed into params for the method call * @return void Prints some HTML * @author Simon Wheatley **/ function al_list_authors( $args = null ) { global $AuthorLocation; // Traditional WP argument munging. $defaults = array( 'include_protected_posts' => false, 'echo' => true ); $r = wp_parse_args( $args, $defaults ); // Sort out include_protected_posts arg if ( $r['include_protected_posts'] == 'yes' ) { $r['include_protected_posts'] = true; } if ( $r['include_protected_posts'] == 'no' ) { $r['include_protected_posts'] = false; } // Now cast to a boolean to be sure $r['include_protected_posts'] = (bool) $r['include_protected_posts']; // Sort out echo arg if ( $r['echo'] === 'yes' ) { $r['echo'] = true; } if ( $r['echo'] === 'no' ) { $r['echo'] = false; } // Now cast to a boolean to be sure $r['echo'] = (bool) $r['echo']; // Set the protected posts $include_protected_posts = $r['include_protected_posts']; // Set the echo (or not) $echo = $r['echo']; // Call the method $AuthorLocation->list_authors( $include_protected_posts, $echo ); } ?>