From 230d854fea543ccce2d341a2c8d0399263717763 Mon Sep 17 00:00:00 2001 From: Matthew Sigley Date: Mon, 29 Jan 2018 14:10:21 -0500 Subject: [PATCH 01/14] Added theme template for location pin content. Reworked javascript to eliminate a ton of unnecessary info in the CDATA. --- assets/js/build/main.js | 37 +---------------- includes/class-obj-public.php | 46 +++++++++++++++++----- templates/obj-map-location-pin-content.php | 11 ++++++ 3 files changed, 50 insertions(+), 44 deletions(-) create mode 100644 templates/obj-map-location-pin-content.php diff --git a/assets/js/build/main.js b/assets/js/build/main.js index c28a3f7..3a8cc2f 100644 --- a/assets/js/build/main.js +++ b/assets/js/build/main.js @@ -82,39 +82,6 @@ GoogleMapsLoader.load(function (google) { if (lat && lng) { var numLat = parseFloat(lat); var numLng = parseFloat(lng); - var components = location.address_components; - var streetNumber; - var streetName; - var cityName; - var stateName; - var countryName; - var zip; - - for (var i = 0; i < components.length; i++) { - if (components[i].types[0] === "street_number") { - streetNumber = components[i].long_name; - } - - if (components[i].types[0] === "route") { - streetName = components[i].short_name; - } - - if (components[i].types[0] === "locality") { - cityName = components[i].long_name; - } - - if (components[i].types[0] === "administrative_area_level_1") { - stateName = components[i].short_name; - } - - if (components[i].types[0] === "country") { - countryName = components[i].short_name; - } - - if (components[i].types[0] === "postal_code") { - zip = components[i].short_name; - } - } var marker = new google.maps.Marker({ map: map, @@ -122,7 +89,7 @@ GoogleMapsLoader.load(function (google) { }); var infoWindow = new google.maps.InfoWindow({ - content: '

' + location.post_title + '

' + '

' + '' + streetNumber + ' ' + streetName + '
' + cityName + ', ' + stateName + ' ' + countryName + ' ' + zip + '

' + '

' + content: location.content }); marker.addListener('click', function () { @@ -152,7 +119,7 @@ GoogleMapsLoader.load(function (google) { var markerLatLng = new google.maps.LatLng({ lat: numLat, lng: numLng }), toCenterDistance = google.maps.geometry.spherical.computeDistanceBetween( center, markerLatLng ); console.log(toCenterDistance); - + if ( markerBoundsRange >= toCenterDistance) { markersInBoundsRange.push( markerLatLng ); } diff --git a/includes/class-obj-public.php b/includes/class-obj-public.php index 2aa3449..6eb9ee3 100644 --- a/includes/class-obj-public.php +++ b/includes/class-obj-public.php @@ -71,31 +71,45 @@ public function map_shortcode_markup() { $posts = get_posts( $posts_arg ); + $location_pin_content_template = dirname( __FILE__ ) . '/../templates/obj-map-location-pin-content.php'; + if( $overridden_template = locate_template( 'obj-map-location-pin-content.php' ) ) + $location_pin_content_template = $overridden_template; + + $locations = array(); foreach( $posts as $key => $post ) { $lat = get_post_meta( $post->ID, 'obj_location_lat', true ); $lng = get_post_meta( $post->ID, 'obj_location_lng', true ); if ( $lat && $lng ) { - $posts[$key]->lat = $lat; - $posts[$key]->lng = $lng; - $posts[$key]->permalink = get_the_permalink( $post->ID ); - $posts[$key]->post_type_label = $post_type_object_labels->singular_name; - $posts[$key]->address = get_post_meta( $post->ID, 'obj_google_address', true ); - $posts[$key]->address_components = get_post_meta( $post->ID, 'obj_location_address_components', true ); + $location = new StdClass; + $location->lat = $lat; + $location->lng = $lng; + + $address_components = get_post_meta( $post->ID, 'obj_location_address_components', true ); + $template_varables = $this->rekey_address_components_array( $address_components ); + $template_varables['post_title'] = $posts[$key]->post_title; + $template_varables['post_type_label'] = $post_type_object_labels->singular_name; + $template_varables['permalink'] = get_the_permalink( $post->ID ); + + extract( $template_varables, EXTR_OVERWRITE|EXTR_PREFIX_ALL, 'obj_location' ); + ob_start(); + include "$location_pin_content_template"; + $location_pin_content = ob_get_clean(); + $location->content = $location_pin_content; + + $locations[] = $location; } } $data_array = array( 'apiKey' => get_option( 'obj_api_key' ), 'mapType' => get_option( 'obj_map_type' ), - 'mapCenter' => get_option( 'obj_map_center' ), - 'mapCenterAddressCompnents' => wp_cache_get( 'obj_map_center_address_components' ), 'mapCenterLat' => wp_cache_get( 'obj_map_center_lat' ), 'mapCenterLng' => wp_cache_get( 'obj_map_center_lng' ), 'mapZoom' => get_option( 'obj_map_zoom' ), 'mapSearch' => get_option( 'obj_map_search_by' ), 'mapLocationIcon' => get_option( 'obj_map_location_icon' ), - 'locations' => $posts + 'locations' => $locations ); if( !empty( $data_array['mapCenter'] ) @@ -151,4 +165,18 @@ private function update_center_lat_long_cache( &$data_array ) { $data_array['mapCenterLng'] = $longitude; } } + + private function rekey_address_components_array( $raw_address_components ) { + $address_components = array(); + foreach( $raw_address_components as $component ) { + $key = $component->types[0]; + $value = $component->long_name; + if( !empty( $component->short_name ) ) + $value = $component->short_name; + + $address_components[$key] = $value; + } + + return $address_components; + } } diff --git a/templates/obj-map-location-pin-content.php b/templates/obj-map-location-pin-content.php new file mode 100644 index 0000000..15affde --- /dev/null +++ b/templates/obj-map-location-pin-content.php @@ -0,0 +1,11 @@ +
+

+

+
+ , +

+

+
\ No newline at end of file From cc7b3b4ada36f29fca0c1d81242083b4b7203875 Mon Sep 17 00:00:00 2001 From: Matthew Sigley Date: Mon, 29 Jan 2018 14:36:07 -0500 Subject: [PATCH 02/14] Added unset loop to prevent data leaks between extract calls. --- includes/class-obj-public.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/includes/class-obj-public.php b/includes/class-obj-public.php index 6eb9ee3..763ffcf 100644 --- a/includes/class-obj-public.php +++ b/includes/class-obj-public.php @@ -95,6 +95,11 @@ public function map_shortcode_markup() { ob_start(); include "$location_pin_content_template"; $location_pin_content = ob_get_clean(); + foreach( array_keys($template_varables) as $key ) { + $key = 'obj_location_' . $key; + unset($$key); + } + $location->content = $location_pin_content; $locations[] = $location; From e12fbe6b0da0a6222a2e10927c1b5358e6d73d0a Mon Sep 17 00:00:00 2001 From: Matthew Sigley Date: Mon, 29 Jan 2018 14:36:35 -0500 Subject: [PATCH 03/14] Added apt and ste numbers to location pin content. --- templates/obj-map-location-pin-content.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/obj-map-location-pin-content.php b/templates/obj-map-location-pin-content.php index 15affde..5f1da89 100644 --- a/templates/obj-map-location-pin-content.php +++ b/templates/obj-map-location-pin-content.php @@ -1,7 +1,7 @@

-
+
, Date: Mon, 29 Jan 2018 15:06:12 -0500 Subject: [PATCH 04/14] Removed debugging call. --- assets/js/build/main.js | 1 - 1 file changed, 1 deletion(-) diff --git a/assets/js/build/main.js b/assets/js/build/main.js index 3a8cc2f..7daa63f 100644 --- a/assets/js/build/main.js +++ b/assets/js/build/main.js @@ -118,7 +118,6 @@ GoogleMapsLoader.load(function (google) { var markerLatLng = new google.maps.LatLng({ lat: numLat, lng: numLng }), toCenterDistance = google.maps.geometry.spherical.computeDistanceBetween( center, markerLatLng ); - console.log(toCenterDistance); if ( markerBoundsRange >= toCenterDistance) { markersInBoundsRange.push( markerLatLng ); From 75ab9c2e89f125c09dd937fec6a829210d485ec7 Mon Sep 17 00:00:00 2001 From: Matthew Sigley Date: Mon, 29 Jan 2018 15:07:15 -0500 Subject: [PATCH 05/14] Added geo schema markup to location pin content. Fix typo in variable name. --- includes/class-obj-public.php | 31 ++++++++++++---------- templates/obj-map-location-pin-content.php | 4 +++ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/includes/class-obj-public.php b/includes/class-obj-public.php index 763ffcf..433090e 100644 --- a/includes/class-obj-public.php +++ b/includes/class-obj-public.php @@ -78,28 +78,31 @@ public function map_shortcode_markup() { $locations = array(); foreach( $posts as $key => $post ) { $lat = get_post_meta( $post->ID, 'obj_location_lat', true ); + $lat = filter_var( $lat, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION ); $lng = get_post_meta( $post->ID, 'obj_location_lng', true ); + $lng = filter_var( $lng, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION ); if ( $lat && $lng ) { - $location = new StdClass; - $location->lat = $lat; - $location->lng = $lng; - $address_components = get_post_meta( $post->ID, 'obj_location_address_components', true ); - $template_varables = $this->rekey_address_components_array( $address_components ); - $template_varables['post_title'] = $posts[$key]->post_title; - $template_varables['post_type_label'] = $post_type_object_labels->singular_name; - $template_varables['permalink'] = get_the_permalink( $post->ID ); - - extract( $template_varables, EXTR_OVERWRITE|EXTR_PREFIX_ALL, 'obj_location' ); + $template_variables = $this->rekey_address_components_array( $address_components ); + $template_variables['lat'] = $lat; + $template_variables['lat'] = $lng; + $template_variables['post_title'] = $posts[$key]->post_title; + $template_variables['post_type_label'] = $post_type_object_labels->singular_name; + $template_variables['permalink'] = get_the_permalink( $post->ID ); + + extract( $template_variables, EXTR_OVERWRITE|EXTR_PREFIX_ALL, 'obj_location' ); ob_start(); include "$location_pin_content_template"; $location_pin_content = ob_get_clean(); - foreach( array_keys($template_varables) as $key ) { + foreach( array_keys($template_variables) as $key ) { $key = 'obj_location_' . $key; unset($$key); } - + + $location = new StdClass; + $location->lat = $lat; + $location->lng = $lng; $location->content = $location_pin_content; $locations[] = $location; @@ -109,8 +112,8 @@ public function map_shortcode_markup() { $data_array = array( 'apiKey' => get_option( 'obj_api_key' ), 'mapType' => get_option( 'obj_map_type' ), - 'mapCenterLat' => wp_cache_get( 'obj_map_center_lat' ), - 'mapCenterLng' => wp_cache_get( 'obj_map_center_lng' ), + 'mapCenterLat' => filter_var( wp_cache_get( 'obj_map_center_lat' ), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION ), + 'mapCenterLng' => filter_var( wp_cache_get( 'obj_map_center_lng' ), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION ), 'mapZoom' => get_option( 'obj_map_zoom' ), 'mapSearch' => get_option( 'obj_map_search_by' ), 'mapLocationIcon' => get_option( 'obj_map_location_icon' ), diff --git a/templates/obj-map-location-pin-content.php b/templates/obj-map-location-pin-content.php index 5f1da89..9a4447e 100644 --- a/templates/obj-map-location-pin-content.php +++ b/templates/obj-map-location-pin-content.php @@ -1,4 +1,8 @@

+
+ + +


From 9f7394c4c2bd5868e9a6fc5bedcc7d6198a56713 Mon Sep 17 00:00:00 2001 From: Matthew Sigley Date: Tue, 30 Jan 2018 15:23:42 -0500 Subject: [PATCH 06/14] Added support for location custom post meta. Added nonce verification and post save validation to location save handling. Reworked layout of metabox to better support multiple fields. --- includes/class-obj-admin.php | 121 ++++++++--- includes/class-obj-public.php | 22 +- includes/class-obj-uibuilder.php | 345 +++++++++++++++++++++++++++++++ 3 files changed, 462 insertions(+), 26 deletions(-) create mode 100644 includes/class-obj-uibuilder.php diff --git a/includes/class-obj-admin.php b/includes/class-obj-admin.php index 7dbaaee..5c443a6 100644 --- a/includes/class-obj-admin.php +++ b/includes/class-obj-admin.php @@ -17,8 +17,11 @@ class Obj_Gmaps_Admin { public function __construct( $file ) { + require_once 'class-obj-uibuilder.php'; + $this->file = $file; $this->dir = dirname( $file ); + $this->uibuilder = new Obj_Gmaps_UIBuilder( 'obj_location' ); // Activation and Deactivation Hooks register_activation_hook( $file, array( $this, 'activate_plugin' ) ); @@ -27,7 +30,6 @@ public function __construct( $file ) { if ( is_admin() ) { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_js' ) ); add_action( 'admin_init', array( $this, 'metaboxes_setup' ) ); - add_action( 'save_post', array( $this, 'save_location_lat_long' ), 10, 3 ); } } @@ -73,8 +75,13 @@ public function enqueue_js( $hook ) { */ public function metaboxes_setup () { $selected_post_type = get_option( 'obj_post_type' ); - add_action( 'add_meta_boxes_' . $selected_post_type, array( $this, 'create_metabox' ), 10, 1 ); - add_action( 'save_post', array( $this, 'save_metabox' ), 10, 2 ); + if( !empty($selected_post_type) ) { + add_action( 'add_meta_boxes_'.$selected_post_type, array( $this, 'create_metabox' ), 10, 1 ); + add_action( 'save_post_'.$selected_post_type, array( $this, 'save_post_validate' ), 9, 2 ); + add_action( 'save_post_'.$selected_post_type, array( $this, 'verify_wp_nonces' ), 10, 2 ); + add_action( 'save_post_'.$selected_post_type, array( $this, 'save_metabox' ), 11, 1 ); + add_action( 'save_post_'.$selected_post_type, array( $this, 'save_location_lat_long' ), 12, 1 ); + } } /** @@ -99,25 +106,79 @@ public function create_metabox( $post ) { * @since 1.0 */ public function metabox_content( $object ) { - wp_nonce_field( plugin_basename( $this->dir ), 'obj_google_address_nonce' ); - $data = array(); + wp_nonce_field( 'obj_google_save', 'obj_google_save_nonce' ); ?> -

- -
- -

+ + + + + + + $field_array ) { + if( empty($field_array['type']) || empty($field_array['label']) + || !is_callable( array($this->uibuilder, $field_array['type']) ) ) + continue; + + $meta_value = get_post_meta( $object->ID, $this->uibuilder->get_name_id($meta_key), true ); + ?> + + + + + + +
+ + + +
+ + + uibuilder->{$field_array['type']}( $meta_key, $meta_value, 'widefat' ); ?> +
is_valid_post_save($post) ) { + //Remove post saving actions + $post_type = $post->post_type; + remove_action( 'save_post_'.$post_type, array( $this, 'verify_wp_nonces' ), 10 ); + remove_action( 'save_post_'.$post_type, array( $this, 'save_metabox' ), 11 ); + remove_action( 'save_post_'.$post_type, array( $this, 'save_location_lat_long' ), 12 ); + } + } - $post_type = get_post_type($post_id); + private function is_valid_post_save($post) { + //Check for auto saves, creating a new post, no post array, and unhandled post types + if( is_array($post) ) + $post = (object) $post; + if( empty($_POST) + || 'auto-draft' == $post->post_status + || 'trash' == $post->post_status + || (defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE) ) + return false; + return true; + } - // If this isn't a 'book' post, don't update it. - if ( get_option( 'obj_post_type' ) != $post_type ) return; + public function verify_wp_nonces( $post_id, $post ) { + if( !$this->is_valid_post_save($post) ) + return; //Do nothing + + if( !isset( $_POST['obj_google_save_nonce'] ) ) + $this->display_error('verify_nonce', 'Unable to verify security nonce.'); + + check_admin_referer( 'obj_google_save', 'obj_google_save_nonce' ); + } + public function save_location_lat_long( $post_id ) { // - Update the post's metadata. if ( isset( $_POST['obj-google-address'] ) ) { $address = $_POST['obj-google-address']; @@ -144,15 +205,27 @@ function save_location_lat_long( $post_id, $post, $update ) { * * @since 1.0 */ - public function save_metabox( $post_id, $post ) { - - $selected_post_type = get_option( 'obj_post_type' ); - - $new_meta_value = ( isset( $_POST['obj-google-address'] ) ? sanitize_text_field( $_POST['obj-google-address'] ) : '' ); - $new_place_id_value = ( isset( $_POST['obj-google-address-place-id'] ) ? sanitize_text_field( $_POST['obj-google-address-place-id'] ) : '' ); - $meta_key = 'obj_google_address'; - $meta_value = get_post_meta( $post_id, $meta_key, true ); - update_post_meta( $post_id, $meta_key, $new_meta_value ); + public function save_metabox( $post_id ) { + //Save location address + $obj_google_address = ''; + if( isset( $_POST['obj-google-address'] ) ) + $obj_google_address = sanitize_text_field( $_POST['obj-google-address'] ); + update_post_meta( $post_id, 'obj_google_address', $obj_google_address ); + + //Save location post meta + $custom_post_meta = array(); + $custom_post_meta = apply_filters( 'obj_location_post_meta', $custom_post_meta ); + // Supported field types: date, textbox, url, email, hidden, textarea + // TODO: Add support for checkbox, number, and selectbox field types. UI functions exist but the saving logic below will not work for them. + foreach( $custom_post_meta as $meta_key => $field_array ) { + $meta_value = ''; + if( isset( $_POST[$this->uibuilder->get_name_id($meta_key)] ) ) + $meta_value = sanitize_text_field( $_POST[$this->uibuilder->get_name_id($meta_key)] ); + update_post_meta( $post_id, $this->uibuilder->get_name_id($meta_key), $meta_value ); + } } + private function display_error($code, $message) { + wp_die( new WP_Error('obj_google_'.$code, 'Objectiv Google Maps: '.$message) ); + } } diff --git a/includes/class-obj-public.php b/includes/class-obj-public.php index 433090e..a215ad9 100644 --- a/includes/class-obj-public.php +++ b/includes/class-obj-public.php @@ -63,7 +63,18 @@ public function map_shortcode_markup() { $posts_arg = array( 'post_type' => $selected_post_type, - 'posts_per_page' => -1 + 'posts_per_page' => -1, + 'meta_query' => array( + 'relation' => 'AND', + array( + 'key' => 'obj_location_lat', + 'compare' => 'EXISTS' + ), + array( + 'key' => 'obj_location_lng', + 'compare' => 'EXISTS' + ) + ) ); $post_type_object = get_post_type_object( $selected_post_type ); @@ -75,6 +86,9 @@ public function map_shortcode_markup() { if( $overridden_template = locate_template( 'obj-map-location-pin-content.php' ) ) $location_pin_content_template = $overridden_template; + $post_meta_to_load = array(); + $post_meta_to_load = array_keys( apply_filters( 'obj_location_post_meta', $post_meta_to_load ) ); + $locations = array(); foreach( $posts as $key => $post ) { $lat = get_post_meta( $post->ID, 'obj_location_lat', true ); @@ -86,11 +100,15 @@ public function map_shortcode_markup() { $address_components = get_post_meta( $post->ID, 'obj_location_address_components', true ); $template_variables = $this->rekey_address_components_array( $address_components ); $template_variables['lat'] = $lat; - $template_variables['lat'] = $lng; + $template_variables['lng'] = $lng; $template_variables['post_title'] = $posts[$key]->post_title; + $template_variables['post_excerpt'] = $posts[$key]->post_excerpt; $template_variables['post_type_label'] = $post_type_object_labels->singular_name; $template_variables['permalink'] = get_the_permalink( $post->ID ); + foreach( $post_meta_to_load as $post_meta_key ) + $template_variables[$post_meta_key] = get_post_meta( $post->ID, 'obj_location_'.$post_meta_key, true ); + extract( $template_variables, EXTR_OVERWRITE|EXTR_PREFIX_ALL, 'obj_location' ); ob_start(); include "$location_pin_content_template"; diff --git a/includes/class-obj-uibuilder.php b/includes/class-obj-uibuilder.php new file mode 100644 index 0000000..007251f --- /dev/null +++ b/includes/class-obj-uibuilder.php @@ -0,0 +1,345 @@ +name_id_prefix = $name_id_prefix; + } + + public function get_name_id( $field_name ) { + return $this->name_id_prefix.'_'.$field_name; + } + + /* + * Function name: selectbox + * Purpose: Builds and echos html for a selectbox. + * Agruements: + * -$field_name: Value of name attribute for form submission. + * Doubles as the field's id. + * -$options: An array of options to populate the selectbox with. + * Formatted as value => text. + * -$selected: Value of option to be selected by default.' + * If multiple is true, this can be an array of values. + * -$default_option: Text to be displayed in the default option. + * Blank or false for no default option. + * -$multiple: Boolean value indicates that multiple options can be selected. + */ + public function selectbox( $field_name, $options, $class=false, $selected='', + $default_option='Select an option', + $use_prefix=true, $multiple=false, $disabled=false ) { + + $field_name_id = $field_name; + if( $use_prefix ) + $field_name_id = $this->get_name_id($field_name); + $field_name = $field_name_id; + $field_id = $field_name_id; + if( $multiple ) + $field_name .= '[]'; + + $attrs = ''; + if( $multiple ) + $attrs[] .= 'multiple="multiple"'; + if( $disabled ) + $attrs[] .= 'disabled="disabled"'; + if( $class ) + $attrs[] .= 'class="'.$class.'"'; + $attrs = implode(' ', $attrs); + + if( !is_array($selected) ) + $selected = array($selected); + + $output = ''."\n"; + + echo $output; + } + + /* + * Function name: selectbox_posts + * Purpose: Builds and echos html for a selectbox to select a post id. + * Agruements: + * -$field_name: Value of name attribute for form submission. + * Doubles as the field's id. + * -$posts: An array of WP Post objects to populate selectbox options. + * -$selected: Value of option to be selected by default. + * -$default_option: Text to be displayed in the default option. + * Blank or false for no default option. + */ + public function selectbox_posts( $field_name, $posts, $class='', $selected='', + $default_option='Select an option', + $use_prefix=true, $multiple=false ) { + //Do nothing if no posts were provided + if( empty($posts) || !is_array($posts) ) + return; + + //Format post array into select options + $options = array(); + foreach( $posts as $post_obj ) { + $options[$post_obj->ID] = $post_obj->post_title; + } + + //Output selectbox + $this->selectbox( $field_name, $options, $selected, + $default_option, $use_prefix, $multiple, $class); + } + + /* + * Function name: selectbox_terms + * Purpose: Builds and echos html for a selectbox to select a term id + * Agruements: + * -$field_name: Value of name attribute for form submission. + * Doubles as the field's id. + * -$terms: An array of WP Term objects to populate selectbox options. + * -$selected: Value of option to be selected by default. + * -$default_option: Text to be displayed in the default option. + * Blank or false for no default option. + */ + public function selectbox_terms( $field_name, $terms, $class='', $selected='', + $default_option='Select an option', + $use_prefix=true, $multiple=false ) { + //Do nothing if no posts were provided + if( empty($terms) || !is_array($terms) ) + return; + + //Format post array into select options + $options = array(); + foreach( $terms as $term_obj ) { + $options[$term_obj->term_id] = $term_obj->name; + } + + //Output selectbox + $this->selectbox( $field_name, $options, $selected, + $default_option, $use_prefix, $multiple, $class); + } + + /* + * Function name: date + * Purpose: Builds and echos html for a date selector. + * Arguements: + * -$field_name: Value of name attribute for form submission. + * -$value: Initial value of the date field. + */ + public function date($field_name, $value='', $class='' ) { + $field_name_id = $this->get_name_id($field_name); + + $output = ''; + + echo $output; + } + + /* + * Function name: textbox + * Purpose: Builds and echos html for a textbox. + * Arguements: + * -$field_name: Value of name attribute for form submission. + * -$value: Initial value of the text field. + */ + public function textbox($field_name, $value='', $class='' ) { + $field_name_id = $this->get_name_id($field_name); + + $output = ''; + + echo $output; + } + + /* + * Function name: url + * Purpose: Builds and echos html for a url field. + * Arguements: + * -$field_name: Value of name attribute for form submission. + * -$value: Initial value of the url field. + */ + public function url($field_name, $value='', $class='' ) { + $field_name_id = $this->get_name_id($field_name); + + $output = ''; + + echo $output; + } + + /* + * Function name: email + * Purpose: Builds and echos html for an email field. + * Arguements: + * -$field_name: Value of name attribute for form submission. + * -$value: Initial value of the email field. + */ + public function email($field_name, $value='', $class='' ) { + $field_name_id = $this->get_name_id($field_name); + + $output = ''; + + echo $output; + } + + /* + * Function name: hidden + * Purpose: Builds and echos html for a hidden field. + * Arguements: + * -$field_name: Value of name attribute for form submission. + * -$value: Initial value of the hidden field. + */ + public function hidden($field_name, $value='', $class='' ) { + $field_name_id = $this->get_name_id($field_name); + + $output = ''; + + echo $output; + } + + /* + * Function name: checkbox + * Purpose: Builds and echos html for a checkbox. + * Arguements: + * -$field_name: Value of name attribute for form submission. + * -$value: Value of the checkbox. + * -$checked: Whether the checkbox is checked by default. + */ + public function checkbox($field_name, $value='', $class='', $checked=false ) { + $field_name_id = $this->get_name_id($field_name); + + if( $checked ) $checked = ' checked="checked"'; + else $checked = ''; + + $output = ''; + + echo $output; + } + + /* + * Function name: number + * Purpose: Builds and echos html for a number field. + * Arguements: + * -$field_name: Value of name attribute for form submission. + * Doubles as the field's id. + * -$value: Initial value of the number field. + * Must be a $step value inside the $min to $max range. + * -$step: Number to increment by starting with $min. + * -$min: Minimum allowed numeric value, inclusive. + * -$max: Maximum allowed numeric value, inclusive. + */ + public function number($field_name, $value=0, $class='', $step=1, $min='', $max='') { + //Validate and correct arguements + if(empty($value)) + $value = 0; + if(empty($step)) + $step = 1; + + //Ensure numbers were passed into the arguements + if(!empty($value)) + $value = (float) $value; + if(!empty($step)) + $step = (float) $step; + if(!empty($min)) + $min = (float) $min; + if(!empty($max)) + $max = (float) $max; + + //Correct initial value of field + if(!empty($min) && $value < $min) + $value = $min; + elseif(!empty($max) && $value > $max) + $value = $max; + elseif(!empty($min) && ($value - $min) % $step != 0) { + //Round down to nearest step + $num_steps = floor(($value - $min) / $step); + $value = $min + ($step * $num_steps); + } + + $field_name_id = $this->get_name_id($field_name); + + $output = ''; + + echo $output; + } + + /* + * Function name: file + * Purpose: Builds and echos html for a file field. + * Arguements: + * -$field_name: Value of name attribute for form submission. + * -$accept: Comma separated string or array of valid file extensions or mime types + * -$max-size: Maximum file size in bytes. For JS form validation. + * -$class: Value of class attribute. + * -$ajax_url: URL to be used by AJAX scripts for uploading the file. + */ + public function file($field_name, $accept='', $max_size='', $class='', $ajax_url='') { + $field_name_id = $this->get_name_id($field_name); + + $output = ''; + + echo $output; + } + + /* + * Function name: textarea + * Purpose: Builds and echos html for a textarea. + * Arguements: + * -$field_name: Value of name attribute for form submission. + * -$value: Initial value of the textarea. + */ + public function textarea($field_name, $value='', $class='') { + $field_name_id = $this->get_name_id($field_name); + + $output = ''; + + echo $output; + } + + /* + * Function name: submit + * Purpose: Builds and echos html for a submit button. + * Arguements: + * -$field_name: Value of name attribute for form submission. + * -$value: Value of the submit button. + */ + public function submit($field_name, $value='Submit', $class='') { + $field_name_id = $this->get_name_id($field_name); + + $output = ''; + + echo $output; + } + + /* + * Function name: tinymce_box + * Purpose: Builds and echos html for a TinyMCE WYSIWYG editor. + * Arguements: + * -$field_name: Value of name attribute for form submission. + * Doubles as the field's id. + * -$value: String containing html to rendered by TinyMCE. + */ + public function tinymce_editor($field_name, $value='', $tinymce_settings=array()) { + wp_editor($value, $this->get_name_id($field_name), $tinymce_settings); + } +} \ No newline at end of file From 42df3cc5a36f89b45583f5bc4f269e0a18b2eecd Mon Sep 17 00:00:00 2001 From: Matthew Sigley Date: Wed, 31 Jan 2018 12:00:18 -0500 Subject: [PATCH 07/14] Added objMap:infowindowDomready jquery event trigger for customizing the location pin content with javascript. --- assets/js/build/main.js | 588 +++++++++++++++++----------------- includes/class-obj-public.php | 8 +- 2 files changed, 303 insertions(+), 293 deletions(-) diff --git a/assets/js/build/main.js b/assets/js/build/main.js index 7daa63f..b7f1262 100644 --- a/assets/js/build/main.js +++ b/assets/js/build/main.js @@ -1,383 +1,391 @@ -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= toCenterDistance) { - markersInBoundsRange.push( markerLatLng ); - } + GoogleMapsLoader.load(function (google) { + var map; + var autocomplete; + var places; + var el = document.getElementById('obj-google-maps'); + var input = document.getElementById('obj-search-input'); + var searchBox = new google.maps.places.SearchBox(input); + var locations = data.locations; + var userIcon = 'https://maps.google.com/mapfiles/ms/micons/man.png'; + var options = { + zoom: parseInt(data.mapZoom), + mapTypeId: data.mapType, + center: new google.maps.LatLng(data.mapCenterLat, data.mapCenterLng) + }; + + /** + * When a city has been selected pan and zoom to the center + */ + function onPlaceChanged() { + var place = autocomplete.getPlace(); + if (place.geometry) { + clearUserMarkers(); + map.panTo(place.geometry.location); + map.setZoom(options.zoom); + + var marker = new google.maps.Marker({ + map: map, + position: { lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }, + icon: userIcon + }); + userMarkers.push(marker); + + fitMapBounds( new google.maps.LatLng( place.geometry.location.lat(), place.geometry.location.lng() ) ); + } else { + input.placeholder = 'Search by city...'; + } } - }); - var mapBounds = map.getBounds(); - markersInBoundsRange.forEach( function(markerLatLng) { - mapBounds.extend( markerLatLng ); - }); - map.fitBounds( mapBounds, 20 ); - } + function loadMap() { + // initiate the map + map = new google.maps.Map(el, options); + // add the input to the top left of the map + map.controls[google.maps.ControlPosition.TOP_LEFT].push(input); - /* - * Get lat and long from browsers geolocation API - */ - if (window.location.protocol === 'https:' && "geolocation" in navigator) { - navigator.geolocation.getCurrentPosition( function (position) { - //Success - options.center = new google.maps.LatLng(position.coords.latitude, position.coords.longitude); - loadMap(); + // set the bounds for the search box + map.addListener('bounds_changed', function () { + searchBox.setBounds(map.getBounds()); + }); - // add user marker on center of map - var marker = new google.maps.Marker({ - map: map, - position: { lat: options.center.lat(), lng: options.center.lng() }, - icon: userIcon + // set up the google places autocomplete restricted to cities + autocomplete = new google.maps.places.Autocomplete( + /** @type {!HTMLInputElement} */document.getElementById('obj-search-input'), { + types: [data.mapSearch] }); - userMarkers.push(marker); - }, function() { - //Error - loadMap(); - }); - } else { - //Geolocation API not available - loadMap(); - } -}); -},{"google-maps":2}],2:[function(require,module,exports){ -(function(root, factory) { + places = new google.maps.places.PlacesService(map); + + // add listener to run onPlaceChanged when a city has been selected + autocomplete.addListener('place_changed', onPlaceChanged); + + // Loop through locations and add the markers + locations.forEach(function (location) { + var lat = location.lat; + var lng = location.lng; + + if (lat && lng) { + var numLat = parseFloat(lat); + var numLng = parseFloat(lng); + + var marker = new google.maps.Marker({ + map: map, + position: { lat: numLat, lng: numLng } + }); + var infoWindowContentNode = document.createElement('div'); + infoWindowContentNode.className = 'obj-maps-location-pin-content'; + infoWindowContentNode.innerHTML = location.content; + var infoWindow = new google.maps.InfoWindow({ + content: infoWindowContentNode + }); - if (root === null) { - throw new Error('Google-maps package can be used only in browser'); - } + google.maps.event.addListener(infoWindow, 'domready', function(){ + $('body').trigger('objMap:infowindowDomready', infoWindowContentNode); + }); - if (typeof define === 'function' && define.amd) { - define(factory); - } else if (typeof exports === 'object') { - module.exports = factory(); - } else { - root.GoogleMapsLoader = factory(); - } + marker.addListener('click', function () { + infoWindow.open(map, marker); + }); + } + }); -})(typeof window !== 'undefined' ? window : null, function() { + google.maps.event.addListenerOnce(map, 'idle', function(){ + fitMapBounds( options.center ); + }); + } + //Ensure map has all markers in view within 100 miles + function fitMapBounds( center ) { + var markerBoundsRange = 160934, + markersInBoundsRange = []; //100 Miles + + locations.forEach(function (location) { + var lat = location.lat; + var lng = location.lng; + + if (lat && lng) { + var numLat = parseFloat(lat); + var numLng = parseFloat(lng); + + var markerLatLng = new google.maps.LatLng({ lat: numLat, lng: numLng }), + toCenterDistance = google.maps.geometry.spherical.computeDistanceBetween( center, markerLatLng ); + + if ( markerBoundsRange >= toCenterDistance) { + markersInBoundsRange.push( markerLatLng ); + } + } + }); - 'use strict'; + var mapBounds = map.getBounds(); + markersInBoundsRange.forEach( function(markerLatLng) { + mapBounds.extend( markerLatLng ); + }); + map.fitBounds( mapBounds, 20 ); + } - var googleVersion = '3.18'; + /* + * Get lat and long from browsers geolocation API + */ + if (window.location.protocol === 'https:' && "geolocation" in navigator) { + navigator.geolocation.getCurrentPosition( function (position) { + //Success + options.center = new google.maps.LatLng(position.coords.latitude, position.coords.longitude); + loadMap(); - var script = null; + // add user marker on center of map + var marker = new google.maps.Marker({ + map: map, + position: { lat: options.center.lat(), lng: options.center.lng() }, + icon: userIcon + }); + userMarkers.push(marker); + }, function() { + //Error + loadMap(); + }); + } else { + //Geolocation API not available + loadMap(); + } + }); - var google = null; + },{"google-maps":2}],2:[function(require,module,exports){ + (function(root, factory) { - var loading = false; + if (root === null) { + throw new Error('Google-maps package can be used only in browser'); + } - var callbacks = []; + if (typeof define === 'function' && define.amd) { + define(factory); + } else if (typeof exports === 'object') { + module.exports = factory(); + } else { + root.GoogleMapsLoader = factory(); + } - var onLoadEvents = []; + })(typeof window !== 'undefined' ? window : null, function() { - var originalCreateLoaderMethod = null; + 'use strict'; - var GoogleMapsLoader = {}; + var googleVersion = '3.18'; - GoogleMapsLoader.URL = 'https://maps.googleapis.com/maps/api/js'; + var script = null; - GoogleMapsLoader.KEY = null; + var google = null; - GoogleMapsLoader.LIBRARIES = []; + var loading = false; - GoogleMapsLoader.CLIENT = null; + var callbacks = []; - GoogleMapsLoader.CHANNEL = null; + var onLoadEvents = []; - GoogleMapsLoader.LANGUAGE = null; + var originalCreateLoaderMethod = null; - GoogleMapsLoader.REGION = null; - GoogleMapsLoader.VERSION = googleVersion; + var GoogleMapsLoader = {}; - GoogleMapsLoader.WINDOW_CALLBACK_NAME = '__google_maps_api_provider_initializator__'; + GoogleMapsLoader.URL = 'https://maps.googleapis.com/maps/api/js'; - GoogleMapsLoader._googleMockApiObject = {}; + GoogleMapsLoader.KEY = null; + GoogleMapsLoader.LIBRARIES = []; - GoogleMapsLoader.load = function(fn) { - if (google === null) { - if (loading === true) { - if (fn) { - callbacks.push(fn); - } - } else { - loading = true; + GoogleMapsLoader.CLIENT = null; - window[GoogleMapsLoader.WINDOW_CALLBACK_NAME] = function() { - ready(fn); - }; + GoogleMapsLoader.CHANNEL = null; - GoogleMapsLoader.createLoader(); - } - } else if (fn) { - fn(google); - } - }; + GoogleMapsLoader.LANGUAGE = null; + GoogleMapsLoader.REGION = null; - GoogleMapsLoader.createLoader = function() { - script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = GoogleMapsLoader.createUrl(); + GoogleMapsLoader.VERSION = googleVersion; - document.body.appendChild(script); - }; + GoogleMapsLoader.WINDOW_CALLBACK_NAME = '__google_maps_api_provider_initializator__'; - GoogleMapsLoader.isLoaded = function() { - return google !== null; - }; + GoogleMapsLoader._googleMockApiObject = {}; - GoogleMapsLoader.createUrl = function() { - var url = GoogleMapsLoader.URL; + GoogleMapsLoader.load = function(fn) { + if (google === null) { + if (loading === true) { + if (fn) { + callbacks.push(fn); + } + } else { + loading = true; - url += '?callback=' + GoogleMapsLoader.WINDOW_CALLBACK_NAME; + window[GoogleMapsLoader.WINDOW_CALLBACK_NAME] = function() { + ready(fn); + }; - if (GoogleMapsLoader.KEY) { - url += '&key=' + GoogleMapsLoader.KEY; - } + GoogleMapsLoader.createLoader(); + } + } else if (fn) { + fn(google); + } + }; - if (GoogleMapsLoader.LIBRARIES.length > 0) { - url += '&libraries=' + GoogleMapsLoader.LIBRARIES.join(','); - } - if (GoogleMapsLoader.CLIENT) { - url += '&client=' + GoogleMapsLoader.CLIENT + '&v=' + GoogleMapsLoader.VERSION; - } + GoogleMapsLoader.createLoader = function() { + script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = GoogleMapsLoader.createUrl(); - if (GoogleMapsLoader.CHANNEL) { - url += '&channel=' + GoogleMapsLoader.CHANNEL; - } + document.body.appendChild(script); + }; - if (GoogleMapsLoader.LANGUAGE) { - url += '&language=' + GoogleMapsLoader.LANGUAGE; - } - if (GoogleMapsLoader.REGION) { - url += '®ion=' + GoogleMapsLoader.REGION; - } + GoogleMapsLoader.isLoaded = function() { + return google !== null; + }; - return url; - }; + GoogleMapsLoader.createUrl = function() { + var url = GoogleMapsLoader.URL; - GoogleMapsLoader.release = function(fn) { - var release = function() { - GoogleMapsLoader.KEY = null; - GoogleMapsLoader.LIBRARIES = []; - GoogleMapsLoader.CLIENT = null; - GoogleMapsLoader.CHANNEL = null; - GoogleMapsLoader.LANGUAGE = null; - GoogleMapsLoader.REGION = null; - GoogleMapsLoader.VERSION = googleVersion; + url += '?callback=' + GoogleMapsLoader.WINDOW_CALLBACK_NAME; - google = null; - loading = false; - callbacks = []; - onLoadEvents = []; + if (GoogleMapsLoader.KEY) { + url += '&key=' + GoogleMapsLoader.KEY; + } - if (typeof window.google !== 'undefined') { - delete window.google; + if (GoogleMapsLoader.LIBRARIES.length > 0) { + url += '&libraries=' + GoogleMapsLoader.LIBRARIES.join(','); } - if (typeof window[GoogleMapsLoader.WINDOW_CALLBACK_NAME] !== 'undefined') { - delete window[GoogleMapsLoader.WINDOW_CALLBACK_NAME]; + if (GoogleMapsLoader.CLIENT) { + url += '&client=' + GoogleMapsLoader.CLIENT + '&v=' + GoogleMapsLoader.VERSION; } - if (originalCreateLoaderMethod !== null) { - GoogleMapsLoader.createLoader = originalCreateLoaderMethod; - originalCreateLoaderMethod = null; + if (GoogleMapsLoader.CHANNEL) { + url += '&channel=' + GoogleMapsLoader.CHANNEL; } - if (script !== null) { - script.parentElement.removeChild(script); - script = null; + if (GoogleMapsLoader.LANGUAGE) { + url += '&language=' + GoogleMapsLoader.LANGUAGE; } - if (fn) { - fn(); + if (GoogleMapsLoader.REGION) { + url += '®ion=' + GoogleMapsLoader.REGION; } + + return url; }; - if (loading) { - GoogleMapsLoader.load(function() { + + GoogleMapsLoader.release = function(fn) { + var release = function() { + GoogleMapsLoader.KEY = null; + GoogleMapsLoader.LIBRARIES = []; + GoogleMapsLoader.CLIENT = null; + GoogleMapsLoader.CHANNEL = null; + GoogleMapsLoader.LANGUAGE = null; + GoogleMapsLoader.REGION = null; + GoogleMapsLoader.VERSION = googleVersion; + + google = null; + loading = false; + callbacks = []; + onLoadEvents = []; + + if (typeof window.google !== 'undefined') { + delete window.google; + } + + if (typeof window[GoogleMapsLoader.WINDOW_CALLBACK_NAME] !== 'undefined') { + delete window[GoogleMapsLoader.WINDOW_CALLBACK_NAME]; + } + + if (originalCreateLoaderMethod !== null) { + GoogleMapsLoader.createLoader = originalCreateLoaderMethod; + originalCreateLoaderMethod = null; + } + + if (script !== null) { + script.parentElement.removeChild(script); + script = null; + } + + if (fn) { + fn(); + } + }; + + if (loading) { + GoogleMapsLoader.load(function() { + release(); + }); + } else { release(); - }); - } else { - release(); - } - }; + } + }; - GoogleMapsLoader.onLoad = function(fn) { - onLoadEvents.push(fn); - }; + GoogleMapsLoader.onLoad = function(fn) { + onLoadEvents.push(fn); + }; - GoogleMapsLoader.makeMock = function() { - originalCreateLoaderMethod = GoogleMapsLoader.createLoader; + GoogleMapsLoader.makeMock = function() { + originalCreateLoaderMethod = GoogleMapsLoader.createLoader; - GoogleMapsLoader.createLoader = function() { - window.google = GoogleMapsLoader._googleMockApiObject; - window[GoogleMapsLoader.WINDOW_CALLBACK_NAME](); + GoogleMapsLoader.createLoader = function() { + window.google = GoogleMapsLoader._googleMockApiObject; + window[GoogleMapsLoader.WINDOW_CALLBACK_NAME](); + }; }; - }; - var ready = function(fn) { - var i; + var ready = function(fn) { + var i; - loading = false; + loading = false; - if (google === null) { - google = window.google; - } + if (google === null) { + google = window.google; + } - for (i = 0; i < onLoadEvents.length; i++) { - onLoadEvents[i](google); - } + for (i = 0; i < onLoadEvents.length; i++) { + onLoadEvents[i](google); + } - if (fn) { - fn(google); - } + if (fn) { + fn(google); + } - for (i = 0; i < callbacks.length; i++) { - callbacks[i](google); - } + for (i = 0; i < callbacks.length; i++) { + callbacks[i](google); + } - callbacks = []; - }; + callbacks = []; + }; - return GoogleMapsLoader; + return GoogleMapsLoader; -}); + }); -},{}]},{},[1]); + },{}]},{},[1]); +})( jQuery ); \ No newline at end of file diff --git a/includes/class-obj-public.php b/includes/class-obj-public.php index a215ad9..be64cf4 100644 --- a/includes/class-obj-public.php +++ b/includes/class-obj-public.php @@ -33,7 +33,7 @@ public function __construct( $file, $version ) { public function enqueue_js() { if ( obj_has_shortcode( 'objectiv_google_maps' ) ) { - wp_enqueue_script( 'obj-google-maps', plugins_url( '/assets/js/build/main.js', $this->file ), array(), $this->version, true ); + wp_enqueue_script( 'obj-google-maps', plugins_url( '/assets/js/build/main.js', $this->file ), array('jquery'), $this->version, true ); wp_enqueue_style( 'obj-google-maps-style', plugins_url( '/assets/css/public/public.css', $this->file ), array(), $this->version ); } @@ -101,8 +101,10 @@ public function map_shortcode_markup() { $template_variables = $this->rekey_address_components_array( $address_components ); $template_variables['lat'] = $lat; $template_variables['lng'] = $lng; - $template_variables['post_title'] = $posts[$key]->post_title; - $template_variables['post_excerpt'] = $posts[$key]->post_excerpt; + $template_variables['post_id'] = $post->ID; + $template_variables['post_title'] = trim( $post->post_title ); + $template_variables['post_excerpt'] = trim( $post->post_excerpt ); + $template_variables['post_content'] = trim( $post->post_content ); $template_variables['post_type_label'] = $post_type_object_labels->singular_name; $template_variables['permalink'] = get_the_permalink( $post->ID ); From 3b81ec946b9e9d40e0f61f76e928516fe580a4b7 Mon Sep 17 00:00:00 2001 From: Matthew Sigley Date: Wed, 31 Jan 2018 17:33:01 -0500 Subject: [PATCH 08/14] Added time function to support time inputs. --- includes/class-obj-uibuilder.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/includes/class-obj-uibuilder.php b/includes/class-obj-uibuilder.php index 007251f..098c316 100644 --- a/includes/class-obj-uibuilder.php +++ b/includes/class-obj-uibuilder.php @@ -137,6 +137,22 @@ public function date($field_name, $value='', $class='' ) { echo $output; } + + /* + * Function name: time + * Purpose: Builds and echos html for a time selector. + * Arguements: + * -$field_name: Value of name attribute for form submission. + * -$value: Initial value of the date field. + */ + public function time($field_name, $value='', $class='' ) { + $field_name_id = $this->get_name_id($field_name); + + $output = ''; + + echo $output; + } /* * Function name: textbox From ebaebfbb014ebd1e3e0c03d9b74bbb4592334da5 Mon Sep 17 00:00:00 2001 From: Matthew Sigley Date: Wed, 31 Jan 2018 17:33:18 -0500 Subject: [PATCH 09/14] Added lng lat to address metabox. --- includes/class-obj-admin.php | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/includes/class-obj-admin.php b/includes/class-obj-admin.php index 5c443a6..4357cf9 100644 --- a/includes/class-obj-admin.php +++ b/includes/class-obj-admin.php @@ -107,9 +107,40 @@ public function create_metabox( $post ) { */ public function metabox_content( $object ) { wp_nonce_field( 'obj_google_save', 'obj_google_save_nonce' ); + + $lat = get_post_meta( $object->ID, 'obj_location_lat', true ); + if( empty($lat) ) + $lat = 'Not set. Marker will not appear on map. Save the post to try geocoding the address again.'; + $lng = get_post_meta( $object->ID, 'obj_location_lng', true ); + if( empty($lng) ) + $lng = 'Not set. Marker will not appear on map. Save the post to try geocoding the address again.'; ?> + + + + + + + + + + + + - - - - $field_array ) { if( empty($field_array['type']) || empty($field_array['label']) @@ -183,7 +176,6 @@ public function save_post_validate( $post_id, $post ) { $post_type = $post->post_type; remove_action( 'save_post_'.$post_type, array( $this, 'verify_wp_nonces' ), 10 ); remove_action( 'save_post_'.$post_type, array( $this, 'save_metabox' ), 11 ); - remove_action( 'save_post_'.$post_type, array( $this, 'save_location_lat_long' ), 12 ); } } @@ -203,32 +195,40 @@ public function verify_wp_nonces( $post_id, $post ) { if( !$this->is_valid_post_save($post) ) return; //Do nothing - if( !isset( $_POST['obj_google_save_nonce'] ) ) + if( !isset( $_POST['obj_google_save_nonce_'.$post_id] ) ) $this->display_error('verify_nonce', 'Unable to verify security nonce.'); - check_admin_referer( 'obj_google_save', 'obj_google_save_nonce' ); + check_admin_referer( 'obj_google_save', 'obj_google_save_nonce_'.$post_id ); } - public function save_location_lat_long( $post_id ) { - // - Update the post's metadata. - if ( isset( $_POST['obj-google-address'] ) ) { - $address = $_POST['obj-google-address']; - $string = str_replace (" ", "+", urlencode( $address ) ); - $url = "http://maps.googleapis.com/maps/api/geocode/json?address=".$string."&sensor=false"; + public function save_location_lat_long( $post_id, $address ) { + // - Update the post's metadata. + if ( !empty($address) ) { + $string = str_replace (" ", "+", urlencode( $address ) ); + $url = "https://maps.googleapis.com/maps/api/geocode/json?address=".$string; + if( !empty( $this->geocode_api_key) ) + $url .= '&key='.urlencode($this->geocode_api_key); - $response = wp_remote_get( $url ); - $data = wp_remote_retrieve_body( $response ); - $output = json_decode( $data ); - if (!empty($output) && $output->status == 'OK') { + $response = wp_remote_get( $url ); + $data = wp_remote_retrieve_body( $response ); + $output = json_decode( $data ); + + if ( !empty($output) && $output->status == 'OK' ) { $address_components = $output->results[0]->address_components; - $geometry = $output->results[0]->geometry; - $longitude = $geometry->location->lng; - $latitude = $geometry->location->lat; + $geometry = $output->results[0]->geometry; + $latitude = $geometry->location->lat; + $longitude = $geometry->location->lng; update_post_meta( $post_id, 'obj_location_address_components', $address_components ); - update_post_meta( $post_id, 'obj_location_lat', $latitude ); - update_post_meta( $post_id, 'obj_location_lng', $longitude ); - } - } + update_post_meta( $post_id, 'obj_location_lat', $latitude ); + update_post_meta( $post_id, 'obj_location_lng', $longitude ); + return true; + } + } + + delete_post_meta( $post_id, 'obj_location_address_components' ); + delete_post_meta( $post_id, 'obj_location_lat' ); + delete_post_meta( $post_id, 'obj_location_lng' ); + return false; } /** @@ -238,22 +238,70 @@ public function save_location_lat_long( $post_id ) { */ public function save_metabox( $post_id ) { //Save location address - $obj_google_address = ''; + $new_address = ''; + $address = get_post_meta( $post_id, 'obj_google_address', true ); + $address_components = get_post_meta( $post_id, 'obj_location_address_components', true ); + $latitude = get_post_meta( $post_id, 'obj_location_lat', true ); + $longitude = get_post_meta( $post_id, 'obj_location_lng', true ); if( isset( $_POST['obj-google-address'] ) ) - $obj_google_address = sanitize_text_field( $_POST['obj-google-address'] ); - update_post_meta( $post_id, 'obj_google_address', $obj_google_address ); + $new_address = sanitize_text_field( $_POST['obj-google-address'] ); + //Prevent resaves from regeocoding address if lat and lng already exist + if( $new_address != $address || empty( $address_components ) || empty( $latitude ) || empty( $longitude ) ) { + update_post_meta( $post_id, 'obj_google_address', $new_address ); + $this->save_location_lat_long( $post_id, $new_address ); + } //Save location post meta $custom_post_meta = array(); $custom_post_meta = apply_filters( 'obj_location_post_meta', $custom_post_meta ); - // Supported field types: date, textbox, url, email, hidden, textarea + // Supported field types: date, time, textbox, url, email, hidden, tel, textarea // TODO: Add support for checkbox, number, and selectbox field types. UI functions exist but the saving logic below will not work for them. + $errors = array(); foreach( $custom_post_meta as $meta_key => $field_array ) { $meta_value = ''; - if( isset( $_POST[$this->uibuilder->get_name_id($meta_key)] ) ) - $meta_value = sanitize_text_field( $_POST[$this->uibuilder->get_name_id($meta_key)] ); + if( isset( $_POST[$this->uibuilder->get_name_id($meta_key)] ) ) { + $meta_value = trim( $_POST[$this->uibuilder->get_name_id($meta_key)] ); + if( !empty( $meta_value ) ) { + switch( $field_array['type'] ) { + case 'textbox': + case 'hidden': + case 'tel': + $meta_value = sanitize_text_field( $meta_value ); + break; + case 'date': + if( !$this->datavalidator->validate_date( $meta_value ) ) { + $meta_value = ''; + $errors[] = $field_array['label'] . ': Please enter a valid date in YYYY-MM-DD format.'; + } + break; + case 'time': + if( !$this->datavalidator->validate_time( $meta_value ) ) { + $meta_value = ''; + $errors[] = $field_array['label'] . ': Please enter a valid time in HH:MM format.'; + } + break; + case 'url': + $meta_value = $this->datavalidator->sanitize_url( $meta_value, array( 'http', 'https' ) ); + break; + case 'email': + if( !$this->datavalidator->validate_email( $meta_value ) ) { + $meta_value = ''; + $errors[] = $field_array['label'] . ': Please enter a valid email address.'; + } + break; + case 'textarea': + $meta_value = wp_filter_kses( $meta_value ); + break; + } + } + } update_post_meta( $post_id, $this->uibuilder->get_name_id($meta_key), $meta_value ); } + + if( !empty( $errors ) ) { + $errors = "
\n" . implode( "
\n", $errors ); + $this->display_error('location_meta', $errors); + } } private function display_error($code, $message) { diff --git a/includes/class-obj-data-validator.php b/includes/class-obj-data-validator.php new file mode 100644 index 0000000..1d7c7e4 --- /dev/null +++ b/includes/class-obj-data-validator.php @@ -0,0 +1,136 @@ +filter_exists('url') ) { + $url = filter_var($url, FILTER_SANITIZE_URL); + } else { + $url = preg_replace( '|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\\x80-\\xff]|i', '', $url ); + $url = $this->deep_replace( array('%0d', '%0a', '%0D', '%0A'), $url ); + } + if( empty($url) ) + return false; + + //Fix common mistakes + $url = str_replace(';//', '://', $url); + + //Append http:// if protocol is missing and http is allowed + if( false === strpos($url, ':') ) { + if( in_array('http', $protocols) ) + return 'http://' . $url; + return false; + } + + //Verify protocol is valid + foreach( $protocols as $protocol ) { + if( 0 == strcasecmp( $protocol, substr($url, 0, strlen($protocol)) ) ) + return $url; + } + + return false; + } + + public function validate_email ($email_string) { + if( $this->filter_exists('validate_email') ) + return filter_var($email_string, FILTER_VALIDATE_EMAIL); + + //Emails must be at least 3 characters and @ must appear somewhere after the first character + if( 3 > strlen($email_string) + || false === strpos( $email_string, '@', 1 ) ) + return false; + + list( $local, $domain ) = explode( '@', $email_string, 2 ); + + //Check for invalid characters in local + if( !preg_match( '/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]+$/', $local ) ) + return false; + + //Check for valid domain period syntax + if( preg_match( '/\.{2,}/', $domain ) + || $domain != trim( $domain, " \t\n\r\0\x0B." ) ) + return false; + + $namespaces = explode( '.', $domain ); + + //Domain must have at least 2 namespaces + if( 2 > count( $namespaces ) ) + return false; + + foreach( $namespaces as $namespace ) { + //Check for invalid characters in namespace + if( $namespace !== trim( $namespace, " \t\n\r\0\x0B-" ) + || !preg_match('/^[a-z0-9-]+$/i', $namespace) ) + return false; + } + + return true; + } + + private function deep_replace ($search, $subject) { + $subject = (string) $subject; + + $count = 1; + while ( $count ) { + $subject = str_replace( $search, '', $subject, $count ); + } + + return $subject; + } +} + \ No newline at end of file diff --git a/includes/class-obj-settings.php b/includes/class-obj-settings.php index 31b4bb1..cb6f9a7 100644 --- a/includes/class-obj-settings.php +++ b/includes/class-obj-settings.php @@ -192,12 +192,22 @@ private function settings_fields() { 'type' => 'checkbox' ), array( - 'id' => 'api_key', - 'label' => __( 'Google API Key', 'obj-google-maps' ), - 'description' => __( 'Enter the Google API key to use this plugin.', 'obj-google-maps' ), + 'id' => 'maps_api_key', + 'label' => __( 'Google Maps Javascript API Key', 'obj-google-maps' ), + 'description' => __( 'Enter the Google Maps Javascript API Key to use this plugin.', 'obj-google-maps' ), 'type' => 'text', 'default' => '', - 'placeholder' => __( 'Google API Key', 'obj-google-maps' ), + 'placeholder' => __( 'Google Maps Javascript API Key', 'obj-google-maps' ), + 'class' => 'regular-text', + 'callback' => 'wp_strip_all_tags' + ), + array( + 'id' => 'geocode_api_key', + 'label' => __( 'Google Maps Geocode API Key', 'obj-google-maps' ), + 'description' => __( 'Enter the Google Maps Geocode API Key to use this plugin.', 'obj-google-maps' ), + 'type' => 'text', + 'default' => '', + 'placeholder' => __( 'Google Maps Geocode API Key', 'obj-google-maps' ), 'class' => 'regular-text', 'callback' => 'wp_strip_all_tags' ) From dcf91ce78f7241c741a30ae4bf6bc3c573bc3e56 Mon Sep 17 00:00:00 2001 From: Matthew Sigley Date: Fri, 9 Feb 2018 15:45:35 -0500 Subject: [PATCH 11/14] Implemented main plugin script as an object instead of inline proceedures with globals. --- obj-google-maps.php | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/obj-google-maps.php b/obj-google-maps.php index f3e921c..bfcedf4 100644 --- a/obj-google-maps.php +++ b/obj-google-maps.php @@ -3,7 +3,7 @@ Plugin Name: Objectiv Google Maps Plugin URI: http://objectiv.co Description: Create searchable Google maps -Version: 1.2 +Version: 2.0 Author: Objectiv, Matthew Sigley Author URI: http://objectiv.co License: GPL2 @@ -24,18 +24,23 @@ along with Objectiv Google Maps. If not, see https://www.gnu.org/licenses/gpl-2.0.html. */ -if ( ! defined( 'ABSPATH' ) ) { - exit; -} +// Prevent direct access +defined( 'WPINC' ) || header( 'HTTP/1.1 403' ) & exit; + +class Obj_Gmaps { -$version = 1.1; + function __construct () { + require_once 'includes/class-obj-admin.php'; + require_once 'includes/class-obj-settings.php'; + require_once 'includes/class-obj-public.php'; + require_once 'includes/obj-functions.php'; + + $this->version = 2.0; + $this->obj_admin = new Obj_Gmaps_Admin( __FILE__ ); + $this->obj_settings = new Obj_Gmaps_Settings( __FILE__, $version ); + $this->obj_public = new Obj_Gmaps_Public( __FILE__, $version ); + } +} -require_once( 'includes/class-obj-admin.php' ); -require_once( 'includes/class-obj-settings.php' ); -require_once( 'includes/class-obj-public.php' ); -require_once( 'includes/obj-functions.php' ); +$Obj_Gmaps = new Obj_Gmaps(); -global $obj_admin, $obj_settings, $obj_public; -$obj_admin = new Obj_Gmaps_Admin( __FILE__ ); -$obj_settings = new Obj_Gmaps_Settings( __FILE__, $version ); -$obj_public = new Obj_Gmaps_Public( __FILE__, $version ); From 7647078e314ea782c728d7d29987ba9a5051779b Mon Sep 17 00:00:00 2001 From: Matthew Sigley Date: Fri, 9 Feb 2018 15:47:50 -0500 Subject: [PATCH 12/14] Implemented Overlapping Marker Spiderfier. Close open infoWindows on marker and map click. Fixed code formating. Converted space indentation to tabs. Reorganized assets. Removed unused files. Updated direct file access prevention. Changed name of CDATA varable from 'data' to 'objGoogleMapsData' to prevent conflicts with other scripts and plugins. Removed unused location icon setting. Use local icon resources instead of undocumented google images. Added active location icon functionality. Added title to map markers. --- .gitignore | 1 - assets/images/dot_pinlet.png | Bin 0 -> 528 bytes assets/images/dot_pinlet_spider.png | Bin 0 -> 521 bytes assets/images/pegman.png | Bin 0 -> 420 bytes assets/images/spotlight_pin.png | Bin 0 -> 467 bytes assets/js/admin/{build => }/main.js | 0 assets/js/admin/src/main.js | 14 --- assets/js/{build => public}/main.js | 121 ++++++++++++++++------ assets/js/public/oms.min.js | 29 ++++++ assets/js/src/main.js | 144 -------------------------- includes/class-obj-admin.php | 85 +++++++-------- includes/class-obj-data-validator.php | 3 +- includes/class-obj-public.php | 89 ++++++++-------- includes/class-obj-settings.php | 15 +-- includes/class-obj-uibuilder.php | 19 +++- includes/obj-functions.php | 4 +- package.json | 28 ----- 17 files changed, 224 insertions(+), 328 deletions(-) delete mode 100644 .gitignore create mode 100644 assets/images/dot_pinlet.png create mode 100644 assets/images/dot_pinlet_spider.png create mode 100644 assets/images/pegman.png create mode 100644 assets/images/spotlight_pin.png rename assets/js/admin/{build => }/main.js (100%) delete mode 100644 assets/js/admin/src/main.js rename assets/js/{build => public}/main.js (73%) create mode 100644 assets/js/public/oms.min.js delete mode 100644 assets/js/src/main.js delete mode 100644 package.json diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 3c3629e..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/assets/images/dot_pinlet.png b/assets/images/dot_pinlet.png new file mode 100644 index 0000000000000000000000000000000000000000..6b214b86a99747563acad19e81ee2ae9166b39a9 GIT binary patch literal 528 zcmeAS@N?(olHy`uVBq!ia0vp^;y|px!3-qNbqluxDb50q$YKTtZeb8+WSBKa0w~xO z;1lBNUf}cp|9>EF;@W+G{`>*5k6gYB$@2}r~_MEx;{rB(sMO)r} z{5o&P5s2)Bb$cL;L#HnQ1tGeQ961sm9=>72hKH};uRd_*_506TPF#Q}yM6n1{k#o8 zb~Tt>dEm_R_n(1g{{H>@QmNuTpni>#AirRSaE|XD%Inzp*b-k?PFQ~8^R<1`^xiL9 z*MCDoC`7|x!<93S&*ap6z9Rl!V>wXI4^J1z5RLP#lWs~iDG0R22Hd=JI%lh`SMC4n zrx}YI_gRG>t}n6RiMW=pTTxgU!?kZlpuYdi+a1SJGvD<}9QCO=l4SDWis<4yTg>-V zbXhBht#wj+mbB2PNNAVegjZEz-Qjny>4(l+FMEDEm%Zbuh-b3#rkmEOu1~$YreSq` z^vUS03~@C-_chFj|K%J|W%5ZzQ)au-r|?e)YSun94gbtJS<>%N-IWvb3+pAe*qYW< z?+|x#KfF9;k3o{VZC{Sj-Us~)6EmikS}b;{*NC3i(URZGF0#NUMZ>_rZid;0RvK*9d~-)lB~14{k=3z7dn zVgL8sm0zB{{;>b%@8_R?S8e_dlvsTD_2I*Z!^6WjZ{B?H{N3x%|2=;Hd-mb)$%{Wj zw0TbhF*xpivU!7t9c@c*-Mr1sflm&ga$3Ka0NJUwL|3 z&+X5}%corzn(eJ&(6RdV>-U?l`#g^V>G|mC;uxZFzID<|u_gt9*4PLao$p(&elvLe zH(o!{hxy#b-`PL(c#6}G6+RP9Q>lNV!fpCWbb9tzvG=d4H1}{Wwh@e1wUpYpUu5#M ze+d;*)7X}12`|*v3{~A{@bvVm4_tib*S(!5u&&^IH&=l+t6S)oIeS9xXx>}-!?mie zeY-9PU&^z(%FUndpPE^9XmjZQ$WqhWQWdiu>&;>g*BEb+T5GybPWr8zy+zUSod!0N zf@zlz%-AjQ;ZW*+=6_Ob(^g)RTRvlE_omf5AACI*rO*{LQ7P-;gB{|(lM|#&^8Ef> tIU>gY$ho%T?QfF_i>~~OIQ4Tm|5*pK*}}7`dx4&3@O1TaS?83{1OU!34E_KB literal 0 HcmV?d00001 diff --git a/assets/images/pegman.png b/assets/images/pegman.png new file mode 100644 index 0000000000000000000000000000000000000000..6bba73594f3c533b797097fb0631c7596ac10a73 GIT binary patch literal 420 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eM!3-q(^mLSg6lZ})WHAE+w=f7ZGR&GI0TgTr z@Ck8sFYqzO02XFYAqN`_Ik3Q=eL6EM^?&Wr`L#pm@fzLULWA8C^&D-CUT@ZI$}%X3 zHGI^dyRudP$aFoR^0PfUt8?{UP0?B0pueG0|51x>lBeOvr8>!;h94H{T$rzSxOzh? z&^Jq0<9OGhQ?tT-TJ5p^|7& z&2r`h^K26ym+RMo1==SbEp(~memVcZqT7LT@&~Ry+OUwvZ_A7P8^W6FcfJ>R a!0nLB8?o@rxvfAqGkCiCxvX>Rs2Yz|-3Oha;!>M&tYcE4FLg6?kpi$@%njrnc?@#myO_MFCZY7f$c^ zkjQ#{WBa1nbBY)B`@K4NV3MM`*v5p}%?__zGs-2W7F%hjhi32myH~U%AV0e)%5xS2<2!?@nCEnO}cexcW)*{`}Z| zde { - var autocomplete; - - autocomplete = new google.maps.places.Autocomplete( - (document.getElementById('autocomplete')), - {types: ['address']}); - -}); diff --git a/assets/js/build/main.js b/assets/js/public/main.js similarity index 73% rename from assets/js/build/main.js rename to assets/js/public/main.js index b7f1262..af50a65 100644 --- a/assets/js/build/main.js +++ b/assets/js/public/main.js @@ -1,17 +1,36 @@ -(function( $ ) { +jQuery(document).ready(function($) { (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;od)return this;g=this.s.splice(d,1)[0];b=0;for(f=g.length;bb||this.c[a].splice(b,1);return this};c.clearListeners=function(a){this.c[a]=[];return this};c.trigger=function(){var a,d,b,f,e,g;d=arguments[0];a=2<=arguments.length?A.call(arguments,1):[];d=null!=(b=this.c[d])?b:[];g=[];f=0;for(e=d.length;fa;b=0<=a?++e:--e)b= +this.circleStartAngle+b*f,c.push(new h.Point(d.x+g*Math.cos(b),d.y+g*Math.sin(b)));return c};c.M=function(a,d){var b,f,e,c,k;c=this.spiralLengthStart;b=0;k=[];for(f=e=0;0<=a?ea;f=0<=a?++e:--e)b+=this.spiralFootSeparation/c+5E-4*f,f=new h.Point(d.x+c*Math.cos(b),d.y+c*Math.sin(b)),c+=x*this.spiralLengthFactor/b,k.push(f);return k};c.V=function(a,d){var b,f,e,c,k,q,n,l,h;(q=null!=a._omsData)&&this.keepSpiderfied||this.unspiderfy();if(q||this.map.getStreetView().getVisible()||"GoogleEarthAPI"=== +this.map.getMapTypeId())return this.trigger("click",a,d);q=[];n=[];b=this.nearbyDistance;l=b*b;k=this.f(a.position);h=this.a;b=0;for(f=h.length;b=this.circleSpiralSwitchover?this.M(r,b).reverse():this.L(r,b);b=function(){var b,d,f;f=[];b=0;for(d=g.length;b

' + location.post_title + '

' + - '

' + '' + streetNumber + ' ' + streetName + '
' + cityName + ', ' + stateName + ' ' + countryName + ' ' + zip + '

' + - '

' - }); - - marker.addListener('click', function() { - infoWindow.open(map, marker); - }); - - } - }); - } - }); - }); diff --git a/includes/class-obj-admin.php b/includes/class-obj-admin.php index 517a75c..00d9322 100644 --- a/includes/class-obj-admin.php +++ b/includes/class-obj-admin.php @@ -1,9 +1,6 @@ file = $file; - $this->dir = dirname( $file ); - $this->uibuilder = new Obj_Gmaps_UIBuilder( 'obj_location' ); - $this->datavalidator = new Obj_Gmaps_DataValidator(); - $this->maps_api_key = get_option( 'obj_maps_api_key' ); - $this->geocode_api_key = get_option( 'obj_geocode_api_key' ); + $this->file = $file; + $this->dir = dirname( $file ); + $this->uibuilder = new Obj_Gmaps_UIBuilder( 'obj_location' ); + $this->datavalidator = new Obj_Gmaps_DataValidator(); + $this->maps_api_key = get_option( 'obj_maps_api_key' ); + $this->geocode_api_key = get_option( 'obj_geocode_api_key' ); - // Activation and Deactivation Hooks - register_activation_hook( $file, array( $this, 'activate_plugin' ) ); - register_deactivation_hook( $file, array( $this, 'deactivate_plugin' ) ); + // Activation and Deactivation Hooks + register_activation_hook( $file, array( $this, 'activate_plugin' ) ); + register_deactivation_hook( $file, array( $this, 'deactivate_plugin' ) ); - if ( is_admin() ) { - add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_js' ) ); - add_action( 'admin_init', array( $this, 'metaboxes_setup' ) ); - } + if ( is_admin() ) { + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_js' ) ); + add_action( 'admin_init', array( $this, 'metaboxes_setup' ) ); } + } - /** - * Activation callback - */ - public function activate_plugin() { - flush_rewrite_rules(); - } + /** + * Activation callback + */ + public function activate_plugin() { + flush_rewrite_rules(); + } - /** - * Deactivation Callback - */ - public function deactivate_plugin() { - flush_rewrite_rules(); - } + /** + * Deactivation Callback + */ + public function deactivate_plugin() { + flush_rewrite_rules(); + } /** - * Enqueue JS - * - * @since 1.0 - */ - public function enqueue_js( $hook ) { + * Enqueue JS + * + * @since 1.0 + */ + public function enqueue_js( $hook ) { $screen = get_current_screen(); $selected_post_type = get_option( 'obj_post_type' ); - $data_array = array( - 'api_key' => $this->api_key - ); - if ( $hook == 'settings_page_obj_google_map_settings' || $screen->post_type == $selected_post_type ) { - wp_enqueue_script( 'obj-google-maps-admin', plugins_url( '/assets/js/admin/build/main.js', $this->file ), array(), $this->version, true ); - wp_localize_script( 'obj-google-maps-admin', 'data', $data_array ); + wp_localize_script( 'obj-google-maps-admin', 'objGoogleMapData', array( 'api_key' => $this->api_key ) ); + wp_enqueue_script( 'obj-google-maps-admin', plugins_url( '/assets/js/admin/main.js', $this->file ), false, $this->version, true ); } - - } + } /** * Register address metabox diff --git a/includes/class-obj-data-validator.php b/includes/class-obj-data-validator.php index 1d7c7e4..9843fef 100644 --- a/includes/class-obj-data-validator.php +++ b/includes/class-obj-data-validator.php @@ -1,5 +1,6 @@ file = $file; - $this->version = $version; - - add_action( 'init', array( $this, 'add_map_shortcode' ) ); - add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_js' ) ); + public function __construct( $file, $version ) { + $this->file = $file; + $this->version = $version; - } + add_action( 'init', array( $this, 'add_map_shortcode' ) ); + add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_js' ) ); + } /** - * Enqueue JS - * - * @since 1.0 - */ - public function enqueue_js() { - - if ( obj_has_shortcode( 'objectiv_google_maps' ) ) { - wp_enqueue_script( 'obj-google-maps', plugins_url( '/assets/js/build/main.js', $this->file ), array('jquery'), $this->version, true ); + * Enqueue JS + * + * @since 1.0 + */ + public function enqueue_js() { + if ( obj_has_shortcode( 'objectiv_google_maps' ) ) { + wp_enqueue_script( 'obj-google-maps-oms', plugins_url( '/assets/js/public/oms.min.js', $this->file ), false, $this->version ); + wp_enqueue_script( 'obj-google-maps', plugins_url( '/assets/js/public/main.js', $this->file ), array('jquery', 'obj-google-maps-oms'), $this->version, true ); wp_enqueue_style( 'obj-google-maps-style', plugins_url( '/assets/css/public/public.css', $this->file ), array(), $this->version ); - } - - } - - /** - * Add map shortcode - * - * @since 1.0 - */ - public function add_map_shortcode() { - - add_shortcode( 'objectiv_google_maps', array( $this, 'map_shortcode_markup' ) ); + } + } - } + /** + * Add map shortcode + * + * @since 1.0 + */ + public function add_map_shortcode() { + add_shortcode( 'objectiv_google_maps', array( $this, 'map_shortcode_markup' ) ); + } - /** - * Create Map Shortcode Markup - * - * @since 1.0 - */ - public function map_shortcode_markup() { + /** + * Create Map Shortcode Markup + * + * @since 1.0 + */ + public function map_shortcode_markup() { $selected_post_type = get_option( 'obj_post_type' ); $height = get_option( 'obj_map_height' ); $search_by = get_option( 'obj_map_search_by' ); @@ -123,6 +115,7 @@ public function map_shortcode_markup() { $location = new StdClass; $location->lat = $lat; $location->lng = $lng; + $location->title = htmlspecialchars( trim( $post->post_title ) ); $location->content = $location_pin_content; $locations[] = $location; @@ -136,7 +129,10 @@ public function map_shortcode_markup() { 'mapCenterLng' => filter_var( wp_cache_get( 'obj_map_center_lng' ), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION ), 'mapZoom' => get_option( 'obj_map_zoom' ), 'mapSearch' => get_option( 'obj_map_search_by' ), - 'mapLocationIcon' => get_option( 'obj_map_location_icon' ), + 'userIcon' => plugins_url( '/assets/images/pegman.png', $this->file ), + 'locationIcon' => plugins_url( '/assets/images/dot_pinlet.png', $this->file ), + 'spiderLocationIcon' => plugins_url( '/assets/images/dot_pinlet_spider.png', $this->file ), + 'activeLocationIcon' => plugins_url( '/assets/images/spotlight_pin.png', $this->file ), 'locations' => $locations ); @@ -146,7 +142,7 @@ public function map_shortcode_markup() { } if ( obj_has_shortcode( 'objectiv_google_maps' ) ) { - wp_localize_script( 'obj-google-maps', 'data', $data_array ); + wp_localize_script( 'obj-google-maps', 'objGoogleMapData', $data_array ); } if ( $search_by == 'geocode' ) { @@ -162,16 +158,15 @@ public function map_shortcode_markup() { } - ob_start(); + ob_start(); echo '
'; echo ''; - echo '
'; + echo '
'; echo '
'; - return ob_get_clean(); - - } + return ob_get_clean(); + } private function update_center_lat_long_cache( &$data_array ) { $string = str_replace (" ", "+", urlencode( $data_array['mapCenter'] ) ); diff --git a/includes/class-obj-settings.php b/includes/class-obj-settings.php index cb6f9a7..8eaaa7a 100644 --- a/includes/class-obj-settings.php +++ b/includes/class-obj-settings.php @@ -1,9 +1,6 @@ id == 'settings_page_obj_google_map_settings' ) { - wp_enqueue_style( 'obj-google-maps-admin-css', plugins_url( '/assets/css/admin/admin.css', $this->file ), array(), $this->version ); + wp_enqueue_style( 'obj-google-maps-admin-css', plugins_url( '/assets/css/admin/admin.css', $this->file ), false, $this->version ); } } @@ -185,12 +182,6 @@ private function settings_fields() { ), 'default' => 'address' ), - array( - 'id' => 'map_location_icon', - 'label' => __( 'Location Icon', 'obj-google-maps' ), - 'description' => __( 'Display an icon on the location that was searched for.' ), - 'type' => 'checkbox' - ), array( 'id' => 'maps_api_key', 'label' => __( 'Google Maps Javascript API Key', 'obj-google-maps' ), diff --git a/includes/class-obj-uibuilder.php b/includes/class-obj-uibuilder.php index 098c316..0ba563b 100644 --- a/includes/class-obj-uibuilder.php +++ b/includes/class-obj-uibuilder.php @@ -1,5 +1,6 @@ get_name_id($field_name); + + $output = ''; + + echo $output; + } + /* * Function name: hidden * Purpose: Builds and echos html for a hidden field. diff --git a/includes/obj-functions.php b/includes/obj-functions.php index 169ebd7..0f6b35b 100644 --- a/includes/obj-functions.php +++ b/includes/obj-functions.php @@ -1,4 +1,6 @@ ID ); $found = false; @@ -20,5 +21,4 @@ function obj_has_shortcode( $shortcode = '' ) { $found = true; return $found; - } diff --git a/package.json b/package.json deleted file mode 100644 index e17f652..0000000 --- a/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "objectiv-google-maps", - "description": "Create searchable Google Maps for specific post types.", - "version": "1.0.0", - "dependencies": {}, - "devDependencies": { - "babel-preset-es2015": "^6.18.0", - "babelify": "^7.3.0", - "gulp": "^3.9.1", - "gulp-cheerio": "^0.6.2", - "gulp-clean-css": "^2.0.2", - "gulp-concat": "^2.6.0", - "gulp-minify": "0.0.14", - "gulp-rename": "^1.2.2", - "gulp-sass": "^2.3.2", - "gulp-sourcemaps": "^1.6.0", - "gulp-svgmin": "^1.2.2", - "gulp-svgstore": "^6.0.0", - "gulp-uglify": "^2.0.0" - }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build": "browserify -t [ babelify --presets [ es2015 ] ] assets/js/src/main.js -o assets/js/build/main.js", - "watch": "watchify -t [ babelify --presets [ es2015 ] ] assets/js/src/main.js -o assets/js/build/main.js", - "build-admin": "browserify -t [ babelify --presets [ es2015 ] ] assets/js/admin/src/main.js -o assets/js/admin/build/main.js", - "watch-admin": "watchify -t [ babelify --presets [ es2015 ] ] assets/js/admin/src/main.js -o assets/js/admin/build/main.js" - } -} From c584768ff3d7143ae6a5b9a29f79661af7a6d0bf Mon Sep 17 00:00:00 2001 From: Matthew Sigley Date: Mon, 12 Feb 2018 11:52:36 -0500 Subject: [PATCH 13/14] Fixed maps api key option name --- includes/class-obj-public.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-obj-public.php b/includes/class-obj-public.php index 79c8f3c..6802cfa 100644 --- a/includes/class-obj-public.php +++ b/includes/class-obj-public.php @@ -123,7 +123,7 @@ public function map_shortcode_markup() { } $data_array = array( - 'apiKey' => get_option( 'obj_api_key' ), + 'apiKey' => get_option( 'obj_maps_api_key' ), 'mapType' => get_option( 'obj_map_type' ), 'mapCenterLat' => filter_var( wp_cache_get( 'obj_map_center_lat' ), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION ), 'mapCenterLng' => filter_var( wp_cache_get( 'obj_map_center_lng' ), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION ), @@ -161,7 +161,7 @@ public function map_shortcode_markup() { ob_start(); echo '
'; - echo ''; + echo ''; echo '
'; echo '
'; From 6e559adb70d046564e88d45924d9d1c9fa441d7c Mon Sep 17 00:00:00 2001 From: Matthew Sigley Date: Mon, 12 Feb 2018 14:50:30 -0500 Subject: [PATCH 14/14] Fixed missing mapCenter element in data_array. --- includes/class-obj-public.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/includes/class-obj-public.php b/includes/class-obj-public.php index 6802cfa..a1713f9 100644 --- a/includes/class-obj-public.php +++ b/includes/class-obj-public.php @@ -125,6 +125,7 @@ public function map_shortcode_markup() { $data_array = array( 'apiKey' => get_option( 'obj_maps_api_key' ), 'mapType' => get_option( 'obj_map_type' ), + 'mapCenter' => get_option( 'obj_map_center' ), 'mapCenterLat' => filter_var( wp_cache_get( 'obj_map_center_lat' ), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION ), 'mapCenterLng' => filter_var( wp_cache_get( 'obj_map_center_lng' ), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION ), 'mapZoom' => get_option( 'obj_map_zoom' ), @@ -180,10 +181,8 @@ private function update_center_lat_long_cache( &$data_array ) { $geometry = $output->results[0]->geometry; $longitude = $geometry->location->lng; $latitude = $geometry->location->lat; - wp_cache_set( 'obj_map_center_address_components', $address_components ); wp_cache_set( 'obj_map_center_lat', $latitude ); wp_cache_set( 'obj_map_center_lng', $longitude ); - $data_array['mapCenterAddressCompnents'] = $address_components; $data_array['mapCenterLat'] = $latitude; $data_array['mapCenterLng'] = $longitude; }
+ + + +
+ + + +
+ + + +
From 02e636346e7ae19376ff01cac86e30aa68028860 Mon Sep 17 00:00:00 2001 From: Matthew Sigley Date: Thu, 1 Feb 2018 14:33:08 -0500 Subject: [PATCH 10/14] Added data validation and sanitation to custom location meta. Added geocode api key setting to allow for better google maps api key security settings. --- includes/class-obj-admin.php | 154 +++++++++++++++++--------- includes/class-obj-data-validator.php | 136 +++++++++++++++++++++++ includes/class-obj-settings.php | 18 ++- 3 files changed, 251 insertions(+), 57 deletions(-) create mode 100644 includes/class-obj-data-validator.php diff --git a/includes/class-obj-admin.php b/includes/class-obj-admin.php index 4357cf9..517a75c 100644 --- a/includes/class-obj-admin.php +++ b/includes/class-obj-admin.php @@ -15,25 +15,27 @@ */ class Obj_Gmaps_Admin { - public function __construct( $file ) { + public function __construct( $file ) { + require_once 'class-obj-uibuilder.php'; + require_once 'class-obj-data-validator.php'; - require_once 'class-obj-uibuilder.php'; + $this->file = $file; + $this->dir = dirname( $file ); + $this->uibuilder = new Obj_Gmaps_UIBuilder( 'obj_location' ); + $this->datavalidator = new Obj_Gmaps_DataValidator(); + $this->maps_api_key = get_option( 'obj_maps_api_key' ); + $this->geocode_api_key = get_option( 'obj_geocode_api_key' ); - $this->file = $file; - $this->dir = dirname( $file ); - $this->uibuilder = new Obj_Gmaps_UIBuilder( 'obj_location' ); + // Activation and Deactivation Hooks + register_activation_hook( $file, array( $this, 'activate_plugin' ) ); + register_deactivation_hook( $file, array( $this, 'deactivate_plugin' ) ); - // Activation and Deactivation Hooks - register_activation_hook( $file, array( $this, 'activate_plugin' ) ); - register_deactivation_hook( $file, array( $this, 'deactivate_plugin' ) ); - - if ( is_admin() ) { - add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_js' ) ); - add_action( 'admin_init', array( $this, 'metaboxes_setup' ) ); + if ( is_admin() ) { + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_js' ) ); + add_action( 'admin_init', array( $this, 'metaboxes_setup' ) ); + } } - } - /** * Activation callback */ @@ -58,7 +60,7 @@ public function enqueue_js( $hook ) { $selected_post_type = get_option( 'obj_post_type' ); $data_array = array( - 'api_key' => get_option( 'obj_api_key' ) + 'api_key' => $this->api_key ); if ( $hook == 'settings_page_obj_google_map_settings' || $screen->post_type == $selected_post_type ) { @@ -80,7 +82,6 @@ public function metaboxes_setup () { add_action( 'save_post_'.$selected_post_type, array( $this, 'save_post_validate' ), 9, 2 ); add_action( 'save_post_'.$selected_post_type, array( $this, 'verify_wp_nonces' ), 10, 2 ); add_action( 'save_post_'.$selected_post_type, array( $this, 'save_metabox' ), 11, 1 ); - add_action( 'save_post_'.$selected_post_type, array( $this, 'save_location_lat_long' ), 12, 1 ); } } @@ -106,8 +107,8 @@ public function create_metabox( $post ) { * @since 1.0 */ public function metabox_content( $object ) { - wp_nonce_field( 'obj_google_save', 'obj_google_save_nonce' ); - + wp_nonce_field( 'obj_google_save', 'obj_google_save_nonce_'.$object->ID ); + $lat = get_post_meta( $object->ID, 'obj_location_lat', true ); if( empty($lat) ) $lat = 'Not set. Marker will not appear on map. Save the post to try geocoding the address again.'; @@ -141,18 +142,10 @@ public function metabox_content( $object ) {
- - - -