Файловый менеджер - Редактировать - /home/infrafs/INFRABIKEIT/wp-content/plugins/admin.tar
Назад
class-metaboxes.php 0000644 00000111022 15132765753 0010362 0 ustar 00 <?php /** * Handle the metaboxes * * @author Tijmen Smit * @since 2.0.0 */ if ( !defined( 'ABSPATH' ) ) exit; if ( !class_exists( 'WPSL_Metaboxes' ) ) { /** * Handle the meta boxes * * @since 2.0.0 */ class WPSL_Metaboxes { public function __construct() { add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) ); add_action( 'save_post', array( $this, 'save_post' ) ); add_action( 'post_updated_messages', array( $this, 'store_update_messages' ) ); } /** * Add the meta boxes. * * @since 2.0.0 * @return void */ public function add_meta_boxes() { global $pagenow; add_meta_box( 'wpsl-store-details', __( 'Store Details', 'wpsl' ), array( $this, 'create_meta_fields' ), 'wpsl_stores', 'normal', 'high' ); add_meta_box( 'wpsl-map-preview', __( 'Store Map', 'wpsl' ), array( $this, 'map_preview' ), 'wpsl_stores', 'side' ); $enable_option = apply_filters( 'wpsl_enable_export_option', true ); if ( $enable_option && $pagenow == 'post.php' ) { add_meta_box( 'wpsl-data-export', __( 'Export', 'wpsl' ), array( $this, 'export_data' ), 'wpsl_stores', 'side', 'low' ); } } /** * The store locator meta box fields. * * @since 2.0.0 * @return array $meta_fields The meta box fields used for the store details */ public function meta_box_fields() { global $wpsl_settings; $meta_fields = array( __( 'Location', 'wpsl' ) => array( 'address' => array( 'label' => __( 'Address', 'wpsl' ), 'required' => true ), 'address2' => array( 'label' => __( 'Address 2', 'wpsl' ) ), 'city' => array( 'label' => __( 'City', 'wpsl' ), 'required' => true ), 'state' => array( 'label' => __( 'State', 'wpsl' ) ), 'zip' => array( 'label' => __( 'Zip Code', 'wpsl' ) ), 'country' => array( 'label' => __( 'Country', 'wpsl' ), 'required' => true ), 'country_iso' => array( 'type' => 'hidden' ), 'lat' => array( 'label' => __( 'Latitude', 'wpsl' ) ), 'lng' => array( 'label' => __( 'Longitude', 'wpsl' ) ) ), __( 'Opening Hours', 'wpsl' ) => array( 'hours' => array( 'label' => __( 'Hours', 'wpsl' ), 'type' => $wpsl_settings['editor_hour_input'] //Either set to textarea or dropdown. This is defined through the 'Opening hours input format: ' option on the settings page ) ), __( 'Additional Information', 'wpsl' ) => array( 'phone' => array( 'label' => __( 'Tel', 'wpsl' ) ), 'fax' => array( 'label' => __( 'Fax', 'wpsl' ) ), 'email' => array( 'label' => __( 'Email', 'wpsl' ) ), 'url' => array( 'label' => __( 'Url', 'wpsl' ) ) ) ); return apply_filters( 'wpsl_meta_box_fields', $meta_fields ); } /** * Create the store locator metabox input fields. * * @since 2.0.0 * @return void */ function create_meta_fields() { global $wpsl_settings, $wp_version; $i = 0; $j = 0; $tab_items = ''; wp_nonce_field( 'save_store_meta', 'wpsl_meta_nonce' ); ?> <div class="wpsl-store-meta <?php if ( floatval( $wp_version ) < 3.8 ) { echo 'wpsl-pre-38'; } // Fix CSS issue with < 3.8 versions ?>"> <?php // Create the tab navigation for the meta boxes. foreach ( $this->meta_box_fields() as $tab => $meta_fields ) { $active_class = ( $i == 0 ) ? ' wpsl-active' : ''; if ( $wpsl_settings['hide_hours'] && $tab == __( 'Opening Hours', 'wpsl' ) ) { continue; } else { $tab_items .= $this->meta_field_nav( $tab, $active_class ); } $i++; } echo '<ul id="wpsl-meta-nav">' . $tab_items . '</ul>'; // Create the input fields for the meta boxes. foreach ( $this->meta_box_fields() as $tab => $meta_fields ) { $active_class = ( $j == 0 ) ? ' wpsl-active' : ''; if ( $wpsl_settings['hide_hours'] && $tab == __( 'Opening Hours', 'wpsl' ) ) { continue; } else { echo '<div class="wpsl-tab wpsl-' . esc_attr( strtolower( str_replace( ' ', '-', $tab ) ) ) . $active_class . '">'; foreach ( $meta_fields as $field_key => $field_data ) { // If no specific field type is set, we set it to text. $field_type = ( empty( $field_data['type'] ) ) ? 'text' : $field_data['type']; $args = array( 'key' => $field_key, 'data' => $field_data ); // Check for a class method, otherwise enable a plugin hook. if ( method_exists( $this, $field_type . '_input' ) ) { call_user_func( array( $this, $field_type . '_input' ), $args ); } else { do_action( 'wpsl_metabox_' . $field_type . '_input', $args ); } } echo '</div>'; } $j++; } ?> </div> <?php } /** * Create the li elements that are used in the tabs above the store meta fields. * * @since 2.0.0 * @param string $tab The name of the tab * @param string $active_class Either the class name or empty * @return string $nav_item The HTML for the nav list */ public function meta_field_nav( $tab, $active_class ) { $tab_lower = strtolower( str_replace( ' ', '-', $tab ) ); $nav_item = '<li class="wpsl-' . esc_attr( $tab_lower ) . '-tab ' . $active_class . '"><a href="#wpsl-' . esc_attr( $tab_lower ) . '">' . esc_html( $tab ) . '</a></li>'; return $nav_item; } /** * Set the CSS class that tells JS it's an required input field. * * @since 2.0.0 * @param array $args The css classes * @param string $single Whether to return just the class name, or also include the class="" * @return string|void $response The required CSS class or nothing */ public function set_required_class( $args, $single = false ) { if ( isset( $args['required'] ) && ( $args['required'] ) ) { if ( !$single ) { $response = 'class="wpsl-required"'; } else { $response = 'wpsl-required'; } return $response; } } /** * Check if the current field is required. * * @since 2.0.0 * @param array $args The CSS classes * @return string|void The HTML for the required element or nothing */ public function is_required_field( $args ) { if ( isset( $args['required'] ) && ( $args['required'] ) ) { $response = '<span class="wpsl-star"> *</span>'; return $response; } } /** * Get the prefilled field data. * * @since 2.0.0 * @param string $field_name The name of the field to get the data for * @return string $field_data The field data */ public function get_prefilled_field_data( $field_name ) { global $wpsl_settings, $pagenow; $field_data = ''; // Prefilled values are only used for new pages, not when a user edits an existing page. if ( $pagenow == 'post.php' && isset( $_GET['action'] ) && $_GET['action'] == 'edit' ) { return; } $prefilled_fields = array( 'country', 'hours' ); if ( in_array( $field_name, $prefilled_fields ) ) { $field_data = $wpsl_settings['editor_' . $field_name]; } return $field_data; } /** * Create a text input field. * * @since 2.0.0 * @param array $args The input name and label * @return void */ public function text_input( $args ) { $saved_value = $this->get_store_meta( $args['key'] ); // If there is no existing meta value, check if a prefilled value exists for the input field. if ( !$saved_value ) { $saved_value = $this->get_prefilled_field_data( $args['key'] ); } ?> <p> <label for="wpsl-<?php echo esc_attr( $args['key'] ); ?>"><?php echo esc_html( $args['data']['label'] ) . ': ' . $this->is_required_field( $args['data'] ); ?></label> <input id="wpsl-<?php echo esc_attr( $args['key'] ); ?>" <?php echo $this->set_required_class( $args['data'] ); ?> type="text" name="wpsl[<?php echo esc_attr( $args['key'] ); ?>]" value="<?php echo esc_attr( $saved_value ); ?>" /> </p> <?php } /** * Create a hidden input field. * * @since 2.0.0 * @param array $args The name of the meta value * @return void */ public function hidden_input( $args ) { $saved_value = $this->get_store_meta( $args['key'] ); ?> <input id="wpsl-<?php echo esc_attr( $args['key'] ); ?>" type="hidden" name="wpsl[<?php echo esc_attr( $args['key'] ); ?>]" value="<?php echo esc_attr( $saved_value ); ?>" /> <?php } /** * Create a textarea field. * * @since 2.0.0 * @param array $args The textarea name and label * @return void */ public function textarea_input( $args ) { $saved_value = $this->get_store_meta( $args['key'] ); if ( $args['key'] == 'hours' && gettype( $saved_value ) !== 'string' ) { $saved_value = ''; } // If there is no existing meta value, check if a prefilled value exists for the textarea. if ( !$saved_value ) { $prefilled_value = $this->get_prefilled_field_data( $args['key'] ); if ( isset( $prefilled_value['textarea'] ) ) { $saved_value = $prefilled_value['textarea']; } } ?> <p> <label for="wpsl-<?php echo esc_attr( $args['key'] ); ?>"><?php echo esc_html( $args['data']['label'] ) . ': ' . $this->is_required_field( $args['data'] ); ?></label> <textarea id="wpsl-<?php echo esc_attr( $args['key'] ); ?>" <?php echo $this->set_required_class( $args['data'] ); ?> name="wpsl[<?php echo esc_attr( $args['key'] ); ?>]" cols="5" rows="5"><?php echo esc_html( $saved_value ); ?></textarea> </p> <?php } /** * Create a wp editor field. * * @since 2.1.1 * @param array $args The wp editor name and label * @return void */ public function wp_editor_input( $args ) { $saved_value = $this->get_store_meta( $args['key'] ); ?> <p> <label for="wpsl-<?php echo esc_attr( $args['key'] ); ?>"><?php echo esc_html( $args['data']['label'] ) . ': ' . $this->is_required_field( $args['data'] ); ?></label> <?php wp_editor( $saved_value, 'wpsleditor_' . wpsl_random_chars(), $settings = array('textarea_name' => 'wpsl['. esc_attr( $args['key'] ).']') ); ?> </p> <?php } /** * Create a checkbox field. * * @since 2.0.0 * @param array $args The checkbox name and label * @return void */ public function checkbox_input( $args ) { $saved_value = $this->get_store_meta( $args['key'] ); ?> <p> <label for="wpsl-<?php echo esc_attr( $args['key'] ); ?>"><?php echo esc_html( $args['data']['label'] ) . ': ' . $this->is_required_field( $args['data'] ); ?></label> <input id="wpsl-<?php echo esc_attr( $args['key'] ); ?>" <?php echo $this->set_required_class( $args['data'] ); ?> type="checkbox" name="wpsl[<?php echo esc_attr( $args['key'] ); ?>]" <?php checked( $saved_value, true ); ?> value="1" /> </p> <?php } /** * Create a dropdown field. * * @since 2.0.0 * @param array $args The dropdown name and label * @return void */ public function dropdown_input( $args ) { // The hour dropdown requires a different structure with multiple dropdowns. if ( $args['key'] == 'hours' ) { $this->opening_hours(); } else { $option_list = $args['data']['options']; $saved_value = $this->get_store_meta( $args['key'] ); ?> <p> <label for="wpsl-<?php echo esc_attr( $args['key'] ); ?>"><?php echo esc_html( $args['data']['label'] ) . ': ' . $this->is_required_field( $args['data'] ); ?></label> <select id="wpsl-<?php echo esc_attr( $args['key'] ); ?>" <?php echo $this->set_required_class( $args['data'] ); ?> name="wpsl[<?php echo esc_attr( $args['key'] ); ?>]" autocomplete="off" /> <?php foreach ( $option_list as $key => $option ) { ?> <option value="<?php echo esc_attr( $key ); ?>" <?php if ( isset( $saved_value ) ) { selected( $saved_value, $key ); } ?>><?php echo esc_html( $option ); ?></option> <?php } ?> </select> </p> <?php } } /** * Create the openings hours table with the hours as dropdowns. * * @since 2.0.0 * @param string $location The location were the opening hours are shown. * @return void */ public function opening_hours( $location = 'store_page' ) { global $wpsl_settings, $wpsl_admin, $post; $name = ( $location == 'settings' ) ? 'wpsl_editor[dropdown]' : 'wpsl[hours]'; // the name of the input or select field $opening_days = wpsl_get_weekdays(); $opening_hours = ''; $hours = ''; if ( $location == 'store_page' ) { $opening_hours = get_post_meta( $post->ID, 'wpsl_hours' ); } // If we don't have any opening hours, we use the defaults. if ( !isset( $opening_hours[0]['monday'] ) ) { $opening_hours = $wpsl_settings['editor_hours']['dropdown']; } else { $opening_hours = $opening_hours[0]; } // Find out whether we have a 12 or 24hr format. $hour_format = $this->find_hour_format( $opening_hours ); if ( $hour_format == 24 ) { $hour_class = 'wpsl-twentyfour-format'; } else { $hour_class = 'wpsl-twelve-format'; } /* * Only include the 12 / 24hr dropdown switch if we are on store page, * otherwise just show the table with the opening hour dropdowns. */ if ( $location == 'store_page' ) { ?> <p class="wpsl-hours-dropdown"> <label for="wpsl-editor-hour-input"><?php _e( 'Hour format', 'wpsl' ); ?>:</label> <?php echo $wpsl_admin->settings_page->show_opening_hours_format( $hour_format ); ?> </p> <?php } ?> <table id="wpsl-store-hours" class="<?php echo $hour_class; ?>"> <tr> <th><?php _e( 'Days', 'wpsl' ); ?></th> <th><?php _e( 'Opening Periods', 'wpsl' ); ?></th> <th></th> </tr> <?php foreach ( $opening_days as $index => $day ) { $i = 0; if ( is_array( $opening_hours[$index] ) ) { $hour_count = count( $opening_hours[$index] ); } else { $hour_count = 0; } ?> <tr> <td class="wpsl-opening-day"><?php echo esc_html( $day ); ?></td> <td id="wpsl-hours-<?php echo esc_attr( $index ); ?>" class="wpsl-opening-hours" data-day="<?php echo esc_attr( $index ); ?>"> <?php if ( $hour_count > 0 ) { // Loop over the opening periods. while ( $i < $hour_count ) { if ( isset( $opening_hours[$index][$i] ) ) { $hours = explode( ',', $opening_hours[$index][$i] ); } else { $hours = ''; } // If we don't have two parts or one of them is empty, then we set the store to closed. if ( ( count( $hours ) == 2 ) && ( !empty( $hours[0] ) ) && ( !empty( $hours[1] ) ) ) { $args = array( 'day' => $index, 'name' => $name, 'hour_format' => $hour_format, 'hours' => $hours ); ?> <div class="wpsl-current-period <?php if ( $i > 0 ) { echo 'wpsl-multiple-periods'; } ?>"> <?php echo $this->opening_hours_dropdown( $args, 'open' ); ?> <span> - </span> <?php echo $this->opening_hours_dropdown( $args, 'close' ); ?> <div class="wpsl-icon-cancel-circled"></div> </div> <?php } else { $this->show_store_closed( $name, $index ); } $i++; } } else { $this->show_store_closed( $name, $index ); } ?> </td> <td> <div class="wpsl-add-period"> <div class="wpsl-icon-plus-circled"></div> </div> </td> </tr> <?php } ?> </table> <?php } /** * Show the 'store closed' message. * * @since 2.0.0 * @param string $name The name for the input field * @param string $day The day the store is closed * @return void */ public function show_store_closed( $name, $day ) { echo '<p class="wpsl-store-closed">' . __( 'Closed', 'wpsl' ) . '<input type="hidden" name="' . esc_attr( $name ) . '[' . esc_attr( $day ) . ']" value="closed"></p>'; } /** * Find out whether the opening hours are set in the 12 or 24hr format. * * We use this to determine the selected value for the dropdown in the store editor. * So a user can decide to change the opening hour format. * * @since 2.0.0 * @param array $opening_hours The opening hours for the whole week * @return string The hour format used in the opening hours */ public function find_hour_format( $opening_hours ) { $week_days = wpsl_get_weekdays(); foreach ( $week_days as $key => $day ) { if ( isset( $opening_hours[$key][0] ) ) { $time = $opening_hours[$key][0]; if ( ( strpos( $time, 'AM' ) !== false ) || ( strpos( $time, 'PM' ) !== false ) ) { return '12'; } else { return '24'; } } } } /** * Create the opening hours dropdown. * * @since 2.0.0 * @param array $args The data to create the opening hours dropdown * @param string $period Either set to open or close * @return string $select The html for the dropdown */ public function opening_hours_dropdown( $args, $period ) { $select_index = ( $period == 'open' ) ? 0 : 1; $selected_time = $args['hours'][$select_index]; $select_name = $args['name'] . '[' . strtolower( $args['day'] ) . '_' . $period . ']'; $open = strtotime( '12:00am' ); $close = strtotime( '11:59pm' ); $hour_interval = 900; if ( $args['hour_format'] == 12 ) { $format = 'g:i A'; } else { $format = 'H:i'; } $select = '<select class="wpsl-' . esc_attr( $period ) . '-hour" name="' . esc_attr( $select_name ) . '[]" autocomplete="off">'; for ( $i = $open; $i <= $close; $i += $hour_interval ) { // If the selected time matches the current time then we set it to active. if ( $selected_time == date( $format, $i ) ) { $selected = 'selected="selected"'; } else { $selected = ''; } $select .= "<option value='" . date( $format, $i ) . "' $selected>" . date( $format, $i ) . "</option>"; } $select .= '</select>'; return $select; } /** * Get the store post meta. * * @since 2.0.0 * @param string $key The name of the meta value * @return mixed|void $store_meta Meta value for the store field */ public function get_store_meta( $key ) { global $post; $store_meta = get_post_meta( $post->ID, 'wpsl_' . $key, true ); if ( $store_meta ) { return $store_meta; } else { return; } } /** * Save the custom post data. * * @since 2.0.0 * @param integer $post_id store post ID * @return void */ public function save_post( $post_id ) { global $wpsl_admin; if ( empty( $_POST['wpsl_meta_nonce'] ) || !wp_verify_nonce( $_POST['wpsl_meta_nonce'], 'save_store_meta' ) ) return; if ( !isset( $_POST['post_type'] ) || 'wpsl_stores' !== $_POST['post_type'] ) return; if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return; if ( is_int( wp_is_post_revision( $post_id ) ) ) return; if ( !current_user_can( 'edit_post', $post_id ) ) return; $this->store_data = $_POST['wpsl']; // Check if the hours are set through dropdowns. if ( isset( $this->store_data['hours'] ) && is_array( $this->store_data['hours'] ) && ( !empty( $this->store_data['hours'] ) ) ) { $this->store_data['hours'] = $this->format_opening_hours(); } // Loop over the meta fields defined in the meta_box_fields and update the post meta data. foreach ( $this->meta_box_fields() as $tab => $meta_fields ) { foreach ( $meta_fields as $field_key => $field_data ) { // Either update or delete the post meta. if ( isset( $this->store_data[ $field_key ] ) && ( $this->store_data[ $field_key ] != "" ) ) { if ( isset( $field_data['type'] ) && $field_data['type'] ) { $field_type = $field_data['type']; } else { $field_type = ''; } switch ( $field_type ) { case 'thumbnail': update_post_meta( $post_id, 'wpsl_' . $field_key, absint( $this->store_data[ $field_key ] ) ); break; case 'checkbox': $checkbox_val = ( isset( $this->store_data[ $field_key ] ) ) ? 1 : 0; update_post_meta( $post_id, 'wpsl_' . $field_key, $checkbox_val ); break; case 'wp_editor': case 'textarea': update_post_meta( $post_id, 'wpsl_' . $field_key, wp_kses_post( trim( stripslashes( $this->store_data[ $field_key ] ) ) ) ); break; default: if ( is_array( $this->store_data[ $field_key ] ) ) { if ( wpsl_is_multi_array( $this->store_data[ $field_key ] ) ) { array_walk_recursive( $this->store_data[ $field_key ], 'wpsl_sanitize_multi_array' ); update_post_meta( $post_id, 'wpsl_' . $field_key, $this->store_data[ $field_key ] ); } else { update_post_meta( $post_id, 'wpsl_' . $field_key, array_map( 'sanitize_text_field', $this->store_data[ $field_key ] ) ); } } else { update_post_meta( $post_id, 'wpsl_' . $field_key, sanitize_text_field( $this->store_data[ $field_key ] ) ); } break; } } else { delete_post_meta( $post_id, 'wpsl_' . $field_key ); } } } do_action( 'wpsl_save_post', $this->store_data ); /* * If all the required fields contain data, then check if we need to * geocode the address and if we should delete the autoload transient. * * Otherwise show a notice for 'missing data' and set the post status to pending. */ if ( !$this->check_missing_meta_data( $post_id ) ) { $wpsl_admin->geocode->check_geocode_data( $post_id, $this->store_data ); $wpsl_admin->maybe_delete_autoload_transient( $post_id ); } else { $wpsl_admin->notices->save( 'error', __( 'Failed to publish the store. Please fill in the required store details.', 'wpsl' ) ); $this->set_post_pending( $post_id ); } } /** * Loop through the opening hours and structure the data in a new array. * * @since 2.0.0 * @return array $opening_hours The formatted opening hours */ public function format_opening_hours() { $week_days = wpsl_get_weekdays(); // Use the opening hours from the editor page or the add/edit store page. if ( isset( $_POST['wpsl_editor']['dropdown'] ) ) { $store_hours = $_POST['wpsl_editor']['dropdown']; } else if ( isset( $this->store_data['hours'] ) ) { $store_hours = $this->store_data['hours']; } foreach ( $week_days as $day => $value ) { $i = 0; $periods = array(); if ( isset( $store_hours[$day . '_open'] ) && $store_hours[$day . '_open'] ) { foreach ( $store_hours[$day . '_open'] as $opening_hour ) { $hours = $this->validate_hour( $store_hours[$day.'_open'][$i] ) . ',' . $this->validate_hour( $store_hours[$day.'_close'][$i] ); $periods[] = $hours; $i++; } } $opening_hours[$day] = $periods; } return $opening_hours; } /* * Validate the 12 or 24 hr time format. * * @since 2.0.0 * @param string $hour The opening hour * @return boolean true if the $hour format is valid */ public function validate_hour( $hour ) { global $wpsl_settings; /* * On the add/edit store we can always use the $wpsl_settings value. * But if validate_hour is called from the settings page then we * should use the $_POST value to make sure we have the correct value. */ if ( isset( $_POST['wpsl_editor']['hour_format'] ) ) { $hour_format = ( $_POST['wpsl_editor']['hour_format'] == 12 ) ? 12 : 24; } else { $hour_format = $wpsl_settings['editor_hour_format']; } if ( $hour_format == 12 ) { $format = 'g:i A'; } else { $format = 'H:i'; } if ( date( $format, strtotime( $hour ) ) == $hour ) { return $hour; } } /** * Set the post status to pending instead of publish. * * @since 2.0.0 * @param integer $post_id store post ID * @return void */ public function set_post_pending( $post_id ) { global $wpdb; $wpdb->update( $wpdb->posts, array( 'post_status' => 'pending' ), array( 'ID' => $post_id ) ); add_filter( 'redirect_post_location', array( $this, 'remove_message_arg' ) ); } /** * Remove the message query arg. * * If one or more of the required fields are empty, we show a custom msg. * So no need for the normal post update messages arg. * * @since 2.0.0 * @param string $location The destination url * @return void */ public function remove_message_arg( $location ) { return remove_query_arg( 'message', $location ); } /** * Make sure all the required post meta fields contain data. * * @since 2.0.0 * @param integer $post_id store post ID * @return boolean */ public function check_missing_meta_data( $post_id ) { foreach ( $this->meta_box_fields() as $tab => $meta_fields ) { foreach ( $meta_fields as $field_key => $field_data ) { if ( isset( $field_data['required'] ) && $field_data['required'] ) { $post_meta = get_post_meta( $post_id, 'wpsl_' . $field_key, true ); if ( empty( $post_meta ) ) { return true; } } } } } /** * The html for the map preview in the sidebar. * * @since 2.0.0 * @return void */ public function map_preview() { ?> <div id="wpsl-gmap-wrap"></div> <p class="wpsl-submit-wrap"> <a id="wpsl-lookup-location" class="button-primary" href="#wpsl-meta-nav"><?php _e( 'Preview Location', 'wpsl' ); ?></a> <span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'The map preview is based on the provided address, city and country details. %s It will ignore any custom latitude or longitude values.', 'wpsl' ), '<br><br>' ); ?></span></span> <em class="wpsl-desc"><?php _e( 'You can drag the marker to adjust the exact location of the marker.', 'wpsl' ); ?></em> </p> <?php } /** * The html for the export details section in the sidebar. * * @since 2.2.15 * @return void */ public function export_data() { global $post; $link_url = wp_nonce_url( admin_url( 'post.php?'. $_SERVER['QUERY_STRING'] . '&wpsl_data_export=1' ), 'wpsl_export_' . $post->ID, 'wpsl_export_nonce' ); ?> <p class="wpsl-submit-wrap"> <a id="wpsl-export-data" class="button-primary" href="<?php echo esc_url( $link_url ); ?>"><?php _e( 'Export Location Data', 'wpsl' ); ?></a> </p> <?php } /** * Store update messages. * * @since 2.0.0 * @param array $messages Existing post update messages. * @return array $messages Amended post update messages with new CPT update messages. */ function store_update_messages( $messages ) { $post = get_post(); $post_type = get_post_type( $post ); $post_type_object = get_post_type_object( $post_type ); $messages['wpsl_stores'] = array( 0 => '', // Unused. Messages start at index 1. 1 => __( 'Store updated.', 'wpsl' ), 2 => __( 'Custom field updated.', 'wpsl' ), 3 => __( 'Custom field deleted.', 'wpsl' ), 4 => __( 'Store updated.', 'wpsl' ), 5 => isset( $_GET['revision'] ) ? sprintf( __( 'Store restored to revision from %s', 'wpsl' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false, 6 => __( 'Store published.', 'wpsl' ), 7 => __( 'Store saved.', 'wpsl' ), 8 => __( 'Store submitted.', 'wpsl' ), 9 => sprintf( __( 'Store scheduled for: <strong>%1$s</strong>.', 'wpsl' ), date_i18n( __( 'M j, Y @ G:i', 'wpsl' ), strtotime( $post->post_date ) ) ), 10 => __( 'Store draft updated.', 'wpsl' ) ); if ( ( 'wpsl_stores' == $post_type ) && ( $post_type_object->publicly_queryable ) ) { $permalink = get_permalink( $post->ID ); $view_link = sprintf( ' <a href="%s">%s</a>', esc_url( $permalink ), __( 'View store', 'wpsl' ) ); $messages[ $post_type ][1] .= $view_link; $messages[ $post_type ][6] .= $view_link; $messages[ $post_type ][9] .= $view_link; $preview_permalink = add_query_arg( 'preview', 'true', $permalink ); $preview_link = sprintf( ' <a target="_blank" href="%s">%s</a>', esc_url( $preview_permalink ), __( 'Preview store', 'wpsl' ) ); $messages[ $post_type ][8] .= $preview_link; $messages[ $post_type ][10] .= $preview_link; } return $messages; } } } class-admin.php 0000644 00000064010 15132765753 0007467 0 ustar 00 <?php /** * Admin class * * @author Tijmen Smit * @since 1.0.0 */ if ( !defined( 'ABSPATH' ) ) exit; if ( !class_exists( 'WPSL_Admin' ) ) { /** * Handle the backend of the store locator * * @since 1.0.0 */ class WPSL_Admin { /** * @since 2.0.0 * @var WPSL_Metaboxes */ public $metaboxes; /** * @since 2.0.0 * @var WPSL_Geocode */ public $geocode; /** * @since 2.0.0 * @var WPSL_Notices */ public $notices; /** * @since 2.0.0 * @var WPSL_Settings */ public $settings_page; /** * Class constructor */ function __construct() { $this->includes(); add_action( 'init', array( $this, 'init' ) ); add_action( 'admin_menu', array( $this, 'create_admin_menu' ) ); add_action( 'admin_init', array( $this, 'setting_warnings' ) ); add_action( 'delete_post', array( $this, 'maybe_delete_autoload_transient' ) ); add_action( 'wp_trash_post', array( $this, 'maybe_delete_autoload_transient' ) ); add_action( 'untrash_post', array( $this, 'maybe_delete_autoload_transient' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) ); add_filter( 'plugin_row_meta', array( $this, 'add_plugin_meta_row' ), 10, 2 ); add_filter( 'plugin_action_links_' . WPSL_BASENAME, array( $this, 'add_action_links' ), 10, 2 ); add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ), 1 ); add_action( 'wp_loaded', array( $this, 'disable_setting_notices' ) ); add_action( 'wp_ajax_validate_server_key', array( $this, 'ajax_validate_server_key' ) ); add_action( 'wp_ajax_nopriv_validate_server_key', array( $this, 'ajax_validate_server_key' ) ); } /** * @since 2.2.234 * @return void */ public function ajax_validate_server_key() { $this->settings_page->ajax_validate_server_key(); } /** * Include the required files. * * @since 2.0.0 * @return void */ public function includes() { require_once( WPSL_PLUGIN_DIR . 'admin/class-shortcode-generator.php' ); require_once( WPSL_PLUGIN_DIR . 'admin/class-notices.php' ); require_once( WPSL_PLUGIN_DIR . 'admin/class-license-manager.php' ); require_once( WPSL_PLUGIN_DIR . 'admin/class-metaboxes.php' ); require_once( WPSL_PLUGIN_DIR . 'admin/class-geocode.php' ); require_once( WPSL_PLUGIN_DIR . 'admin/class-settings.php' ); require_once( WPSL_PLUGIN_DIR . 'admin/upgrade.php' ); require_once( WPSL_PLUGIN_DIR . 'admin/data-export.php' ); } /** * Init the classes. * * @since 2.0.0 * @return void */ public function init() { $this->notices = new WPSL_Notices(); $this->metaboxes = new WPSL_Metaboxes(); $this->geocode = new WPSL_Geocode(); $this->settings_page = new WPSL_Settings(); } /** * Check if we need to show warnings after * the user installed the plugin. * * @since 1.0.0 * @todo move to class-notices? * @return void */ public function setting_warnings() { global $current_user, $wpsl_settings; $this->setting_warning = array(); // The fields settings field to check for data. $warnings = array( 'start_latlng' => 'location', 'api_browser_key' => 'key' ); if ( ( current_user_can( 'install_plugins' ) ) && is_admin() ) { foreach ( $warnings as $setting_name => $warning ) { if ( empty( $wpsl_settings[$setting_name] ) && !get_user_meta( $current_user->ID, 'wpsl_disable_' . $warning . '_warning' ) ) { if ( $warning == 'key' ) { $this->setting_warning[$warning] = sprintf( __( "You need to create %sAPI keys%s for Google Maps before you can use the store locator! %sDismiss%s", "wpsl" ), '<a href="https://wpstorelocator.co/document/create-google-api-keys/">', "</a>", "<a href='" . esc_url( wp_nonce_url( add_query_arg( 'wpsl-notice', 'key' ), 'wpsl_notices_nonce', '_wpsl_notice_nonce' ) ) . "'>", "</a>" ); } else { $this->setting_warning[$warning] = sprintf( __( "Before adding the [wpsl] shortcode to a page, please don't forget to define a start point on the %ssettings%s page. %sDismiss%s", "wpsl" ), "<a href='" . admin_url( 'edit.php?post_type=wpsl_stores&page=wpsl_settings' ) . "'>", "</a>", "<a href='" . esc_url( wp_nonce_url( add_query_arg( 'wpsl-notice', 'location' ), 'wpsl_notices_nonce', '_wpsl_notice_nonce' ) ) . "'>", "</a>" ); } } } if ( $this->setting_warning ) { add_action( 'admin_notices', array( $this, 'show_warning' ) ); } } } /** * Show the admin warnings * * @since 1.2.0 * @return void */ public function show_warning() { foreach ( $this->setting_warning as $k => $warning ) { echo "<div id='message' class='error'><p>" . $warning . "</p></div>"; } } /** * Disable notices about the plugin settings. * * @todo move to class-notices? * @since 2.2.3 * @return void */ public function disable_setting_notices() { global $current_user; if ( isset( $_GET['wpsl-notice'] ) && isset( $_GET['_wpsl_notice_nonce'] ) ) { if ( !wp_verify_nonce( $_GET['_wpsl_notice_nonce'], 'wpsl_notices_nonce' ) ) { wp_die( __( 'Security check failed. Please reload the page and try again.', 'wpsl' ) ); } $notice = sanitize_text_field( $_GET['wpsl-notice'] ); add_user_meta( $current_user->ID, 'wpsl_disable_' . $notice . '_warning', 'true', true ); } } /** * Add the admin menu pages. * * @since 1.0.0 * @return void */ public function create_admin_menu() { $sub_menus = apply_filters( 'wpsl_sub_menu_items', array( array( 'page_title' => __( 'Settings', 'wpsl' ), 'menu_title' => __( 'Settings', 'wpsl' ), 'caps' => 'manage_wpsl_settings', 'menu_slug' => 'wpsl_settings', 'function' => array( $this, 'load_template' ) ), array( 'page_title' => __( 'Add-Ons', 'wpsl' ), 'menu_title' => __( 'Add-Ons', 'wpsl' ), 'caps' => 'manage_wpsl_settings', 'menu_slug' => 'wpsl_add_ons', 'function' => array( $this, 'load_template' ) ) ) ); if ( count( $sub_menus ) ) { foreach ( $sub_menus as $sub_menu ) { add_submenu_page( 'edit.php?post_type=wpsl_stores', $sub_menu['page_title'], $sub_menu['menu_title'], $sub_menu['caps'], $sub_menu['menu_slug'], $sub_menu['function'] ); } } } /** * Load the correct page template. * * @since 2.1.0 * @return void */ public function load_template() { switch ( $_GET['page'] ) { case 'wpsl_settings': require 'templates/map-settings.php'; break; case 'wpsl_add_ons': require 'templates/add-ons.php'; break; } } /** * Check if we need to delete the autoload transient. * * This is called when a post it saved, deleted, trashed or untrashed. * * @since 2.0.0 * @return void */ public function maybe_delete_autoload_transient( $post_id ) { global $wpsl_settings; if ( isset( $wpsl_settings['autoload'] ) && $wpsl_settings['autoload'] && get_post_type( $post_id ) == 'wpsl_stores' ) { $this->delete_autoload_transient(); } } /** * Delete the transients that are used on the front-end * if the autoload option is enabled. * * The transient names used by the store locator are partly dynamic. * They always start with wpsl_autoload_, followed by the number of * stores to load and ends with the language code. * * So you get wpsl_autoload_20_de if the language is set to German * and 20 stores are set to show on page load. * * The language code has to be included in case a multilingual plugin is used. * Otherwise it can happen the user switches to Spanish, * but ends up seeing the store data in the wrong language. * * @since 2.0.0 * @return void */ public function delete_autoload_transient() { global $wpdb; $option_names = $wpdb->get_results( "SELECT option_name AS transient_name FROM " . $wpdb->options . " WHERE option_name LIKE ('\_transient\_wpsl\_autoload\_%')" ); if ( $option_names ) { foreach ( $option_names as $option_name ) { $transient_name = str_replace( "_transient_", "", $option_name->transient_name ); delete_transient( $transient_name ); } } } /** * Check if we can use a font for the plugin icon. * * This is supported by WP 3.8 or higher * * @since 1.0.0 * @return void */ private function check_icon_font_usage() { global $wp_version; if ( ( version_compare( $wp_version, '3.8', '>=' ) == TRUE ) ) { $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min'; wp_enqueue_style( 'wpsl-admin-38', plugins_url( '/css/style-3.8'. $min .'.css', __FILE__ ), false ); } } /** * The text messages used in wpsl-admin.js. * * @since 1.2.20 * @return array $admin_js_l10n The texts used in the wpsl-admin.js */ public function admin_js_l10n() { global $wpsl_settings; $admin_js_l10n = array( 'noAddress' => __( 'Cannot determine the address at this location.', 'wpsl' ), 'geocodeFail' => __( 'Geocode was not successful for the following reason', 'wpsl' ), 'securityFail' => __( 'Security check failed, reload the page and try again.', 'wpsl' ), 'requiredFields' => __( 'Please fill in all the required store details.', 'wpsl' ), 'missingGeoData' => __( 'The map preview requires all the location details.', 'wpsl' ), 'closedDate' => __( 'Closed', 'wpsl' ), 'styleError' => __( 'The code for the map style is invalid.', 'wpsl' ), 'dismissNotice' => __( 'Dismiss this notice.', 'wpsl' ), 'browserKeyError' => sprintf( __( 'There\'s a problem with the provided %sbrowser key%s. %s You will have to open the %sbrowser console%s ( %sctrl%s %sshift%s %sk%s in Firefox, or %sctrl%s %sshift%s %sj%s in Chrome ) to see the error details returned by the Google Maps API. %s The error itself includes a link explaining the problem in more detail. %s Common API errors are also covered in the %stroubleshooting section%s.', 'wpsl' ), '<a target="_blank" href="https://wpstorelocator.co/document/create-google-api-keys/#browser-key">','</a>', '<br><br>', '<a target="_blank" href="https://codex.wordpress.org/Using_Your_Browser_to_Diagnose_JavaScript_Errors#Step_3:_Diagnosis">', '</a>', '<kbd>', '</kbd>', '<kbd>', '</kbd>','<kbd>', '</kbd>', '<kbd>', '</kbd>', '<kbd>', '</kbd>','<kbd>', '</kbd>', '<br><br>', '<br><br>', '<a target="_blank" href="https://wpstorelocator.co/document/create-google-api-keys/#api-errors">', '</a>' ), 'browserKeySuccess' => __( 'No problems found with the browser key.', 'wpsl' ), 'serverKey' => __( 'Server key', 'wpsl' ), 'serverKeyMissing' => sprintf( __( 'No %sserver key%s found!' ), '<a target="_blank" href="https://wpstorelocator.co/document/create-google-api-keys/#server-key">', '</a>' ), 'browserKey' => __( 'Browser key', 'wpsl' ), 'browserKeyMissing' => sprintf( __( 'No %sbrowser key%s found!' ), '<a target="_blank" href="https://wpstorelocator.co/document/create-google-api-keys/#browser-key">', '</a>' ), 'restrictedZipCode' => __( 'and will only work for zip codes.', 'wpsl' ), 'noRestriction' => sprintf( __( 'because no %smap region%s is selected the geocode API will search for matching results around the world. This may result in unexpected results.'), '<a class="wpsl-region-href" href="#wpsl-tabs">', '</a>' ), 'loadingError' => sprintf( __( 'Google Maps didn\'t load correctly. Make sure you have an active %sbilling%s %saccount%s for Google Maps. %s If the "For development purposes only" text keeps showing after creating a billing account, then you will have to contact %sGoogle Billing Support%s.', 'wpsl' ), '<a target="_blank" href="https://wpstorelocator.co/document/create-google-api-keys/#billing">', '</a>', '<a href="http://g.co/dev/maps-no-account">', '</a>', '<br><br>', '<a target="_blank" href="https://cloud.google.com/support/billing/">', '</a>' ), 'loadingFailed' => sprintf( __( 'Google Maps failed to load correctly. This is likely due to a problem with the provided %sbrowser key%s. %s You will have to open the %sbrowser console%s ( %sctrl%s %sshift%s %sk%s in Firefox, or %sctrl%s %sshift%s %sj%s in Chrome ) to see the error details returned by the Google Maps API. %s The error itself includes a link explaining the problem in more detail. %s Common API errors are also covered in the %stroubleshooting section%s.', 'wpsl' ), '<a target="_blank" href="https://wpstorelocator.co/document/create-google-api-keys/#browser-key">','</a>', '<br><br>', '<a target="_blank" href="https://codex.wordpress.org/Using_Your_Browser_to_Diagnose_JavaScript_Errors#Step_3:_Diagnosis">', '</a>', '<kbd>', '</kbd>', '<kbd>', '</kbd>','<kbd>', '</kbd>', '<kbd>', '</kbd>', '<kbd>', '</kbd>','<kbd>', '</kbd>', '<br><br>', '<br><br>', '<a target="_blank" href="https://wpstorelocator.co/document/create-google-api-keys/#api-errors">', '</a>' ), 'close' => __( 'Close', 'wpsl' ), ); /** * This text is only shown when the user checks the API response * for a provided address ( tools section ), and a map region is selected. */ if ( $wpsl_settings['api_region'] ) { if ( $wpsl_settings['api_geocode_component'] ) { $restriction_type = 'restricted'; } else { $restriction_type = 'biased'; } $admin_js_l10n['resultsWarning'] = sprintf( __( 'with the current settings the results are %s to' ), $restriction_type ); } return $admin_js_l10n; } /** * Plugin settings that are used in the wpsl-admin.js. * * @since 2.0.0 * @return array $settings_js The settings used in the wpsl-admin.js */ public function js_settings() { global $wpsl_settings; $js_settings = array( 'hourFormat' => $wpsl_settings['editor_hour_format'], 'defaultLatLng' => $this->get_default_lat_lng(), 'defaultZoom' => 6, 'mapType' => $wpsl_settings['editor_map_type'], 'requiredFields' => array( 'address', 'city', 'country' ), 'ajaxurl' => wpsl_get_ajax_url(), 'url' => WPSL_URL, 'storeMarker' => $wpsl_settings['store_marker'] ); // Make sure that the Geocode API testing tool correctly restricts the results if required. if ( $wpsl_settings['api_region'] && $wpsl_settings['api_geocode_component'] ) { $geocode_components = array(); $geocode_components['country'] = strtoupper( $wpsl_settings['api_region'] ); if ( $wpsl_settings['force_postalcode'] ) { $geocode_components['postalCode'] = ''; } $js_settings['geocodeComponents'] = $geocode_components; } return apply_filters( 'wpsl_admin_js_settings', $js_settings ); } /** * Get the coordinates that are used to * show the map on the settings page. * * @since 2.2.5 * @return string $startLatLng The start coordinates */ public function get_default_lat_lng() { global $wpsl_settings; $startLatLng = $wpsl_settings['start_latlng']; // If no start coordinates exists, then set the default to Holland. if ( !$startLatLng ) { $startLatLng = '52.378153,4.899363'; } return $startLatLng; } /** * Add the required admin script. * * @since 1.0.0 * @return void */ public function admin_scripts() { $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min'; // Always load the main js admin file to make sure the "dismiss" link in the location notice works. wp_enqueue_script( 'wpsl-admin-js', plugins_url( '/js/wpsl-admin'. $min .'.js', __FILE__ ), array( 'jquery' ), WPSL_VERSION_NUM, true ); $this->maybe_show_pointer(); $this->check_icon_font_usage(); // Only enqueue the rest of the css/js files if we are on a page that belongs to the store locator. if ( ( get_post_type() == 'wpsl_stores' ) || ( isset( $_GET['post_type'] ) && ( $_GET['post_type'] == 'wpsl_stores' ) ) ) { // Make sure no other Google Map scripts can interfere with the one from the store locator. wpsl_deregister_other_gmaps(); wp_enqueue_style( 'jquery-style', '//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/themes/smoothness/jquery-ui.css' ); wp_enqueue_style( 'wpsl-admin-css', plugins_url( '/css/style'. $min .'.css', __FILE__ ), false ); wp_enqueue_media(); wp_enqueue_script( 'jquery-ui-dialog' ); wp_enqueue_script( 'jquery-ui-tabs' ); wp_enqueue_script( 'wpsl-gmap', ( '//maps.google.com/maps/api/js' . wpsl_get_gmap_api_params( 'browser_key' ) ), false, WPSL_VERSION_NUM, true ); wp_enqueue_script( 'wpsl-queue', plugins_url( '/js/ajax-queue'. $min .'.js', __FILE__ ), array( 'jquery' ), WPSL_VERSION_NUM, true ); wp_enqueue_script( 'wpsl-retina', plugins_url( '/js/retina'. $min .'.js', __FILE__ ), array( 'jquery' ), WPSL_VERSION_NUM, true ); wp_localize_script( 'wpsl-admin-js', 'wpslL10n', $this->admin_js_l10n() ); wp_localize_script( 'wpsl-admin-js', 'wpslSettings', $this->js_settings() ); } } /** * Check if we need to show the wpsl pointer. * * @since 2.0.0 * @return void */ public function maybe_show_pointer() { $disable_pointer = apply_filters( 'wpsl_disable_welcome_pointer', false ); if ( $disable_pointer ) { return; } $dismissed_pointers = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ); // If the user hasn't dismissed the wpsl pointer, enqueue the script and style, and call the action hook. if ( !in_array( 'wpsl_signup_pointer', $dismissed_pointers ) ) { wp_enqueue_style( 'wp-pointer' ); wp_enqueue_script( 'wp-pointer' ); add_action( 'admin_print_footer_scripts', array( $this, 'welcome_pointer_script' ) ); } } /** * Add the script for the welcome pointer. * * @since 2.0.0 * @return void */ public function welcome_pointer_script() { $pointer_content = '<h3>' . __( 'Welcome to WP Store Locator', 'wpsl' ) . '</h3>'; $pointer_content .= '<p>' . __( 'Sign up for the latest plugin updates and announcements.', 'wpsl' ) . '</p>'; $pointer_content .= '<div id="mc_embed_signup" class="wpsl-mc-wrap" style="padding:0 15px; margin-bottom:13px;"><form action="//wpstorelocator.us10.list-manage.com/subscribe/post?u=34e4c75c3dc990d14002e19f6&id=4be03427d7" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate><div id="mc_embed_signup_scroll"><input type="email" value="" name="EMAIL" class="email" id="mce-EMAIL" placeholder="email address" required style="margin-right:5px;width:230px;"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"><div style="position: absolute; left: -5000px;"><input type="text" name="b_34e4c75c3dc990d14002e19f6_4be03427d7" tabindex="-1" value=""></div></div></form></div>'; ?> <script type="text/javascript"> //<![CDATA[ jQuery( document ).ready( function( $ ) { $( '#menu-posts-wpsl_stores' ).pointer({ content: '<?php echo $pointer_content; ?>', position: { edge: 'left', align: 'center' }, pointerWidth: 350, close: function () { $.post( ajaxurl, { pointer: 'wpsl_signup_pointer', action: 'dismiss-wp-pointer' }); } }).pointer( 'open' ); // If a user clicked the "subscribe" button trigger the close button for the pointer. $( ".wpsl-mc-wrap #mc-embedded-subscribe" ).on( "click", function() { $( ".wp-pointer .close" ).trigger( "click" ); }); }); //]]> </script> <?php } /** * Add link to the plugin action row. * * @since 2.0.0 * @param array $links The existing action links * @param string $file The file path of the current plugin * @return array $links The modified links */ public function add_action_links( $links, $file ) { if ( strpos( $file, 'wp-store-locator.php' ) !== false ) { $settings_link = '<a href="' . admin_url( 'edit.php?post_type=wpsl_stores&page=wpsl_settings' ) . '" title="View WP Store Locator Settings">' . __( 'Settings', 'wpsl' ) . '</a>'; array_unshift( $links, $settings_link ); } return $links; } /** * Add links to the plugin meta row. * * @since 2.1.1 * @param array $links The existing meta links * @param string $file The file path of the current plugin * @return array $links The modified meta links */ public function add_plugin_meta_row( $links, $file ) { if ( strpos( $file, 'wp-store-locator.php' ) !== false ) { $new_links = array( '<a href="https://wpstorelocator.co/documentation/" title="View Documentation">'. __( 'Documentation', 'wpsl' ).'</a>', '<a href="https://wpstorelocator.co/add-ons/" title="View Add-Ons">'. __( 'Add-Ons', 'wpsl' ).'</a>' ); $links = array_merge( $links, $new_links ); } return $links; } /** * Change the footer text on the settings page. * * @since 2.0.0 * @param string $text The current footer text * @return string $text Either the original or modified footer text */ public function admin_footer_text( $text ) { $current_screen = get_current_screen(); // Only modify the footer text if we are on the settings page of the wp store locator. if ( isset( $current_screen->id ) && $current_screen->id == 'wpsl_stores_page_wpsl_settings' ) { $text = sprintf( __( 'If you like this plugin please leave us a %s5 star%s rating.', 'wpsl' ), '<a href="https://wordpress.org/support/view/plugin-reviews/wp-store-locator?filter=5#postform" target="_blank"><strong>', '</strong></a>' ); } return $text; } } $GLOBALS['wpsl_admin'] = new WPSL_Admin(); } class-geocode.php 0000644 00000033203 15132765753 0010004 0 ustar 00 <?php /** * Geocode store locations * * @author Tijmen Smit * @since 2.0.0 */ if ( !defined( 'ABSPATH' ) ) exit; if ( !class_exists( 'WPSL_Geocode' ) ) { class WPSL_Geocode { /** * Check if we need to run a geocode request or use the current location data. * * The latlng value is only present if the user provided it himself, or used the preview * on the map. Otherwise the latlng will be missing and we need to geocode the supplied address. * * @since 2.0.0 * @param integer $post_id Store post ID * @param array $store_data The store data * @return void */ public function check_geocode_data( $post_id, $store_data ) { $location_data = array(); // Check if the latlng data is valid. $latlng = $this->validate_latlng( $store_data['lat'], $store_data['lng'] ); // If we don't have a valid latlng value, we geocode the supplied address to get one. if ( !$latlng ) { $response = $this->geocode_location( $post_id, $store_data ); if ( empty( $response ) ) { return; } $location_data['country_iso'] = $response['country_iso']; $location_data['latlng'] = $response['latlng']; } else { $location_data['latlng'] = $latlng; } // Restrict the latlng to a max of 6 decimals. $location_data['latlng'] = $this->format_latlng( $location_data['latlng'] ); $location_data['lat'] = $location_data['latlng']['lat']; $location_data['lng'] = $location_data['latlng']['lng']; $this->save_store_location( $post_id, $location_data ); } /** * Geocode the store location. * * @since 1.0.0 * @param integer $post_id Store post ID * @param array $store_data The submitted store data ( address, city, country etc ) * @return void */ public function geocode_location( $post_id, $store_data ) { $geocode_response = $this->get_latlng( $store_data ); if ( isset( $geocode_response['status'] ) ) { switch ( $geocode_response['status'] ) { case 'OK': $country = $this->filter_country_name( $geocode_response ); $location_data = array( 'country_iso' => $country['short_name'], 'latlng' => $this->format_latlng( $geocode_response['results'][0]['geometry']['location'] ) ); return $location_data; case 'ZERO_RESULTS': $msg = __( 'The Google Geocoding API returned no results for the supplied address. Please change the address and try again.', 'wpsl' ); break; case 'OVER_QUERY_LIMIT': $msg = sprintf( __( 'You have reached the daily allowed geocoding limit, you can read more %shere%s.', 'wpsl' ), '<a target="_blank" href="https://developers.google.com/maps/documentation/geocoding/#Limits">', '</a>' ); break; case 'REQUEST_DENIED': $msg = sprintf( __( 'The Google Geocoding API returned REQUEST_DENIED. %s', 'wpsl' ), $this->check_geocode_error_msg( $geocode_response ) ); break; default: $msg = __( 'The Google Geocoding API failed to return valid data, please try again later.', 'wpsl' ); break; } } else { $msg = $geocode_response; } // Handle the geocode code errors messages. if ( !empty( $msg ) ) { $this->geocode_failed( $msg, $post_id ); } } /** * Check if the response from the Geocode API contains an error message. * * @since 2.1.0 * @param array $geocode_response The response from the Geocode API. * @return string $error_msg The error message, or empty if none exists. */ public function check_geocode_error_msg( $geocode_response, $inc_breaks = true ) { $breaks = ( $inc_breaks ) ? '<br><br>' : ''; if ( isset( $geocode_response['error_message'] ) && $geocode_response['error_message'] ) { // If the problem is IP based, then show a different error msg. if ( strpos( $geocode_response['error_message'], 'IP' ) !== false ) { $error_msg = sprintf( __( '%sError message: %s. %s Make sure the IP address mentioned in the error matches with the IP set as the %sreferrer%s for the server API key in the %sGoogle API Console%s.', 'wpsl' ), $breaks, $this->clickable_error_links( $geocode_response['error_message'] ), $breaks, '<a href="https://wpstorelocator.co/document/create-google-api-keys/#server-key-referrer">', '</a>', '<a href="https://console.developers.google.com">', '</a>' ); } else { $error_msg = sprintf( __( '%sError message: %s %s Check if your issue is covered in the %stroubleshooting%s section, if not, then please open a %ssupport ticket%s.', 'wpsl' ), $breaks, $this->clickable_error_links( $geocode_response['error_message'] ), $breaks, '<a href="https://wpstorelocator.co/document/create-google-api-keys/#troubleshooting">', '</a>', '<a href="https://wpstorelocator.co/support/">', '</a>' ); } } else { $error_msg = ''; } return $error_msg; } /** * Make the API call to Google to geocode the address. * * @since 1.0.0 * @param array $store_data The store data * @return array|string $geo_response The response from the Google Geocode API, or the wp_remote_get error message. */ public function get_latlng( $store_data ) { $address = $this->create_geocode_address( $store_data ); $response = wpsl_call_geocode_api( $address ); if ( is_wp_error( $response ) ) { $geo_response = sprintf( __( 'Something went wrong connecting to the Google Geocode API: %s %s Please try again later.', 'wpsl' ), $response->get_error_message(), '<br><br>' ); } else if ( $response['response']['code'] == 500 ) { $geo_response = sprintf( __( 'The Google Geocode API reported the following problem: error %s %s %s Please try again later.', 'wpsl' ), $response['response']['code'], $response['response']['message'], '<br><br>' ); } else if ( $response['response']['code'] == 400 ) { // Check on which page the 400 error was triggered, and based on that adjust the msg. if ( isset( $_GET['page'] ) && $_GET['page'] == 'wpsl_csv' ) { $data_issue = sprintf( __( 'You can fix this by making sure the CSV file uses %sUTF-8 encoding%s.', 'wpsl' ), '<a href="https://wpstorelocator.co/document/csv-manager/#utf8">', '</a>' ); } else if ( !$address ) { $data_issue = __( 'You need to provide the details for either the address, city, state or country before the API can return coordinates.', 'wpsl' ); // this is only possible if the required fields are disabled with custom code. } $geo_response = sprintf( __( 'The Google Geocode API reported the following problem: error %s %s %s %s', 'wpsl' ), $response['response']['code'], $response['response']['message'], '<br><br>', $data_issue ); } else if ( $response['response']['code'] != 200 ) { $geo_response = sprintf( __( 'The Google Geocode API reported the following problem: error %s %s %s Please contact %ssupport%s if the problem persists.', 'wpsl' ), $response['response']['code'], $response['response']['message'], '<br><br>', '<a href="https://wpstorelocator.co/support/">', '</a>' ); } else { $geo_response = json_decode( $response['body'], true ); } return $geo_response; } /** * Create the address we need to Geocode. * * @since 2.1.0 * @param array $store_data The provided store data * @return string $geocode_address The address we are sending to the Geocode API separated by , */ public function create_geocode_address( $store_data ) { $address = array(); $address_parts = array( 'address', 'city', 'state', 'zip', 'country' ); foreach ( $address_parts as $address_part ) { if ( isset( $store_data[$address_part] ) && $store_data[$address_part] ) { $address[] = trim( $store_data[$address_part] ); } } $geocode_address = implode( ',', $address ); return $geocode_address; } /** * If there is a problem with the geocoding then we save the notice and change the post status to pending. * * @since 2.0.0 * @param string $msg The geocode error message * @param integer $post_id Store post ID * @return void */ public function geocode_failed( $msg, $post_id ) { global $wpsl_admin; $wpsl_admin->notices->save( 'error', $msg ); $wpsl_admin->metaboxes->set_post_pending( $post_id ); } /** * Save the store location data. * * @since 2.0.0 * @param integer $post_id Store post ID * @param array $location_data The country code and latlng * @return void */ public function save_store_location( $post_id, $location_data ) { if ( isset( $location_data['country_iso'] ) && ( !empty( $location_data['country_iso'] ) ) ) { update_post_meta( $post_id, 'wpsl_country_iso', $location_data['country_iso'] ); } update_post_meta( $post_id, 'wpsl_lat', $location_data['latlng']['lat'] ); update_post_meta( $post_id, 'wpsl_lng', $location_data['latlng']['lng'] ); } /** * Make sure the latlng value has a max of 6 decimals. * * @since 2.0.0 * @param array $latlng The latlng data * @return array $latlng The formatted latlng */ public function format_latlng( $latlng ) { foreach ( $latlng as $key => $value ) { if ( strlen( substr( strrchr( $value, '.' ), 1 ) ) > 6 ) { $latlng[$key] = round( $value, 6 ); } } return $latlng; } /** * Filter out the two letter country code from the Geocode API response. * * @since 1.0.0 * @param array $response The full API geocode response * @return array $country The country ISO code and full name */ public function filter_country_name( $response ) { $length = count( $response['results'][0]['address_components'] ); $country = array(); // Loop over the address components until we find the country, political part. for ( $i = 0; $i < $length; $i++ ) { $address_component = $response['results'][0]['address_components'][$i]['types']; if ( $address_component[0] == 'country' && $address_component[1] == 'political' ) { $country = $response['results'][0]['address_components'][$i]; break; } } return $country; } /** * Validate the latlng values. * * @since 1.0.0 * @param string $lat The latitude value * @param string $lng The longitude value * @return boolean|array $latlng The validated latlng values or false if it fails */ public function validate_latlng( $lat, $lng ) { if ( !is_numeric( $lat ) || ( $lat > 90 ) || ( $lat < -90 ) ) { return false; } if ( !is_numeric( $lng ) || ( $lng > 180 ) || ( $lng < -180 ) ) { return false; } $latlng = array( 'lat' => $lat, 'lng' => $lng ); return $latlng; } /** * Error messages returned by the Google Maps API * don't always contain clickable links. * * They now just look like this http://g.co/dev/maps-no-account * and are not clickable. To change this we wrap an href around it. * * @since 2.2.22 * @return void */ public function clickable_error_links( $msg ) { // Make sure the URLS aren't clickable yet. They aren't at the moment, but maybe Google changes this in the future. if ( strpos( $msg,'href' ) === false ) { preg_match_all( '#\bhttp(s?)?://[^,\s()<>]+(?:\([\w\d]+\)|([^,[:punct:]\s]|/))#', $msg, $match ); foreach ( $match[0] as $k => $url ) { $msg = str_replace( $url, '<a href="' . esc_url( $url ) . '">' . esc_html( $url ) . '</a>', $msg ); } } return $msg; } } } upgrade.php 0000644 00000063211 15132765753 0006725 0 ustar 00 <?php add_action( 'admin_init', 'wpsl_check_upgrade' ); add_action( 'admin_init', 'wpsl_cpt_update_state' ); /** * If the db doesn't hold the current version, run the upgrade procedure * * @since 1.2 * @return void */ function wpsl_check_upgrade() { global $wpsl_settings; $current_version = get_option( 'wpsl_version' ); if ( version_compare( $current_version, WPSL_VERSION_NUM, '==' ) ) return; if ( version_compare( $current_version, '1.1', '<' ) ) { if ( is_array( $wpsl_settings ) ) { if ( empty( $wpsl_settings['reset_map'] ) ) { $wpsl_settings['reset_map'] = 0; } if ( empty( $wpsl_settings['auto_load'] ) ) { $wpsl_settings['auto_load'] = 1; } if ( empty( $wpsl_settings['new_window'] ) ) { $wpsl_settings['new_window'] = 0; } update_option( 'wpsl_settings', $wpsl_settings ); } } if ( version_compare( $current_version, '1.2', '<' ) ) { if ( is_array( $wpsl_settings ) ) { if ( empty( $wpsl_settings['store_below'] ) ) { $wpsl_settings['store_below'] = 0; } if ( empty( $wpsl_settings['direction_redirect'] ) ) { $wpsl_settings['direction_redirect'] = 0; } update_option( 'wpsl_settings', $wpsl_settings ); } } if ( version_compare( $current_version, '1.2.11', '<' ) ) { if ( is_array( $wpsl_settings ) ) { if ( empty( $wpsl_settings['more_info'] ) ) { $wpsl_settings['more_info'] = 0; } if ( empty( $wpsl_settings['more_label'] ) ) { $wpsl_settings['more_label'] = __( 'More info', 'wpsl' ); } if ( empty( $wpsl_settings['mouse_focus'] ) ) { $wpsl_settings['mouse_focus'] = 0; } update_option( 'wpsl_settings', $wpsl_settings ); } } if ( version_compare( $current_version, '1.2.12', '<' ) ) { if ( is_array( $wpsl_settings ) ) { if ( empty( $wpsl_settings['more_info_location'] ) ) { $wpsl_settings['more_info_location'] = __( 'info window', 'wpsl' ); } if ( empty( $wpsl_settings['back_label'] ) ) { $wpsl_settings['back_label'] = __( 'Back', 'wpsl' ); } if ( empty( $wpsl_settings['reset_label'] ) ) { $wpsl_settings['reset_label'] = __( 'Reset', 'wpsl' ); } if ( empty( $wpsl_settings['store_below_scroll'] ) ) { $wpsl_settings['store_below_scroll'] = 0; } update_option( 'wpsl_settings', $wpsl_settings ); } } if ( version_compare( $current_version, '1.2.20', '<' ) ) { global $wpdb; $wpsl_table = $wpdb->prefix . 'wpsl_stores'; // Rename the street field to address. $wpdb->query( "ALTER TABLE $wpsl_table CHANGE street address VARCHAR(255)" ); // Add the second address field. $wpdb->query( "ALTER TABLE $wpsl_table ADD address2 VARCHAR(255) NULL AFTER address" ); if ( is_array( $wpsl_settings ) ) { if ( empty( $wpsl_settings['store_url'] ) ) { $wpsl_settings['store_url'] = 0; } if ( empty( $wpsl_settings['phone_url'] ) ) { $wpsl_settings['phone_url'] = 0; } if ( empty( $wpsl_settings['marker_clusters'] ) ) { $wpsl_settings['marker_clusters'] = 0; } if ( empty( $wpsl_settings['cluster_zoom'] ) ) { $wpsl_settings['cluster_zoom'] = 0; } if ( empty( $wpsl_settings['cluster_size'] ) ) { $wpsl_settings['cluster_size'] = 0; } if ( empty( $wpsl_settings['template_id'] ) ) { $wpsl_settings['template_id'] = ( $wpsl_settings['store_below'] ) ? 1 : 0; unset( $wpsl_settings['store_below'] ); } if ( empty( $wpsl_settings['marker_streetview'] ) ) { $wpsl_settings['marker_streetview'] = 0; } if ( empty( $wpsl_settings['marker_zoom_to'] ) ) { $wpsl_settings['marker_zoom_to'] = 0; } if ( !isset( $wpsl_settings['editor_country'] ) ) { $wpsl_settings['editor_country'] = ''; } if ( empty( $wpsl_settings['street_view_label'] ) ) { $wpsl_settings['street_view_label'] = __( 'Street view', 'wpsl' ); } if ( empty( $wpsl_settings['zoom_here_label'] ) ) { $wpsl_settings['zoom_here_label'] = __( 'Zoom here', 'wpsl' ); } if ( empty( $wpsl_settings['no_directions_label'] ) ) { $wpsl_settings['no_directions_label'] = __( 'No route could be found between the origin and destination', 'wpsl' ); } update_option( 'wpsl_settings', $wpsl_settings ); } } if ( version_compare( $current_version, '2.0', '<' ) ) { global $wpdb; $wpsl_table = $wpdb->prefix . 'wpsl_stores'; if ( is_array( $wpsl_settings ) ) { if ( empty( $wpsl_settings['radius_dropdown'] ) ) { $wpsl_settings['radius_dropdown'] = 1; } if ( empty( $wpsl_settings['permalinks'] ) ) { $wpsl_settings['permalinks'] = 0; } if ( empty( $wpsl_settings['permalink_slug'] ) ) { $wpsl_settings['permalink_slug'] = __( 'stores', 'wpsl' ); } if ( empty( $wpsl_settings['category_slug'] ) ) { $wpsl_settings['category_slug'] = __( 'store-category', 'wpsl' ); } if ( empty( $wpsl_settings['editor_hours'] ) ) { $wpsl_settings['editor_hours'] = wpsl_default_opening_hours(); } if ( empty( $wpsl_settings['editor_hour_format'] ) ) { $wpsl_settings['editor_hour_format'] = 12; } if ( empty( $wpsl_settings['editor_map_type'] ) ) { $wpsl_settings['editor_map_type'] = 'roadmap'; } if ( empty( $wpsl_settings['infowindow_style'] ) ) { $wpsl_settings['infowindow_style'] = 'default'; } if ( empty( $wpsl_settings['email_label'] ) ) { $wpsl_settings['email_label'] = __( 'Email', 'wpsl' ); } if ( empty( $wpsl_settings['url_label'] ) ) { $wpsl_settings['url_label'] = __( 'Url', 'wpsl' ); } if ( empty( $wpsl_settings['category_label'] ) ) { $wpsl_settings['category_label'] = __( 'Category filter', 'wpsl' ); } if ( empty( $wpsl_settings['show_credits'] ) ) { $wpsl_settings['show_credits'] = 0; } if ( empty( $wpsl_settings['autoload_limit'] ) ) { $wpsl_settings['autoload_limit'] = 50; } if ( empty( $wpsl_settings['scrollwheel'] ) ) { $wpsl_settings['scrollwheel'] = 1; } if ( empty( $wpsl_settings['type_control'] ) ) { $wpsl_settings['type_control'] = 0; } if ( empty( $wpsl_settings['hide_hours'] ) ) { $wpsl_settings['hide_hours'] = 0; } // Either correct the existing map style format from the 2.0 beta or set it to empty. if ( isset( $wpsl_settings['map_style'] ) && is_array( $wpsl_settings['map_style'] ) && isset( $wpsl_settings['map_style']['id'] ) ) { switch( $wpsl_settings['map_style']['id'] ) { case 'custom': $map_style = $wpsl_settings['map_style']['custom_json']; break; case 'default': $map_style = ''; break; default: $map_style = $wpsl_settings['map_style']['theme_json']; break; } $wpsl_settings['map_style'] = $map_style; } else { $wpsl_settings['map_style'] = ''; } if ( empty( $wpsl_settings['autoload'] ) ) { $wpsl_settings['autoload'] = $wpsl_settings['auto_load']; unset( $wpsl_settings['auto_load'] ); } if ( empty( $wpsl_settings['address_format'] ) ) { $wpsl_settings['address_format'] = 'city_state_zip'; } if ( empty( $wpsl_settings['auto_zoom_level'] ) ) { $wpsl_settings['auto_zoom_level'] = 15; } if ( empty( $wpsl_settings['hide_distance'] ) ) { $wpsl_settings['hide_distance'] = 0; } if ( empty( $wpsl_settings['debug'] ) ) { $wpsl_settings['debug'] = 0; } if ( empty( $wpsl_settings['category_dropdown'] ) ) { $wpsl_settings['category_dropdown'] = 0; } /* * Replace marker_bounce with marker_effect to better reflect what the option contains. * * If a user hovers over the result list then either the corresponding marker will bounce, * the info window will open, or nothing will happen. * * The default behaviour is that the marker will bounce. */ if ( empty( $wpsl_settings['marker_effect'] ) ) { $wpsl_settings['marker_effect'] = ( $wpsl_settings['marker_bounce'] ) ? 'bounce' : 'ignore'; unset( $wpsl_settings['marker_bounce'] ); } /* * The default input for the opening hours is set to textarea for current users, * for new users it will be set to dropdown ( easier to format in a table output and to use with schema.org in the future ). */ if ( empty( $wpsl_settings['editor_hour_input'] ) ) { $wpsl_settings['editor_hour_input'] = 'textarea'; } // Rename store_below_scroll to listing_below_no_scroll, it better reflects what it does. if ( empty( $wpsl_settings['listing_below_no_scroll'] ) && isset( $wpsl_settings['store_below_scroll'] ) ) { $wpsl_settings['listing_below_no_scroll'] = $wpsl_settings['store_below_scroll']; unset( $wpsl_settings['store_below_scroll'] ); } // Change the template ids from number based to name based. if ( is_numeric( $wpsl_settings['template_id'] ) ) { $wpsl_settings['template_id'] = ( !$wpsl_settings['template_id'] ) ? 'default' : 'below_map'; } $replace_data = array( 'max_results' => $wpsl_settings['max_results'], 'search_radius' => $wpsl_settings['search_radius'] ); /* * Replace the () with [], this fixes an issue with the mod_security module that is installed on some servers. * It triggerd a 'Possible SQL injection attack' warning probably because of the int,(int) format of the data. */ foreach ( $replace_data as $index => $option_value ) { $wpsl_settings[$index] = str_replace( array( '(', ')' ), array( '[', ']' ), $option_value ); } // The reset button now uses an icon instead of text, so no need for the label anymore. unset( $wpsl_settings['reset_label'] ); update_option( 'wpsl_settings', $wpsl_settings ); /* * Users upgrading from 1.x will be given the choice between the textarea or * dropdowns for the opening hours. * * New users don't get that choice, they will only get the dropdowns. * * The wpsl_legacy_support option is used to determine if we need to show both options. */ update_option( 'wpsl_legacy_support', 1 ); // Add the WPSL roles and caps. wpsl_add_roles(); wpsl_add_caps(); // If there is a wpsl_stores table, then we need to convert all the locations to the 'wpsl_stores' custom post type. if ( $wpdb->get_var( "SHOW TABLES LIKE '$wpsl_table'" ) && version_compare( $current_version, '1.9', '<' ) ) { if ( wpsl_remaining_cpt_count() ) { update_option( 'wpsl_convert_cpt', 'in_progress' ); } } } } /* * Both map options are no longer supported in 3.22 of the Google Maps API. * See: https://developers.google.com/maps/articles/v322-controls-diff */ if ( version_compare( $current_version, '2.0.3', '<' ) ) { unset( $wpsl_settings['control_style'] ); unset( $wpsl_settings['pan_controls'] ); update_option( 'wpsl_settings', $wpsl_settings ); } if ( version_compare( $current_version, '2.1.0', '<' ) ) { if ( !isset( $wpsl_settings['api_geocode_component'] ) ) { $wpsl_settings['api_geocode_component'] = 0; } update_option( 'wpsl_settings', $wpsl_settings ); } if ( version_compare( $current_version, '2.2', '<' ) ) { $wpsl_settings['autocomplete'] = 0; $wpsl_settings['category_default_label'] = __( 'Any', 'wpsl' ); // Rename the 'zoom_name' and 'zoom_latlng' to 'start_name' and 'start_latlng'. if ( isset( $wpsl_settings['zoom_name'] ) ) { $wpsl_settings['start_name'] = $wpsl_settings['zoom_name']; unset( $wpsl_settings['zoom_name'] ); } if ( isset( $wpsl_settings['zoom_latlng'] ) ) { $wpsl_settings['start_latlng'] = $wpsl_settings['zoom_latlng']; unset( $wpsl_settings['zoom_latlng'] ); } if ( isset( $wpsl_settings['category_dropdown'] ) ) { $wpsl_settings['category_filter'] = $wpsl_settings['category_dropdown']; unset( $wpsl_settings['category_dropdown'] ); } // We now have separate browser and server key fields, and assume the existing key is a server key. if ( isset( $wpsl_settings['api_key'] ) ) { $wpsl_settings['api_server_key'] = $wpsl_settings['api_key']; unset( $wpsl_settings['api_key'] ); } $wpsl_settings['api_browser_key'] = ''; $wpsl_settings['category_filter_type'] = 'dropdown'; $wpsl_settings['hide_country'] = 0; $wpsl_settings['show_contact_details'] = 0; update_option( 'wpsl_settings', $wpsl_settings ); } if ( version_compare( $current_version, '2.2.4', '<' ) ) { $wpsl_settings['deregister_gmaps'] = 0; update_option( 'wpsl_settings', $wpsl_settings ); } if ( version_compare( $current_version, '2.2.9', '<' ) ) { $wpsl_settings['run_fitbounds'] = 1; update_option( 'wpsl_settings', $wpsl_settings ); } if ( version_compare( $current_version, '2.2.13', '<' ) ) { $wpsl_settings['clickable_contact_details'] = 0; update_option( 'wpsl_settings', $wpsl_settings ); } if ( version_compare( $current_version, '2.2.20', '<' ) ) { $wpsl_settings['force_postalcode'] = 0; $wpsl_settings['permalink_remove_front'] = 0; update_option( 'wpsl_settings', $wpsl_settings ); } if ( version_compare( $current_version, '2.2.22', '<' ) ) { $wpsl_settings['delay_loading'] = 0; update_option( 'wpsl_settings', $wpsl_settings ); } update_option( 'wpsl_version', WPSL_VERSION_NUM ); } /** * Check if we need to show the notice that tells users that the store locations * need to be converted to custom post types before the update from 1.x to 2.x is complete. * * @since 2.0 * @return void */ function wpsl_cpt_update_state() { global $wpsl_admin; $conversion_state = get_option( 'wpsl_convert_cpt' ); if ( $conversion_state == 'in_progress' ) { if ( ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ) ) { $remaining = wpsl_remaining_cpt_count(); $wpsl_admin->notices->save( 'error', sprintf( __( 'Because you updated WP Store Locator from version 1.x, the %s current store locations need to be %sconverted%s to custom post types.', 'wpsl' ), "<span class='wpsl-cpt-remaining'>" . $remaining . "</span>", "<a href='#' id='wpsl-cpt-dialog'>", "</a>" ) ); add_action( 'admin_footer', 'wpsl_cpt_dialog_html' ); } add_action( 'admin_enqueue_scripts', 'wpsl_convert_cpt_js' ); add_action( 'wp_ajax_convert_cpt', 'wpsl_convert_cpt' ); add_action( 'wp_ajax_convert_cpt_count', 'wpsl_convert_cpt_count' ); } } /** * Include the js file that handles the ajax request to * start converting the 1.x store locations to custom post types. * * @since 2.0 * @return void */ function wpsl_convert_cpt_js() { $cpt_js_l10n = array( 'timeout' => sprintf( __( 'The script converting the locations timed out. %s You can click the "Start Converting" button again to restart the script. %s If there are thousands of store locations left to convert and you keep seeing this message, then you can try to contact your host and ask if they can increase the maximum execution time. %s The plugin tried to disable the maximum execution time, but if you are reading this then that failed.', 'wpsl' ), '<br><br>', '<br><br>', '<br><br>' ), 'securityFail' => __( 'Security check failed, reload the page and try again.', 'wpsl' ) ); wp_enqueue_script( 'jquery-ui-dialog' ); wp_enqueue_script( 'wpsl-queue', plugins_url( '/js/ajax-queue.js', __FILE__ ), array( 'jquery' ), false ); wp_enqueue_script( 'wpsl-cpt-js', plugins_url( '/js/wpsl-cpt-upgrade.js', __FILE__ ), array( 'jquery' ), false ); wp_localize_script( 'wpsl-cpt-js', 'wpslCptConversion', $cpt_js_l10n ); } /** * The html for the lightbox * * @since 2.0 * @return void */ function wpsl_cpt_dialog_html() { ?> <div id="wpsl-cpt-lightbox" style="display:none;"> <span class="tb-close-icon"></span> <p class="wpsl-cpt-remaining"><?php _e( 'Store locations to convert:', 'wpsl' ); echo '<span></span>'; ?></p> <div class="wslp-cpt-fix-wrap"> <input id="wpsl-start-cpt-conversion" class="button-primary" type="submit" value="<?php _e( 'Start Converting', 'wpsl' ); ?>" > <img class="wpsl-preloader" alt="preloader" src="<?php echo WPSL_URL . 'img/ajax-loader.gif'; ?>" /> </div> <input type="hidden" name="wpsl-cpt-fix-nonce" value="<?php echo wp_create_nonce( 'wpsl-cpt-fix' ); ?>" /> <input type="hidden" name="wpsl-cpt-conversion-count" value="<?php echo wp_create_nonce( 'wpsl-cpt-count' ); ?>" /> </div> <div id="wpsl-cpt-overlay" style="display:none;"></div> <style> .wslp-cpt-fix-wrap { float:left; clear:both; width:100%; margin:0 0 15px 0; } #wpsl-cpt-lightbox .wpsl-cpt-remaining span { margin-left:5px; } #wpsl-start-cpt-conversion { float:left; } .wslp-cpt-fix-wrap .wpsl-preloader, .wslp-cpt-fix-wrap span { float:left; margin:8px 0 0 10px; } .wslp-cpt-fix-wrap .wpsl-preloader { display: none; } #wpsl-cpt-lightbox { position:fixed; width:450px; left:50%; right:50%; top:3.8em; padding:15px; background:none repeat scroll 0 0 #fff; border-radius:3px; margin-left:-225px; z-index: 9999; } #wpsl-cpt-overlay { position:fixed; right:0; top:0; z-index:9998; background:none repeat scroll 0 0 #000; bottom:0; left:0; opacity:0.5; } .tb-close-icon { color: #666; text-align: center; line-height: 29px; width: 29px; height: 29px; position: absolute; top: 0; right: 0; } .tb-close-icon:before { content: '\f158'; font: normal 20px/29px 'dashicons'; speak: none; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .tb-close-icon:hover { color: #999 !important; cursor: pointer; } </style> <?php } /** * Handle the ajax call to start converting the * store locations to custom post types. * * @since 2.0 * @return void|string json on completion */ function wpsl_convert_cpt() { if ( !current_user_can( 'manage_options' ) ) die( '-1' ); check_ajax_referer( 'wpsl-cpt-fix' ); // Start the cpt coversion. wpsl_cpt_conversion(); exit(); } /** * Get the amount of locations that still need to be converted. * * @since 2.0 * @return string json The amount of locations that still need to be converted */ function wpsl_convert_cpt_count() { if ( !current_user_can( 'manage_options' ) ) die( '-1' ); check_ajax_referer( 'wpsl-cpt-count' ); $remaining_count = wpsl_remaining_cpt_count(); $response['success'] = true; if ( $remaining_count ) { $response['count'] = $remaining_count; } else { $response['url'] = sprintf( __( 'All the store locations are now converted to custom post types. %s You can view them on the %sAll Stores%s page.', 'wpsl' ), '<br><br>', '<a href="' . admin_url( 'edit.php?post_type=wpsl_stores' ) . '">', '</a>' ); delete_option( 'wpsl_convert_cpt' ); } wp_send_json( $response ); exit(); } /** * Return the difference between the number of existing wpsl custom post types, * and the number of records in the old wpsl_stores database. * * @since 2.0 * @return int|boolean $remaining The amount of locations that still need to be converted */ function wpsl_remaining_cpt_count() { global $wpdb; $table = $wpdb->prefix . 'wpsl_stores'; $count = wp_count_posts( 'wpsl_stores' ); if ( isset( $count->publish ) && isset( $count->draft ) ) { $cpt_count = $count->publish + $count->draft; } else { $cpt_count = 0; } $db_count = $wpdb->get_var( "SELECT COUNT(wpsl_id) FROM $table" ); $difference = $db_count - $cpt_count; /* * This prevents users who used the 2.0 beta, and later added * more stores from seeing the upgrade notice again. */ $remaining = ( $difference < 0 ) ? false : $difference; return $remaining; } /** * Convert the existing locations to custom post types. * * @since 2.0 * @return void|boolean True if the conversion is completed */ function wpsl_cpt_conversion() { global $wpdb; // Try to disable the time limit to prevent timeouts. @set_time_limit( 0 ); $meta_keys = array( 'address', 'address2', 'city', 'state', 'zip', 'country', 'country_iso', 'lat', 'lng', 'phone', 'fax', 'url', 'email', 'hours' ); $offset = wpsl_remaining_cpt_count(); $wpsl_table = $wpdb->prefix . 'wpsl_stores'; $stores = $wpdb->get_results( "(SELECT * FROM $wpsl_table ORDER BY wpsl_id DESC LIMIT $offset) ORDER BY wpsl_id ASC" ); foreach ( $stores as $store ) { // Make sure we set the correct post status. if ( $store->active ) { $post_status = 'publish'; } else { $post_status = 'draft'; } $post = array ( 'post_type' => 'wpsl_stores', 'post_status' => $post_status, 'post_title' => $store->store, 'post_content' => $store->description ); $post_id = wp_insert_post( $post ); if ( $post_id ) { // Save the data from the wpsl_stores db table as post meta data. foreach ( $meta_keys as $meta_key ) { if ( isset( $store->{$meta_key} ) && !empty( $store->{$meta_key} ) ) { update_post_meta( $post_id, 'wpsl_' . $meta_key, $store->{$meta_key} ); } } // If we have a thumb ID set the post thumbnail for the inserted post. if ( $store->thumb_id ) { set_post_thumbnail( $post_id, $store->thumb_id ); } } } } data-export.php 0000644 00000005541 15132765753 0007530 0 ustar 00 <?php add_action( 'admin_init', 'wpsl_single_location_export' ); /** * Handle the export of a single store location. * * Creates a CSV file holding the location details * that can be handed over in case a GDPR related * data access request is received. * * @since 2.2.15 * @return void */ function wpsl_single_location_export() { global $wpsl_admin; // From the WPSL plugin if ( isset( $_GET['wpsl_data_export'] ) && isset( $_GET['wpsl_export_nonce'] ) ) { $post_id = absint( $_GET['post'] ); if ( !wp_verify_nonce( $_GET['wpsl_export_nonce'], 'wpsl_export_' . $post_id ) ) return; if ( is_int( wp_is_post_revision( $post_id ) ) ) return; if ( !current_user_can( 'edit_post', $post_id ) ) return; $meta_fields = array(); $wp_field_map = array( 'wpsl_id' => 'ID', 'name' => 'post_title', 'status' => 'post_status', 'permalink' => 'post_name', 'description' => 'post_content', 'excerpt' => 'post_excerpt', 'author' => 'post_author', 'date' => 'post_date' ); $meta_box_fields = $wpsl_admin->metaboxes->meta_box_fields(); $fields = array_keys( $wp_field_map ); array_push( $fields, 'image', 'category', 'tags' ); foreach ( $meta_box_fields as $k => $field_section ) { foreach ( $field_section as $field_name => $field_value ) { $meta_fields[] = $field_name; } } $meta_data = get_post_custom( $post_id ); $post_meta = array(); // Loop over the wpsl meta fields, and collect the meta data. foreach ( $meta_fields as $meta_field ) { if ( $meta_field !== 'hours' ) { if ( isset( $meta_data['wpsl_' . $meta_field][0] ) ) { $post_meta['data'][$meta_field] = $meta_data['wpsl_' . $meta_field][0]; } else { $post_meta['data'][$meta_field] = ''; } $post_meta['headers'][] = $meta_field; } } // Make it possible to add additional custom data from for example ACF $post_meta = apply_filters( 'wpsl_single_location_export_data', $post_meta, $post_id ); if ( $post_meta ) { $file_name = 'wpsl-export-' . $post_id . '-' . date('Ymd' ) . '.csv'; // Set the download headers for the CSV file. header( 'Content-Type: text/csv; charset=utf-8' ); header( 'Content-Disposition: attachment; filename=' . $file_name . '' ); $output = fopen( 'php://output', 'w' ); fputcsv( $output, $post_meta['headers'] ); fputcsv( $output, $post_meta['data'] ); fclose( $output ); } exit(); } } font/fontello.svg 0000644 00000003153 15132765753 0010075 0 ustar 00 <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg xmlns="http://www.w3.org/2000/svg"> <metadata>Copyright (C) 2015 by original authors @ fontello.com</metadata> <defs> <font id="fontello" horiz-adv-x="1000" > <font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" /> <missing-glyph horiz-adv-x="1000" /> <glyph glyph-name="location" unicode="" d="m429 493q0 59-42 101t-101 42-101-42-42-101 42-101 101-42 101 42 42 101z m142 0q0-61-18-100l-203-432q-9-18-27-29t-37-11-38 11-26 29l-204 432q-18 39-18 100 0 118 84 202t202 84 202-84 83-202z" horiz-adv-x="571.4" /> <glyph glyph-name="attention-circled" unicode="" d="m429 779q116 0 215-58t156-156 57-215-57-215-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58z m71-696v106q0 8-5 13t-12 5h-107q-8 0-13-5t-6-13v-106q0-8 6-13t13-6h107q7 0 12 6t5 13z m-1 192l10 346q0 7-6 10-5 5-13 5h-123q-8 0-13-5-6-3-6-10l10-346q0-6 5-10t14-4h103q8 0 13 4t6 10z" horiz-adv-x="857.1" /> <glyph glyph-name="cancel-circle" unicode="" d="m1000 349q0-136-67-251t-182-182-251-67-251 67-182 182-67 251 67 251 182 182 251 67 251-67 182-182 67-251z m-339-232l71 71-161 161 161 161-71 71-161-161-161 161-71-71 161-161-161-161 71-71 161 161z" horiz-adv-x="1000" /> <glyph glyph-name="plus-circle" unicode="" d="m1000 349q0-136-67-251t-182-182-251-67-251 67-182 182-67 251 67 251 182 182 251 67 251-67 182-182 67-251z m-440-58h235v118h-235v235h-118v-235h-236v-118h236v-236h118v236z" horiz-adv-x="1000" /> </font> </defs> </svg>