From b7c4f9a6211a945d31bb17c9b9d272370ac3be63 Mon Sep 17 00:00:00 2001 From: Jory Hogeveen Date: Sun, 3 Jul 2016 23:10:01 +0200 Subject: [PATCH 01/96] First commit for address/map field patched from https://github.com/pods-framework/pods-address-maps --- classes/fields/addressmap.php | 431 ++++++++++++++++++++++++++++++++++ components/AddressMaps.php | 152 ++++++++++++ ui/css/pods-address-maps.css | 5 + ui/fields/address.php | 48 ++++ ui/fields/lat-long.php | 14 ++ ui/fields/map.php | 93 ++++++++ ui/front/address-map.php | 1 + ui/front/address.php | 1 + ui/front/lat-long.php | 1 + ui/front/map.php | 1 + ui/js/pods-address-maps.js | 16 ++ 11 files changed, 763 insertions(+) create mode 100644 classes/fields/addressmap.php create mode 100644 components/AddressMaps.php create mode 100644 ui/css/pods-address-maps.css create mode 100644 ui/fields/address.php create mode 100644 ui/fields/lat-long.php create mode 100644 ui/fields/map.php create mode 100644 ui/front/address-map.php create mode 100644 ui/front/address.php create mode 100644 ui/front/lat-long.php create mode 100644 ui/front/map.php create mode 100644 ui/js/pods-address-maps.js diff --git a/classes/fields/addressmap.php b/classes/fields/addressmap.php new file mode 100644 index 0000000000..da08b4ebd2 --- /dev/null +++ b/classes/fields/addressmap.php @@ -0,0 +1,431 @@ + array( + 'label' => __( 'Address Type', 'pods' ), + 'default' => 'address', + 'type' => 'pick', + 'data' => array( + 'address' => __( 'Address Field Group', 'pods' ), + 'text' => __( 'Freeform Text', 'pods' ), + 'lat-long' => __( 'Latitude / Longitude', 'pods' ) + ), + 'dependency' => true + ), + self::$type . '_address_options' => array( + 'label' => __( 'Address Options', 'pods' ), + 'depends-on' => array( self::$type . '_type' => 'address' ), + 'group' => array( + self::$type . '_address_line_1' => array( + 'label' => __( 'Enable Address Line 1', 'pods' ), + 'default' => 1, + 'type' => 'boolean' + ), + self::$type . '_address_line_2' => array( + 'label' => __( 'Enable Address Line 2', 'pods' ), + 'default' => 0, + 'type' => 'boolean' + ), + self::$type . '_address_city' => array( + 'label' => __( 'Enable City', 'pods' ), + 'default' => 1, + 'type' => 'boolean' + ), + self::$type . '_address_postal_code' => array( + 'label' => __( 'Enable ZIP / Postal Code', 'pods' ), + 'default' => 0, + 'type' => 'boolean' + ), + self::$type . '_address_state' => array( + 'label' => __( 'Enable State / Province', 'pods' ), + 'default' => 1, + 'type' => 'boolean', + 'dependency' => true + ), + self::$type . '_address_country' => array( + 'label' => __( 'Enable Country', 'pods' ), + 'default' => 0, + 'type' => 'boolean', + 'dependency' => true + ) + ) + ), + self::$type . '_address_state_input' => array( + 'label' => __( 'State Input Type', 'pods' ), + 'depends-on' => array( self::$type . '_address_state' => true, self::$type . '_type' => 'address' ), + 'default' => 'text', + 'type' => 'pick', + 'data' => array( + 'text' => __( 'Freeform Text', 'pods' ), + 'pick' => __( 'Drop-down Select Box', 'pods' ) + ), + ), + self::$type . '_address_country_input' => array( + 'label' => __( 'Country Input Type', 'pods' ), + 'depends-on' => array( self::$type . '_address_country' => true, self::$type . '_type' => 'address' ), + 'default' => 'text', + 'type' => 'pick', + 'data' => array( + 'text' => __( 'Freeform Text', 'pods' ), + 'pick' => __( 'Drop-down Select Box', 'pods' ) + ), + ), + self::$type . '_autocorrect' => array( + 'label' => __( 'Autocorrect Address during save', 'pods' ), + 'depends-on' => array( self::$type . '_display_type' => array( 'single', 'multi' ) ), + 'default' => 0, + 'type' => 'boolean' + ), + self::$type . '_show_map_input' => array( + 'label' => __( 'Show Map below Input', 'pods' ), + 'default' => 0, + 'type' => 'boolean' + ), + self::$type . '_display_type' => array( + 'label' => __( 'Display Type', 'pods' ), + 'default' => 'address', + 'type' => 'pick', + 'data' => array( + 'map' => __( 'Map', 'pods' ), + 'address-map' => __( 'Address and Map', 'pods' ), + 'address' => __( 'Address', 'pods' ), + 'lat-long' => __( 'Latitude, Longitude', 'pods' ) + ), + 'dependency' => true + ), + self::$type . '_style' => array( + 'label' => __( 'Map Output Type', 'pods' ), + 'depends-on' => array( self::$type . '_display_type' => array( 'map', 'address-map' ) ), + 'default' => pods_v( self::$type . '_style', self::$component_options, 'static', true ), + 'type' => 'pick', + 'data' => array( + 'static' => __( 'Static (Image)', 'pods' ), + 'js' => __( 'Javascript (Interactive)', 'pods' ) + ) + ), + self::$type . '_type_of_map' => array( + 'label' => __( 'Map Type', 'pods' ), + 'depends-on' => array( self::$type . '_display_type' => array( 'map', 'address-map' ) ), + 'default' => pods_v( self::$type . '_type', self::$component_options, 'roadmap', true ), + 'type' => 'pick', + 'data' => array( + 'roadmap' => __( 'Roadmap', 'pods' ), + 'satellite' => __( 'Satellite', 'pods' ), + 'terrain' => __( 'Terrain', 'pods' ), + 'hybrid' => __( 'Hybrid', 'pods' ) + ) + ), + self::$type . '_zoom' => array( + 'label' => __( 'Map Zoom Level', 'pods' ), + 'depends-on' => array( self::$type . '_display_type' => array( 'map', 'address-map' ) ), + 'help' => array( + __( 'Google Maps has documentation on the different zoom levels you can use.', 'pods' ), + 'https://developers.google.com/maps/documentation/javascript/tutorial#zoom-levels' + //'https://developers.google.com/maps/documentation/staticmaps/#Zoomlevels' + ), + 'default' => pods_v( self::$type . '_zoom', self::$component_options, 12, true ), + 'type' => 'number', + 'options' => array( + 'number_decimals' => 0, + 'number_max_length' => 2 + ) + ), + self::$type . '_marker' => array( + 'label' => __( 'Map Custom Marker', 'pods' ), + 'depends-on' => array( self::$type . '_display_type' => array( 'map', 'address-map' ) ), + 'default' => pods_v( self::$type . '_marker', self::$component_options ), + 'type' => 'file', + 'options' => array( + 'file_uploader' => 'plupload', + 'file_edit_title' => 0, + 'file_restrict_filesize' => '1MB', + 'file_type' => 'images', + 'file_add_button' => 'Upload Marker Icon' + ) + ) + ); + + return $options; + + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + public function schema( $options = null ) { + + $schema = 'LONGTEXT'; + + return $schema; + + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + public function display( $value = null, $name = null, $options = null, $pod = null, $id = null ) { + + $display_type = pods_v( self::$type . '_display_type', $options ); + $view = PODS_DIR . 'ui/front/address.php'; + if ( 'lat-long' == $display_type ) { + $view = PODS_DIR . 'ui/front/lat-long.php'; + } elseif ( 'address-map' == $display_type ) { + $view = PODS_DIR . 'ui/front/address-map.php'; + + } elseif ( 'map' == $display_type ) { + $view = PODS_DIR . 'ui/front/map.php'; + } + $value = pods_view( $view, compact( array_keys( get_defined_vars() ) ), false, 'cache', true ); + + return $value; + + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + public function input( $name, $value = null, $options = null, $pod = null, $id = null ) { + + $form_field_type = PodsForm::$field_type; + + $field = PODS_DIR . 'ui/fields/text.php'; + if ( 'address' == pods_v( self::$type . '_type', $options ) ) { + $field = PODS_DIR . 'ui/fields/address.php'; + } elseif ( 'lat-long' == pods_v( self::$type . '_type', $options ) ) { + $field = PODS_DIR . 'ui/fields/lat-long.php'; + } + + pods_view( $field, compact( array_keys( get_defined_vars() ) ) ); + + if ( 1 == pods_v( self::$type . '_show_map_input', $options ) ) { + pods_view( PODS_DIR . 'ui/fields/map.php', compact( array_keys( get_defined_vars() ) ) ); + } + + } + + /** + * {@inheritDoc} + * @since 1.0 + */ + public function validate( $value, $name = null, $options = null, $fields = null, $pod = null, $id = null, $params = null ) { + + // TODO: Validate based on address type ( lat / lon, address fields) + $errors = array(); + + if ( 1 == pods_v( 'required', $options ) ) { + $errors[] = __( 'This field is required.', 'pods' ); + } + + if ( ! empty( $errors ) ) { + return $errors; + } + + return true; + + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + public function pre_save( $value, $id = null, $name = null, $options = null, $fields = null, $pod = null, $params = null ) { + + return $value; + + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + public function ui( $id, $value, $name = null, $options = null, $fields = null, $pod = null ) { + + return $value; + + } + + /** + * Output a map + * + * @param array $args Map options + * + * @return string Map output + * @since 1.0 + */ + public static function map( $args ) { + + $defaults = array( + 'address' => '', + 'lat' => '', + 'long' => '', + 'width' => '', + 'height' => '', + 'type' => '', + 'zoom' => '', + 'style' => '', + 'marker' => '', + 'expires' => ( 60 * 60 * 24 ), + 'cache_type' => 'cache' + ); + + $args = array_merge( (array) $args, $defaults ); + + $lat_long = array( + 'lat' => $args['lat'], + 'long' => $args['long'] + ); + + if ( empty( $lat_long['lat'] ) && empty( $lat_long['long'] ) ) { + if ( ! empty( $args['address'] ) ) { + $address_data = self::geocode_address( $args['address'] ); + + if ( ! empty( $address_data ) ) { + $lat_long['lat'] = $address_data['lat']; + $lat_long['long'] = $address_data['long']; + } else { + return ''; + } + } else { + return ''; + } + } + + return pods_view( self::$file_path . 'ui/front/map.php', compact( array_keys( get_defined_vars() ) ), $args['expires'], $args['cache_type'], true ); + + } + + /** + * Geocode a specific address into Latitude and Longitude values + * + * @param string|array $address Address + * + * @return array Latitude, Longitude, and Formatted Address values + * + * @public + * @since 1.0 + */ + public function geocode_address( $address ) { + + return array(); + + } + + /** + * Get an address from a lat / long + * + * @param string|array $lat_long Lat / long numbers + * + * @return string Address information + * + * @public + * @static + * @since 1.0 + */ + public function geocode_lat_long( $lat_long ) { + + return ''; + + } + + /** + * @param $result + * + * @return array|bool + * @since 1.0 + */ + public function parse_address( $result ) { + + return false; + + } + +} \ No newline at end of file diff --git a/components/AddressMaps.php b/components/AddressMaps.php new file mode 100644 index 0000000000..e5e23b8e32 --- /dev/null +++ b/components/AddressMaps.php @@ -0,0 +1,152 @@ + realpath( self::$component_file ) ); + + return $components; + + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + public function options( $settings ) { + + $options = array( + 'provider' => array( + 'label' => __( 'Maps Provider', 'pods' ), + 'help' => __( 'help', 'pods' ), + 'default' => 'google', + 'type' => 'pick', + 'data' => apply_filters( 'pods_component_maps_providers', array( + 'google' => __( 'Google Maps', 'pods' ), + //'bing' => __( 'Bing Maps', 'pods' ) + ) ), + 'dependency' => true + ), + 'api_key' => array( + 'label' => __( 'Maps API Key', 'pods' ), + 'help' => __( 'help', 'pods' ), + 'default' => '', + 'type' => 'text' + ), + 'google_client_id' => array( + 'label' => __( 'Google Maps Client ID', 'pods' ), + 'help' => __( 'For use with Google Maps API for Business and Geocoding; A Client ID does not come with the Free edition.', 'pods' ), + 'includes-on' => array( 'provider' => 'google' ), + 'default' => '', + 'type' => 'text' + ), + 'address_map_style' => array( + 'label' => __( 'Default Map Output Type', 'pods' ), + 'default' => 'static', + 'type' => 'pick', + 'data' => array( + 'static' => __( 'Static (Image)', 'pods' ), + 'js' => __( 'Javascript (Interactive)', 'pods' ) + ) + ), + 'address_map_type_of_map' => array( + 'label' => __( 'Default Map Type', 'pods' ), + 'default' => 'roadmap', + 'type' => 'pick', + 'data' => array( + 'roadmap' => __( 'Roadmap', 'pods' ), + 'satellite' => __( 'Satellite', 'pods' ), + 'terrain' => __( 'Terrain', 'pods' ), + 'hybrid' => __( 'Hybrid', 'pods' ) + ) + ), + 'address_map_zoom' => array( + 'label' => __( 'Default Map Zoom Level', 'pods' ), + 'help' => array( + __( 'Google Maps has documentation on the different zoom levels you can use.', 'pods' ), + 'https://developers.google.com/maps/documentation/staticmaps/#Zoomlevels' + ), + 'default' => 12, + 'type' => 'number', + 'options' => array( + 'number_decimals' => 0, + 'number_max_length' => 2 + ) + ), + 'address_map_marker' => array( + 'label' => __( 'Default Map Custom Marker', 'pods' ), + 'type' => 'file', + 'options' => array( + 'file_uploader' => 'plupload', + 'file_edit_title' => 0, + 'file_restrict_filesize' => '1MB', + 'file_type' => 'images', + 'file_add_button' => 'Upload Marker Icon' + ) + ) + ); + + return $options; + + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + public function handler( $options ) { + + self::$options = $options; + + } +} \ No newline at end of file diff --git a/ui/css/pods-address-maps.css b/ui/css/pods-address-maps.css new file mode 100644 index 0000000000..bb38c9d26d --- /dev/null +++ b/ui/css/pods-address-maps.css @@ -0,0 +1,5 @@ +.pods-address-maps-map-canvas { + margin-top: 10px; + height: 300px; + width: 95%; +} \ No newline at end of file diff --git a/ui/fields/address.php b/ui/fields/address.php new file mode 100644 index 0000000000..c24c1083cf --- /dev/null +++ b/ui/fields/address.php @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 'us_state' ) ) ?> + + + + + + + + + + 'country' ) ) ?> + + + + + + + + + + + + +
+ + + + + + 1 ) ) ?> + + + 1 ) ) ?> + \ No newline at end of file diff --git a/ui/front/address-map.php b/ui/front/address-map.php new file mode 100644 index 0000000000..2ce1cc9a9c --- /dev/null +++ b/ui/front/address-map.php @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/front/address.php b/ui/front/address.php new file mode 100644 index 0000000000..d6a7817068 --- /dev/null +++ b/ui/front/address.php @@ -0,0 +1 @@ + diff --git a/ui/front/lat-long.php b/ui/front/lat-long.php new file mode 100644 index 0000000000..2ce1cc9a9c --- /dev/null +++ b/ui/front/lat-long.php @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/front/map.php b/ui/front/map.php new file mode 100644 index 0000000000..2ce1cc9a9c --- /dev/null +++ b/ui/front/map.php @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/js/pods-address-maps.js b/ui/js/pods-address-maps.js new file mode 100644 index 0000000000..95feedbf8f --- /dev/null +++ b/ui/js/pods-address-maps.js @@ -0,0 +1,16 @@ +(function ( $ ) { + + var methods = {}; + + $.fn.PodsAddressMap = function ( method ) { + + if ( methods [method] ) { + return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ) ) + } + else { + $.error( 'Method ' + method ) + } + + } + +})( jQuery ); \ No newline at end of file From 5dab8ee2aea1171cb2eb9a3ceafa17e632d010d7 Mon Sep 17 00:00:00 2001 From: Jory Hogeveen Date: Sat, 6 Aug 2016 04:42:17 +0200 Subject: [PATCH 02/96] First version of Address field - Add address in backend - Display address in frontend with HTML markup (config with format) - Custom HTML markup format possible with magic tags - Optionally add schema.org microdata --- classes/PodsForm.php | 1 + classes/fields/address.php | 395 +++++++++++++++++++++++++++++++++++++ ui/fields/address.php | 12 +- ui/front/address.php | 17 +- 4 files changed, 418 insertions(+), 7 deletions(-) create mode 100644 classes/fields/address.php diff --git a/classes/PodsForm.php b/classes/PodsForm.php index 2f0b7dec05..0feae22951 100644 --- a/classes/PodsForm.php +++ b/classes/PodsForm.php @@ -1328,6 +1328,7 @@ public static function field_types() { 'boolean', 'color', 'slug', + 'address', ); $field_types = array_merge( $field_types, array_keys( self::$field_types ) ); diff --git a/classes/fields/address.php b/classes/fields/address.php new file mode 100644 index 0000000000..d53eac4753 --- /dev/null +++ b/classes/fields/address.php @@ -0,0 +1,395 @@ + array( + 'label' => __( 'Address Type', 'pods' ), + 'default' => 'address', + 'type' => 'pick', + 'data' => array( + 'address' => __( 'Address Field Group', 'pods' ), + 'text' => __( 'Freeform Text', 'pods' ) + ), + 'dependency' => true + ), + self::$type . '_address_options' => array( + 'label' => __( 'Address Options', 'pods' ), + 'depends-on' => array( self::$type . '_type' => 'address' ), + 'group' => array( + self::$type . '_address_line_1' => array( + 'label' => __( 'Enable Address Line 1', 'pods' ), + 'default' => 1, + 'type' => 'boolean' + ), + self::$type . '_address_line_2' => array( + 'label' => __( 'Enable Address Line 2', 'pods' ), + 'default' => 0, + 'type' => 'boolean' + ), + self::$type . '_address_postal_code' => array( + 'label' => __( 'Enable ZIP / Postal Code', 'pods' ), + 'default' => 0, + 'type' => 'boolean' + ), + self::$type . '_address_city' => array( + 'label' => __( 'Enable City', 'pods' ), + 'default' => 1, + 'type' => 'boolean' + ), + self::$type . '_address_region' => array( + 'label' => __( 'Enable Region (State / Province)', 'pods' ), + 'default' => 1, + 'type' => 'boolean', + 'dependency' => true + ), + self::$type . '_address_country' => array( + 'label' => __( 'Enable Country', 'pods' ), + 'default' => 0, + 'type' => 'boolean', + 'dependency' => true + ) + ) + ), + self::$type . '_address_region_input' => array( + 'label' => __( 'Region Input Type', 'pods' ), + 'depends-on' => array( self::$type . '_address_region' => true, self::$type . '_type' => 'address' ), + 'default' => 'text', + 'type' => 'pick', + 'data' => array( + 'text' => __( 'Freeform Text', 'pods' ), + 'pick' => __( 'Drop-down Select Box (US States)', 'pods' ) + ), + ), + self::$type . '_address_country_input' => array( + 'label' => __( 'Country Input Type', 'pods' ), + 'depends-on' => array( self::$type . '_address_country' => true, self::$type . '_type' => 'address' ), + 'default' => 'text', + 'type' => 'pick', + 'data' => array( + 'text' => __( 'Freeform Text', 'pods' ), + 'pick' => __( 'Drop-down Select Box', 'pods' ) + ), + ), + self::$type . '_display_type' => array( + 'label' => __( 'Display Type', 'pods' ), + 'default' => 'default', + 'type' => 'pick', + 'data' => array( + 'default' => __( 'Default', 'pods' ), + 'custom' => __( 'Custom', 'pods' ) + ), + 'dependency' => true + ), + self::$type . '_display_type_custom' => array( + 'label' => __( 'Custom display', 'pods' ), + 'help' => __( 'You can use the following tags for address fields', 'pods' ) . ': {{line_1}}, {{line_2}}, {{postal_code}}, {{city}}, {{region}}, {{country}}', + 'default' => self::default_display_format(), + 'type' => 'paragraph', + 'depends-on' => array( self::$type . '_display_type' => 'custom' ) + ), + self::$type . '_microdata' => array( + 'label' => __( 'Format with microdata?', 'pods' ) . ' (schema.org)', + 'default' => 0, + 'type' => 'boolean' + ) + ); + + return $options; + + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + public function schema( $options = null ) { + + $schema = 'LONGTEXT'; + + return $schema; + + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + public function display( $value = null, $name = null, $options = null, $pod = null, $id = null ) { + + $display_type = pods_v( self::$type . '_display_type', $options ); + + $view = PODS_DIR . 'ui/front/address.php'; + $view = apply_filters( 'pods_ui_field_address_display_view', $view, $display_type, $value, $name, $options, $pod, $id ); + + $value = pods_view( $view, compact( array_keys( get_defined_vars() ) ), false, 'cache', true ); + $value = apply_filters( 'pods_ui_field_address_display_value', $value, $view, $display_type, $value, $name, $options, $pod, $id ); + + return $value; + + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + public function input( $name, $value = null, $options = null, $pod = null, $id = null ) { + + $form_field_type = PodsForm::$field_type; + + $type = pods_v( self::$type . '_type', $options ); + + $view = PODS_DIR . 'ui/fields/text.php'; + if ( 'address' == $type ) { + $view = PODS_DIR . 'ui/fields/address.php'; + } + $view = apply_filters( 'pods_ui_field_address_input_view', $view, $type, $name, $value, $options, $pod, $id ); + + if ( ! empty( $view ) ) { + pods_view( $view, compact( array_keys( get_defined_vars() ) ) ); + } + + do_action( 'pods_ui_field_address_input_view_extra', $view, $type, $name, $value, $options, $pod, $id ); + + } + + /** + * {@inheritDoc} + * @since 1.0 + */ + public function validate( $value, $name = null, $options = null, $fields = null, $pod = null, $id = null, $params = null ) { + + // @todo: Validate based on address type ( lat / lon, address fields) + $errors = array(); + + if ( 1 == pods_v( 'required', $options ) ) { + $errors[] = __( 'This field is required.', 'pods' ); + } + + if ( ! empty( $errors ) ) { + return $errors; + } + + return true; + + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + public function pre_save( $value, $id = null, $name = null, $options = null, $fields = null, $pod = null, $params = null ) { + + return $value; + + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + public function ui( $id, $value, $name = null, $options = null, $fields = null, $pod = null ) { + + return $value; + + } + + /** + * Convert the field format into HTML for display + * + * @since 2.7 + * + * @param string $format The format to be used (default or custom) + * @param array $value The field value + * @param array $options The field options + * + * @return string + */ + public static function format_to_html( $format, $value, $options ) { + $output = ''; + + if ( ! empty ( $value['address'] ) ) { + $address = $value['address']; + } + + if ( ! empty( $address ) ) { + + // Format in microdata? + $microdata = ( ! empty( $options[ self::$type . '_microdata' ] ) ) ? true : false; + + // Convert actual line breaks into an array + $lines = explode( '\r\n', preg_replace("/\n/m", '\r\n', $format) ); + + foreach ( $lines as $key => $line ) { + + // preg_match to all tags + preg_match_all( '#{{(.*?)}}#', $line, $tags ); + if ( ! empty( $tags[1] ) ) { + foreach( $tags[1] as $tag ) { + // Default value is empty. Only known tags are allowed, remove all unknown tags + $value = ''; + if ( ! empty( $address[ $tag ] ) ) { + $value = $address[ $tag ]; + if ( $microdata ) { + $value = self::wrap_microdata( $value, $tag ); + } + } + $lines[ $key ] = str_replace( '{{' . $tag . '}}', $value, $lines[ $key ] ); + } + } + if ( empty( trim( $lines[ $key ] ) ) ) { + unset( $lines[ $key ] ); + } + } + // Lines to HTML line breaks + $output = implode( '
', $lines ); + + if ( $microdata ) { + $output = '
' . $output . '
'; + } + } + return $output; + } + + /** + * Wrap values in the correct schema.org microdata based on the address tag + * + * @since 2.7 + * + * @param string $value + * @param string $tag The address tag + * + * @return string + */ + public static function wrap_microdata( $value, $tag ) { + + switch ( $tag ) { + case 'line_1': + case 'line_2': + return '' . $value . ''; + break; + case 'postal_code': + return '' . $value . ''; + break; + case 'city': + return '' . $value . ''; + break; + case 'region': + return '' . $value . ''; + break; + case 'country': + return '' . $value . ''; + break; + } + + return $value; + } + + /** + * The default display format + * + * @since 2.7 + * + * @return string + */ + public static function default_display_format() { + return '{{line_1}} +{{line_2}} +{{postal_code}} {{city}} +{{region}} +{{country}}'; + } + +} \ No newline at end of file diff --git a/ui/fields/address.php b/ui/fields/address.php index c24c1083cf..cf5a79da7f 100644 --- a/ui/fields/address.php +++ b/ui/fields/address.php @@ -22,15 +22,15 @@ - + - - - - 'us_state' ) ) ?> + + + + 'us_state' ) ) ?> - + diff --git a/ui/front/address.php b/ui/front/address.php index d6a7817068..a2a1f083c9 100644 --- a/ui/front/address.php +++ b/ui/front/address.php @@ -1 +1,16 @@ - + \ No newline at end of file From 4af54f31d0a4aa122255b4318455f0d396bf184d Mon Sep 17 00:00:00 2001 From: Jory Hogeveen Date: Sat, 6 Aug 2016 04:50:28 +0200 Subject: [PATCH 03/96] Improve microdata handler --- classes/fields/address.php | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/classes/fields/address.php b/classes/fields/address.php index d53eac4753..de9bc542e0 100644 --- a/classes/fields/address.php +++ b/classes/fields/address.php @@ -323,7 +323,7 @@ public static function format_to_html( $format, $value, $options ) { if ( ! empty( $address[ $tag ] ) ) { $value = $address[ $tag ]; if ( $microdata ) { - $value = self::wrap_microdata( $value, $tag ); + $value = self::wrap_microdata( $value, $tag, 'span' ); } } $lines[ $key ] = str_replace( '{{' . $tag . '}}', $value, $lines[ $key ] ); @@ -337,7 +337,7 @@ public static function format_to_html( $format, $value, $options ) { $output = implode( '
', $lines ); if ( $microdata ) { - $output = '
' . $output . '
'; + $output = self::wrap_microdata( $output, 'address', 'div' ); } } return $output; @@ -353,24 +353,27 @@ public static function format_to_html( $format, $value, $options ) { * * @return string */ - public static function wrap_microdata( $value, $tag ) { + public static function wrap_microdata( $value, $tag, $element ) { switch ( $tag ) { + case 'address': + return '<' . $element . ' itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">' . $value . ''; + break; case 'line_1': case 'line_2': - return '' . $value . ''; + return '<' . $element . ' itemprop="streetAddress">' . $value . ''; break; case 'postal_code': - return '' . $value . ''; + return '<' . $element . ' itemprop="postalCode">' . $value . ''; break; case 'city': - return '' . $value . ''; + return '<' . $element . ' itemprop="addressLocality">' . $value . ''; break; case 'region': - return '' . $value . ''; + return '<' . $element . ' itemprop="addressRegion">' . $value . ''; break; case 'country': - return '' . $value . ''; + return '<' . $element . ' itemprop="addressCountry">' . $value . ''; break; } From 20dc696f04234541443e871d078669e72352e0ab Mon Sep 17 00:00:00 2001 From: Jory Hogeveen Date: Sat, 6 Aug 2016 15:57:03 +0200 Subject: [PATCH 04/96] First commit for Maps component Currently only handles PodsAdmin fields Requires PR: #3711 --- classes/fields/address.php | 2 +- components/Maps/Maps.php | 307 +++++++++++++++++++++++++++++++++++++ 2 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 components/Maps/Maps.php diff --git a/classes/fields/address.php b/classes/fields/address.php index de9bc542e0..55d5ec0027 100644 --- a/classes/fields/address.php +++ b/classes/fields/address.php @@ -173,7 +173,7 @@ public function options() { 'help' => __( 'You can use the following tags for address fields', 'pods' ) . ': {{line_1}}, {{line_2}}, {{postal_code}}, {{city}}, {{region}}, {{country}}', 'default' => self::default_display_format(), 'type' => 'paragraph', - 'depends-on' => array( self::$type . '_display_type' => 'custom' ) + 'depends-on' => array( self::$type . '_display_type' => array( 'custom' ) ) ), self::$type . '_microdata' => array( 'label' => __( 'Format with microdata?', 'pods' ) . ' (schema.org)', diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php new file mode 100644 index 0000000000..9cceff4bd6 --- /dev/null +++ b/components/Maps/Maps.php @@ -0,0 +1,307 @@ + realpath( self::$component_file ) ); + + return $components; + + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + public function options( $settings ) { + + $options = array( + 'provider' => array( + 'label' => __( 'Maps Provider', 'pods' ), + 'help' => __( 'help', 'pods' ), + 'default' => 'google', + 'type' => 'pick', + 'data' => apply_filters( 'pods_component_maps_providers', array( + 'google' => __( 'Google Maps', 'pods' ), + //'bing' => __( 'Bing Maps', 'pods' ), + //'openstreetmap' => __( 'OpenStreetMap', 'pods' ), + ) ), + 'dependency' => true + ), + 'api_key' => array( + 'label' => __( 'Maps API Key', 'pods' ), + 'help' => __( 'help', 'pods' ), + 'default' => '', + 'type' => 'text' + ), + 'google_client_id' => array( + 'label' => __( 'Google Maps Client ID', 'pods' ), + 'help' => __( 'For use with Google Maps API for Business and Geocoding; A Client ID does not come with the Free edition.', 'pods' ), + 'includes-on' => array( 'provider' => 'google' ), + 'default' => '', + 'type' => 'text' + ), + 'address_map_style' => array( + 'label' => __( 'Default Map Output Type', 'pods' ), + 'default' => 'static', + 'type' => 'pick', + 'data' => array( + 'static' => __( 'Static (Image)', 'pods' ), + 'js' => __( 'Javascript (Interactive)', 'pods' ) + ) + ), + 'address_map_type_of_map' => array( + 'label' => __( 'Default Map Type', 'pods' ), + 'default' => 'roadmap', + 'type' => 'pick', + 'data' => array( + 'roadmap' => __( 'Roadmap', 'pods' ), + 'satellite' => __( 'Satellite', 'pods' ), + 'terrain' => __( 'Terrain', 'pods' ), + 'hybrid' => __( 'Hybrid', 'pods' ) + ) + ), + 'address_map_zoom' => array( + 'label' => __( 'Default Map Zoom Level', 'pods' ), + 'help' => array( + __( 'Google Maps has documentation on the different zoom levels you can use.', 'pods' ), + 'https://developers.google.com/maps/documentation/staticmaps/#Zoomlevels' + ), + 'default' => 12, + 'type' => 'number', + 'options' => array( + 'number_decimals' => 0, + 'number_max_length' => 2 + ) + ), + 'address_map_marker' => array( + 'label' => __( 'Default Map Custom Marker', 'pods' ), + 'type' => 'file', + 'options' => array( + 'file_uploader' => 'plupload', + 'file_edit_title' => 0, + 'file_restrict_filesize' => '1MB', + 'file_type' => 'images', + 'file_add_button' => 'Upload Marker Icon' + ) + ) + ); + + return $options; + + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + public function handler( $options ) { + + self::$options = $options; + + } + + public function maps_options( $options, $type ) { + + // Add lat/lng input type + $options[ $type . '_type' ]['data']['lat-lng'] = __( 'Latitude / Longitude', 'pods' ); + + // Add Map display types + $options[ $type . '_display_type' ]['data']['map'] = __( 'Map', 'pods' ); + $options[ $type . '_display_type' ]['data']['default-map'] = __( 'Default and map', 'pods' ); + $options[ $type . '_display_type' ]['data']['custom-map'] = __( 'Custom and map', 'pods' ); + + // Add extra options + $options[ $type . '_style' ] = array( + 'label' => __( 'Map Output Type', 'pods' ), + 'depends-on' => array( $type . '_display_type' => array( 'map', 'default-map', 'custom-map' ) ), + 'default' => pods_v( $type . '_style', self::$options, 'static', true ), + 'type' => 'pick', + 'data' => array( + 'static' => __( 'Static (Image)', 'pods' ), + 'js' => __( 'Javascript (Interactive)', 'pods' ) + ) + ); + $options[ $type . '_type_of_map' ] = array( + 'label' => __( 'Map Type', 'pods' ), + 'depends-on' => array( $type . '_display_type' => array( 'map', 'default-map', 'custom-map' ) ), + 'default' => pods_v( $type . '_type', self::$options, 'roadmap', true ), + 'type' => 'pick', + 'data' => array( + 'roadmap' => __( 'Roadmap', 'pods' ), + 'satellite' => __( 'Satellite', 'pods' ), + 'terrain' => __( 'Terrain', 'pods' ), + 'hybrid' => __( 'Hybrid', 'pods' ) + ) + ); + $options[ $type . '_zoom' ] = array( + 'label' => __( 'Map Zoom Level', 'pods' ), + 'depends-on' => array( $type . '_display_type' => array( 'map', 'default-map', 'custom-map' ) ), + 'help' => array( + __( 'Google Maps has documentation on the different zoom levels you can use.', 'pods' ), + 'https://developers.google.com/maps/documentation/javascript/tutorial#zoom-levels' + //'https://developers.google.com/maps/documentation/staticmaps/#Zoomlevels' + ), + 'default' => pods_v( $type . '_zoom', self::$options, 12, true ), + 'type' => 'number', + 'options' => array( + 'number_decimals' => 0, + 'number_max_length' => 2 + ) + ); + $options[ $type . '_marker' ] = array( + 'label' => __( 'Map Custom Marker', 'pods' ), + 'depends-on' => array( $type . '_display_type' => array( 'map', 'default-map', 'custom-map' ) ), + 'default' => pods_v( $type . '_marker', self::$options ), + 'type' => 'file', + 'options' => array( + 'file_uploader' => 'plupload', + 'file_edit_title' => 0, + 'file_restrict_filesize' => '1MB', + 'file_type' => 'images', + 'file_add_button' => 'Upload Marker Icon' + ) + ); + + // Add option dependencies + if ( empty( $options[ $type . '_display_type_custom' ]['depends-on'][ $type . '_display_type' ] ) ) { + $options[ $type . '_display_type_custom' ]['depends-on'][ $type . '_display_type' ] = array( 'custom-map' ); + } else { + $options[ $type . '_display_type_custom' ]['depends-on'][ $type . '_display_type' ] = $this->append_dependency( + $options[ $type . '_display_type_custom' ]['depends-on'][ $type . '_display_type' ], + 'custom-map' + ); + } + + if ( empty( $options[ $type . '_microdata' ]['excludes-on'][ $type . '_display_type' ] ) ) { + $options[ $type . '_microdata' ]['excludes-on'][ $type . '_display_type' ] = array( 'map' ); + } else { + $options[ $type . '_microdata' ]['excludes-on'][ $type . '_display_type' ] = $this->append_dependency( + $options[ $type . '_microdata' ]['excludes-on'][ $type . '_display_type' ], + 'map' + ); + } + + return $options; + } + + public function append_dependency( $value, $new ) { + if ( ! is_array( $value ) ) { + $value = array( + (string) $value, + $new + ); + } else { + $value[] = $new; + } + return $value; + } + + /** + * Geocode a specific address into Latitude and Longitude values + * + * @param string|array $address Address + * + * @return array Latitude, Longitude, and Formatted Address values + * + * @public + * @since 2.7 + */ + public static function geocode_address( $address ) { + + if ( is_array( $address ) ) { + $address = implode( ', ', $address ); + } + + $address_data = array(); + + $post = wp_remote_post( 'https://maps.googleapis.com/maps/api/geocode/json?address=' . $address . '&key=' . self::$api_key ); + + if ( ! empty( $post['body'] ) ) { + $data = json_decode( $post['body'] ); + + if ( ! empty( $post['results']['geometry']['location'] ) ) { + $lat_lng = $post['results']['geometry']['location']; + $address_data = array_merge( $address_data, $lat_lng ); + } + } + + return $address_data; + + } + + /** + * Get an address from a lat / long + * + * @param string|array $lat_lng Lat / long numbers + * + * @return string Address information + * + * @public + * @static + * @since 2.7 + */ + public function geocode_lat_long( $lat_lng ) { + + return ''; + + } + + /** + * @param $result + * + * @return array|bool + * @since 2.7 + */ + public function parse_address( $result ) { + + return false; + + } +} \ No newline at end of file From ba9b3c822b82df9f86d266f4bb91c68cf6212c18 Mon Sep 17 00:00:00 2001 From: Jory Hogeveen Date: Sat, 6 Aug 2016 16:55:03 +0200 Subject: [PATCH 05/96] Improve HTML and microdata formatting Also adds classes for use in front-end prepended with `pods-address` --- classes/fields/address.php | 58 ++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/classes/fields/address.php b/classes/fields/address.php index 55d5ec0027..683534df6c 100644 --- a/classes/fields/address.php +++ b/classes/fields/address.php @@ -321,10 +321,7 @@ public static function format_to_html( $format, $value, $options ) { // Default value is empty. Only known tags are allowed, remove all unknown tags $value = ''; if ( ! empty( $address[ $tag ] ) ) { - $value = $address[ $tag ]; - if ( $microdata ) { - $value = self::wrap_microdata( $value, $tag, 'span' ); - } + $value = self::wrap_html_format( $address[ $tag ], $tag, 'span', $microdata ); } $lines[ $key ] = str_replace( '{{' . $tag . '}}', $value, $lines[ $key ] ); } @@ -336,15 +333,13 @@ public static function format_to_html( $format, $value, $options ) { // Lines to HTML line breaks $output = implode( '
', $lines ); - if ( $microdata ) { - $output = self::wrap_microdata( $output, 'address', 'div' ); - } + $output = self::wrap_html_format( $output, 'address', 'div', $microdata ); } return $output; } /** - * Wrap values in the correct schema.org microdata based on the address tag + * Wrap values in the correct HTML format with optional schema.org microdata based on the address tag * * @since 2.7 * @@ -353,28 +348,61 @@ public static function format_to_html( $format, $value, $options ) { * * @return string */ - public static function wrap_microdata( $value, $tag, $element ) { + public static function wrap_html_format( $value, $tag, $element, $microdata = false ) { + + $atts['class'] = 'pods-address' . $tags; switch ( $tag ) { case 'address': - return '<' . $element . ' itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">' . $value . ''; + $atts['class'] = 'pods-address'; + if ( $microdata ) { + $atts['itemprop'] = 'address'; + $atts['itemscope'] = ''; + $atts['itemtype'] = 'http://schema.org/PostalAddress'; + }; break; + case 'line_1': case 'line_2': - return '<' . $element . ' itemprop="streetAddress">' . $value . ''; + if ( $microdata ) { + $atts['itemprop'] = 'streetAddress'; + }; break; case 'postal_code': - return '<' . $element . ' itemprop="postalCode">' . $value . ''; + if ( $microdata ) { + $atts['itemprop'] = 'postalCode'; + }; break; + case 'city': - return '<' . $element . ' itemprop="addressLocality">' . $value . ''; + if ( $microdata ) { + $atts['itemprop'] = 'addressLocality'; + }; break; + case 'region': - return '<' . $element . ' itemprop="addressRegion">' . $value . ''; + if ( $microdata ) { + $atts['itemprop'] = 'addressRegion'; + }; break; + case 'country': - return '<' . $element . ' itemprop="addressCountry">' . $value . ''; + if ( $microdata ) { + $atts['itemprop'] = 'addressCountry'; + }; break; + + default: + $atts = false; + break; + } + + if ( $atts ) { + $attributes = ''; + foreach ( $atts as $key => $val ) { + $attributes .= ' ' . $key . '="' . $val . '"'; + } + $value = '<' . $element . $attributes . '>' . $value . ''; } return $value; From f60e71623b3dc1d6f39d1ac5bd11bc6cd4a1535e Mon Sep 17 00:00:00 2001 From: Jory Hogeveen Date: Sat, 6 Aug 2016 16:55:32 +0200 Subject: [PATCH 06/96] Improve UX and options for Maps component --- classes/fields/address.php | 7 +++- components/Maps/Maps.php | 79 +++++++++++++++++++++++++++----------- 2 files changed, 62 insertions(+), 24 deletions(-) diff --git a/classes/fields/address.php b/classes/fields/address.php index 683534df6c..dd87282603 100644 --- a/classes/fields/address.php +++ b/classes/fields/address.php @@ -166,6 +166,7 @@ public function options() { 'default' => __( 'Default', 'pods' ), 'custom' => __( 'Custom', 'pods' ) ), + 'depends-on' => array( self::$type . '_type' => 'address' ), 'dependency' => true ), self::$type . '_display_type_custom' => array( @@ -173,12 +174,13 @@ public function options() { 'help' => __( 'You can use the following tags for address fields', 'pods' ) . ': {{line_1}}, {{line_2}}, {{postal_code}}, {{city}}, {{region}}, {{country}}', 'default' => self::default_display_format(), 'type' => 'paragraph', - 'depends-on' => array( self::$type . '_display_type' => array( 'custom' ) ) + 'depends-on' => array( self::$type . '_display_type' => 'custom', self::$type . '_type' => 'address' ) ), self::$type . '_microdata' => array( 'label' => __( 'Format with microdata?', 'pods' ) . ' (schema.org)', 'default' => 0, - 'type' => 'boolean' + 'type' => 'boolean', + 'depends-on' => array( self::$type . '_type' => 'address' ) ) ); @@ -368,6 +370,7 @@ public static function wrap_html_format( $value, $tag, $element, $microdata = fa $atts['itemprop'] = 'streetAddress'; }; break; + case 'postal_code': if ( $microdata ) { $atts['itemprop'] = 'postalCode'; diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index 9cceff4bd6..fd0daa9fa1 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -115,8 +115,12 @@ public function options( $settings ) { 'default' => 12, 'type' => 'number', 'options' => array( - 'number_decimals' => 0, - 'number_max_length' => 2 + 'number_decimals' => 0, // 2 + 'number_max_length' => 2, + 'number_min' => 1, + 'number_max' => 21, + 'number_format' => '9999.99', + //'number_format_type' => 'slider' ) ), 'address_map_marker' => array( @@ -153,14 +157,38 @@ public function maps_options( $options, $type ) { $options[ $type . '_type' ]['data']['lat-lng'] = __( 'Latitude / Longitude', 'pods' ); // Add Map display types - $options[ $type . '_display_type' ]['data']['map'] = __( 'Map', 'pods' ); - $options[ $type . '_display_type' ]['data']['default-map'] = __( 'Default and map', 'pods' ); - $options[ $type . '_display_type' ]['data']['custom-map'] = __( 'Custom and map', 'pods' ); + //$options[ $type . '_display_type' ]['data']['map'] = __( 'Map', 'pods' ); + //$options[ $type . '_display_type' ]['data']['default-map'] = __( 'Default and map', 'pods' ); + //$options[ $type . '_display_type' ]['data']['custom-map'] = __( 'Custom and map', 'pods' ); // Add extra options + + $options[ $type . '_map' ] = array( + 'label' => __( 'Display a map', 'pods' ), + 'default' => 0, + 'type' => 'boolean', + 'dependency' => true + ); + $options[ $type . '_map_display' ] = array( + 'label' => __( 'Map Display', 'pods' ), + 'depends-on' => array( $type . '_map' => true ), + 'default' => 'replace', + 'type' => 'pick', + 'data' => array( + 'replace' => __( 'Replace default display', 'pods' ), + 'before' => __( 'Before default display', 'pods' ), + 'after' => __( 'After default display', 'pods' ) + ) + ); + $options[ $type . '_autocorrect' ] = array( + 'label' => __( 'Autocorrect Address during save', 'pods' ), + 'depends-on' => array( $type . '_map' => true, $type . '_type' => array( 'address', 'text' ) ), + 'default' => 0, + 'type' => 'boolean' + ); $options[ $type . '_style' ] = array( 'label' => __( 'Map Output Type', 'pods' ), - 'depends-on' => array( $type . '_display_type' => array( 'map', 'default-map', 'custom-map' ) ), + 'depends-on' => array( $type . '_map' => true ), 'default' => pods_v( $type . '_style', self::$options, 'static', true ), 'type' => 'pick', 'data' => array( @@ -170,7 +198,7 @@ public function maps_options( $options, $type ) { ); $options[ $type . '_type_of_map' ] = array( 'label' => __( 'Map Type', 'pods' ), - 'depends-on' => array( $type . '_display_type' => array( 'map', 'default-map', 'custom-map' ) ), + 'depends-on' => array( $type . '_map' => true ), 'default' => pods_v( $type . '_type', self::$options, 'roadmap', true ), 'type' => 'pick', 'data' => array( @@ -182,7 +210,7 @@ public function maps_options( $options, $type ) { ); $options[ $type . '_zoom' ] = array( 'label' => __( 'Map Zoom Level', 'pods' ), - 'depends-on' => array( $type . '_display_type' => array( 'map', 'default-map', 'custom-map' ) ), + 'depends-on' => array( $type . '_map' => true ), 'help' => array( __( 'Google Maps has documentation on the different zoom levels you can use.', 'pods' ), 'https://developers.google.com/maps/documentation/javascript/tutorial#zoom-levels' @@ -191,13 +219,28 @@ public function maps_options( $options, $type ) { 'default' => pods_v( $type . '_zoom', self::$options, 12, true ), 'type' => 'number', 'options' => array( - 'number_decimals' => 0, - 'number_max_length' => 2 + 'number_decimals' => 0, // 2 + 'number_max_length' => 2, + 'number_min' => 1, + 'number_max' => 21, + 'number_format' => '9999.99', + //'number_format_type' => 'slider' + ) + ); + $options[ $type . '_info_window_content' ] = array( + 'label' => __( 'Map Info Window content', 'pods' ), + 'depends-on' => array( $type . '_map' => true ), + 'default' => 'default', + 'type' => 'pick', + 'data' => array( + 'default' => __( 'Default display', 'pods' ), + // Custom will add a WYSIWYG window at the edit screen + 'custom' => __( 'Custom (WYSIWYG)', 'pods' ) ) ); $options[ $type . '_marker' ] = array( 'label' => __( 'Map Custom Marker', 'pods' ), - 'depends-on' => array( $type . '_display_type' => array( 'map', 'default-map', 'custom-map' ) ), + 'depends-on' => array( $type . '_map' => true ), 'default' => pods_v( $type . '_marker', self::$options ), 'type' => 'file', 'options' => array( @@ -210,23 +253,15 @@ public function maps_options( $options, $type ) { ); // Add option dependencies - if ( empty( $options[ $type . '_display_type_custom' ]['depends-on'][ $type . '_display_type' ] ) ) { + /*if ( empty( $options[ $type . '_display_type_custom' ]['depends-on'][ $type . '_display_type' ] ) ) { $options[ $type . '_display_type_custom' ]['depends-on'][ $type . '_display_type' ] = array( 'custom-map' ); } else { $options[ $type . '_display_type_custom' ]['depends-on'][ $type . '_display_type' ] = $this->append_dependency( $options[ $type . '_display_type_custom' ]['depends-on'][ $type . '_display_type' ], 'custom-map' ); - } - - if ( empty( $options[ $type . '_microdata' ]['excludes-on'][ $type . '_display_type' ] ) ) { - $options[ $type . '_microdata' ]['excludes-on'][ $type . '_display_type' ] = array( 'map' ); - } else { - $options[ $type . '_microdata' ]['excludes-on'][ $type . '_display_type' ] = $this->append_dependency( - $options[ $type . '_microdata' ]['excludes-on'][ $type . '_display_type' ], - 'map' - ); - } + }*/ + $options[ $type . '_microdata' ]['excludes-on'][ $type . '_map' ] = true; return $options; } From 7b8f3cf04f3fe746c555ee0ff3686c0d2bef8319 Mon Sep 17 00:00:00 2001 From: Jory Hogeveen Date: Wed, 10 Aug 2016 07:58:56 +0200 Subject: [PATCH 07/96] First commit for Google Maps API class --- components/Maps/Maps-Google.php | 205 ++++++++++++++++++++++++++++++++ components/Maps/Maps.php | 117 ++++++++---------- 2 files changed, 253 insertions(+), 69 deletions(-) create mode 100644 components/Maps/Maps-Google.php diff --git a/components/Maps/Maps-Google.php b/components/Maps/Maps-Google.php new file mode 100644 index 0000000000..7a499ecdef --- /dev/null +++ b/components/Maps/Maps-Google.php @@ -0,0 +1,205 @@ +geocode_url = apply_filters( 'pods_maps_google_geocode_url', 'https://maps.googleapis.com/maps/api/geocode/' ); + + } + + /** + * Geocode an address with given data + * + * @param string|array $data Any type of address data + * @param string $api_key + * + * @return array Latitude, Longitude (format: array( 'lat' => value, 'lng' => value ) ) + * + * @public + * @static + * @since 2.7 + */ + public static function geocode_address( $data, $api_key = '' ) { + + $data = self::geocode( $data, $api_key ); + + $address = self::get_address( $data ); + $latlng = self::get_latlng( $data ); + + return array_merge( $address, $latlng ); + } + + /** + * Geocode an address into Latitude and Longitude values + * + * @param string|array $address Address + * @param string $api_key + * + * @return array Latitude, Longitude (format: array( 'lat' => value, 'lng' => value ) ) + * + * @public + * @static + * @since 2.7 + */ + public static function geocode_address_to_latlng( $address, $api_key = '' ) { + + if ( is_array( $address ) ) { + $address = implode( ', ', $address ); + } + + $data = self::geocode( $address, $api_key ); + + return self::get_latlng( $data ); + } + + /** + * Get address data from Latitude and Longitude values + * + * @param string|array $lat_lng Lat / long numbers + * @param string $api_key + * + * @return string Address information + * + * @public + * @static + * @since 2.7 + */ + public static function geocode_latlng_to_address( $lat_lng, $api_key = '' ) { + + if ( is_array( $lat_lng ) ) { + $lat_lng = implode( ',', array_map( 'floatval', $lat_lng ) ); + } + + $data = self::geocode( $lat_lng, $api_key, 'latlng' ); + + return self::get_address( $data ); + } + + /** + * Return address data from returned Google data + * + * @param array $data The data from Google + * + * @return array + * + * @public + * @static + * @since 2.7 + */ + public static function get_address( $data ) { + + if ( ! empty( $data['results'][0] ) ) { + $data = $data['results'][0]; + } + + if ( ! empty( $data['address_components'] ) ) { + + $address = array( + 'line_1' => array(), + 'line_2' => array(), + 'postal_code' => '', + 'city' => '', + 'region' => array(), + 'country' => '', + ); + + foreach ( $data['address_components'] as $component ) { + + switch ( $component['types'] ) { + case 'street_number': + $address['line_1'][1] = $component['long_name']; + break; + case 'route': + $address['line_1'][0] = $component['long_name']; + break; + case 'locality': + $address['city'] = $component['long_name']; + break; + case 'country': + $address['country'] = $component['long_name']; + break; + case 'postal_code': + $address['postal_code'] = $component['long_name']; + break; + case 'administrative_area_level_1': + case 'administrative_area_level_2': + case 'administrative_area_level_3': + $address['region'][] = $component['long_name']; + break; + } + } + + foreach ( $address as $key => $value ) { + if ( is_array( $value ) ) { + $address[ $key ] = implode( ' ', $value ); + } + } + + return $address; + } + return array(); + } + + /** + * Return lat/lng data from returned Google data + * + * @param array $data The data from Google + * + * @return array + * + * @public + * @static + * @since 2.7 + */ + public static function get_latlng( $data ) { + + if ( ! empty( $data['results'][0] ) ) { + $data = $data['results'][0]; + } + + if ( ! empty( $data[ 'geometry' ][ 'location' ] ) ) { + $latlng = $data[ 'geometry' ][ 'location' ]; + return array_map( 'floatval', $latlng ); + } + return array(); + } + + /** + * Call to Google Maps API + * + * @param string|array $data + * @param string $api_key Optional + * @param string $type ( address | latlng ) + * + * @return array + * + * @public + * @static + * @since 2.7 + */ + public static function geocode( $data, $api_key = '', $type = 'address' ) { + + if ( is_array( $data ) ) { + $data = implode( ',', $data ); + } + + $url = $this->geocode_url . 'json?' . $type . '=' . $data; + if ( ! empty( $api_key ) ) { + $url .= '&key=' . $api_key; + } + + $post = wp_remote_post( $url ); + + if ( ! empty( $post[ 'body' ] ) ) { + $data = json_decode( $post[ 'body' ], true ); + if ( ! empty( $data['results'][0] ) ) { + return $data['results'][0]; + } + } + return array(); + } + +} \ No newline at end of file diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index fd0daa9fa1..96ea6f3be3 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -21,6 +21,8 @@ class Pods_Component_Maps extends PodsComponent { static $options; + static $provider; + public function __construct() { // See https://github.com/pods-framework/pods/pull/3711 add_filter( 'pods_admin_setup_edit_address_additional_field_options', array( $this, 'maps_options' ), 10, 2 ); @@ -53,6 +55,37 @@ public static function component_register( $components ) { } + /** + * {@inheritDoc} + * + * @since 1.0 + */ + public function handler( $options ) { + + self::$options = $options; + + $this->load_provider(); + + } + + /** + * Load the selected provider + * + * @since 2.7 + */ + private function load_provider() { + + switch ( self::$options['provider'] ) { + case 'google': + if ( file_exists( plugin_dir_path( __FILE__ ) . '/Maps-Google.php' ) ) { + include_once( plugin_dir_path( __FILE__ ) . '/Maps-Google.php' ); + self::$provider = new Pods_Component_Maps_Google(); + } + break; + } + + } + /** * {@inheritDoc} * @@ -141,16 +174,15 @@ public function options( $settings ) { } /** - * {@inheritDoc} + * Add map field options * - * @since 1.0 + * @param array $options + * @param string $type + * + * @return array + * + * @since 2.7 */ - public function handler( $options ) { - - self::$options = $options; - - } - public function maps_options( $options, $type ) { // Add lat/lng input type @@ -266,6 +298,14 @@ public function maps_options( $options, $type ) { return $options; } + /** + * Append new dependency to existing data + * + * @param $value + * @param $new + * + * @return array + */ public function append_dependency( $value, $new ) { if ( ! is_array( $value ) ) { $value = array( @@ -278,65 +318,4 @@ public function append_dependency( $value, $new ) { return $value; } - /** - * Geocode a specific address into Latitude and Longitude values - * - * @param string|array $address Address - * - * @return array Latitude, Longitude, and Formatted Address values - * - * @public - * @since 2.7 - */ - public static function geocode_address( $address ) { - - if ( is_array( $address ) ) { - $address = implode( ', ', $address ); - } - - $address_data = array(); - - $post = wp_remote_post( 'https://maps.googleapis.com/maps/api/geocode/json?address=' . $address . '&key=' . self::$api_key ); - - if ( ! empty( $post['body'] ) ) { - $data = json_decode( $post['body'] ); - - if ( ! empty( $post['results']['geometry']['location'] ) ) { - $lat_lng = $post['results']['geometry']['location']; - $address_data = array_merge( $address_data, $lat_lng ); - } - } - - return $address_data; - - } - - /** - * Get an address from a lat / long - * - * @param string|array $lat_lng Lat / long numbers - * - * @return string Address information - * - * @public - * @static - * @since 2.7 - */ - public function geocode_lat_long( $lat_lng ) { - - return ''; - - } - - /** - * @param $result - * - * @return array|bool - * @since 2.7 - */ - public function parse_address( $result ) { - - return false; - - } } \ No newline at end of file From cc95f1ac0efe24ad5aeb41d230462d5bb8fe83e5 Mon Sep 17 00:00:00 2001 From: Jory Hogeveen Date: Wed, 10 Aug 2016 15:09:05 +0200 Subject: [PATCH 08/96] plugin_dir_path already has a trailing slash --- components/Maps/Maps.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index 96ea6f3be3..d124bb03f0 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -77,8 +77,8 @@ private function load_provider() { switch ( self::$options['provider'] ) { case 'google': - if ( file_exists( plugin_dir_path( __FILE__ ) . '/Maps-Google.php' ) ) { - include_once( plugin_dir_path( __FILE__ ) . '/Maps-Google.php' ); + if ( file_exists( plugin_dir_path( __FILE__ ) . 'Maps-Google.php' ) ) { + include_once( plugin_dir_path( __FILE__ ) . 'Maps-Google.php' ); self::$provider = new Pods_Component_Maps_Google(); } break; From 46c973bef467565f2f2442f50916ad28f5fabfd3 Mon Sep 17 00:00:00 2001 From: Jory Hogeveen Date: Wed, 10 Aug 2016 15:10:18 +0200 Subject: [PATCH 09/96] Prepend option names with "map_" --- components/Maps/Maps.php | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index d124bb03f0..7b6e2ad1c3 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -119,7 +119,7 @@ public function options( $settings ) { 'default' => '', 'type' => 'text' ), - 'address_map_style' => array( + 'map_style' => array( 'label' => __( 'Default Map Output Type', 'pods' ), 'default' => 'static', 'type' => 'pick', @@ -128,7 +128,7 @@ public function options( $settings ) { 'js' => __( 'Javascript (Interactive)', 'pods' ) ) ), - 'address_map_type_of_map' => array( + 'map_type' => array( 'label' => __( 'Default Map Type', 'pods' ), 'default' => 'roadmap', 'type' => 'pick', @@ -139,7 +139,7 @@ public function options( $settings ) { 'hybrid' => __( 'Hybrid', 'pods' ) ) ), - 'address_map_zoom' => array( + 'map_zoom' => array( 'label' => __( 'Default Map Zoom Level', 'pods' ), 'help' => array( __( 'Google Maps has documentation on the different zoom levels you can use.', 'pods' ), @@ -156,7 +156,7 @@ public function options( $settings ) { //'number_format_type' => 'slider' ) ), - 'address_map_marker' => array( + 'map_marker' => array( 'label' => __( 'Default Map Custom Marker', 'pods' ), 'type' => 'file', 'options' => array( @@ -201,6 +201,12 @@ public function maps_options( $options, $type ) { 'type' => 'boolean', 'dependency' => true ); + $options[ $type . '_autocorrect' ] = array( + 'label' => __( 'Autocorrect Address during save', 'pods' ), + 'depends-on' => array( $type . '_map' => true, $type . '_type' => array( 'address', 'text' ) ), + 'default' => 0, + 'type' => 'boolean' + ); $options[ $type . '_map_display' ] = array( 'label' => __( 'Map Display', 'pods' ), 'depends-on' => array( $type . '_map' => true ), @@ -212,13 +218,7 @@ public function maps_options( $options, $type ) { 'after' => __( 'After default display', 'pods' ) ) ); - $options[ $type . '_autocorrect' ] = array( - 'label' => __( 'Autocorrect Address during save', 'pods' ), - 'depends-on' => array( $type . '_map' => true, $type . '_type' => array( 'address', 'text' ) ), - 'default' => 0, - 'type' => 'boolean' - ); - $options[ $type . '_style' ] = array( + $options[ $type . '_map_style' ] = array( 'label' => __( 'Map Output Type', 'pods' ), 'depends-on' => array( $type . '_map' => true ), 'default' => pods_v( $type . '_style', self::$options, 'static', true ), @@ -228,7 +228,7 @@ public function maps_options( $options, $type ) { 'js' => __( 'Javascript (Interactive)', 'pods' ) ) ); - $options[ $type . '_type_of_map' ] = array( + $options[ $type . '_map_type' ] = array( 'label' => __( 'Map Type', 'pods' ), 'depends-on' => array( $type . '_map' => true ), 'default' => pods_v( $type . '_type', self::$options, 'roadmap', true ), @@ -240,7 +240,7 @@ public function maps_options( $options, $type ) { 'hybrid' => __( 'Hybrid', 'pods' ) ) ); - $options[ $type . '_zoom' ] = array( + $options[ $type . '_map_zoom' ] = array( 'label' => __( 'Map Zoom Level', 'pods' ), 'depends-on' => array( $type . '_map' => true ), 'help' => array( @@ -259,7 +259,7 @@ public function maps_options( $options, $type ) { //'number_format_type' => 'slider' ) ); - $options[ $type . '_info_window_content' ] = array( + $options[ $type . '_map_info_window_content' ] = array( 'label' => __( 'Map Info Window content', 'pods' ), 'depends-on' => array( $type . '_map' => true ), 'default' => 'default', @@ -270,7 +270,7 @@ public function maps_options( $options, $type ) { 'custom' => __( 'Custom (WYSIWYG)', 'pods' ) ) ); - $options[ $type . '_marker' ] = array( + $options[ $type . '_map_marker' ] = array( 'label' => __( 'Map Custom Marker', 'pods' ), 'depends-on' => array( $type . '_map' => true ), 'default' => pods_v( $type . '_marker', self::$options ), From c4e79796bab678dfc0d51f34556710aee86409b2 Mon Sep 17 00:00:00 2001 From: Jory Hogeveen Date: Wed, 10 Aug 2016 15:11:39 +0200 Subject: [PATCH 10/96] var api_key needs to be static + api_key disable apikey for geocode testing --- components/Maps/Maps-Google.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/Maps/Maps-Google.php b/components/Maps/Maps-Google.php index 7a499ecdef..416c900519 100644 --- a/components/Maps/Maps-Google.php +++ b/components/Maps/Maps-Google.php @@ -2,7 +2,7 @@ class Pods_Component_Maps_Google { - private $geocode_url = ''; + private static $geocode_url = ''; public function __construct() { @@ -186,10 +186,10 @@ public static function geocode( $data, $api_key = '', $type = 'address' ) { $data = implode( ',', $data ); } - $url = $this->geocode_url . 'json?' . $type . '=' . $data; - if ( ! empty( $api_key ) ) { + $url = self::$geocode_url . 'json?' . $type . '=' . $data; + /*if ( ! empty( $api_key ) ) { $url .= '&key=' . $api_key; - } + }*/ $post = wp_remote_post( $url ); From a23c249aa2354e7cc13cf90d37dd934882b385cc Mon Sep 17 00:00:00 2001 From: Jory Hogeveen Date: Wed, 10 Aug 2016 15:12:15 +0200 Subject: [PATCH 11/96] Add assets --- components/Maps/Maps-Google.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/components/Maps/Maps-Google.php b/components/Maps/Maps-Google.php index 416c900519..c4845c3561 100644 --- a/components/Maps/Maps-Google.php +++ b/components/Maps/Maps-Google.php @@ -5,6 +5,19 @@ class Pods_Component_Maps_Google { private static $geocode_url = ''; public function __construct() { + self::$geocode_url = apply_filters( 'pods_maps_google_geocode_url', 'https://maps.googleapis.com/maps/api/geocode/' ); + } + + public function assets() { + + // Static map: http://maps.googleapis.com/maps/api/staticmap + + if ( ! empty( Pods_Component_Maps::$api_key ) ) { + $googlemaps_js = '//maps.googleapis.com/maps/api/js?key=' . Pods_Component_Maps::$api_key; + wp_register_script( 'googlemaps', $googlemaps_js, false, '3' ); //sensor=false& + } + + } $this->geocode_url = apply_filters( 'pods_maps_google_geocode_url', 'https://maps.googleapis.com/maps/api/geocode/' ); From d25be8bb383385149732c3479b7e10c32e03be2e Mon Sep 17 00:00:00 2001 From: Jory Hogeveen Date: Wed, 10 Aug 2016 15:18:36 +0200 Subject: [PATCH 12/96] Base of backend field working - All maps additions are now in the component folder for consistency - Main component handles default (global) calls and redirects them to the right provider class - Field UI: Use the localization JS object and extend this for more usages like ajax calls to the main component for geodata - Field UI: Geodata lookup when typing a new address - Provider: API key is recently required for Google Maps --- components/Maps/Maps-Google.php | 7 +- components/Maps/Maps.php | 120 ++++++++++++++++++- components/Maps/ui/css/pods-maps.css | 5 + components/Maps/ui/fields/map-google.php | 145 +++++++++++++++++++++++ components/Maps/ui/js/pods-maps.js | 75 ++++++++++++ 5 files changed, 348 insertions(+), 4 deletions(-) create mode 100644 components/Maps/ui/css/pods-maps.css create mode 100644 components/Maps/ui/fields/map-google.php create mode 100644 components/Maps/ui/js/pods-maps.js diff --git a/components/Maps/Maps-Google.php b/components/Maps/Maps-Google.php index c4845c3561..3f804f44be 100644 --- a/components/Maps/Maps-Google.php +++ b/components/Maps/Maps-Google.php @@ -19,8 +19,13 @@ public function assets() { } - $this->geocode_url = apply_filters( 'pods_maps_google_geocode_url', 'https://maps.googleapis.com/maps/api/geocode/' ); + public function pods_ui_field_view_extra() { + $view = false; + if ( ! empty( Pods_Component_Maps::$api_key ) ) { + $view = plugin_dir_path( __FILE__ ) . 'ui/fields/map-google.php'; + } + return $view; } /** diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index 7b6e2ad1c3..ae80e9eba8 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -16,16 +16,28 @@ class Pods_Component_Maps extends PodsComponent { static $component_path; - static $component_file; - static $options; - static $provider; + static $api_key = ''; + + private static $nonce = 'pods_maps'; public function __construct() { // See https://github.com/pods-framework/pods/pull/3711 add_filter( 'pods_admin_setup_edit_address_additional_field_options', array( $this, 'maps_options' ), 10, 2 ); + + // Add Maps input + // do_action( 'pods_ui_field_address_input_view_extra', $view, $type, $name, $value, $options, $pod, $id ); + add_action( 'pods_ui_field_address_input_view_extra', array( $this, 'pods_ui_field_address_input_view_extra' ), 10, 7 ); + + // Ajax call handler + add_action( 'wp_ajax_pods_maps', array( $this, 'ajax_handler' ) ); + // Allow calls from frontend when needed (always verify nonce!) + add_action( 'wp_ajax_nopriv_pods_maps', array( $this, 'ajax_handler' ) ); + + add_action( 'admin_enqueue_scripts', array( $this, 'global_assets' ) ); + add_action( 'wp_enqueue_scripts', array( $this, 'global_assets' ) ); } /** @@ -39,6 +51,16 @@ public function admin_assets() { } + public function global_assets() { + wp_register_style( 'pods-maps', plugin_dir_url( __FILE__ ) . 'ui/css/pods-maps.css', array(), '1.0' ); + wp_register_script( 'pods-maps', plugin_dir_url( __FILE__ ) . 'ui/js/pods-maps.js', array( 'jquery' ), '1.0' ); + wp_localize_script( 'pods-maps', 'PodsMaps', array( + 'ajaxurl' => admin_url( 'admin-ajax.php' ), + '_nonce' => wp_create_nonce( self::$nonce ), + ) ); + self::$provider->assets(); + } + /** * Register the component * @@ -64,6 +86,10 @@ public function handler( $options ) { self::$options = $options; + if ( ! empty( $options['api_key'] ) ) { + self::$api_key = $options['api_key']; + } + $this->load_provider(); } @@ -86,6 +112,66 @@ private function load_provider() { } + /** + * Ajax handler for geocode calls + * + * AJAX call data setup: + * action => pods_maps + * _pods_maps_nonce => PodsMaps._nonce + * pods_maps_action => 'string' (the maps action) + * pods_maps_data => 'string|array' (the provided data) + */ + public function ajax_handler() { + + if ( ! defined('DOING_AJAX') + || ! DOING_AJAX + || ! isset( $_POST['_pods_maps_nonce'] ) + || ! wp_verify_nonce( $_POST['_pods_maps_nonce'], self::$nonce ) + ) { + wp_send_json_error( __( 'Cheatin uh?', 'pods' ) ); + die(); + } + + if ( isset( $_POST['pods_maps_action'] ) ) { + $return = false; + $data = ''; + if ( ! empty( $_POST['pods_maps_data'] ) ) { + if ( is_array( $_POST['pods_maps_data'] ) ) { + $data = array_map( 'pods_sanitize', $_POST['pods_maps_data'] ); + } else { + $data = pods_sanitize( $_POST['pods_maps_data'] ); + } + } + if ( ! empty( $data ) && is_object( self::$provider ) ) { + $provider = get_class( self::$provider ); + switch ( pods_sanitize( $_POST['pods_maps_action'] ) ) { + case 'geocode': + case 'geocode_address': + if ( method_exists( $provider, 'geocode_address' ) ) { + $return = $provider::geocode_address( $data, self::$api_key ); + } + break; + case 'geocode_address_to_latlng': + if ( method_exists( $provider, 'geocode_address_to_latlng' ) ) { + $return = $provider::geocode_address_to_latlng( $data, self::$api_key ); + } + break; + case 'geocode_latlng_to_address': + if ( method_exists( $provider, 'geocode_latlng_to_address' ) ) { + $return = $provider::geocode_latlng_to_address( $data, self::$api_key ); + } + break; + } + } + if ( ! empty( $return ) ) { + wp_send_json_success( $return ); + } else { + wp_send_json_error( __( 'Geocode error, please try again or type different address data.', 'pods' ) ); + } + } + die(); + } + /** * {@inheritDoc} * @@ -298,6 +384,34 @@ public function maps_options( $options, $type ) { return $options; } + /** + * Allow Map providers to add a map to the field input field + * + * @param $view + * @param $type + * @param $name + * @param $value + * @param $options + * @param $pod + * @param $id + */ + public function pods_ui_field_address_input_view_extra( $view, $type, $name, $value, $options, $pod, $id ) { + + if ( ! empty ( $options['address_map'] ) ) { + + $provider = get_class( self::$provider ); + if ( method_exists( $provider, 'pods_ui_field_view_extra' ) ) { + $view = $provider::pods_ui_field_view_extra(); + } + + if ( $view && file_exists( $view ) ) { + pods_view( $view, compact( array_keys( get_defined_vars() ) ) ); + } + + } + + } + /** * Append new dependency to existing data * diff --git a/components/Maps/ui/css/pods-maps.css b/components/Maps/ui/css/pods-maps.css new file mode 100644 index 0000000000..9db477d9bd --- /dev/null +++ b/components/Maps/ui/css/pods-maps.css @@ -0,0 +1,5 @@ +.pods-maps-map-canvas { + margin-top: 10px; + height: 300px; + width: 95%; +} \ No newline at end of file diff --git a/components/Maps/ui/fields/map-google.php b/components/Maps/ui/fields/map-google.php new file mode 100644 index 0000000000..62765846cb --- /dev/null +++ b/components/Maps/ui/fields/map-google.php @@ -0,0 +1,145 @@ + +
+ + \ No newline at end of file diff --git a/components/Maps/ui/js/pods-maps.js b/components/Maps/ui/js/pods-maps.js new file mode 100644 index 0000000000..eea4c5445b --- /dev/null +++ b/components/Maps/ui/js/pods-maps.js @@ -0,0 +1,75 @@ + +if ( typeof PodsMaps == 'undefined' ) { + var PodsMaps = {}; +} + +(function ( $ ) { + + PodsMaps.ajaxData = {}; + PodsMaps.doingAjax = false; + PodsMaps.ajaxResults = false; + + PodsMaps.mergeAddressFromInputs = function( fields ) { + var address = []; + if ( fields.line_1.length ) { address.push( fields.line_1.val() ); } + if ( fields.line_2.length ) { address.push( fields.line_2.val() ); } + if ( fields.city.length ) { address.push( fields.city.val() ); } + if ( fields.postal_code.length ) { address.push( fields.postal_code.val() ); } + if ( fields.region.length ) { address.push( fields.region.val() ); } + if ( fields.country.length ) { address.push( fields.country.val() ); } + return address.join(', '); + }; + + PodsMaps.geocode = function( data ) { + PodsMaps.ajaxData.pods_maps_action = 'geocode'; + PodsMaps.ajaxData.pods_maps_data = data; + return PodsMaps.doAjaxPost(); + }; + + PodsMaps.geocodeAddressToLatLng = function( data ) { + PodsMaps.ajaxData.pods_maps_action = 'geocode_address_to_latlng'; + PodsMaps.ajaxData.pods_maps_data = data; + return PodsMaps.doAjaxPost(); + }; + + PodsMaps.geocodeLatLngToAddress = function( data ) { + PodsMaps.ajaxData.pods_maps_action = 'geocode_latlng_to_address'; + PodsMaps.ajaxData.pods_maps_data = data; + return PodsMaps.doAjaxPost(); + }; + + PodsMaps.doAjaxPost = function() { + PodsMaps.ajaxData.action = 'pods_maps'; + PodsMaps.ajaxData._pods_maps_nonce = PodsMaps._nonce; + PodsMaps.doingAjax = true; + PodsMaps.ajaxResults = false; + $.post( + PodsMaps.ajaxurl, + PodsMaps.ajaxData, + function( response ) { + PodsMaps.ajaxData = {}; + PodsMaps.doingAjax = false; + if ( typeof response.data != 'undefined' ) { + PodsMaps.ajaxResults = response.data; + $(document).trigger('PodsMapsAjaxDone'); + return response.data; + } + return false; + } + ); + }; + + var methods = {}; + + $.fn.PodsMap = function ( method ) { + + if ( methods [method] ) { + return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ) ); + } + else { + $.error( 'Method ' + method ); + } + + }; + +})( jQuery ); \ No newline at end of file From 77ffb87097fcb285e0d3dfb0f3fcdb0888136c12 Mon Sep 17 00:00:00 2001 From: Jory Hogeveen Date: Wed, 10 Aug 2016 15:24:19 +0200 Subject: [PATCH 13/96] Just the old file, this is where the frond-end code will go --- components/Maps/ui/front/map.php | 76 ++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 components/Maps/ui/front/map.php diff --git a/components/Maps/ui/front/map.php b/components/Maps/ui/front/map.php new file mode 100644 index 0000000000..e9d403ac79 --- /dev/null +++ b/components/Maps/ui/front/map.php @@ -0,0 +1,76 @@ + +
+ + \ No newline at end of file From 12ffa74368c164c5f063c7790514dd35bcaf3881 Mon Sep 17 00:00:00 2001 From: Jory Hogeveen Date: Wed, 10 Aug 2016 20:28:12 +0200 Subject: [PATCH 14/96] Generate hidden lat/lng fields for geo storage on the field UI --- components/Maps/Maps.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index ae80e9eba8..d2249e8bcf 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -398,14 +398,19 @@ public function maps_options( $options, $type ) { public function pods_ui_field_address_input_view_extra( $view, $type, $name, $value, $options, $pod, $id ) { if ( ! empty ( $options['address_map'] ) ) { - $provider = get_class( self::$provider ); if ( method_exists( $provider, 'pods_ui_field_view_extra' ) ) { $view = $provider::pods_ui_field_view_extra(); } if ( $view && file_exists( $view ) ) { + // Add hidden lat/lng fields for non latlng view types pods_view( $view, compact( array_keys( get_defined_vars() ) ) ); + if ( $type != 'lat-lng' ) { + echo '
'; + pods_view( plugin_dir_path( __FILE__ ) . 'ui/fields/lat-lng.php', compact( array_keys( get_defined_vars() ) ) ); + echo '
'; + } } } From af5a9584851bc643861309235f4c2d6256576089 Mon Sep 17 00:00:00 2001 From: Jory Hogeveen Date: Wed, 10 Aug 2016 20:28:49 +0200 Subject: [PATCH 15/96] Patch the lat/lng field to the component UI directory + fix field types --- components/Maps/ui/fields/lat-lng.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 components/Maps/ui/fields/lat-lng.php diff --git a/components/Maps/ui/fields/lat-lng.php b/components/Maps/ui/fields/lat-lng.php new file mode 100644 index 0000000000..4a9c5ff2e6 --- /dev/null +++ b/components/Maps/ui/fields/lat-lng.php @@ -0,0 +1,14 @@ + + + 10, 'number_format' => '9999.99' ) ) ?> + + + 10, 'number_format' => '9999.99' ) ) ?> + Date: Wed, 10 Aug 2016 20:30:25 +0200 Subject: [PATCH 16/96] JS fixes for geodata - Bring back "lookup" button, seems like a better approach - Validate the view type that this map field is appended to --- components/Maps/ui/fields/map-google.php | 53 +++++++++++++++--------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/components/Maps/ui/fields/map-google.php b/components/Maps/ui/fields/map-google.php index 62765846cb..d964b850db 100644 --- a/components/Maps/ui/fields/map-google.php +++ b/components/Maps/ui/fields/map-google.php @@ -24,6 +24,7 @@ $attributes = PodsForm::merge_attributes( $attributes, $name, $form_field_type, $options ); echo PodsForm::label( 'map-google', __( 'Google Maps', 'pod' ) ); ?> +
\ No newline at end of file From b63f335de8235b102e195beec2095e458dbef2e0 Mon Sep 17 00:00:00 2001 From: Scott Kingsley Clark Date: Sat, 22 Apr 2017 09:12:27 -0500 Subject: [PATCH 50/96] Ensure add button is i18n'd --- components/Maps/Maps.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index 26df9eb0cf..8918a6a3a6 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -261,7 +261,7 @@ public function options( $settings ) { 'file_edit_title' => 0, 'file_restrict_filesize' => '1MB', 'file_type' => 'images', - 'file_add_button' => 'Upload Marker Icon' + 'file_add_button' => __( 'Upload Marker Icon', 'pods' ), ) ) ); From 5b8007a3ef678f2723d797710feafe67f5f820ae Mon Sep 17 00:00:00 2001 From: Scott Kingsley Clark Date: Sat, 22 Apr 2017 09:13:48 -0500 Subject: [PATCH 51/96] Hack around component setting saving for file field types, the post data was multidimensional array with 'id' keys --- classes/PodsComponents.php | 71 ++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/classes/PodsComponents.php b/classes/PodsComponents.php index 2050469e6a..229f69fc33 100644 --- a/classes/PodsComponents.php +++ b/classes/PodsComponents.php @@ -691,38 +691,59 @@ public function admin_ajax () { die(); // KBAI! } - public function admin_ajax_settings ( $component, $params ) { - if ( !isset( $this->components[ $component ] ) ) - wp_die( 'Invalid Component' ); - elseif ( !method_exists( $this->components[ $component ][ 'object' ], 'options' ) ) - pods_error( 'Component options method does not exist', $this ); + public function admin_ajax_settings( $component, $params ) { - $options = $this->components[ $component ][ 'object' ]->options( $this->settings[ 'components' ][ $component ] ); + if ( ! isset( $this->components[ $component ] ) ) { + wp_die( 'Invalid Component' ); + } elseif ( ! method_exists( $this->components[ $component ]['object'], 'options' ) ) { + pods_error( 'Component options method does not exist', $this ); + } - if ( empty( $this->settings[ 'components' ][ $component ] ) ) - $this->settings[ 'components' ][ $component ] = array(); + $options = $this->components[ $component ]['object']->options( $this->settings['components'][ $component ] ); - foreach ( $options as $field_name => $field_option ) { - $field_option = PodsForm::field_setup( $field_option, null, $field_option[ 'type' ] ); + if ( empty( $this->settings['components'][ $component ] ) ) { + $this->settings['components'][ $component ] = array(); + } - if ( !is_array( $field_option[ 'group' ] ) ) { - $field_value = pods_var_raw( 'pods_setting_' . $field_name, $params ); + foreach ( $options as $field_name => $field_option ) { + $field_option = PodsForm::field_setup( $field_option, null, $field_option['type'] ); - $this->settings[ 'components' ][ $component ][ $field_name ] = $field_value; - } - else { - foreach ( $field_option[ 'group' ] as $field_group_name => $field_group_option ) { - $field_value = pods_var_raw( 'pods_setting_' . $field_group_name, $params ); + if ( ! is_array( $field_option['group'] ) ) { + $field_value = pods_var_raw( 'pods_setting_' . $field_name, $params ); - $this->settings[ 'components' ][ $component ][ $field_group_name ] = $field_value; - } - } - } + if ( is_array( $field_value ) ) { + $first_value = current( $field_value ); - $settings = version_compare( PHP_VERSION, '5.4.0', '>=' ) ? json_encode( $this->settings, JSON_UNESCAPED_UNICODE ) : json_encode( $this->settings ); + if ( is_array( $first_value ) && ! empty( $first_value['id'] ) ) { + $field_value = wp_list_pluck( $field_value, 'id' ); + $field_value = array_map( 'absint', $field_value ); + } + } - update_option( 'pods_component_settings', $settings ); + $this->settings['components'][ $component ][ $field_name ] = $field_value; + } else { + foreach ( $field_option['group'] as $field_group_name => $field_group_option ) { + $field_value = pods_var_raw( 'pods_setting_' . $field_group_name, $params ); - return '1'; - } + if ( is_array( $field_value ) ) { + $first_value = current( $field_value ); + + if ( is_array( $first_value ) && ! empty( $first_value['id'] ) ) { + $field_value = wp_list_pluck( $field_value, 'id' ); + $field_value = array_map( 'absint', $field_value ); + } + } + + $this->settings['components'][ $component ][ $field_group_name ] = $field_value; + } + } + } + + $settings = version_compare( PHP_VERSION, '5.4.0', '>=' ) ? json_encode( $this->settings, JSON_UNESCAPED_UNICODE ) : json_encode( $this->settings ); + + update_option( 'pods_component_settings', $settings ); + + return '1'; + + } } From 48176e5340642f21576ae192c6f74d5b3bee745b Mon Sep 17 00:00:00 2001 From: Scott Kingsley Clark Date: Sat, 22 Apr 2017 10:20:29 -0500 Subject: [PATCH 52/96] Fix phpdoc --- classes/PodsField.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/PodsField.php b/classes/PodsField.php index c942971ca5..5ab6c6fc3c 100644 --- a/classes/PodsField.php +++ b/classes/PodsField.php @@ -188,7 +188,7 @@ public function value( $value = null, $name = null, $options = null, $pod = null } /** - * Change the way the value of the field is displayed with Pods::get + * Change the way the value of the field is displayed with Pods::field * * @param mixed|null $value * @param string|null $name From d68e720b3c9fa53fe84ee01dd462c0f548ce7e19 Mon Sep 17 00:00:00 2001 From: Scott Kingsley Clark Date: Sat, 22 Apr 2017 10:20:50 -0500 Subject: [PATCH 53/96] Fix selected handling for single-value or multi-value simple pick fields --- classes/fields/pick.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/classes/fields/pick.php b/classes/fields/pick.php index 023f88353e..338bf08ac3 100644 --- a/classes/fields/pick.php +++ b/classes/fields/pick.php @@ -1145,6 +1145,18 @@ public function build_dfv_field_item_data_recurse_item( $item_id, $item_title, $ $use_dashicon = true; } + $selected = false; + + if ( is_array( $args->value ) ) { + if ( isset( $args->value[ $item_id ] ) ) { + $selected = true; + } elseif ( in_array( $item_id, $args->value, true ) ) { + $selected = true; + } + } elseif ( $item_id === $args->value ) { + $selected = true; + } + $item = array( 'id' => $item_id, 'use_dashicon' => $use_dashicon, @@ -1152,7 +1164,7 @@ public function build_dfv_field_item_data_recurse_item( $item_id, $item_title, $ 'name' => $item_title, 'edit_link' => $edit_link, 'link' => $link, - 'selected' => ( isset( $args->value[ $item_id ] ) ), + 'selected' => $selected, ); return $item; From d2118605c292622e63107baf54e33e86150eb425 Mon Sep 17 00:00:00 2001 From: Scott Kingsley Clark Date: Sat, 22 Apr 2017 10:21:22 -0500 Subject: [PATCH 54/96] Add display_list handling (just display once, not for every key in our field value) --- classes/PodsForm.php | 41 +++++++++--- classes/fields/address.php | 132 +++++++++++++++++++++++-------------- 2 files changed, 113 insertions(+), 60 deletions(-) diff --git a/classes/PodsForm.php b/classes/PodsForm.php index 2d06e530d8..32dc93ca81 100644 --- a/classes/PodsForm.php +++ b/classes/PodsForm.php @@ -953,15 +953,38 @@ public static function display( $type, $value = null, $name = null, $options = n $tableless_field_types = self::tableless_field_types(); - if ( method_exists( self::$loaded[ $type ], 'display' ) ) { - if ( is_array( $value ) && !in_array( $type, $tableless_field_types ) ) { - foreach ( $value as $k => $display_value ) { - $value[ $k ] = call_user_func_array( array( self::$loaded[ $type ], 'display' ), array( $display_value, $name, $options, $pod, $id, $traverse ) ); - } - } - else - $value = call_user_func_array( array( self::$loaded[ $type ], 'display' ), array( $value, $name, $options, $pod, $id, $traverse ) ); - } + if ( method_exists( self::$loaded[ $type ], 'display_list' ) ) { + $value = call_user_func_array( array( self::$loaded[ $type ], 'display_list' ), array( + $value, + $name, + $options, + $pod, + $id, + $traverse + ) ); + } elseif ( method_exists( self::$loaded[ $type ], 'display' ) ) { + if ( is_array( $value ) && ! in_array( $type, $tableless_field_types ) ) { + foreach ( $value as $k => $display_value ) { + $value[ $k ] = call_user_func_array( array( self::$loaded[ $type ], 'display' ), array( + $display_value, + $name, + $options, + $pod, + $id, + $traverse + ) ); + } + } else { + $value = call_user_func_array( array( self::$loaded[ $type ], 'display' ), array( + $value, + $name, + $options, + $pod, + $id, + $traverse + ) ); + } + } $value = apply_filters( 'pods_form_display_' . $type, $value, $name, $options, $pod, $id, $traverse ); diff --git a/classes/fields/address.php b/classes/fields/address.php index bc77c71abf..589adf7cb8 100644 --- a/classes/fields/address.php +++ b/classes/fields/address.php @@ -1,4 +1,5 @@ array( + self::$type . '_type' => array( 'label' => __( 'Address Type', 'pods' ), 'default' => 'address', 'type' => 'pick', 'data' => array( - 'address' => __( 'Address Field Group', 'pods' ), - 'text' => __( 'Freeform Text', 'pods' ) + 'address' => __( 'Address Field Group', 'pods' ), + 'text' => __( 'Freeform Text', 'pods' ) ), 'dependency' => true ), - self::$type . '_address_options' => array( + self::$type . '_address_options' => array( 'label' => __( 'Address Options', 'pods' ), 'depends-on' => array( self::$type . '_type' => 'address' ), 'group' => array( - self::$type . '_address_line_1' => array( + self::$type . '_address_line_1' => array( 'label' => __( 'Enable Address Line 1', 'pods' ), 'default' => 1, 'type' => 'boolean' ), - self::$type . '_address_line_2' => array( + self::$type . '_address_line_2' => array( 'label' => __( 'Enable Address Line 2', 'pods' ), 'default' => 0, 'type' => 'boolean' ), - self::$type . '_address_postal_code' => array( + self::$type . '_address_postal_code' => array( 'label' => __( 'Enable ZIP / Postal Code', 'pods' ), 'default' => 0, 'type' => 'boolean' ), - self::$type . '_address_city' => array( + self::$type . '_address_city' => array( 'label' => __( 'Enable City', 'pods' ), 'default' => 1, 'type' => 'boolean' ), - self::$type . '_address_region' => array( + self::$type . '_address_region' => array( 'label' => __( 'Enable Region (State / Province)', 'pods' ), 'default' => 1, 'type' => 'boolean', 'dependency' => true ), - self::$type . '_address_country' => array( + self::$type . '_address_country' => array( 'label' => __( 'Enable Country', 'pods' ), 'default' => 0, 'type' => 'boolean', @@ -110,7 +112,7 @@ public function options() { ) ) ), - self::$type . '_address_region_input' => array( + self::$type . '_address_region_input' => array( 'label' => __( 'Region Input Type', 'pods' ), 'depends-on' => array( self::$type . '_address_region' => true, self::$type . '_type' => 'address' ), 'default' => 'text', @@ -121,21 +123,21 @@ public function options() { ), 'dependency' => true ), - self::$type . '_address_region_output' => array( + self::$type . '_address_region_output' => array( 'label' => __( 'Region Output Type', 'pods' ), 'depends-on' => array( self::$type . '_address_region_input' => 'pick', - self::$type . '_address_region' => true, - self::$type . '_type' => 'address' + self::$type . '_address_region' => true, + self::$type . '_type' => 'address' ), 'default' => 'long', 'type' => 'pick', 'data' => array( - 'long' => __( 'Full name', 'pods' ), + 'long' => __( 'Full name', 'pods' ), 'short' => __( 'Stage / Province code', 'pods' ) ) ), - self::$type . '_address_country_input' => array( + self::$type . '_address_country_input' => array( 'label' => __( 'Country Input Type', 'pods' ), 'depends-on' => array( self::$type . '_address_country' => true, self::$type . '_type' => 'address' ), 'default' => 'text', @@ -150,17 +152,17 @@ public function options() { 'label' => __( 'Country Output Type', 'pods' ), 'depends-on' => array( self::$type . '_address_country_input' => 'pick', - self::$type . '_address_country' => true, - self::$type . '_type' => 'address' + self::$type . '_address_country' => true, + self::$type . '_type' => 'address' ), 'default' => 'long', 'type' => 'pick', 'data' => array( - 'long' => __( 'Full name', 'pods' ), + 'long' => __( 'Full name', 'pods' ), 'short' => __( 'Country code', 'pods' ) ) ), - self::$type . '_display_type' => array( + self::$type . '_display_type' => array( 'label' => __( 'Display Type', 'pods' ), 'default' => 'default', 'type' => 'pick', @@ -171,14 +173,14 @@ public function options() { 'depends-on' => array( self::$type . '_type' => 'address' ), 'dependency' => true ), - self::$type . '_display_type_custom' => array( + self::$type . '_display_type_custom' => array( 'label' => __( 'Custom display', 'pods' ), - 'help' => __( 'You can use the following tags for address fields', 'pods' ) . ': {{line_1}}, {{line_2}}, {{postal_code}}, {{city}}, {{region}}, {{country}}', + 'help' => __( 'You can use the following tags for address fields', 'pods' ) . ': {{line_1}}, {{line_2}}, {{postal_code}}, {{city}}, {{region}}, {{country}}', 'default' => self::default_display_format(), 'type' => 'paragraph', 'depends-on' => array( self::$type . '_display_type' => 'custom', self::$type . '_type' => 'address' ) ), - self::$type . '_microdata' => array( + self::$type . '_microdata' => array( 'label' => __( 'Format with microdata?', 'pods' ) . ' (schema.org)', 'default' => 0, 'type' => 'boolean', @@ -221,7 +223,7 @@ public function schema( $options = null ) { */ public function display( $value = null, $name = null, $options = null, $pod = null, $id = null ) { - $value_raw = $value; + $value_raw = $value; $display_type = pods_v( self::$type . '_display_type', $options ); $view = PODS_DIR . 'ui/front/address.php'; @@ -234,6 +236,25 @@ public function display( $value = null, $name = null, $options = null, $pod = nu } + /** + * Change the way the a list of values of the field are displayed with Pods::field + * + * @param mixed|null $value + * @param string|null $name + * @param array|null $options + * @param array|null $pod + * @param int|null $id + * + * @return mixed|null|string + * + * @since 2.7 + */ + public function display_list( $value = null, $name = null, $options = null, $pod = null, $id = null ) { + + return call_user_func_array( array( $this, 'display' ), func_get_args() ); + + } + /** * {@inheritDoc} * @@ -271,15 +292,15 @@ public function validate( $value, $name = null, $options = null, $fields = null, /** * Add extra validation checks * - * @param array $errors - * @param mixed $value + * @param array $errors + * @param mixed $value * @param string $type Field address type * @param string $name - * @param array $options - * @param array $fields - * @param array $pod - * @param int $id - * @param array $params + * @param array $options + * @param array $fields + * @param array $pod + * @param int $id + * @param array $params */ $errors = apply_filters( 'pods_ui_field_address_validate', $errors, $value, $type, $name, $options, $fields, $pod, $id, $params ); @@ -307,14 +328,14 @@ public function pre_save( $value, $id = null, $name = null, $options = null, $fi /** * Add extra value sanitation * - * @param mixed $value + * @param mixed $value * @param string $type Field address type - * @param int $id + * @param int $id * @param string $name - * @param array $options - * @param array $fields - * @param array $pod - * @param array $params + * @param array $options + * @param array $fields + * @param array $pod + * @param array $params */ $value = apply_filters( 'pods_ui_field_address_pre_save', $value, $type, $id, $name, $options, $fields, $pod, $params ); @@ -344,16 +365,17 @@ public static function format_value_for_output( $value, $options ) { if ( ! empty( $value['address'] ) ) { foreach ( $value['address'] as $key => $val ) { - // Display full region names if enabled - if ( $key == 'region' && $options[ self::$type . '_address_country_input' ] == 'pick' && $options[ self::$type . '_address_country_output' ] == 'long' ) { + if ( $key == 'region' && $options[ self::$type . '_address_region_input' ] == 'pick' && $options[ self::$type . '_address_region_output' ] == 'long' ) { + // Display full region names if enabled $regions = PodsForm::field_method( 'pick', 'data_us_states' ); + if ( array_key_exists( $val, $regions ) ) { $value['address'][ $key ] = $regions[ $val ]; } - } - // Display full country names if enabled - if ( $key == 'country' && $options[ self::$type . '_address_country_input' ] == 'pick' && $options[ self::$type . '_address_country_output' ] == 'long' ) { + } elseif ( $key == 'country' && $options[ self::$type . '_address_country_input' ] == 'pick' && $options[ self::$type . '_address_country_output' ] == 'long' ) { + // Display full country names if enabled $countries = PodsForm::field_method( 'pick', 'data_countries' ); + if ( array_key_exists( $val, $countries ) ) { $value['address'][ $key ] = $countries[ $val ]; } @@ -362,6 +384,7 @@ public static function format_value_for_output( $value, $options ) { } return $value; + } /** @@ -369,13 +392,14 @@ public static function format_value_for_output( $value, $options ) { * * @since 2.7 * - * @param string $format The format to be used (default or custom) - * @param array $value The field value - * @param array $options The field options + * @param string $format The format to be used (default or custom) + * @param array $value The field value + * @param array $options The field options * * @return string */ public static function format_to_html( $format, $value, $options ) { + $output = ''; $value = self::format_value_for_output( $value, $options ); @@ -391,14 +415,14 @@ public static function format_to_html( $format, $value, $options ) { // @todo check pregreplace, maybe this can be done better (nl2br not working) // Convert actual line breaks into an array - $lines = explode( '\r\n', preg_replace("/\n/m", '\r\n', $format) ); + $lines = explode( '\r\n', preg_replace( "/\n/m", '\r\n', $format ) ); foreach ( $lines as $key => $line ) { // preg_match to all tags preg_match_all( '#{{(.*?)}}#', $line, $tags ); if ( ! empty( $tags[1] ) ) { - foreach( $tags[1] as $tag ) { + foreach ( $tags[1] as $tag ) { // Default value is empty. Only known tags are allowed, remove all unknown tags $value = ''; if ( ! empty( $address[ $tag ] ) ) { @@ -408,15 +432,20 @@ public static function format_to_html( $format, $value, $options ) { $lines[ $key ] = str_replace( '{{' . $tag . '}}', $value, $lines[ $key ] ); } } - if ( empty( trim( $lines[ $key ] ) ) ) { + + $lines[ $key ] = trim( $lines[ $key ] ); + + if ( empty( $lines[ $key ] ) ) { unset( $lines[ $key ] ); } } + // Lines to HTML line breaks $output = implode( '
', $lines ); $output = self::wrap_html_format( $output, 'address', 'div', $microdata ); } + return $output; } @@ -432,15 +461,15 @@ public static function format_to_html( $format, $value, $options ) { */ public static function wrap_html_format( $value, $tag, $element, $microdata = false ) { - $atts['class'] = 'pods-address' . $tags; + $atts['class'] = 'pods-address' . $tag; switch ( $tag ) { case 'address': $atts['class'] = 'pods-address'; if ( $microdata ) { - $atts['itemprop'] = 'address'; + $atts['itemprop'] = 'address'; $atts['itemscope'] = ''; - $atts['itemtype'] = 'http://schema.org/PostalAddress'; + $atts['itemtype'] = 'http://schema.org/PostalAddress'; }; break; @@ -499,6 +528,7 @@ public static function wrap_html_format( $value, $tag, $element, $microdata = fa * @return string */ public static function default_display_format() { + return '{{line_1}} {{line_2}} {{postal_code}} {{city}} From e6cd8406e18d031f09c24b9dcabb60819f6e356b Mon Sep 17 00:00:00 2001 From: Scott Kingsley Clark Date: Sat, 22 Apr 2017 10:21:45 -0500 Subject: [PATCH 55/96] Add output escaping and get image url from marker file ID --- components/Maps/ui/fields/map-google.php | 49 ++++++++++++++---------- components/Maps/ui/front/map-google.php | 20 ++++++---- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/components/Maps/ui/fields/map-google.php b/components/Maps/ui/fields/map-google.php index 57521df6e4..e21ee8b3b4 100644 --- a/components/Maps/ui/fields/map-google.php +++ b/components/Maps/ui/fields/map-google.php @@ -24,6 +24,10 @@ $map_options['marker'] = Pods_Component_Maps::$options['map_marker']; } +if ( ! empty( $map_options['marker'] ) ) { + $map_options['marker'] = wp_get_attachment_image_url( $map_options['marker'], 'full' ); +} + $attributes = array(); $attributes = PodsForm::merge_attributes( $attributes, $name, $form_field_type, $options ); @@ -37,38 +41,41 @@ echo PodsForm::label( 'map-google', __( 'Google Maps', 'pod' ) ); ?> - -
+ +
\ No newline at end of file diff --git a/ui/front/address-map.php b/ui/front/address-map.php deleted file mode 100644 index 2ce1cc9a9c..0000000000 --- a/ui/front/address-map.php +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ui/front/lat-long.php b/ui/front/lat-long.php deleted file mode 100644 index 2ce1cc9a9c..0000000000 --- a/ui/front/lat-long.php +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ui/front/map.php b/ui/front/map.php deleted file mode 100644 index 2ce1cc9a9c..0000000000 --- a/ui/front/map.php +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 11e0cb2de376af8f5e41c7eebf8fd16adafd4dce Mon Sep 17 00:00:00 2001 From: Scott Kingsley Clark Date: Sat, 22 Apr 2017 10:38:26 -0500 Subject: [PATCH 59/96] Formatting stuff --- ui/front/address.php | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/ui/front/address.php b/ui/front/address.php index a2a1f083c9..c0dde53ab4 100644 --- a/ui/front/address.php +++ b/ui/front/address.php @@ -1,16 +1,14 @@ \ No newline at end of file + echo PodsForm::field_method( 'address', 'format_to_html', $format, $value, $options ); +} \ No newline at end of file From 7bdbecb309ac16c6759fd4a85032d0ba6be869ee Mon Sep 17 00:00:00 2001 From: Scott Kingsley Clark Date: Sat, 22 Apr 2017 10:53:33 -0500 Subject: [PATCH 60/96] Handle output display option --- components/Maps/Maps.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index ee61f9db14..24662d8b08 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -386,7 +386,6 @@ public function maps_options( $options, $type ) { 'display_type' => __( 'Display Type', 'pods' ) ) ); - // @todo Make file type working as a field option $options['maps_marker'] = array( 'label' => __( 'Map Custom Marker', 'pods' ), 'depends-on' => array( 'maps' => true ), @@ -431,7 +430,7 @@ public function maps_options( $options, $type ) { */ public function pods_ui_field_address_display_value( $output, $value, $view, $display_type, $name, $options, $pod, $id ) { - if ( ! empty ( $options['maps'] ) ) { + if ( ! empty( $options['maps'] ) ) { $view = ''; $provider = get_class( self::$provider ); @@ -443,8 +442,15 @@ public function pods_ui_field_address_display_value( $output, $value, $view, $di // Add hidden lat/lng fields for non latlng view types $maps_value = pods_view( $view, compact( array_keys( get_defined_vars() ) ), false, 'cache', true ); - // @todo change location based on option - $output .= $maps_value; + $maps_display = pods_v( 'maps_display', $options, 'replace', true ); + + if ( 'before' === $maps_display ) { + $output = $maps_value . $output; + } elseif ( 'after' === $maps_display ) { + $output .= $maps_value; + } else { + $output = $maps_value; + } } } @@ -465,7 +471,7 @@ public function pods_ui_field_address_display_value( $output, $value, $view, $di */ public function pods_ui_field_address_input_view_extra( $view, $type, $name, $value, $options, $pod, $id ) { - if ( ! empty ( $options['maps'] ) ) { + if ( ! empty( $options['maps'] ) ) { $provider = get_class( self::$provider ); if ( is_callable( array( $provider, 'pods_ui_field_view_extra' ) ) ) { $view = self::$provider->pods_ui_field_view_extra(); From fb3169c8d1d9aa6e66347a74f6985c6af51e5eff Mon Sep 17 00:00:00 2001 From: Scott Kingsley Clark Date: Sat, 22 Apr 2017 10:53:46 -0500 Subject: [PATCH 61/96] code formatting and output escaping for address --- classes/fields/address.php | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/classes/fields/address.php b/classes/fields/address.php index 589adf7cb8..805a5c86b2 100644 --- a/classes/fields/address.php +++ b/classes/fields/address.php @@ -409,7 +409,6 @@ public static function format_to_html( $format, $value, $options ) { } if ( ! empty( $address ) ) { - // Format in microdata? $microdata = ( ! empty( $options[ self::$type . '_microdata' ] ) ) ? true : false; @@ -418,17 +417,19 @@ public static function format_to_html( $format, $value, $options ) { $lines = explode( '\r\n', preg_replace( "/\n/m", '\r\n', $format ) ); foreach ( $lines as $key => $line ) { - // preg_match to all tags preg_match_all( '#{{(.*?)}}#', $line, $tags ); + if ( ! empty( $tags[1] ) ) { foreach ( $tags[1] as $tag ) { // Default value is empty. Only known tags are allowed, remove all unknown tags $value = ''; + if ( ! empty( $address[ $tag ] ) ) { // Format the value for HTML $value = self::wrap_html_format( $address[ $tag ], $tag, 'span', $microdata ); } + $lines[ $key ] = str_replace( '{{' . $tag . '}}', $value, $lines[ $key ] ); } } @@ -447,6 +448,7 @@ public static function format_to_html( $format, $value, $options ) { } return $output; + } /** @@ -461,63 +463,76 @@ public static function format_to_html( $format, $value, $options ) { */ public static function wrap_html_format( $value, $tag, $element, $microdata = false ) { - $atts['class'] = 'pods-address' . $tag; + $atts = array( + 'class' => 'pods-address' . $tag, + ); switch ( $tag ) { case 'address': $atts['class'] = 'pods-address'; + if ( $microdata ) { $atts['itemprop'] = 'address'; $atts['itemscope'] = ''; $atts['itemtype'] = 'http://schema.org/PostalAddress'; - }; + } + break; case 'line_1': case 'line_2': if ( $microdata ) { $atts['itemprop'] = 'streetAddress'; - }; + } + break; case 'postal_code': if ( $microdata ) { $atts['itemprop'] = 'postalCode'; - }; + } + break; case 'city': if ( $microdata ) { $atts['itemprop'] = 'addressLocality'; - }; + } + break; case 'region': if ( $microdata ) { $atts['itemprop'] = 'addressRegion'; - }; + } + break; case 'country': if ( $microdata ) { $atts['itemprop'] = 'addressCountry'; - }; + } + break; default: $atts = false; + break; } if ( $atts ) { $attributes = ''; + foreach ( $atts as $key => $val ) { - $attributes .= ' ' . $key . '="' . $val . '"'; + $attributes .= ' ' . esc_html( $key ) . '="' . esc_attr( $val ) . '"'; } - $value = '<' . $element . $attributes . '>' . $value . ''; + + $value = '<' . esc_html( $element ) . $attributes . '>' . wp_kses_post( $value ) . ''; } return $value; + } /** From 25b8871faab9fbdb7513b9ef44a8446880302a75 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Sat, 22 Apr 2017 18:32:37 +0200 Subject: [PATCH 62/96] Fix geo lookup location button --- components/Maps/ui/fields/map-google.php | 1 + 1 file changed, 1 insertion(+) diff --git a/components/Maps/ui/fields/map-google.php b/components/Maps/ui/fields/map-google.php index 40963bc638..9092597b6f 100644 --- a/components/Maps/ui/fields/map-google.php +++ b/components/Maps/ui/fields/map-google.php @@ -374,6 +374,7 @@ function podsMergeAddressFields() { if ( fields.region.length ) { tmpAddress.push( fields.region.val() ); } if ( fields.country.length ) { tmpAddress.push( fields.country.val() ); } address = tmpAddress.join(', '); + return address; } function podsFormatFieldsToHTML( html ) { From bc0e18c7816c90c628dc434f6be7b71bcbb3b194 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Sat, 22 Apr 2017 18:53:32 +0200 Subject: [PATCH 63/96] Marker icon field input + improve var declaration --- components/Maps/ui/fields/map-google.php | 71 +++++++++++++----------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/components/Maps/ui/fields/map-google.php b/components/Maps/ui/fields/map-google.php index 9092597b6f..c3949ea811 100644 --- a/components/Maps/ui/fields/map-google.php +++ b/components/Maps/ui/fields/map-google.php @@ -63,41 +63,42 @@ class="pods-maps-map-canvas pods--map if ( typeof google !== 'undefined' ) { - var fieldId = ''; - var fieldType = ''; - var mapCanvas = document.getElementById( '' ); - var geocodeButton = $( '#' ); - - var fields = { - line_1: $( '#' ), - line_2: $( '#' ), - city: $( '#' ), - postal_code: $( '#' ), - region: $( '#' ), - country: $( '#' ), - text: $( '#' ), - info_window: $( '#' ), - lat: $( '#' ), - lng: $( '#' ) - }; + var fieldId = '', + fieldType = '', + mapCanvas = document.getElementById( '' ), + geocodeButton = $( '#' ), + + fields = { + line_1: $( '#' ), + line_2: $( '#' ), + city: $( '#' ), + postal_code: $( '#' ), + region: $( '#' ), + country: $( '#' ), + text: $( '#' ), + info_window: $( '#' ), + lat: $( '#' ), + lng: $( '#' ) + }, // @todo check pregreplace, maybe this can be done better (nl2br not working) // @todo check field type - var fieldsFormat = '', pods_v( 'address_display_type_custom', $options ) ) ); ?>'; - - var map = null; - var marker = null; - var infowindow = ; - var infowindowContent = ''; - var infowindowEditor = ''; - var geocoder = null; - var address = null; - var latlng = null; - var mapOptions = { - center: new google.maps.LatLng( 41.850033, -87.6500523 ), // default (Chicago) - marker: '', - zoom: , - type: '' - }; + fieldsFormat = '', pods_v( 'address_display_type_custom', $options ) ) ); ?>', + + map = null, + marker = null, + marker_icon = , + infowindow = , + infowindowContent = '', + infowindowEditor = '', + geocoder = null, + address = null, + latlng = null, + mapOptions = { + center: new google.maps.LatLng( 41.850033, -87.6500523 ), // default (Chicago) + marker: '', + zoom: , + type: '' + }; //------------------------------------------------------------------------ // Initialze the map @@ -213,6 +214,10 @@ function podsMapsMarker() { draggable : true }; + if ( marker_icon ) { + markerOptions.icon = marker_icon; + } + // Create a new marker, if needed, and set the event listeners if ( ! marker ) { marker = new google.maps.Marker( markerOptions ); From 8df69886d7a217578fa49020319d2076038029f9 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Sat, 22 Apr 2017 18:57:15 +0200 Subject: [PATCH 64/96] Marker icon display --- components/Maps/ui/front/map-google.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/Maps/ui/front/map-google.php b/components/Maps/ui/front/map-google.php index 25472196f2..21f883c0f0 100644 --- a/components/Maps/ui/front/map-google.php +++ b/components/Maps/ui/front/map-google.php @@ -55,7 +55,8 @@ class="pods-address-maps-map-canvas" marker: '', zoom: , type: '' - }; + }, + marker_icon = ; if ( value ) { try { @@ -86,6 +87,11 @@ class="pods-address-maps-map-canvas" position: latlng, draggable: false }; + + if ( marker_icon ) { + markerOptions.icon = marker_icon; + } + var marker = new google.maps.Marker( markerOptions ); map.setCenter( mapOptions.center ); From f11511d9fd29c8289d0a393d900f3d6c95873799 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Sat, 22 Apr 2017 19:07:14 +0200 Subject: [PATCH 65/96] Fix map type display --- components/Maps/ui/fields/map-google.php | 2 +- components/Maps/ui/front/map-google.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/Maps/ui/fields/map-google.php b/components/Maps/ui/fields/map-google.php index c3949ea811..ef51fad94f 100644 --- a/components/Maps/ui/fields/map-google.php +++ b/components/Maps/ui/fields/map-google.php @@ -97,7 +97,7 @@ class="pods-maps-map-canvas pods--map center: new google.maps.LatLng( 41.850033, -87.6500523 ), // default (Chicago) marker: '', zoom: , - type: '' + mapTypeId: '' }; //------------------------------------------------------------------------ diff --git a/components/Maps/ui/front/map-google.php b/components/Maps/ui/front/map-google.php index 21f883c0f0..a3d5c94a57 100644 --- a/components/Maps/ui/front/map-google.php +++ b/components/Maps/ui/front/map-google.php @@ -54,7 +54,7 @@ class="pods-address-maps-map-canvas" center: new google.maps.LatLng( 41.850033, -87.6500523 ), // default (Chicago) marker: '', zoom: , - type: '' + mapTypeId: '' }, marker_icon = ; From e1b5eb776952afa9e6d4f2c36afde0a4073c713a Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Sat, 22 Apr 2017 19:07:25 +0200 Subject: [PATCH 66/96] Set infowindow triggers on display field --- components/Maps/ui/front/map-google.php | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/components/Maps/ui/front/map-google.php b/components/Maps/ui/front/map-google.php index a3d5c94a57..463ede1343 100644 --- a/components/Maps/ui/front/map-google.php +++ b/components/Maps/ui/front/map-google.php @@ -103,9 +103,27 @@ class="pods-address-maps-map-canvas" infowindowContent = podsFormatFieldsToHTML( value.info_window, value.address ); } var infowindow = new google.maps.InfoWindow(); + podsMapsInfoWindow( true ); - infowindow.setContent( infowindowContent ); - infowindow.open( map, marker ); + function podsMapsInfoWindow( open ) { + + if ( ! infowindow ) { + infowindow = new google.maps.InfoWindow(); + } + + infowindow.setContent( infowindowContent ); + if ( open ) { + infowindow.open( map, marker ); + } + } + + + // InfoWindow trigger + if ( infowindow !== false ) { + google.maps.event.addListener( marker, 'click', function () { + podsMapsInfoWindow( true ); + } ); + } //------------------------------------------------------------------------ // Helpers From c8021381eb31017bf4587d2d7d5554a5e798bc28 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Sat, 22 Apr 2017 19:57:53 +0200 Subject: [PATCH 67/96] Add filters to add your own custom maps provider --- components/Maps/Maps.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index 24662d8b08..7acfd82759 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -127,13 +127,31 @@ public function handler( $options ) { */ private function load_provider() { - switch ( self::$options['provider'] ) { + /** + * Let Pods use a custom provider. + * Return string should match the instance you return in `pods_component_maps_provider_{provider}` + * + * @param string $provider Provider name slug. + * @return string Custom provider name slug. + */ + $provider = (string) apply_filters( 'pods_component_maps_provider', self::$options['provider'] ); + + switch ( $provider ) { case 'google': if ( file_exists( plugin_dir_path( __FILE__ ) . 'Maps-Google.php' ) ) { include_once( plugin_dir_path( __FILE__ ) . 'Maps-Google.php' ); self::$provider = new Pods_Component_Maps_Google(); } break; + default: + /** + * Add your own maps API provider instance + * + * @param string $provider + * @return object Custom provider class instance. + */ + self::$provider = apply_filters( 'pods_component_maps_provider_' . $provider, self::$options['provider'] ); + break; } } From ee3288706ab3e1003fdaf1159fc0b1fc4307670f Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Sat, 22 Apr 2017 19:58:21 +0200 Subject: [PATCH 68/96] Add todo's for maps options. These can be different for each provider. --- components/Maps/Maps.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index 7acfd82759..4ed0bc7e99 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -212,6 +212,8 @@ public function ajax_handler() { */ public function options( $settings ) { + // @todo Put part of this in the maps provider class? This could be different between providers. + $options = array( 'provider' => array( 'label' => __( 'Maps Provider', 'pods' ), @@ -304,6 +306,8 @@ public function options( $settings ) { */ public function maps_options( $options, $type ) { + // @todo Put this in the maps provider class? This could be different between providers. + // Add lat/lng input type $options[ $type . '_type' ]['data']['lat-lng'] = __( 'Latitude / Longitude', 'pods' ); From e5926995ab9d0c743d96b6918a5cbfe976bed227 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Sat, 22 Apr 2017 20:09:16 +0200 Subject: [PATCH 69/96] Better method names for providers --- components/Maps/Maps-Google.php | 4 ++-- components/Maps/Maps.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/Maps/Maps-Google.php b/components/Maps/Maps-Google.php index cd1d40966a..245af2c4d8 100644 --- a/components/Maps/Maps-Google.php +++ b/components/Maps/Maps-Google.php @@ -18,7 +18,7 @@ public function assets() { } - public function pods_ui_field_view_extra() { + public function field_input_view() { $view = false; if ( ! empty( Pods_Component_Maps::$api_key ) ) { @@ -28,7 +28,7 @@ public function pods_ui_field_view_extra() { return $view; } - public function pods_ui_field_display_extra() { + public function field_display_view() { $view = false; if ( ! empty( Pods_Component_Maps::$api_key ) ) { diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index 4ed0bc7e99..ef7a7aaa4a 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -456,8 +456,8 @@ public function pods_ui_field_address_display_value( $output, $value, $view, $di $view = ''; $provider = get_class( self::$provider ); - if ( is_callable( array( $provider, 'pods_ui_field_display_extra' ) ) ) { - $view = self::$provider->pods_ui_field_display_extra(); + if ( is_callable( array( $provider, 'field_display_view' ) ) ) { + $view = self::$provider->field_display_view(); } if ( $view && file_exists( $view ) ) { @@ -495,8 +495,8 @@ public function pods_ui_field_address_input_view_extra( $view, $type, $name, $va if ( ! empty( $options['maps'] ) ) { $provider = get_class( self::$provider ); - if ( is_callable( array( $provider, 'pods_ui_field_view_extra' ) ) ) { - $view = self::$provider->pods_ui_field_view_extra(); + if ( is_callable( array( $provider, 'field_input_view' ) ) ) { + $view = self::$provider->field_input_view(); } if ( $view && file_exists( $view ) ) { From 43ac8d2f5500c933d14c30b4bdebd0d9c87777b3 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Sat, 22 Apr 2017 20:14:34 +0200 Subject: [PATCH 70/96] Improve code readability --- components/Maps/Maps-Google.php | 89 ++++++++++++++++----------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/components/Maps/Maps-Google.php b/components/Maps/Maps-Google.php index 245af2c4d8..689950bd6d 100644 --- a/components/Maps/Maps-Google.php +++ b/components/Maps/Maps-Google.php @@ -124,52 +124,53 @@ public static function get_address( $data ) { } if ( ! empty( $data['address_components'] ) ) { + return array(); + } - $address = array( - 'line_1' => array(), - 'line_2' => array(), - 'postal_code' => '', - 'city' => '', - 'region' => array(), - 'country' => '', - ); - - foreach ( $data['address_components'] as $component ) { - - switch ( $component['types'] ) { - case 'street_number': - $address['line_1'][1] = $component['long_name']; - break; - case 'route': - $address['line_1'][0] = $component['long_name']; - break; - case 'locality': - $address['city'] = $component['long_name']; - break; - case 'country': - $address['country'] = $component['long_name']; - break; - case 'postal_code': - $address['postal_code'] = $component['long_name']; - break; - case 'administrative_area_level_1': - case 'administrative_area_level_2': - case 'administrative_area_level_3': - $address['region'][] = $component['long_name']; - break; - } + $address = array( + 'line_1' => array(), + 'line_2' => array(), + 'postal_code' => '', + 'city' => '', + 'region' => array(), + 'country' => '', + ); + + foreach ( $data['address_components'] as $component ) { + + $value = $component['long_name']; + + switch ( $component['types'] ) { + case 'street_number': + $address['line_1'][1] = $value; + break; + case 'route': + $address['line_1'][0] = $value; + break; + case 'locality': + $address['city'] = $value; + break; + case 'country': + $address['country'] = $value; + break; + case 'postal_code': + $address['postal_code'] = $value; + break; + case 'administrative_area_level_1': + case 'administrative_area_level_2': + case 'administrative_area_level_3': + $address['region'][] = $value; + break; } + } - foreach ( $address as $key => $value ) { - if ( is_array( $value ) ) { - $address[ $key ] = implode( ' ', $value ); - } + foreach ( $address as $key => $value ) { + if ( is_array( $value ) ) { + $address[ $key ] = implode( ' ', $value ); } - - return $address; } - return array(); + return $address; } /** @@ -189,13 +190,11 @@ public static function get_latlng( $data ) { $data = $data['results'][0]; } - if ( ! empty( $data['geometry']['location'] ) ) { - $latlng = $data['geometry']['location']; - - return array_map( 'floatval', $latlng ); + if ( empty( $data['geometry']['location'] ) ) { + return array(); } - return array(); + return array_map( 'floatval', $data['geometry']['location'] ); } /** From b9d46ce31189e58fe40ab9ed1ee8349c334275e7 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Mon, 22 May 2017 20:50:40 +0200 Subject: [PATCH 71/96] Use pods_v() --- classes/fields/address.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/classes/fields/address.php b/classes/fields/address.php index 805a5c86b2..1a882524e5 100644 --- a/classes/fields/address.php +++ b/classes/fields/address.php @@ -365,14 +365,20 @@ public static function format_value_for_output( $value, $options ) { if ( ! empty( $value['address'] ) ) { foreach ( $value['address'] as $key => $val ) { - if ( $key == 'region' && $options[ self::$type . '_address_region_input' ] == 'pick' && $options[ self::$type . '_address_region_output' ] == 'long' ) { + if ( 'region' === $key && + 'pick' === pods_v( self::$type . '_address_region_input', $options ) && + 'long' === pods_v( self::$type . '_address_region_output', $options ) + ) { // Display full region names if enabled $regions = PodsForm::field_method( 'pick', 'data_us_states' ); if ( array_key_exists( $val, $regions ) ) { $value['address'][ $key ] = $regions[ $val ]; } - } elseif ( $key == 'country' && $options[ self::$type . '_address_country_input' ] == 'pick' && $options[ self::$type . '_address_country_output' ] == 'long' ) { + } elseif ( 'country' === $key && + 'pick' === pods_v( self::$type . '_address_country_input', $options ) && + 'long' === pods_v( self::$type . '_address_country_output', $options ) + ) { // Display full country names if enabled $countries = PodsForm::field_method( 'pick', 'data_countries' ); From 0afc08503bd00c713d474f04b52bca2767209e48 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Mon, 22 May 2017 20:51:04 +0200 Subject: [PATCH 72/96] Option to disable the scrollwheel + use pods_v() --- components/Maps/Maps.php | 10 ++++++++++ components/Maps/ui/front/map-google.php | 11 +++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index ef7a7aaa4a..582965b375 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -277,6 +277,11 @@ public function options( $settings ) { //'number_format_type' => 'slider' ) ), + 'map_scrollwheel' => array( + 'label' => __( 'Enable scroll wheel?', 'pods' ), + 'default' => 1, + 'type' => 'boolean', + ), 'map_marker' => array( 'label' => __( 'Default Map Custom Marker', 'pods' ), 'type' => 'file', @@ -386,6 +391,11 @@ public function maps_options( $options, $type ) { //'number_format_type' => 'slider' ) ); + $options['maps_scrollwheel'] = array( + 'label' => __( 'Enable scroll wheel?', 'pods' ), + 'default' => 1, + 'type' => 'boolean', + ); $options['maps_info_window'] = array( 'label' => __( 'Display an Info Window', 'pods' ), 'default' => 0, diff --git a/components/Maps/ui/front/map-google.php b/components/Maps/ui/front/map-google.php index 463ede1343..3e5f8fd19c 100644 --- a/components/Maps/ui/front/map-google.php +++ b/components/Maps/ui/front/map-google.php @@ -11,21 +11,23 @@ if ( ! empty( $options['maps_zoom'] ) ) { $map_options['zoom'] = (int) $options['maps_zoom']; } else { - $map_options['zoom'] = (int) Pods_Component_Maps::$options['map_zoom']; + $map_options['zoom'] = (int) pods_v( 'map_zoom', Pods_Component_Maps::$options ); } if ( ! empty( $options['maps_type'] ) ) { $map_options['type'] = $options['maps_type']; } else { - $map_options['type'] = Pods_Component_Maps::$options['map_type']; + $map_options['type'] = pods_v( 'map_type', Pods_Component_Maps::$options ); } if ( ! empty( $options['maps_marker'] ) ) { $map_options['marker'] = $options['maps_marker']; } else { - $map_options['marker'] = Pods_Component_Maps::$options['map_marker']; + $map_options['marker'] = pods_v( 'map_marker', Pods_Component_Maps::$options ); } +$map_options['scrollwheel'] = (bool) pods_v( 'maps_scrollwheel', $options, pods_v( 'map_scrollwheel', Pods_Component_Maps::$options, true ) ); + if ( ! empty( $map_options['marker'] ) ) { $map_options['marker'] = wp_get_attachment_image_url( $map_options['marker'], 'full' ); } @@ -54,7 +56,8 @@ class="pods-address-maps-map-canvas" center: new google.maps.LatLng( 41.850033, -87.6500523 ), // default (Chicago) marker: '', zoom: , - mapTypeId: '' + mapTypeId: '', + scrollwheel: }, marker_icon = ; From c79b0b427fed305d0475f902894b380566612376 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Tue, 23 May 2017 12:49:29 +0200 Subject: [PATCH 73/96] Refactor google maps display to work with multiple values. --- components/Maps/ui/front/map-google.php | 144 +++++++++++++----------- 1 file changed, 78 insertions(+), 66 deletions(-) diff --git a/components/Maps/ui/front/map-google.php b/components/Maps/ui/front/map-google.php index 3e5f8fd19c..3a9d4cf08c 100644 --- a/components/Maps/ui/front/map-google.php +++ b/components/Maps/ui/front/map-google.php @@ -1,4 +1,11 @@ $val ) { + if ( ! isset( $address_html ) || $multiple ) { + // @todo Check field type + if ( ! empty( $val['info_window'] ) ) { + $format = $val['info_window']; + } else { + $format = PodsForm::field_method( 'address', 'default_display_format' ); + if ( 'custom' === pods_v( 'address_display_type', $options ) ) { + $format = pods_v( 'address_display_type_custom', $options ); + } + } + $address_html = PodsForm::field_method( 'address', 'format_to_html', $format, $val, $options ); } - $address_html = PodsForm::field_method( 'address', 'format_to_html', $format, $value, $options ); + + unset( $value[ $key ]['info_window'] ); + $value[ $key ]['address_html'] = $address_html; } -$value['address_html'] = $address_html; ?>
' ); - $.each( lines, function( key, line ) { - if ( line === '{{REMOVE}}' ) { - // Delete the key it this line only has {{REMOVE}} - delete lines[ key ]; - } else { - // Remove {{REMOVE}} - lines[ key ] = line.replace('{{REMOVE}}', '') + // InfoWindow trigger + google.maps.event.addListener( value[ i ].marker, 'click', function () { + value[ i ].infowindowOpen( true ); + } ); } - } ); - // Reset array keys and join it back together - html = lines.filter(function(){return true;}).join( '
' ); - return html; + } + + } ); + + if ( 1 < value.length ) { + + // Automatic sizing for multiple markers. + map.fitBounds( bounds ); + map.panToBounds( bounds ); + mapOptions.center = map.getCenter(); + + //(optional) restore the zoom level after the map is done scaling + /*var listener = google.maps.event.addListener( map, "idle", function () { + map.setZoom( mapOptions.zoom ); + google.maps.event.removeListener( listener ); + } );*/ + + } else { + map.setCenter( mapOptions.center ); } } ); // end document ready From b8c0858ea9ab09fd0190ecb9d092511e3d9783bb Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Tue, 23 May 2017 18:40:24 +0200 Subject: [PATCH 74/96] Typo --- classes/fields/address.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/classes/fields/address.php b/classes/fields/address.php index 1a882524e5..042fca101b 100644 --- a/classes/fields/address.php +++ b/classes/fields/address.php @@ -1,5 +1,4 @@ 'pick', 'data' => array( 'long' => __( 'Full name', 'pods' ), - 'short' => __( 'Stage / Province code', 'pods' ) + 'short' => __( 'State / Province code', 'pods' ) ) ), self::$type . '_address_country_input' => array( From 9357d7a47f251584443e64daf84edf454a82ab43 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Tue, 23 May 2017 18:41:04 +0200 Subject: [PATCH 75/96] Allow values to overwrite the field marker --- components/Maps/ui/front/map-google.php | 36 ++++++++++++++++--------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/components/Maps/ui/front/map-google.php b/components/Maps/ui/front/map-google.php index 3a9d4cf08c..27c4e27d9b 100644 --- a/components/Maps/ui/front/map-google.php +++ b/components/Maps/ui/front/map-google.php @@ -60,6 +60,10 @@ unset( $value[ $key ]['info_window'] ); $value[ $key ]['address_html'] = $address_html; + + if ( ! empty( $val['marker_icon'] ) && is_numeric( $val['marker_icon'] ) ) { + $value[ $key ]['marker_icon'] = wp_get_attachment_image_url( $val['marker_icon'], 'full' ); + } } ?> @@ -104,26 +108,32 @@ class="pods-address-maps-map-canvas" //var geocoder = new google.maps.Geocoder(); //------------------------------------------------------------------------ - // Basic marker options. + // Add the items. // - var markerOptions = { - map : map, - draggable: false - }; - if ( marker_icon ) { - markerOptions.icon = marker_icon; - } var autoOpenInfoWindow = ( 1 === value.length ); - - // Add the markers $.each( value, function( i, val ) { if ( value[ i ].hasOwnProperty('geo') ) { - markerOptions.position = value[ i ].geo; + + //------------------------------------------------------------------------ + // Initialize marker. + // + value[ i ].markerOptions = { + map : map, + draggable: false + }; + + value[ i ].markerOptions.position = value[ i ].geo; + + if ( value[ i ].hasOwnProperty('marker_icon') ) { + value[ i ].markerOptions.icon = value[ i ].marker_icon; + } else if ( marker_icon ) { + value[ i ].markerOptions.icon = marker_icon; + } // Add the marker. - value[ i ].marker = new google.maps.Marker( markerOptions ); - bounds.extend( markerOptions.position ); + value[ i ].marker = new google.maps.Marker( value[ i ].markerOptions ); + bounds.extend( value[ i ].markerOptions.position ); //------------------------------------------------------------------------ // Initialize info window. From a24e6da32cfc2e611c7e9be5380144f1df887d41 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Tue, 23 May 2017 18:41:16 +0200 Subject: [PATCH 76/96] Ensure geodata is passed as float values --- components/Maps/ui/front/map-google.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/Maps/ui/front/map-google.php b/components/Maps/ui/front/map-google.php index 27c4e27d9b..e39d924084 100644 --- a/components/Maps/ui/front/map-google.php +++ b/components/Maps/ui/front/map-google.php @@ -64,6 +64,10 @@ if ( ! empty( $val['marker_icon'] ) && is_numeric( $val['marker_icon'] ) ) { $value[ $key ]['marker_icon'] = wp_get_attachment_image_url( $val['marker_icon'], 'full' ); } + + if ( ! empty( $val['geo'] ) && is_array( $val['geo'] ) ) { + $value[ $key ]['geo'] = array_map( 'floatval', $val['geo'] ); + } } ?> From c018f0e4ded15ed9c34a323e90d11ad74fb71883 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Wed, 6 Dec 2017 22:29:47 +0100 Subject: [PATCH 77/96] Maps provider interface --- components/Maps/Maps-Google.php | 2 +- components/Maps/Maps-Provider.php | 98 +++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 components/Maps/Maps-Provider.php diff --git a/components/Maps/Maps-Google.php b/components/Maps/Maps-Google.php index 689950bd6d..658be602e0 100644 --- a/components/Maps/Maps-Google.php +++ b/components/Maps/Maps-Google.php @@ -1,6 +1,6 @@ value, 'lng' => value ) ) + * + * @public + * @static + * @since 2.x + */ + public static function geocode_address( $data, $api_key = '' ); + + /** + * Geocode an address into Latitude and Longitude values + * + * @param string|array $address Address + * @param string $api_key + * + * @return array Latitude, Longitude (format: array( 'lat' => value, 'lng' => value ) ) + * + * @public + * @static + * @since 2.x + */ + public static function geocode_address_to_latlng( $address, $api_key = '' ); + + /** + * Get address data from Latitude and Longitude values + * + * @param string|array $lat_lng Lat / long numbers + * @param string $api_key + * + * @return string Address information + * + * @public + * @static + * @since 2.x + */ + public static function geocode_latlng_to_address( $lat_lng, $api_key = '' ); + +} From 5e8db12405d10781eb5541b7d537aecaf920ed1d Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Wed, 6 Dec 2017 22:31:02 +0100 Subject: [PATCH 78/96] Move provider related map options into the provider class --- components/Maps/Maps-Google.php | 200 ++++++++++++++++++++++++++++++++ components/Maps/Maps.php | 171 +++------------------------ 2 files changed, 215 insertions(+), 156 deletions(-) diff --git a/components/Maps/Maps-Google.php b/components/Maps/Maps-Google.php index 658be602e0..82cc34c8a3 100644 --- a/components/Maps/Maps-Google.php +++ b/components/Maps/Maps-Google.php @@ -18,6 +18,206 @@ public function assets() { } + /** + * Add options to the maps component. + * @inheritdoc + */ + public function options( $options = array() ) { + + $options['api_key'] = array( + 'label' => __( 'Maps API Key or Client ID', 'pods' ), + 'help' => __( 'help', 'pods' ), + 'default' => '', + 'type' => 'text', + ); + + /*$options['google_client_id'] = array( + 'label' => __( 'Google Maps Client ID', 'pods' ), + 'help' => __( 'For use with Google Maps API for Business and Geocoding; A Client ID does not come with the Free edition.', 'pods' ), + 'includes-on' => array( 'provider' => 'google' ), + 'default' => '', + 'type' => 'text', + );*/ + + $options['maps_style'] = array( + 'label' => __( 'Default Map Output Type', 'pods' ), + 'default' => 'static', + 'type' => 'pick', + 'data' => array( + 'static' => __( 'Static (Image)', 'pods' ), + 'js' => __( 'Javascript (Interactive)', 'pods' ), + ), + ); + + $options['maps_type'] = array( + 'label' => __( 'Default Map Type', 'pods' ), + 'default' => 'roadmap', + 'type' => 'pick', + 'data' => array( + 'roadmap' => __( 'Roadmap', 'pods' ), + 'satellite' => __( 'Satellite', 'pods' ), + 'terrain' => __( 'Terrain', 'pods' ), + 'hybrid' => __( 'Hybrid', 'pods' ), + ), + ); + + $options['maps_zoom'] = array( + 'label' => __( 'Default Map Zoom Level', 'pods' ), + 'help' => array( + __( 'Google Maps has documentation on the different zoom levels you can use.', 'pods' ), + 'https://developers.google.com/maps/documentation/javascript/tutorial#zoom-levels', + //'https://developers.google.com/maps/documentation/staticmaps/#Zoomlevels' + ), + 'default' => 12, + 'type' => 'number', + 'options' => array( + 'number_decimals' => 0, // 2 + 'number_max_length' => 2, + 'number_min' => 1, + 'number_max' => 21, + 'number_format' => '9999.99', + //'number_format_type' => 'slider', + ), + ); + + $options['maps_scrollwheel'] = array( + 'label' => __( 'Enable scrollwheel?', 'pods' ), + 'default' => 1, + 'type' => 'boolean', + ); + + $options['maps_marker'] = array( + 'label' => __( 'Default Map Custom Marker', 'pods' ), + 'type' => 'file', + 'options' => array( + 'file_uploader' => 'plupload', + 'file_edit_title' => 0, + 'file_restrict_filesize' => '1MB', + 'file_type' => 'images', + 'file_add_button' => __( 'Upload Marker Icon', 'pods' ), + ), + ); + + return $options; + } + + /** + * Add options to the maps fields. + * @inheritdoc + */ + public function field_options( $options = array(), $type = '' ) { + + $options['maps_style'] = array( + 'label' => __( 'Map Output Type', 'pods' ), + 'depends-on' => array( 'maps' => true ), + 'default' => pods_v( 'maps_style', Pods_Component_Maps::$options, 'static', true ), + 'type' => 'pick', + 'data' => array( + 'static' => __( 'Static (Image)', 'pods' ), + 'js' => __( 'Javascript (Interactive)', 'pods' ), + ), + ); + + $options['maps_type'] = array( + 'label' => __( 'Map Type', 'pods' ), + 'depends-on' => array( 'maps' => true ), + 'default' => pods_v( 'maps_type', Pods_Component_Maps::$options, 'roadmap', true ), + 'type' => 'pick', + 'data' => array( + 'roadmap' => __( 'Roadmap', 'pods' ), + 'satellite' => __( 'Satellite', 'pods' ), + 'terrain' => __( 'Terrain', 'pods' ), + 'hybrid' => __( 'Hybrid', 'pods' ), + ), + ); + + $options['maps_zoom'] = array( + 'label' => __( 'Map Zoom Level', 'pods' ), + 'depends-on' => array( 'maps' => true ), + 'help' => array( + __( 'Google Maps has documentation on the different zoom levels you can use.', 'pods' ), + 'https://developers.google.com/maps/documentation/javascript/tutorial#zoom-levels', + //'https://developers.google.com/maps/documentation/staticmaps/#Zoomlevels' + ), + 'default' => pods_v( 'maps_zoom', Pods_Component_Maps::$options, 12, true ), + 'type' => 'number', + 'options' => array( + 'number_decimals' => 0, // 2 + 'number_max_length' => 2, + 'number_min' => 1, + 'number_max' => 21, + 'number_format' => '9999.99', + //'number_format_type' => 'slider', + ), + ); + + $options['maps_scrollwheel'] = array( + 'label' => __( 'Enable scroll wheel?', 'pods' ), + 'default' => 1, + 'type' => 'boolean', + ); + + $options['maps_info_window'] = array( + 'label' => __( 'Display an Info Window', 'pods' ), + 'default' => 0, + 'type' => 'boolean', + 'depends-on' => array( 'maps' => true ), + 'dependency' => true, + ); + + $options['maps_info_window_content'] = array( + 'label' => __( 'Info Window content', 'pods' ), + 'depends-on' => array( + 'maps' => true, + 'maps_info_window' => true, + ), + 'default' => 'paragraph', + 'type' => 'pick', + 'data' => array( + 'paragraph' => __( 'Custom', 'pods' ), + 'wysiwyg' => __( 'Custom (WYSIWYG)', 'pods' ), + // @todo 'display_type' is only available for field type 'address' + 'display_type' => __( 'Display Type', 'pods' ), + ), + ); + + if ( pods_components()->is_component_active( 'templates' ) ) { + //$titles = (array) $this->get_template_titles(); + //if ( ! empty( $title ) ) { + $options['maps_info_window_content']['data']['template'] = __( 'Template', 'pods' ); + $options['maps_info_window_template'] = array( + 'label' => __( 'Info Window template', 'pods' ), + 'depends-on' => array( + 'maps' => true, + 'maps_info_window' => true, + 'maps_info_window_content' => 'template', + ), + 'default' => 'true', + 'type' => 'pick', + 'data' => (array) Pods_Component_Maps::get_template_titles(),//array_combine( $titles, $titles ), + 'pick_format_type' => 'single', + 'pick_format_single' => 'dropdown', + ); + //} + } + + $options['maps_marker'] = array( + 'label' => __( 'Map Custom Marker', 'pods' ), + 'depends-on' => array( 'maps' => true ), + 'default' => pods_v( 'maps_marker', Pods_Component_Maps::$options ), + 'type' => 'file', + 'options' => array( + 'file_uploader' => 'plupload', + 'file_edit_title' => 0, + 'file_restrict_filesize' => '1MB', + 'file_type' => 'images', + 'file_add_button' => 'Upload Marker Icon', + ), + ); + + return $options; + } + public function field_input_view() { $view = false; diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index 582965b375..9f66180662 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -123,7 +123,7 @@ public function handler( $options ) { /** * Load the selected provider * - * @since 2.7 + * @since 2.x */ private function load_provider() { @@ -212,8 +212,6 @@ public function ajax_handler() { */ public function options( $settings ) { - // @todo Put part of this in the maps provider class? This could be different between providers. - $options = array( 'provider' => array( 'label' => __( 'Maps Provider', 'pods' ), @@ -227,74 +225,12 @@ public function options( $settings ) { ) ), 'dependency' => true ), - 'api_key' => array( - 'label' => __( 'Maps API Key', 'pods' ), - 'help' => __( 'help', 'pods' ), - 'default' => '', - 'type' => 'text' - ), - 'google_client_id' => array( - 'label' => __( 'Google Maps Client ID', 'pods' ), - 'help' => __( 'For use with Google Maps API for Business and Geocoding; A Client ID does not come with the Free edition.', 'pods' ), - 'includes-on' => array( 'provider' => 'google' ), - 'default' => '', - 'type' => 'text' - ), - 'map_style' => array( - 'label' => __( 'Default Map Output Type', 'pods' ), - 'default' => 'static', - 'type' => 'pick', - 'data' => array( - 'static' => __( 'Static (Image)', 'pods' ), - 'js' => __( 'Javascript (Interactive)', 'pods' ) - ) - ), - 'map_type' => array( - 'label' => __( 'Default Map Type', 'pods' ), - 'default' => 'roadmap', - 'type' => 'pick', - 'data' => array( - 'roadmap' => __( 'Roadmap', 'pods' ), - 'satellite' => __( 'Satellite', 'pods' ), - 'terrain' => __( 'Terrain', 'pods' ), - 'hybrid' => __( 'Hybrid', 'pods' ) - ) - ), - 'map_zoom' => array( - 'label' => __( 'Default Map Zoom Level', 'pods' ), - 'help' => array( - __( 'Google Maps has documentation on the different zoom levels you can use.', 'pods' ), - 'https://developers.google.com/maps/documentation/staticmaps/#Zoomlevels' - ), - 'default' => 12, - 'type' => 'number', - 'options' => array( - 'number_decimals' => 0, // 2 - 'number_max_length' => 2, - 'number_min' => 1, - 'number_max' => 21, - 'number_format' => '9999.99', - //'number_format_type' => 'slider' - ) - ), - 'map_scrollwheel' => array( - 'label' => __( 'Enable scroll wheel?', 'pods' ), - 'default' => 1, - 'type' => 'boolean', - ), - 'map_marker' => array( - 'label' => __( 'Default Map Custom Marker', 'pods' ), - 'type' => 'file', - 'options' => array( - 'file_uploader' => 'plupload', - 'file_edit_title' => 0, - 'file_restrict_filesize' => '1MB', - 'file_type' => 'images', - 'file_add_button' => __( 'Upload Marker Icon', 'pods' ), - ) - ) ); + if ( is_callable( array( self::$provider, 'options' ) ) ) { + $options = self::$provider->options( $options ); + } + return $options; } @@ -307,11 +243,9 @@ public function options( $settings ) { * * @return array * - * @since 2.7 + * @since 2.x */ - public function maps_options( $options, $type ) { - - // @todo Put this in the maps provider class? This could be different between providers. + public function field_options( $options, $type ) { // Add lat/lng input type $options[ $type . '_type' ]['data']['lat-lng'] = __( 'Latitude / Longitude', 'pods' ); @@ -347,91 +281,15 @@ public function maps_options( $options, $type ) { 'data' => array( 'replace' => __( 'Replace default display', 'pods' ), 'before' => __( 'Before default display', 'pods' ), - 'after' => __( 'After default display', 'pods' ) - ) - ); - $options[ $type . '_map_style' ] = array( - 'label' => __( 'Map Output Type', 'pods' ), - 'depends-on' => array( 'maps' => true ), - 'default' => pods_v( 'maps_style', self::$options, 'static', true ), - 'type' => 'pick', - 'data' => array( - 'static' => __( 'Static (Image)', 'pods' ), - 'js' => __( 'Javascript (Interactive)', 'pods' ) - ) - ); - $options['maps_type'] = array( - 'label' => __( 'Map Type', 'pods' ), - 'depends-on' => array( 'maps' => true ), - 'default' => pods_v( 'maps_type', self::$options, 'roadmap', true ), - 'type' => 'pick', - 'data' => array( - 'roadmap' => __( 'Roadmap', 'pods' ), - 'satellite' => __( 'Satellite', 'pods' ), - 'terrain' => __( 'Terrain', 'pods' ), - 'hybrid' => __( 'Hybrid', 'pods' ) - ) - ); - $options['maps_zoom'] = array( - 'label' => __( 'Map Zoom Level', 'pods' ), - 'depends-on' => array( 'maps' => true ), - 'help' => array( - __( 'Google Maps has documentation on the different zoom levels you can use.', 'pods' ), - 'https://developers.google.com/maps/documentation/javascript/tutorial#zoom-levels' - //'https://developers.google.com/maps/documentation/staticmaps/#Zoomlevels' - ), - 'default' => pods_v( 'maps_zoom', self::$options, 12, true ), - 'type' => 'number', - 'options' => array( - 'number_decimals' => 0, // 2 - 'number_max_length' => 2, - 'number_min' => 1, - 'number_max' => 21, - 'number_format' => '9999.99', - //'number_format_type' => 'slider' - ) - ); - $options['maps_scrollwheel'] = array( - 'label' => __( 'Enable scroll wheel?', 'pods' ), - 'default' => 1, - 'type' => 'boolean', - ); - $options['maps_info_window'] = array( - 'label' => __( 'Display an Info Window', 'pods' ), - 'default' => 0, - 'type' => 'boolean', - 'depends-on' => array( 'maps' => true ), - 'dependency' => true - ); - $options['maps_info_window_content'] = array( - 'label' => __( 'Info Window content', 'pods' ), - 'depends-on' => array( - 'maps' => true, - 'maps_info_window' => true - ), - 'default' => 'paragraph', - 'type' => 'pick', - 'data' => array( - 'paragraph' => __( 'Custom', 'pods' ), - 'wysiwyg' => __( 'Custom (WYSIWYG)', 'pods' ), - // @todo 'display_type' is only available for field type 'address' - 'display_type' => __( 'Display Type', 'pods' ) - ) - ); - $options['maps_marker'] = array( - 'label' => __( 'Map Custom Marker', 'pods' ), - 'depends-on' => array( 'maps' => true ), - 'default' => pods_v( 'maps_marker', self::$options ), - 'type' => 'file', - 'options' => array( - 'file_uploader' => 'plupload', - 'file_edit_title' => 0, - 'file_restrict_filesize' => '1MB', - 'file_type' => 'images', - 'file_add_button' => 'Upload Marker Icon' + 'after' => __( 'After default display', 'pods' ), + 'admin' => __( 'Admin only', 'pods' ), ) ); + if ( is_callable( array( self::$provider, 'field_options' ) ) ) { + $options = self::$provider->field_options( $options, $type ); + } + // Add option dependencies /*if ( empty( $options[ $type . '_display_type_custom' ]['depends-on'][ $type . '_display_type' ] ) ) { $options[ $type . '_display_type_custom' ]['depends-on'][ $type . '_display_type' ] = array( 'custom-map' ); @@ -441,7 +299,8 @@ public function maps_options( $options, $type ) { 'custom-map' ); }*/ - $options['maps_microdata']['excludes-on']['maps'] = true; + + //$options['maps_microdata']['excludes-on']['maps'] = true; return $options; } From 1f77bb269c8596ead5bff81b2e65cc64db443873 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Wed, 6 Dec 2017 22:31:14 +0100 Subject: [PATCH 79/96] Get template titles helper --- components/Maps/Maps.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index 9f66180662..8481dc4f79 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -305,6 +305,30 @@ public function field_options( $options, $type ) { return $options; } + /** + * Get titles of all Pods Templates + * + * @return string[] Array of template names + * + * @since 2.x + */ + public static function get_template_titles() { + + static $template_titles; + + if ( empty( $template_titles ) ) { + $all_templates = (array) pods_api()->load_templates( array() ); + + $template_titles = array(); + foreach ( $all_templates as $template ) { + $template_titles[ $template['id'] ] = $template['name']; + } + } + + return $template_titles; + + } + /** * Add/Change the display value * From 58e5ce30b4af96543300f0dc562bbb5280af2804 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Wed, 6 Dec 2017 22:32:57 +0100 Subject: [PATCH 80/96] Autocomplete option + filter --- components/Maps/Maps.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index 8481dc4f79..7cecee1650 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -453,14 +453,29 @@ public function pods_ui_field_address_validate( $errors, $value, $type, $name, $ */ public function pods_ui_field_address_pre_save( $value, $type, $id, $name, $options, $fields, $pod, $params ) { + $org_value = $value; + // Get geocode from address fields if ( isset( $value['address'] ) ) { - $geocode = self::geocode_address_to_latlng( $value['address'] ); + $geocode = array(); + if ( pods_v( 'maps_autocorrect', $options, 0 ) ) { + $address = self::geocode_address( $value['address'] ); + if ( ! empty( $address['address'] ) ) { + $value['address'] = $address['address']; + } + if ( ! empty( $address['geo'] ) ) { + $geocode = $address['geo']; + } + } else { + $geocode = self::geocode_address_to_latlng( $value['address'] ); + } if ( isset( $geocode['lat'] ) && isset( $geocode['lng'] ) ) { $value['geo'] = $geocode; } } + $value = apply_filters( 'pods_ui_field_address_maps_pre_save', $value, $org_value, $type, $id, $name, $options, $fields, $pod, $params ); + return $value; } From ce65b7327ac4d195a9b6d00b62f32b9fdc12f94b Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Wed, 6 Dec 2017 22:33:10 +0100 Subject: [PATCH 81/96] Enhance error handling --- components/Maps/Maps.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index 7cecee1650..02af35b99d 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -425,8 +425,13 @@ public function pods_ui_field_address_validate( $errors, $value, $type, $name, $ // @todo: Validate based on address type ( lat / lon, address fields) + if ( ! $value ) { + return $errors; + } + // Get geocode from address fields if ( isset( $value['address'] ) ) { + // @todo: What to do if Google doesn't respond? $geocode = self::geocode_address_to_latlng( $value['address'] ); if ( empty( $geocode['lat'] ) && empty( $geocode['lng'] ) ) { $errors[] = __( 'Could not find geodata for this address', 'pods' ); From 8db630a9af972c91c70c78269b6781cfa870d933 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Wed, 6 Dec 2017 22:33:23 +0100 Subject: [PATCH 82/96] autocorrect option setting --- components/Maps/Maps.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index 02af35b99d..c091e081ec 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -263,8 +263,7 @@ public function field_options( $options, $type ) { 'type' => 'boolean', 'dependency' => true ); - // @todo Autocorrect is not implemented yet - /*$options[ 'maps_autocorrect' ] = array( + $options['maps_autocorrect'] = array( 'label' => __( 'Autocorrect Address during save', 'pods' ), 'depends-on' => array( 'maps' => true, @@ -272,7 +271,7 @@ public function field_options( $options, $type ) { ), 'default' => 0, 'type' => 'boolean' - );*/ + ); $options['maps_display'] = array( 'label' => __( 'Map Display', 'pods' ), 'depends-on' => array( 'maps' => true ), From 7f3496fa0da83574694378bd18d0c511656fec80 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Wed, 6 Dec 2017 22:33:31 +0100 Subject: [PATCH 83/96] Fix method name --- components/Maps/Maps.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index c091e081ec..a5bf27720a 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -26,7 +26,7 @@ class Pods_Component_Maps extends PodsComponent { public function __construct() { // See https://github.com/pods-framework/pods/pull/3711 - add_filter( 'pods_admin_setup_edit_address_additional_field_options', array( $this, 'maps_options' ), 10, 2 ); + add_filter( 'pods_admin_setup_edit_address_additional_field_options', array( $this, 'field_options' ), 10, 2 ); // Add Maps input // do_action( 'pods_ui_field_address_input_view_extra', $view, $type, $name, $value, $options, $pod, $id ); From 89b3463c82dc10dd610c218ac09f2cf0762c173b Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Wed, 6 Dec 2017 22:33:58 +0100 Subject: [PATCH 84/96] Check display setting, use pods_v & phpdoc --- components/Maps/Maps.php | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/components/Maps/Maps.php b/components/Maps/Maps.php index a5bf27720a..bef41d4354 100644 --- a/components/Maps/Maps.php +++ b/components/Maps/Maps.php @@ -15,16 +15,44 @@ */ class Pods_Component_Maps extends PodsComponent { + /** + * @var string + */ static $component_path; + + /** + * @var string + */ static $component_file; + + /** + * @var array + */ static $options; + + /** + * @var \Pods_Component_Maps_Provider + */ static $provider; + + /** + * @var string + */ static $api_key = ''; + /** + * @var string + */ + static $dir = ''; + private static $nonce = 'pods_maps'; public function __construct() { + self::$dir = plugin_dir_path( __FILE__ ); + + include_once( self::$dir . 'Maps-Provider.php' ); + // See https://github.com/pods-framework/pods/pull/3711 add_filter( 'pods_admin_setup_edit_address_additional_field_options', array( $this, 'field_options' ), 10, 2 ); @@ -83,7 +111,7 @@ public function global_assets() { ) ); // @todo Allways load required front end assets (Maybe as an option?) - // Enqueue doesn't work in the display function anymore (hook is already fires before that) + // Enqueue doesn't work in the display function anymore (hook is already fires before that) self::$provider->assets(); } @@ -344,7 +372,7 @@ public static function get_template_titles() { */ public function pods_ui_field_address_display_value( $output, $value, $view, $display_type, $name, $options, $pod, $id ) { - if ( ! empty( $options['maps'] ) ) { + if ( pods_v( 'maps', $options ) && 'admin' !== pods_v( 'maps_display', $options ) ) { $view = ''; $provider = get_class( self::$provider ); @@ -385,7 +413,7 @@ public function pods_ui_field_address_display_value( $output, $value, $view, $di */ public function pods_ui_field_address_input_view_extra( $view, $type, $name, $value, $options, $pod, $id ) { - if ( ! empty( $options['maps'] ) ) { + if ( pods_v( 'maps', $options ) ) { $provider = get_class( self::$provider ); if ( is_callable( array( $provider, 'field_input_view' ) ) ) { $view = self::$provider->field_input_view(); From ca1fc1205bf57050c577343d83ff447140fca07c Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Wed, 6 Dec 2017 22:34:23 +0100 Subject: [PATCH 85/96] Only return string values for address parts --- components/Maps/Maps-Google.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/Maps/Maps-Google.php b/components/Maps/Maps-Google.php index 82cc34c8a3..866163882b 100644 --- a/components/Maps/Maps-Google.php +++ b/components/Maps/Maps-Google.php @@ -275,6 +275,11 @@ public static function geocode_address( $data, $api_key = '' ) { public static function geocode_address_to_latlng( $address, $api_key = '' ) { if ( is_array( $address ) ) { + foreach ( $address as $key => $val ) { + if ( is_array( $val ) ) { + $address[ $key ] = implode( ', ', $val ); + } + } $address = implode( ', ', $address ); } From d949a24a3caf852d176f3e3e21c0ecf86974146f Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Wed, 6 Dec 2017 22:35:10 +0100 Subject: [PATCH 86/96] Google address data handler enhancements + filter --- components/Maps/Maps-Google.php | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/components/Maps/Maps-Google.php b/components/Maps/Maps-Google.php index 866163882b..b482c53440 100644 --- a/components/Maps/Maps-Google.php +++ b/components/Maps/Maps-Google.php @@ -328,7 +328,7 @@ public static function get_address( $data ) { $data = $data['results'][0]; } - if ( ! empty( $data['address_components'] ) ) { + if ( empty( $data['address_components'] ) ) { return array(); } @@ -345,7 +345,8 @@ public static function get_address( $data ) { $value = $component['long_name']; - switch ( $component['types'] ) { + // https://developers.google.com/maps/documentation/javascript/geocoding#GeocodingAddressTypes + switch ( $component['types'][0] ) { case 'street_number': $address['line_1'][1] = $value; break; @@ -364,11 +365,26 @@ public static function get_address( $data ) { case 'administrative_area_level_1': case 'administrative_area_level_2': case 'administrative_area_level_3': - $address['region'][] = $value; + case 'administrative_area_level_4': + case 'administrative_area_level_5': + $address['region'][ $component['types'][0] ] = $value; break; } } + /** + * Change the values and format passed by Google Maps. + * @param array $address The parsed value. + * @param array $address_components The address parts from Google Maps API. + */ + $address = apply_filters( 'pods_component_maps_google_get_address', $address, $data['address_components'] ); + + foreach ( $address as $key => $parts ) { + if ( is_array( $parts ) ) { + ksort( $address[ $key ] ); + } + } + foreach ( $address as $key => $value ) { if ( is_array( $value ) ) { $address[ $key ] = implode( ' ', $value ); From 18623a1308f8338e0144e6e0d8712a4987e9f095 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Wed, 6 Dec 2017 22:35:24 +0100 Subject: [PATCH 87/96] Try again if Google failes the first time --- components/Maps/Maps-Google.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/components/Maps/Maps-Google.php b/components/Maps/Maps-Google.php index b482c53440..09797b095c 100644 --- a/components/Maps/Maps-Google.php +++ b/components/Maps/Maps-Google.php @@ -451,6 +451,16 @@ public static function geocode( $data, $api_key = '', $type = 'address' ) { } } + // Try again once. + $post = wp_remote_post( $url ); + + if ( ! empty( $post['body'] ) ) { + $data = json_decode( $post['body'], true ); + if ( ! empty( $data['results'][0] ) ) { + return $data['results'][0]; + } + } + return array(); } From b3e002208bc4c537e769e64cd589e16748b38aeb Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Wed, 6 Dec 2017 22:35:32 +0100 Subject: [PATCH 88/96] phpDoc --- components/Maps/Maps-Google.php | 46 ++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/components/Maps/Maps-Google.php b/components/Maps/Maps-Google.php index 09797b095c..7926d5c2de 100644 --- a/components/Maps/Maps-Google.php +++ b/components/Maps/Maps-Google.php @@ -9,6 +9,10 @@ public function __construct() { self::$geocode_url = apply_filters( 'pods_maps_google_geocode_url', 'https://maps.googleapis.com/maps/api/geocode/json' ); } + /** + * Load provider assets. + * @inheritdoc + */ public function assets() { if ( ! empty( Pods_Component_Maps::$api_key ) ) { @@ -218,6 +222,10 @@ public function field_options( $options = array(), $type = '' ) { return $options; } + /** + * The input field view file. Used by pods_view(); + * @inheritdoc + */ public function field_input_view() { $view = false; @@ -228,6 +236,10 @@ public function field_input_view() { return $view; } + /** + * The display field view file. Used by pods_view(); + * @inheritdoc + */ public function field_display_view() { $view = false; @@ -244,11 +256,14 @@ public function field_display_view() { * @param string|array $data Any type of address data * @param string $api_key * - * @return array Latitude, Longitude (format: array( 'lat' => value, 'lng' => value ) ) + * @return array { + * @type array $address See get_address() + * @type array $geo See get_latlng() + * } * * @public * @static - * @since 2.7 + * @since 2.x */ public static function geocode_address( $data, $api_key = '' ) { @@ -270,7 +285,7 @@ public static function geocode_address( $data, $api_key = '' ) { * * @public * @static - * @since 2.7 + * @since 2.x */ public static function geocode_address_to_latlng( $address, $api_key = '' ) { @@ -294,11 +309,11 @@ public static function geocode_address_to_latlng( $address, $api_key = '' ) { * @param string|array $lat_lng Lat / long numbers * @param string $api_key * - * @return string Address information + * @return array Address information * * @public * @static - * @since 2.7 + * @since 2.x */ public static function geocode_latlng_to_address( $lat_lng, $api_key = '' ) { @@ -316,11 +331,18 @@ public static function geocode_latlng_to_address( $lat_lng, $api_key = '' ) { * * @param array $data The data from Google * - * @return array + * @return array { + * @type string $line_1 Line 1 address parts, space separated. + * @type string $line_2 Line 2 address parts, space separated. + * @type string $postal_code Postal/ZIP code. + * @type string $city City. + * @type string $region Region parts, space separated. + * @type string $country Country. + * } * * @public * @static - * @since 2.7 + * @since 2.x */ public static function get_address( $data ) { @@ -399,11 +421,14 @@ public static function get_address( $data ) { * * @param array $data The data from Google * - * @return array + * @return array { + * @type float|string $lat Latitude. + * @type float|string $lng Longitude. + * } * * @public * @static - * @since 2.7 + * @since 2.x */ public static function get_latlng( $data ) { @@ -429,7 +454,7 @@ public static function get_latlng( $data ) { * * @public * @static - * @since 2.7 + * @since 2.x */ public static function geocode( $data, $api_key = '', $type = 'address' ) { @@ -438,6 +463,7 @@ public static function geocode( $data, $api_key = '', $type = 'address' ) { } $url = self::$geocode_url . '?' . $type . '=' . $data; + /*if ( ! empty( $api_key ) ) { $url .= '&key=' . $api_key; }*/ From 4e92cad5d110b5c38e033bd00f90c919d4af3401 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Wed, 6 Dec 2017 22:36:10 +0100 Subject: [PATCH 89/96] Option handler fixes --- components/Maps/ui/fields/map-google.php | 6 ++--- components/Maps/ui/front/map-google.php | 34 ++++++++++++++++++++---- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/components/Maps/ui/fields/map-google.php b/components/Maps/ui/fields/map-google.php index ef51fad94f..93113e2654 100644 --- a/components/Maps/ui/fields/map-google.php +++ b/components/Maps/ui/fields/map-google.php @@ -11,19 +11,19 @@ if ( ! empty( $options['maps_zoom'] ) ) { $map_options['zoom'] = (int) $options['maps_zoom']; } else { - $map_options['zoom'] = (int) Pods_Component_Maps::$options['map_zoom']; + $map_options['zoom'] = (int) Pods_Component_Maps::$options['maps_zoom']; } if ( ! empty( $options['maps_type'] ) ) { $map_options['type'] = $options['maps_type']; } else { - $map_options['type'] = Pods_Component_Maps::$options['map_type']; + $map_options['type'] = Pods_Component_Maps::$options['maps_type']; } if ( ! empty( $options['maps_marker'] ) ) { $map_options['marker'] = $options['maps_marker']; } else { - $map_options['marker'] = Pods_Component_Maps::$options['map_marker']; + $map_options['marker'] = Pods_Component_Maps::$options['maps_marker']; } if ( ! empty( $map_options['marker'] ) ) { diff --git a/components/Maps/ui/front/map-google.php b/components/Maps/ui/front/map-google.php index e39d924084..9edcb8a004 100644 --- a/components/Maps/ui/front/map-google.php +++ b/components/Maps/ui/front/map-google.php @@ -1,8 +1,32 @@ Date: Wed, 6 Dec 2017 22:37:09 +0100 Subject: [PATCH 90/96] Code format --- components/Maps/ui/fields/map-google.php | 96 +++++++++++++++--------- 1 file changed, 62 insertions(+), 34 deletions(-) diff --git a/components/Maps/ui/fields/map-google.php b/components/Maps/ui/fields/map-google.php index 93113e2654..ac7ad8e390 100644 --- a/components/Maps/ui/fields/map-google.php +++ b/components/Maps/ui/fields/map-google.php @@ -59,7 +59,9 @@ class="pods-maps-map-canvas pods--maps-map-canvas">
\ No newline at end of file + } ); // end window load +} ); // end document ready + From e71287d2c8fdf4fa89a9789315b10cef782f3e38 Mon Sep 17 00:00:00 2001 From: JoryHogeveen Date: Wed, 6 Dec 2017 22:37:58 +0100 Subject: [PATCH 91/96] Handle multiple values --- components/Maps/ui/front/map-google.php | 72 ++++++++++++++++--------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/components/Maps/ui/front/map-google.php b/components/Maps/ui/front/map-google.php index 9edcb8a004..f1795beb2a 100644 --- a/components/Maps/ui/front/map-google.php +++ b/components/Maps/ui/front/map-google.php @@ -69,7 +69,27 @@ } foreach( $value as $key => $val ) { - if ( ! isset( $address_html ) || $multiple ) { + + $val = wp_parse_args( $val, array( + 'address' => array(), + 'geo' => array(), + 'address_html' => '', + 'info_window' => '', // Format. + 'marker_icon' => null, + ) ); + + // Allow custom overwrites. + if ( 'custom' === pods_v( 'maps_info_window_content', $options, true ) ) { + $address_html = ''; + if ( ! empty( $val['address_html'] ) ) { + $address_html = $val['address_html']; + } elseif ( ! empty( $val['info_window'] ) ) { + $address_html = $val['info_window']; + } + } + + // Parse format. + elseif ( ! isset( $address_html ) || $multiple ) { // @todo Check field type if ( ! empty( $val['info_window'] ) ) { $format = $val['info_window']; @@ -85,13 +105,15 @@ unset( $value[ $key ]['info_window'] ); $value[ $key ]['address_html'] = $address_html; - if ( ! empty( $val['marker_icon'] ) && is_numeric( $val['marker_icon'] ) ) { + if ( is_numeric( $val['marker_icon'] ) ) { $value[ $key ]['marker_icon'] = wp_get_attachment_image_url( $val['marker_icon'], 'full' ); } - if ( ! empty( $val['geo'] ) && is_array( $val['geo'] ) ) { + if ( is_array( $val['geo'] ) ) { $value[ $key ]['geo'] = array_map( 'floatval', $val['geo'] ); } + + unset( $value[ $key ]['pod'] ); } ?> @@ -102,7 +124,7 @@ class="pods-address-maps-map-canvas" \ No newline at end of file +