WP Store Locator - Version 2.2.18

Version Description

Download this release

Release Info

Developer tijmensmit
Plugin Icon 128x128 WP Store Locator
Version 2.2.18
Comparing to
See all releases

Code changes from version 2.2.17 to 2.2.18

admin/EDD_SL_Plugin_Updater.php CHANGED
@@ -1,491 +1,491 @@
1
- <?php
2
-
3
- // Exit if accessed directly
4
- if ( ! defined( 'ABSPATH' ) ) exit;
5
-
6
- /**
7
- * Allows plugins to use their own update API.
8
- *
9
- * @author Easy Digital Downloads
10
- * @version 1.6.14
11
- */
12
- class EDD_SL_Plugin_Updater {
13
-
14
- private $api_url = '';
15
- private $api_data = array();
16
- private $name = '';
17
- private $slug = '';
18
- private $version = '';
19
- private $wp_override = false;
20
- private $cache_key = '';
21
-
22
- /**
23
- * Class constructor.
24
- *
25
- * @uses plugin_basename()
26
- * @uses hook()
27
- *
28
- * @param string $_api_url The URL pointing to the custom API endpoint.
29
- * @param string $_plugin_file Path to the plugin file.
30
- * @param array $_api_data Optional data to send with API calls.
31
- */
32
- public function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
33
-
34
- global $edd_plugin_data;
35
-
36
- $this->api_url = trailingslashit( $_api_url );
37
- $this->api_data = $_api_data;
38
- $this->name = plugin_basename( $_plugin_file );
39
- $this->slug = basename( $_plugin_file, '.php' );
40
- $this->version = $_api_data['version'];
41
- $this->wp_override = isset( $_api_data['wp_override'] ) ? (bool) $_api_data['wp_override'] : false;
42
- $this->beta = ! empty( $this->api_data['beta'] ) ? true : false;
43
- $this->cache_key = md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
44
-
45
- $edd_plugin_data[ $this->slug ] = $this->api_data;
46
-
47
- // Set up hooks.
48
- $this->init();
49
-
50
- }
51
-
52
- /**
53
- * Set up WordPress filters to hook into WP's update process.
54
- *
55
- * @uses add_filter()
56
- *
57
- * @return void
58
- */
59
- public function init() {
60
-
61
- add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
62
- add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
63
- remove_action( 'after_plugin_row_' . $this->name, 'wp_plugin_update_row', 10 );
64
- add_action( 'after_plugin_row_' . $this->name, array( $this, 'show_update_notification' ), 10, 2 );
65
- add_action( 'admin_init', array( $this, 'show_changelog' ) );
66
-
67
- }
68
-
69
- /**
70
- * Check for Updates at the defined API endpoint and modify the update array.
71
- *
72
- * This function dives into the update API just when WordPress creates its update array,
73
- * then adds a custom API call and injects the custom plugin data retrieved from the API.
74
- * It is reassembled from parts of the native WordPress plugin update code.
75
- * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
76
- *
77
- * @uses api_request()
78
- *
79
- * @param array $_transient_data Update array build by WordPress.
80
- * @return array Modified update array with custom plugin data.
81
- */
82
- public function check_update( $_transient_data ) {
83
-
84
- global $pagenow;
85
-
86
- if ( ! is_object( $_transient_data ) ) {
87
- $_transient_data = new stdClass;
88
- }
89
-
90
- if ( 'plugins.php' == $pagenow && is_multisite() ) {
91
- return $_transient_data;
92
- }
93
-
94
- if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[ $this->name ] ) && false === $this->wp_override ) {
95
- return $_transient_data;
96
- }
97
-
98
- $version_info = $this->get_cached_version_info();
99
-
100
- if ( false === $version_info ) {
101
- $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
102
-
103
- $this->set_version_info_cache( $version_info );
104
-
105
- }
106
-
107
- if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
108
-
109
- if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
110
-
111
- $_transient_data->response[ $this->name ] = $version_info;
112
-
113
- }
114
-
115
- $_transient_data->last_checked = current_time( 'timestamp' );
116
- $_transient_data->checked[ $this->name ] = $this->version;
117
-
118
- }
119
-
120
- return $_transient_data;
121
- }
122
-
123
- /**
124
- * show update nofication row -- needed for multisite subsites, because WP won't tell you otherwise!
125
- *
126
- * @param string $file
127
- * @param array $plugin
128
- */
129
- public function show_update_notification( $file, $plugin ) {
130
-
131
- if ( is_network_admin() ) {
132
- return;
133
- }
134
-
135
- if( ! current_user_can( 'update_plugins' ) ) {
136
- return;
137
- }
138
-
139
- if( ! is_multisite() ) {
140
- return;
141
- }
142
-
143
- if ( $this->name != $file ) {
144
- return;
145
- }
146
-
147
- // Remove our filter on the site transient
148
- remove_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ), 10 );
149
-
150
- $update_cache = get_site_transient( 'update_plugins' );
151
-
152
- $update_cache = is_object( $update_cache ) ? $update_cache : new stdClass();
153
-
154
- if ( empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
155
-
156
- $version_info = $this->get_cached_version_info();
157
-
158
- if ( false === $version_info ) {
159
- $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
160
-
161
- $this->set_version_info_cache( $version_info );
162
- }
163
-
164
- if ( ! is_object( $version_info ) ) {
165
- return;
166
- }
167
-
168
- if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
169
-
170
- $update_cache->response[ $this->name ] = $version_info;
171
-
172
- }
173
-
174
- $update_cache->last_checked = current_time( 'timestamp' );
175
- $update_cache->checked[ $this->name ] = $this->version;
176
-
177
- set_site_transient( 'update_plugins', $update_cache );
178
-
179
- } else {
180
-
181
- $version_info = $update_cache->response[ $this->name ];
182
-
183
- }
184
-
185
- // Restore our filter
186
- add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
187
-
188
- if ( ! empty( $update_cache->response[ $this->name ] ) && version_compare( $this->version, $version_info->new_version, '<' ) ) {
189
-
190
- // build a plugin list row, with update notification
191
- $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
192
- # <tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange">
193
- echo '<tr class="plugin-update-tr" id="' . $this->slug . '-update" data-slug="' . $this->slug . '" data-plugin="' . $this->slug . '/' . $file . '">';
194
- echo '<td colspan="3" class="plugin-update colspanchange">';
195
- echo '<div class="update-message notice inline notice-warning notice-alt">';
196
-
197
- $changelog_link = self_admin_url( 'index.php?edd_sl_action=view_plugin_changelog&plugin=' . $this->name . '&slug=' . $this->slug . '&TB_iframe=true&width=772&height=911' );
198
-
199
- if ( empty( $version_info->download_link ) ) {
200
- printf(
201
- __( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s.', 'easy-digital-downloads' ),
202
- esc_html( $version_info->name ),
203
- '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
204
- esc_html( $version_info->new_version ),
205
- '</a>'
206
- );
207
- } else {
208
- printf(
209
- __( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s or %5$supdate now%6$s.', 'easy-digital-downloads' ),
210
- esc_html( $version_info->name ),
211
- '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
212
- esc_html( $version_info->new_version ),
213
- '</a>',
214
- '<a href="' . esc_url( wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $this->name, 'upgrade-plugin_' . $this->name ) ) .'">',
215
- '</a>'
216
- );
217
- }
218
-
219
- do_action( "in_plugin_update_message-{$file}", $plugin, $version_info );
220
-
221
- echo '</div></td></tr>';
222
- }
223
- }
224
-
225
- /**
226
- * Updates information on the "View version x.x details" page with custom data.
227
- *
228
- * @uses api_request()
229
- *
230
- * @param mixed $_data
231
- * @param string $_action
232
- * @param object $_args
233
- * @return object $_data
234
- */
235
- public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
236
-
237
- if ( $_action != 'plugin_information' ) {
238
-
239
- return $_data;
240
-
241
- }
242
-
243
- if ( ! isset( $_args->slug ) || ( $_args->slug != $this->slug ) ) {
244
-
245
- return $_data;
246
-
247
- }
248
-
249
- $to_send = array(
250
- 'slug' => $this->slug,
251
- 'is_ssl' => is_ssl(),
252
- 'fields' => array(
253
- 'banners' => array(),
254
- 'reviews' => false
255
- )
256
- );
257
-
258
- $cache_key = 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
259
-
260
- // Get the transient where we store the api request for this plugin for 24 hours
261
- $edd_api_request_transient = $this->get_cached_version_info( $cache_key );
262
-
263
- //If we have no transient-saved value, run the API, set a fresh transient with the API value, and return that value too right now.
264
- if ( empty( $edd_api_request_transient ) ) {
265
-
266
- $api_response = $this->api_request( 'plugin_information', $to_send );
267
-
268
- // Expires in 3 hours
269
- $this->set_version_info_cache( $api_response, $cache_key );
270
-
271
- if ( false !== $api_response ) {
272
- $_data = $api_response;
273
- }
274
-
275
- } else {
276
- $_data = $edd_api_request_transient;
277
- }
278
-
279
- // Convert sections into an associative array, since we're getting an object, but Core expects an array.
280
- if ( isset( $_data->sections ) && ! is_array( $_data->sections ) ) {
281
- $new_sections = array();
282
- foreach ( $_data->sections as $key => $value ) {
283
- $new_sections[ $key ] = $value;
284
- }
285
-
286
- $_data->sections = $new_sections;
287
- }
288
-
289
- // Convert banners into an associative array, since we're getting an object, but Core expects an array.
290
- if ( isset( $_data->banners ) && ! is_array( $_data->banners ) ) {
291
- $new_banners = array();
292
- foreach ( $_data->banners as $key => $value ) {
293
- $new_banners[ $key ] = $value;
294
- }
295
-
296
- $_data->banners = $new_banners;
297
- }
298
-
299
- return $_data;
300
- }
301
-
302
- /**
303
- * Disable SSL verification in order to prevent download update failures
304
- *
305
- * @param array $args
306
- * @param string $url
307
- * @return object $array
308
- */
309
- public function http_request_args( $args, $url ) {
310
-
311
- $verify_ssl = $this->verify_ssl();
312
- if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
313
- $args['sslverify'] = $verify_ssl;
314
- }
315
- return $args;
316
-
317
- }
318
-
319
- /**
320
- * Calls the API and, if successfull, returns the object delivered by the API.
321
- *
322
- * @uses get_bloginfo()
323
- * @uses wp_remote_post()
324
- * @uses is_wp_error()
325
- *
326
- * @param string $_action The requested action.
327
- * @param array $_data Parameters for the API action.
328
- * @return false|object
329
- */
330
- private function api_request( $_action, $_data ) {
331
-
332
- global $wp_version;
333
-
334
- $data = array_merge( $this->api_data, $_data );
335
-
336
- if ( $data['slug'] != $this->slug ) {
337
- return;
338
- }
339
-
340
- if( $this->api_url == trailingslashit (home_url() ) ) {
341
- return false; // Don't allow a plugin to ping itself
342
- }
343
-
344
- $api_params = array(
345
- 'edd_action' => 'get_version',
346
- 'license' => ! empty( $data['license'] ) ? $data['license'] : '',
347
- 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
348
- 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
349
- 'version' => isset( $data['version'] ) ? $data['version'] : false,
350
- 'slug' => $data['slug'],
351
- 'author' => $data['author'],
352
- 'url' => home_url(),
353
- 'beta' => ! empty( $data['beta'] ),
354
- );
355
-
356
- $verify_ssl = $this->verify_ssl();
357
- $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
358
-
359
- if ( ! is_wp_error( $request ) ) {
360
- $request = json_decode( wp_remote_retrieve_body( $request ) );
361
- }
362
-
363
- if ( $request && isset( $request->sections ) ) {
364
- $request->sections = maybe_unserialize( $request->sections );
365
- } else {
366
- $request = false;
367
- }
368
-
369
- if ( $request && isset( $request->banners ) ) {
370
- $request->banners = maybe_unserialize( $request->banners );
371
- }
372
-
373
- if( ! empty( $request->sections ) ) {
374
- foreach( $request->sections as $key => $section ) {
375
- $request->$key = (array) $section;
376
- }
377
- }
378
-
379
- return $request;
380
- }
381
-
382
- public function show_changelog() {
383
-
384
- global $edd_plugin_data;
385
-
386
- if( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action'] ) {
387
- return;
388
- }
389
-
390
- if( empty( $_REQUEST['plugin'] ) ) {
391
- return;
392
- }
393
-
394
- if( empty( $_REQUEST['slug'] ) ) {
395
- return;
396
- }
397
-
398
- if( ! current_user_can( 'update_plugins' ) ) {
399
- wp_die( __( 'You do not have permission to install plugin updates', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
400
- }
401
-
402
- $data = $edd_plugin_data[ $_REQUEST['slug'] ];
403
- $beta = ! empty( $data['beta'] ) ? true : false;
404
- $cache_key = md5( 'edd_plugin_' . sanitize_key( $_REQUEST['plugin'] ) . '_' . $beta . '_version_info' );
405
- $version_info = $this->get_cached_version_info( $cache_key );
406
-
407
- if( false === $version_info ) {
408
-
409
- $api_params = array(
410
- 'edd_action' => 'get_version',
411
- 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
412
- 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
413
- 'slug' => $_REQUEST['slug'],
414
- 'author' => $data['author'],
415
- 'url' => home_url(),
416
- 'beta' => ! empty( $data['beta'] )
417
- );
418
-
419
- $verify_ssl = $this->verify_ssl();
420
- $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
421
-
422
- if ( ! is_wp_error( $request ) ) {
423
- $version_info = json_decode( wp_remote_retrieve_body( $request ) );
424
- }
425
-
426
-
427
- if ( ! empty( $version_info ) && isset( $version_info->sections ) ) {
428
- $version_info->sections = maybe_unserialize( $version_info->sections );
429
- } else {
430
- $version_info = false;
431
- }
432
-
433
- if( ! empty( $version_info ) ) {
434
- foreach( $version_info->sections as $key => $section ) {
435
- $version_info->$key = (array) $section;
436
- }
437
- }
438
-
439
- $this->set_version_info_cache( $version_info, $cache_key );
440
-
441
- }
442
-
443
- if( ! empty( $version_info ) && isset( $version_info->sections['changelog'] ) ) {
444
- echo '<div style="background:#fff;padding:10px;">' . $version_info->sections['changelog'] . '</div>';
445
- }
446
-
447
- exit;
448
- }
449
-
450
- public function get_cached_version_info( $cache_key = '' ) {
451
-
452
- if( empty( $cache_key ) ) {
453
- $cache_key = $this->cache_key;
454
- }
455
-
456
- $cache = get_option( $cache_key );
457
-
458
- if( empty( $cache['timeout'] ) || current_time( 'timestamp' ) > $cache['timeout'] ) {
459
- return false; // Cache is expired
460
- }
461
-
462
- return json_decode( $cache['value'] );
463
-
464
- }
465
-
466
- public function set_version_info_cache( $value = '', $cache_key = '' ) {
467
-
468
- if( empty( $cache_key ) ) {
469
- $cache_key = $this->cache_key;
470
- }
471
-
472
- $data = array(
473
- 'timeout' => strtotime( '+3 hours', current_time( 'timestamp' ) ),
474
- 'value' => json_encode( $value )
475
- );
476
-
477
- update_option( $cache_key, $data, 'no' );
478
-
479
- }
480
-
481
- /**
482
- * Returns if the SSL of the store should be verified.
483
- *
484
- * @since 1.6.13
485
- * @return bool
486
- */
487
- private function verify_ssl() {
488
- return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', true, $this );
489
- }
490
-
491
- }
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ /**
7
+ * Allows plugins to use their own update API.
8
+ *
9
+ * @author Easy Digital Downloads
10
+ * @version 1.6.14
11
+ */
12
+ class EDD_SL_Plugin_Updater {
13
+
14
+ private $api_url = '';
15
+ private $api_data = array();
16
+ private $name = '';
17
+ private $slug = '';
18
+ private $version = '';
19
+ private $wp_override = false;
20
+ private $cache_key = '';
21
+
22
+ /**
23
+ * Class constructor.
24
+ *
25
+ * @uses plugin_basename()
26
+ * @uses hook()
27
+ *
28
+ * @param string $_api_url The URL pointing to the custom API endpoint.
29
+ * @param string $_plugin_file Path to the plugin file.
30
+ * @param array $_api_data Optional data to send with API calls.
31
+ */
32
+ public function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
33
+
34
+ global $edd_plugin_data;
35
+
36
+ $this->api_url = trailingslashit( $_api_url );
37
+ $this->api_data = $_api_data;
38
+ $this->name = plugin_basename( $_plugin_file );
39
+ $this->slug = basename( $_plugin_file, '.php' );
40
+ $this->version = $_api_data['version'];
41
+ $this->wp_override = isset( $_api_data['wp_override'] ) ? (bool) $_api_data['wp_override'] : false;
42
+ $this->beta = ! empty( $this->api_data['beta'] ) ? true : false;
43
+ $this->cache_key = md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
44
+
45
+ $edd_plugin_data[ $this->slug ] = $this->api_data;
46
+
47
+ // Set up hooks.
48
+ $this->init();
49
+
50
+ }
51
+
52
+ /**
53
+ * Set up WordPress filters to hook into WP's update process.
54
+ *
55
+ * @uses add_filter()
56
+ *
57
+ * @return void
58
+ */
59
+ public function init() {
60
+
61
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
62
+ add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
63
+ remove_action( 'after_plugin_row_' . $this->name, 'wp_plugin_update_row', 10 );
64
+ add_action( 'after_plugin_row_' . $this->name, array( $this, 'show_update_notification' ), 10, 2 );
65
+ add_action( 'admin_init', array( $this, 'show_changelog' ) );
66
+
67
+ }
68
+
69
+ /**
70
+ * Check for Updates at the defined API endpoint and modify the update array.
71
+ *
72
+ * This function dives into the update API just when WordPress creates its update array,
73
+ * then adds a custom API call and injects the custom plugin data retrieved from the API.
74
+ * It is reassembled from parts of the native WordPress plugin update code.
75
+ * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
76
+ *
77
+ * @uses api_request()
78
+ *
79
+ * @param array $_transient_data Update array build by WordPress.
80
+ * @return array Modified update array with custom plugin data.
81
+ */
82
+ public function check_update( $_transient_data ) {
83
+
84
+ global $pagenow;
85
+
86
+ if ( ! is_object( $_transient_data ) ) {
87
+ $_transient_data = new stdClass;
88
+ }
89
+
90
+ if ( 'plugins.php' == $pagenow && is_multisite() ) {
91
+ return $_transient_data;
92
+ }
93
+
94
+ if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[ $this->name ] ) && false === $this->wp_override ) {
95
+ return $_transient_data;
96
+ }
97
+
98
+ $version_info = $this->get_cached_version_info();
99
+
100
+ if ( false === $version_info ) {
101
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
102
+
103
+ $this->set_version_info_cache( $version_info );
104
+
105
+ }
106
+
107
+ if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
108
+
109
+ if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
110
+
111
+ $_transient_data->response[ $this->name ] = $version_info;
112
+
113
+ }
114
+
115
+ $_transient_data->last_checked = current_time( 'timestamp' );
116
+ $_transient_data->checked[ $this->name ] = $this->version;
117
+
118
+ }
119
+
120
+ return $_transient_data;
121
+ }
122
+
123
+ /**
124
+ * show update nofication row -- needed for multisite subsites, because WP won't tell you otherwise!
125
+ *
126
+ * @param string $file
127
+ * @param array $plugin
128
+ */
129
+ public function show_update_notification( $file, $plugin ) {
130
+
131
+ if ( is_network_admin() ) {
132
+ return;
133
+ }
134
+
135
+ if( ! current_user_can( 'update_plugins' ) ) {
136
+ return;
137
+ }
138
+
139
+ if( ! is_multisite() ) {
140
+ return;
141
+ }
142
+
143
+ if ( $this->name != $file ) {
144
+ return;
145
+ }
146
+
147
+ // Remove our filter on the site transient
148
+ remove_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ), 10 );
149
+
150
+ $update_cache = get_site_transient( 'update_plugins' );
151
+
152
+ $update_cache = is_object( $update_cache ) ? $update_cache : new stdClass();
153
+
154
+ if ( empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
155
+
156
+ $version_info = $this->get_cached_version_info();
157
+
158
+ if ( false === $version_info ) {
159
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
160
+
161
+ $this->set_version_info_cache( $version_info );
162
+ }
163
+
164
+ if ( ! is_object( $version_info ) ) {
165
+ return;
166
+ }
167
+
168
+ if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
169
+
170
+ $update_cache->response[ $this->name ] = $version_info;
171
+
172
+ }
173
+
174
+ $update_cache->last_checked = current_time( 'timestamp' );
175
+ $update_cache->checked[ $this->name ] = $this->version;
176
+
177
+ set_site_transient( 'update_plugins', $update_cache );
178
+
179
+ } else {
180
+
181
+ $version_info = $update_cache->response[ $this->name ];
182
+
183
+ }
184
+
185
+ // Restore our filter
186
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
187
+
188
+ if ( ! empty( $update_cache->response[ $this->name ] ) && version_compare( $this->version, $version_info->new_version, '<' ) ) {
189
+
190
+ // build a plugin list row, with update notification
191
+ $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
192
+ # <tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange">
193
+ echo '<tr class="plugin-update-tr" id="' . $this->slug . '-update" data-slug="' . $this->slug . '" data-plugin="' . $this->slug . '/' . $file . '">';
194
+ echo '<td colspan="3" class="plugin-update colspanchange">';
195
+ echo '<div class="update-message notice inline notice-warning notice-alt">';
196
+
197
+ $changelog_link = self_admin_url( 'index.php?edd_sl_action=view_plugin_changelog&plugin=' . $this->name . '&slug=' . $this->slug . '&TB_iframe=true&width=772&height=911' );
198
+
199
+ if ( empty( $version_info->download_link ) ) {
200
+ printf(
201
+ __( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s.', 'easy-digital-downloads' ),
202
+ esc_html( $version_info->name ),
203
+ '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
204
+ esc_html( $version_info->new_version ),
205
+ '</a>'
206
+ );
207
+ } else {
208
+ printf(
209
+ __( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s or %5$supdate now%6$s.', 'easy-digital-downloads' ),
210
+ esc_html( $version_info->name ),
211
+ '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
212
+ esc_html( $version_info->new_version ),
213
+ '</a>',
214
+ '<a href="' . esc_url( wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $this->name, 'upgrade-plugin_' . $this->name ) ) .'">',
215
+ '</a>'
216
+ );
217
+ }
218
+
219
+ do_action( "in_plugin_update_message-{$file}", $plugin, $version_info );
220
+
221
+ echo '</div></td></tr>';
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Updates information on the "View version x.x details" page with custom data.
227
+ *
228
+ * @uses api_request()
229
+ *
230
+ * @param mixed $_data
231
+ * @param string $_action
232
+ * @param object $_args
233
+ * @return object $_data
234
+ */
235
+ public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
236
+
237
+ if ( $_action != 'plugin_information' ) {
238
+
239
+ return $_data;
240
+
241
+ }
242
+
243
+ if ( ! isset( $_args->slug ) || ( $_args->slug != $this->slug ) ) {
244
+
245
+ return $_data;
246
+
247
+ }
248
+
249
+ $to_send = array(
250
+ 'slug' => $this->slug,
251
+ 'is_ssl' => is_ssl(),
252
+ 'fields' => array(
253
+ 'banners' => array(),
254
+ 'reviews' => false
255
+ )
256
+ );
257
+
258
+ $cache_key = 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
259
+
260
+ // Get the transient where we store the api request for this plugin for 24 hours
261
+ $edd_api_request_transient = $this->get_cached_version_info( $cache_key );
262
+
263
+ //If we have no transient-saved value, run the API, set a fresh transient with the API value, and return that value too right now.
264
+ if ( empty( $edd_api_request_transient ) ) {
265
+
266
+ $api_response = $this->api_request( 'plugin_information', $to_send );
267
+
268
+ // Expires in 3 hours
269
+ $this->set_version_info_cache( $api_response, $cache_key );
270
+
271
+ if ( false !== $api_response ) {
272
+ $_data = $api_response;
273
+ }
274
+
275
+ } else {
276
+ $_data = $edd_api_request_transient;
277
+ }
278
+
279
+ // Convert sections into an associative array, since we're getting an object, but Core expects an array.
280
+ if ( isset( $_data->sections ) && ! is_array( $_data->sections ) ) {
281
+ $new_sections = array();
282
+ foreach ( $_data->sections as $key => $value ) {
283
+ $new_sections[ $key ] = $value;
284
+ }
285
+
286
+ $_data->sections = $new_sections;
287
+ }
288
+
289
+ // Convert banners into an associative array, since we're getting an object, but Core expects an array.
290
+ if ( isset( $_data->banners ) && ! is_array( $_data->banners ) ) {
291
+ $new_banners = array();
292
+ foreach ( $_data->banners as $key => $value ) {
293
+ $new_banners[ $key ] = $value;
294
+ }
295
+
296
+ $_data->banners = $new_banners;
297
+ }
298
+
299
+ return $_data;
300
+ }
301
+
302
+ /**
303
+ * Disable SSL verification in order to prevent download update failures
304
+ *
305
+ * @param array $args
306
+ * @param string $url
307
+ * @return object $array
308
+ */
309
+ public function http_request_args( $args, $url ) {
310
+
311
+ $verify_ssl = $this->verify_ssl();
312
+ if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
313
+ $args['sslverify'] = $verify_ssl;
314
+ }
315
+ return $args;
316
+
317
+ }
318
+
319
+ /**
320
+ * Calls the API and, if successfull, returns the object delivered by the API.
321
+ *
322
+ * @uses get_bloginfo()
323
+ * @uses wp_remote_post()
324
+ * @uses is_wp_error()
325
+ *
326
+ * @param string $_action The requested action.
327
+ * @param array $_data Parameters for the API action.
328
+ * @return false|object
329
+ */
330
+ private function api_request( $_action, $_data ) {
331
+
332
+ global $wp_version;
333
+
334
+ $data = array_merge( $this->api_data, $_data );
335
+
336
+ if ( $data['slug'] != $this->slug ) {
337
+ return;
338
+ }
339
+
340
+ if( $this->api_url == trailingslashit (home_url() ) ) {
341
+ return false; // Don't allow a plugin to ping itself
342
+ }
343
+
344
+ $api_params = array(
345
+ 'edd_action' => 'get_version',
346
+ 'license' => ! empty( $data['license'] ) ? $data['license'] : '',
347
+ 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
348
+ 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
349
+ 'version' => isset( $data['version'] ) ? $data['version'] : false,
350
+ 'slug' => $data['slug'],
351
+ 'author' => $data['author'],
352
+ 'url' => home_url(),
353
+ 'beta' => ! empty( $data['beta'] ),
354
+ );
355
+
356
+ $verify_ssl = $this->verify_ssl();
357
+ $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
358
+
359
+ if ( ! is_wp_error( $request ) ) {
360
+ $request = json_decode( wp_remote_retrieve_body( $request ) );
361
+ }
362
+
363
+ if ( $request && isset( $request->sections ) ) {
364
+ $request->sections = maybe_unserialize( $request->sections );
365
+ } else {
366
+ $request = false;
367
+ }
368
+
369
+ if ( $request && isset( $request->banners ) ) {
370
+ $request->banners = maybe_unserialize( $request->banners );
371
+ }
372
+
373
+ if( ! empty( $request->sections ) ) {
374
+ foreach( $request->sections as $key => $section ) {
375
+ $request->$key = (array) $section;
376
+ }
377
+ }
378
+
379
+ return $request;
380
+ }
381
+
382
+ public function show_changelog() {
383
+
384
+ global $edd_plugin_data;
385
+
386
+ if( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action'] ) {
387
+ return;
388
+ }
389
+
390
+ if( empty( $_REQUEST['plugin'] ) ) {
391
+ return;
392
+ }
393
+
394
+ if( empty( $_REQUEST['slug'] ) ) {
395
+ return;
396
+ }
397
+
398
+ if( ! current_user_can( 'update_plugins' ) ) {
399
+ wp_die( __( 'You do not have permission to install plugin updates', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
400
+ }
401
+
402
+ $data = $edd_plugin_data[ $_REQUEST['slug'] ];
403
+ $beta = ! empty( $data['beta'] ) ? true : false;
404
+ $cache_key = md5( 'edd_plugin_' . sanitize_key( $_REQUEST['plugin'] ) . '_' . $beta . '_version_info' );
405
+ $version_info = $this->get_cached_version_info( $cache_key );
406
+
407
+ if( false === $version_info ) {
408
+
409
+ $api_params = array(
410
+ 'edd_action' => 'get_version',
411
+ 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
412
+ 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
413
+ 'slug' => $_REQUEST['slug'],
414
+ 'author' => $data['author'],
415
+ 'url' => home_url(),
416
+ 'beta' => ! empty( $data['beta'] )
417
+ );
418
+
419
+ $verify_ssl = $this->verify_ssl();
420
+ $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
421
+
422
+ if ( ! is_wp_error( $request ) ) {
423
+ $version_info = json_decode( wp_remote_retrieve_body( $request ) );
424
+ }
425
+
426
+
427
+ if ( ! empty( $version_info ) && isset( $version_info->sections ) ) {
428
+ $version_info->sections = maybe_unserialize( $version_info->sections );
429
+ } else {
430
+ $version_info = false;
431
+ }
432
+
433
+ if( ! empty( $version_info ) ) {
434
+ foreach( $version_info->sections as $key => $section ) {
435
+ $version_info->$key = (array) $section;
436
+ }
437
+ }
438
+
439
+ $this->set_version_info_cache( $version_info, $cache_key );
440
+
441
+ }
442
+
443
+ if( ! empty( $version_info ) && isset( $version_info->sections['changelog'] ) ) {
444
+ echo '<div style="background:#fff;padding:10px;">' . $version_info->sections['changelog'] . '</div>';
445
+ }
446
+
447
+ exit;
448
+ }
449
+
450
+ public function get_cached_version_info( $cache_key = '' ) {
451
+
452
+ if( empty( $cache_key ) ) {
453
+ $cache_key = $this->cache_key;
454
+ }
455
+
456
+ $cache = get_option( $cache_key );
457
+
458
+ if( empty( $cache['timeout'] ) || current_time( 'timestamp' ) > $cache['timeout'] ) {
459
+ return false; // Cache is expired
460
+ }
461
+
462
+ return json_decode( $cache['value'] );
463
+
464
+ }
465
+
466
+ public function set_version_info_cache( $value = '', $cache_key = '' ) {
467
+
468
+ if( empty( $cache_key ) ) {
469
+ $cache_key = $this->cache_key;
470
+ }
471
+
472
+ $data = array(
473
+ 'timeout' => strtotime( '+3 hours', current_time( 'timestamp' ) ),
474
+ 'value' => json_encode( $value )
475
+ );
476
+
477
+ update_option( $cache_key, $data, 'no' );
478
+
479
+ }
480
+
481
+ /**
482
+ * Returns if the SSL of the store should be verified.
483
+ *
484
+ * @since 1.6.13
485
+ * @return bool
486
+ */
487
+ private function verify_ssl() {
488
+ return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', true, $this );
489
+ }
490
+
491
+ }
admin/class-admin.php CHANGED
@@ -1,517 +1,517 @@
1
- <?php
2
- /**
3
- * Admin class
4
- *
5
- * @author Tijmen Smit
6
- * @since 1.0.0
7
- */
8
-
9
- if ( !defined( 'ABSPATH' ) ) exit;
10
-
11
- if ( !class_exists( 'WPSL_Admin' ) ) {
12
-
13
- /**
14
- * Handle the backend of the store locator
15
- *
16
- * @since 1.0.0
17
- */
18
- class WPSL_Admin {
19
-
20
- /**
21
- * @since 2.0.0
22
- * @var WPSL_Metaboxes
23
- */
24
- public $metaboxes;
25
-
26
- /**
27
- * @since 2.0.0
28
- * @var WPSL_Geocode
29
- */
30
- public $geocode;
31
-
32
- /**
33
- * @since 2.0.0
34
- * @var WPSL_Notices
35
- */
36
- public $notices;
37
-
38
- /**
39
- * @since 2.0.0
40
- * @var WPSL_Settings
41
- */
42
- public $settings_page;
43
-
44
- /**
45
- * Class constructor
46
- */
47
- function __construct() {
48
-
49
- $this->includes();
50
-
51
- add_action( 'init', array( $this, 'init' ) );
52
- add_action( 'admin_menu', array( $this, 'create_admin_menu' ) );
53
- add_action( 'admin_init', array( $this, 'setting_warnings' ) );
54
- add_action( 'delete_post', array( $this, 'maybe_delete_autoload_transient' ) );
55
- add_action( 'wp_trash_post', array( $this, 'maybe_delete_autoload_transient' ) );
56
- add_action( 'untrash_post', array( $this, 'maybe_delete_autoload_transient' ) );
57
- add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
58
- add_filter( 'plugin_row_meta', array( $this, 'add_plugin_meta_row' ), 10, 2 );
59
- add_filter( 'plugin_action_links_' . WPSL_BASENAME, array( $this, 'add_action_links' ), 10, 2 );
60
- add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ), 1 );
61
- add_action( 'wp_loaded', array( $this, 'disable_setting_notices' ) );
62
- }
63
-
64
- /**
65
- * Include the required files.
66
- *
67
- * @since 2.0.0
68
- * @return void
69
- */
70
- public function includes() {
71
- require_once( WPSL_PLUGIN_DIR . 'admin/class-shortcode-generator.php' );
72
- require_once( WPSL_PLUGIN_DIR . 'admin/class-notices.php' );
73
- require_once( WPSL_PLUGIN_DIR . 'admin/class-license-manager.php' );
74
- require_once( WPSL_PLUGIN_DIR . 'admin/class-metaboxes.php' );
75
- require_once( WPSL_PLUGIN_DIR . 'admin/class-geocode.php' );
76
- require_once( WPSL_PLUGIN_DIR . 'admin/class-settings.php' );
77
- require_once( WPSL_PLUGIN_DIR . 'admin/upgrade.php' );
78
- require_once( WPSL_PLUGIN_DIR . 'admin/data-export.php' );
79
- }
80
-
81
- /**
82
- * Init the classes.
83
- *
84
- * @since 2.0.0
85
- * @return void
86
- */
87
- public function init() {
88
- $this->notices = new WPSL_Notices();
89
- $this->metaboxes = new WPSL_Metaboxes();
90
- $this->geocode = new WPSL_Geocode();
91
- $this->settings_page = new WPSL_Settings();
92
- }
93
-
94
- /**
95
- * Check if we need to show warnings after
96
- * the user installed the plugin.
97
- *
98
- * @since 1.0.0
99
- * @todo move to class-notices?
100
- * @return void
101
- */
102
- public function setting_warnings() {
103
-
104
- global $current_user, $wpsl_settings;
105
-
106
- $this->setting_warning = array();
107
-
108
- // The fields settings field to check for data.
109
- $warnings = array(
110
- 'start_latlng' => 'location',
111
- 'api_browser_key' => 'key'
112
- );
113
-
114
- if ( ( current_user_can( 'install_plugins' ) ) && is_admin() ) {
115
- foreach ( $warnings as $setting_name => $warning ) {
116
- if ( empty( $wpsl_settings[$setting_name] ) && !get_user_meta( $current_user->ID, 'wpsl_disable_' . $warning . '_warning' ) ) {
117
- if ( $warning == 'key' ) {
118
- $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>" );
119
- } else {
120
- $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>" );
121
- }
122
- }
123
- }
124
-
125
- if ( $this->setting_warning ) {
126
- add_action( 'admin_notices', array( $this, 'show_warning' ) );
127
- }
128
- }
129
- }
130
-
131
- /**
132
- * Show the admin warnings
133
- *
134
- * @since 1.2.0
135
- * @return void
136
- */
137
- public function show_warning() {
138
- foreach ( $this->setting_warning as $k => $warning ) {
139
- echo "<div id='message' class='error'><p>" . $warning . "</p></div>";
140
- }
141
- }
142
-
143
- /**
144
- * Disable notices about the plugin settings.
145
- *
146
- * @todo move to class-notices?
147
- * @since 2.2.3
148
- * @return void
149
- */
150
- public function disable_setting_notices() {
151
-
152
- global $current_user;
153
-
154
- if ( isset( $_GET['wpsl-notice'] ) && isset( $_GET['_wpsl_notice_nonce'] ) ) {
155
-
156
- if ( !wp_verify_nonce( $_GET['_wpsl_notice_nonce'], 'wpsl_notices_nonce' ) ) {
157
- wp_die( __( 'Security check failed. Please reload the page and try again.', 'wpsl' ) );
158
- }
159
-
160
- $notice = sanitize_text_field( $_GET['wpsl-notice'] );
161
-
162
- add_user_meta( $current_user->ID, 'wpsl_disable_' . $notice . '_warning', 'true', true );
163
- }
164
- }
165
-
166
- /**
167
- * Add the admin menu pages.
168
- *
169
- * @since 1.0.0
170
- * @return void
171
- */
172
- public function create_admin_menu() {
173
-
174
- $sub_menus = apply_filters( 'wpsl_sub_menu_items', array(
175
- array(
176
- 'page_title' => __( 'Settings', 'wpsl' ),
177
- 'menu_title' => __( 'Settings', 'wpsl' ),
178
- 'caps' => 'manage_wpsl_settings',
179
- 'menu_slug' => 'wpsl_settings',
180
- 'function' => array( $this, 'load_template' )
181
- ),
182
- array(
183
- 'page_title' => __( 'Add-Ons', 'wpsl' ),
184
- 'menu_title' => __( 'Add-Ons', 'wpsl' ),
185
- 'caps' => 'manage_wpsl_settings',
186
- 'menu_slug' => 'wpsl_add_ons',
187
- 'function' => array( $this, 'load_template' )
188
- )
189
- )
190
- );
191
-
192
- if ( count( $sub_menus ) ) {
193
- foreach ( $sub_menus as $sub_menu ) {
194
- 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'] );
195
- }
196
- }
197
- }
198
-
199
- /**
200
- * Load the correct page template.
201
- *
202
- * @since 2.1.0
203
- * @return void
204
- */
205
- public function load_template() {
206
-
207
- switch ( $_GET['page'] ) {
208
- case 'wpsl_settings':
209
- require 'templates/map-settings.php';
210
- break;
211
- case 'wpsl_add_ons':
212
- require 'templates/add-ons.php';
213
- break;
214
- }
215
- }
216
-
217
- /**
218
- * Check if we need to delete the autoload transient.
219
- *
220
- * This is called when a post it saved, deleted, trashed or untrashed.
221
- *
222
- * @since 2.0.0
223
- * @return void
224
- */
225
- public function maybe_delete_autoload_transient( $post_id ) {
226
-
227
- global $wpsl_settings;
228
-
229
- if ( isset( $wpsl_settings['autoload'] ) && $wpsl_settings['autoload'] && get_post_type( $post_id ) == 'wpsl_stores' ) {
230
- $this->delete_autoload_transient();
231
- }
232
- }
233
-
234
- /**
235
- * Delete the transients that are used on the front-end
236
- * if the autoload option is enabled.
237
- *
238
- * The transient names used by the store locator are partly dynamic.
239
- * They always start with wpsl_autoload_, followed by the number of
240
- * stores to load and ends with the language code.
241
- *
242
- * So you get wpsl_autoload_20_de if the language is set to German
243
- * and 20 stores are set to show on page load.
244
- *
245
- * The language code has to be included in case a multilingual plugin is used.
246
- * Otherwise it can happen the user switches to Spanish,
247
- * but ends up seeing the store data in the wrong language.
248
- *
249
- * @since 2.0.0
250
- * @return void
251
- */
252
- public function delete_autoload_transient() {
253
-
254
- global $wpdb;
255
-
256
- $option_names = $wpdb->get_results( "SELECT option_name AS transient_name FROM " . $wpdb->options . " WHERE option_name LIKE ('\_transient\_wpsl\_autoload\_%')" );
257
-
258
- if ( $option_names ) {
259
- foreach ( $option_names as $option_name ) {
260
- $transient_name = str_replace( "_transient_", "", $option_name->transient_name );
261
-
262
- delete_transient( $transient_name );
263
- }
264
- }
265
- }
266
-
267
- /**
268
- * Check if we can use a font for the plugin icon.
269
- *
270
- * This is supported by WP 3.8 or higher
271
- *
272
- * @since 1.0.0
273
- * @return void
274
- */
275
- private function check_icon_font_usage() {
276
-
277
- global $wp_version;
278
-
279
- if ( ( version_compare( $wp_version, '3.8', '>=' ) == TRUE ) ) {
280
- $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
281
-
282
- wp_enqueue_style( 'wpsl-admin-38', plugins_url( '/css/style-3.8'. $min .'.css', __FILE__ ), false );
283
- }
284
- }
285
-
286
- /**
287
- * The text messages used in wpsl-admin.js.
288
- *
289
- * @since 1.2.20
290
- * @return array $admin_js_l10n The texts used in the wpsl-admin.js
291
- */
292
- public function admin_js_l10n() {
293
-
294
- $admin_js_l10n = array(
295
- 'noAddress' => __( 'Cannot determine the address at this location.', 'wpsl' ),
296
- 'geocodeFail' => __( 'Geocode was not successful for the following reason', 'wpsl' ),
297
- 'securityFail' => __( 'Security check failed, reload the page and try again.', 'wpsl' ),
298
- 'requiredFields' => __( 'Please fill in all the required store details.', 'wpsl' ),
299
- 'missingGeoData' => __( 'The map preview requires all the location details.', 'wpsl' ),
300
- 'closedDate' => __( 'Closed', 'wpsl' ),
301
- 'styleError' => __( 'The code for the map style is invalid.', 'wpsl' ),
302
- 'browserKeyError' => sprintf( __( 'There\'s a problem with the provided %sbrowser key%s. %s You can read more about how to determine the exact issue, and how to solve it %shere%s.','wpsl' ), '<a href="https://wpstorelocator.co/document/create-google-api-keys/#browser-key">','</a>', '<br><br>', '<a href="https://wpstorelocator.co/document/create-google-api-keys/#troubleshooting">','</a>' ),
303
- 'dismissNotice' => __( 'Dismiss this notice.', 'wpsl' )
304
- );
305
-
306
- return $admin_js_l10n;
307
- }
308
-
309
- /**
310
- * Plugin settings that are used in the wpsl-admin.js.
311
- *
312
- * @since 2.0.0
313
- * @return array $settings_js The settings used in the wpsl-admin.js
314
- */
315
- public function js_settings() {
316
-
317
- global $wpsl_settings;
318
-
319
- $js_settings = array(
320
- 'hourFormat' => $wpsl_settings['editor_hour_format'],
321
- 'defaultLatLng' => $this->get_default_lat_lng(),
322
- 'defaultZoom' => 6,
323
- 'mapType' => $wpsl_settings['editor_map_type'],
324
- 'requiredFields' => array( 'address', 'city', 'country' ),
325
- 'ajaxurl' => wpsl_get_ajax_url()
326
- );
327
-
328
- return apply_filters( 'wpsl_admin_js_settings', $js_settings );
329
- }
330
-
331
- /**
332
- * Get the coordinates that are used to
333
- * show the map on the settings page.
334
- *
335
- * @since 2.2.5
336
- * @return string $startLatLng The start coordinates
337
- */
338
- public function get_default_lat_lng() {
339
-
340
- global $wpsl_settings;
341
-
342
- $startLatLng = $wpsl_settings['start_latlng'];
343
-
344
- // If no start coordinates exists, then set the default to Holland.
345
- if ( !$startLatLng ) {
346
- $startLatLng = '52.378153,4.899363';
347
- }
348
-
349
- return $startLatLng;
350
- }
351
-
352
- /**
353
- * Add the required admin script.
354
- *
355
- * @since 1.0.0
356
- * @return void
357
- */
358
- public function admin_scripts() {
359
-
360
- $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
361
-
362
- // Always load the main js admin file to make sure the "dismiss" link in the location notice works.
363
- wp_enqueue_script( 'wpsl-admin-js', plugins_url( '/js/wpsl-admin'. $min .'.js', __FILE__ ), array( 'jquery' ), WPSL_VERSION_NUM, true );
364
-
365
- $this->maybe_show_pointer();
366
- $this->check_icon_font_usage();
367
-
368
- // Only enqueue the rest of the css/js files if we are on a page that belongs to the store locator.
369
- if ( ( get_post_type() == 'wpsl_stores' ) || ( isset( $_GET['post_type'] ) && ( $_GET['post_type'] == 'wpsl_stores' ) ) ) {
370
-
371
- // Make sure no other Google Map scripts can interfere with the one from the store locator.
372
- wpsl_deregister_other_gmaps();
373
-
374
- wp_enqueue_style( 'jquery-style', '//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/themes/smoothness/jquery-ui.css' );
375
- wp_enqueue_style( 'wpsl-admin-css', plugins_url( '/css/style'. $min .'.css', __FILE__ ), false );
376
-
377
- wp_enqueue_media();
378
- wp_enqueue_script( 'jquery-ui-dialog' );
379
- wp_enqueue_script( 'wpsl-gmap', ( '//maps.google.com/maps/api/js' . wpsl_get_gmap_api_params( 'browser_key' ) ), false, WPSL_VERSION_NUM, true );
380
-
381
- wp_enqueue_script( 'wpsl-queue', plugins_url( '/js/ajax-queue'. $min .'.js', __FILE__ ), array( 'jquery' ), WPSL_VERSION_NUM, true );
382
- wp_enqueue_script( 'wpsl-retina', plugins_url( '/js/retina'. $min .'.js', __FILE__ ), array( 'jquery' ), WPSL_VERSION_NUM, true );
383
-
384
- wp_localize_script( 'wpsl-admin-js', 'wpslL10n', $this->admin_js_l10n() );
385
- wp_localize_script( 'wpsl-admin-js', 'wpslSettings', $this->js_settings() );
386
- }
387
- }
388
-
389
- /**
390
- * Check if we need to show the wpsl pointer.
391
- *
392
- * @since 2.0.0
393
- * @return void
394
- */
395
- public function maybe_show_pointer() {
396
-
397
- $disable_pointer = apply_filters( 'wpsl_disable_welcome_pointer', false );
398
-
399
- if ( $disable_pointer ) {
400
- return;
401
- }
402
-
403
- $dismissed_pointers = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
404
-
405
- // If the user hasn't dismissed the wpsl pointer, enqueue the script and style, and call the action hook.
406
- if ( !in_array( 'wpsl_signup_pointer', $dismissed_pointers ) ) {
407
- wp_enqueue_style( 'wp-pointer' );
408
- wp_enqueue_script( 'wp-pointer' );
409
-
410
- add_action( 'admin_print_footer_scripts', array( $this, 'welcome_pointer_script' ) );
411
- }
412
- }
413
-
414
- /**
415
- * Add the script for the welcome pointer.
416
- *
417
- * @since 2.0.0
418
- * @return void
419
- */
420
- public function welcome_pointer_script() {
421
-
422
- $pointer_content = '<h3>' . __( 'Welcome to WP Store Locator', 'wpsl' ) . '</h3>';
423
- $pointer_content .= '<p>' . __( 'Sign up for the latest plugin updates and announcements.', 'wpsl' ) . '</p>';
424
- $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&amp;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>';
425
- ?>
426
-
427
- <script type="text/javascript">
428
- //<![CDATA[
429
- jQuery( document ).ready( function( $ ) {
430
- $( '#menu-posts-wpsl_stores' ).pointer({
431
- content: '<?php echo $pointer_content; ?>',
432
- position: {
433
- edge: 'left',
434
- align: 'center'
435
- },
436
- pointerWidth: 350,
437
- close: function () {
438
- $.post( ajaxurl, {
439
- pointer: 'wpsl_signup_pointer',
440
- action: 'dismiss-wp-pointer'
441
- });
442
- }
443
- }).pointer( 'open' );
444
-
445
- // If a user clicked the "subscribe" button trigger the close button for the pointer.
446
- $( ".wpsl-mc-wrap #mc-embedded-subscribe" ).on( "click", function() {
447
- $( ".wp-pointer .close" ).trigger( "click" );
448
- });
449
- });
450
- //]]>
451
- </script>
452
-
453
- <?php
454
- }
455
-
456
- /**
457
- * Add link to the plugin action row.
458
- *
459
- * @since 2.0.0
460
- * @param array $links The existing action links
461
- * @param string $file The file path of the current plugin
462
- * @return array $links The modified links
463
- */
464
- public function add_action_links( $links, $file ) {
465
-
466
- if ( strpos( $file, 'wp-store-locator.php' ) !== false ) {
467
- $settings_link = '<a href="' . admin_url( 'edit.php?post_type=wpsl_stores&page=wpsl_settings' ) . '" title="View WP Store Locator Settings">' . __( 'Settings', 'wpsl' ) . '</a>';
468
- array_unshift( $links, $settings_link );
469
- }
470
-
471
- return $links;
472
- }
473
-
474
- /**
475
- * Add links to the plugin meta row.
476
- *
477
- * @since 2.1.1
478
- * @param array $links The existing meta links
479
- * @param string $file The file path of the current plugin
480
- * @return array $links The modified meta links
481
- */
482
- public function add_plugin_meta_row( $links, $file ) {
483
-
484
- if ( strpos( $file, 'wp-store-locator.php' ) !== false ) {
485
- $new_links = array(
486
- '<a href="https://wpstorelocator.co/documentation/" title="View Documentation">'. __( 'Documentation', 'wpsl' ).'</a>',
487
- '<a href="https://wpstorelocator.co/add-ons/" title="View Add-Ons">'. __( 'Add-Ons', 'wpsl' ).'</a>'
488
- );
489
-
490
- $links = array_merge( $links, $new_links );
491
- }
492
-
493
- return $links;
494
- }
495
-
496
- /**
497
- * Change the footer text on the settings page.
498
- *
499
- * @since 2.0.0
500
- * @param string $text The current footer text
501
- * @return string $text Either the original or modified footer text
502
- */
503
- public function admin_footer_text( $text ) {
504
-
505
- $current_screen = get_current_screen();
506
-
507
- // Only modify the footer text if we are on the settings page of the wp store locator.
508
- if ( isset( $current_screen->id ) && $current_screen->id == 'wpsl_stores_page_wpsl_settings' ) {
509
- $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>' );
510
- }
511
-
512
- return $text;
513
- }
514
- }
515
-
516
- $GLOBALS['wpsl_admin'] = new WPSL_Admin();
517
  }
1
+ <?php
2
+ /**
3
+ * Admin class
4
+ *
5
+ * @author Tijmen Smit
6
+ * @since 1.0.0
7
+ */
8
+
9
+ if ( !defined( 'ABSPATH' ) ) exit;
10
+
11
+ if ( !class_exists( 'WPSL_Admin' ) ) {
12
+
13
+ /**
14
+ * Handle the backend of the store locator
15
+ *
16
+ * @since 1.0.0
17
+ */
18
+ class WPSL_Admin {
19
+
20
+ /**
21
+ * @since 2.0.0
22
+ * @var WPSL_Metaboxes
23
+ */
24
+ public $metaboxes;
25
+
26
+ /**
27
+ * @since 2.0.0
28
+ * @var WPSL_Geocode
29
+ */
30
+ public $geocode;
31
+
32
+ /**
33
+ * @since 2.0.0
34
+ * @var WPSL_Notices
35
+ */
36
+ public $notices;
37
+
38
+ /**
39
+ * @since 2.0.0
40
+ * @var WPSL_Settings
41
+ */
42
+ public $settings_page;
43
+
44
+ /**
45
+ * Class constructor
46
+ */
47
+ function __construct() {
48
+
49
+ $this->includes();
50
+
51
+ add_action( 'init', array( $this, 'init' ) );
52
+ add_action( 'admin_menu', array( $this, 'create_admin_menu' ) );
53
+ add_action( 'admin_init', array( $this, 'setting_warnings' ) );
54
+ add_action( 'delete_post', array( $this, 'maybe_delete_autoload_transient' ) );
55
+ add_action( 'wp_trash_post', array( $this, 'maybe_delete_autoload_transient' ) );
56
+ add_action( 'untrash_post', array( $this, 'maybe_delete_autoload_transient' ) );
57
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
58
+ add_filter( 'plugin_row_meta', array( $this, 'add_plugin_meta_row' ), 10, 2 );
59
+ add_filter( 'plugin_action_links_' . WPSL_BASENAME, array( $this, 'add_action_links' ), 10, 2 );
60
+ add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ), 1 );
61
+ add_action( 'wp_loaded', array( $this, 'disable_setting_notices' ) );
62
+ }
63
+
64
+ /**
65
+ * Include the required files.
66
+ *
67
+ * @since 2.0.0
68
+ * @return void
69
+ */
70
+ public function includes() {
71
+ require_once( WPSL_PLUGIN_DIR . 'admin/class-shortcode-generator.php' );
72
+ require_once( WPSL_PLUGIN_DIR . 'admin/class-notices.php' );
73
+ require_once( WPSL_PLUGIN_DIR . 'admin/class-license-manager.php' );
74
+ require_once( WPSL_PLUGIN_DIR . 'admin/class-metaboxes.php' );
75
+ require_once( WPSL_PLUGIN_DIR . 'admin/class-geocode.php' );
76
+ require_once( WPSL_PLUGIN_DIR . 'admin/class-settings.php' );
77
+ require_once( WPSL_PLUGIN_DIR . 'admin/upgrade.php' );
78
+ require_once( WPSL_PLUGIN_DIR . 'admin/data-export.php' );
79
+ }
80
+
81
+ /**
82
+ * Init the classes.
83
+ *
84
+ * @since 2.0.0
85
+ * @return void
86
+ */
87
+ public function init() {
88
+ $this->notices = new WPSL_Notices();
89
+ $this->metaboxes = new WPSL_Metaboxes();
90
+ $this->geocode = new WPSL_Geocode();
91
+ $this->settings_page = new WPSL_Settings();
92
+ }
93
+
94
+ /**
95
+ * Check if we need to show warnings after
96
+ * the user installed the plugin.
97
+ *
98
+ * @since 1.0.0
99
+ * @todo move to class-notices?
100
+ * @return void
101
+ */
102
+ public function setting_warnings() {
103
+
104
+ global $current_user, $wpsl_settings;
105
+
106
+ $this->setting_warning = array();
107
+
108
+ // The fields settings field to check for data.
109
+ $warnings = array(
110
+ 'start_latlng' => 'location',
111
+ 'api_browser_key' => 'key'
112
+ );
113
+
114
+ if ( ( current_user_can( 'install_plugins' ) ) && is_admin() ) {
115
+ foreach ( $warnings as $setting_name => $warning ) {
116
+ if ( empty( $wpsl_settings[$setting_name] ) && !get_user_meta( $current_user->ID, 'wpsl_disable_' . $warning . '_warning' ) ) {
117
+ if ( $warning == 'key' ) {
118
+ $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>" );
119
+ } else {
120
+ $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>" );
121
+ }
122
+ }
123
+ }
124
+
125
+ if ( $this->setting_warning ) {
126
+ add_action( 'admin_notices', array( $this, 'show_warning' ) );
127
+ }
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Show the admin warnings
133
+ *
134
+ * @since 1.2.0
135
+ * @return void
136
+ */
137
+ public function show_warning() {
138
+ foreach ( $this->setting_warning as $k => $warning ) {
139
+ echo "<div id='message' class='error'><p>" . $warning . "</p></div>";
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Disable notices about the plugin settings.
145
+ *
146
+ * @todo move to class-notices?
147
+ * @since 2.2.3
148
+ * @return void
149
+ */
150
+ public function disable_setting_notices() {
151
+
152
+ global $current_user;
153
+
154
+ if ( isset( $_GET['wpsl-notice'] ) && isset( $_GET['_wpsl_notice_nonce'] ) ) {
155
+
156
+ if ( !wp_verify_nonce( $_GET['_wpsl_notice_nonce'], 'wpsl_notices_nonce' ) ) {
157
+ wp_die( __( 'Security check failed. Please reload the page and try again.', 'wpsl' ) );
158
+ }
159
+
160
+ $notice = sanitize_text_field( $_GET['wpsl-notice'] );
161
+
162
+ add_user_meta( $current_user->ID, 'wpsl_disable_' . $notice . '_warning', 'true', true );
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Add the admin menu pages.
168
+ *
169
+ * @since 1.0.0
170
+ * @return void
171
+ */
172
+ public function create_admin_menu() {
173
+
174
+ $sub_menus = apply_filters( 'wpsl_sub_menu_items', array(
175
+ array(
176
+ 'page_title' => __( 'Settings', 'wpsl' ),
177
+ 'menu_title' => __( 'Settings', 'wpsl' ),
178
+ 'caps' => 'manage_wpsl_settings',
179
+ 'menu_slug' => 'wpsl_settings',
180
+ 'function' => array( $this, 'load_template' )
181
+ ),
182
+ array(
183
+ 'page_title' => __( 'Add-Ons', 'wpsl' ),
184
+ 'menu_title' => __( 'Add-Ons', 'wpsl' ),
185
+ 'caps' => 'manage_wpsl_settings',
186
+ 'menu_slug' => 'wpsl_add_ons',
187
+ 'function' => array( $this, 'load_template' )
188
+ )
189
+ )
190
+ );
191
+
192
+ if ( count( $sub_menus ) ) {
193
+ foreach ( $sub_menus as $sub_menu ) {
194
+ 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'] );
195
+ }
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Load the correct page template.
201
+ *
202
+ * @since 2.1.0
203
+ * @return void
204
+ */
205
+ public function load_template() {
206
+
207
+ switch ( $_GET['page'] ) {
208
+ case 'wpsl_settings':
209
+ require 'templates/map-settings.php';
210
+ break;
211
+ case 'wpsl_add_ons':
212
+ require 'templates/add-ons.php';
213
+ break;
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Check if we need to delete the autoload transient.
219
+ *
220
+ * This is called when a post it saved, deleted, trashed or untrashed.
221
+ *
222
+ * @since 2.0.0
223
+ * @return void
224
+ */
225
+ public function maybe_delete_autoload_transient( $post_id ) {
226
+
227
+ global $wpsl_settings;
228
+
229
+ if ( isset( $wpsl_settings['autoload'] ) && $wpsl_settings['autoload'] && get_post_type( $post_id ) == 'wpsl_stores' ) {
230
+ $this->delete_autoload_transient();
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Delete the transients that are used on the front-end
236
+ * if the autoload option is enabled.
237
+ *
238
+ * The transient names used by the store locator are partly dynamic.
239
+ * They always start with wpsl_autoload_, followed by the number of
240
+ * stores to load and ends with the language code.
241
+ *
242
+ * So you get wpsl_autoload_20_de if the language is set to German
243
+ * and 20 stores are set to show on page load.
244
+ *
245
+ * The language code has to be included in case a multilingual plugin is used.
246
+ * Otherwise it can happen the user switches to Spanish,
247
+ * but ends up seeing the store data in the wrong language.
248
+ *
249
+ * @since 2.0.0
250
+ * @return void
251
+ */
252
+ public function delete_autoload_transient() {
253
+
254
+ global $wpdb;
255
+
256
+ $option_names = $wpdb->get_results( "SELECT option_name AS transient_name FROM " . $wpdb->options . " WHERE option_name LIKE ('\_transient\_wpsl\_autoload\_%')" );
257
+
258
+ if ( $option_names ) {
259
+ foreach ( $option_names as $option_name ) {
260
+ $transient_name = str_replace( "_transient_", "", $option_name->transient_name );
261
+
262
+ delete_transient( $transient_name );
263
+ }
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Check if we can use a font for the plugin icon.
269
+ *
270
+ * This is supported by WP 3.8 or higher
271
+ *
272
+ * @since 1.0.0
273
+ * @return void
274
+ */
275
+ private function check_icon_font_usage() {
276
+
277
+ global $wp_version;
278
+
279
+ if ( ( version_compare( $wp_version, '3.8', '>=' ) == TRUE ) ) {
280
+ $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
281
+
282
+ wp_enqueue_style( 'wpsl-admin-38', plugins_url( '/css/style-3.8'. $min .'.css', __FILE__ ), false );
283
+ }
284
+ }
285
+
286
+ /**
287
+ * The text messages used in wpsl-admin.js.
288
+ *
289
+ * @since 1.2.20
290
+ * @return array $admin_js_l10n The texts used in the wpsl-admin.js
291
+ */
292
+ public function admin_js_l10n() {
293
+
294
+ $admin_js_l10n = array(
295
+ 'noAddress' => __( 'Cannot determine the address at this location.', 'wpsl' ),
296
+ 'geocodeFail' => __( 'Geocode was not successful for the following reason', 'wpsl' ),
297
+ 'securityFail' => __( 'Security check failed, reload the page and try again.', 'wpsl' ),
298
+ 'requiredFields' => __( 'Please fill in all the required store details.', 'wpsl' ),
299
+ 'missingGeoData' => __( 'The map preview requires all the location details.', 'wpsl' ),
300
+ 'closedDate' => __( 'Closed', 'wpsl' ),
301
+ 'styleError' => __( 'The code for the map style is invalid.', 'wpsl' ),
302
+ 'browserKeyError' => sprintf( __( 'There\'s a problem with the provided %sbrowser key%s. %s You can read more about how to determine the exact issue, and how to solve it %shere%s.','wpsl' ), '<a href="https://wpstorelocator.co/document/create-google-api-keys/#browser-key">','</a>', '<br><br>', '<a href="https://wpstorelocator.co/document/create-google-api-keys/#troubleshooting">','</a>' ),
303
+ 'dismissNotice' => __( 'Dismiss this notice.', 'wpsl' )
304
+ );
305
+
306
+ return $admin_js_l10n;
307
+ }
308
+
309
+ /**
310
+ * Plugin settings that are used in the wpsl-admin.js.
311
+ *
312
+ * @since 2.0.0
313
+ * @return array $settings_js The settings used in the wpsl-admin.js
314
+ */
315
+ public function js_settings() {
316
+
317
+ global $wpsl_settings;
318
+
319
+ $js_settings = array(
320
+ 'hourFormat' => $wpsl_settings['editor_hour_format'],
321
+ 'defaultLatLng' => $this->get_default_lat_lng(),
322
+ 'defaultZoom' => 6,
323
+ 'mapType' => $wpsl_settings['editor_map_type'],
324
+ 'requiredFields' => array( 'address', 'city', 'country' ),
325
+ 'ajaxurl' => wpsl_get_ajax_url()
326
+ );
327
+
328
+ return apply_filters( 'wpsl_admin_js_settings', $js_settings );
329
+ }
330
+
331
+ /**
332
+ * Get the coordinates that are used to
333
+ * show the map on the settings page.
334
+ *
335
+ * @since 2.2.5
336
+ * @return string $startLatLng The start coordinates
337
+ */
338
+ public function get_default_lat_lng() {
339
+
340
+ global $wpsl_settings;
341
+
342
+ $startLatLng = $wpsl_settings['start_latlng'];
343
+
344
+ // If no start coordinates exists, then set the default to Holland.
345
+ if ( !$startLatLng ) {
346
+ $startLatLng = '52.378153,4.899363';
347
+ }
348
+
349
+ return $startLatLng;
350
+ }
351
+
352
+ /**
353
+ * Add the required admin script.
354
+ *
355
+ * @since 1.0.0
356
+ * @return void
357
+ */
358
+ public function admin_scripts() {
359
+
360
+ $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
361
+
362
+ // Always load the main js admin file to make sure the "dismiss" link in the location notice works.
363
+ wp_enqueue_script( 'wpsl-admin-js', plugins_url( '/js/wpsl-admin'. $min .'.js', __FILE__ ), array( 'jquery' ), WPSL_VERSION_NUM, true );
364
+
365
+ $this->maybe_show_pointer();
366
+ $this->check_icon_font_usage();
367
+
368
+ // Only enqueue the rest of the css/js files if we are on a page that belongs to the store locator.
369
+ if ( ( get_post_type() == 'wpsl_stores' ) || ( isset( $_GET['post_type'] ) && ( $_GET['post_type'] == 'wpsl_stores' ) ) ) {
370
+
371
+ // Make sure no other Google Map scripts can interfere with the one from the store locator.
372
+ wpsl_deregister_other_gmaps();
373
+
374
+ wp_enqueue_style( 'jquery-style', '//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/themes/smoothness/jquery-ui.css' );
375
+ wp_enqueue_style( 'wpsl-admin-css', plugins_url( '/css/style'. $min .'.css', __FILE__ ), false );
376
+
377
+ wp_enqueue_media();
378
+ wp_enqueue_script( 'jquery-ui-dialog' );
379
+ wp_enqueue_script( 'wpsl-gmap', ( '//maps.google.com/maps/api/js' . wpsl_get_gmap_api_params( 'browser_key' ) ), false, WPSL_VERSION_NUM, true );
380
+
381
+ wp_enqueue_script( 'wpsl-queue', plugins_url( '/js/ajax-queue'. $min .'.js', __FILE__ ), array( 'jquery' ), WPSL_VERSION_NUM, true );
382
+ wp_enqueue_script( 'wpsl-retina', plugins_url( '/js/retina'. $min .'.js', __FILE__ ), array( 'jquery' ), WPSL_VERSION_NUM, true );
383
+
384
+ wp_localize_script( 'wpsl-admin-js', 'wpslL10n', $this->admin_js_l10n() );
385
+ wp_localize_script( 'wpsl-admin-js', 'wpslSettings', $this->js_settings() );
386
+ }
387
+ }
388
+
389
+ /**
390
+ * Check if we need to show the wpsl pointer.
391
+ *
392
+ * @since 2.0.0
393
+ * @return void
394
+ */
395
+ public function maybe_show_pointer() {
396
+
397
+ $disable_pointer = apply_filters( 'wpsl_disable_welcome_pointer', false );
398
+
399
+ if ( $disable_pointer ) {
400
+ return;
401
+ }
402
+
403
+ $dismissed_pointers = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
404
+
405
+ // If the user hasn't dismissed the wpsl pointer, enqueue the script and style, and call the action hook.
406
+ if ( !in_array( 'wpsl_signup_pointer', $dismissed_pointers ) ) {
407
+ wp_enqueue_style( 'wp-pointer' );
408
+ wp_enqueue_script( 'wp-pointer' );
409
+
410
+ add_action( 'admin_print_footer_scripts', array( $this, 'welcome_pointer_script' ) );
411
+ }
412
+ }
413
+
414
+ /**
415
+ * Add the script for the welcome pointer.
416
+ *
417
+ * @since 2.0.0
418
+ * @return void
419
+ */
420
+ public function welcome_pointer_script() {
421
+
422
+ $pointer_content = '<h3>' . __( 'Welcome to WP Store Locator', 'wpsl' ) . '</h3>';
423
+ $pointer_content .= '<p>' . __( 'Sign up for the latest plugin updates and announcements.', 'wpsl' ) . '</p>';
424
+ $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&amp;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>';
425
+ ?>
426
+
427
+ <script type="text/javascript">
428
+ //<![CDATA[
429
+ jQuery( document ).ready( function( $ ) {
430
+ $( '#menu-posts-wpsl_stores' ).pointer({
431
+ content: '<?php echo $pointer_content; ?>',
432
+ position: {
433
+ edge: 'left',
434
+ align: 'center'
435
+ },
436
+ pointerWidth: 350,
437
+ close: function () {
438
+ $.post( ajaxurl, {
439
+ pointer: 'wpsl_signup_pointer',
440
+ action: 'dismiss-wp-pointer'
441
+ });
442
+ }
443
+ }).pointer( 'open' );
444
+
445
+ // If a user clicked the "subscribe" button trigger the close button for the pointer.
446
+ $( ".wpsl-mc-wrap #mc-embedded-subscribe" ).on( "click", function() {
447
+ $( ".wp-pointer .close" ).trigger( "click" );
448
+ });
449
+ });
450
+ //]]>
451
+ </script>
452
+
453
+ <?php
454
+ }
455
+
456
+ /**
457
+ * Add link to the plugin action row.
458
+ *
459
+ * @since 2.0.0
460
+ * @param array $links The existing action links
461
+ * @param string $file The file path of the current plugin
462
+ * @return array $links The modified links
463
+ */
464
+ public function add_action_links( $links, $file ) {
465
+
466
+ if ( strpos( $file, 'wp-store-locator.php' ) !== false ) {
467
+ $settings_link = '<a href="' . admin_url( 'edit.php?post_type=wpsl_stores&page=wpsl_settings' ) . '" title="View WP Store Locator Settings">' . __( 'Settings', 'wpsl' ) . '</a>';
468
+ array_unshift( $links, $settings_link );
469
+ }
470
+
471
+ return $links;
472
+ }
473
+
474
+ /**
475
+ * Add links to the plugin meta row.
476
+ *
477
+ * @since 2.1.1
478
+ * @param array $links The existing meta links
479
+ * @param string $file The file path of the current plugin
480
+ * @return array $links The modified meta links
481
+ */
482
+ public function add_plugin_meta_row( $links, $file ) {
483
+
484
+ if ( strpos( $file, 'wp-store-locator.php' ) !== false ) {
485
+ $new_links = array(
486
+ '<a href="https://wpstorelocator.co/documentation/" title="View Documentation">'. __( 'Documentation', 'wpsl' ).'</a>',
487
+ '<a href="https://wpstorelocator.co/add-ons/" title="View Add-Ons">'. __( 'Add-Ons', 'wpsl' ).'</a>'
488
+ );
489
+
490
+ $links = array_merge( $links, $new_links );
491
+ }
492
+
493
+ return $links;
494
+ }
495
+
496
+ /**
497
+ * Change the footer text on the settings page.
498
+ *
499
+ * @since 2.0.0
500
+ * @param string $text The current footer text
501
+ * @return string $text Either the original or modified footer text
502
+ */
503
+ public function admin_footer_text( $text ) {
504
+
505
+ $current_screen = get_current_screen();
506
+
507
+ // Only modify the footer text if we are on the settings page of the wp store locator.
508
+ if ( isset( $current_screen->id ) && $current_screen->id == 'wpsl_stores_page_wpsl_settings' ) {
509
+ $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>' );
510
+ }
511
+
512
+ return $text;
513
+ }
514
+ }
515
+
516
+ $GLOBALS['wpsl_admin'] = new WPSL_Admin();
517
  }
admin/class-geocode.php CHANGED
@@ -1,291 +1,291 @@
1
- <?php
2
- /**
3
- * Geocode store locations
4
- *
5
- * @author Tijmen Smit
6
- * @since 2.0.0
7
- */
8
-
9
- if ( !defined( 'ABSPATH' ) ) exit;
10
-
11
- if ( !class_exists( 'WPSL_Geocode' ) ) {
12
-
13
- class WPSL_Geocode {
14
-
15
- /**
16
- * Check if we need to run a geocode request or use the current location data.
17
- *
18
- * The latlng value is only present if the user provided it himself, or used the preview
19
- * on the map. Otherwise the latlng will be missing and we need to geocode the supplied address.
20
- *
21
- * @since 2.0.0
22
- * @param integer $post_id Store post ID
23
- * @param array $store_data The store data
24
- * @return void
25
- */
26
- public function check_geocode_data( $post_id, $store_data ) {
27
-
28
- $location_data = array();
29
-
30
- // Check if the latlng data is valid.
31
- $latlng = $this->validate_latlng( $store_data['lat'], $store_data['lng'] );
32
-
33
- // If we don't have a valid latlng value, we geocode the supplied address to get one.
34
- if ( !$latlng ) {
35
- $response = $this->geocode_location( $post_id, $store_data );
36
-
37
- if ( empty( $response ) ) {
38
- return;
39
- }
40
-
41
- $location_data['country_iso'] = $response['country_iso'];
42
- $location_data['latlng'] = $response['latlng'];
43
- } else {
44
- $location_data['latlng'] = $latlng;
45
- }
46
-
47
- // Restrict the latlng to a max of 6 decimals.
48
- $location_data['latlng'] = $this->format_latlng( $location_data['latlng'] );
49
-
50
- $location_data['lat'] = $location_data['latlng']['lat'];
51
- $location_data['lng'] = $location_data['latlng']['lng'];
52
-
53
- $this->save_store_location( $post_id, $location_data );
54
- }
55
-
56
- /**
57
- * Geocode the store location.
58
- *
59
- * @since 1.0.0
60
- * @param integer $post_id Store post ID
61
- * @param array $store_data The submitted store data ( address, city, country etc )
62
- * @return void
63
- */
64
- public function geocode_location( $post_id, $store_data ) {
65
-
66
- $geocode_response = $this->get_latlng( $store_data );
67
-
68
- if ( isset( $geocode_response['status'] ) ) {
69
- switch ( $geocode_response['status'] ) {
70
- case 'OK':
71
- $country = $this->filter_country_name( $geocode_response );
72
-
73
- $location_data = array(
74
- 'country_iso' => $country['short_name'],
75
- 'latlng' => $this->format_latlng( $geocode_response['results'][0]['geometry']['location'] )
76
- );
77
-
78
- return $location_data;
79
- case 'ZERO_RESULTS':
80
- $msg = __( 'The Google Geocoding API returned no results for the supplied address. Please change the address and try again.', 'wpsl' );
81
- break;
82
- case 'OVER_QUERY_LIMIT':
83
- $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>' );
84
- break;
85
- case 'REQUEST_DENIED':
86
- $msg = sprintf( __( 'The Google Geocoding API returned REQUEST_DENIED. %s', 'wpsl' ), $this->check_geocode_error_msg( $geocode_response ) );
87
- break;
88
- default:
89
- $msg = __( 'The Google Geocoding API failed to return valid data, please try again later.', 'wpsl' );
90
- break;
91
- }
92
- } else {
93
- $msg = $geocode_response;
94
- }
95
-
96
- // Handle the geocode code errors messages.
97
- if ( !empty( $msg ) ) {
98
- $this->geocode_failed( $msg, $post_id );
99
- }
100
- }
101
-
102
- /**
103
- * Check if the response from the Geocode API contains an error message.
104
- *
105
- * @since 2.1.0
106
- * @param array $geocode_response The response from the Geocode API.
107
- * @return string $error_msg The error message, or empty if none exists.
108
- */
109
- public function check_geocode_error_msg( $geocode_response, $inc_breaks = true ) {
110
-
111
- $breaks = ( $inc_breaks ) ? '<br><br>' : '';
112
-
113
- if ( isset( $geocode_response['error_message'] ) && $geocode_response['error_message'] ) {
114
-
115
- // If the problem is IP based, then show a different error msg.
116
- if ( strpos( $geocode_response['error_message'], 'IP' ) !== false ) {
117
- $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, $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>' );
118
- } else {
119
- $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, $geocode_response['error_message'], $breaks, '<a href="https://wpstorelocator.co/document/create-google-api-keys/#troubleshooting">', '</a>', '<a href="https://wpstorelocator.co/support/">', '</a>' );
120
- }
121
- } else {
122
- $error_msg = '';
123
- }
124
-
125
- return $error_msg;
126
- }
127
-
128
- /**
129
- * Make the API call to Google to geocode the address.
130
- *
131
- * @since 1.0.0
132
- * @param array $store_data The store data
133
- * @return array|string $geo_response The response from the Google Geocode API, or the wp_remote_get error message.
134
- */
135
- public function get_latlng( $store_data ) {
136
-
137
- $address = $this->create_geocode_address( $store_data );
138
- $response = wpsl_call_geocode_api( $address );
139
-
140
- if ( is_wp_error( $response ) ) {
141
- $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>' );
142
- } else if ( $response['response']['code'] == 500 ) {
143
- $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>' );
144
- } else if ( $response['response']['code'] == 400 ) {
145
-
146
- // Check on which page the 400 error was triggered, and based on that adjust the msg.
147
- if ( isset( $_GET['page'] ) && $_GET['page'] == 'wpsl_csv' ) {
148
- $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>' );
149
- } else if ( !$address ) {
150
- $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.
151
- }
152
-
153
- $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 );
154
- } else if ( $response['response']['code'] != 200 ) {
155
- $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>' );
156
- } else {
157
- $geo_response = json_decode( $response['body'], true );
158
- }
159
-
160
- return $geo_response;
161
- }
162
-
163
- /**
164
- * Create the address we need to Geocode.
165
- *
166
- * @since 2.1.0
167
- * @param array $store_data The provided store data
168
- * @return string $geocode_address The address we are sending to the Geocode API separated by ,
169
- */
170
- public function create_geocode_address( $store_data ) {
171
-
172
- $address = array();
173
- $address_parts = array( 'address', 'city', 'state', 'zip', 'country' );
174
-
175
- foreach ( $address_parts as $address_part ) {
176
- if ( isset( $store_data[$address_part] ) && $store_data[$address_part] ) {
177
- $address[] = trim( $store_data[$address_part] );
178
- }
179
- }
180
-
181
- $geocode_address = implode( ',', $address );
182
-
183
- return $geocode_address;
184
- }
185
-
186
- /**
187
- * If there is a problem with the geocoding then we save the notice and change the post status to pending.
188
- *
189
- * @since 2.0.0
190
- * @param string $msg The geocode error message
191
- * @param integer $post_id Store post ID
192
- * @return void
193
- */
194
- public function geocode_failed( $msg, $post_id ) {
195
-
196
- global $wpsl_admin;
197
-
198
- $wpsl_admin->notices->save( 'error', $msg );
199
- $wpsl_admin->metaboxes->set_post_pending( $post_id );
200
- }
201
-
202
- /**
203
- * Save the store location data.
204
- *
205
- * @since 2.0.0
206
- * @param integer $post_id Store post ID
207
- * @param array $location_data The country code and latlng
208
- * @return void
209
- */
210
- public function save_store_location( $post_id, $location_data ) {
211
-
212
- if ( isset( $location_data['country_iso'] ) && ( !empty( $location_data['country_iso'] ) ) ) {
213
- update_post_meta( $post_id, 'wpsl_country_iso', $location_data['country_iso'] );
214
- }
215
-
216
- update_post_meta( $post_id, 'wpsl_lat', $location_data['latlng']['lat'] );
217
- update_post_meta( $post_id, 'wpsl_lng', $location_data['latlng']['lng'] );
218
- }
219
-
220
- /**
221
- * Make sure the latlng value has a max of 6 decimals.
222
- *
223
- * @since 2.0.0
224
- * @param array $latlng The latlng data
225
- * @return array $latlng The formatted latlng
226
- */
227
- public function format_latlng( $latlng ) {
228
-
229
- foreach ( $latlng as $key => $value ) {
230
- if ( strlen( substr( strrchr( $value, '.' ), 1 ) ) > 6 ) {
231
- $latlng[$key] = round( $value, 6 );
232
- }
233
- }
234
-
235
- return $latlng;
236
- }
237
-
238
- /**
239
- * Filter out the two letter country code from the Geocode API response.
240
- *
241
- * @since 1.0.0
242
- * @param array $response The full API geocode response
243
- * @return array $country The country ISO code and full name
244
- */
245
- public function filter_country_name( $response ) {
246
-
247
- $length = count( $response['results'][0]['address_components'] );
248
-
249
- $country = array();
250
-
251
- // Loop over the address components until we find the country, political part.
252
- for ( $i = 0; $i < $length; $i++ ) {
253
- $address_component = $response['results'][0]['address_components'][$i]['types'];
254
-
255
- if ( $address_component[0] == 'country' && $address_component[1] == 'political' ) {
256
- $country = $response['results'][0]['address_components'][$i];
257
-
258
- break;
259
- }
260
- }
261
-
262
- return $country;
263
- }
264
-
265
- /**
266
- * Validate the latlng values.
267
- *
268
- * @since 1.0.0
269
- * @param string $lat The latitude value
270
- * @param string $lng The longitude value
271
- * @return boolean|array $latlng The validated latlng values or false if it fails
272
- */
273
- public function validate_latlng( $lat, $lng ) {
274
-
275
- if ( !is_numeric( $lat ) || ( $lat > 90 ) || ( $lat < -90 ) ) {
276
- return false;
277
- }
278
-
279
- if ( !is_numeric( $lng ) || ( $lng > 180 ) || ( $lng < -180 ) ) {
280
- return false;
281
- }
282
-
283
- $latlng = array(
284
- 'lat' => $lat,
285
- 'lng' => $lng
286
- );
287
-
288
- return $latlng;
289
- }
290
- }
291
  }
1
+ <?php
2
+ /**
3
+ * Geocode store locations
4
+ *
5
+ * @author Tijmen Smit
6
+ * @since 2.0.0
7
+ */
8
+
9
+ if ( !defined( 'ABSPATH' ) ) exit;
10
+
11
+ if ( !class_exists( 'WPSL_Geocode' ) ) {
12
+
13
+ class WPSL_Geocode {
14
+
15
+ /**
16
+ * Check if we need to run a geocode request or use the current location data.
17
+ *
18
+ * The latlng value is only present if the user provided it himself, or used the preview
19
+ * on the map. Otherwise the latlng will be missing and we need to geocode the supplied address.
20
+ *
21
+ * @since 2.0.0
22
+ * @param integer $post_id Store post ID
23
+ * @param array $store_data The store data
24
+ * @return void
25
+ */
26
+ public function check_geocode_data( $post_id, $store_data ) {
27
+
28
+ $location_data = array();
29
+
30
+ // Check if the latlng data is valid.
31
+ $latlng = $this->validate_latlng( $store_data['lat'], $store_data['lng'] );
32
+
33
+ // If we don't have a valid latlng value, we geocode the supplied address to get one.
34
+ if ( !$latlng ) {
35
+ $response = $this->geocode_location( $post_id, $store_data );
36
+
37
+ if ( empty( $response ) ) {
38
+ return;
39
+ }
40
+
41
+ $location_data['country_iso'] = $response['country_iso'];
42
+ $location_data['latlng'] = $response['latlng'];
43
+ } else {
44
+ $location_data['latlng'] = $latlng;
45
+ }
46
+
47
+ // Restrict the latlng to a max of 6 decimals.
48
+ $location_data['latlng'] = $this->format_latlng( $location_data['latlng'] );
49
+
50
+ $location_data['lat'] = $location_data['latlng']['lat'];
51
+ $location_data['lng'] = $location_data['latlng']['lng'];
52
+
53
+ $this->save_store_location( $post_id, $location_data );
54
+ }
55
+
56
+ /**
57
+ * Geocode the store location.
58
+ *
59
+ * @since 1.0.0
60
+ * @param integer $post_id Store post ID
61
+ * @param array $store_data The submitted store data ( address, city, country etc )
62
+ * @return void
63
+ */
64
+ public function geocode_location( $post_id, $store_data ) {
65
+
66
+ $geocode_response = $this->get_latlng( $store_data );
67
+
68
+ if ( isset( $geocode_response['status'] ) ) {
69
+ switch ( $geocode_response['status'] ) {
70
+ case 'OK':
71
+ $country = $this->filter_country_name( $geocode_response );
72
+
73
+ $location_data = array(
74
+ 'country_iso' => $country['short_name'],
75
+ 'latlng' => $this->format_latlng( $geocode_response['results'][0]['geometry']['location'] )
76
+ );
77
+
78
+ return $location_data;
79
+ case 'ZERO_RESULTS':
80
+ $msg = __( 'The Google Geocoding API returned no results for the supplied address. Please change the address and try again.', 'wpsl' );
81
+ break;
82
+ case 'OVER_QUERY_LIMIT':
83
+ $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>' );
84
+ break;
85
+ case 'REQUEST_DENIED':
86
+ $msg = sprintf( __( 'The Google Geocoding API returned REQUEST_DENIED. %s', 'wpsl' ), $this->check_geocode_error_msg( $geocode_response ) );
87
+ break;
88
+ default:
89
+ $msg = __( 'The Google Geocoding API failed to return valid data, please try again later.', 'wpsl' );
90
+ break;
91
+ }
92
+ } else {
93
+ $msg = $geocode_response;
94
+ }
95
+
96
+ // Handle the geocode code errors messages.
97
+ if ( !empty( $msg ) ) {
98
+ $this->geocode_failed( $msg, $post_id );
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Check if the response from the Geocode API contains an error message.
104
+ *
105
+ * @since 2.1.0
106
+ * @param array $geocode_response The response from the Geocode API.
107
+ * @return string $error_msg The error message, or empty if none exists.
108
+ */
109
+ public function check_geocode_error_msg( $geocode_response, $inc_breaks = true ) {
110
+
111
+ $breaks = ( $inc_breaks ) ? '<br><br>' : '';
112
+
113
+ if ( isset( $geocode_response['error_message'] ) && $geocode_response['error_message'] ) {
114
+
115
+ // If the problem is IP based, then show a different error msg.
116
+ if ( strpos( $geocode_response['error_message'], 'IP' ) !== false ) {
117
+ $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, $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>' );
118
+ } else {
119
+ $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, $geocode_response['error_message'], $breaks, '<a href="https://wpstorelocator.co/document/create-google-api-keys/#troubleshooting">', '</a>', '<a href="https://wpstorelocator.co/support/">', '</a>' );
120
+ }
121
+ } else {
122
+ $error_msg = '';
123
+ }
124
+
125
+ return $error_msg;
126
+ }
127
+
128
+ /**
129
+ * Make the API call to Google to geocode the address.
130
+ *
131
+ * @since 1.0.0
132
+ * @param array $store_data The store data
133
+ * @return array|string $geo_response The response from the Google Geocode API, or the wp_remote_get error message.
134
+ */
135
+ public function get_latlng( $store_data ) {
136
+
137
+ $address = $this->create_geocode_address( $store_data );
138
+ $response = wpsl_call_geocode_api( $address );
139
+
140
+ if ( is_wp_error( $response ) ) {
141
+ $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>' );
142
+ } else if ( $response['response']['code'] == 500 ) {
143
+ $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>' );
144
+ } else if ( $response['response']['code'] == 400 ) {
145
+
146
+ // Check on which page the 400 error was triggered, and based on that adjust the msg.
147
+ if ( isset( $_GET['page'] ) && $_GET['page'] == 'wpsl_csv' ) {
148
+ $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>' );
149
+ } else if ( !$address ) {
150
+ $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.
151
+ }
152
+
153
+ $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 );
154
+ } else if ( $response['response']['code'] != 200 ) {
155
+ $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>' );
156
+ } else {
157
+ $geo_response = json_decode( $response['body'], true );
158
+ }
159
+
160
+ return $geo_response;
161
+ }
162
+
163
+ /**
164
+ * Create the address we need to Geocode.
165
+ *
166
+ * @since 2.1.0
167
+ * @param array $store_data The provided store data
168
+ * @return string $geocode_address The address we are sending to the Geocode API separated by ,
169
+ */
170
+ public function create_geocode_address( $store_data ) {
171
+
172
+ $address = array();
173
+ $address_parts = array( 'address', 'city', 'state', 'zip', 'country' );
174
+
175
+ foreach ( $address_parts as $address_part ) {
176
+ if ( isset( $store_data[$address_part] ) && $store_data[$address_part] ) {
177
+ $address[] = trim( $store_data[$address_part] );
178
+ }
179
+ }
180
+
181
+ $geocode_address = implode( ',', $address );
182
+
183
+ return $geocode_address;
184
+ }
185
+
186
+ /**
187
+ * If there is a problem with the geocoding then we save the notice and change the post status to pending.
188
+ *
189
+ * @since 2.0.0
190
+ * @param string $msg The geocode error message
191
+ * @param integer $post_id Store post ID
192
+ * @return void
193
+ */
194
+ public function geocode_failed( $msg, $post_id ) {
195
+
196
+ global $wpsl_admin;
197
+
198
+ $wpsl_admin->notices->save( 'error', $msg );
199
+ $wpsl_admin->metaboxes->set_post_pending( $post_id );
200
+ }
201
+
202
+ /**
203
+ * Save the store location data.
204
+ *
205
+ * @since 2.0.0
206
+ * @param integer $post_id Store post ID
207
+ * @param array $location_data The country code and latlng
208
+ * @return void
209
+ */
210
+ public function save_store_location( $post_id, $location_data ) {
211
+
212
+ if ( isset( $location_data['country_iso'] ) && ( !empty( $location_data['country_iso'] ) ) ) {
213
+ update_post_meta( $post_id, 'wpsl_country_iso', $location_data['country_iso'] );
214
+ }
215
+
216
+ update_post_meta( $post_id, 'wpsl_lat', $location_data['latlng']['lat'] );
217
+ update_post_meta( $post_id, 'wpsl_lng', $location_data['latlng']['lng'] );
218
+ }
219
+
220
+ /**
221
+ * Make sure the latlng value has a max of 6 decimals.
222
+ *
223
+ * @since 2.0.0
224
+ * @param array $latlng The latlng data
225
+ * @return array $latlng The formatted latlng
226
+ */
227
+ public function format_latlng( $latlng ) {
228
+
229
+ foreach ( $latlng as $key => $value ) {
230
+ if ( strlen( substr( strrchr( $value, '.' ), 1 ) ) > 6 ) {
231
+ $latlng[$key] = round( $value, 6 );
232
+ }
233
+ }
234
+
235
+ return $latlng;
236
+ }
237
+
238
+ /**
239
+ * Filter out the two letter country code from the Geocode API response.
240
+ *
241
+ * @since 1.0.0
242
+ * @param array $response The full API geocode response
243
+ * @return array $country The country ISO code and full name
244
+ */
245
+ public function filter_country_name( $response ) {
246
+
247
+ $length = count( $response['results'][0]['address_components'] );
248
+
249
+ $country = array();
250
+
251
+ // Loop over the address components until we find the country, political part.
252
+ for ( $i = 0; $i < $length; $i++ ) {
253
+ $address_component = $response['results'][0]['address_components'][$i]['types'];
254
+
255
+ if ( $address_component[0] == 'country' && $address_component[1] == 'political' ) {
256
+ $country = $response['results'][0]['address_components'][$i];
257
+
258
+ break;
259
+ }
260
+ }
261
+
262
+ return $country;
263
+ }
264
+
265
+ /**
266
+ * Validate the latlng values.
267
+ *
268
+ * @since 1.0.0
269
+ * @param string $lat The latitude value
270
+ * @param string $lng The longitude value
271
+ * @return boolean|array $latlng The validated latlng values or false if it fails
272
+ */
273
+ public function validate_latlng( $lat, $lng ) {
274
+
275
+ if ( !is_numeric( $lat ) || ( $lat > 90 ) || ( $lat < -90 ) ) {
276
+ return false;
277
+ }
278
+
279
+ if ( !is_numeric( $lng ) || ( $lng > 180 ) || ( $lng < -180 ) ) {
280
+ return false;
281
+ }
282
+
283
+ $latlng = array(
284
+ 'lat' => $lat,
285
+ 'lng' => $lng
286
+ );
287
+
288
+ return $latlng;
289
+ }
290
+ }
291
  }
admin/class-license-manager.php CHANGED
@@ -1,294 +1,294 @@
1
- <?php
2
- /**
3
- * Handle the add-on license and updates.
4
- *
5
- * @author Tijmen Smit
6
- * @since 2.1.0
7
- */
8
-
9
- if ( !defined( 'ABSPATH' ) ) exit;
10
-
11
- class WPSL_License_Manager {
12
-
13
- public $item_name;
14
- public $item_shortname;
15
- public $version;
16
- public $author;
17
- public $file;
18
- public $api_url = 'https://wpstorelocator.co/';
19
-
20
- /**
21
- * Class constructor
22
- *
23
- * @param string $item_name
24
- * @param string $version
25
- * @param string $author
26
- * @param string $file
27
- */
28
- function __construct( $item_name, $version, $author, $file ) {
29
-
30
- $this->item_name = $item_name;
31
- $this->item_shortname = 'wpsl_' . preg_replace( '/[^a-zA-Z0-9_\s]/', '', str_replace( ' ', '_', strtolower( $this->item_name ) ) );
32
- $this->version = $version;
33
- $this->author = $author;
34
- $this->file = $file;
35
-
36
- $this->includes();
37
-
38
- add_action( 'admin_init', array( $this, 'auto_updater' ), 0 );
39
- add_action( 'admin_init', array( $this, 'license_actions' ) );
40
- add_filter( 'wpsl_license_settings', array( $this, 'add_license_field' ), 1 );
41
- }
42
-
43
- /**
44
- * Include the updater class
45
- *
46
- * @since 2.1.0
47
- * @access private
48
- * @return void
49
- */
50
- private function includes() {
51
- if ( !class_exists( 'EDD_SL_Plugin_Updater' ) ) {
52
- require_once 'EDD_SL_Plugin_Updater.php';
53
- }
54
- }
55
-
56
- /**
57
- * Handle the add-on updates.
58
- *
59
- * @since 2.1.0
60
- * @return void
61
- */
62
- public function auto_updater() {
63
-
64
- if ( $this->get_license_option( 'status' ) !== 'valid' ) {
65
- return;
66
- }
67
-
68
- $args = array(
69
- 'version' => $this->version,
70
- 'license' => $this->get_license_option( 'key' ),
71
- 'author' => $this->author,
72
- 'item_name' => $this->item_name
73
- );
74
-
75
- // Setup the updater.
76
- $edd_updater = new EDD_SL_Plugin_Updater(
77
- $this->api_url,
78
- $this->file,
79
- $args
80
- );
81
- }
82
-
83
- /**
84
- * Check which license actions to take.
85
- *
86
- * @since 2.1.0
87
- * @return void
88
- */
89
- public function license_actions() {
90
-
91
- if ( !isset( $_POST['wpsl_licenses'] ) ) {
92
- return;
93
- }
94
-
95
- if ( !isset( $_POST['wpsl_licenses'][ $this->item_shortname ] ) || empty( $_POST['wpsl_licenses'][ $this->item_shortname ] ) ) {
96
- return;
97
- }
98
-
99
- if ( !check_admin_referer( $this->item_shortname . '_license-nonce', $this->item_shortname . '_license-nonce' ) ) {
100
- return;
101
- }
102
-
103
- if ( !current_user_can( 'manage_wpsl_settings' ) ) {
104
- return;
105
- }
106
-
107
- if ( isset( $_POST[ $this->item_shortname . '_license_key_deactivate' ] ) ) {
108
- $this->deactivate_license();
109
- } else {
110
- $this->activate_license();
111
- }
112
- }
113
-
114
- /**
115
- * Try to activate the license key.
116
- *
117
- * @since 2.1.0
118
- * @return void
119
- */
120
- public function activate_license() {
121
-
122
- // Stop if the current license is already active.
123
- if ( $this->get_license_option( 'status' ) == 'valid' ) {
124
- return;
125
- }
126
-
127
- $license = sanitize_text_field( $_POST['wpsl_licenses'][ $this->item_shortname ] );
128
-
129
- // data to send in our API request.
130
- $api_params = array(
131
- 'edd_action' => 'activate_license',
132
- 'license' => $license,
133
- 'item_name' => urlencode( $this->item_name ),
134
- 'url' => home_url()
135
- );
136
-
137
- // Get the license data from the API.
138
- $license_data = $this->call_license_api( $api_params );
139
-
140
- if ( $license_data ) {
141
- update_option(
142
- $this->item_shortname . '_license_data',
143
- array(
144
- 'key' => $license,
145
- 'expiration' => $license_data->expires,
146
- 'status' => $license_data->license
147
- )
148
- );
149
-
150
- if ( $license_data->success ) {
151
- $this->set_license_notice( $this->item_name . ' license activated.', 'updated' );
152
- } else if ( !empty( $license_data->error ) ) {
153
- $this->handle_activation_errors( $license_data->error );
154
- }
155
- }
156
- }
157
-
158
- /**
159
- * Deactivate the license key.
160
- *
161
- * @since 2.1.0
162
- * @return void
163
- */
164
- public function deactivate_license() {
165
-
166
- // Data to send to the API
167
- $api_params = array(
168
- 'edd_action' => 'deactivate_license',
169
- 'license' => $this->get_license_option( 'key' ),
170
- 'item_name' => urlencode( $this->item_name ),
171
- 'url' => home_url()
172
- );
173
-
174
- // Get the license data from the API.
175
- $license_data = $this->call_license_api( $api_params );
176
-
177
- if ( $license_data ) {
178
- if ( $license_data->license == 'deactivated' ) {
179
- delete_option( $this->item_shortname . '_license_data' );
180
-
181
- $this->set_license_notice( $this->item_name . ' license deactivated.', 'updated' );
182
- } else {
183
- $message = sprintf (__( 'The %s license failed to deactivate, please try again later or contact support!', 'wpsl' ), $this->item_name );
184
- $this->set_license_notice( $message, 'error' );
185
- }
186
- }
187
- }
188
-
189
- /**
190
- * Access the license API.
191
- *
192
- * @since 2.1.0
193
- * @params array $api_params The used API parameters
194
- * @return void|array $license_data The returned license data on success
195
- */
196
- public function call_license_api( $api_params ) {
197
-
198
- $response = wp_remote_post(
199
- $this->api_url,
200
- array(
201
- 'timeout' => 15,
202
- 'sslverify' => false,
203
- 'body' => $api_params
204
- )
205
- );
206
-
207
- // Make sure the response came back okay.
208
- if ( is_wp_error( $response ) ) {
209
- $message = $response->get_error_message() . '. ' . __( 'Please try again later!', 'wpsl' );
210
- $this->set_license_notice( $message, 'error' );
211
- } else {
212
- $license_data = json_decode( wp_remote_retrieve_body( $response ) );
213
-
214
- return $license_data;
215
- }
216
- }
217
-
218
- /**
219
- * Get a single license option.
220
- *
221
- * @since 2.1.0
222
- * @param string $option Name of the license option.
223
- * @return void|string The value for the license option.
224
- */
225
- public function get_license_option( $option ) {
226
-
227
- $license_data = get_option( $this->item_shortname . '_license_data' );
228
-
229
- if ( isset( $license_data[ $option ] ) ) {
230
- return $license_data[ $option ];
231
- }
232
- }
233
-
234
- /**
235
- * Set a notice holding license information.
236
- *
237
- * @since 2.1.0
238
- * @param string $message The license message to display.
239
- * @param string $type Either updated or error.
240
- * @return void
241
- */
242
- public function set_license_notice( $message, $type ) {
243
- add_settings_error( $this->item_shortname . '-license', 'license-notice', $message, $type );
244
- }
245
-
246
- /**
247
- * Check the different license activation errors.
248
- *
249
- * @since 2.1.0
250
- * @param string $activation_errors The activation errors returned by the license API.
251
- * @return void
252
- */
253
- public function handle_activation_errors( $activation_errors ) {
254
-
255
- switch ( $activation_errors ) {
256
- case 'item_name_mismatch':
257
- $error_msg = sprintf( __( 'The %s license key does not belong to this add-on.', 'wpsl' ), $this->item_name );
258
- break;
259
- case 'no_activations_left':
260
- $error_msg = sprintf( __( 'The %s license key does not have any activations left.', 'wpsl' ), $this->item_name );
261
- break;
262
- case 'expired':
263
- $error_msg = sprintf( __( 'The %s license key is expired. Please renew it.', 'wpsl' ), $this->item_name );
264
- break;
265
- default:
266
- $error_msg = sprintf( __( 'There was a problem activating the license key for the %s, please try again or contact support. Error code: %s', 'wpsl' ), $this->item_name, $activation_errors );
267
- break;
268
- }
269
-
270
- $this->set_license_notice( $error_msg, 'error' );
271
- }
272
-
273
- /**
274
- * Add license fields to the settings.
275
- *
276
- * @since 2.1.0
277
- * @param array $settings The existing settings.
278
- * @return array
279
- */
280
- public function add_license_field( $settings ) {
281
-
282
- $license_setting = array(
283
- array(
284
- 'name' => $this->item_name,
285
- 'short_name' => $this->item_shortname,
286
- 'status' => $this->get_license_option( 'status' ),
287
- 'key' => $this->get_license_option( 'key' ),
288
- 'expiration' => $this->get_license_option( 'expiration' )
289
- )
290
- );
291
-
292
- return array_merge( $settings, $license_setting );
293
- }
294
  }
1
+ <?php
2
+ /**
3
+ * Handle the add-on license and updates.
4
+ *
5
+ * @author Tijmen Smit
6
+ * @since 2.1.0
7
+ */
8
+
9
+ if ( !defined( 'ABSPATH' ) ) exit;
10
+
11
+ class WPSL_License_Manager {
12
+
13
+ public $item_name;
14
+ public $item_shortname;
15
+ public $version;
16
+ public $author;
17
+ public $file;
18
+ public $api_url = 'https://wpstorelocator.co/';
19
+
20
+ /**
21
+ * Class constructor
22
+ *
23
+ * @param string $item_name
24
+ * @param string $version
25
+ * @param string $author
26
+ * @param string $file
27
+ */
28
+ function __construct( $item_name, $version, $author, $file ) {
29
+
30
+ $this->item_name = $item_name;
31
+ $this->item_shortname = 'wpsl_' . preg_replace( '/[^a-zA-Z0-9_\s]/', '', str_replace( ' ', '_', strtolower( $this->item_name ) ) );
32
+ $this->version = $version;
33
+ $this->author = $author;
34
+ $this->file = $file;
35
+
36
+ $this->includes();
37
+
38
+ add_action( 'admin_init', array( $this, 'auto_updater' ), 0 );
39
+ add_action( 'admin_init', array( $this, 'license_actions' ) );
40
+ add_filter( 'wpsl_license_settings', array( $this, 'add_license_field' ), 1 );
41
+ }
42
+
43
+ /**
44
+ * Include the updater class
45
+ *
46
+ * @since 2.1.0
47
+ * @access private
48
+ * @return void
49
+ */
50
+ private function includes() {
51
+ if ( !class_exists( 'EDD_SL_Plugin_Updater' ) ) {
52
+ require_once 'EDD_SL_Plugin_Updater.php';
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Handle the add-on updates.
58
+ *
59
+ * @since 2.1.0
60
+ * @return void
61
+ */
62
+ public function auto_updater() {
63
+
64
+ if ( $this->get_license_option( 'status' ) !== 'valid' ) {
65
+ return;
66
+ }
67
+
68
+ $args = array(
69
+ 'version' => $this->version,
70
+ 'license' => $this->get_license_option( 'key' ),
71
+ 'author' => $this->author,
72
+ 'item_name' => $this->item_name
73
+ );
74
+
75
+ // Setup the updater.
76
+ $edd_updater = new EDD_SL_Plugin_Updater(
77
+ $this->api_url,
78
+ $this->file,
79
+ $args
80
+ );
81
+ }
82
+
83
+ /**
84
+ * Check which license actions to take.
85
+ *
86
+ * @since 2.1.0
87
+ * @return void
88
+ */
89
+ public function license_actions() {
90
+
91
+ if ( !isset( $_POST['wpsl_licenses'] ) ) {
92
+ return;
93
+ }
94
+
95
+ if ( !isset( $_POST['wpsl_licenses'][ $this->item_shortname ] ) || empty( $_POST['wpsl_licenses'][ $this->item_shortname ] ) ) {
96
+ return;
97
+ }
98
+
99
+ if ( !check_admin_referer( $this->item_shortname . '_license-nonce', $this->item_shortname . '_license-nonce' ) ) {
100
+ return;
101
+ }
102
+
103
+ if ( !current_user_can( 'manage_wpsl_settings' ) ) {
104
+ return;
105
+ }
106
+
107
+ if ( isset( $_POST[ $this->item_shortname . '_license_key_deactivate' ] ) ) {
108
+ $this->deactivate_license();
109
+ } else {
110
+ $this->activate_license();
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Try to activate the license key.
116
+ *
117
+ * @since 2.1.0
118
+ * @return void
119
+ */
120
+ public function activate_license() {
121
+
122
+ // Stop if the current license is already active.
123
+ if ( $this->get_license_option( 'status' ) == 'valid' ) {
124
+ return;
125
+ }
126
+
127
+ $license = sanitize_text_field( $_POST['wpsl_licenses'][ $this->item_shortname ] );
128
+
129
+ // data to send in our API request.
130
+ $api_params = array(
131
+ 'edd_action' => 'activate_license',
132
+ 'license' => $license,
133
+ 'item_name' => urlencode( $this->item_name ),
134
+ 'url' => home_url()
135
+ );
136
+
137
+ // Get the license data from the API.
138
+ $license_data = $this->call_license_api( $api_params );
139
+
140
+ if ( $license_data ) {
141
+ update_option(
142
+ $this->item_shortname . '_license_data',
143
+ array(
144
+ 'key' => $license,
145
+ 'expiration' => $license_data->expires,
146
+ 'status' => $license_data->license
147
+ )
148
+ );
149
+
150
+ if ( $license_data->success ) {
151
+ $this->set_license_notice( $this->item_name . ' license activated.', 'updated' );
152
+ } else if ( !empty( $license_data->error ) ) {
153
+ $this->handle_activation_errors( $license_data->error );
154
+ }
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Deactivate the license key.
160
+ *
161
+ * @since 2.1.0
162
+ * @return void
163
+ */
164
+ public function deactivate_license() {
165
+
166
+ // Data to send to the API
167
+ $api_params = array(
168
+ 'edd_action' => 'deactivate_license',
169
+ 'license' => $this->get_license_option( 'key' ),
170
+ 'item_name' => urlencode( $this->item_name ),
171
+ 'url' => home_url()
172
+ );
173
+
174
+ // Get the license data from the API.
175
+ $license_data = $this->call_license_api( $api_params );
176
+
177
+ if ( $license_data ) {
178
+ if ( $license_data->license == 'deactivated' ) {
179
+ delete_option( $this->item_shortname . '_license_data' );
180
+
181
+ $this->set_license_notice( $this->item_name . ' license deactivated.', 'updated' );
182
+ } else {
183
+ $message = sprintf (__( 'The %s license failed to deactivate, please try again later or contact support!', 'wpsl' ), $this->item_name );
184
+ $this->set_license_notice( $message, 'error' );
185
+ }
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Access the license API.
191
+ *
192
+ * @since 2.1.0
193
+ * @params array $api_params The used API parameters
194
+ * @return void|array $license_data The returned license data on success
195
+ */
196
+ public function call_license_api( $api_params ) {
197
+
198
+ $response = wp_remote_post(
199
+ $this->api_url,
200
+ array(
201
+ 'timeout' => 15,
202
+ 'sslverify' => false,
203
+ 'body' => $api_params
204
+ )
205
+ );
206
+
207
+ // Make sure the response came back okay.
208
+ if ( is_wp_error( $response ) ) {
209
+ $message = $response->get_error_message() . '. ' . __( 'Please try again later!', 'wpsl' );
210
+ $this->set_license_notice( $message, 'error' );
211
+ } else {
212
+ $license_data = json_decode( wp_remote_retrieve_body( $response ) );
213
+
214
+ return $license_data;
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Get a single license option.
220
+ *
221
+ * @since 2.1.0
222
+ * @param string $option Name of the license option.
223
+ * @return void|string The value for the license option.
224
+ */
225
+ public function get_license_option( $option ) {
226
+
227
+ $license_data = get_option( $this->item_shortname . '_license_data' );
228
+
229
+ if ( isset( $license_data[ $option ] ) ) {
230
+ return $license_data[ $option ];
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Set a notice holding license information.
236
+ *
237
+ * @since 2.1.0
238
+ * @param string $message The license message to display.
239
+ * @param string $type Either updated or error.
240
+ * @return void
241
+ */
242
+ public function set_license_notice( $message, $type ) {
243
+ add_settings_error( $this->item_shortname . '-license', 'license-notice', $message, $type );
244
+ }
245
+
246
+ /**
247
+ * Check the different license activation errors.
248
+ *
249
+ * @since 2.1.0
250
+ * @param string $activation_errors The activation errors returned by the license API.
251
+ * @return void
252
+ */
253
+ public function handle_activation_errors( $activation_errors ) {
254
+
255
+ switch ( $activation_errors ) {
256
+ case 'item_name_mismatch':
257
+ $error_msg = sprintf( __( 'The %s license key does not belong to this add-on.', 'wpsl' ), $this->item_name );
258
+ break;
259
+ case 'no_activations_left':
260
+ $error_msg = sprintf( __( 'The %s license key does not have any activations left.', 'wpsl' ), $this->item_name );
261
+ break;
262
+ case 'expired':
263
+ $error_msg = sprintf( __( 'The %s license key is expired. Please renew it.', 'wpsl' ), $this->item_name );
264
+ break;
265
+ default:
266
+ $error_msg = sprintf( __( 'There was a problem activating the license key for the %s, please try again or contact support. Error code: %s', 'wpsl' ), $this->item_name, $activation_errors );
267
+ break;
268
+ }
269
+
270
+ $this->set_license_notice( $error_msg, 'error' );
271
+ }
272
+
273
+ /**
274
+ * Add license fields to the settings.
275
+ *
276
+ * @since 2.1.0
277
+ * @param array $settings The existing settings.
278
+ * @return array
279
+ */
280
+ public function add_license_field( $settings ) {
281
+
282
+ $license_setting = array(
283
+ array(
284
+ 'name' => $this->item_name,
285
+ 'short_name' => $this->item_shortname,
286
+ 'status' => $this->get_license_option( 'status' ),
287
+ 'key' => $this->get_license_option( 'key' ),
288
+ 'expiration' => $this->get_license_option( 'expiration' )
289
+ )
290
+ );
291
+
292
+ return array_merge( $settings, $license_setting );
293
+ }
294
  }
admin/class-metaboxes.php CHANGED
@@ -1,924 +1,924 @@
1
- <?php
2
- /**
3
- * Handle the metaboxes
4
- *
5
- * @author Tijmen Smit
6
- * @since 2.0.0
7
- */
8
-
9
- if ( !defined( 'ABSPATH' ) ) exit;
10
-
11
- if ( !class_exists( 'WPSL_Metaboxes' ) ) {
12
-
13
- /**
14
- * Handle the meta boxes
15
- *
16
- * @since 2.0.0
17
- */
18
- class WPSL_Metaboxes {
19
-
20
- public function __construct() {
21
- add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
22
- add_action( 'save_post', array( $this, 'save_post' ) );
23
- add_action( 'post_updated_messages', array( $this, 'store_update_messages' ) );
24
- }
25
-
26
- /**
27
- * Add the meta boxes.
28
- *
29
- * @since 2.0.0
30
- * @return void
31
- */
32
- public function add_meta_boxes() {
33
-
34
- global $pagenow;
35
-
36
- add_meta_box( 'wpsl-store-details', __( 'Store Details', 'wpsl' ), array( $this, 'create_meta_fields' ), 'wpsl_stores', 'normal', 'high' );
37
- add_meta_box( 'wpsl-map-preview', __( 'Store Map', 'wpsl' ), array( $this, 'map_preview' ), 'wpsl_stores', 'side' );
38
-
39
- $enable_option = apply_filters( 'wpsl_enable_export_option', true );
40
-
41
- if ( $enable_option && $pagenow == 'post.php' ) {
42
- add_meta_box( 'wpsl-data-export', __( 'Export', 'wpsl' ), array( $this, 'export_data' ), 'wpsl_stores', 'side', 'low' );
43
- }
44
- }
45
-
46
- /**
47
- * The store locator meta box fields.
48
- *
49
- * @since 2.0.0
50
- * @return array $meta_fields The meta box fields used for the store details
51
- */
52
- public function meta_box_fields() {
53
-
54
- global $wpsl_settings;
55
-
56
- $meta_fields = array(
57
- __( 'Location', 'wpsl' ) => array(
58
- 'address' => array(
59
- 'label' => __( 'Address', 'wpsl' ),
60
- 'required' => true
61
- ),
62
- 'address2' => array(
63
- 'label' => __( 'Address 2', 'wpsl' )
64
- ),
65
- 'city' => array(
66
- 'label' => __( 'City', 'wpsl' ),
67
- 'required' => true
68
- ),
69
- 'state' => array(
70
- 'label' => __( 'State', 'wpsl' )
71
- ),
72
- 'zip' => array(
73
- 'label' => __( 'Zip Code', 'wpsl' )
74
- ),
75
- 'country' => array(
76
- 'label' => __( 'Country', 'wpsl' ),
77
- 'required' => true
78
- ),
79
- 'country_iso' => array(
80
- 'type' => 'hidden'
81
- ),
82
- 'lat' => array(
83
- 'label' => __( 'Latitude', 'wpsl' )
84
- ),
85
- 'lng' => array(
86
- 'label' => __( 'Longitude', 'wpsl' )
87
- )
88
- ),
89
- __( 'Opening Hours', 'wpsl' ) => array(
90
- 'hours' => array(
91
- 'label' => __( 'Hours', 'wpsl' ),
92
- '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
93
- )
94
- ),
95
- __( 'Additional Information', 'wpsl' ) => array(
96
- 'phone' => array(
97
- 'label' => __( 'Tel', 'wpsl' )
98
- ),
99
- 'fax' => array(
100
- 'label' => __( 'Fax', 'wpsl' )
101
- ),
102
- 'email' => array(
103
- 'label' => __( 'Email', 'wpsl' )
104
- ),
105
- 'url' => array(
106
- 'label' => __( 'Url', 'wpsl' )
107
- )
108
- )
109
- );
110
-
111
- return apply_filters( 'wpsl_meta_box_fields', $meta_fields );
112
- }
113
-
114
- /**
115
- * Create the store locator metabox input fields.
116
- *
117
- * @since 2.0.0
118
- * @return void
119
- */
120
- function create_meta_fields() {
121
-
122
- global $wpsl_settings, $wp_version;
123
-
124
- $i = 0;
125
- $j = 0;
126
- $tab_items = '';
127
-
128
- wp_nonce_field( 'save_store_meta', 'wpsl_meta_nonce' );
129
- ?>
130
-
131
- <div class="wpsl-store-meta <?php if ( floatval( $wp_version ) < 3.8 ) { echo 'wpsl-pre-38'; } // Fix CSS issue with < 3.8 versions ?>">
132
- <?php
133
-
134
- // Create the tab navigation for the meta boxes.
135
- foreach ( $this->meta_box_fields() as $tab => $meta_fields ) {
136
- $active_class = ( $i == 0 ) ? ' wpsl-active' : '';
137
-
138
- if ( $wpsl_settings['hide_hours'] && $tab == __( 'Opening Hours', 'wpsl' ) ) {
139
- continue;
140
- } else {
141
- $tab_items .= $this->meta_field_nav( $tab, $active_class );
142
- }
143
-
144
- $i++;
145
- }
146
-
147
- echo '<ul id="wpsl-meta-nav">' . $tab_items . '</ul>';
148
-
149
- // Create the input fields for the meta boxes.
150
- foreach ( $this->meta_box_fields() as $tab => $meta_fields ) {
151
- $active_class = ( $j == 0 ) ? ' wpsl-active' : '';
152
-
153
- if ( $wpsl_settings['hide_hours'] && $tab == __( 'Opening Hours', 'wpsl' ) ) {
154
- continue;
155
- } else {
156
- echo '<div class="wpsl-tab wpsl-' . esc_attr( strtolower( str_replace( ' ', '-', $tab ) ) ) . $active_class . '">';
157
-
158
- foreach ( $meta_fields as $field_key => $field_data ) {
159
-
160
- // If no specific field type is set, we set it to text.
161
- $field_type = ( empty( $field_data['type'] ) ) ? 'text' : $field_data['type'];
162
- $args = array(
163
- 'key' => $field_key,
164
- 'data' => $field_data
165
- );
166
-
167
- // Check for a class method, otherwise enable a plugin hook.
168
- if ( method_exists( $this, $field_type . '_input' ) ) {
169
- call_user_func( array( $this, $field_type . '_input' ), $args );
170
- } else {
171
- do_action( 'wpsl_metabox_' . $field_type . '_input', $args );
172
- }
173
- }
174
-
175
- echo '</div>';
176
- }
177
-
178
- $j++;
179
- }
180
- ?>
181
- </div>
182
- <?php
183
- }
184
-
185
- /**
186
- * Create the li elements that are used in the tabs above the store meta fields.
187
- *
188
- * @since 2.0.0
189
- * @param string $tab The name of the tab
190
- * @param string $active_class Either the class name or empty
191
- * @return string $nav_item The HTML for the nav list
192
- */
193
- public function meta_field_nav( $tab, $active_class ) {
194
-
195
- $tab_lower = strtolower( str_replace( ' ', '-', $tab ) );
196
- $nav_item = '<li class="wpsl-' . esc_attr( $tab_lower ) . '-tab ' . $active_class . '"><a href="#wpsl-' . esc_attr( $tab_lower ) . '">' . esc_html( $tab ) . '</a></li>';
197
-
198
- return $nav_item;
199
- }
200
-
201
- /**
202
- * Set the CSS class that tells JS it's an required input field.
203
- *
204
- * @since 2.0.0
205
- * @param array $args The css classes
206
- * @param string $single Whether to return just the class name, or also include the class=""
207
- * @return string|void $response The required CSS class or nothing
208
- */
209
- public function set_required_class( $args, $single = false ) {
210
-
211
- if ( isset( $args['required'] ) && ( $args['required'] ) ) {
212
- if ( !$single ) {
213
- $response = 'class="wpsl-required"';
214
- } else {
215
- $response = 'wpsl-required';
216
- }
217
-
218
- return $response;
219
- }
220
- }
221
-
222
- /**
223
- * Check if the current field is required.
224
- *
225
- * @since 2.0.0
226
- * @param array $args The CSS classes
227
- * @return string|void The HTML for the required element or nothing
228
- */
229
- public function is_required_field( $args ) {
230
-
231
- if ( isset( $args['required'] ) && ( $args['required'] ) ) {
232
- $response = '<span class="wpsl-star"> *</span>';
233
-
234
- return $response;
235
- }
236
- }
237
-
238
- /**
239
- * Get the prefilled field data.
240
- *
241
- * @since 2.0.0
242
- * @param string $field_name The name of the field to get the data for
243
- * @return string $field_data The field data
244
- */
245
- public function get_prefilled_field_data( $field_name ) {
246
-
247
- global $wpsl_settings, $pagenow;
248
-
249
- $field_data = '';
250
-
251
- // Prefilled values are only used for new pages, not when a user edits an existing page.
252
- if ( $pagenow == 'post.php' && isset( $_GET['action'] ) && $_GET['action'] == 'edit' ) {
253
- return;
254
- }
255
-
256
- $prefilled_fields = array(
257
- 'country',
258
- 'hours'
259
- );
260
-
261
- if ( in_array( $field_name, $prefilled_fields ) ) {
262
- $field_data = $wpsl_settings['editor_' . $field_name];
263
- }
264
-
265
- return $field_data;
266
- }
267
-
268
- /**
269
- * Create a text input field.
270
- *
271
- * @since 2.0.0
272
- * @param array $args The input name and label
273
- * @return void
274
- */
275
- public function text_input( $args ) {
276
-
277
- $saved_value = $this->get_store_meta( $args['key'] );
278
-
279
- // If there is no existing meta value, check if a prefilled value exists for the input field.
280
- if ( !$saved_value ) {
281
- $saved_value = $this->get_prefilled_field_data( $args['key'] );
282
- }
283
- ?>
284
-
285
- <p>
286
- <label for="wpsl-<?php echo esc_attr( $args['key'] ); ?>"><?php echo esc_html( $args['data']['label'] ) . ': ' . $this->is_required_field( $args['data'] ); ?></label>
287
- <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 ); ?>" />
288
- </p>
289
-
290
- <?php
291
- }
292
-
293
- /**
294
- * Create a hidden input field.
295
- *
296
- * @since 2.0.0
297
- * @param array $args The name of the meta value
298
- * @return void
299
- */
300
- public function hidden_input( $args ) {
301
-
302
- $saved_value = $this->get_store_meta( $args['key'] );
303
- ?>
304
-
305
- <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 ); ?>" />
306
-
307
- <?php
308
- }
309
-
310
- /**
311
- * Create a textarea field.
312
- *
313
- * @since 2.0.0
314
- * @param array $args The textarea name and label
315
- * @return void
316
- */
317
- public function textarea_input( $args ) {
318
-
319
- $saved_value = $this->get_store_meta( $args['key'] );
320
-
321
- if ( $args['key'] == 'hours' && gettype( $saved_value ) !== 'string' ) {
322
- $saved_value = '';
323
- }
324
-
325
- // If there is no existing meta value, check if a prefilled value exists for the textarea.
326
- if ( !$saved_value ) {
327
- $prefilled_value = $this->get_prefilled_field_data( $args['key'] );
328
-
329
- if ( isset( $prefilled_value['textarea'] ) ) {
330
- $saved_value = $prefilled_value['textarea'];
331
- }
332
- }
333
- ?>
334
-
335
- <p>
336
- <label for="wpsl-<?php echo esc_attr( $args['key'] ); ?>"><?php echo esc_html( $args['data']['label'] ) . ': ' . $this->is_required_field( $args['data'] ); ?></label>
337
- <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>
338
- </p>
339
-
340
- <?php
341
- }
342
-
343
- /**
344
- * Create a wp editor field.
345
- *
346
- * @since 2.1.1
347
- * @param array $args The wp editor name and label
348
- * @return void
349
- */
350
- public function wp_editor_input( $args ) {
351
-
352
- $saved_value = $this->get_store_meta( $args['key'] );
353
- ?>
354
-
355
- <p>
356
- <label for="wpsl-<?php echo esc_attr( $args['key'] ); ?>"><?php echo esc_html( $args['data']['label'] ) . ': ' . $this->is_required_field( $args['data'] ); ?></label>
357
- <?php wp_editor( $saved_value, 'wpsleditor_' . wpsl_random_chars(), $settings = array('textarea_name' => 'wpsl['. esc_attr( $args['key'] ).']') ); ?>
358
- </p>
359
-
360
- <?php
361
- }
362
-
363
- /**
364
- * Create a checkbox field.
365
- *
366
- * @since 2.0.0
367
- * @param array $args The checkbox name and label
368
- * @return void
369
- */
370
- public function checkbox_input( $args ) {
371
-
372
- $saved_value = $this->get_store_meta( $args['key'] );
373
- ?>
374
-
375
- <p>
376
- <label for="wpsl-<?php echo esc_attr( $args['key'] ); ?>"><?php echo esc_html( $args['data']['label'] ) . ': ' . $this->is_required_field( $args['data'] ); ?></label>
377
- <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" />
378
- </p>
379
-
380
- <?php
381
- }
382
-
383
- /**
384
- * Create a dropdown field.
385
- *
386
- * @since 2.0.0
387
- * @param array $args The dropdown name and label
388
- * @return void
389
- */
390
- public function dropdown_input( $args ) {
391
-
392
- // The hour dropdown requires a different structure with multiple dropdowns.
393
- if ( $args['key'] == 'hours' ) {
394
- $this->opening_hours();
395
- } else {
396
- $option_list = $args['data']['options'];
397
- $saved_value = $this->get_store_meta( $args['key'] );
398
- ?>
399
-
400
- <p>
401
- <label for="wpsl-<?php echo esc_attr( $args['key'] ); ?>"><?php echo esc_html( $args['data']['label'] ) . ': ' . $this->is_required_field( $args['data'] ); ?></label>
402
- <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" />
403
- <?php foreach ( $option_list as $key => $option ) { ?>
404
- <option value="<?php echo esc_attr( $key ); ?>" <?php if ( isset( $saved_value ) ) { selected( $saved_value, $key ); } ?>><?php echo esc_html( $option ); ?></option>
405
- <?php } ?>
406
- </select>
407
- </p>
408
-
409
- <?php
410
- }
411
- }
412
-
413
- /**
414
- * Create the openings hours table with the hours as dropdowns.
415
- *
416
- * @since 2.0.0
417
- * @param string $location The location were the opening hours are shown.
418
- * @return void
419
- */
420
- public function opening_hours( $location = 'store_page' ) {
421
-
422
- global $wpsl_settings, $wpsl_admin, $post;
423
-
424
- $name = ( $location == 'settings' ) ? 'wpsl_editor[dropdown]' : 'wpsl[hours]'; // the name of the input or select field
425
- $opening_days = wpsl_get_weekdays();
426
- $opening_hours = '';
427
- $hours = '';
428
-
429
- if ( $location == 'store_page' ) {
430
- $opening_hours = get_post_meta( $post->ID, 'wpsl_hours' );
431
- }
432
-
433
- // If we don't have any opening hours, we use the defaults.
434
- if ( !isset( $opening_hours[0]['monday'] ) ) {
435
- $opening_hours = $wpsl_settings['editor_hours']['dropdown'];
436
- } else {
437
- $opening_hours = $opening_hours[0];
438
- }
439
-
440
- // Find out whether we have a 12 or 24hr format.
441
- $hour_format = $this->find_hour_format( $opening_hours );
442
-
443
- if ( $hour_format == 24 ) {
444
- $hour_class = 'wpsl-twentyfour-format';
445
- } else {
446
- $hour_class = 'wpsl-twelve-format';
447
- }
448
-
449
- /*
450
- * Only include the 12 / 24hr dropdown switch if we are on store page,
451
- * otherwise just show the table with the opening hour dropdowns.
452
- */
453
- if ( $location == 'store_page' ) {
454
- ?>
455
- <p class="wpsl-hours-dropdown">
456
- <label for="wpsl-editor-hour-input"><?php _e( 'Hour format', 'wpsl' ); ?>:</label>
457
- <?php echo $wpsl_admin->settings_page->show_opening_hours_format( $hour_format ); ?>
458
- </p>
459
- <?php } ?>
460
-
461
- <table id="wpsl-store-hours" class="<?php echo $hour_class; ?>">
462
- <tr>
463
- <th><?php _e( 'Days', 'wpsl' ); ?></th>
464
- <th><?php _e( 'Opening Periods', 'wpsl' ); ?></th>
465
- <th></th>
466
- </tr>
467
- <?php
468
- foreach ( $opening_days as $index => $day ) {
469
- $i = 0;
470
- $hour_count = count( $opening_hours[$index] );
471
- ?>
472
- <tr>
473
- <td class="wpsl-opening-day"><?php echo esc_html( $day ); ?></td>
474
- <td id="wpsl-hours-<?php echo esc_attr( $index ); ?>" class="wpsl-opening-hours" data-day="<?php echo esc_attr( $index ); ?>">
475
- <?php
476
- if ( $hour_count > 0 ) {
477
- // Loop over the opening periods.
478
- while ( $i < $hour_count ) {
479
- if ( isset( $opening_hours[$index][$i] ) ) {
480
- $hours = explode( ',', $opening_hours[$index][$i] );
481
- } else {
482
- $hours = '';
483
- }
484
-
485
- // If we don't have two parts or one of them is empty, then we set the store to closed.
486
- if ( ( count( $hours ) == 2 ) && ( !empty( $hours[0] ) ) && ( !empty( $hours[1] ) ) ) {
487
- $args = array(
488
- 'day' => $index,
489
- 'name' => $name,
490
- 'hour_format' => $hour_format,
491
- 'hours' => $hours
492
- );
493
- ?>
494
- <div class="wpsl-current-period <?php if ( $i > 0 ) { echo 'wpsl-multiple-periods'; } ?>">
495
- <?php echo $this->opening_hours_dropdown( $args, 'open' ); ?>
496
- <span> - </span>
497
- <?php echo $this->opening_hours_dropdown( $args, 'close' ); ?>
498
- <div class="wpsl-icon-cancel-circled"></div>
499
- </div>
500
- <?php
501
- } else {
502
- $this->show_store_closed( $name, $index );
503
- }
504
-
505
- $i++;
506
- }
507
- } else {
508
- $this->show_store_closed( $name, $index );
509
- }
510
- ?>
511
- </td>
512
- <td>
513
- <div class="wpsl-add-period">
514
- <div class="wpsl-icon-plus-circled"></div>
515
- </div>
516
- </td>
517
- </tr>
518
- <?php
519
- }
520
- ?>
521
- </table>
522
- <?php
523
- }
524
-
525
- /**
526
- * Show the 'store closed' message.
527
- *
528
- * @since 2.0.0
529
- * @param string $name The name for the input field
530
- * @param string $day The day the store is closed
531
- * @return void
532
- */
533
- public function show_store_closed( $name, $day ) {
534
- echo '<p class="wpsl-store-closed">' . __( 'Closed', 'wpsl' ) . '<input type="hidden" name="' . esc_attr( $name ) . '[' . esc_attr( $day ) . ']" value="closed"></p>';
535
- }
536
-
537
- /**
538
- * Find out whether the opening hours are set in the 12 or 24hr format.
539
- *
540
- * We use this to determine the selected value for the dropdown in the store editor.
541
- * So a user can decide to change the opening hour format.
542
- *
543
- * @since 2.0.0
544
- * @param array $opening_hours The opening hours for the whole week
545
- * @return string The hour format used in the opening hours
546
- */
547
- public function find_hour_format( $opening_hours ) {
548
-
549
- $week_days = wpsl_get_weekdays();
550
-
551
- foreach ( $week_days as $key => $day ) {
552
- if ( isset( $opening_hours[$key][0] ) ) {
553
- $time = $opening_hours[$key][0];
554
-
555
- if ( ( strpos( $time, 'AM' ) !== false ) || ( strpos( $time, 'PM' ) !== false ) ) {
556
- return '12';
557
- } else {
558
- return '24';
559
- }
560
- }
561
- }
562
- }
563
-
564
- /**
565
- * Create the opening hours dropdown.
566
- *
567
- * @since 2.0.0
568
- * @param array $args The data to create the opening hours dropdown
569
- * @param string $period Either set to open or close
570
- * @return string $select The html for the dropdown
571
- */
572
- public function opening_hours_dropdown( $args, $period ) {
573
-
574
- $select_index = ( $period == 'open' ) ? 0 : 1;
575
- $selected_time = $args['hours'][$select_index];
576
- $select_name = $args['name'] . '[' . strtolower( $args['day'] ) . '_' . $period . ']';
577
- $open = strtotime( '12:00am' );
578
- $close = strtotime( '11:59pm' );
579
- $hour_interval = 900;
580
-
581
- if ( $args['hour_format'] == 12 ) {
582
- $format = 'g:i A';
583
- } else {
584
- $format = 'H:i';
585
- }
586
-
587
- $select = '<select class="wpsl-' . esc_attr( $period ) . '-hour" name="' . esc_attr( $select_name ) . '[]" autocomplete="off">';
588
-
589
- for ( $i = $open; $i <= $close; $i += $hour_interval ) {
590
-
591
- // If the selected time matches the current time then we set it to active.
592
- if ( $selected_time == date( $format, $i ) ) {
593
- $selected = 'selected="selected"';
594
- } else {
595
- $selected = '';
596
- }
597
-
598
- $select .= "<option value='" . date( $format, $i ) . "' $selected>" . date( $format, $i ) . "</option>";
599
- }
600
-
601
- $select .= '</select>';
602
-
603
- return $select;
604
- }
605
-
606
- /**
607
- * Get the store post meta.
608
- *
609
- * @since 2.0.0
610
- * @param string $key The name of the meta value
611
- * @return mixed|void $store_meta Meta value for the store field
612
- */
613
- public function get_store_meta( $key ) {
614
-
615
- global $post;
616
-
617
- $store_meta = get_post_meta( $post->ID, 'wpsl_' . $key, true );
618
-
619
- if ( $store_meta ) {
620
- return $store_meta;
621
- } else {
622
- return;
623
- }
624
- }
625
-
626
- /**
627
- * Save the custom post data.
628
- *
629
- * @since 2.0.0
630
- * @param integer $post_id store post ID
631
- * @return void
632
- */
633
- public function save_post( $post_id ) {
634
-
635
- global $wpsl_admin;
636
-
637
- if ( empty( $_POST['wpsl_meta_nonce'] ) || !wp_verify_nonce( $_POST['wpsl_meta_nonce'], 'save_store_meta' ) )
638
- return;
639
-
640
- if ( !isset( $_POST['post_type'] ) || 'wpsl_stores' !== $_POST['post_type'] )
641
- return;
642
-
643
- if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
644
- return;
645
-
646
- if ( is_int( wp_is_post_revision( $post_id ) ) )
647
- return;
648
-
649
- if ( !current_user_can( 'edit_post', $post_id ) )
650
- return;
651
-
652
- $this->store_data = $_POST['wpsl'];
653
-
654
- // Check if the hours are set through dropdowns.
655
- if ( isset( $this->store_data['hours'] ) && is_array( $this->store_data['hours'] ) && ( !empty( $this->store_data['hours'] ) ) ) {
656
- $this->store_data['hours'] = $this->format_opening_hours();
657
- }
658
-
659
- // Loop over the meta fields defined in the meta_box_fields and update the post meta data.
660
- foreach ( $this->meta_box_fields() as $tab => $meta_fields ) {
661
- foreach ( $meta_fields as $field_key => $field_data ) {
662
-
663
- // Either update or delete the post meta.
664
- if ( isset( $this->store_data[ $field_key ] ) && ( $this->store_data[ $field_key ] != "" ) ) {
665
- if ( isset( $field_data['type'] ) && $field_data['type'] ) {
666
- $field_type = $field_data['type'];
667
- } else {
668
- $field_type = '';
669
- }
670
-
671
- switch ( $field_type ) {
672
- case 'thumbnail':
673
- update_post_meta( $post_id, 'wpsl_' . $field_key, absint( $this->store_data[ $field_key ] ) );
674
- break;
675
- case 'checkbox':
676
- $checkbox_val = ( isset( $this->store_data[ $field_key ] ) ) ? 1 : 0;
677
- update_post_meta( $post_id, 'wpsl_' . $field_key, $checkbox_val );
678
- break;
679
- case 'wp_editor':
680
- case 'textarea':
681
- update_post_meta( $post_id, 'wpsl_' . $field_key, wp_kses_post( trim( stripslashes( $this->store_data[ $field_key ] ) ) ) );
682
- break;
683
- default:
684
- if ( is_array( $this->store_data[ $field_key ] ) ) {
685
- if ( wpsl_is_multi_array( $this->store_data[ $field_key ] ) ) {
686
- array_walk_recursive( $this->store_data[ $field_key ], 'wpsl_sanitize_multi_array' );
687
- update_post_meta( $post_id, 'wpsl_' . $field_key, $this->store_data[ $field_key ] );
688
- } else {
689
- update_post_meta( $post_id, 'wpsl_' . $field_key, array_map( 'sanitize_text_field', $this->store_data[ $field_key ] ) );
690
- }
691
- } else {
692
- update_post_meta( $post_id, 'wpsl_' . $field_key, sanitize_text_field( $this->store_data[ $field_key ] ) );
693
- }
694
- break;
695
- }
696
- } else {
697
- delete_post_meta( $post_id, 'wpsl_' . $field_key );
698
- }
699
- }
700
- }
701
-
702
- do_action( 'wpsl_save_post', $this->store_data );
703
-
704
- /*
705
- * If all the required fields contain data, then check if we need to
706
- * geocode the address and if we should delete the autoload transient.
707
- *
708
- * Otherwise show a notice for 'missing data' and set the post status to pending.
709
- */
710
- if ( !$this->check_missing_meta_data( $post_id ) ) {
711
- $wpsl_admin->geocode->check_geocode_data( $post_id, $this->store_data );
712
- $wpsl_admin->maybe_delete_autoload_transient( $post_id );
713
- } else {
714
- $wpsl_admin->notices->save( 'error', __( 'Failed to publish the store. Please fill in the required store details.', 'wpsl' ) );
715
- $this->set_post_pending( $post_id );
716
- }
717
- }
718
-
719
- /**
720
- * Loop through the opening hours and structure the data in a new array.
721
- *
722
- * @since 2.0.0
723
- * @return array $opening_hours The formatted opening hours
724
- */
725
- public function format_opening_hours() {
726
-
727
- $week_days = wpsl_get_weekdays();
728
-
729
- // Use the opening hours from the editor page or the add/edit store page.
730
- if ( isset( $_POST['wpsl_editor']['dropdown'] ) ) {
731
- $store_hours = $_POST['wpsl_editor']['dropdown'];
732
- } else if ( isset( $this->store_data['hours'] ) ) {
733
- $store_hours = $this->store_data['hours'];
734
- }
735
-
736
- foreach ( $week_days as $day => $value ) {
737
- $i = 0;
738
- $periods = array();
739
-
740
- if ( isset( $store_hours[$day . '_open'] ) && $store_hours[$day . '_open'] ) {
741
- foreach ( $store_hours[$day . '_open'] as $opening_hour ) {
742
- $hours = $this->validate_hour( $store_hours[$day.'_open'][$i] ) . ',' . $this->validate_hour( $store_hours[$day.'_close'][$i] );
743
- $periods[] = $hours;
744
- $i++;
745
- }
746
- }
747
-
748
- $opening_hours[$day] = $periods;
749
- }
750
-
751
- return $opening_hours;
752
- }
753
-
754
- /*
755
- * Validate the 12 or 24 hr time format.
756
- *
757
- * @since 2.0.0
758
- * @param string $hour The opening hour
759
- * @return boolean true if the $hour format is valid
760
- */
761
- public function validate_hour( $hour ) {
762
-
763
- global $wpsl_settings;
764
-
765
- /*
766
- * On the add/edit store we can always use the $wpsl_settings value.
767
- * But if validate_hour is called from the settings page then we
768
- * should use the $_POST value to make sure we have the correct value.
769
- */
770
- if ( isset( $_POST['wpsl_editor']['hour_format'] ) ) {
771
- $hour_format = ( $_POST['wpsl_editor']['hour_format'] == 12 ) ? 12 : 24;
772
- } else {
773
- $hour_format = $wpsl_settings['editor_hour_format'];
774
- }
775
-
776
- if ( $hour_format == 12 ) {
777
- $format = 'g:i A';
778
- } else {
779
- $format = 'H:i';
780
- }
781
-
782
- if ( date( $format, strtotime( $hour ) ) == $hour ) {
783
- return $hour;
784
- }
785
- }
786
-
787
- /**
788
- * Set the post status to pending instead of publish.
789
- *
790
- * @since 2.0.0
791
- * @param integer $post_id store post ID
792
- * @return void
793
- */
794
- public function set_post_pending( $post_id ) {
795
-
796
- global $wpdb;
797
-
798
- $wpdb->update( $wpdb->posts, array( 'post_status' => 'pending' ), array( 'ID' => $post_id ) );
799
-
800
- add_filter( 'redirect_post_location', array( $this, 'remove_message_arg' ) );
801
- }
802
-
803
- /**
804
- * Remove the message query arg.
805
- *
806
- * If one or more of the required fields are empty, we show a custom msg.
807
- * So no need for the normal post update messages arg.
808
- *
809
- * @since 2.0.0
810
- * @param string $location The destination url
811
- * @return void
812
- */
813
- public function remove_message_arg( $location ) {
814
- return remove_query_arg( 'message', $location );
815
- }
816
-
817
- /**
818
- * Make sure all the required post meta fields contain data.
819
- *
820
- * @since 2.0.0
821
- * @param integer $post_id store post ID
822
- * @return boolean
823
- */
824
- public function check_missing_meta_data( $post_id ) {
825
-
826
- foreach ( $this->meta_box_fields() as $tab => $meta_fields ) {
827
- foreach ( $meta_fields as $field_key => $field_data ) {
828
-
829
- if ( isset( $field_data['required'] ) && $field_data['required'] ) {
830
- $post_meta = get_post_meta( $post_id, 'wpsl_' . $field_key, true );
831
-
832
- if ( empty( $post_meta ) ) {
833
- return true;
834
- }
835
- }
836
- }
837
- }
838
- }
839
-
840
- /**
841
- * The html for the map preview in the sidebar.
842
- *
843
- * @since 2.0.0
844
- * @return void
845
- */
846
- public function map_preview() {
847
- ?>
848
- <div id="wpsl-gmap-wrap"></div>
849
- <p class="wpsl-submit-wrap">
850
- <a id="wpsl-lookup-location" class="button-primary" href="#wpsl-meta-nav"><?php _e( 'Preview Location', 'wpsl' ); ?></a>
851
- <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>
852
- <em class="wpsl-desc"><?php _e( 'You can drag the marker to adjust the exact location of the marker.', 'wpsl' ); ?></em>
853
- </p>
854
- <?php
855
- }
856
-
857
- /**
858
- * The html for the export details section in the sidebar.
859
- *
860
- * @since 2.2.15
861
- * @return void
862
- */
863
- public function export_data() {
864
-
865
- global $post;
866
-
867
- $link_url = wp_nonce_url( admin_url( 'post.php?'. $_SERVER['QUERY_STRING'] . '&wpsl_data_export=1' ), 'wpsl_export_' . $post->ID, 'wpsl_export_nonce' );
868
-
869
- ?>
870
- <p class="wpsl-submit-wrap">
871
- <a id="wpsl-export-data" class="button-primary" href="<?php echo esc_url( $link_url ); ?>"><?php _e( 'Export Location Data', 'wpsl' ); ?></a>
872
- </p>
873
- <?php
874
- }
875
-
876
- /**
877
- * Store update messages.
878
- *
879
- * @since 2.0.0
880
- * @param array $messages Existing post update messages.
881
- * @return array $messages Amended post update messages with new CPT update messages.
882
- */
883
- function store_update_messages( $messages ) {
884
-
885
- $post = get_post();
886
- $post_type = get_post_type( $post );
887
- $post_type_object = get_post_type_object( $post_type );
888
-
889
- $messages['wpsl_stores'] = array(
890
- 0 => '', // Unused. Messages start at index 1.
891
- 1 => __( 'Store updated.', 'wpsl' ),
892
- 2 => __( 'Custom field updated.', 'wpsl' ),
893
- 3 => __( 'Custom field deleted.', 'wpsl' ),
894
- 4 => __( 'Store updated.', 'wpsl' ),
895
- 5 => isset( $_GET['revision'] ) ? sprintf( __( 'Store restored to revision from %s', 'wpsl' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
896
- 6 => __( 'Store published.', 'wpsl' ),
897
- 7 => __( 'Store saved.', 'wpsl' ),
898
- 8 => __( 'Store submitted.', 'wpsl' ),
899
- 9 => sprintf(
900
- __( 'Store scheduled for: <strong>%1$s</strong>.', 'wpsl' ),
901
- date_i18n( __( 'M j, Y @ G:i', 'wpsl' ), strtotime( $post->post_date ) )
902
- ),
903
- 10 => __( 'Store draft updated.', 'wpsl' )
904
- );
905
-
906
- if ( ( 'wpsl_stores' == $post_type ) && ( $post_type_object->publicly_queryable ) ) {
907
- $permalink = get_permalink( $post->ID );
908
-
909
- $view_link = sprintf( ' <a href="%s">%s</a>', esc_url( $permalink ), __( 'View store', 'wpsl' ) );
910
- $messages[ $post_type ][1] .= $view_link;
911
- $messages[ $post_type ][6] .= $view_link;
912
- $messages[ $post_type ][9] .= $view_link;
913
-
914
- $preview_permalink = add_query_arg( 'preview', 'true', $permalink );
915
- $preview_link = sprintf( ' <a target="_blank" href="%s">%s</a>', esc_url( $preview_permalink ), __( 'Preview store', 'wpsl' ) );
916
- $messages[ $post_type ][8] .= $preview_link;
917
- $messages[ $post_type ][10] .= $preview_link;
918
- }
919
-
920
- return $messages;
921
- }
922
-
923
- }
924
  }
1
+ <?php
2
+ /**
3
+ * Handle the metaboxes
4
+ *
5
+ * @author Tijmen Smit
6
+ * @since 2.0.0
7
+ */
8
+
9
+ if ( !defined( 'ABSPATH' ) ) exit;
10
+
11
+ if ( !class_exists( 'WPSL_Metaboxes' ) ) {
12
+
13
+ /**
14
+ * Handle the meta boxes
15
+ *
16
+ * @since 2.0.0
17
+ */
18
+ class WPSL_Metaboxes {
19
+
20
+ public function __construct() {
21
+ add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
22
+ add_action( 'save_post', array( $this, 'save_post' ) );
23
+ add_action( 'post_updated_messages', array( $this, 'store_update_messages' ) );
24
+ }
25
+
26
+ /**
27
+ * Add the meta boxes.
28
+ *
29
+ * @since 2.0.0
30
+ * @return void
31
+ */
32
+ public function add_meta_boxes() {
33
+
34
+ global $pagenow;
35
+
36
+ add_meta_box( 'wpsl-store-details', __( 'Store Details', 'wpsl' ), array( $this, 'create_meta_fields' ), 'wpsl_stores', 'normal', 'high' );
37
+ add_meta_box( 'wpsl-map-preview', __( 'Store Map', 'wpsl' ), array( $this, 'map_preview' ), 'wpsl_stores', 'side' );
38
+
39
+ $enable_option = apply_filters( 'wpsl_enable_export_option', true );
40
+
41
+ if ( $enable_option && $pagenow == 'post.php' ) {
42
+ add_meta_box( 'wpsl-data-export', __( 'Export', 'wpsl' ), array( $this, 'export_data' ), 'wpsl_stores', 'side', 'low' );
43
+ }
44
+ }
45
+
46
+ /**
47
+ * The store locator meta box fields.
48
+ *
49
+ * @since 2.0.0
50
+ * @return array $meta_fields The meta box fields used for the store details
51
+ */
52
+ public function meta_box_fields() {
53
+
54
+ global $wpsl_settings;
55
+
56
+ $meta_fields = array(
57
+ __( 'Location', 'wpsl' ) => array(
58
+ 'address' => array(
59
+ 'label' => __( 'Address', 'wpsl' ),
60
+ 'required' => true
61
+ ),
62
+ 'address2' => array(
63
+ 'label' => __( 'Address 2', 'wpsl' )
64
+ ),
65
+ 'city' => array(
66
+ 'label' => __( 'City', 'wpsl' ),
67
+ 'required' => true
68
+ ),
69
+ 'state' => array(
70
+ 'label' => __( 'State', 'wpsl' )
71
+ ),
72
+ 'zip' => array(
73
+ 'label' => __( 'Zip Code', 'wpsl' )
74
+ ),
75
+ 'country' => array(
76
+ 'label' => __( 'Country', 'wpsl' ),
77
+ 'required' => true
78
+ ),
79
+ 'country_iso' => array(
80
+ 'type' => 'hidden'
81
+ ),
82
+ 'lat' => array(
83
+ 'label' => __( 'Latitude', 'wpsl' )
84
+ ),
85
+ 'lng' => array(
86
+ 'label' => __( 'Longitude', 'wpsl' )
87
+ )
88
+ ),
89
+ __( 'Opening Hours', 'wpsl' ) => array(
90
+ 'hours' => array(
91
+ 'label' => __( 'Hours', 'wpsl' ),
92
+ '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
93
+ )
94
+ ),
95
+ __( 'Additional Information', 'wpsl' ) => array(
96
+ 'phone' => array(
97
+ 'label' => __( 'Tel', 'wpsl' )
98
+ ),
99
+ 'fax' => array(
100
+ 'label' => __( 'Fax', 'wpsl' )
101
+ ),
102
+ 'email' => array(
103
+ 'label' => __( 'Email', 'wpsl' )
104
+ ),
105
+ 'url' => array(
106
+ 'label' => __( 'Url', 'wpsl' )
107
+ )
108
+ )
109
+ );
110
+
111
+ return apply_filters( 'wpsl_meta_box_fields', $meta_fields );
112
+ }
113
+
114
+ /**
115
+ * Create the store locator metabox input fields.
116
+ *
117
+ * @since 2.0.0
118
+ * @return void
119
+ */
120
+ function create_meta_fields() {
121
+
122
+ global $wpsl_settings, $wp_version;
123
+
124
+ $i = 0;
125
+ $j = 0;
126
+ $tab_items = '';
127
+
128
+ wp_nonce_field( 'save_store_meta', 'wpsl_meta_nonce' );
129
+ ?>
130
+
131
+ <div class="wpsl-store-meta <?php if ( floatval( $wp_version ) < 3.8 ) { echo 'wpsl-pre-38'; } // Fix CSS issue with < 3.8 versions ?>">
132
+ <?php
133
+
134
+ // Create the tab navigation for the meta boxes.
135
+ foreach ( $this->meta_box_fields() as $tab => $meta_fields ) {
136
+ $active_class = ( $i == 0 ) ? ' wpsl-active' : '';
137
+
138
+ if ( $wpsl_settings['hide_hours'] && $tab == __( 'Opening Hours', 'wpsl' ) ) {
139
+ continue;
140
+ } else {
141
+ $tab_items .= $this->meta_field_nav( $tab, $active_class );
142
+ }
143
+
144
+ $i++;
145
+ }
146
+
147
+ echo '<ul id="wpsl-meta-nav">' . $tab_items . '</ul>';
148
+
149
+ // Create the input fields for the meta boxes.
150
+ foreach ( $this->meta_box_fields() as $tab => $meta_fields ) {
151
+ $active_class = ( $j == 0 ) ? ' wpsl-active' : '';
152
+
153
+ if ( $wpsl_settings['hide_hours'] && $tab == __( 'Opening Hours', 'wpsl' ) ) {
154
+ continue;
155
+ } else {
156
+ echo '<div class="wpsl-tab wpsl-' . esc_attr( strtolower( str_replace( ' ', '-', $tab ) ) ) . $active_class . '">';
157
+
158
+ foreach ( $meta_fields as $field_key => $field_data ) {
159
+
160
+ // If no specific field type is set, we set it to text.
161
+ $field_type = ( empty( $field_data['type'] ) ) ? 'text' : $field_data['type'];
162
+ $args = array(
163
+ 'key' => $field_key,
164
+ 'data' => $field_data
165
+ );
166
+
167
+ // Check for a class method, otherwise enable a plugin hook.
168
+ if ( method_exists( $this, $field_type . '_input' ) ) {
169
+ call_user_func( array( $this, $field_type . '_input' ), $args );
170
+ } else {
171
+ do_action( 'wpsl_metabox_' . $field_type . '_input', $args );
172
+ }
173
+ }
174
+
175
+ echo '</div>';
176
+ }
177
+
178
+ $j++;
179
+ }
180
+ ?>
181
+ </div>
182
+ <?php
183
+ }
184
+
185
+ /**
186
+ * Create the li elements that are used in the tabs above the store meta fields.
187
+ *
188
+ * @since 2.0.0
189
+ * @param string $tab The name of the tab
190
+ * @param string $active_class Either the class name or empty
191
+ * @return string $nav_item The HTML for the nav list
192
+ */
193
+ public function meta_field_nav( $tab, $active_class ) {
194
+
195
+ $tab_lower = strtolower( str_replace( ' ', '-', $tab ) );
196
+ $nav_item = '<li class="wpsl-' . esc_attr( $tab_lower ) . '-tab ' . $active_class . '"><a href="#wpsl-' . esc_attr( $tab_lower ) . '">' . esc_html( $tab ) . '</a></li>';
197
+
198
+ return $nav_item;
199
+ }
200
+
201
+ /**
202
+ * Set the CSS class that tells JS it's an required input field.
203
+ *
204
+ * @since 2.0.0
205
+ * @param array $args The css classes
206
+ * @param string $single Whether to return just the class name, or also include the class=""
207
+ * @return string|void $response The required CSS class or nothing
208
+ */
209
+ public function set_required_class( $args, $single = false ) {
210
+
211
+ if ( isset( $args['required'] ) && ( $args['required'] ) ) {
212
+ if ( !$single ) {
213
+ $response = 'class="wpsl-required"';
214
+ } else {
215
+ $response = 'wpsl-required';
216
+ }
217
+
218
+ return $response;
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Check if the current field is required.
224
+ *
225
+ * @since 2.0.0
226
+ * @param array $args The CSS classes
227
+ * @return string|void The HTML for the required element or nothing
228
+ */
229
+ public function is_required_field( $args ) {
230
+
231
+ if ( isset( $args['required'] ) && ( $args['required'] ) ) {
232
+ $response = '<span class="wpsl-star"> *</span>';
233
+
234
+ return $response;
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Get the prefilled field data.
240
+ *
241
+ * @since 2.0.0
242
+ * @param string $field_name The name of the field to get the data for
243
+ * @return string $field_data The field data
244
+ */
245
+ public function get_prefilled_field_data( $field_name ) {
246
+
247
+ global $wpsl_settings, $pagenow;
248
+
249
+ $field_data = '';
250
+
251
+ // Prefilled values are only used for new pages, not when a user edits an existing page.
252
+ if ( $pagenow == 'post.php' && isset( $_GET['action'] ) && $_GET['action'] == 'edit' ) {
253
+ return;
254
+ }
255
+
256
+ $prefilled_fields = array(
257
+ 'country',
258
+ 'hours'
259
+ );
260
+
261
+ if ( in_array( $field_name, $prefilled_fields ) ) {
262
+ $field_data = $wpsl_settings['editor_' . $field_name];
263
+ }
264
+
265
+ return $field_data;
266
+ }
267
+
268
+ /**
269
+ * Create a text input field.
270
+ *
271
+ * @since 2.0.0
272
+ * @param array $args The input name and label
273
+ * @return void
274
+ */
275
+ public function text_input( $args ) {
276
+
277
+ $saved_value = $this->get_store_meta( $args['key'] );
278
+
279
+ // If there is no existing meta value, check if a prefilled value exists for the input field.
280
+ if ( !$saved_value ) {
281
+ $saved_value = $this->get_prefilled_field_data( $args['key'] );
282
+ }
283
+ ?>
284
+
285
+ <p>
286
+ <label for="wpsl-<?php echo esc_attr( $args['key'] ); ?>"><?php echo esc_html( $args['data']['label'] ) . ': ' . $this->is_required_field( $args['data'] ); ?></label>
287
+ <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 ); ?>" />
288
+ </p>
289
+
290
+ <?php
291
+ }
292
+
293
+ /**
294
+ * Create a hidden input field.
295
+ *
296
+ * @since 2.0.0
297
+ * @param array $args The name of the meta value
298
+ * @return void
299
+ */
300
+ public function hidden_input( $args ) {
301
+
302
+ $saved_value = $this->get_store_meta( $args['key'] );
303
+ ?>
304
+
305
+ <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 ); ?>" />
306
+
307
+ <?php
308
+ }
309
+
310
+ /**
311
+ * Create a textarea field.
312
+ *
313
+ * @since 2.0.0
314
+ * @param array $args The textarea name and label
315
+ * @return void
316
+ */
317
+ public function textarea_input( $args ) {
318
+
319
+ $saved_value = $this->get_store_meta( $args['key'] );
320
+
321
+ if ( $args['key'] == 'hours' && gettype( $saved_value ) !== 'string' ) {
322
+ $saved_value = '';
323
+ }
324
+
325
+ // If there is no existing meta value, check if a prefilled value exists for the textarea.
326
+ if ( !$saved_value ) {
327
+ $prefilled_value = $this->get_prefilled_field_data( $args['key'] );
328
+
329
+ if ( isset( $prefilled_value['textarea'] ) ) {
330
+ $saved_value = $prefilled_value['textarea'];
331
+ }
332
+ }
333
+ ?>
334
+
335
+ <p>
336
+ <label for="wpsl-<?php echo esc_attr( $args['key'] ); ?>"><?php echo esc_html( $args['data']['label'] ) . ': ' . $this->is_required_field( $args['data'] ); ?></label>
337
+ <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>
338
+ </p>
339
+
340
+ <?php
341
+ }
342
+
343
+ /**
344
+ * Create a wp editor field.
345
+ *
346
+ * @since 2.1.1
347
+ * @param array $args The wp editor name and label
348
+ * @return void
349
+ */
350
+ public function wp_editor_input( $args ) {
351
+
352
+ $saved_value = $this->get_store_meta( $args['key'] );
353
+ ?>
354
+
355
+ <p>
356
+ <label for="wpsl-<?php echo esc_attr( $args['key'] ); ?>"><?php echo esc_html( $args['data']['label'] ) . ': ' . $this->is_required_field( $args['data'] ); ?></label>
357
+ <?php wp_editor( $saved_value, 'wpsleditor_' . wpsl_random_chars(), $settings = array('textarea_name' => 'wpsl['. esc_attr( $args['key'] ).']') ); ?>
358
+ </p>
359
+
360
+ <?php
361
+ }
362
+
363
+ /**
364
+ * Create a checkbox field.
365
+ *
366
+ * @since 2.0.0
367
+ * @param array $args The checkbox name and label
368
+ * @return void
369
+ */
370
+ public function checkbox_input( $args ) {
371
+
372
+ $saved_value = $this->get_store_meta( $args['key'] );
373
+ ?>
374
+
375
+ <p>
376
+ <label for="wpsl-<?php echo esc_attr( $args['key'] ); ?>"><?php echo esc_html( $args['data']['label'] ) . ': ' . $this->is_required_field( $args['data'] ); ?></label>
377
+ <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" />
378
+ </p>
379
+
380
+ <?php
381
+ }
382
+
383
+ /**
384
+ * Create a dropdown field.
385
+ *
386
+ * @since 2.0.0
387
+ * @param array $args The dropdown name and label
388
+ * @return void
389
+ */
390
+ public function dropdown_input( $args ) {
391
+
392
+ // The hour dropdown requires a different structure with multiple dropdowns.
393
+ if ( $args['key'] == 'hours' ) {
394
+ $this->opening_hours();
395
+ } else {
396
+ $option_list = $args['data']['options'];
397
+ $saved_value = $this->get_store_meta( $args['key'] );
398
+ ?>
399
+
400
+ <p>
401
+ <label for="wpsl-<?php echo esc_attr( $args['key'] ); ?>"><?php echo esc_html( $args['data']['label'] ) . ': ' . $this->is_required_field( $args['data'] ); ?></label>
402
+ <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" />
403
+ <?php foreach ( $option_list as $key => $option ) { ?>
404
+ <option value="<?php echo esc_attr( $key ); ?>" <?php if ( isset( $saved_value ) ) { selected( $saved_value, $key ); } ?>><?php echo esc_html( $option ); ?></option>
405
+ <?php } ?>
406
+ </select>
407
+ </p>
408
+
409
+ <?php
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Create the openings hours table with the hours as dropdowns.
415
+ *
416
+ * @since 2.0.0
417
+ * @param string $location The location were the opening hours are shown.
418
+ * @return void
419
+ */
420
+ public function opening_hours( $location = 'store_page' ) {
421
+
422
+ global $wpsl_settings, $wpsl_admin, $post;
423
+
424
+ $name = ( $location == 'settings' ) ? 'wpsl_editor[dropdown]' : 'wpsl[hours]'; // the name of the input or select field
425
+ $opening_days = wpsl_get_weekdays();
426
+ $opening_hours = '';
427
+ $hours = '';
428
+
429
+ if ( $location == 'store_page' ) {
430
+ $opening_hours = get_post_meta( $post->ID, 'wpsl_hours' );
431
+ }
432
+
433
+ // If we don't have any opening hours, we use the defaults.
434
+ if ( !isset( $opening_hours[0]['monday'] ) ) {
435
+ $opening_hours = $wpsl_settings['editor_hours']['dropdown'];
436
+ } else {
437
+ $opening_hours = $opening_hours[0];
438
+ }
439
+
440
+ // Find out whether we have a 12 or 24hr format.
441
+ $hour_format = $this->find_hour_format( $opening_hours );
442
+
443
+ if ( $hour_format == 24 ) {
444
+ $hour_class = 'wpsl-twentyfour-format';
445
+ } else {
446
+ $hour_class = 'wpsl-twelve-format';
447
+ }
448
+
449
+ /*
450
+ * Only include the 12 / 24hr dropdown switch if we are on store page,
451
+ * otherwise just show the table with the opening hour dropdowns.
452
+ */
453
+ if ( $location == 'store_page' ) {
454
+ ?>
455
+ <p class="wpsl-hours-dropdown">
456
+ <label for="wpsl-editor-hour-input"><?php _e( 'Hour format', 'wpsl' ); ?>:</label>
457
+ <?php echo $wpsl_admin->settings_page->show_opening_hours_format( $hour_format ); ?>
458
+ </p>
459
+ <?php } ?>
460
+
461
+ <table id="wpsl-store-hours" class="<?php echo $hour_class; ?>">
462
+ <tr>
463
+ <th><?php _e( 'Days', 'wpsl' ); ?></th>
464
+ <th><?php _e( 'Opening Periods', 'wpsl' ); ?></th>
465
+ <th></th>
466
+ </tr>
467
+ <?php
468
+ foreach ( $opening_days as $index => $day ) {
469
+ $i = 0;
470
+ $hour_count = count( $opening_hours[$index] );
471
+ ?>
472
+ <tr>
473
+ <td class="wpsl-opening-day"><?php echo esc_html( $day ); ?></td>
474
+ <td id="wpsl-hours-<?php echo esc_attr( $index ); ?>" class="wpsl-opening-hours" data-day="<?php echo esc_attr( $index ); ?>">
475
+ <?php
476
+ if ( $hour_count > 0 ) {
477
+ // Loop over the opening periods.
478
+ while ( $i < $hour_count ) {
479
+ if ( isset( $opening_hours[$index][$i] ) ) {
480
+ $hours = explode( ',', $opening_hours[$index][$i] );
481
+ } else {
482
+ $hours = '';
483
+ }
484
+
485
+ // If we don't have two parts or one of them is empty, then we set the store to closed.
486
+ if ( ( count( $hours ) == 2 ) && ( !empty( $hours[0] ) ) && ( !empty( $hours[1] ) ) ) {
487
+ $args = array(
488
+ 'day' => $index,
489
+ 'name' => $name,
490
+ 'hour_format' => $hour_format,
491
+ 'hours' => $hours
492
+ );
493
+ ?>
494
+ <div class="wpsl-current-period <?php if ( $i > 0 ) { echo 'wpsl-multiple-periods'; } ?>">
495
+ <?php echo $this->opening_hours_dropdown( $args, 'open' ); ?>
496
+ <span> - </span>
497
+ <?php echo $this->opening_hours_dropdown( $args, 'close' ); ?>
498
+ <div class="wpsl-icon-cancel-circled"></div>
499
+ </div>
500
+ <?php
501
+ } else {
502
+ $this->show_store_closed( $name, $index );
503
+ }
504
+
505
+ $i++;
506
+ }
507
+ } else {
508
+ $this->show_store_closed( $name, $index );
509
+ }
510
+ ?>
511
+ </td>
512
+ <td>
513
+ <div class="wpsl-add-period">
514
+ <div class="wpsl-icon-plus-circled"></div>
515
+ </div>
516
+ </td>
517
+ </tr>
518
+ <?php
519
+ }
520
+ ?>
521
+ </table>
522
+ <?php
523
+ }
524
+
525
+ /**
526
+ * Show the 'store closed' message.
527
+ *
528
+ * @since 2.0.0
529
+ * @param string $name The name for the input field
530
+ * @param string $day The day the store is closed
531
+ * @return void
532
+ */
533
+ public function show_store_closed( $name, $day ) {
534
+ echo '<p class="wpsl-store-closed">' . __( 'Closed', 'wpsl' ) . '<input type="hidden" name="' . esc_attr( $name ) . '[' . esc_attr( $day ) . ']" value="closed"></p>';
535
+ }
536
+
537
+ /**
538
+ * Find out whether the opening hours are set in the 12 or 24hr format.
539
+ *
540
+ * We use this to determine the selected value for the dropdown in the store editor.
541
+ * So a user can decide to change the opening hour format.
542
+ *
543
+ * @since 2.0.0
544
+ * @param array $opening_hours The opening hours for the whole week
545
+ * @return string The hour format used in the opening hours
546
+ */
547
+ public function find_hour_format( $opening_hours ) {
548
+
549
+ $week_days = wpsl_get_weekdays();
550
+
551
+ foreach ( $week_days as $key => $day ) {
552
+ if ( isset( $opening_hours[$key][0] ) ) {
553
+ $time = $opening_hours[$key][0];
554
+
555
+ if ( ( strpos( $time, 'AM' ) !== false ) || ( strpos( $time, 'PM' ) !== false ) ) {
556
+ return '12';
557
+ } else {
558
+ return '24';
559
+ }
560
+ }
561
+ }
562
+ }
563
+
564
+ /**
565
+ * Create the opening hours dropdown.
566
+ *
567
+ * @since 2.0.0
568
+ * @param array $args The data to create the opening hours dropdown
569
+ * @param string $period Either set to open or close
570
+ * @return string $select The html for the dropdown
571
+ */
572
+ public function opening_hours_dropdown( $args, $period ) {
573
+
574
+ $select_index = ( $period == 'open' ) ? 0 : 1;
575
+ $selected_time = $args['hours'][$select_index];
576
+ $select_name = $args['name'] . '[' . strtolower( $args['day'] ) . '_' . $period . ']';
577
+ $open = strtotime( '12:00am' );
578
+ $close = strtotime( '11:59pm' );
579
+ $hour_interval = 900;
580
+
581
+ if ( $args['hour_format'] == 12 ) {
582
+ $format = 'g:i A';
583
+ } else {
584
+ $format = 'H:i';
585
+ }
586
+
587
+ $select = '<select class="wpsl-' . esc_attr( $period ) . '-hour" name="' . esc_attr( $select_name ) . '[]" autocomplete="off">';
588
+
589
+ for ( $i = $open; $i <= $close; $i += $hour_interval ) {
590
+
591
+ // If the selected time matches the current time then we set it to active.
592
+ if ( $selected_time == date( $format, $i ) ) {
593
+ $selected = 'selected="selected"';
594
+ } else {
595
+ $selected = '';
596
+ }
597
+
598
+ $select .= "<option value='" . date( $format, $i ) . "' $selected>" . date( $format, $i ) . "</option>";
599
+ }
600
+
601
+ $select .= '</select>';
602
+
603
+ return $select;
604
+ }
605
+
606
+ /**
607
+ * Get the store post meta.
608
+ *
609
+ * @since 2.0.0
610
+ * @param string $key The name of the meta value
611
+ * @return mixed|void $store_meta Meta value for the store field
612
+ */
613
+ public function get_store_meta( $key ) {
614
+
615
+ global $post;
616
+
617
+ $store_meta = get_post_meta( $post->ID, 'wpsl_' . $key, true );
618
+
619
+ if ( $store_meta ) {
620
+ return $store_meta;
621
+ } else {
622
+ return;
623
+ }
624
+ }
625
+
626
+ /**
627
+ * Save the custom post data.
628
+ *
629
+ * @since 2.0.0
630
+ * @param integer $post_id store post ID
631
+ * @return void
632
+ */
633
+ public function save_post( $post_id ) {
634
+
635
+ global $wpsl_admin;
636
+
637
+ if ( empty( $_POST['wpsl_meta_nonce'] ) || !wp_verify_nonce( $_POST['wpsl_meta_nonce'], 'save_store_meta' ) )
638
+ return;
639
+
640
+ if ( !isset( $_POST['post_type'] ) || 'wpsl_stores' !== $_POST['post_type'] )
641
+ return;
642
+
643
+ if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
644
+ return;
645
+
646
+ if ( is_int( wp_is_post_revision( $post_id ) ) )
647
+ return;
648
+
649
+ if ( !current_user_can( 'edit_post', $post_id ) )
650
+ return;
651
+
652
+ $this->store_data = $_POST['wpsl'];
653
+
654
+ // Check if the hours are set through dropdowns.
655
+ if ( isset( $this->store_data['hours'] ) && is_array( $this->store_data['hours'] ) && ( !empty( $this->store_data['hours'] ) ) ) {
656
+ $this->store_data['hours'] = $this->format_opening_hours();
657
+ }
658
+
659
+ // Loop over the meta fields defined in the meta_box_fields and update the post meta data.
660
+ foreach ( $this->meta_box_fields() as $tab => $meta_fields ) {
661
+ foreach ( $meta_fields as $field_key => $field_data ) {
662
+
663
+ // Either update or delete the post meta.
664
+ if ( isset( $this->store_data[ $field_key ] ) && ( $this->store_data[ $field_key ] != "" ) ) {
665
+ if ( isset( $field_data['type'] ) && $field_data['type'] ) {
666
+ $field_type = $field_data['type'];
667
+ } else {
668
+ $field_type = '';
669
+ }
670
+
671
+ switch ( $field_type ) {
672
+ case 'thumbnail':
673
+ update_post_meta( $post_id, 'wpsl_' . $field_key, absint( $this->store_data[ $field_key ] ) );
674
+ break;
675
+ case 'checkbox':
676
+ $checkbox_val = ( isset( $this->store_data[ $field_key ] ) ) ? 1 : 0;
677
+ update_post_meta( $post_id, 'wpsl_' . $field_key, $checkbox_val );
678
+ break;
679
+ case 'wp_editor':
680
+ case 'textarea':
681
+ update_post_meta( $post_id, 'wpsl_' . $field_key, wp_kses_post( trim( stripslashes( $this->store_data[ $field_key ] ) ) ) );
682
+ break;
683
+ default:
684
+ if ( is_array( $this->store_data[ $field_key ] ) ) {
685
+ if ( wpsl_is_multi_array( $this->store_data[ $field_key ] ) ) {
686
+ array_walk_recursive( $this->store_data[ $field_key ], 'wpsl_sanitize_multi_array' );
687
+ update_post_meta( $post_id, 'wpsl_' . $field_key, $this->store_data[ $field_key ] );
688
+ } else {
689
+ update_post_meta( $post_id, 'wpsl_' . $field_key, array_map( 'sanitize_text_field', $this->store_data[ $field_key ] ) );
690
+ }
691
+ } else {
692
+ update_post_meta( $post_id, 'wpsl_' . $field_key, sanitize_text_field( $this->store_data[ $field_key ] ) );
693
+ }
694
+ break;
695
+ }
696
+ } else {
697
+ delete_post_meta( $post_id, 'wpsl_' . $field_key );
698
+ }
699
+ }
700
+ }
701
+
702
+ do_action( 'wpsl_save_post', $this->store_data );
703
+
704
+ /*
705
+ * If all the required fields contain data, then check if we need to
706
+ * geocode the address and if we should delete the autoload transient.
707
+ *
708
+ * Otherwise show a notice for 'missing data' and set the post status to pending.
709
+ */
710
+ if ( !$this->check_missing_meta_data( $post_id ) ) {
711
+ $wpsl_admin->geocode->check_geocode_data( $post_id, $this->store_data );
712
+ $wpsl_admin->maybe_delete_autoload_transient( $post_id );
713
+ } else {
714
+ $wpsl_admin->notices->save( 'error', __( 'Failed to publish the store. Please fill in the required store details.', 'wpsl' ) );
715
+ $this->set_post_pending( $post_id );
716
+ }
717
+ }
718
+
719
+ /**
720
+ * Loop through the opening hours and structure the data in a new array.
721
+ *
722
+ * @since 2.0.0
723
+ * @return array $opening_hours The formatted opening hours
724
+ */
725
+ public function format_opening_hours() {
726
+
727
+ $week_days = wpsl_get_weekdays();
728
+
729
+ // Use the opening hours from the editor page or the add/edit store page.
730
+ if ( isset( $_POST['wpsl_editor']['dropdown'] ) ) {
731
+ $store_hours = $_POST['wpsl_editor']['dropdown'];
732
+ } else if ( isset( $this->store_data['hours'] ) ) {
733
+ $store_hours = $this->store_data['hours'];
734
+ }
735
+
736
+ foreach ( $week_days as $day => $value ) {
737
+ $i = 0;
738
+ $periods = array();
739
+
740
+ if ( isset( $store_hours[$day . '_open'] ) && $store_hours[$day . '_open'] ) {
741
+ foreach ( $store_hours[$day . '_open'] as $opening_hour ) {
742
+ $hours = $this->validate_hour( $store_hours[$day.'_open'][$i] ) . ',' . $this->validate_hour( $store_hours[$day.'_close'][$i] );
743
+ $periods[] = $hours;
744
+ $i++;
745
+ }
746
+ }
747
+
748
+ $opening_hours[$day] = $periods;
749
+ }
750
+
751
+ return $opening_hours;
752
+ }
753
+
754
+ /*
755
+ * Validate the 12 or 24 hr time format.
756
+ *
757
+ * @since 2.0.0
758
+ * @param string $hour The opening hour
759
+ * @return boolean true if the $hour format is valid
760
+ */
761
+ public function validate_hour( $hour ) {
762
+
763
+ global $wpsl_settings;
764
+
765
+ /*
766
+ * On the add/edit store we can always use the $wpsl_settings value.
767
+ * But if validate_hour is called from the settings page then we
768
+ * should use the $_POST value to make sure we have the correct value.
769
+ */
770
+ if ( isset( $_POST['wpsl_editor']['hour_format'] ) ) {
771
+ $hour_format = ( $_POST['wpsl_editor']['hour_format'] == 12 ) ? 12 : 24;
772
+ } else {
773
+ $hour_format = $wpsl_settings['editor_hour_format'];
774
+ }
775
+
776
+ if ( $hour_format == 12 ) {
777
+ $format = 'g:i A';
778
+ } else {
779
+ $format = 'H:i';
780
+ }
781
+
782
+ if ( date( $format, strtotime( $hour ) ) == $hour ) {
783
+ return $hour;
784
+ }
785
+ }
786
+
787
+ /**
788
+ * Set the post status to pending instead of publish.
789
+ *
790
+ * @since 2.0.0
791
+ * @param integer $post_id store post ID
792
+ * @return void
793
+ */
794
+ public function set_post_pending( $post_id ) {
795
+
796
+ global $wpdb;
797
+
798
+ $wpdb->update( $wpdb->posts, array( 'post_status' => 'pending' ), array( 'ID' => $post_id ) );
799
+
800
+ add_filter( 'redirect_post_location', array( $this, 'remove_message_arg' ) );
801
+ }
802
+
803
+ /**
804
+ * Remove the message query arg.
805
+ *
806
+ * If one or more of the required fields are empty, we show a custom msg.
807
+ * So no need for the normal post update messages arg.
808
+ *
809
+ * @since 2.0.0
810
+ * @param string $location The destination url
811
+ * @return void
812
+ */
813
+ public function remove_message_arg( $location ) {
814
+ return remove_query_arg( 'message', $location );
815
+ }
816
+
817
+ /**
818
+ * Make sure all the required post meta fields contain data.
819
+ *
820
+ * @since 2.0.0
821
+ * @param integer $post_id store post ID
822
+ * @return boolean
823
+ */
824
+ public function check_missing_meta_data( $post_id ) {
825
+
826
+ foreach ( $this->meta_box_fields() as $tab => $meta_fields ) {
827
+ foreach ( $meta_fields as $field_key => $field_data ) {
828
+
829
+ if ( isset( $field_data['required'] ) && $field_data['required'] ) {
830
+ $post_meta = get_post_meta( $post_id, 'wpsl_' . $field_key, true );
831
+
832
+ if ( empty( $post_meta ) ) {
833
+ return true;
834
+ }
835
+ }
836
+ }
837
+ }
838
+ }
839
+
840
+ /**
841
+ * The html for the map preview in the sidebar.
842
+ *
843
+ * @since 2.0.0
844
+ * @return void
845
+ */
846
+ public function map_preview() {
847
+ ?>
848
+ <div id="wpsl-gmap-wrap"></div>
849
+ <p class="wpsl-submit-wrap">
850
+ <a id="wpsl-lookup-location" class="button-primary" href="#wpsl-meta-nav"><?php _e( 'Preview Location', 'wpsl' ); ?></a>
851
+ <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>
852
+ <em class="wpsl-desc"><?php _e( 'You can drag the marker to adjust the exact location of the marker.', 'wpsl' ); ?></em>
853
+ </p>
854
+ <?php
855
+ }
856
+
857
+ /**
858
+ * The html for the export details section in the sidebar.
859
+ *
860
+ * @since 2.2.15
861
+ * @return void
862
+ */
863
+ public function export_data() {
864
+
865
+ global $post;
866
+
867
+ $link_url = wp_nonce_url( admin_url( 'post.php?'. $_SERVER['QUERY_STRING'] . '&wpsl_data_export=1' ), 'wpsl_export_' . $post->ID, 'wpsl_export_nonce' );
868
+
869
+ ?>
870
+ <p class="wpsl-submit-wrap">
871
+ <a id="wpsl-export-data" class="button-primary" href="<?php echo esc_url( $link_url ); ?>"><?php _e( 'Export Location Data', 'wpsl' ); ?></a>
872
+ </p>
873
+ <?php
874
+ }
875
+
876
+ /**
877
+ * Store update messages.
878
+ *
879
+ * @since 2.0.0
880
+ * @param array $messages Existing post update messages.
881
+ * @return array $messages Amended post update messages with new CPT update messages.
882
+ */
883
+ function store_update_messages( $messages ) {
884
+
885
+ $post = get_post();
886
+ $post_type = get_post_type( $post );
887
+ $post_type_object = get_post_type_object( $post_type );
888
+
889
+ $messages['wpsl_stores'] = array(
890
+ 0 => '', // Unused. Messages start at index 1.
891
+ 1 => __( 'Store updated.', 'wpsl' ),
892
+ 2 => __( 'Custom field updated.', 'wpsl' ),
893
+ 3 => __( 'Custom field deleted.', 'wpsl' ),
894
+ 4 => __( 'Store updated.', 'wpsl' ),
895
+ 5 => isset( $_GET['revision'] ) ? sprintf( __( 'Store restored to revision from %s', 'wpsl' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
896
+ 6 => __( 'Store published.', 'wpsl' ),
897
+ 7 => __( 'Store saved.', 'wpsl' ),
898
+ 8 => __( 'Store submitted.', 'wpsl' ),
899
+ 9 => sprintf(
900
+ __( 'Store scheduled for: <strong>%1$s</strong>.', 'wpsl' ),
901
+ date_i18n( __( 'M j, Y @ G:i', 'wpsl' ), strtotime( $post->post_date ) )
902
+ ),
903
+ 10 => __( 'Store draft updated.', 'wpsl' )
904
+ );
905
+
906
+ if ( ( 'wpsl_stores' == $post_type ) && ( $post_type_object->publicly_queryable ) ) {
907
+ $permalink = get_permalink( $post->ID );
908
+
909
+ $view_link = sprintf( ' <a href="%s">%s</a>', esc_url( $permalink ), __( 'View store', 'wpsl' ) );
910
+ $messages[ $post_type ][1] .= $view_link;
911
+ $messages[ $post_type ][6] .= $view_link;
912
+ $messages[ $post_type ][9] .= $view_link;
913
+
914
+ $preview_permalink = add_query_arg( 'preview', 'true', $permalink );
915
+ $preview_link = sprintf( ' <a target="_blank" href="%s">%s</a>', esc_url( $preview_permalink ), __( 'Preview store', 'wpsl' ) );
916
+ $messages[ $post_type ][8] .= $preview_link;
917
+ $messages[ $post_type ][10] .= $preview_link;
918
+ }
919
+
920
+ return $messages;
921
+ }
922
+
923
+ }
924
  }
admin/class-notices.php CHANGED
@@ -1,138 +1,138 @@
1
- <?php
2
- /**
3
- * Admin Notices
4
- *
5
- * @author Tijmen Smit
6
- * @since 2.0.0
7
- */
8
-
9
- if ( !defined( 'ABSPATH' ) ) exit;
10
-
11
- if ( !class_exists( 'WPSL_Notices' ) ) {
12
-
13
- /**
14
- * Handle the meta boxes.
15
- *
16
- * @since 2.0.0
17
- */
18
- class WPSL_Notices {
19
-
20
- /**
21
- * Holds the notices.
22
- * @since 2.0.0
23
- * @var array
24
- */
25
- private $notices = array();
26
-
27
- public function __construct() {
28
-
29
- $this->notices = get_option( 'wpsl_notices' );
30
-
31
- add_action( 'all_admin_notices', array( $this, 'show' ) );
32
- }
33
-
34
- /**
35
- * Show one or more notices.
36
- *
37
- * @since 2.0.0
38
- * @return void
39
- */
40
- public function show() {
41
-
42
- if ( !empty( $this->notices ) ) {
43
- $allowed_html = array(
44
- 'a' => array(
45
- 'href' => array(),
46
- 'id' => array(),
47
- 'class' => array(),
48
- 'data-nonce' => array(),
49
- 'title' => array(),
50
- 'target' => array()
51
- ),
52
- 'p' => array(),
53
- 'br' => array(),
54
- 'em' => array(),
55
- 'strong' => array(
56
- 'class' => array()
57
- ),
58
- 'span' => array(
59
- 'class' => array()
60
- ),
61
- 'ul' => array(
62
- 'class' => array()
63
- ),
64
- 'li' => array(
65
- 'class' => array()
66
- )
67
- );
68
-
69
- if ( wpsl_is_multi_array( $this->notices ) ) {
70
- foreach ( $this->notices as $k => $notice ) {
71
- $this->create_notice_content( $notice, $allowed_html );
72
- }
73
- } else {
74
- $this->create_notice_content( $this->notices, $allowed_html );
75
- }
76
-
77
- // Empty the notices.
78
- $this->notices = array();
79
- update_option( 'wpsl_notices', $this->notices );
80
- }
81
- }
82
-
83
- /**
84
- * Create the content shown in the notice.
85
- *
86
- * @since 2.1.0
87
- * @param array $notice
88
- * @param array $allowed_html
89
- */
90
- public function create_notice_content( $notice, $allowed_html ) {
91
-
92
- $class = ( 'update' == $notice['type'] ) ? 'updated' : 'error';
93
-
94
- if ( isset( $notice['multiline'] ) && $notice['multiline'] ) {
95
- $notice_msg = wp_kses( $notice['message'], $allowed_html );
96
- } else {
97
- $notice_msg = '<p>' . wp_kses( $notice['message'], $allowed_html ) . '</p>';
98
- }
99
-
100
- echo '<div class="' . esc_attr( $class ) . '">' . $notice_msg . '</div>';
101
- }
102
-
103
- /**
104
- * Save the notice.
105
- *
106
- * @since 2.0.0
107
- * @param string $type The type of notice, either 'update' or 'error'
108
- * @param string $message The user message
109
- * @param bool $multiline True if the message contains multiple lines ( used with notices created in add-ons ).
110
- * @return void
111
- */
112
- public function save( $type, $message, $multiline = false ) {
113
-
114
- $current_notices = get_option( 'wpsl_notices' );
115
-
116
- $new_notice = array(
117
- 'type' => $type,
118
- 'message' => $message
119
- );
120
-
121
- if ( $multiline ) {
122
- $new_notice['multiline'] = true;
123
- }
124
-
125
- if ( $current_notices ) {
126
- if ( !wpsl_is_multi_array( $current_notices ) ) {
127
- $current_notices = array( $current_notices );
128
- }
129
-
130
- array_push( $current_notices, $new_notice );
131
-
132
- update_option( 'wpsl_notices', $current_notices );
133
- } else {
134
- update_option( 'wpsl_notices', $new_notice );
135
- }
136
- }
137
- }
138
  }
1
+ <?php
2
+ /**
3
+ * Admin Notices
4
+ *
5
+ * @author Tijmen Smit
6
+ * @since 2.0.0
7
+ */
8
+
9
+ if ( !defined( 'ABSPATH' ) ) exit;
10
+
11
+ if ( !class_exists( 'WPSL_Notices' ) ) {
12
+
13
+ /**
14
+ * Handle the meta boxes.
15
+ *
16
+ * @since 2.0.0
17
+ */
18
+ class WPSL_Notices {
19
+
20
+ /**
21
+ * Holds the notices.
22
+ * @since 2.0.0
23
+ * @var array
24
+ */
25
+ private $notices = array();
26
+
27
+ public function __construct() {
28
+
29
+ $this->notices = get_option( 'wpsl_notices' );
30
+
31
+ add_action( 'all_admin_notices', array( $this, 'show' ) );
32
+ }
33
+
34
+ /**
35
+ * Show one or more notices.
36
+ *
37
+ * @since 2.0.0
38
+ * @return void
39
+ */
40
+ public function show() {
41
+
42
+ if ( !empty( $this->notices ) ) {
43
+ $allowed_html = array(
44
+ 'a' => array(
45
+ 'href' => array(),
46
+ 'id' => array(),
47
+ 'class' => array(),
48
+ 'data-nonce' => array(),
49
+ 'title' => array(),
50
+ 'target' => array()
51
+ ),
52
+ 'p' => array(),
53
+ 'br' => array(),
54
+ 'em' => array(),
55
+ 'strong' => array(
56
+ 'class' => array()
57
+ ),
58
+ 'span' => array(
59
+ 'class' => array()
60
+ ),
61
+ 'ul' => array(
62
+ 'class' => array()
63
+ ),
64
+ 'li' => array(
65
+ 'class' => array()
66
+ )
67
+ );
68
+
69
+ if ( wpsl_is_multi_array( $this->notices ) ) {
70
+ foreach ( $this->notices as $k => $notice ) {
71
+ $this->create_notice_content( $notice, $allowed_html );
72
+ }
73
+ } else {
74
+ $this->create_notice_content( $this->notices, $allowed_html );
75
+ }
76
+
77
+ // Empty the notices.
78
+ $this->notices = array();
79
+ update_option( 'wpsl_notices', $this->notices );
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Create the content shown in the notice.
85
+ *
86
+ * @since 2.1.0
87
+ * @param array $notice
88
+ * @param array $allowed_html
89
+ */
90
+ public function create_notice_content( $notice, $allowed_html ) {
91
+
92
+ $class = ( 'update' == $notice['type'] ) ? 'updated' : 'error';
93
+
94
+ if ( isset( $notice['multiline'] ) && $notice['multiline'] ) {
95
+ $notice_msg = wp_kses( $notice['message'], $allowed_html );
96
+ } else {
97
+ $notice_msg = '<p>' . wp_kses( $notice['message'], $allowed_html ) . '</p>';
98
+ }
99
+
100
+ echo '<div class="' . esc_attr( $class ) . '">' . $notice_msg . '</div>';
101
+ }
102
+
103
+ /**
104
+ * Save the notice.
105
+ *
106
+ * @since 2.0.0
107
+ * @param string $type The type of notice, either 'update' or 'error'
108
+ * @param string $message The user message
109
+ * @param bool $multiline True if the message contains multiple lines ( used with notices created in add-ons ).
110
+ * @return void
111
+ */
112
+ public function save( $type, $message, $multiline = false ) {
113
+
114
+ $current_notices = get_option( 'wpsl_notices' );
115
+
116
+ $new_notice = array(
117
+ 'type' => $type,
118
+ 'message' => $message
119
+ );
120
+
121
+ if ( $multiline ) {
122
+ $new_notice['multiline'] = true;
123
+ }
124
+
125
+ if ( $current_notices ) {
126
+ if ( !wpsl_is_multi_array( $current_notices ) ) {
127
+ $current_notices = array( $current_notices );
128
+ }
129
+
130
+ array_push( $current_notices, $new_notice );
131
+
132
+ update_option( 'wpsl_notices', $current_notices );
133
+ } else {
134
+ update_option( 'wpsl_notices', $new_notice );
135
+ }
136
+ }
137
+ }
138
  }
admin/class-settings.php CHANGED
@@ -1,1198 +1,1198 @@
1
- <?php
2
- /**
3
- * Handle the plugin settings.
4
- *
5
- * @author Tijmen Smit
6
- * @since 2.0.0
7
- */
8
-
9
- if ( !defined( 'ABSPATH' ) ) exit;
10
-
11
- if ( !class_exists( 'WPSL_Settings' ) ) {
12
-
13
- class WPSL_Settings {
14
-
15
- public function __construct() {
16
-
17
- $this->manually_clear_transient();
18
-
19
- add_action( 'wp_ajax_validate_server_key', array( $this, 'ajax_validate_server_key' ) );
20
- add_action( 'wp_ajax_nopriv_validate_server_key', array( $this, 'ajax_validate_server_key' ) );
21
- add_action( 'admin_init', array( $this, 'register_settings' ) );
22
- add_action( 'admin_init', array( $this, 'maybe_flush_rewrite_and_transient' ) );
23
- }
24
-
25
- /**
26
- * Determine if we need to clear the autoload transient.
27
- *
28
- * User can do this manually from the 'Tools' section on the settings page.
29
- *
30
- * @since 2.0.0
31
- * @return void
32
- */
33
- public function manually_clear_transient() {
34
-
35
- global $wpsl_admin;
36
-
37
- if ( isset( $_GET['action'] ) && $_GET['action'] == 'clear_wpsl_transients' && isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'clear_transients' ) ) {
38
- $wpsl_admin->delete_autoload_transient();
39
-
40
- $msg = __( 'WP Store Locator Transients Cleared', 'wpsl' );
41
- $wpsl_admin->notices->save( 'update', $msg );
42
-
43
- /*
44
- * Make sure the &action=clear_wpsl_transients param is removed from the url.
45
- *
46
- * Otherwise if the user later clicks the 'Save Changes' button,
47
- * and the &action=clear_wpsl_transients param is still there it
48
- * will show two notices 'WP Store Locator Transients Cleared' and 'Settings Saved'.
49
- */
50
- wp_redirect( admin_url( 'edit.php?post_type=wpsl_stores&page=wpsl_settings' ) );
51
- exit;
52
- }
53
- }
54
-
55
- /**
56
- * Register the settings.
57
- *
58
- * @since 2.0.0
59
- * @return void
60
- */
61
- public function register_settings() {
62
- register_setting( 'wpsl_settings', 'wpsl_settings', array( $this, 'sanitize_settings' ) );
63
- }
64
-
65
- /**
66
- * Sanitize the submitted plugin settings.
67
- *
68
- * @since 1.0.0
69
- * @return array $output The setting values
70
- */
71
- public function sanitize_settings() {
72
-
73
- global $wpsl_settings, $wpsl_admin;
74
-
75
- $ux_absints = array(
76
- 'height',
77
- 'infowindow_width',
78
- 'search_width',
79
- 'label_width'
80
- );
81
-
82
- $marker_effects = array(
83
- 'bounce',
84
- 'info_window',
85
- 'ignore'
86
- );
87
-
88
- $ux_checkboxes = array(
89
- 'new_window',
90
- 'reset_map',
91
- 'listing_below_no_scroll',
92
- 'direction_redirect',
93
- 'more_info',
94
- 'store_url',
95
- 'phone_url',
96
- 'marker_streetview',
97
- 'marker_zoom_to',
98
- 'mouse_focus',
99
- 'reset_map',
100
- 'show_contact_details',
101
- 'clickable_contact_details',
102
- 'hide_country',
103
- 'hide_distance'
104
- );
105
-
106
- /*
107
- * If the provided server key is different then the existing value,
108
- * then we test if it's valid by making a call to the Geocode API.
109
- */
110
- if ( $_POST['wpsl_api']['server_key'] && $wpsl_settings['api_server_key'] != $_POST['wpsl_api']['server_key'] || !get_option( 'wpsl_valid_server_key' ) ) {
111
- $server_key = sanitize_text_field( $_POST['wpsl_api']['server_key'] );
112
-
113
- $this->validate_server_key( $server_key );
114
- }
115
-
116
- $output['api_server_key'] = sanitize_text_field( $_POST['wpsl_api']['server_key'] );
117
- $output['api_browser_key'] = sanitize_text_field( $_POST['wpsl_api']['browser_key'] );
118
- $output['api_language'] = wp_filter_nohtml_kses( $_POST['wpsl_api']['language'] );
119
- $output['api_region'] = wp_filter_nohtml_kses( $_POST['wpsl_api']['region'] );
120
- $output['api_geocode_component'] = isset( $_POST['wpsl_api']['geocode_component'] ) ? 1 : 0;
121
-
122
- // Check the search filter.
123
- $output['autocomplete'] = isset( $_POST['wpsl_search']['autocomplete'] ) ? 1 : 0;
124
- $output['results_dropdown'] = isset( $_POST['wpsl_search']['results_dropdown'] ) ? 1 : 0;
125
- $output['radius_dropdown'] = isset( $_POST['wpsl_search']['radius_dropdown'] ) ? 1 : 0;
126
- $output['category_filter'] = isset( $_POST['wpsl_search']['category_filter'] ) ? 1 : 0;
127
- $output['category_filter_type'] = ( $_POST['wpsl_search']['category_filter_type'] == 'dropdown' ) ? 'dropdown' : 'checkboxes';
128
-
129
- $output['distance_unit'] = ( $_POST['wpsl_search']['distance_unit'] == 'km' ) ? 'km' : 'mi';
130
-
131
- // Check for a valid max results value, otherwise we use the default.
132
- if ( !empty( $_POST['wpsl_search']['max_results'] ) ) {
133
- $output['max_results'] = sanitize_text_field( $_POST['wpsl_search']['max_results'] );
134
- } else {
135
- $this->settings_error( 'max_results' );
136
- $output['max_results'] = wpsl_get_default_setting( 'max_results' );
137
- }
138
-
139
- // See if a search radius value exist, otherwise we use the default.
140
- if ( !empty( $_POST['wpsl_search']['radius'] ) ) {
141
- $output['search_radius'] = sanitize_text_field( $_POST['wpsl_search']['radius'] );
142
- } else {
143
- $this->settings_error( 'search_radius' );
144
- $output['search_radius'] = wpsl_get_default_setting( 'search_radius' );
145
- }
146
-
147
- // Check if we have a valid zoom level, it has to be between 1 or 12. If not set it to the default of 3.
148
- $output['zoom_level'] = wpsl_valid_zoom_level( $_POST['wpsl_map']['zoom_level'] );
149
-
150
- // Check for a valid max auto zoom level.
151
- $max_zoom_levels = wpsl_get_max_zoom_levels();
152
-
153
- if ( in_array( absint( $_POST['wpsl_map']['max_auto_zoom'] ), $max_zoom_levels ) ) {
154
- $output['auto_zoom_level'] = $_POST['wpsl_map']['max_auto_zoom'];
155
- } else {
156
- $output['auto_zoom_level'] = wpsl_get_default_setting( 'auto_zoom_level' );
157
- }
158
-
159
- if ( isset( $_POST['wpsl_map']['start_name'] ) ) {
160
- $output['start_name'] = sanitize_text_field( $_POST['wpsl_map']['start_name'] );
161
- } else {
162
- $output['start_name'] = '';
163
- }
164
-
165
- // If no location name is then we also empty the latlng values from the hidden input field.
166
- if ( empty( $output['start_name'] ) ) {
167
- $this->settings_error( 'start_point' );
168
- $output['start_latlng'] = '';
169
- } else {
170
-
171
- /*
172
- * If the start latlng is empty, but a start location name is provided,
173
- * then make a request to the Geocode API to get it.
174
- *
175
- * This can only happen if there is a JS error in the admin area that breaks the
176
- * Google Maps Autocomplete. So this code is only used as fallback to make sure
177
- * the provided start location is always geocoded.
178
- */
179
- if ( $wpsl_settings['start_name'] != $_POST['wpsl_map']['start_name']
180
- && $wpsl_settings['start_latlng'] == $_POST['wpsl_map']['start_latlng']
181
- || empty( $_POST['wpsl_map']['start_latlng'] ) ) {
182
- $start_latlng = wpsl_get_address_latlng( $_POST['wpsl_map']['start_name'] );
183
- } else {
184
- $start_latlng = sanitize_text_field( $_POST['wpsl_map']['start_latlng'] );
185
- }
186
-
187
- $output['start_latlng'] = $start_latlng;
188
- }
189
-
190
- // Do we need to run the fitBounds function make the markers fit in the viewport?
191
- $output['run_fitbounds'] = isset( $_POST['wpsl_map']['run_fitbounds'] ) ? 1 : 0;
192
-
193
- // Check if we have a valid map type.
194
- $output['map_type'] = wpsl_valid_map_type( $_POST['wpsl_map']['type'] );
195
- $output['auto_locate'] = isset( $_POST['wpsl_map']['auto_locate'] ) ? 1 : 0;
196
- $output['autoload'] = isset( $_POST['wpsl_map']['autoload'] ) ? 1 : 0;
197
-
198
- // Make sure the auto load limit is either empty or an int.
199
- if ( empty( $_POST['wpsl_map']['autoload_limit'] ) ) {
200
- $output['autoload_limit'] = '';
201
- } else {
202
- $output['autoload_limit'] = absint( $_POST['wpsl_map']['autoload_limit'] );
203
- }
204
-
205
- $output['streetview'] = isset( $_POST['wpsl_map']['streetview'] ) ? 1 : 0;
206
- $output['type_control'] = isset( $_POST['wpsl_map']['type_control'] ) ? 1 : 0;
207
- $output['scrollwheel'] = isset( $_POST['wpsl_map']['scrollwheel'] ) ? 1 : 0;
208
- $output['control_position'] = ( $_POST['wpsl_map']['control_position'] == 'left' ) ? 'left' : 'right';
209
-
210
- $output['map_style'] = json_encode( strip_tags( trim( $_POST['wpsl_map']['map_style'] ) ) );
211
-
212
- // Make sure we have a valid template ID.
213
- if ( isset( $_POST['wpsl_ux']['template_id'] ) && ( $_POST['wpsl_ux']['template_id'] ) ) {
214
- $output['template_id'] = sanitize_text_field( $_POST['wpsl_ux']['template_id'] );
215
- } else {
216
- $output['template_id'] = wpsl_get_default_setting( 'template_id' );
217
- }
218
-
219
- $output['marker_clusters'] = isset( $_POST['wpsl_map']['marker_clusters'] ) ? 1 : 0;
220
-
221
- // Check for a valid cluster zoom value.
222
- if ( in_array( $_POST['wpsl_map']['cluster_zoom'], $this->get_default_cluster_option( 'cluster_zoom' ) ) ) {
223
- $output['cluster_zoom'] = $_POST['wpsl_map']['cluster_zoom'];
224
- } else {
225
- $output['cluster_zoom'] = wpsl_get_default_setting( 'cluster_zoom' );
226
- }
227
-
228
- // Check for a valid cluster size value.
229
- if ( in_array( $_POST['wpsl_map']['cluster_size'], $this->get_default_cluster_option( 'cluster_size' ) ) ) {
230
- $output['cluster_size'] = $_POST['wpsl_map']['cluster_size'];
231
- } else {
232
- $output['cluster_size'] = wpsl_get_default_setting( 'cluster_size' );
233
- }
234
-
235
- /*
236
- * Make sure all the ux related fields that should contain an int, actually are an int.
237
- * Otherwise we use the default value.
238
- */
239
- foreach ( $ux_absints as $ux_key ) {
240
- if ( absint( $_POST['wpsl_ux'][$ux_key] ) ) {
241
- $output[$ux_key] = $_POST['wpsl_ux'][$ux_key];
242
- } else {
243
- $output[$ux_key] = wpsl_get_default_setting( $ux_key );
244
- }
245
- }
246
-
247
- // Check if the ux checkboxes are checked.
248
- foreach ( $ux_checkboxes as $ux_key ) {
249
- $output[$ux_key] = isset( $_POST['wpsl_ux'][$ux_key] ) ? 1 : 0;
250
- }
251
-
252
- // Check if we have a valid marker effect.
253
- if ( in_array( $_POST['wpsl_ux']['marker_effect'], $marker_effects ) ) {
254
- $output['marker_effect'] = $_POST['wpsl_ux']['marker_effect'];
255
- } else {
256
- $output['marker_effect'] = wpsl_get_default_setting( 'marker_effect' );
257
- }
258
-
259
- // Check if we have a valid address format.
260
- if ( array_key_exists( $_POST['wpsl_ux']['address_format'], wpsl_get_address_formats() ) ) {
261
- $output['address_format'] = $_POST['wpsl_ux']['address_format'];
262
- } else {
263
- $output['address_format'] = wpsl_get_default_setting( 'address_format' );
264
- }
265
-
266
- $output['more_info_location'] = ( $_POST['wpsl_ux']['more_info_location'] == 'store listings' ) ? 'store listings' : 'info window';
267
- $output['infowindow_style'] = isset( $_POST['wpsl_ux']['infowindow_style'] ) ? 'default' : 'infobox';
268
- $output['start_marker'] = wp_filter_nohtml_kses( $_POST['wpsl_map']['start_marker'] );
269
- $output['store_marker'] = wp_filter_nohtml_kses( $_POST['wpsl_map']['store_marker'] );
270
- $output['editor_country'] = sanitize_text_field( $_POST['wpsl_editor']['default_country'] );
271
- $output['editor_map_type'] = wpsl_valid_map_type( $_POST['wpsl_editor']['map_type'] );
272
- $output['hide_hours'] = isset( $_POST['wpsl_editor']['hide_hours'] ) ? 1 : 0;
273
-
274
- if ( isset( $_POST['wpsl_editor']['hour_input'] ) ) {
275
- $output['editor_hour_input'] = ( $_POST['wpsl_editor']['hour_input'] == 'textarea' ) ? 'textarea' : 'dropdown';
276
- } else {
277
- $output['editor_hour_input'] = 'dropdown';
278
- }
279
-
280
- $output['editor_hour_format'] = ( $_POST['wpsl_editor']['hour_format'] == 12 ) ? 12 : 24;
281
-
282
- // The default opening hours.
283
- if ( isset( $_POST['wpsl_editor']['textarea'] ) ) {
284
- $output['editor_hours']['textarea'] = wp_kses_post( trim( stripslashes( $_POST['wpsl_editor']['textarea'] ) ) );
285
- }
286
-
287
- $output['editor_hours']['dropdown'] = $wpsl_admin->metaboxes->format_opening_hours();
288
- array_walk_recursive( $output['editor_hours']['dropdown'], 'wpsl_sanitize_multi_array' );
289
-
290
- // Permalink and taxonomy slug.
291
- $output['permalinks'] = isset( $_POST['wpsl_permalinks']['active'] ) ? 1 : 0;
292
-
293
- if ( !empty( $_POST['wpsl_permalinks']['slug'] ) ) {
294
- $output['permalink_slug'] = sanitize_text_field( $_POST['wpsl_permalinks']['slug'] );
295
- } else {
296
- $output['permalink_slug'] = wpsl_get_default_setting( 'permalink_slug' );
297
- }
298
-
299
- if ( !empty( $_POST['wpsl_permalinks']['category_slug'] ) ) {
300
- $output['category_slug'] = sanitize_text_field( $_POST['wpsl_permalinks']['category_slug'] );
301
- } else {
302
- $output['category_slug'] = wpsl_get_default_setting( 'category_slug' );
303
- }
304
-
305
- $required_labels = wpsl_labels();
306
-
307
- // Sanitize the labels.
308
- foreach ( $required_labels as $label ) {
309
- $output[$label.'_label'] = sanitize_text_field( $_POST['wpsl_label'][$label] );
310
- }
311
-
312
- $output['show_credits'] = isset( $_POST['wpsl_credits'] ) ? 1 : 0;
313
- $output['debug'] = isset( $_POST['wpsl_tools']['debug'] ) ? 1 : 0;
314
- $output['deregister_gmaps'] = isset( $_POST['wpsl_tools']['deregister_gmaps'] ) ? 1 : 0;
315
-
316
- // Check if we need to flush the permalinks.
317
- $this->set_flush_rewrite_option( $output );
318
-
319
- // Check if there is a reason to delete the autoload transient.
320
- if ( $wpsl_settings['autoload'] ) {
321
- $this->set_delete_transient_option( $output );
322
- }
323
-
324
- return $output;
325
- }
326
-
327
- /**
328
- * Handle the AJAX call to validate the provided
329
- * server key for the Google Maps API.
330
- *
331
- * @since 2.2.10
332
- * @return void
333
- */
334
- public function ajax_validate_server_key() {
335
-
336
- if ( ( current_user_can( 'manage_wpsl_settings' ) ) && is_admin() ) {
337
- $server_key = sanitize_text_field( $_GET['server_key'] );
338
-
339
- if ( $server_key ) {
340
- $this->validate_server_key( $server_key );
341
- }
342
- }
343
- }
344
-
345
- /**
346
- * Check if the provided server key for
347
- * the Google Maps API is valid.
348
- *
349
- * @since 2.2.10
350
- * @param string $server_key The server key to validate
351
- * @return json|void If the validation failed and AJAX is used, then json
352
- */
353
- public function validate_server_key( $server_key ) {
354
-
355
- global $wpsl_admin;
356
-
357
- // Test the server key by making a request to the Geocode API.
358
- $address = 'Manhattan, NY 10036, USA';
359
- $url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' . urlencode( $address ) .'&key=' . $server_key;
360
- $response = wp_remote_get( $url );
361
-
362
- if ( !is_wp_error( $response ) ) {
363
- $response = json_decode( $response['body'], true );
364
-
365
- // If the state is not OK, then there's a problem with the key.
366
- if ( $response['status'] !== 'OK' ) {
367
- $geocode_errors = $wpsl_admin->geocode->check_geocode_error_msg( $response, true );
368
- $error_msg = sprintf( __( 'There\'s a problem with the provided %sserver key%s. %s' ), '<a href="https://wpstorelocator.co/document/create-google-api-keys/#server-key">', '</a>', $geocode_errors );
369
-
370
- update_option( 'wpsl_valid_server_key', 0 );
371
-
372
- // If the server key input field has 'wpsl-validate-me' class on it, then it's validated with AJAX in the background.
373
- if ( defined('DOING_AJAX' ) && DOING_AJAX ) {
374
- $key_status = array(
375
- 'valid' => 0,
376
- 'msg' => $error_msg
377
- );
378
-
379
- wp_send_json( $key_status );
380
-
381
- exit();
382
- } else {
383
- add_settings_error( 'setting-errors', esc_attr( 'server-key' ), $error_msg, 'error' );
384
- }
385
- } else {
386
- update_option( 'wpsl_valid_server_key', 1 );
387
- }
388
- }
389
- }
390
-
391
- /**
392
- * Check if we need set the option that will be used to determine
393
- * if we need to flush the permalinks once the setting page reloads.
394
- *
395
- * @since 2.0.0
396
- * @param array $new_settings The submitted plugin settings
397
- * @return void
398
- */
399
- public function set_flush_rewrite_option( $new_settings ) {
400
-
401
- global $wpsl_settings;
402
-
403
- if ( ( $wpsl_settings['permalinks'] != $new_settings['permalinks'] ) || ( $wpsl_settings['permalink_slug'] != $new_settings['permalink_slug'] ) || ( $wpsl_settings['category_slug'] != $new_settings['category_slug'] ) ) {
404
- update_option( 'wpsl_flush_rewrite', 1 );
405
- }
406
- }
407
-
408
- /**
409
- * Check if we need set the option that is used to determine
410
- * if we need to delete the autoload transient once the settings page reloads.
411
- *
412
- * @since 2.0.0
413
- * @param array $new_settings The submitted plugin settings
414
- * @return void
415
- */
416
- public function set_delete_transient_option( $new_settings ) {
417
-
418
- global $wpsl_settings;
419
-
420
- // The options we need to check for changes.
421
- $options = array(
422
- 'start_name',
423
- 'debug',
424
- 'autoload',
425
- 'autoload_limit',
426
- 'more_info',
427
- 'more_info_location',
428
- 'hide_hours',
429
- 'hide_distance',
430
- 'hide_country',
431
- 'show_contact_details'
432
- );
433
-
434
- foreach ( $options as $option_name ) {
435
- if ( $wpsl_settings[$option_name] != $new_settings[$option_name] ) {
436
- update_option( 'wpsl_delete_transient', 1 );
437
- break;
438
- }
439
- }
440
- }
441
-
442
- /**
443
- * Check if the permalinks settings changed.
444
- *
445
- * @since 2.0.0
446
- * @return void
447
- */
448
- public function maybe_flush_rewrite_and_transient() {
449
-
450
- global $wpsl_admin;
451
-
452
- if ( isset( $_GET['page'] ) && ( $_GET['page'] == 'wpsl_settings' ) ) {
453
- $flush_rewrite = get_option( 'wpsl_flush_rewrite' );
454
- $delete_transient = get_option( 'wpsl_delete_transient' );
455
-
456
- if ( $flush_rewrite ) {
457
- flush_rewrite_rules();
458
- update_option( 'wpsl_flush_rewrite', 0 );
459
- }
460
-
461
- if ( $delete_transient ) {
462
- update_option( 'wpsl_delete_transient', 0 );
463
- }
464
-
465
- if ( $flush_rewrite || $delete_transient ) {
466
- $wpsl_admin->delete_autoload_transient();
467
- }
468
- }
469
- }
470
-
471
- /**
472
- * Handle the different validation errors for the plugin settings.
473
- *
474
- * @since 1.0.0
475
- * @param string $error_type Contains the type of validation error that occured
476
- * @return void
477
- */
478
- private function settings_error( $error_type ) {
479
-
480
- switch ( $error_type ) {
481
- case 'max_results':
482
- $error_msg = __( 'The max results field cannot be empty, the default value has been restored.', 'wpsl' );
483
- break;
484
- case 'search_radius':
485
- $error_msg = __( 'The search radius field cannot be empty, the default value has been restored.', 'wpsl' );
486
- break;
487
- case 'start_point':
488
- $error_msg = sprintf( __( 'Please provide the name of a city or country that can be used as a starting point under "Map Settings". %s This will only be used if auto-locating the user fails, or the option itself is disabled.', 'wpsl' ), '<br><br>' );
489
- break;
490
- }
491
-
492
- add_settings_error( 'setting-errors', esc_attr( 'settings_fail' ), $error_msg, 'error' );
493
- }
494
-
495
- /**
496
- * Options for the language and region list.
497
- *
498
- * @since 1.0.0
499
- * @param string $list The request list type
500
- * @return string|void $option_list The html for the selected list, or nothing if the $list contains invalud values
501
- */
502
- public function get_api_option_list( $list ) {
503
-
504
- global $wpsl_settings;
505
-
506
- switch ( $list ) {
507
- case 'language':
508
- $api_option_list = array (
509
- __('Select your language', 'wpsl') => '',
510
- __('English', 'wpsl') => 'en',
511
- __('Arabic', 'wpsl') => 'ar',
512
- __('Basque', 'wpsl') => 'eu',
513
- __('Bulgarian', 'wpsl') => 'bg',
514
- __('Bengali', 'wpsl') => 'bn',
515
- __('Catalan', 'wpsl') => 'ca',
516
- __('Czech', 'wpsl') => 'cs',
517
- __('Danish', 'wpsl') => 'da',
518
- __('German', 'wpsl') => 'de',
519
- __('Greek', 'wpsl') => 'el',
520
- __('English (Australian)', 'wpsl') => 'en-AU',
521
- __('English (Great Britain)', 'wpsl') => 'en-GB',
522
- __('Spanish', 'wpsl') => 'es',
523
- __('Farsi', 'wpsl') => 'fa',
524
- __('Finnish', 'wpsl') => 'fi',
525
- __('Filipino', 'wpsl') => 'fil',
526
- __('French', 'wpsl') => 'fr',
527
- __('Galician', 'wpsl') => 'gl',
528
- __('Gujarati', 'wpsl') => 'gu',
529
- __('Hindi', 'wpsl') => 'hi',
530
- __('Croatian', 'wpsl') => 'hr',
531
- __('Hungarian', 'wpsl') => 'hu',
532
- __('Indonesian', 'wpsl') => 'id',
533
- __('Italian', 'wpsl') => 'it',
534
- __('Hebrew', 'wpsl') => 'iw',
535
- __('Japanese', 'wpsl') => 'ja',
536
- __('Kannada', 'wpsl') => 'kn',
537
- __('Korean', 'wpsl') => 'ko',
538
- __('Lithuanian', 'wpsl') => 'lt',
539
- __('Latvian', 'wpsl') => 'lv',
540
- __('Malayalam', 'wpsl') => 'ml',
541
- __('Marathi', 'wpsl') => 'mr',
542
- __('Dutch', 'wpsl') => 'nl',
543
- __('Norwegian', 'wpsl') => 'no',
544
- __('Norwegian Nynorsk', 'wpsl') => 'nn',
545
- __('Polish', 'wpsl') => 'pl',
546
- __('Portuguese', 'wpsl') => 'pt',
547
- __('Portuguese (Brazil)', 'wpsl') => 'pt-BR',
548
- __('Portuguese (Portugal)', 'wpsl') => 'pt-PT',
549
- __('Romanian', 'wpsl') => 'ro',
550
- __('Russian', 'wpsl') => 'ru',
551
- __('Slovak', 'wpsl') => 'sk',
552
- __('Slovenian', 'wpsl') => 'sl',
553
- __('Serbian', 'wpsl') => 'sr',
554
- __('Swedish', 'wpsl') => 'sv',
555
- __('Tagalog', 'wpsl') => 'tl',
556
- __('Tamil', 'wpsl') => 'ta',
557
- __('Telugu', 'wpsl') => 'te',
558
- __('Thai', 'wpsl') => 'th',
559
- __('Turkish', 'wpsl') => 'tr',
560
- __('Ukrainian', 'wpsl') => 'uk',
561
- __('Vietnamese', 'wpsl') => 'vi',
562
- __('Chinese (Simplified)', 'wpsl') => 'zh-CN',
563
- __('Chinese (Traditional)' ,'wpsl') => 'zh-TW'
564
- );
565
- break;
566
- case 'region':
567
- $api_option_list = array (
568
- __('Select your region', 'wpsl') => '',
569
- __('Afghanistan', 'wpsl') => 'af',
570
- __('Albania', 'wpsl') => 'al',
571
- __('Algeria', 'wpsl') => 'dz',
572
- __('American Samoa', 'wpsl') => 'as',
573
- __('Andorra', 'wpsl') => 'ad',
574
- __('Angola', 'wpsl') => 'ao',
575
- __('Anguilla', 'wpsl') => 'ai',
576
- __('Antarctica', 'wpsl') => 'aq',
577
- __('Antigua and Barbuda', 'wpsl') => 'ag',
578
- __('Argentina', 'wpsl') => 'ar',
579
- __('Armenia', 'wpsl') => 'am',
580
- __('Aruba', 'wpsl') => 'aw',
581
- __('Ascension Island', 'wpsl') => 'ac',
582
- __('Australia', 'wpsl') => 'au',
583
- __('Austria', 'wpsl') => 'at',
584
- __('Azerbaijan', 'wpsl') => 'az',
585
- __('Bahamas', 'wpsl') => 'bs',
586
- __('Bahrain', 'wpsl') => 'bh',
587
- __('Bangladesh', 'wpsl') => 'bd',
588
- __('Barbados', 'wpsl') => 'bb',
589
- __('Belarus', 'wpsl') => 'by',
590
- __('Belgium', 'wpsl') => 'be',
591
- __('Belize', 'wpsl') => 'bz',
592
- __('Benin', 'wpsl') => 'bj',
593
- __('Bermuda', 'wpsl') => 'bm',
594
- __('Bhutan', 'wpsl') => 'bt',
595
- __('Bolivia', 'wpsl') => 'bo',
596
- __('Bosnia and Herzegovina', 'wpsl') => 'ba',
597
- __('Botswana', 'wpsl') => 'bw',
598
- __('Bouvet Island', 'wpsl') => 'bv',
599
- __('Brazil', 'wpsl') => 'br',
600
- __('British Indian Ocean Territory', 'wpsl') => 'io',
601
- __('British Virgin Islands', 'wpsl') => 'vg',
602
- __('Brunei', 'wpsl') => 'bn',
603
- __('Bulgaria', 'wpsl') => 'bg',
604
- __('Burkina Faso', 'wpsl') => 'bf',
605
- __('Burundi', 'wpsl') => 'bi',
606
- __('Cambodia', 'wpsl') => 'kh',
607
- __('Cameroon', 'wpsl') => 'cm',
608
- __('Canada', 'wpsl') => 'ca',
609
- __('Canary Islands', 'wpsl') => 'ic',
610
- __('Cape Verde', 'wpsl') => 'cv',
611
- __('Caribbean Netherlands', 'wpsl') => 'bq',
612
- __('Cayman Islands', 'wpsl') => 'ky',
613
- __('Central African Republic', 'wpsl') => 'cf',
614
- __('Ceuta and Melilla', 'wpsl') => 'ea',
615
- __('Chad', 'wpsl') => 'td',
616
- __('Chile', 'wpsl') => 'cl',
617
- __('China', 'wpsl') => 'cn',
618
- __('Christmas Island', 'wpsl') => 'cx',
619
- __('Clipperton Island', 'wpsl') => 'cp',
620
- __('Cocos (Keeling) Islands', 'wpsl') => 'cc',
621
- __('Colombia', 'wpsl') => 'co',
622
- __('Comoros', 'wpsl') => 'km',
623
- __('Congo (DRC)', 'wpsl') => 'cd',
624
- __('Congo (Republic)', 'wpsl') => 'cg',
625
- __('Cook Islands', 'wpsl') => 'ck',
626
- __('Costa Rica', 'wpsl') => 'cr',
627
- __('Croatia', 'wpsl') => 'hr',
628
- __('Cuba', 'wpsl') => 'cu',
629
- __('Curaçao', 'wpsl') => 'cw',
630
- __('Cyprus', 'wpsl') => 'cy',
631
- __('Czech Republic', 'wpsl') => 'cz',
632
- __('Côte d\'Ivoire', 'wpsl') => 'ci',
633
- __('Denmark', 'wpsl') => 'dk',
634
- __('Djibouti', 'wpsl') => 'dj',
635
- __('Democratic Republic of the Congo', 'wpsl') => 'cd',
636
- __('Dominica', 'wpsl') => 'dm',
637
- __('Dominican Republic', 'wpsl') => 'do',
638
- __('Ecuador', 'wpsl') => 'ec',
639
- __('Egypt', 'wpsl') => 'eg',
640
- __('El Salvador', 'wpsl') => 'sv',
641
- __('Equatorial Guinea', 'wpsl') => 'gq',
642
- __('Eritrea', 'wpsl') => 'er',
643
- __('Estonia', 'wpsl') => 'ee',
644
- __('Ethiopia', 'wpsl') => 'et',
645
- __('Falkland Islands(Islas Malvinas)', 'wpsl') => 'fk',
646
- __('Faroe Islands', 'wpsl') => 'fo',
647
- __('Fiji', 'wpsl') => 'fj',
648
- __('Finland', 'wpsl') => 'fi',
649
- __('France', 'wpsl') => 'fr',
650
- __('French Guiana', 'wpsl') => 'gf',
651
- __('French Polynesia', 'wpsl') => 'pf',
652
- __('French Southern Territories', 'wpsl') => 'tf',
653
- __('Gabon', 'wpsl') => 'ga',
654
- __('Gambia', 'wpsl') => 'gm',
655
- __('Georgia', 'wpsl') => 'ge',
656
- __('Germany', 'wpsl') => 'de',
657
- __('Ghana', 'wpsl') => 'gh',
658
- __('Gibraltar', 'wpsl') => 'gi',
659
- __('Greece', 'wpsl') => 'gr',
660
- __('Greenland', 'wpsl') => 'gl',
661
- __('Grenada', 'wpsl') => 'gd',
662
- __('Guam', 'wpsl') => 'gu',
663
- __('Guadeloupe', 'wpsl') => 'gp',
664
- __('Guam', 'wpsl') => 'gu',
665
- __('Guatemala', 'wpsl') => 'gt',
666
- __('Guernsey', 'wpsl') => 'gg',
667
- __('Guinea', 'wpsl') => 'gn',
668
- __('Guinea-Bissau', 'wpsl') => 'gw',
669
- __('Guyana', 'wpsl') => 'gy',
670
- __('Haiti', 'wpsl') => 'ht',
671
- __('Heard and McDonald Islands', 'wpsl') => 'hm',
672
- __('Honduras', 'wpsl') => 'hn',
673
- __('Hong Kong', 'wpsl') => 'hk',
674
- __('Hungary', 'wpsl') => 'hu',
675
- __('Iceland', 'wpsl') => 'is',
676
- __('India', 'wpsl') => 'in',
677
- __('Indonesia', 'wpsl') => 'id',
678
- __('Iran', 'wpsl') => 'ir',
679
- __('Iraq', 'wpsl') => 'iq',
680
- __('Ireland', 'wpsl') => 'ie',
681
- __('Isle of Man', 'wpsl') => 'im',
682
- __('Israel', 'wpsl') => 'il',
683
- __('Italy', 'wpsl') => 'it',
684
- __('Jamaica', 'wpsl') => 'jm',
685
- __('Japan', 'wpsl') => 'jp',
686
- __('Jersey', 'wpsl') => 'je',
687
- __('Jordan', 'wpsl') => 'jo',
688
- __('Kazakhstan', 'wpsl') => 'kz',
689
- __('Kenya', 'wpsl') => 'ke',
690
- __('Kiribati', 'wpsl') => 'ki',
691
- __('Kosovo', 'wpsl') => 'xk',
692
- __('Kuwait', 'wpsl') => 'kw',
693
- __('Kyrgyzstan', 'wpsl') => 'kg',
694
- __('Laos', 'wpsl') => 'la',
695
- __('Latvia', 'wpsl') => 'lv',
696
- __('Lebanon', 'wpsl') => 'lb',
697
- __('Lesotho', 'wpsl') => 'ls',
698
- __('Liberia', 'wpsl') => 'lr',
699
- __('Libya', 'wpsl') => 'ly',
700
- __('Liechtenstein', 'wpsl') => 'li',
701
- __('Lithuania', 'wpsl') => 'lt',
702
- __('Luxembourg', 'wpsl') => 'lu',
703
- __('Macau', 'wpsl') => 'mo',
704
- __('Macedonia (FYROM)', 'wpsl') => 'mk',
705
- __('Madagascar', 'wpsl') => 'mg',
706
- __('Malawi', 'wpsl') => 'mw',
707
- __('Malaysia ', 'wpsl') => 'my',
708
- __('Maldives ', 'wpsl') => 'mv',
709
- __('Mali', 'wpsl') => 'ml',
710
- __('Malta', 'wpsl') => 'mt',
711
- __('Marshall Islands', 'wpsl') => 'mh',
712
- __('Martinique', 'wpsl') => 'mq',
713
- __('Mauritania', 'wpsl') => 'mr',
714
- __('Mauritius', 'wpsl') => 'mu',
715
- __('Mayotte', 'wpsl') => 'yt',
716
- __('Mexico', 'wpsl') => 'mx',
717
- __('Micronesia', 'wpsl') => 'fm',
718
- __('Moldova', 'wpsl') => 'md',
719
- __('Monaco' ,'wpsl') => 'mc',
720
- __('Mongolia', 'wpsl') => 'mn',
721
- __('Montenegro', 'wpsl') => 'me',
722
- __('Montserrat', 'wpsl') => 'ms',
723
- __('Morocco', 'wpsl') => 'ma',
724
- __('Mozambique', 'wpsl') => 'mz',
725
- __('Myanmar (Burma)', 'wpsl') => 'mm',
726
- __('Namibia', 'wpsl') => 'na',
727
- __('Nauru', 'wpsl') => 'nr',
728
- __('Nepal', 'wpsl') => 'np',
729
- __('Netherlands', 'wpsl') => 'nl',
730
- __('Netherlands Antilles', 'wpsl') => 'an',
731
- __('New Caledonia', 'wpsl') => 'nc',
732
- __('New Zealand', 'wpsl') => 'nz',
733
- __('Nicaragua', 'wpsl') => 'ni',
734
- __('Niger', 'wpsl') => 'ne',
735
- __('Nigeria', 'wpsl') => 'ng',
736
- __('Niue', 'wpsl') => 'nu',
737
- __('Norfolk Island', 'wpsl') => 'nf',
738
- __('North Korea', 'wpsl') => 'kp',
739
- __('Northern Mariana Islands', 'wpsl') => 'mp',
740
- __('Norway', 'wpsl') => 'no',
741
- __('Oman', 'wpsl') => 'om',
742
- __('Pakistan', 'wpsl') => 'pk',
743
- __('Palau', 'wpsl') => 'pw',
744
- __('Palestine', 'wpsl') => 'ps',
745
- __('Panama' ,'wpsl') => 'pa',
746
- __('Papua New Guinea', 'wpsl') => 'pg',
747
- __('Paraguay' ,'wpsl') => 'py',
748
- __('Peru', 'wpsl') => 'pe',
749
- __('Philippines', 'wpsl') => 'ph',
750
- __('Pitcairn Islands', 'wpsl') => 'pn',
751
- __('Poland', 'wpsl') => 'pl',
752
- __('Portugal', 'wpsl') => 'pt',
753
- __('Puerto Rico', 'wpsl') => 'pr',
754
- __('Qatar', 'wpsl') => 'qa',
755
- __('Reunion', 'wpsl') => 're',
756
- __('Romania', 'wpsl') => 'ro',
757
- __('Russia', 'wpsl') => 'ru',
758
- __('Rwanda', 'wpsl') => 'rw',
759
- __('Saint Helena', 'wpsl') => 'sh',
760
- __('Saint Kitts and Nevis', 'wpsl') => 'kn',
761
- __('Saint Vincent and the Grenadines', 'wpsl') => 'vc',
762
- __('Saint Lucia', 'wpsl') => 'lc',
763
- __('Samoa', 'wpsl') => 'ws',
764
- __('San Marino', 'wpsl') => 'sm',
765
- __('São Tomé and Príncipe', 'wpsl') => 'st',
766
- __('Saudi Arabia', 'wpsl') => 'sa',
767
- __('Senegal', 'wpsl') => 'sn',
768
- __('Serbia', 'wpsl') => 'rs',
769
- __('Seychelles', 'wpsl') => 'sc',
770
- __('Sierra Leone', 'wpsl') => 'sl',
771
- __('Singapore', 'wpsl') => 'sg',
772
- __('Sint Maarten', 'wpsl') => 'sx',
773
- __('Slovakia', 'wpsl') => 'sk',
774
- __('Slovenia', 'wpsl') => 'si',
775
- __('Solomon Islands', 'wpsl') => 'sb',
776
- __('Somalia', 'wpsl') => 'so',
777
- __('South Africa', 'wpsl') => 'za',
778
- __('South Georgia and South Sandwich Islands', 'wpsl') => 'gs',
779
- __('South Korea', 'wpsl') => 'kr',
780
- __('South Sudan', 'wpsl') => 'ss',
781
- __('Spain', 'wpsl') => 'es',
782
- __('Sri Lanka', 'wpsl') => 'lk',
783
- __('Sudan', 'wpsl') => 'sd',
784
- __('Swaziland', 'wpsl') => 'sz',
785
- __('Sweden', 'wpsl') => 'se',
786
- __('Switzerland', 'wpsl') => 'ch',
787
- __('Syria', 'wpsl') => 'sy',
788
- __('São Tomé & Príncipe', 'wpsl') => 'st',
789
- __('Taiwan', 'wpsl') => 'tw',
790
- __('Tajikistan', 'wpsl') => 'tj',
791
- __('Tanzania', 'wpsl') => 'tz',
792
- __('Thailand', 'wpsl') => 'th',
793
- __('Timor-Leste', 'wpsl') => 'tl',
794
- __('Tokelau' ,'wpsl') => 'tk',
795
- __('Togo', 'wpsl') => 'tg',
796
- __('Tokelau' ,'wpsl') => 'tk',
797
- __('Tonga', 'wpsl') => 'to',
798
- __('Trinidad and Tobago', 'wpsl') => 'tt',
799
- __('Tristan da Cunha', 'wpsl') => 'ta',
800
- __('Tunisia', 'wpsl') => 'tn',
801
- __('Turkey', 'wpsl') => 'tr',
802
- __('Turkmenistan', 'wpsl') => 'tm',
803
- __('Turks and Caicos Islands', 'wpsl') => 'tc',
804
- __('Tuvalu', 'wpsl') => 'tv',
805
- __('Uganda', 'wpsl') => 'ug',
806
- __('Ukraine', 'wpsl') => 'ua',
807
- __('United Arab Emirates', 'wpsl') => 'ae',
808
- __('United Kingdom', 'wpsl') => 'gb',
809
- __('United States', 'wpsl') => 'us',
810
- __('Uruguay', 'wpsl') => 'uy',
811
- __('Uzbekistan', 'wpsl') => 'uz',
812
- __('Vanuatu', 'wpsl') => 'vu',
813
- __('Vatican City', 'wpsl') => 'va',
814
- __('Venezuela', 'wpsl') => 've',
815
- __('Vietnam', 'wpsl') => 'vn',
816
- __('Wallis Futuna', 'wpsl') => 'wf',
817
- __('Western Sahara', 'wpsl') => 'eh',
818
- __('Yemen', 'wpsl') => 'ye',
819
- __('Zambia' ,'wpsl') => 'zm',
820
- __('Zimbabwe', 'wpsl') => 'zw',
821
- __('Åland Islands', 'wpsl') => 'ax'
822
- );
823
- }
824
-
825
- // Make sure we have an array with a value.
826
- if ( !empty( $api_option_list ) && ( is_array( $api_option_list ) ) ) {
827
- $option_list = '';
828
- $i = 0;
829
-
830
- foreach ( $api_option_list as $api_option_key => $api_option_value ) {
831
-
832
- // If no option value exist, set the first one as selected.
833
- if ( ( $i == 0 ) && ( empty( $wpsl_settings['api_'.$list] ) ) ) {
834
- $selected = 'selected="selected"';
835
- } else {
836
- $selected = ( $wpsl_settings['api_'.$list] == $api_option_value ) ? 'selected="selected"' : '';
837
- }
838
-
839
- $option_list .= '<option value="' . esc_attr( $api_option_value ) . '" ' . $selected . '> ' . esc_html( $api_option_key ) . '</option>';
840
- $i++;
841
- }
842
-
843
- return $option_list;
844
- }
845
- }
846
-
847
- /**
848
- * Create the dropdown to select the zoom level.
849
- *
850
- * @since 1.0.0
851
- * @return string $dropdown The html for the zoom level list
852
- */
853
- public function show_zoom_levels() {
854
-
855
- global $wpsl_settings;
856
-
857
- $dropdown = '<select id="wpsl-zoom-level" name="wpsl_map[zoom_level]" autocomplete="off">';
858
-
859
- for ( $i = 1; $i < 13; $i++ ) {
860
- $selected = ( $wpsl_settings['zoom_level'] == $i ) ? 'selected="selected"' : '';
861
-
862
- switch ( $i ) {
863
- case 1:
864
- $zoom_desc = ' - ' . __( 'World view', 'wpsl' );
865
- break;
866
- case 3:
867
- $zoom_desc = ' - ' . __( 'Default', 'wpsl' );
868
- break;
869
- case 12:
870
- $zoom_desc = ' - ' . __( 'Roadmap', 'wpsl' );
871
- break;
872
- default:
873
- $zoom_desc = '';
874
- }
875
-
876
- $dropdown .= "<option value='$i' $selected>". $i . esc_html( $zoom_desc ) . "</option>";
877
- }
878
-
879
- $dropdown .= "</select>";
880
-
881
- return $dropdown;
882
- }
883
-
884
- /**
885
- * Create the html output for the marker list that is shown on the settings page.
886
- *
887
- * There are two markers lists, one were the user can set the marker for the start point
888
- * and one were a marker can be set for the store. We also check if the marker img is identical
889
- * to the name in the option field. If so we set it to checked.
890
- *
891
- * @since 1.0.0
892
- * @param string $marker_img The filename of the marker
893
- * @param string $location Either contains "start" or "store"
894
- * @return string $marker_list A list of all the available markers
895
- */
896
- public function create_marker_html( $marker_img, $location ) {
897
-
898
- global $wpsl_settings;
899
-
900
- $marker_path = ( defined( 'WPSL_MARKER_URI' ) ) ? WPSL_MARKER_URI : WPSL_URL . 'img/markers/';
901
- $marker_list = '';
902
-
903
- if ( $wpsl_settings[$location.'_marker'] == $marker_img ) {
904
- $checked = 'checked="checked"';
905
- $css_class = 'class="wpsl-active-marker"';
906
- } else {
907
- $checked = '';
908
- $css_class = '';
909
- }
910
-
911
- $marker_list .= '<li ' . $css_class . '>';
912
- $marker_list .= '<img src="' . $marker_path . $marker_img . '" />';
913
- $marker_list .= '<input ' . $checked . ' type="radio" name="wpsl_map[' . $location . '_marker]" value="' . $marker_img . '" />';
914
- $marker_list .= '</li>';
915
-
916
- return $marker_list;
917
- }
918
-
919
- /**
920
- * Get the default values for the marker clusters dropdown options.
921
- *
922
- * @since 1.2.20
923
- * @param string $type The cluster option type
924
- * @return string $cluster_values The default cluster options
925
- */
926
- public function get_default_cluster_option( $type ) {
927
-
928
- $cluster_values = array(
929
- 'cluster_zoom' => array(
930
- '7',
931
- '8',
932
- '9',
933
- '10',
934
- '11',
935
- '12',
936
- '13'
937
- ),
938
- 'cluster_size' => array(
939
- '40',
940
- '50',
941
- '60',
942
- '70',
943
- '80'
944
- ),
945
- );
946
-
947
- return $cluster_values[$type];
948
- }
949
-
950
- /**
951
- * Create a dropdown for the marker cluster options.
952
- *
953
- * @since 1.2.20
954
- * @param string $type The cluster option type
955
- * @return string $dropdown The html for the distance option list
956
- */
957
- public function show_cluster_options( $type ) {
958
-
959
- global $wpsl_settings;
960
-
961
- $cluster_options = array(
962
- 'cluster_zoom' => array(
963
- 'id' => 'wpsl-marker-zoom',
964
- 'name' => 'cluster_zoom',
965
- 'options' => $this->get_default_cluster_option( $type )
966
- ),
967
- 'cluster_size' => array(
968
- 'id' => 'wpsl-marker-cluster-size',
969
- 'name' => 'cluster_size',
970
- 'options' => $this->get_default_cluster_option( $type )
971
- ),
972
- );
973
-
974
- $dropdown = '<select id="' . esc_attr( $cluster_options[$type]['id'] ) . '" name="wpsl_map[' . esc_attr( $cluster_options[$type]['name'] ) . ']" autocomplete="off">';
975
-
976
- $i = 0;
977
- foreach ( $cluster_options[$type]['options'] as $item => $value ) {
978
- $selected = ( $wpsl_settings[$type] == $value ) ? 'selected="selected"' : '';
979
-
980
- if ( $i == 0 ) {
981
- $dropdown .= "<option value='0' $selected>" . __( 'Default', 'wpsl' ) . "</option>";
982
- } else {
983
- $dropdown .= "<option value=". absint( $value ) . " $selected>" . absint( $value ) . "</option>";
984
- }
985
-
986
- $i++;
987
- }
988
-
989
- $dropdown .= "</select>";
990
-
991
- return $dropdown;
992
- }
993
-
994
- /**
995
- * Show the options of the start and store markers.
996
- *
997
- * @since 1.0.0
998
- * @return string $marker_list The complete list of available and selected markers
999
- */
1000
- public function show_marker_options() {
1001
-
1002
- $marker_list = '';
1003
- $marker_images = $this->get_available_markers();
1004
- $marker_locations = array(
1005
- 'start',
1006
- 'store'
1007
- );
1008
-
1009
- foreach ( $marker_locations as $location ) {
1010
- if ( $location == 'start' ) {
1011
- $marker_list .= __( 'Start location marker', 'wpsl' ) . ':';
1012
- } else {
1013
- $marker_list .= __( 'Store location marker', 'wpsl' ) . ':';
1014
- }
1015
-
1016
- if ( !empty( $marker_images ) ) {
1017
- $marker_list .= '<ul class="wpsl-marker-list">';
1018
-
1019
- foreach ( $marker_images as $marker_img ) {
1020
- $marker_list .= $this->create_marker_html( $marker_img, $location );
1021
- }
1022
-
1023
- $marker_list .= '</ul>';
1024
- }
1025
- }
1026
-
1027
- return $marker_list;
1028
- }
1029
-
1030
- /**
1031
- * Load the markers that are used on the map.
1032
- *
1033
- * @since 1.0.0
1034
- * @return array $marker_images A list of all the available markers.
1035
- */
1036
- public function get_available_markers() {
1037
-
1038
- $marker_images = array();
1039
- $dir = apply_filters( 'wpsl_admin_marker_dir', WPSL_PLUGIN_DIR . 'img/markers/' );
1040
-
1041
- if ( is_dir( $dir ) ) {
1042
- if ( $dh = opendir( $dir ) ) {
1043
- while ( false !== ( $file = readdir( $dh ) ) ) {
1044
- if ( $file == '.' || $file == '..' || ( strpos( $file, '@2x' ) !== false ) ) continue;
1045
- $marker_images[] = $file;
1046
- }
1047
-
1048
- closedir( $dh );
1049
- }
1050
- }
1051
-
1052
- return $marker_images;
1053
- }
1054
-
1055
- /**
1056
- * Show a list of available templates.
1057
- *
1058
- * @since 1.2.20
1059
- * @return string $dropdown The html for the template option list
1060
- */
1061
- public function show_template_options() {
1062
-
1063
- global $wpsl_settings;
1064
-
1065
- $dropdown = '<select id="wpsl-store-template" name="wpsl_ux[template_id]" autocomplete="off">';
1066
-
1067
- foreach ( wpsl_get_templates() as $template ) {
1068
- $template_id = ( isset( $template['id'] ) ) ? $template['id'] : '';
1069
-
1070
- $selected = ( $wpsl_settings['template_id'] == $template_id ) ? ' selected="selected"' : '';
1071
- $dropdown .= "<option value='" . esc_attr( $template_id ) . "' $selected>" . esc_html( $template['name'] ) . "</option>";
1072
- }
1073
-
1074
- $dropdown .= '</select>';
1075
-
1076
- return $dropdown;
1077
- }
1078
-
1079
- /**
1080
- * Create dropdown lists.
1081
- *
1082
- * @since 2.0.0
1083
- * @param string $type The type of dropdown
1084
- * @return string $dropdown The html output for the dropdown
1085
- */
1086
- public function create_dropdown( $type ) {
1087
-
1088
- global $wpsl_settings;
1089
-
1090
- $dropdown_lists = apply_filters( 'wpsl_setting_dropdowns', array(
1091
- 'hour_input' => array(
1092
- 'values' => array(
1093
- 'textarea' => __( 'Textarea', 'wpsl' ),
1094
- 'dropdown' => __( 'Dropdowns (recommended)', 'wpsl' )
1095
- ),
1096
- 'id' => 'wpsl-editor-hour-input',
1097
- 'name' => 'wpsl_editor[hour_input]',
1098
- 'selected' => $wpsl_settings['editor_hour_input']
1099
- ),
1100
- 'marker_effects' => array(
1101
- 'values' => array(
1102
- 'bounce' => __( 'Bounces up and down', 'wpsl' ),
1103
- 'info_window' => __( 'Will open the info window', 'wpsl' ),
1104
- 'ignore' => __( 'Does not respond', 'wpsl' )
1105
- ),
1106
- 'id' => 'wpsl-marker-effect',
1107
- 'name' => 'wpsl_ux[marker_effect]',
1108
- 'selected' => $wpsl_settings['marker_effect']
1109
- ),
1110
- 'more_info' => array(
1111
- 'values' => array(
1112
- 'store listings' => __( 'In the store listings', 'wpsl' ),
1113
- 'info window' => __( 'In the info window on the map', 'wpsl' )
1114
- ),
1115
- 'id' => 'wpsl-more-info-list',
1116
- 'name' => 'wpsl_ux[more_info_location]',
1117
- 'selected' => $wpsl_settings['more_info_location']
1118
- ),
1119
- 'map_types' => array(
1120
- 'values' => wpsl_get_map_types(),
1121
- 'id' => 'wpsl-map-type',
1122
- 'name' => 'wpsl_map[type]',
1123
- 'selected' => $wpsl_settings['map_type']
1124
- ),
1125
- 'editor_map_types' => array(
1126
- 'values' => wpsl_get_map_types(),
1127
- 'id' => 'wpsl-editor-map-type',
1128
- 'name' => 'wpsl_editor[map_type]',
1129
- 'selected' => $wpsl_settings['editor_map_type']
1130
- ),
1131
- 'max_zoom_level' => array(
1132
- 'values' => wpsl_get_max_zoom_levels(),
1133
- 'id' => 'wpsl-max-auto-zoom',
1134
- 'name' => 'wpsl_map[max_auto_zoom]',
1135
- 'selected' => $wpsl_settings['auto_zoom_level']
1136
- ),
1137
- 'address_format' => array(
1138
- 'values' => wpsl_get_address_formats(),
1139
- 'id' => 'wpsl-address-format',
1140
- 'name' => 'wpsl_ux[address_format]',
1141
- 'selected' => $wpsl_settings['address_format']
1142
- ),
1143
- 'filter_types' => array(
1144
- 'values' => array(
1145
- 'dropdown' => __( 'Dropdown', 'wpsl' ),
1146
- 'checkboxes' => __( 'Checkboxes', 'wpsl' )
1147
- ),
1148
- 'id' => 'wpsl-cat-filter-types',
1149
- 'name' => 'wpsl_search[category_filter_type]',
1150
- 'selected' => $wpsl_settings['category_filter_type']
1151
- )
1152
- ) );
1153
-
1154
- $dropdown = '<select id="' . esc_attr( $dropdown_lists[$type]['id'] ) . '" name="' . esc_attr( $dropdown_lists[$type]['name'] ) . '" autocomplete="off">';
1155
-
1156
- foreach ( $dropdown_lists[$type]['values'] as $key => $value ) {
1157
- $selected = ( $key == $dropdown_lists[$type]['selected'] ) ? 'selected="selected"' : '';
1158
- $dropdown .= "<option value='" . esc_attr( $key ) . "' $selected>" . esc_html( $value ) . "</option>";
1159
- }
1160
-
1161
- $dropdown .= '</select>';
1162
-
1163
- return $dropdown;
1164
- }
1165
-
1166
- /**
1167
- * Create a dropdown for the 12/24 opening hours format.
1168
- *
1169
- * @since 2.0.0
1170
- * @param string $hour_format The hour format that should be set to selected
1171
- * @return string $dropdown The html for the dropdown
1172
- */
1173
- public function show_opening_hours_format( $hour_format = '' ) {
1174
-
1175
- global $wpsl_settings;
1176
-
1177
- $items = array(
1178
- '12' => __( '12 Hours', 'wpsl' ),
1179
- '24' => __( '24 Hours', 'wpsl' )
1180
- );
1181
-
1182
- if ( !absint( $hour_format ) ) {
1183
- $hour_format = $wpsl_settings['editor_hour_format'];
1184
- }
1185
-
1186
- $dropdown = '<select id="wpsl-editor-hour-format" name="wpsl_editor[hour_format]" autocomplete="off">';
1187
-
1188
- foreach ( $items as $key => $value ) {
1189
- $selected = ( $hour_format == $key ) ? 'selected="selected"' : '';
1190
- $dropdown .= "<option value='$key' $selected>" . esc_html( $value ) . "</option>";
1191
- }
1192
-
1193
- $dropdown .= '</select>';
1194
-
1195
- return $dropdown;
1196
- }
1197
- }
1198
  }
1
+ <?php
2
+ /**
3
+ * Handle the plugin settings.
4
+ *
5
+ * @author Tijmen Smit
6
+ * @since 2.0.0
7
+ */
8
+
9
+ if ( !defined( 'ABSPATH' ) ) exit;
10
+
11
+ if ( !class_exists( 'WPSL_Settings' ) ) {
12
+
13
+ class WPSL_Settings {
14
+
15
+ public function __construct() {
16
+
17
+ $this->manually_clear_transient();
18
+
19
+ add_action( 'wp_ajax_validate_server_key', array( $this, 'ajax_validate_server_key' ) );
20
+ add_action( 'wp_ajax_nopriv_validate_server_key', array( $this, 'ajax_validate_server_key' ) );
21
+ add_action( 'admin_init', array( $this, 'register_settings' ) );
22
+ add_action( 'admin_init', array( $this, 'maybe_flush_rewrite_and_transient' ) );
23
+ }
24
+
25
+ /**
26
+ * Determine if we need to clear the autoload transient.
27
+ *
28
+ * User can do this manually from the 'Tools' section on the settings page.
29
+ *
30
+ * @since 2.0.0
31
+ * @return void
32
+ */
33
+ public function manually_clear_transient() {
34
+
35
+ global $wpsl_admin;
36
+
37
+ if ( isset( $_GET['action'] ) && $_GET['action'] == 'clear_wpsl_transients' && isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'clear_transients' ) ) {
38
+ $wpsl_admin->delete_autoload_transient();
39
+
40
+ $msg = __( 'WP Store Locator Transients Cleared', 'wpsl' );
41
+ $wpsl_admin->notices->save( 'update', $msg );
42
+
43
+ /*
44
+ * Make sure the &action=clear_wpsl_transients param is removed from the url.
45
+ *
46
+ * Otherwise if the user later clicks the 'Save Changes' button,
47
+ * and the &action=clear_wpsl_transients param is still there it
48
+ * will show two notices 'WP Store Locator Transients Cleared' and 'Settings Saved'.
49
+ */
50
+ wp_redirect( admin_url( 'edit.php?post_type=wpsl_stores&page=wpsl_settings' ) );
51
+ exit;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Register the settings.
57
+ *
58
+ * @since 2.0.0
59
+ * @return void
60
+ */
61
+ public function register_settings() {
62
+ register_setting( 'wpsl_settings', 'wpsl_settings', array( $this, 'sanitize_settings' ) );
63
+ }
64
+
65
+ /**
66
+ * Sanitize the submitted plugin settings.
67
+ *
68
+ * @since 1.0.0
69
+ * @return array $output The setting values
70
+ */
71
+ public function sanitize_settings() {
72
+
73
+ global $wpsl_settings, $wpsl_admin;
74
+
75
+ $ux_absints = array(
76
+ 'height',
77
+ 'infowindow_width',
78
+ 'search_width',
79
+ 'label_width'
80
+ );
81
+
82
+ $marker_effects = array(
83
+ 'bounce',
84
+ 'info_window',
85
+ 'ignore'
86
+ );
87
+
88
+ $ux_checkboxes = array(
89
+ 'new_window',
90
+ 'reset_map',
91
+ 'listing_below_no_scroll',
92
+ 'direction_redirect',
93
+ 'more_info',
94
+ 'store_url',
95
+ 'phone_url',
96
+ 'marker_streetview',
97
+ 'marker_zoom_to',
98
+ 'mouse_focus',
99
+ 'reset_map',
100
+ 'show_contact_details',
101
+ 'clickable_contact_details',
102
+ 'hide_country',
103
+ 'hide_distance'
104
+ );
105
+
106
+ /*
107
+ * If the provided server key is different then the existing value,
108
+ * then we test if it's valid by making a call to the Geocode API.
109
+ */
110
+ if ( $_POST['wpsl_api']['server_key'] && $wpsl_settings['api_server_key'] != $_POST['wpsl_api']['server_key'] || !get_option( 'wpsl_valid_server_key' ) ) {
111
+ $server_key = sanitize_text_field( $_POST['wpsl_api']['server_key'] );
112
+
113
+ $this->validate_server_key( $server_key );
114
+ }
115
+
116
+ $output['api_server_key'] = sanitize_text_field( $_POST['wpsl_api']['server_key'] );
117
+ $output['api_browser_key'] = sanitize_text_field( $_POST['wpsl_api']['browser_key'] );
118
+ $output['api_language'] = wp_filter_nohtml_kses( $_POST['wpsl_api']['language'] );
119
+ $output['api_region'] = wp_filter_nohtml_kses( $_POST['wpsl_api']['region'] );
120
+ $output['api_geocode_component'] = isset( $_POST['wpsl_api']['geocode_component'] ) ? 1 : 0;
121
+
122
+ // Check the search filter.
123
+ $output['autocomplete'] = isset( $_POST['wpsl_search']['autocomplete'] ) ? 1 : 0;
124
+ $output['results_dropdown'] = isset( $_POST['wpsl_search']['results_dropdown'] ) ? 1 : 0;
125
+ $output['radius_dropdown'] = isset( $_POST['wpsl_search']['radius_dropdown'] ) ? 1 : 0;
126
+ $output['category_filter'] = isset( $_POST['wpsl_search']['category_filter'] ) ? 1 : 0;
127
+ $output['category_filter_type'] = ( $_POST['wpsl_search']['category_filter_type'] == 'dropdown' ) ? 'dropdown' : 'checkboxes';
128
+
129
+ $output['distance_unit'] = ( $_POST['wpsl_search']['distance_unit'] == 'km' ) ? 'km' : 'mi';
130
+
131
+ // Check for a valid max results value, otherwise we use the default.
132
+ if ( !empty( $_POST['wpsl_search']['max_results'] ) ) {
133
+ $output['max_results'] = sanitize_text_field( $_POST['wpsl_search']['max_results'] );
134
+ } else {
135
+ $this->settings_error( 'max_results' );
136
+ $output['max_results'] = wpsl_get_default_setting( 'max_results' );
137
+ }
138
+
139
+ // See if a search radius value exist, otherwise we use the default.
140
+ if ( !empty( $_POST['wpsl_search']['radius'] ) ) {
141
+ $output['search_radius'] = sanitize_text_field( $_POST['wpsl_search']['radius'] );
142
+ } else {
143
+ $this->settings_error( 'search_radius' );
144
+ $output['search_radius'] = wpsl_get_default_setting( 'search_radius' );
145
+ }
146
+
147
+ // Check if we have a valid zoom level, it has to be between 1 or 12. If not set it to the default of 3.
148
+ $output['zoom_level'] = wpsl_valid_zoom_level( $_POST['wpsl_map']['zoom_level'] );
149
+
150
+ // Check for a valid max auto zoom level.
151
+ $max_zoom_levels = wpsl_get_max_zoom_levels();
152
+
153
+ if ( in_array( absint( $_POST['wpsl_map']['max_auto_zoom'] ), $max_zoom_levels ) ) {
154
+ $output['auto_zoom_level'] = $_POST['wpsl_map']['max_auto_zoom'];
155
+ } else {
156
+ $output['auto_zoom_level'] = wpsl_get_default_setting( 'auto_zoom_level' );
157
+ }
158
+
159
+ if ( isset( $_POST['wpsl_map']['start_name'] ) ) {
160
+ $output['start_name'] = sanitize_text_field( $_POST['wpsl_map']['start_name'] );
161
+ } else {
162
+ $output['start_name'] = '';
163
+ }
164
+
165
+ // If no location name is then we also empty the latlng values from the hidden input field.
166
+ if ( empty( $output['start_name'] ) ) {
167
+ $this->settings_error( 'start_point' );
168
+ $output['start_latlng'] = '';
169
+ } else {
170
+
171
+ /*
172
+ * If the start latlng is empty, but a start location name is provided,
173
+ * then make a request to the Geocode API to get it.
174
+ *
175
+ * This can only happen if there is a JS error in the admin area that breaks the
176
+ * Google Maps Autocomplete. So this code is only used as fallback to make sure
177
+ * the provided start location is always geocoded.
178
+ */
179
+ if ( $wpsl_settings['start_name'] != $_POST['wpsl_map']['start_name']
180
+ && $wpsl_settings['start_latlng'] == $_POST['wpsl_map']['start_latlng']
181
+ || empty( $_POST['wpsl_map']['start_latlng'] ) ) {
182
+ $start_latlng = wpsl_get_address_latlng( $_POST['wpsl_map']['start_name'] );
183
+ } else {
184
+ $start_latlng = sanitize_text_field( $_POST['wpsl_map']['start_latlng'] );
185
+ }
186
+
187
+ $output['start_latlng'] = $start_latlng;
188
+ }
189
+
190
+ // Do we need to run the fitBounds function make the markers fit in the viewport?
191
+ $output['run_fitbounds'] = isset( $_POST['wpsl_map']['run_fitbounds'] ) ? 1 : 0;
192
+
193
+ // Check if we have a valid map type.
194
+ $output['map_type'] = wpsl_valid_map_type( $_POST['wpsl_map']['type'] );
195
+ $output['auto_locate'] = isset( $_POST['wpsl_map']['auto_locate'] ) ? 1 : 0;
196
+ $output['autoload'] = isset( $_POST['wpsl_map']['autoload'] ) ? 1 : 0;
197
+
198
+ // Make sure the auto load limit is either empty or an int.
199
+ if ( empty( $_POST['wpsl_map']['autoload_limit'] ) ) {
200
+ $output['autoload_limit'] = '';
201
+ } else {
202
+ $output['autoload_limit'] = absint( $_POST['wpsl_map']['autoload_limit'] );
203
+ }
204
+
205
+ $output['streetview'] = isset( $_POST['wpsl_map']['streetview'] ) ? 1 : 0;
206
+ $output['type_control'] = isset( $_POST['wpsl_map']['type_control'] ) ? 1 : 0;
207
+ $output['scrollwheel'] = isset( $_POST['wpsl_map']['scrollwheel'] ) ? 1 : 0;
208
+ $output['control_position'] = ( $_POST['wpsl_map']['control_position'] == 'left' ) ? 'left' : 'right';
209
+
210
+ $output['map_style'] = json_encode( strip_tags( trim( $_POST['wpsl_map']['map_style'] ) ) );
211
+
212
+ // Make sure we have a valid template ID.
213
+ if ( isset( $_POST['wpsl_ux']['template_id'] ) && ( $_POST['wpsl_ux']['template_id'] ) ) {
214
+ $output['template_id'] = sanitize_text_field( $_POST['wpsl_ux']['template_id'] );
215
+ } else {
216
+ $output['template_id'] = wpsl_get_default_setting( 'template_id' );
217
+ }
218
+
219
+ $output['marker_clusters'] = isset( $_POST['wpsl_map']['marker_clusters'] ) ? 1 : 0;
220
+
221
+ // Check for a valid cluster zoom value.
222
+ if ( in_array( $_POST['wpsl_map']['cluster_zoom'], $this->get_default_cluster_option( 'cluster_zoom' ) ) ) {
223
+ $output['cluster_zoom'] = $_POST['wpsl_map']['cluster_zoom'];
224
+ } else {
225
+ $output['cluster_zoom'] = wpsl_get_default_setting( 'cluster_zoom' );
226
+ }
227
+
228
+ // Check for a valid cluster size value.
229
+ if ( in_array( $_POST['wpsl_map']['cluster_size'], $this->get_default_cluster_option( 'cluster_size' ) ) ) {
230
+ $output['cluster_size'] = $_POST['wpsl_map']['cluster_size'];
231
+ } else {
232
+ $output['cluster_size'] = wpsl_get_default_setting( 'cluster_size' );
233
+ }
234
+
235
+ /*
236
+ * Make sure all the ux related fields that should contain an int, actually are an int.
237
+ * Otherwise we use the default value.
238
+ */
239
+ foreach ( $ux_absints as $ux_key ) {
240
+ if ( absint( $_POST['wpsl_ux'][$ux_key] ) ) {
241
+ $output[$ux_key] = $_POST['wpsl_ux'][$ux_key];
242
+ } else {
243
+ $output[$ux_key] = wpsl_get_default_setting( $ux_key );
244
+ }
245
+ }
246
+
247
+ // Check if the ux checkboxes are checked.
248
+ foreach ( $ux_checkboxes as $ux_key ) {
249
+ $output[$ux_key] = isset( $_POST['wpsl_ux'][$ux_key] ) ? 1 : 0;
250
+ }
251
+
252
+ // Check if we have a valid marker effect.
253
+ if ( in_array( $_POST['wpsl_ux']['marker_effect'], $marker_effects ) ) {
254
+ $output['marker_effect'] = $_POST['wpsl_ux']['marker_effect'];
255
+ } else {
256
+ $output['marker_effect'] = wpsl_get_default_setting( 'marker_effect' );
257
+ }
258
+
259
+ // Check if we have a valid address format.
260
+ if ( array_key_exists( $_POST['wpsl_ux']['address_format'], wpsl_get_address_formats() ) ) {
261
+ $output['address_format'] = $_POST['wpsl_ux']['address_format'];
262
+ } else {
263
+ $output['address_format'] = wpsl_get_default_setting( 'address_format' );
264
+ }
265
+
266
+ $output['more_info_location'] = ( $_POST['wpsl_ux']['more_info_location'] == 'store listings' ) ? 'store listings' : 'info window';
267
+ $output['infowindow_style'] = isset( $_POST['wpsl_ux']['infowindow_style'] ) ? 'default' : 'infobox';
268
+ $output['start_marker'] = wp_filter_nohtml_kses( $_POST['wpsl_map']['start_marker'] );
269
+ $output['store_marker'] = wp_filter_nohtml_kses( $_POST['wpsl_map']['store_marker'] );
270
+ $output['editor_country'] = sanitize_text_field( $_POST['wpsl_editor']['default_country'] );
271
+ $output['editor_map_type'] = wpsl_valid_map_type( $_POST['wpsl_editor']['map_type'] );
272
+ $output['hide_hours'] = isset( $_POST['wpsl_editor']['hide_hours'] ) ? 1 : 0;
273
+
274
+ if ( isset( $_POST['wpsl_editor']['hour_input'] ) ) {
275
+ $output['editor_hour_input'] = ( $_POST['wpsl_editor']['hour_input'] == 'textarea' ) ? 'textarea' : 'dropdown';
276
+ } else {
277
+ $output['editor_hour_input'] = 'dropdown';
278
+ }
279
+
280
+ $output['editor_hour_format'] = ( $_POST['wpsl_editor']['hour_format'] == 12 ) ? 12 : 24;
281
+
282
+ // The default opening hours.
283
+ if ( isset( $_POST['wpsl_editor']['textarea'] ) ) {
284
+ $output['editor_hours']['textarea'] = wp_kses_post( trim( stripslashes( $_POST['wpsl_editor']['textarea'] ) ) );
285
+ }
286
+
287
+ $output['editor_hours']['dropdown'] = $wpsl_admin->metaboxes->format_opening_hours();
288
+ array_walk_recursive( $output['editor_hours']['dropdown'], 'wpsl_sanitize_multi_array' );
289
+
290
+ // Permalink and taxonomy slug.
291
+ $output['permalinks'] = isset( $_POST['wpsl_permalinks']['active'] ) ? 1 : 0;
292
+
293
+ if ( !empty( $_POST['wpsl_permalinks']['slug'] ) ) {
294
+ $output['permalink_slug'] = sanitize_text_field( $_POST['wpsl_permalinks']['slug'] );
295
+ } else {
296
+ $output['permalink_slug'] = wpsl_get_default_setting( 'permalink_slug' );
297
+ }
298
+
299
+ if ( !empty( $_POST['wpsl_permalinks']['category_slug'] ) ) {
300
+ $output['category_slug'] = sanitize_text_field( $_POST['wpsl_permalinks']['category_slug'] );
301
+ } else {
302
+ $output['category_slug'] = wpsl_get_default_setting( 'category_slug' );
303
+ }
304
+
305
+ $required_labels = wpsl_labels();
306
+
307
+ // Sanitize the labels.
308
+ foreach ( $required_labels as $label ) {
309
+ $output[$label.'_label'] = sanitize_text_field( $_POST['wpsl_label'][$label] );
310
+ }
311
+
312
+ $output['show_credits'] = isset( $_POST['wpsl_credits'] ) ? 1 : 0;
313
+ $output['debug'] = isset( $_POST['wpsl_tools']['debug'] ) ? 1 : 0;
314
+ $output['deregister_gmaps'] = isset( $_POST['wpsl_tools']['deregister_gmaps'] ) ? 1 : 0;
315
+
316
+ // Check if we need to flush the permalinks.
317
+ $this->set_flush_rewrite_option( $output );
318
+
319
+ // Check if there is a reason to delete the autoload transient.
320
+ if ( $wpsl_settings['autoload'] ) {
321
+ $this->set_delete_transient_option( $output );
322
+ }
323
+
324
+ return $output;
325
+ }
326
+
327
+ /**
328
+ * Handle the AJAX call to validate the provided
329
+ * server key for the Google Maps API.
330
+ *
331
+ * @since 2.2.10
332
+ * @return void
333
+ */
334
+ public function ajax_validate_server_key() {
335
+
336
+ if ( ( current_user_can( 'manage_wpsl_settings' ) ) && is_admin() ) {
337
+ $server_key = sanitize_text_field( $_GET['server_key'] );
338
+
339
+ if ( $server_key ) {
340
+ $this->validate_server_key( $server_key );
341
+ }
342
+ }
343
+ }
344
+
345
+ /**
346
+ * Check if the provided server key for
347
+ * the Google Maps API is valid.
348
+ *
349
+ * @since 2.2.10
350
+ * @param string $server_key The server key to validate
351
+ * @return json|void If the validation failed and AJAX is used, then json
352
+ */
353
+ public function validate_server_key( $server_key ) {
354
+
355
+ global $wpsl_admin;
356
+
357
+ // Test the server key by making a request to the Geocode API.
358
+ $address = 'Manhattan, NY 10036, USA';
359
+ $url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' . urlencode( $address ) .'&key=' . $server_key;
360
+ $response = wp_remote_get( $url );
361
+
362
+ if ( !is_wp_error( $response ) ) {
363
+ $response = json_decode( $response['body'], true );
364
+
365
+ // If the state is not OK, then there's a problem with the key.
366
+ if ( $response['status'] !== 'OK' ) {
367
+ $geocode_errors = $wpsl_admin->geocode->check_geocode_error_msg( $response, true );
368
+ $error_msg = sprintf( __( 'There\'s a problem with the provided %sserver key%s. %s' ), '<a href="https://wpstorelocator.co/document/create-google-api-keys/#server-key">', '</a>', $geocode_errors );
369
+
370
+ update_option( 'wpsl_valid_server_key', 0 );
371
+
372
+ // If the server key input field has 'wpsl-validate-me' class on it, then it's validated with AJAX in the background.
373
+ if ( defined('DOING_AJAX' ) && DOING_AJAX ) {
374
+ $key_status = array(
375
+ 'valid' => 0,
376
+ 'msg' => $error_msg
377
+ );
378
+
379
+ wp_send_json( $key_status );
380
+
381
+ exit();
382
+ } else {
383
+ add_settings_error( 'setting-errors', esc_attr( 'server-key' ), $error_msg, 'error' );
384
+ }
385
+ } else {
386
+ update_option( 'wpsl_valid_server_key', 1 );
387
+ }
388
+ }
389
+ }
390
+
391
+ /**
392
+ * Check if we need set the option that will be used to determine
393
+ * if we need to flush the permalinks once the setting page reloads.
394
+ *
395
+ * @since 2.0.0
396
+ * @param array $new_settings The submitted plugin settings
397
+ * @return void
398
+ */
399
+ public function set_flush_rewrite_option( $new_settings ) {
400
+
401
+ global $wpsl_settings;
402
+
403
+ if ( ( $wpsl_settings['permalinks'] != $new_settings['permalinks'] ) || ( $wpsl_settings['permalink_slug'] != $new_settings['permalink_slug'] ) || ( $wpsl_settings['category_slug'] != $new_settings['category_slug'] ) ) {
404
+ update_option( 'wpsl_flush_rewrite', 1 );
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Check if we need set the option that is used to determine
410
+ * if we need to delete the autoload transient once the settings page reloads.
411
+ *
412
+ * @since 2.0.0
413
+ * @param array $new_settings The submitted plugin settings
414
+ * @return void
415
+ */
416
+ public function set_delete_transient_option( $new_settings ) {
417
+
418
+ global $wpsl_settings;
419
+
420
+ // The options we need to check for changes.
421
+ $options = array(
422
+ 'start_name',
423
+ 'debug',
424
+ 'autoload',
425
+ 'autoload_limit',
426
+ 'more_info',
427
+ 'more_info_location',
428
+ 'hide_hours',
429
+ 'hide_distance',
430
+ 'hide_country',
431
+ 'show_contact_details'
432
+ );
433
+
434
+ foreach ( $options as $option_name ) {
435
+ if ( $wpsl_settings[$option_name] != $new_settings[$option_name] ) {
436
+ update_option( 'wpsl_delete_transient', 1 );
437
+ break;
438
+ }
439
+ }
440
+ }
441
+
442
+ /**
443
+ * Check if the permalinks settings changed.
444
+ *
445
+ * @since 2.0.0
446
+ * @return void
447
+ */
448
+ public function maybe_flush_rewrite_and_transient() {
449
+
450
+ global $wpsl_admin;
451
+
452
+ if ( isset( $_GET['page'] ) && ( $_GET['page'] == 'wpsl_settings' ) ) {
453
+ $flush_rewrite = get_option( 'wpsl_flush_rewrite' );
454
+ $delete_transient = get_option( 'wpsl_delete_transient' );
455
+
456
+ if ( $flush_rewrite ) {
457
+ flush_rewrite_rules();
458
+ update_option( 'wpsl_flush_rewrite', 0 );
459
+ }
460
+
461
+ if ( $delete_transient ) {
462
+ update_option( 'wpsl_delete_transient', 0 );
463
+ }
464
+
465
+ if ( $flush_rewrite || $delete_transient ) {
466
+ $wpsl_admin->delete_autoload_transient();
467
+ }
468
+ }
469
+ }
470
+
471
+ /**
472
+ * Handle the different validation errors for the plugin settings.
473
+ *
474
+ * @since 1.0.0
475
+ * @param string $error_type Contains the type of validation error that occured
476
+ * @return void
477
+ */
478
+ private function settings_error( $error_type ) {
479
+
480
+ switch ( $error_type ) {
481
+ case 'max_results':
482
+ $error_msg = __( 'The max results field cannot be empty, the default value has been restored.', 'wpsl' );
483
+ break;
484
+ case 'search_radius':
485
+ $error_msg = __( 'The search radius field cannot be empty, the default value has been restored.', 'wpsl' );
486
+ break;
487
+ case 'start_point':
488
+ $error_msg = sprintf( __( 'Please provide the name of a city or country that can be used as a starting point under "Map Settings". %s This will only be used if auto-locating the user fails, or the option itself is disabled.', 'wpsl' ), '<br><br>' );
489
+ break;
490
+ }
491
+
492
+ add_settings_error( 'setting-errors', esc_attr( 'settings_fail' ), $error_msg, 'error' );
493
+ }
494
+
495
+ /**
496
+ * Options for the language and region list.
497
+ *
498
+ * @since 1.0.0
499
+ * @param string $list The request list type
500
+ * @return string|void $option_list The html for the selected list, or nothing if the $list contains invalud values
501
+ */
502
+ public function get_api_option_list( $list ) {
503
+
504
+ global $wpsl_settings;
505
+
506
+ switch ( $list ) {
507
+ case 'language':
508
+ $api_option_list = array (
509
+ __('Select your language', 'wpsl') => '',
510
+ __('English', 'wpsl') => 'en',
511
+ __('Arabic', 'wpsl') => 'ar',
512
+ __('Basque', 'wpsl') => 'eu',
513
+ __('Bulgarian', 'wpsl') => 'bg',
514
+ __('Bengali', 'wpsl') => 'bn',
515
+ __('Catalan', 'wpsl') => 'ca',
516
+ __('Czech', 'wpsl') => 'cs',
517
+ __('Danish', 'wpsl') => 'da',
518
+ __('German', 'wpsl') => 'de',
519
+ __('Greek', 'wpsl') => 'el',
520
+ __('English (Australian)', 'wpsl') => 'en-AU',
521
+ __('English (Great Britain)', 'wpsl') => 'en-GB',
522
+ __('Spanish', 'wpsl') => 'es',
523
+ __('Farsi', 'wpsl') => 'fa',
524
+ __('Finnish', 'wpsl') => 'fi',
525
+ __('Filipino', 'wpsl') => 'fil',
526
+ __('French', 'wpsl') => 'fr',
527
+ __('Galician', 'wpsl') => 'gl',
528
+ __('Gujarati', 'wpsl') => 'gu',
529
+ __('Hindi', 'wpsl') => 'hi',
530
+ __('Croatian', 'wpsl') => 'hr',
531
+ __('Hungarian', 'wpsl') => 'hu',
532
+ __('Indonesian', 'wpsl') => 'id',
533
+ __('Italian', 'wpsl') => 'it',
534
+ __('Hebrew', 'wpsl') => 'iw',
535
+ __('Japanese', 'wpsl') => 'ja',
536
+ __('Kannada', 'wpsl') => 'kn',
537
+ __('Korean', 'wpsl') => 'ko',
538
+ __('Lithuanian', 'wpsl') => 'lt',
539
+ __('Latvian', 'wpsl') => 'lv',
540
+ __('Malayalam', 'wpsl') => 'ml',
541
+ __('Marathi', 'wpsl') => 'mr',
542
+ __('Dutch', 'wpsl') => 'nl',
543
+ __('Norwegian', 'wpsl') => 'no',
544
+ __('Norwegian Nynorsk', 'wpsl') => 'nn',
545
+ __('Polish', 'wpsl') => 'pl',
546
+ __('Portuguese', 'wpsl') => 'pt',
547
+ __('Portuguese (Brazil)', 'wpsl') => 'pt-BR',
548
+ __('Portuguese (Portugal)', 'wpsl') => 'pt-PT',
549
+ __('Romanian', 'wpsl') => 'ro',
550
+ __('Russian', 'wpsl') => 'ru',
551
+ __('Slovak', 'wpsl') => 'sk',
552
+ __('Slovenian', 'wpsl') => 'sl',
553
+ __('Serbian', 'wpsl') => 'sr',
554
+ __('Swedish', 'wpsl') => 'sv',
555
+ __('Tagalog', 'wpsl') => 'tl',
556
+ __('Tamil', 'wpsl') => 'ta',
557
+ __('Telugu', 'wpsl') => 'te',
558
+ __('Thai', 'wpsl') => 'th',
559
+ __('Turkish', 'wpsl') => 'tr',
560
+ __('Ukrainian', 'wpsl') => 'uk',
561
+ __('Vietnamese', 'wpsl') => 'vi',
562
+ __('Chinese (Simplified)', 'wpsl') => 'zh-CN',
563
+ __('Chinese (Traditional)' ,'wpsl') => 'zh-TW'
564
+ );
565
+ break;
566
+ case 'region':
567
+ $api_option_list = array (
568
+ __('Select your region', 'wpsl') => '',
569
+ __('Afghanistan', 'wpsl') => 'af',
570
+ __('Albania', 'wpsl') => 'al',
571
+ __('Algeria', 'wpsl') => 'dz',
572
+ __('American Samoa', 'wpsl') => 'as',
573
+ __('Andorra', 'wpsl') => 'ad',
574
+ __('Angola', 'wpsl') => 'ao',
575
+ __('Anguilla', 'wpsl') => 'ai',
576
+ __('Antarctica', 'wpsl') => 'aq',
577
+ __('Antigua and Barbuda', 'wpsl') => 'ag',
578
+ __('Argentina', 'wpsl') => 'ar',
579
+ __('Armenia', 'wpsl') => 'am',
580
+ __('Aruba', 'wpsl') => 'aw',
581
+ __('Ascension Island', 'wpsl') => 'ac',
582
+ __('Australia', 'wpsl') => 'au',
583
+ __('Austria', 'wpsl') => 'at',
584
+ __('Azerbaijan', 'wpsl') => 'az',
585
+ __('Bahamas', 'wpsl') => 'bs',
586
+ __('Bahrain', 'wpsl') => 'bh',
587
+ __('Bangladesh', 'wpsl') => 'bd',
588
+ __('Barbados', 'wpsl') => 'bb',
589
+ __('Belarus', 'wpsl') => 'by',
590
+ __('Belgium', 'wpsl') => 'be',
591
+ __('Belize', 'wpsl') => 'bz',
592
+ __('Benin', 'wpsl') => 'bj',
593
+ __('Bermuda', 'wpsl') => 'bm',
594
+ __('Bhutan', 'wpsl') => 'bt',
595
+ __('Bolivia', 'wpsl') => 'bo',
596
+ __('Bosnia and Herzegovina', 'wpsl') => 'ba',
597
+ __('Botswana', 'wpsl') => 'bw',
598
+ __('Bouvet Island', 'wpsl') => 'bv',
599
+ __('Brazil', 'wpsl') => 'br',
600
+ __('British Indian Ocean Territory', 'wpsl') => 'io',
601
+ __('British Virgin Islands', 'wpsl') => 'vg',
602
+ __('Brunei', 'wpsl') => 'bn',
603
+ __('Bulgaria', 'wpsl') => 'bg',
604
+ __('Burkina Faso', 'wpsl') => 'bf',
605
+ __('Burundi', 'wpsl') => 'bi',
606
+ __('Cambodia', 'wpsl') => 'kh',
607
+ __('Cameroon', 'wpsl') => 'cm',
608
+ __('Canada', 'wpsl') => 'ca',
609
+ __('Canary Islands', 'wpsl') => 'ic',
610
+ __('Cape Verde', 'wpsl') => 'cv',
611
+ __('Caribbean Netherlands', 'wpsl') => 'bq',
612
+ __('Cayman Islands', 'wpsl') => 'ky',
613
+ __('Central African Republic', 'wpsl') => 'cf',
614
+ __('Ceuta and Melilla', 'wpsl') => 'ea',
615
+ __('Chad', 'wpsl') => 'td',
616
+ __('Chile', 'wpsl') => 'cl',
617
+ __('China', 'wpsl') => 'cn',
618
+ __('Christmas Island', 'wpsl') => 'cx',
619
+ __('Clipperton Island', 'wpsl') => 'cp',
620
+ __('Cocos (Keeling) Islands', 'wpsl') => 'cc',
621
+ __('Colombia', 'wpsl') => 'co',
622
+ __('Comoros', 'wpsl') => 'km',
623
+ __('Congo (DRC)', 'wpsl') => 'cd',
624
+ __('Congo (Republic)', 'wpsl') => 'cg',
625
+ __('Cook Islands', 'wpsl') => 'ck',
626
+ __('Costa Rica', 'wpsl') => 'cr',
627
+ __('Croatia', 'wpsl') => 'hr',
628
+ __('Cuba', 'wpsl') => 'cu',
629
+ __('Curaçao', 'wpsl') => 'cw',
630
+ __('Cyprus', 'wpsl') => 'cy',
631
+ __('Czech Republic', 'wpsl') => 'cz',
632
+ __('Côte d\'Ivoire', 'wpsl') => 'ci',
633
+ __('Denmark', 'wpsl') => 'dk',
634
+ __('Djibouti', 'wpsl') => 'dj',
635
+ __('Democratic Republic of the Congo', 'wpsl') => 'cd',
636
+ __('Dominica', 'wpsl') => 'dm',
637
+ __('Dominican Republic', 'wpsl') => 'do',
638
+ __('Ecuador', 'wpsl') => 'ec',
639
+ __('Egypt', 'wpsl') => 'eg',
640
+ __('El Salvador', 'wpsl') => 'sv',
641
+ __('Equatorial Guinea', 'wpsl') => 'gq',
642
+ __('Eritrea', 'wpsl') => 'er',
643
+ __('Estonia', 'wpsl') => 'ee',
644
+ __('Ethiopia', 'wpsl') => 'et',
645
+ __('Falkland Islands(Islas Malvinas)', 'wpsl') => 'fk',
646
+ __('Faroe Islands', 'wpsl') => 'fo',
647
+ __('Fiji', 'wpsl') => 'fj',
648
+ __('Finland', 'wpsl') => 'fi',
649
+ __('France', 'wpsl') => 'fr',
650
+ __('French Guiana', 'wpsl') => 'gf',
651
+ __('French Polynesia', 'wpsl') => 'pf',
652
+ __('French Southern Territories', 'wpsl') => 'tf',
653
+ __('Gabon', 'wpsl') => 'ga',
654
+ __('Gambia', 'wpsl') => 'gm',
655
+ __('Georgia', 'wpsl') => 'ge',
656
+ __('Germany', 'wpsl') => 'de',
657
+ __('Ghana', 'wpsl') => 'gh',
658
+ __('Gibraltar', 'wpsl') => 'gi',
659
+ __('Greece', 'wpsl') => 'gr',
660
+ __('Greenland', 'wpsl') => 'gl',
661
+ __('Grenada', 'wpsl') => 'gd',
662
+ __('Guam', 'wpsl') => 'gu',
663
+ __('Guadeloupe', 'wpsl') => 'gp',
664
+ __('Guam', 'wpsl') => 'gu',
665
+ __('Guatemala', 'wpsl') => 'gt',
666
+ __('Guernsey', 'wpsl') => 'gg',
667
+ __('Guinea', 'wpsl') => 'gn',
668
+ __('Guinea-Bissau', 'wpsl') => 'gw',
669
+ __('Guyana', 'wpsl') => 'gy',
670
+ __('Haiti', 'wpsl') => 'ht',
671
+ __('Heard and McDonald Islands', 'wpsl') => 'hm',
672
+ __('Honduras', 'wpsl') => 'hn',
673
+ __('Hong Kong', 'wpsl') => 'hk',
674
+ __('Hungary', 'wpsl') => 'hu',
675
+ __('Iceland', 'wpsl') => 'is',
676
+ __('India', 'wpsl') => 'in',
677
+ __('Indonesia', 'wpsl') => 'id',
678
+ __('Iran', 'wpsl') => 'ir',
679
+ __('Iraq', 'wpsl') => 'iq',
680
+ __('Ireland', 'wpsl') => 'ie',
681
+ __('Isle of Man', 'wpsl') => 'im',
682
+ __('Israel', 'wpsl') => 'il',
683
+ __('Italy', 'wpsl') => 'it',
684
+ __('Jamaica', 'wpsl') => 'jm',
685
+ __('Japan', 'wpsl') => 'jp',
686
+ __('Jersey', 'wpsl') => 'je',
687
+ __('Jordan', 'wpsl') => 'jo',
688
+ __('Kazakhstan', 'wpsl') => 'kz',
689
+ __('Kenya', 'wpsl') => 'ke',
690
+ __('Kiribati', 'wpsl') => 'ki',
691
+ __('Kosovo', 'wpsl') => 'xk',
692
+ __('Kuwait', 'wpsl') => 'kw',
693
+ __('Kyrgyzstan', 'wpsl') => 'kg',
694
+ __('Laos', 'wpsl') => 'la',
695
+ __('Latvia', 'wpsl') => 'lv',
696
+ __('Lebanon', 'wpsl') => 'lb',
697
+ __('Lesotho', 'wpsl') => 'ls',
698
+ __('Liberia', 'wpsl') => 'lr',
699
+ __('Libya', 'wpsl') => 'ly',
700
+ __('Liechtenstein', 'wpsl') => 'li',
701
+ __('Lithuania', 'wpsl') => 'lt',
702
+ __('Luxembourg', 'wpsl') => 'lu',
703
+ __('Macau', 'wpsl') => 'mo',
704
+ __('Macedonia (FYROM)', 'wpsl') => 'mk',
705
+ __('Madagascar', 'wpsl') => 'mg',
706
+ __('Malawi', 'wpsl') => 'mw',
707
+ __('Malaysia ', 'wpsl') => 'my',
708
+ __('Maldives ', 'wpsl') => 'mv',
709
+ __('Mali', 'wpsl') => 'ml',
710
+ __('Malta', 'wpsl') => 'mt',
711
+ __('Marshall Islands', 'wpsl') => 'mh',
712
+ __('Martinique', 'wpsl') => 'mq',
713
+ __('Mauritania', 'wpsl') => 'mr',
714
+ __('Mauritius', 'wpsl') => 'mu',
715
+ __('Mayotte', 'wpsl') => 'yt',
716
+ __('Mexico', 'wpsl') => 'mx',
717
+ __('Micronesia', 'wpsl') => 'fm',
718
+ __('Moldova', 'wpsl') => 'md',
719
+ __('Monaco' ,'wpsl') => 'mc',
720
+ __('Mongolia', 'wpsl') => 'mn',
721
+ __('Montenegro', 'wpsl') => 'me',
722
+ __('Montserrat', 'wpsl') => 'ms',
723
+ __('Morocco', 'wpsl') => 'ma',
724
+ __('Mozambique', 'wpsl') => 'mz',
725
+ __('Myanmar (Burma)', 'wpsl') => 'mm',
726
+ __('Namibia', 'wpsl') => 'na',
727
+ __('Nauru', 'wpsl') => 'nr',
728
+ __('Nepal', 'wpsl') => 'np',
729
+ __('Netherlands', 'wpsl') => 'nl',
730
+ __('Netherlands Antilles', 'wpsl') => 'an',
731
+ __('New Caledonia', 'wpsl') => 'nc',
732
+ __('New Zealand', 'wpsl') => 'nz',
733
+ __('Nicaragua', 'wpsl') => 'ni',
734
+ __('Niger', 'wpsl') => 'ne',
735
+ __('Nigeria', 'wpsl') => 'ng',
736
+ __('Niue', 'wpsl') => 'nu',
737
+ __('Norfolk Island', 'wpsl') => 'nf',
738
+ __('North Korea', 'wpsl') => 'kp',
739
+ __('Northern Mariana Islands', 'wpsl') => 'mp',
740
+ __('Norway', 'wpsl') => 'no',
741
+ __('Oman', 'wpsl') => 'om',
742
+ __('Pakistan', 'wpsl') => 'pk',
743
+ __('Palau', 'wpsl') => 'pw',
744
+ __('Palestine', 'wpsl') => 'ps',
745
+ __('Panama' ,'wpsl') => 'pa',
746
+ __('Papua New Guinea', 'wpsl') => 'pg',
747
+ __('Paraguay' ,'wpsl') => 'py',
748
+ __('Peru', 'wpsl') => 'pe',
749
+ __('Philippines', 'wpsl') => 'ph',
750
+ __('Pitcairn Islands', 'wpsl') => 'pn',
751
+ __('Poland', 'wpsl') => 'pl',
752
+ __('Portugal', 'wpsl') => 'pt',
753
+ __('Puerto Rico', 'wpsl') => 'pr',
754
+ __('Qatar', 'wpsl') => 'qa',
755
+ __('Reunion', 'wpsl') => 're',
756
+ __('Romania', 'wpsl') => 'ro',
757
+ __('Russia', 'wpsl') => 'ru',
758
+ __('Rwanda', 'wpsl') => 'rw',
759
+ __('Saint Helena', 'wpsl') => 'sh',
760
+ __('Saint Kitts and Nevis', 'wpsl') => 'kn',
761
+ __('Saint Vincent and the Grenadines', 'wpsl') => 'vc',
762
+ __('Saint Lucia', 'wpsl') => 'lc',
763
+ __('Samoa', 'wpsl') => 'ws',
764
+ __('San Marino', 'wpsl') => 'sm',
765
+ __('São Tomé and Príncipe', 'wpsl') => 'st',
766
+ __('Saudi Arabia', 'wpsl') => 'sa',
767
+ __('Senegal', 'wpsl') => 'sn',
768
+ __('Serbia', 'wpsl') => 'rs',
769
+ __('Seychelles', 'wpsl') => 'sc',
770
+ __('Sierra Leone', 'wpsl') => 'sl',
771
+ __('Singapore', 'wpsl') => 'sg',
772
+ __('Sint Maarten', 'wpsl') => 'sx',
773
+ __('Slovakia', 'wpsl') => 'sk',
774
+ __('Slovenia', 'wpsl') => 'si',
775
+ __('Solomon Islands', 'wpsl') => 'sb',
776
+ __('Somalia', 'wpsl') => 'so',
777
+ __('South Africa', 'wpsl') => 'za',
778
+ __('South Georgia and South Sandwich Islands', 'wpsl') => 'gs',
779
+ __('South Korea', 'wpsl') => 'kr',
780
+ __('South Sudan', 'wpsl') => 'ss',
781
+ __('Spain', 'wpsl') => 'es',
782
+ __('Sri Lanka', 'wpsl') => 'lk',
783
+ __('Sudan', 'wpsl') => 'sd',
784
+ __('Swaziland', 'wpsl') => 'sz',
785
+ __('Sweden', 'wpsl') => 'se',
786
+ __('Switzerland', 'wpsl') => 'ch',
787
+ __('Syria', 'wpsl') => 'sy',
788
+ __('São Tomé & Príncipe', 'wpsl') => 'st',
789
+ __('Taiwan', 'wpsl') => 'tw',
790
+ __('Tajikistan', 'wpsl') => 'tj',
791
+ __('Tanzania', 'wpsl') => 'tz',
792
+ __('Thailand', 'wpsl') => 'th',
793
+ __('Timor-Leste', 'wpsl') => 'tl',
794
+ __('Tokelau' ,'wpsl') => 'tk',
795
+ __('Togo', 'wpsl') => 'tg',
796
+ __('Tokelau' ,'wpsl') => 'tk',
797
+ __('Tonga', 'wpsl') => 'to',
798
+ __('Trinidad and Tobago', 'wpsl') => 'tt',
799
+ __('Tristan da Cunha', 'wpsl') => 'ta',
800
+ __('Tunisia', 'wpsl') => 'tn',
801
+ __('Turkey', 'wpsl') => 'tr',
802
+ __('Turkmenistan', 'wpsl') => 'tm',
803
+ __('Turks and Caicos Islands', 'wpsl') => 'tc',
804
+ __('Tuvalu', 'wpsl') => 'tv',
805
+ __('Uganda', 'wpsl') => 'ug',
806
+ __('Ukraine', 'wpsl') => 'ua',
807
+ __('United Arab Emirates', 'wpsl') => 'ae',
808
+ __('United Kingdom', 'wpsl') => 'gb',
809
+ __('United States', 'wpsl') => 'us',
810
+ __('Uruguay', 'wpsl') => 'uy',
811
+ __('Uzbekistan', 'wpsl') => 'uz',
812
+ __('Vanuatu', 'wpsl') => 'vu',
813
+ __('Vatican City', 'wpsl') => 'va',
814
+ __('Venezuela', 'wpsl') => 've',
815
+ __('Vietnam', 'wpsl') => 'vn',
816
+ __('Wallis Futuna', 'wpsl') => 'wf',
817
+ __('Western Sahara', 'wpsl') => 'eh',
818
+ __('Yemen', 'wpsl') => 'ye',
819
+ __('Zambia' ,'wpsl') => 'zm',
820
+ __('Zimbabwe', 'wpsl') => 'zw',
821
+ __('Åland Islands', 'wpsl') => 'ax'
822
+ );
823
+ }
824
+
825
+ // Make sure we have an array with a value.
826
+ if ( !empty( $api_option_list ) && ( is_array( $api_option_list ) ) ) {
827
+ $option_list = '';
828
+ $i = 0;
829
+
830
+ foreach ( $api_option_list as $api_option_key => $api_option_value ) {
831
+
832
+ // If no option value exist, set the first one as selected.
833
+ if ( ( $i == 0 ) && ( empty( $wpsl_settings['api_'.$list] ) ) ) {
834
+ $selected = 'selected="selected"';
835
+ } else {
836
+ $selected = ( $wpsl_settings['api_'.$list] == $api_option_value ) ? 'selected="selected"' : '';
837
+ }
838
+
839
+ $option_list .= '<option value="' . esc_attr( $api_option_value ) . '" ' . $selected . '> ' . esc_html( $api_option_key ) . '</option>';
840
+ $i++;
841
+ }
842
+
843
+ return $option_list;
844
+ }
845
+ }
846
+
847
+ /**
848
+ * Create the dropdown to select the zoom level.
849
+ *
850
+ * @since 1.0.0
851
+ * @return string $dropdown The html for the zoom level list
852
+ */
853
+ public function show_zoom_levels() {
854
+
855
+ global $wpsl_settings;
856
+
857
+ $dropdown = '<select id="wpsl-zoom-level" name="wpsl_map[zoom_level]" autocomplete="off">';
858
+
859
+ for ( $i = 1; $i < 13; $i++ ) {
860
+ $selected = ( $wpsl_settings['zoom_level'] == $i ) ? 'selected="selected"' : '';
861
+
862
+ switch ( $i ) {
863
+ case 1:
864
+ $zoom_desc = ' - ' . __( 'World view', 'wpsl' );
865
+ break;
866
+ case 3:
867
+ $zoom_desc = ' - ' . __( 'Default', 'wpsl' );
868
+ break;
869
+ case 12:
870
+ $zoom_desc = ' - ' . __( 'Roadmap', 'wpsl' );
871
+ break;
872
+ default:
873
+ $zoom_desc = '';
874
+ }
875
+
876
+ $dropdown .= "<option value='$i' $selected>". $i . esc_html( $zoom_desc ) . "</option>";
877
+ }
878
+
879
+ $dropdown .= "</select>";
880
+
881
+ return $dropdown;
882
+ }
883
+
884
+ /**
885
+ * Create the html output for the marker list that is shown on the settings page.
886
+ *
887
+ * There are two markers lists, one were the user can set the marker for the start point
888
+ * and one were a marker can be set for the store. We also check if the marker img is identical
889
+ * to the name in the option field. If so we set it to checked.
890
+ *
891
+ * @since 1.0.0
892
+ * @param string $marker_img The filename of the marker
893
+ * @param string $location Either contains "start" or "store"
894
+ * @return string $marker_list A list of all the available markers
895
+ */
896
+ public function create_marker_html( $marker_img, $location ) {
897
+
898
+ global $wpsl_settings;
899
+
900
+ $marker_path = ( defined( 'WPSL_MARKER_URI' ) ) ? WPSL_MARKER_URI : WPSL_URL . 'img/markers/';
901
+ $marker_list = '';
902
+
903
+ if ( $wpsl_settings[$location.'_marker'] == $marker_img ) {
904
+ $checked = 'checked="checked"';
905
+ $css_class = 'class="wpsl-active-marker"';
906
+ } else {
907
+ $checked = '';
908
+ $css_class = '';
909
+ }
910
+
911
+ $marker_list .= '<li ' . $css_class . '>';
912
+ $marker_list .= '<img src="' . $marker_path . $marker_img . '" />';
913
+ $marker_list .= '<input ' . $checked . ' type="radio" name="wpsl_map[' . $location . '_marker]" value="' . $marker_img . '" />';
914
+ $marker_list .= '</li>';
915
+
916
+ return $marker_list;
917
+ }
918
+
919
+ /**
920
+ * Get the default values for the marker clusters dropdown options.
921
+ *
922
+ * @since 1.2.20
923
+ * @param string $type The cluster option type
924
+ * @return string $cluster_values The default cluster options
925
+ */
926
+ public function get_default_cluster_option( $type ) {
927
+
928
+ $cluster_values = array(
929
+ 'cluster_zoom' => array(
930
+ '7',
931
+ '8',
932
+ '9',
933
+ '10',
934
+ '11',
935
+ '12',
936
+ '13'
937
+ ),
938
+ 'cluster_size' => array(
939
+ '40',
940
+ '50',
941
+ '60',
942
+ '70',
943
+ '80'
944
+ ),
945
+ );
946
+
947
+ return $cluster_values[$type];
948
+ }
949
+
950
+ /**
951
+ * Create a dropdown for the marker cluster options.
952
+ *
953
+ * @since 1.2.20
954
+ * @param string $type The cluster option type
955
+ * @return string $dropdown The html for the distance option list
956
+ */
957
+ public function show_cluster_options( $type ) {
958
+
959
+ global $wpsl_settings;
960
+
961
+ $cluster_options = array(
962
+ 'cluster_zoom' => array(
963
+ 'id' => 'wpsl-marker-zoom',
964
+ 'name' => 'cluster_zoom',
965
+ 'options' => $this->get_default_cluster_option( $type )
966
+ ),
967
+ 'cluster_size' => array(
968
+ 'id' => 'wpsl-marker-cluster-size',
969
+ 'name' => 'cluster_size',
970
+ 'options' => $this->get_default_cluster_option( $type )
971
+ ),
972
+ );
973
+
974
+ $dropdown = '<select id="' . esc_attr( $cluster_options[$type]['id'] ) . '" name="wpsl_map[' . esc_attr( $cluster_options[$type]['name'] ) . ']" autocomplete="off">';
975
+
976
+ $i = 0;
977
+ foreach ( $cluster_options[$type]['options'] as $item => $value ) {
978
+ $selected = ( $wpsl_settings[$type] == $value ) ? 'selected="selected"' : '';
979
+
980
+ if ( $i == 0 ) {
981
+ $dropdown .= "<option value='0' $selected>" . __( 'Default', 'wpsl' ) . "</option>";
982
+ } else {
983
+ $dropdown .= "<option value=". absint( $value ) . " $selected>" . absint( $value ) . "</option>";
984
+ }
985
+
986
+ $i++;
987
+ }
988
+
989
+ $dropdown .= "</select>";
990
+
991
+ return $dropdown;
992
+ }
993
+
994
+ /**
995
+ * Show the options of the start and store markers.
996
+ *
997
+ * @since 1.0.0
998
+ * @return string $marker_list The complete list of available and selected markers
999
+ */
1000
+ public function show_marker_options() {
1001
+
1002
+ $marker_list = '';
1003
+ $marker_images = $this->get_available_markers();
1004
+ $marker_locations = array(
1005
+ 'start',
1006
+ 'store'
1007
+ );
1008
+
1009
+ foreach ( $marker_locations as $location ) {
1010
+ if ( $location == 'start' ) {
1011
+ $marker_list .= __( 'Start location marker', 'wpsl' ) . ':';
1012
+ } else {
1013
+ $marker_list .= __( 'Store location marker', 'wpsl' ) . ':';
1014
+ }
1015
+
1016
+ if ( !empty( $marker_images ) ) {
1017
+ $marker_list .= '<ul class="wpsl-marker-list">';
1018
+
1019
+ foreach ( $marker_images as $marker_img ) {
1020
+ $marker_list .= $this->create_marker_html( $marker_img, $location );
1021
+ }
1022
+
1023
+ $marker_list .= '</ul>';
1024
+ }
1025
+ }
1026
+
1027
+ return $marker_list;
1028
+ }
1029
+
1030
+ /**
1031
+ * Load the markers that are used on the map.
1032
+ *
1033
+ * @since 1.0.0
1034
+ * @return array $marker_images A list of all the available markers.
1035
+ */
1036
+ public function get_available_markers() {
1037
+
1038
+ $marker_images = array();
1039
+ $dir = apply_filters( 'wpsl_admin_marker_dir', WPSL_PLUGIN_DIR . 'img/markers/' );
1040
+
1041
+ if ( is_dir( $dir ) ) {
1042
+ if ( $dh = opendir( $dir ) ) {
1043
+ while ( false !== ( $file = readdir( $dh ) ) ) {
1044
+ if ( $file == '.' || $file == '..' || ( strpos( $file, '@2x' ) !== false ) ) continue;
1045
+ $marker_images[] = $file;
1046
+ }
1047
+
1048
+ closedir( $dh );
1049
+ }
1050
+ }
1051
+
1052
+ return $marker_images;
1053
+ }
1054
+
1055
+ /**
1056
+ * Show a list of available templates.
1057
+ *
1058
+ * @since 1.2.20
1059
+ * @return string $dropdown The html for the template option list
1060
+ */
1061
+ public function show_template_options() {
1062
+
1063
+ global $wpsl_settings;
1064
+
1065
+ $dropdown = '<select id="wpsl-store-template" name="wpsl_ux[template_id]" autocomplete="off">';
1066
+
1067
+ foreach ( wpsl_get_templates() as $template ) {
1068
+ $template_id = ( isset( $template['id'] ) ) ? $template['id'] : '';
1069
+
1070
+ $selected = ( $wpsl_settings['template_id'] == $template_id ) ? ' selected="selected"' : '';
1071
+ $dropdown .= "<option value='" . esc_attr( $template_id ) . "' $selected>" . esc_html( $template['name'] ) . "</option>";
1072
+ }
1073
+
1074
+ $dropdown .= '</select>';
1075
+
1076
+ return $dropdown;
1077
+ }
1078
+
1079
+ /**
1080
+ * Create dropdown lists.
1081
+ *
1082
+ * @since 2.0.0
1083
+ * @param string $type The type of dropdown
1084
+ * @return string $dropdown The html output for the dropdown
1085
+ */
1086
+ public function create_dropdown( $type ) {
1087
+
1088
+ global $wpsl_settings;
1089
+
1090
+ $dropdown_lists = apply_filters( 'wpsl_setting_dropdowns', array(
1091
+ 'hour_input' => array(
1092
+ 'values' => array(
1093
+ 'textarea' => __( 'Textarea', 'wpsl' ),
1094
+ 'dropdown' => __( 'Dropdowns (recommended)', 'wpsl' )
1095
+ ),
1096
+ 'id' => 'wpsl-editor-hour-input',
1097
+ 'name' => 'wpsl_editor[hour_input]',
1098
+ 'selected' => $wpsl_settings['editor_hour_input']
1099
+ ),
1100
+ 'marker_effects' => array(
1101
+ 'values' => array(
1102
+ 'bounce' => __( 'Bounces up and down', 'wpsl' ),
1103
+ 'info_window' => __( 'Will open the info window', 'wpsl' ),
1104
+ 'ignore' => __( 'Does not respond', 'wpsl' )
1105
+ ),
1106
+ 'id' => 'wpsl-marker-effect',
1107
+ 'name' => 'wpsl_ux[marker_effect]',
1108
+ 'selected' => $wpsl_settings['marker_effect']
1109
+ ),
1110
+ 'more_info' => array(
1111
+ 'values' => array(
1112
+ 'store listings' => __( 'In the store listings', 'wpsl' ),
1113
+ 'info window' => __( 'In the info window on the map', 'wpsl' )
1114
+ ),
1115
+ 'id' => 'wpsl-more-info-list',
1116
+ 'name' => 'wpsl_ux[more_info_location]',
1117
+ 'selected' => $wpsl_settings['more_info_location']
1118
+ ),
1119
+ 'map_types' => array(
1120
+ 'values' => wpsl_get_map_types(),
1121
+ 'id' => 'wpsl-map-type',
1122
+ 'name' => 'wpsl_map[type]',
1123
+ 'selected' => $wpsl_settings['map_type']
1124
+ ),
1125
+ 'editor_map_types' => array(
1126
+ 'values' => wpsl_get_map_types(),
1127
+ 'id' => 'wpsl-editor-map-type',
1128
+ 'name' => 'wpsl_editor[map_type]',
1129
+ 'selected' => $wpsl_settings['editor_map_type']
1130
+ ),
1131
+ 'max_zoom_level' => array(
1132
+ 'values' => wpsl_get_max_zoom_levels(),
1133
+ 'id' => 'wpsl-max-auto-zoom',
1134
+ 'name' => 'wpsl_map[max_auto_zoom]',
1135
+ 'selected' => $wpsl_settings['auto_zoom_level']
1136
+ ),
1137
+ 'address_format' => array(
1138
+ 'values' => wpsl_get_address_formats(),
1139
+ 'id' => 'wpsl-address-format',
1140
+ 'name' => 'wpsl_ux[address_format]',
1141
+ 'selected' => $wpsl_settings['address_format']
1142
+ ),
1143
+ 'filter_types' => array(
1144
+ 'values' => array(
1145
+ 'dropdown' => __( 'Dropdown', 'wpsl' ),
1146
+ 'checkboxes' => __( 'Checkboxes', 'wpsl' )
1147
+ ),
1148
+ 'id' => 'wpsl-cat-filter-types',
1149
+ 'name' => 'wpsl_search[category_filter_type]',
1150
+ 'selected' => $wpsl_settings['category_filter_type']
1151
+ )
1152
+ ) );
1153
+
1154
+ $dropdown = '<select id="' . esc_attr( $dropdown_lists[$type]['id'] ) . '" name="' . esc_attr( $dropdown_lists[$type]['name'] ) . '" autocomplete="off">';
1155
+
1156
+ foreach ( $dropdown_lists[$type]['values'] as $key => $value ) {
1157
+ $selected = ( $key == $dropdown_lists[$type]['selected'] ) ? 'selected="selected"' : '';
1158
+ $dropdown .= "<option value='" . esc_attr( $key ) . "' $selected>" . esc_html( $value ) . "</option>";
1159
+ }
1160
+
1161
+ $dropdown .= '</select>';
1162
+
1163
+ return $dropdown;
1164
+ }
1165
+
1166
+ /**
1167
+ * Create a dropdown for the 12/24 opening hours format.
1168
+ *
1169
+ * @since 2.0.0
1170
+ * @param string $hour_format The hour format that should be set to selected
1171
+ * @return string $dropdown The html for the dropdown
1172
+ */
1173
+ public function show_opening_hours_format( $hour_format = '' ) {
1174
+
1175
+ global $wpsl_settings;
1176
+
1177
+ $items = array(
1178
+ '12' => __( '12 Hours', 'wpsl' ),
1179
+ '24' => __( '24 Hours', 'wpsl' )
1180
+ );
1181
+
1182
+ if ( !absint( $hour_format ) ) {
1183
+ $hour_format = $wpsl_settings['editor_hour_format'];
1184
+ }
1185
+
1186
+ $dropdown = '<select id="wpsl-editor-hour-format" name="wpsl_editor[hour_format]" autocomplete="off">';
1187
+
1188
+ foreach ( $items as $key => $value ) {
1189
+ $selected = ( $hour_format == $key ) ? 'selected="selected"' : '';
1190
+ $dropdown .= "<option value='$key' $selected>" . esc_html( $value ) . "</option>";
1191
+ }
1192
+
1193
+ $dropdown .= '</select>';
1194
+
1195
+ return $dropdown;
1196
+ }
1197
+ }
1198
  }
admin/class-shortcode-generator.php CHANGED
@@ -1,366 +1,366 @@
1
- <?php
2
- /**
3
- * Shortcode Generator class
4
- *
5
- * @author Tijmen Smit
6
- * @since 2.2.10
7
- */
8
-
9
- if ( !defined( 'ABSPATH' ) ) exit;
10
-
11
- if ( !class_exists( 'WPSL_Shortcode_Generator' ) ) {
12
-
13
- /**
14
- * Handle the generation of the WPSL shortcode through the media button
15
- *
16
- * @since 2.2.10
17
- */
18
- class WPSL_Shortcode_Generator {
19
-
20
- /**
21
- * Constructor
22
- */
23
- public function __construct() {
24
- add_action( 'media_buttons', array( $this, 'add_wpsl_media_button' ) );
25
- add_action( 'admin_init', array( $this, 'show_thickbox_iframe_content' ) );
26
- }
27
-
28
- /**
29
- * Add the WPSL media button to the media button row
30
- *
31
- * @since 2.2.10
32
- * @return void
33
- */
34
- public function add_wpsl_media_button() {
35
-
36
- global $pagenow, $typenow;
37
-
38
- /* Make sure we're on a post/page or edit screen in the admin area */
39
- if ( in_array( $pagenow, array( 'post.php', 'page.php', 'post-new.php', 'post-edit.php' ) ) && $typenow != 'wpsl_stores' ) {
40
- $changelog_link = self_admin_url( '?wpsl_media_action=store_locator&KeepThis=true&TB_iframe=true&width=783&height=800' );
41
-
42
- echo '<a href="' . esc_url( $changelog_link ) . '" class="thickbox button wpsl-thickbox" name="' . __( 'WP Store Locator' ,'wpsl' ) . '">' . __( 'Insert Store Locator', 'wpsl' ) . '</a>';
43
- }
44
- }
45
-
46
- /**
47
- * Show the shortcode thickbox content
48
- *
49
- * @since 2.2.10
50
- * @return void
51
- */
52
- function show_thickbox_iframe_content() {
53
-
54
- global $wpsl_settings, $wpsl_admin;
55
-
56
- if ( empty( $_REQUEST['wpsl_media_action'] ) ) {
57
- return;
58
- }
59
-
60
- if ( !current_user_can( 'edit_pages' ) ) {
61
- wp_die( __( 'You do not have permission to perform this action', 'wpsl' ), __( 'Error', 'wpsl' ), array( 'response' => 403 ) );
62
- }
63
-
64
- $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
65
-
66
- // Make sure the required JS / CSS files are loaded in the Thickbox iframe
67
- wp_print_scripts( 'jquery-ui-core' );
68
- wp_print_scripts( 'jquery-ui-tabs' );
69
- wp_print_scripts( 'media-upload' );
70
- ?>
71
- <script type="text/javascript" src="<?php echo plugins_url( '/js/wpsl-shortcode-generator' . $min . '.js?ver='. WPSL_VERSION_NUM .'', __FILE__ ); ?>"></script>
72
- <?php
73
- wp_print_styles('buttons' );
74
- wp_print_styles('forms' );
75
- ?>
76
-
77
- <link rel="stylesheet" type="text/css" href="<?php echo plugins_url( '/css/style' . $min . '.css?ver='. WPSL_VERSION_NUM .'', __FILE__ ); ?>" media="all" />
78
- <style>
79
- body {
80
- color: #444;
81
- font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
82
- font-size: 13px;
83
- margin: 0;
84
- }
85
-
86
- #wpsl-media-tabs .ui-tabs-nav {
87
- padding-left: 15px;
88
- background: #fff !important;
89
- border-bottom: 1px solid #dfdfdf;
90
- border-collapse: collapse;
91
- padding-top: .2em;
92
- }
93
-
94
- #wpsl-media-tabs .ui-tabs-nav::after {
95
- clear: both;
96
- content: "";
97
- display: table;
98
- border-collapse: collapse;
99
- }
100
-
101
- #wpsl-media-tabs .ui-tabs-nav li {
102
- list-style: none;
103
- float: left;
104
- position: relative;
105
- top: 0;
106
- margin: 1px .2em 0 0;
107
- padding: 0;
108
- white-space: nowrap;
109
- border-bottom-width: 0;
110
- }
111
-
112
- #wpsl-media-tabs .ui-tabs-anchor {
113
- float: left;
114
- padding: .5em 1em;
115
- text-decoration: none;
116
- font-size: 14.3px;
117
- }
118
-
119
- #wpsl-media-tabs .ui-tabs-active a {
120
- color: #212121;
121
- cursor: text;
122
- }
123
-
124
- #wpsl-media-tabs .ui-tabs .ui-tabs-anchor {
125
- float: left;
126
- padding: .5em 1em;
127
- text-decoration: none;
128
- }
129
-
130
- #wpsl-media-tabs.ui-widget-content {
131
- border: none;
132
- padding: 10px 0 0 0;
133
- }
134
-
135
- #wpsl-media-tabs .ui-tabs-anchor {
136
- outline: none;
137
- }
138
-
139
- #wpsl-shortcode-config tr > td {
140
- width: 25%;
141
- }
142
-
143
- #wpsl-markers-tab .wpsl-marker-list {
144
- display: block;
145
- overflow: hidden;
146
- padding: 0;
147
- list-style-type: none;
148
- }
149
-
150
- #wpsl-markers-tab .wpsl-marker-list li input {
151
- padding: 0;
152
- margin: 0;
153
- }
154
-
155
- #wpsl-shortcode-config .form-table,
156
- #wpsl-shortcode-config .form-table td,
157
- #wpsl-shortcode-config .form-table th,
158
- #wpsl-shortcode-config .form-table td p {
159
- font-size: 13px;
160
- }
161
-
162
- #wpsl-shortcode-config .ui-tabs .ui-tabs-nav {
163
- padding-left: 15px;
164
- border-radius: 0;
165
- margin: 0;
166
- }
167
-
168
- .wpsl-shortcode-markers {
169
- padding: 0 10px;
170
- margin-top: 27px;
171
- font-size: 13px;
172
- }
173
-
174
- #wpsl-insert-shortcode {
175
- margin-left: 19px;
176
- }
177
-
178
- #wpsl-shortcode-config .ui-state-default {
179
- border: 1px solid #d3d3d3;
180
- border-top-left-radius: 4px;
181
- border-top-right-radius: 4px;
182
- background: none;
183
- }
184
-
185
- #wpsl-shortcode-config .ui-state-default a {
186
- color: #909090;
187
- }
188
-
189
- #wpsl-shortcode-config .ui-state-default.ui-tabs-active a {
190
- color: #212121;
191
- }
192
-
193
- #wpsl-shortcode-config .ui-state-hover {
194
- border-bottom: none;
195
- }
196
-
197
- #wpsl-shortcode-config .ui-state-hover a {
198
- color: #72777c;
199
- }
200
-
201
- #wpsl-media-tabs .ui-state-active {
202
- border: 1px solid #aaa;
203
- border-bottom: 1px solid #fff;
204
- padding-bottom: 0;
205
- }
206
-
207
- #wpsl-shortcode-config li.ui-tabs-active.ui-state-hover,
208
- #wpsl-shortcode-config li.ui-tabs-active {
209
- border-bottom: 1px solid #fff;
210
- padding-bottom: 0;
211
- }
212
-
213
- #wpsl-media-tabs li.ui-tabs-active {
214
- margin-bottom: -1px;
215
- }
216
-
217
- #wpsl-general-tab,
218
- #wpsl-markers-tab {
219
- border: 0;
220
- padding: 1em 1.4em;
221
- background: none;
222
- }
223
-
224
- @media ( max-width: 782px ) {
225
- #wpsl-shortcode-config tr > td {
226
- width: 100%;
227
- }
228
- }
229
- </style>
230
- <div id="wpsl-shortcode-config" class="wp-core-ui">
231
- <div id="wpsl-media-tabs">
232
- <ul>
233
- <li><a href="#wpsl-general-tab"><?php _e( 'General Options', 'wpsl' ); ?></a></li>
234
- <li><a href="#wpsl-markers-tab"><?php _e('Markers', 'wpsl' ); ?></a></li>
235
- </ul>
236
- <div id="wpsl-general-tab">
237
- <table class="form-table wpsl-shortcode-config">
238
- <tbody>
239
- <tr>
240
- <td><label for="wpsl-store-template"><?php _e('Select the used template', 'wpsl' ); ?></label></td>
241
- <td><?php echo $wpsl_admin->settings_page->show_template_options(); ?></td>
242
- </tr>
243
- <tr>
244
- <td><label for="wpsl-start-location"><?php _e( 'Start point', 'wpsl' ); ?></label><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'If nothing it set, then the start point from the %ssettings%s page is used.', '' ), '<a href=' . admin_url( 'edit.php?post_type=wpsl_stores&page=wpsl_settings#wpsl-map-settings' ) . '>', '</a>' ); ?></span></span></p></td>
245
- <td><input type="text" placeholder="Optional" value="" id="wpsl-start-location"></td>
246
- </tr>
247
- <tr>
248
- <td>
249
- <label for="wpsl-auto-locate"><?php _e( 'Attempt to auto-locate the user', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'Most modern browsers %srequire%s a HTTPS connection before the Geolocation feature works.', 'wpsl_csv' ), '<a href="https://wpstorelocator.co/document/html-5-geolocation-not-working/">', '</a>' ); ?></span></span></label>
250
- </td>
251
- <td><input type="checkbox" value="" <?php checked( $wpsl_settings['auto_locate'], true ); ?> name="wpsl_map[auto_locate]" id="wpsl-auto-locate"></td>
252
- </tr>
253
- <?php
254
- $terms = get_terms( 'wpsl_store_category', 'hide_empty=1' );
255
-
256
- if ( $terms ) {
257
- ?>
258
- <tr>
259
- <td><label for="wpsl-cat-filter-types"><?php _e( 'Category filter type', 'wpsl' ); ?></label></p></td>
260
- <td>
261
- <select id="wpsl-cat-filter-types" autocomplete="off">
262
- <option value="" selected="selected"><?php _e( 'None', 'wpsl' ); ?></option>
263
- <option value="dropdown"><?php _e( 'Dropdown', 'wpsl' ); ?></option>
264
- <option value="checkboxes"><?php _e( 'Checkboxes', 'wpsl' ); ?></option>
265
- </select>
266
- </td>
267
- </tr>
268
- <tr class="wpsl-cat-restriction">
269
- <td style="vertical-align:top;"><label for="wpsl-cat-restriction"><?php _e('Automatically restrict the returned results to one or more categories?', 'wpsl' ); ?></label></td>
270
- <td>
271
- <?php
272
- $cat_restricton = '<select id="wpsl-cat-restriction" multiple="multiple" autocomplete="off">';
273
-
274
- foreach ( $terms as $term ) {
275
- $cat_restricton .= '<option value="' . esc_attr( $term->slug ) . '">' . esc_html( $term->name ) . '</option>';
276
- }
277
-
278
- $cat_restricton .= '</select>';
279
-
280
- echo $cat_restricton;
281
- ?>
282
- </td>
283
- </tr>
284
- <tr class="wpsl-cat-selection wpsl-hide">
285
- <td style="vertical-align:top;"><label for="wpsl-cat-selection"><?php _e('Set a selected category?', 'wpsl' ); ?></label></td>
286
- <td>
287
- <?php
288
- $cat_selection = '<select id="wpsl-cat-selection" autocomplete="off">';
289
-
290
- $cat_selection .= '<option value="" selected="selected">' . __( 'Select category', 'wpsl' ) . '</option>';
291
-
292
- foreach ( $terms as $term ) {
293
- $cat_selection .= '<option value="' . esc_attr( $term->slug ) . '">' . esc_html( $term->name ) . '</option>';
294
- }
295
-
296
- $cat_selection .= '</select>';
297
-
298
- echo $cat_selection;
299
- ?>
300
- </td>
301
- </tr>
302
- <?php
303
- }
304
- ?>
305
- <tr class="wpsl-checkbox-options wpsl-hide">
306
- <td><label for="wpsl-checkbox-columns"><?php _e('Checkbox columns', 'wpsl' ); ?></label></td>
307
- <td>
308
- <?php
309
- echo '<select id="wpsl-checkbox-columns">';
310
-
311
- $i = 1;
312
-
313
- while ( $i <= 4 ) {
314
- $selected = ( $i == 3 ) ? "selected='selected'" : ''; // 3 is the default
315
-
316
- echo '<option value="' . $i . '" ' . $selected . '>' . $i . '</option>';
317
- $i++;
318
- }
319
-
320
- echo '</select>';
321
- ?>
322
- </td>
323
- </tr>
324
- <tr class="wpsl-checkbox-selection wpsl-hide">
325
- <td><label for="wpsl-checkbox-columns"><?php _e('Set selected checkboxes', 'wpsl' ); ?></label></td>
326
- <td>
327
- <?php
328
- $checkbox_selection = '<select id="wpsl-checkbox-selection" multiple="multiple" autocomplete="off">';
329
-
330
- foreach ( $terms as $term ) {
331
- $checkbox_selection .= '<option value="' . esc_attr( $term->slug ) . '">' . esc_html( $term->name ) . '</option>';
332
- }
333
-
334
- $checkbox_selection .= '</select>';
335
-
336
- echo $checkbox_selection;
337
- ?>
338
- </td>
339
- </tr>
340
- <tr>
341
- <td><label for="wpsl-map-type"><?php _e( 'Map type', 'wpsl' ); ?>:</label></td>
342
- <td><?php echo $wpsl_admin->settings_page->create_dropdown( 'map_types' ); ?></td>
343
- </tr>
344
- </tbody>
345
- </table>
346
- </div>
347
- <div id="wpsl-markers-tab">
348
- <div class="wpsl-shortcode-markers">
349
- <?php echo $wpsl_admin->settings_page->show_marker_options(); ?>
350
- </div>
351
- </div>
352
- </div>
353
-
354
- <p class="submit">
355
- <input type="button" id="wpsl-insert-shortcode" class="button-primary" value="<?php echo _e( 'Insert Store Locator', 'wpsl' ); ?>" onclick="WPSL_InsertShortcode();" />
356
- </p>
357
- </div>
358
-
359
- <?php
360
-
361
- exit();
362
- }
363
- }
364
-
365
- new WPSL_Shortcode_Generator();
366
  }
1
+ <?php
2
+ /**
3
+ * Shortcode Generator class
4
+ *
5
+ * @author Tijmen Smit
6
+ * @since 2.2.10
7
+ */
8
+
9
+ if ( !defined( 'ABSPATH' ) ) exit;
10
+
11
+ if ( !class_exists( 'WPSL_Shortcode_Generator' ) ) {
12
+
13
+ /**
14
+ * Handle the generation of the WPSL shortcode through the media button
15
+ *
16
+ * @since 2.2.10
17
+ */
18
+ class WPSL_Shortcode_Generator {
19
+
20
+ /**
21
+ * Constructor
22
+ */
23
+ public function __construct() {
24
+ add_action( 'media_buttons', array( $this, 'add_wpsl_media_button' ) );
25
+ add_action( 'admin_init', array( $this, 'show_thickbox_iframe_content' ) );
26
+ }
27
+
28
+ /**
29
+ * Add the WPSL media button to the media button row
30
+ *
31
+ * @since 2.2.10
32
+ * @return void
33
+ */
34
+ public function add_wpsl_media_button() {
35
+
36
+ global $pagenow, $typenow;
37
+
38
+ /* Make sure we're on a post/page or edit screen in the admin area */
39
+ if ( in_array( $pagenow, array( 'post.php', 'page.php', 'post-new.php', 'post-edit.php' ) ) && $typenow != 'wpsl_stores' ) {
40
+ $changelog_link = self_admin_url( '?wpsl_media_action=store_locator&KeepThis=true&TB_iframe=true&width=783&height=800' );
41
+
42
+ echo '<a href="' . esc_url( $changelog_link ) . '" class="thickbox button wpsl-thickbox" name="' . __( 'WP Store Locator' ,'wpsl' ) . '">' . __( 'Insert Store Locator', 'wpsl' ) . '</a>';
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Show the shortcode thickbox content
48
+ *
49
+ * @since 2.2.10
50
+ * @return void
51
+ */
52
+ function show_thickbox_iframe_content() {
53
+
54
+ global $wpsl_settings, $wpsl_admin;
55
+
56
+ if ( empty( $_REQUEST['wpsl_media_action'] ) ) {
57
+ return;
58
+ }
59
+
60
+ if ( !current_user_can( 'edit_pages' ) ) {
61
+ wp_die( __( 'You do not have permission to perform this action', 'wpsl' ), __( 'Error', 'wpsl' ), array( 'response' => 403 ) );
62
+ }
63
+
64
+ $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
65
+
66
+ // Make sure the required JS / CSS files are loaded in the Thickbox iframe
67
+ wp_print_scripts( 'jquery-ui-core' );
68
+ wp_print_scripts( 'jquery-ui-tabs' );
69
+ wp_print_scripts( 'media-upload' );
70
+ ?>
71
+ <script type="text/javascript" src="<?php echo plugins_url( '/js/wpsl-shortcode-generator' . $min . '.js?ver='. WPSL_VERSION_NUM .'', __FILE__ ); ?>"></script>
72
+ <?php
73
+ wp_print_styles('buttons' );
74
+ wp_print_styles('forms' );
75
+ ?>
76
+
77
+ <link rel="stylesheet" type="text/css" href="<?php echo plugins_url( '/css/style' . $min . '.css?ver='. WPSL_VERSION_NUM .'', __FILE__ ); ?>" media="all" />
78
+ <style>
79
+ body {
80
+ color: #444;
81
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
82
+ font-size: 13px;
83
+ margin: 0;
84
+ }
85
+
86
+ #wpsl-media-tabs .ui-tabs-nav {
87
+ padding-left: 15px;
88
+ background: #fff !important;
89
+ border-bottom: 1px solid #dfdfdf;
90
+ border-collapse: collapse;
91
+ padding-top: .2em;
92
+ }
93
+
94
+ #wpsl-media-tabs .ui-tabs-nav::after {
95
+ clear: both;
96
+ content: "";
97
+ display: table;
98
+ border-collapse: collapse;
99
+ }
100
+
101
+ #wpsl-media-tabs .ui-tabs-nav li {
102
+ list-style: none;
103
+ float: left;
104
+ position: relative;
105
+ top: 0;
106
+ margin: 1px .2em 0 0;
107
+ padding: 0;
108
+ white-space: nowrap;
109
+ border-bottom-width: 0;
110
+ }
111
+
112
+ #wpsl-media-tabs .ui-tabs-anchor {
113
+ float: left;
114
+ padding: .5em 1em;
115
+ text-decoration: none;
116
+ font-size: 14.3px;
117
+ }
118
+
119
+ #wpsl-media-tabs .ui-tabs-active a {
120
+ color: #212121;
121
+ cursor: text;
122
+ }
123
+
124
+ #wpsl-media-tabs .ui-tabs .ui-tabs-anchor {
125
+ float: left;
126
+ padding: .5em 1em;
127
+ text-decoration: none;
128
+ }
129
+
130
+ #wpsl-media-tabs.ui-widget-content {
131
+ border: none;
132
+ padding: 10px 0 0 0;
133
+ }
134
+
135
+ #wpsl-media-tabs .ui-tabs-anchor {
136
+ outline: none;
137
+ }
138
+
139
+ #wpsl-shortcode-config tr > td {
140
+ width: 25%;
141
+ }
142
+
143
+ #wpsl-markers-tab .wpsl-marker-list {
144
+ display: block;
145
+ overflow: hidden;
146
+ padding: 0;
147
+ list-style-type: none;
148
+ }
149
+
150
+ #wpsl-markers-tab .wpsl-marker-list li input {
151
+ padding: 0;
152
+ margin: 0;
153
+ }
154
+
155
+ #wpsl-shortcode-config .form-table,
156
+ #wpsl-shortcode-config .form-table td,
157
+ #wpsl-shortcode-config .form-table th,
158
+ #wpsl-shortcode-config .form-table td p {
159
+ font-size: 13px;
160
+ }
161
+
162
+ #wpsl-shortcode-config .ui-tabs .ui-tabs-nav {
163
+ padding-left: 15px;
164
+ border-radius: 0;
165
+ margin: 0;
166
+ }
167
+
168
+ .wpsl-shortcode-markers {
169
+ padding: 0 10px;
170
+ margin-top: 27px;
171
+ font-size: 13px;
172
+ }
173
+
174
+ #wpsl-insert-shortcode {
175
+ margin-left: 19px;
176
+ }
177
+
178
+ #wpsl-shortcode-config .ui-state-default {
179
+ border: 1px solid #d3d3d3;
180
+ border-top-left-radius: 4px;
181
+ border-top-right-radius: 4px;
182
+ background: none;
183
+ }
184
+
185
+ #wpsl-shortcode-config .ui-state-default a {
186
+ color: #909090;
187
+ }
188
+
189
+ #wpsl-shortcode-config .ui-state-default.ui-tabs-active a {
190
+ color: #212121;
191
+ }
192
+
193
+ #wpsl-shortcode-config .ui-state-hover {
194
+ border-bottom: none;
195
+ }
196
+
197
+ #wpsl-shortcode-config .ui-state-hover a {
198
+ color: #72777c;
199
+ }
200
+
201
+ #wpsl-media-tabs .ui-state-active {
202
+ border: 1px solid #aaa;
203
+ border-bottom: 1px solid #fff;
204
+ padding-bottom: 0;
205
+ }
206
+
207
+ #wpsl-shortcode-config li.ui-tabs-active.ui-state-hover,
208
+ #wpsl-shortcode-config li.ui-tabs-active {
209
+ border-bottom: 1px solid #fff;
210
+ padding-bottom: 0;
211
+ }
212
+
213
+ #wpsl-media-tabs li.ui-tabs-active {
214
+ margin-bottom: -1px;
215
+ }
216
+
217
+ #wpsl-general-tab,
218
+ #wpsl-markers-tab {
219
+ border: 0;
220
+ padding: 1em 1.4em;
221
+ background: none;
222
+ }
223
+
224
+ @media ( max-width: 782px ) {
225
+ #wpsl-shortcode-config tr > td {
226
+ width: 100%;
227
+ }
228
+ }
229
+ </style>
230
+ <div id="wpsl-shortcode-config" class="wp-core-ui">
231
+ <div id="wpsl-media-tabs">
232
+ <ul>
233
+ <li><a href="#wpsl-general-tab"><?php _e( 'General Options', 'wpsl' ); ?></a></li>
234
+ <li><a href="#wpsl-markers-tab"><?php _e('Markers', 'wpsl' ); ?></a></li>
235
+ </ul>
236
+ <div id="wpsl-general-tab">
237
+ <table class="form-table wpsl-shortcode-config">
238
+ <tbody>
239
+ <tr>
240
+ <td><label for="wpsl-store-template"><?php _e('Select the used template', 'wpsl' ); ?></label></td>
241
+ <td><?php echo $wpsl_admin->settings_page->show_template_options(); ?></td>
242
+ </tr>
243
+ <tr>
244
+ <td><label for="wpsl-start-location"><?php _e( 'Start point', 'wpsl' ); ?></label><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'If nothing it set, then the start point from the %ssettings%s page is used.', '' ), '<a href=' . admin_url( 'edit.php?post_type=wpsl_stores&page=wpsl_settings#wpsl-map-settings' ) . '>', '</a>' ); ?></span></span></p></td>
245
+ <td><input type="text" placeholder="Optional" value="" id="wpsl-start-location"></td>
246
+ </tr>
247
+ <tr>
248
+ <td>
249
+ <label for="wpsl-auto-locate"><?php _e( 'Attempt to auto-locate the user', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'Most modern browsers %srequire%s a HTTPS connection before the Geolocation feature works.', 'wpsl_csv' ), '<a href="https://wpstorelocator.co/document/html-5-geolocation-not-working/">', '</a>' ); ?></span></span></label>
250
+ </td>
251
+ <td><input type="checkbox" value="" <?php checked( $wpsl_settings['auto_locate'], true ); ?> name="wpsl_map[auto_locate]" id="wpsl-auto-locate"></td>
252
+ </tr>
253
+ <?php
254
+ $terms = get_terms( 'wpsl_store_category', 'hide_empty=1' );
255
+
256
+ if ( $terms ) {
257
+ ?>
258
+ <tr>
259
+ <td><label for="wpsl-cat-filter-types"><?php _e( 'Category filter type', 'wpsl' ); ?></label></p></td>
260
+ <td>
261
+ <select id="wpsl-cat-filter-types" autocomplete="off">
262
+ <option value="" selected="selected"><?php _e( 'None', 'wpsl' ); ?></option>
263
+ <option value="dropdown"><?php _e( 'Dropdown', 'wpsl' ); ?></option>
264
+ <option value="checkboxes"><?php _e( 'Checkboxes', 'wpsl' ); ?></option>
265
+ </select>
266
+ </td>
267
+ </tr>
268
+ <tr class="wpsl-cat-restriction">
269
+ <td style="vertical-align:top;"><label for="wpsl-cat-restriction"><?php _e('Automatically restrict the returned results to one or more categories?', 'wpsl' ); ?></label></td>
270
+ <td>
271
+ <?php
272
+ $cat_restricton = '<select id="wpsl-cat-restriction" multiple="multiple" autocomplete="off">';
273
+
274
+ foreach ( $terms as $term ) {
275
+ $cat_restricton .= '<option value="' . esc_attr( $term->slug ) . '">' . esc_html( $term->name ) . '</option>';
276
+ }
277
+
278
+ $cat_restricton .= '</select>';
279
+
280
+ echo $cat_restricton;
281
+ ?>
282
+ </td>
283
+ </tr>
284
+ <tr class="wpsl-cat-selection wpsl-hide">
285
+ <td style="vertical-align:top;"><label for="wpsl-cat-selection"><?php _e('Set a selected category?', 'wpsl' ); ?></label></td>
286
+ <td>
287
+ <?php
288
+ $cat_selection = '<select id="wpsl-cat-selection" autocomplete="off">';
289
+
290
+ $cat_selection .= '<option value="" selected="selected">' . __( 'Select category', 'wpsl' ) . '</option>';
291
+
292
+ foreach ( $terms as $term ) {
293
+ $cat_selection .= '<option value="' . esc_attr( $term->slug ) . '">' . esc_html( $term->name ) . '</option>';
294
+ }
295
+
296
+ $cat_selection .= '</select>';
297
+
298
+ echo $cat_selection;
299
+ ?>
300
+ </td>
301
+ </tr>
302
+ <?php
303
+ }
304
+ ?>
305
+ <tr class="wpsl-checkbox-options wpsl-hide">
306
+ <td><label for="wpsl-checkbox-columns"><?php _e('Checkbox columns', 'wpsl' ); ?></label></td>
307
+ <td>
308
+ <?php
309
+ echo '<select id="wpsl-checkbox-columns">';
310
+
311
+ $i = 1;
312
+
313
+ while ( $i <= 4 ) {
314
+ $selected = ( $i == 3 ) ? "selected='selected'" : ''; // 3 is the default
315
+
316
+ echo '<option value="' . $i . '" ' . $selected . '>' . $i . '</option>';
317
+ $i++;
318
+ }
319
+
320
+ echo '</select>';
321
+ ?>
322
+ </td>
323
+ </tr>
324
+ <tr class="wpsl-checkbox-selection wpsl-hide">
325
+ <td><label for="wpsl-checkbox-columns"><?php _e('Set selected checkboxes', 'wpsl' ); ?></label></td>
326
+ <td>
327
+ <?php
328
+ $checkbox_selection = '<select id="wpsl-checkbox-selection" multiple="multiple" autocomplete="off">';
329
+
330
+ foreach ( $terms as $term ) {
331
+ $checkbox_selection .= '<option value="' . esc_attr( $term->slug ) . '">' . esc_html( $term->name ) . '</option>';
332
+ }
333
+
334
+ $checkbox_selection .= '</select>';
335
+
336
+ echo $checkbox_selection;
337
+ ?>
338
+ </td>
339
+ </tr>
340
+ <tr>
341
+ <td><label for="wpsl-map-type"><?php _e( 'Map type', 'wpsl' ); ?>:</label></td>
342
+ <td><?php echo $wpsl_admin->settings_page->create_dropdown( 'map_types' ); ?></td>
343
+ </tr>
344
+ </tbody>
345
+ </table>
346
+ </div>
347
+ <div id="wpsl-markers-tab">
348
+ <div class="wpsl-shortcode-markers">
349
+ <?php echo $wpsl_admin->settings_page->show_marker_options(); ?>
350
+ </div>
351
+ </div>
352
+ </div>
353
+
354
+ <p class="submit">
355
+ <input type="button" id="wpsl-insert-shortcode" class="button-primary" value="<?php echo _e( 'Insert Store Locator', 'wpsl' ); ?>" onclick="WPSL_InsertShortcode();" />
356
+ </p>
357
+ </div>
358
+
359
+ <?php
360
+
361
+ exit();
362
+ }
363
+ }
364
+
365
+ new WPSL_Shortcode_Generator();
366
  }
admin/css/style.css CHANGED
@@ -1,673 +1,673 @@
1
- @font-face {
2
- font-family: 'fontello';
3
- src: url('../font/fontello.eot?54620740');
4
- src: url('../font/fontello.eot?54620740#iefix') format('embedded-opentype'),
5
- url('../font/fontello.woff?54620740') format('woff'),
6
- url('../font/fontello.ttf?54620740') format('truetype'),
7
- url('../font/fontello.svg?54620740#fontello') format('svg');
8
- font-weight: normal;
9
- font-style: normal;
10
- }
11
-
12
- #wpsl-store-overview .widefat td,
13
- #wpsl-wrap .widefat td {
14
- padding: 12px 7px;
15
- }
16
-
17
- #wpsl-wrap.wpsl-settings h2 {
18
- margin-bottom: 15px;
19
- }
20
-
21
- #wpsl-wrap .submit {
22
- padding: 0!important;
23
- margin-bottom: -10px !important;
24
- }
25
-
26
- #wpsl-store-overview .column-action a {
27
- float: left;
28
- margin-right: 5px;
29
- }
30
-
31
- #wpsl-store-overview p.search-box {
32
- margin: 0 0 1em 0;
33
- }
34
-
35
- .column-action {
36
- width:130px;
37
- }
38
-
39
- #wpsl-delete-confirmation,
40
- .wpsl-hide {
41
- display:none;
42
- }
43
-
44
- .wpsl-preloader {
45
- float:right;
46
- margin:4px 0 0 4px;
47
- }
48
-
49
- /* Plugin nav */
50
- #wpsl-mainnav {
51
- border-bottom: 1px solid #CCCCCC;
52
- float: left;
53
- margin-bottom: 15px;
54
- padding-left: 7px;
55
- width: 99.4%;
56
- }
57
- #wpsl-mainnav li a {
58
- display: block;
59
- padding: 9px 12px;
60
- text-decoration: none;
61
- }
62
-
63
- #wpsl-mainnav li {
64
- float: left;
65
- margin: 0;
66
- }
67
-
68
- #wpsl-wrap.wpsl-add-stores label,
69
- #wpsl-wrap label {
70
- width:85px;
71
- margin-top:6px;
72
- }
73
-
74
- #wpsl-wrap.wpsl-add-stores label {
75
- float:left;
76
- }
77
- #wpsl-wrap.wpsl-add-stores p {
78
- overflow:hidden;
79
- }
80
- #wpsl-wrap.wpsl-add-stores .wpsl-radioboxes label {
81
- float:none;
82
- margin-right:10px;
83
- }
84
-
85
- #wpsl-wrap textarea {
86
- width:489px;
87
- resize:none;
88
- }
89
-
90
- .wpsl-tab #wpsl-hours, #wpsl-wrap textarea {
91
- height: 185px;
92
- }
93
-
94
- /* Todo uitzoeken voor textarea width */
95
- #wpsl-wrap .wpsl-style-input textarea {
96
- width: 509px;
97
- resize: none;
98
- margin-bottom: 12px;
99
- height: 165px;
100
- }
101
-
102
- #wpsl-style-preview {
103
- float: left;
104
- margin-bottom: 12px;
105
- }
106
-
107
- .wpsl-style-preview-error {
108
- float: left;
109
- margin: 6px 0 0 10px;
110
- color: #b91111;
111
- }
112
-
113
- .wpsl-curve {
114
- float: left;
115
- border-radius: 3px;
116
- }
117
-
118
- #wpsl-wrap.wpsl-settings .wpsl-error,
119
- .wpsl-store-meta .wpsl-error {
120
- border: 1px solid #c01313;
121
- }
122
-
123
- #wpsl-lookup-location {
124
- margin-bottom: 7px;
125
- }
126
-
127
- #wpsl-wrap input[type=text],
128
- #wpsl-wrap input[type=email],
129
- #wpsl-wrap input[type=url] {
130
- width:340px;
131
- }
132
-
133
- #wpsl-api-region,
134
- #wpsl-wrap.wpsl-settings input[type=text].textinput {
135
- width:255px;
136
- }
137
-
138
- .wpsl-add-store {
139
- float:left;
140
- width:100%;
141
- clear:both;
142
- }
143
-
144
- #wpsl-wrap .metabox-holder {
145
- float:left;
146
- margin-right:20px;
147
- }
148
- #wpsl-wrap .metabox-holder.wpsl-wide {
149
- width:100%;
150
- padding-top:0;
151
- }
152
-
153
- #wpsl-wrap .wpsl-edit-header {
154
- margin-bottom:12px;
155
- }
156
-
157
- #wpsl-wrap.wpsl-settings .metabox-holder {
158
- width:100%;
159
- }
160
-
161
- #wpsl-wrap.wpsl-settings .metabox-holder h3:hover {
162
- cursor: auto;
163
- }
164
-
165
- #wpsl-gmap-wrap {
166
- float: left;
167
- width: 100%;
168
- height: 250px;
169
- border-radius: 3px;
170
- margin-top: 0;
171
- margin-bottom: 20px;
172
- }
173
-
174
- #wpsl-map-preview #wpsl-gmap-wrap {
175
- margin: 6px 0 12px 0;
176
- }
177
-
178
- #wpsl-gmap-wrap.wpsl-styles-preview {
179
- float: none;
180
- margin: 0;
181
- border-radius: 0;
182
- clear: both;
183
- }
184
-
185
- #wpsl-style-url {
186
- display: none;
187
- margin: 20px 0 0 0;
188
- }
189
-
190
- /* Markers */
191
- .wpsl-marker-list {
192
- overflow: hidden;
193
- }
194
- .wpsl-marker-list li {
195
- float: left;
196
- padding: 10px;
197
- margin-right: 5px;
198
- text-align: center;
199
- }
200
- .wpsl-marker-list li input[type="radio"] {
201
- margin-right: 0;
202
- }
203
- .wpsl-marker-list img {
204
- display: block;
205
- margin-bottom: 7px;
206
- }
207
- .wpsl-marker-list li:hover,
208
- .wpsl-active-marker {
209
- background: #E4E4E4;
210
- border-radius:5px;
211
- cursor: pointer;
212
- }
213
-
214
- /* Settings page */
215
- #wpsl-license-form .postbox-container,
216
- #wpsl-settings-form .postbox-container {
217
- width: 535px;
218
- clear: both;
219
- }
220
- #wpsl-wrap .metabox-holder {
221
- padding-top: 0;
222
- }
223
-
224
- /* Tooltip */
225
- .wpsl-info {
226
- position: relative;
227
- margin-left: 3px;
228
- }
229
-
230
- .wpsl-info:before {
231
- content: '\e802';
232
- font-size: 14px;
233
- font-family: "fontello";
234
- font-style: normal;
235
- font-weight: normal;
236
- speak: none;
237
- display: inline-block;
238
- text-decoration: inherit;
239
- width: 1em;
240
- margin-right: .2em;
241
- text-align: center;
242
- font-variant: normal;
243
- text-transform: none;
244
- line-height: 1em;
245
- margin-left: .2em;
246
- }
247
-
248
- .wpsl-info:hover {
249
- cursor: pointer;
250
- }
251
-
252
- .wpsl-info.wpsl-required-setting:before {
253
- color: #b91111;
254
- }
255
-
256
- .wpsl-info-text {
257
- position: absolute;
258
- padding: 10px;
259
- left: -29px;
260
- bottom: 28px;
261
- color: #eee;
262
- min-width: 200px;
263
- background: #222;
264
- border-radius: 3px;
265
- line-height: 1.4em;
266
- }
267
-
268
- #wpsl-map-preview .wpsl-info-text {
269
- width: 175px;
270
- min-width: 0;
271
- left: -88px;
272
- }
273
-
274
- #wpsl-map-preview .wpsl-info-text::after{
275
- left: auto;
276
- right: 87px;
277
- }
278
-
279
- #wpsl-map-preview .wpsl-info {
280
- position: absolute;
281
- margin-left: 5px;
282
- top: 5px;
283
- }
284
-
285
- .wpsl-submit-wrap {
286
- position: relative;
287
- clear: both;
288
- }
289
-
290
- .wpsl-search-settings .wpsl-info-text {
291
- white-space: nowrap;
292
- }
293
-
294
- .wpsl-info-text:after {
295
- position: absolute;
296
- border-left: 11px solid transparent;
297
- border-right: 11px solid transparent;
298
- border-top: 11px solid #222;
299
- content: "";
300
- left: 27px;
301
- bottom: -10px;
302
- }
303
-
304
- .wpsl-info-text a {
305
- color: #fff;
306
- }
307
-
308
- #wpsl-settings-form label {
309
- position: relative;
310
- display: inline-block;
311
- font-weight: normal;
312
- margin: 0 10px 0 0;
313
- width: 220px;
314
- }
315
-
316
- #wpsl-save-settings {
317
- float: left;
318
- clear: both;
319
- }
320
- #wpsl-settings-form .wpsl-radioboxes label {
321
- float: none;
322
- margin-right: 10px;
323
- width: auto;
324
- }
325
-
326
- #wpsl-faq dt {
327
- margin-bottom: 4px;
328
- font-weight: bold;
329
- font-size: 110%;
330
- }
331
-
332
- #wpsl-faq dd {
333
- margin-left: 0;
334
- }
335
-
336
- #wpsl-faq dl {
337
- margin-bottom: 25px;
338
- }
339
-
340
- /* Overview page */
341
- .wp-list-table .column-action .button {
342
- margin: 3px 5px 3px 0;
343
- }
344
-
345
- /* Custom post type */
346
- .wpsl-store-meta p {
347
- overflow: hidden;
348
- }
349
-
350
- .wpsl-store-meta label,
351
- .wpsl-store-meta legend {
352
- float: left;
353
- width: 95px;
354
- margin-top: 3px;
355
- }
356
-
357
- .wpsl-store-meta fieldset label {
358
- display: inline-block;
359
- line-height: 1.4em;
360
- margin: 0.25em 0 0.5em !important;
361
- }
362
-
363
- .wpsl-store-meta textarea,
364
- .wpsl-store-meta input[type="text"],
365
- .wpsl-store-meta input[type="email"],
366
- .wpsl-store-meta input[type="url"] {
367
- width: 340px;
368
- }
369
-
370
- .wpsl-store-meta textarea {
371
- resize: none;
372
- }
373
-
374
- #wpsl-map-preview em,
375
- #wpsl-settings-form em,
376
- .wpsl-store-meta em {
377
- display: block;
378
- }
379
-
380
- #wpsl-settings-form .wpsl-info em {
381
- display: inline;
382
- }
383
-
384
- #wpsl-meta-nav {
385
- margin: 19px 0 6px 0;
386
- }
387
-
388
- #wpsl-meta-nav li {
389
- display:inline;
390
- margin-right:5px;
391
- }
392
-
393
- #wpsl-meta-nav li:hover {
394
- cursor: pointer;
395
- }
396
-
397
- #wpsl-meta-nav li a {
398
- padding:6px 9px;
399
- border-radius:3px 3px 0 0;
400
- border-bottom: none;
401
- text-decoration: none;
402
- outline:none;
403
- }
404
-
405
- .wpsl-tab {
406
- padding:5px 15px;
407
- display:none;
408
- border: 1px solid #eee;
409
- border-radius:0px 3px 3px 3px;
410
- }
411
-
412
- div.wpsl-active {
413
- display:block;
414
- background: #fdfdfd;
415
- }
416
-
417
- #wpsl-meta-nav .wpsl-active a {
418
- border:1px solid #eee;
419
- border-bottom:1px solid #fdfdfd;
420
- background: #fdfdfd;
421
- color:#444;
422
- }
423
-
424
- .wpsl-star {
425
- color:#c01313;
426
- }
427
-
428
- /* Opening Hours */
429
- #wpsl-store-hours {
430
- border-collapse: collapse;
431
- margin: 5px 0 20px 0;
432
- }
433
-
434
- #wpsl-settings-form #wpsl-store-hours {
435
- width: 100%;
436
- }
437
-
438
- #wpsl-store-hours div {
439
- margin: 0;
440
- padding: 3px;
441
- background: #eee;
442
- border: 1px solid #eee;
443
- border-radius: 3px;
444
- white-space: nowrap;
445
- }
446
-
447
- #wpsl-store-hours .wpsl-store-closed {
448
- border: none;
449
- background: none;
450
- margin-top: 9px;
451
- margin-bottom: 0;
452
- }
453
-
454
- .wpsl-current-period,
455
- .wpsl-add-period {
456
- float: left;
457
- }
458
-
459
- #wpsl-store-hours .wpsl-multiple-periods {
460
- float: left;
461
- clear: both;
462
- margin-top: 8px;
463
- }
464
-
465
- .wpsl-add-period span,
466
- .wpsl-current-period span {
467
- float:left;
468
- margin:6px 7px 0;
469
- }
470
-
471
- .wpsl-add-period span {
472
- margin:6px 0 0 7px;
473
- }
474
-
475
- #wpsl-store-hours .wpsl-remove-period {
476
- background:#999;
477
- border-radius: 9px;
478
- }
479
-
480
- .wpsl-add-period {
481
- border:none;
482
- background:#eee;
483
- border-radius: 3px;
484
- font-size: 13px;
485
- padding: 3px 10px;
486
- }
487
-
488
- .wpsl-default-hours {
489
- margin-top:25px;
490
- }
491
-
492
- #wpsl-store-hours select {
493
- float:left;
494
- }
495
-
496
- #wpsl-store-hours th {
497
- text-align:left;
498
- padding:8px 10px 8px 0;
499
- border-bottom:1px solid #eee;
500
- }
501
-
502
- #wpsl-settings-form #wpsl-store-hours th {
503
- text-align: left;
504
- }
505
-
506
- #wpsl-store-hours td {
507
- border-bottom:1px solid #eee;
508
- padding:7px 10px 7px 0;
509
- vertical-align: top;
510
- }
511
-
512
- #wpsl-store-hours .wpsl-opening-day {
513
- min-width:80px;
514
- padding:17px 17px 0 0;
515
- text-align:left;
516
- vertical-align:top;
517
- }
518
-
519
- .wpsl-twentyfour-format .wpsl-opening-hours {
520
- width: 197px;
521
- }
522
-
523
- .wpsl-twelve-format .wpsl-opening-hours {
524
- width: 245px;
525
- }
526
-
527
- #wpsl-settings-form #wpsl-store-hours .wpsl-opening-day {
528
- width: 150px;
529
- }
530
-
531
- #wpsl-settings-form #wpsl-store-hours td p {
532
- padding: 10px 0 0 0;
533
- margin: 0;
534
- text-align: left;
535
- }
536
-
537
- #wpsl-store-hours .wpsl-add-period {
538
- height: 30px;
539
- }
540
-
541
- .wpsl-pre-38 .wpsl-add-period {
542
- height: 27px;
543
- }
544
-
545
- #wpsl-store-hours .dashicons:hover,
546
- .wpsl-add-period:hover {
547
- cursor:pointer;
548
- }
549
-
550
- #wpsl-store-hours .dashicons {
551
- color: #999;
552
- margin: 0 3px;
553
- }
554
-
555
- #wpsl-store-hours .wpsl-add-period:hover .dashicons,
556
- #wpsl-store-hours .dashicons:hover {
557
- color: #444;
558
- }
559
-
560
- /* Fix the bottom spacing on the submit buttons */
561
- #wpsl-wrap.wpsl-pre-38 .submit {
562
- margin-bottom: 0 !important;
563
- }
564
-
565
- /* Fontello fonts */
566
- [class^="wpsl-icon-"]:before, [class*=" wpsl-icon-"]:before {
567
- font-family: "fontello";
568
- font-style: normal;
569
- font-weight: normal;
570
- speak: none;
571
- display: inline-block;
572
- text-decoration: inherit;
573
- width: 1em;
574
- margin-right: .2em;
575
- text-align: center;
576
- font-variant: normal;
577
- text-transform: none;
578
- line-height: 1em;
579
- margin-left: .2em;
580
- -webkit-font-smoothing: antialiased;
581
- -moz-osx-font-smoothing: grayscale;
582
- }
583
-
584
- [class^="wpsl-icon-"]:hover, [class*=" wpsl-icon-"]:hover {
585
- cursor: pointer;
586
- }
587
-
588
- .wpsl-icon-location:before {
589
- content: '\e801';
590
- }
591
-
592
- .wpsl-icon-attention-circled:before {
593
- content: '\e802';
594
- }
595
-
596
- .wpsl-icon-cancel-circled:before {
597
- content: '\e803';
598
- }
599
-
600
- .wpsl-icon-plus-circled:before {
601
- content: '\e805';
602
- }
603
-
604
- #wpsl-store-hours .wpsl-icon-plus-circled,
605
- #wpsl-store-hours .wpsl-icon-cancel-circled {
606
- margin-top: 1px;
607
- font-size: 18px;
608
- display: inline-block;
609
- color: #999;
610
- }
611
-
612
- #wpsl-store-hours .wpsl-icon-plus-circled:hover,
613
- #wpsl-store-hours .wpsl-icon-cancel-circled:hover {
614
- color: #444;
615
- }
616
-
617
- .wpsl-add-on {
618
- float: left;
619
- position: relative;
620
- width: 300px;
621
- height: 240px;
622
- background: #fff;
623
- margin: 20px 20px 0 0;
624
- border: 1px solid #e8e8e8;
625
- border-radius: 3px;
626
- }
627
-
628
- .wpsl-add-on p {
629
- margin-top: 0;
630
- }
631
-
632
- .wpsl-add-on img {
633
- height: auto;
634
- max-width: 100%;
635
- vertical-align: bottom;
636
- }
637
-
638
- .wpsl-add-on > a {
639
- width: 300px;
640
- display: inline-block;
641
- }
642
-
643
- .wpsl-add-on a img:hover {
644
- opacity: 0.95;
645
- }
646
-
647
- .wpsl-add-on .wpsl-add-on-desc {
648
- padding: 20px;
649
- }
650
-
651
- .wpsl-add-on-status {
652
- position: absolute;
653
- left: 20px;
654
- bottom: 20px;
655
- }
656
-
657
- .wpsl-add-on-status p {
658
- margin: 0 0 4px 0;
659
- }
660
-
661
- /* Classes to handle the API errors */
662
- .wpsl-api-error {
663
- margin-top: 13px;
664
- padding: 10px;
665
- color: #fff;
666
- border-radius: 3px;
667
- background: #c01313;
668
- }
669
-
670
- .wpsl-api-error a {
671
- font-weight: bold;
672
- color: #fff !important;
673
  }
1
+ @font-face {
2
+ font-family: 'fontello';
3
+ src: url('../font/fontello.eot?54620740');
4
+ src: url('../font/fontello.eot?54620740#iefix') format('embedded-opentype'),
5
+ url('../font/fontello.woff?54620740') format('woff'),
6
+ url('../font/fontello.ttf?54620740') format('truetype'),
7
+ url('../font/fontello.svg?54620740#fontello') format('svg');
8
+ font-weight: normal;
9
+ font-style: normal;
10
+ }
11
+
12
+ #wpsl-store-overview .widefat td,
13
+ #wpsl-wrap .widefat td {
14
+ padding: 12px 7px;
15
+ }
16
+
17
+ #wpsl-wrap.wpsl-settings h2 {
18
+ margin-bottom: 15px;
19
+ }
20
+
21
+ #wpsl-wrap .submit {
22
+ padding: 0!important;
23
+ margin-bottom: -10px !important;
24
+ }
25
+
26
+ #wpsl-store-overview .column-action a {
27
+ float: left;
28
+ margin-right: 5px;
29
+ }
30
+
31
+ #wpsl-store-overview p.search-box {
32
+ margin: 0 0 1em 0;
33
+ }
34
+
35
+ .column-action {
36
+ width:130px;
37
+ }
38
+
39
+ #wpsl-delete-confirmation,
40
+ .wpsl-hide {
41
+ display:none;
42
+ }
43
+
44
+ .wpsl-preloader {
45
+ float:right;
46
+ margin:4px 0 0 4px;
47
+ }
48
+
49
+ /* Plugin nav */
50
+ #wpsl-mainnav {
51
+ border-bottom: 1px solid #CCCCCC;
52
+ float: left;
53
+ margin-bottom: 15px;
54
+ padding-left: 7px;
55
+ width: 99.4%;
56
+ }
57
+ #wpsl-mainnav li a {
58
+ display: block;
59
+ padding: 9px 12px;
60
+ text-decoration: none;
61
+ }
62
+
63
+ #wpsl-mainnav li {
64
+ float: left;
65
+ margin: 0;
66
+ }
67
+
68
+ #wpsl-wrap.wpsl-add-stores label,
69
+ #wpsl-wrap label {
70
+ width:85px;
71
+ margin-top:6px;
72
+ }
73
+
74
+ #wpsl-wrap.wpsl-add-stores label {
75
+ float:left;
76
+ }
77
+ #wpsl-wrap.wpsl-add-stores p {
78
+ overflow:hidden;
79
+ }
80
+ #wpsl-wrap.wpsl-add-stores .wpsl-radioboxes label {
81
+ float:none;
82
+ margin-right:10px;
83
+ }
84
+
85
+ #wpsl-wrap textarea {
86
+ width:489px;
87
+ resize:none;
88
+ }
89
+
90
+ .wpsl-tab #wpsl-hours, #wpsl-wrap textarea {
91
+ height: 185px;
92
+ }
93
+
94
+ /* Todo uitzoeken voor textarea width */
95
+ #wpsl-wrap .wpsl-style-input textarea {
96
+ width: 509px;
97
+ resize: none;
98
+ margin-bottom: 12px;
99
+ height: 165px;
100
+ }
101
+
102
+ #wpsl-style-preview {
103
+ float: left;
104
+ margin-bottom: 12px;
105
+ }
106
+
107
+ .wpsl-style-preview-error {
108
+ float: left;
109
+ margin: 6px 0 0 10px;
110
+ color: #b91111;
111
+ }
112
+
113
+ .wpsl-curve {
114
+ float: left;
115
+ border-radius: 3px;
116
+ }
117
+
118
+ #wpsl-wrap.wpsl-settings .wpsl-error,
119
+ .wpsl-store-meta .wpsl-error {
120
+ border: 1px solid #c01313;
121
+ }
122
+
123
+ #wpsl-lookup-location {
124
+ margin-bottom: 7px;
125
+ }
126
+
127
+ #wpsl-wrap input[type=text],
128
+ #wpsl-wrap input[type=email],
129
+ #wpsl-wrap input[type=url] {
130
+ width:340px;
131
+ }
132
+
133
+ #wpsl-api-region,
134
+ #wpsl-wrap.wpsl-settings input[type=text].textinput {
135
+ width:255px;
136
+ }
137
+
138
+ .wpsl-add-store {
139
+ float:left;
140
+ width:100%;
141
+ clear:both;
142
+ }
143
+
144
+ #wpsl-wrap .metabox-holder {
145
+ float:left;
146
+ margin-right:20px;
147
+ }
148
+ #wpsl-wrap .metabox-holder.wpsl-wide {
149
+ width:100%;
150
+ padding-top:0;
151
+ }
152
+
153
+ #wpsl-wrap .wpsl-edit-header {
154
+ margin-bottom:12px;
155
+ }
156
+
157
+ #wpsl-wrap.wpsl-settings .metabox-holder {
158
+ width:100%;
159
+ }
160
+
161
+ #wpsl-wrap.wpsl-settings .metabox-holder h3:hover {
162
+ cursor: auto;
163
+ }
164
+
165
+ #wpsl-gmap-wrap {
166
+ float: left;
167
+ width: 100%;
168
+ height: 250px;
169
+ border-radius: 3px;
170
+ margin-top: 0;
171
+ margin-bottom: 20px;
172
+ }
173
+
174
+ #wpsl-map-preview #wpsl-gmap-wrap {
175
+ margin: 6px 0 12px 0;
176
+ }
177
+
178
+ #wpsl-gmap-wrap.wpsl-styles-preview {
179
+ float: none;
180
+ margin: 0;
181
+ border-radius: 0;
182
+ clear: both;
183
+ }
184
+
185
+ #wpsl-style-url {
186
+ display: none;
187
+ margin: 20px 0 0 0;
188
+ }
189
+
190
+ /* Markers */
191
+ .wpsl-marker-list {
192
+ overflow: hidden;
193
+ }
194
+ .wpsl-marker-list li {
195
+ float: left;
196
+ padding: 10px;
197
+ margin-right: 5px;
198
+ text-align: center;
199
+ }
200
+ .wpsl-marker-list li input[type="radio"] {
201
+ margin-right: 0;
202
+ }
203
+ .wpsl-marker-list img {
204
+ display: block;
205
+ margin-bottom: 7px;
206
+ }
207
+ .wpsl-marker-list li:hover,
208
+ .wpsl-active-marker {
209
+ background: #E4E4E4;
210
+ border-radius:5px;
211
+ cursor: pointer;
212
+ }
213
+
214
+ /* Settings page */
215
+ #wpsl-license-form .postbox-container,
216
+ #wpsl-settings-form .postbox-container {
217
+ width: 535px;
218
+ clear: both;
219
+ }
220
+ #wpsl-wrap .metabox-holder {
221
+ padding-top: 0;
222
+ }
223
+
224
+ /* Tooltip */
225
+ .wpsl-info {
226
+ position: relative;
227
+ margin-left: 3px;
228
+ }
229
+
230
+ .wpsl-info:before {
231
+ content: '\e802';
232
+ font-size: 14px;
233
+ font-family: "fontello";
234
+ font-style: normal;
235
+ font-weight: normal;
236
+ speak: none;
237
+ display: inline-block;
238
+ text-decoration: inherit;
239
+ width: 1em;
240
+ margin-right: .2em;
241
+ text-align: center;
242
+ font-variant: normal;
243
+ text-transform: none;
244
+ line-height: 1em;
245
+ margin-left: .2em;
246
+ }
247
+
248
+ .wpsl-info:hover {
249
+ cursor: pointer;
250
+ }
251
+
252
+ .wpsl-info.wpsl-required-setting:before {
253
+ color: #b91111;
254
+ }
255
+
256
+ .wpsl-info-text {
257
+ position: absolute;
258
+ padding: 10px;
259
+ left: -29px;
260
+ bottom: 28px;
261
+ color: #eee;
262
+ min-width: 200px;
263
+ background: #222;
264
+ border-radius: 3px;
265
+ line-height: 1.4em;
266
+ }
267
+
268
+ #wpsl-map-preview .wpsl-info-text {
269
+ width: 175px;
270
+ min-width: 0;
271
+ left: -88px;
272
+ }
273
+
274
+ #wpsl-map-preview .wpsl-info-text::after {
275
+ left: auto;
276
+ right: 87px;
277
+ }
278
+
279
+ #wpsl-map-preview .wpsl-info {
280
+ position: absolute;
281
+ margin-left: 5px;
282
+ top: 5px;
283
+ }
284
+
285
+ .wpsl-submit-wrap {
286
+ position: relative;
287
+ clear: both;
288
+ }
289
+
290
+ .wpsl-search-settings .wpsl-info-text {
291
+ white-space: nowrap;
292
+ }
293
+
294
+ .wpsl-info-text:after {
295
+ position: absolute;
296
+ border-left: 11px solid transparent;
297
+ border-right: 11px solid transparent;
298
+ border-top: 11px solid #222;
299
+ content: "";
300
+ left: 27px;
301
+ bottom: -10px;
302
+ }
303
+
304
+ .wpsl-info-text a {
305
+ color: #fff;
306
+ }
307
+
308
+ #wpsl-settings-form label {
309
+ position: relative;
310
+ display: inline-block;
311
+ font-weight: normal;
312
+ margin: 0 10px 0 0;
313
+ width: 220px;
314
+ }
315
+
316
+ #wpsl-save-settings {
317
+ float: left;
318
+ clear: both;
319
+ }
320
+ #wpsl-settings-form .wpsl-radioboxes label {
321
+ float: none;
322
+ margin-right: 10px;
323
+ width: auto;
324
+ }
325
+
326
+ #wpsl-faq dt {
327
+ margin-bottom: 4px;
328
+ font-weight: bold;
329
+ font-size: 110%;
330
+ }
331
+
332
+ #wpsl-faq dd {
333
+ margin-left: 0;
334
+ }
335
+
336
+ #wpsl-faq dl {
337
+ margin-bottom: 25px;
338
+ }
339
+
340
+ /* Overview page */
341
+ .wp-list-table .column-action .button {
342
+ margin: 3px 5px 3px 0;
343
+ }
344
+
345
+ /* Custom post type */
346
+ .wpsl-store-meta p {
347
+ overflow: hidden;
348
+ }
349
+
350
+ .wpsl-store-meta label,
351
+ .wpsl-store-meta legend {
352
+ float: left;
353
+ width: 95px;
354
+ margin-top: 3px;
355
+ }
356
+
357
+ .wpsl-store-meta fieldset label {
358
+ display: inline-block;
359
+ line-height: 1.4em;
360
+ margin: 0.25em 0 0.5em !important;
361
+ }
362
+
363
+ .wpsl-store-meta textarea,
364
+ .wpsl-store-meta input[type="text"],
365
+ .wpsl-store-meta input[type="email"],
366
+ .wpsl-store-meta input[type="url"] {
367
+ width: 340px;
368
+ }
369
+
370
+ .wpsl-store-meta textarea {
371
+ resize: none;
372
+ }
373
+
374
+ #wpsl-map-preview em,
375
+ #wpsl-settings-form em,
376
+ .wpsl-store-meta em {
377
+ display: block;
378
+ }
379
+
380
+ #wpsl-settings-form .wpsl-info em {
381
+ display: inline;
382
+ }
383
+
384
+ #wpsl-meta-nav {
385
+ margin: 19px 0 6px 0;
386
+ }
387
+
388
+ #wpsl-meta-nav li {
389
+ display:inline;
390
+ margin-right:5px;
391
+ }
392
+
393
+ #wpsl-meta-nav li:hover {
394
+ cursor: pointer;
395
+ }
396
+
397
+ #wpsl-meta-nav li a {
398
+ padding:6px 9px;
399
+ border-radius:3px 3px 0 0;
400
+ border-bottom: none;
401
+ text-decoration: none;
402
+ outline:none;
403
+ }
404
+
405
+ .wpsl-tab {
406
+ padding:5px 15px;
407
+ display:none;
408
+ border: 1px solid #eee;
409
+ border-radius:0px 3px 3px 3px;
410
+ }
411
+
412
+ div.wpsl-active {
413
+ display:block;
414
+ background: #fdfdfd;
415
+ }
416
+
417
+ #wpsl-meta-nav .wpsl-active a {
418
+ border:1px solid #eee;
419
+ border-bottom:1px solid #fdfdfd;
420
+ background: #fdfdfd;
421
+ color:#444;
422
+ }
423
+
424
+ .wpsl-star {
425
+ color:#c01313;
426
+ }
427
+
428
+ /* Opening Hours */
429
+ #wpsl-store-hours {
430
+ border-collapse: collapse;
431
+ margin: 5px 0 20px 0;
432
+ }
433
+
434
+ #wpsl-settings-form #wpsl-store-hours {
435
+ width: 100%;
436
+ }
437
+
438
+ #wpsl-store-hours div {
439
+ margin: 0;
440
+ padding: 3px;
441
+ background: #eee;
442
+ border: 1px solid #eee;
443
+ border-radius: 3px;
444
+ white-space: nowrap;
445
+ }
446
+
447
+ #wpsl-store-hours .wpsl-store-closed {
448
+ border: none;
449
+ background: none;
450
+ margin-top: 9px;
451
+ margin-bottom: 0;
452
+ }
453
+
454
+ .wpsl-current-period,
455
+ .wpsl-add-period {
456
+ float: left;
457
+ }
458
+
459
+ #wpsl-store-hours .wpsl-multiple-periods {
460
+ float: left;
461
+ clear: both;
462
+ margin-top: 8px;
463
+ }
464
+
465
+ .wpsl-add-period span,
466
+ .wpsl-current-period span {
467
+ float:left;
468
+ margin:6px 7px 0;
469
+ }
470
+
471
+ .wpsl-add-period span {
472
+ margin:6px 0 0 7px;
473
+ }
474
+
475
+ #wpsl-store-hours .wpsl-remove-period {
476
+ background:#999;
477
+ border-radius: 9px;
478
+ }
479
+
480
+ .wpsl-add-period {
481
+ border:none;
482
+ background:#eee;
483
+ border-radius: 3px;
484
+ font-size: 13px;
485
+ padding: 3px 10px;
486
+ }
487
+
488
+ .wpsl-default-hours {
489
+ margin-top:25px;
490
+ }
491
+
492
+ #wpsl-store-hours select {
493
+ float:left;
494
+ }
495
+
496
+ #wpsl-store-hours th {
497
+ text-align:left;
498
+ padding:8px 10px 8px 0;
499
+ border-bottom:1px solid #eee;
500
+ }
501
+
502
+ #wpsl-settings-form #wpsl-store-hours th {
503
+ text-align: left;
504
+ }
505
+
506
+ #wpsl-store-hours td {
507
+ border-bottom:1px solid #eee;
508
+ padding:7px 10px 7px 0;
509
+ vertical-align: top;
510
+ }
511
+
512
+ #wpsl-store-hours .wpsl-opening-day {
513
+ min-width:80px;
514
+ padding:17px 17px 0 0;
515
+ text-align:left;
516
+ vertical-align:top;
517
+ }
518
+
519
+ .wpsl-twentyfour-format .wpsl-opening-hours {
520
+ width: 197px;
521
+ }
522
+
523
+ .wpsl-twelve-format .wpsl-opening-hours {
524
+ width: 245px;
525
+ }
526
+
527
+ #wpsl-settings-form #wpsl-store-hours .wpsl-opening-day {
528
+ width: 150px;
529
+ }
530
+
531
+ #wpsl-settings-form #wpsl-store-hours td p {
532
+ padding: 10px 0 0 0;
533
+ margin: 0;
534
+ text-align: left;
535
+ }
536
+
537
+ #wpsl-store-hours .wpsl-add-period {
538
+ height: 30px;
539
+ }
540
+
541
+ .wpsl-pre-38 .wpsl-add-period {
542
+ height: 27px;
543
+ }
544
+
545
+ #wpsl-store-hours .dashicons:hover,
546
+ .wpsl-add-period:hover {
547
+ cursor:pointer;
548
+ }
549
+
550
+ #wpsl-store-hours .dashicons {
551
+ color: #999;
552
+ margin: 0 3px;
553
+ }
554
+
555
+ #wpsl-store-hours .wpsl-add-period:hover .dashicons,
556
+ #wpsl-store-hours .dashicons:hover {
557
+ color: #444;
558
+ }
559
+
560
+ /* Fix the bottom spacing on the submit buttons */
561
+ #wpsl-wrap.wpsl-pre-38 .submit {
562
+ margin-bottom: 0 !important;
563
+ }
564
+
565
+ /* Fontello fonts */
566
+ [class^="wpsl-icon-"]:before, [class*=" wpsl-icon-"]:before {
567
+ font-family: "fontello";
568
+ font-style: normal;
569
+ font-weight: normal;
570
+ speak: none;
571
+ display: inline-block;
572
+ text-decoration: inherit;
573
+ width: 1em;
574
+ margin-right: .2em;
575
+ text-align: center;
576
+ font-variant: normal;
577
+ text-transform: none;
578
+ line-height: 1em;
579
+ margin-left: .2em;
580
+ -webkit-font-smoothing: antialiased;
581
+ -moz-osx-font-smoothing: grayscale;
582
+ }
583
+
584
+ [class^="wpsl-icon-"]:hover, [class*=" wpsl-icon-"]:hover {
585
+ cursor: pointer;
586
+ }
587
+
588
+ .wpsl-icon-location:before {
589
+ content: '\e801';
590
+ }
591
+
592
+ .wpsl-icon-attention-circled:before {
593
+ content: '\e802';
594
+ }
595
+
596
+ .wpsl-icon-cancel-circled:before {
597
+ content: '\e803';
598
+ }
599
+
600
+ .wpsl-icon-plus-circled:before {
601
+ content: '\e805';
602
+ }
603
+
604
+ #wpsl-store-hours .wpsl-icon-plus-circled,
605
+ #wpsl-store-hours .wpsl-icon-cancel-circled {
606
+ margin-top: 1px;
607
+ font-size: 18px;
608
+ display: inline-block;
609
+ color: #999;
610
+ }
611
+
612
+ #wpsl-store-hours .wpsl-icon-plus-circled:hover,
613
+ #wpsl-store-hours .wpsl-icon-cancel-circled:hover {
614
+ color: #444;
615
+ }
616
+
617
+ .wpsl-add-on {
618
+ float: left;
619
+ position: relative;
620
+ width: 300px;
621
+ height: 240px;
622
+ background: #fff;
623
+ margin: 20px 20px 0 0;
624
+ border: 1px solid #e8e8e8;
625
+ border-radius: 3px;
626
+ }
627
+
628
+ .wpsl-add-on p {
629
+ margin-top: 0;
630
+ }
631
+
632
+ .wpsl-add-on img {
633
+ height: auto;
634
+ max-width: 100%;
635
+ vertical-align: bottom;
636
+ }
637
+
638
+ .wpsl-add-on > a {
639
+ width: 300px;
640
+ display: inline-block;
641
+ }
642
+
643
+ .wpsl-add-on a img:hover {
644
+ opacity: 0.95;
645
+ }
646
+
647
+ .wpsl-add-on .wpsl-add-on-desc {
648
+ padding: 20px;
649
+ }
650
+
651
+ .wpsl-add-on-status {
652
+ position: absolute;
653
+ left: 20px;
654
+ bottom: 20px;
655
+ }
656
+
657
+ .wpsl-add-on-status p {
658
+ margin: 0 0 4px 0;
659
+ }
660
+
661
+ /* Classes to handle the API errors */
662
+ .wpsl-api-error {
663
+ margin-top: 13px;
664
+ padding: 10px;
665
+ color: #fff;
666
+ border-radius: 3px;
667
+ background: #c01313;
668
+ }
669
+
670
+ .wpsl-api-error a {
671
+ font-weight: bold;
672
+ color: #fff !important;
673
  }
admin/data-export.php CHANGED
@@ -1,65 +1,65 @@
1
- <?php
2
- add_action( 'admin_init', 'wpsl_single_location_export' );
3
-
4
- /**
5
- * Handle the export of a single store location.
6
- *
7
- * Creates a CSV file holding the location details
8
- * that can be handed over in case a GDPR related
9
- * data access request is received.
10
- *
11
- * @since 2.2.15
12
- * @return void
13
- */
14
- function wpsl_single_location_export() {
15
-
16
- if ( isset( $_GET['wpsl_data_export'] ) && isset( $_GET['wpsl_export_nonce'] ) ) {
17
- $post_id = absint( $_GET['post'] );
18
-
19
- if ( !wp_verify_nonce( $_GET['wpsl_export_nonce'], 'wpsl_export_' . $post_id ) )
20
- return;
21
-
22
- if ( is_int( wp_is_post_revision( $post_id ) ) )
23
- return;
24
-
25
- if ( !current_user_can( 'edit_post', $post_id ) )
26
- return;
27
-
28
- $meta_fields = wpsl_get_field_names( false );
29
- $meta_data = get_post_custom( $post_id );
30
- $post_meta = '';
31
-
32
- // Loop over the wpsl meta fields, and collect the meta data.
33
- foreach ( $meta_fields as $meta_field ) {
34
- if ( $meta_field !== 'hours' ) {
35
- if ( isset( $meta_data['wpsl_' . $meta_field][0] ) ) {
36
- $post_meta['data'][$meta_field] = $meta_data['wpsl_' . $meta_field][0];
37
- } else {
38
- $post_meta['data'][$meta_field] = '';
39
- }
40
-
41
- $post_meta['headers'][] = $meta_field;
42
- }
43
- }
44
-
45
- // Make it possible to add additional custom data from for example ACF
46
- $post_meta = apply_filters( 'wpsl_single_location_export_data', $post_meta, $post_id );
47
-
48
- if ( $post_meta ) {
49
- $file_name = 'wpsl-export-' . $post_id . '-' . date('Ymd' ) . '.csv';
50
-
51
- // Set the download headers for the CSV file.
52
- header( 'Content-Type: text/csv; charset=utf-8' );
53
- header( 'Content-Disposition: attachment; filename=' . $file_name . '' );
54
-
55
- $output = fopen( 'php://output', 'w' );
56
-
57
- fputcsv( $output, $post_meta['headers'] );
58
- fputcsv( $output, $post_meta['data'] );
59
-
60
- fclose( $output );
61
- }
62
-
63
- exit();
64
- }
65
  }
1
+ <?php
2
+ add_action( 'admin_init', 'wpsl_single_location_export' );
3
+
4
+ /**
5
+ * Handle the export of a single store location.
6
+ *
7
+ * Creates a CSV file holding the location details
8
+ * that can be handed over in case a GDPR related
9
+ * data access request is received.
10
+ *
11
+ * @since 2.2.15
12
+ * @return void
13
+ */
14
+ function wpsl_single_location_export() {
15
+
16
+ if ( isset( $_GET['wpsl_data_export'] ) && isset( $_GET['wpsl_export_nonce'] ) ) {
17
+ $post_id = absint( $_GET['post'] );
18
+
19
+ if ( !wp_verify_nonce( $_GET['wpsl_export_nonce'], 'wpsl_export_' . $post_id ) )
20
+ return;
21
+
22
+ if ( is_int( wp_is_post_revision( $post_id ) ) )
23
+ return;
24
+
25
+ if ( !current_user_can( 'edit_post', $post_id ) )
26
+ return;
27
+
28
+ $meta_fields = wpsl_get_field_names( false );
29
+ $meta_data = get_post_custom( $post_id );
30
+ $post_meta = '';
31
+
32
+ // Loop over the wpsl meta fields, and collect the meta data.
33
+ foreach ( $meta_fields as $meta_field ) {
34
+ if ( $meta_field !== 'hours' ) {
35
+ if ( isset( $meta_data['wpsl_' . $meta_field][0] ) ) {
36
+ $post_meta['data'][$meta_field] = $meta_data['wpsl_' . $meta_field][0];
37
+ } else {
38
+ $post_meta['data'][$meta_field] = '';
39
+ }
40
+
41
+ $post_meta['headers'][] = $meta_field;
42
+ }
43
+ }
44
+
45
+ // Make it possible to add additional custom data from for example ACF
46
+ $post_meta = apply_filters( 'wpsl_single_location_export_data', $post_meta, $post_id );
47
+
48
+ if ( $post_meta ) {
49
+ $file_name = 'wpsl-export-' . $post_id . '-' . date('Ymd' ) . '.csv';
50
+
51
+ // Set the download headers for the CSV file.
52
+ header( 'Content-Type: text/csv; charset=utf-8' );
53
+ header( 'Content-Disposition: attachment; filename=' . $file_name . '' );
54
+
55
+ $output = fopen( 'php://output', 'w' );
56
+
57
+ fputcsv( $output, $post_meta['headers'] );
58
+ fputcsv( $output, $post_meta['data'] );
59
+
60
+ fclose( $output );
61
+ }
62
+
63
+ exit();
64
+ }
65
  }
admin/js/ajax-queue.js CHANGED
@@ -1,54 +1,54 @@
1
- /*
2
- * jQuery.ajaxQueue - A queue for ajax requests
3
- *
4
- * (c) 2011 Corey Frang
5
- * Dual licensed under the MIT and GPL licenses.
6
- *
7
- * Requires jQuery 1.5+
8
- */
9
- (function($) {
10
-
11
- // jQuery on an empty object, we are going to use this as our Queue
12
- var ajaxQueue = $({});
13
-
14
- $.ajaxQueue = function( ajaxOpts ) {
15
- var jqXHR,
16
- dfd = $.Deferred(),
17
- promise = dfd.promise();
18
-
19
- // run the actual query
20
- function doRequest( next ) {
21
- jqXHR = $.ajax( ajaxOpts );
22
- jqXHR.done( dfd.resolve )
23
- .fail( dfd.reject )
24
- .then( next, next );
25
- }
26
-
27
- // queue our ajax request
28
- ajaxQueue.queue( doRequest );
29
-
30
- // add the abort method
31
- promise.abort = function( statusText ) {
32
-
33
- // proxy abort to the jqXHR if it is active
34
- if ( jqXHR ) {
35
- return jqXHR.abort( statusText );
36
- }
37
-
38
- // if there wasn't already a jqXHR we need to remove from queue
39
- var queue = ajaxQueue.queue(),
40
- index = $.inArray( doRequest, queue );
41
-
42
- if ( index > -1 ) {
43
- queue.splice( index, 1 );
44
- }
45
-
46
- // and then reject the deferred
47
- dfd.rejectWith( ajaxOpts.context || ajaxOpts, [ promise, statusText, "" ] );
48
- return promise;
49
- };
50
-
51
- return promise;
52
- };
53
-
54
- })(jQuery);
1
+ /*
2
+ * jQuery.ajaxQueue - A queue for ajax requests
3
+ *
4
+ * (c) 2011 Corey Frang
5
+ * Dual licensed under the MIT and GPL licenses.
6
+ *
7
+ * Requires jQuery 1.5+
8
+ */
9
+ (function($) {
10
+
11
+ // jQuery on an empty object, we are going to use this as our Queue
12
+ var ajaxQueue = $({});
13
+
14
+ $.ajaxQueue = function( ajaxOpts ) {
15
+ var jqXHR,
16
+ dfd = $.Deferred(),
17
+ promise = dfd.promise();
18
+
19
+ // run the actual query
20
+ function doRequest( next ) {
21
+ jqXHR = $.ajax( ajaxOpts );
22
+ jqXHR.done( dfd.resolve )
23
+ .fail( dfd.reject )
24
+ .then( next, next );
25
+ }
26
+
27
+ // queue our ajax request
28
+ ajaxQueue.queue( doRequest );
29
+
30
+ // add the abort method
31
+ promise.abort = function( statusText ) {
32
+
33
+ // proxy abort to the jqXHR if it is active
34
+ if ( jqXHR ) {
35
+ return jqXHR.abort( statusText );
36
+ }
37
+
38
+ // if there wasn't already a jqXHR we need to remove from queue
39
+ var queue = ajaxQueue.queue(),
40
+ index = $.inArray( doRequest, queue );
41
+
42
+ if ( index > -1 ) {
43
+ queue.splice( index, 1 );
44
+ }
45
+
46
+ // and then reject the deferred
47
+ dfd.rejectWith( ajaxOpts.context || ajaxOpts, [ promise, statusText, "" ] );
48
+ return promise;
49
+ };
50
+
51
+ return promise;
52
+ };
53
+
54
+ })(jQuery);
admin/js/wpsl-admin.js CHANGED
@@ -1,946 +1,929 @@
1
  jQuery( document ).ready( function( $ ) {
2
- var map, geocoder, markersArray = [];
3
 
4
- if ( $( "#wpsl-gmap-wrap" ).length ) {
5
- initializeGmap();
6
- }
7
 
8
- /*
9
- * If we're on the settings page, then check for returned
10
- * browser key errors from the autocomplete field
11
- * and validate the server key when necessary.
12
- */
13
- if ( $( "#wpsl-map-settings").length ) {
14
- observeBrowserKeyErrors();
 
 
 
 
 
 
 
 
 
15
 
16
  /**
17
- * Check if we need to validate the server key, and if no
18
- * error message is already visible after saving the setting page.
 
 
19
  */
20
- if ( $( "#wpsl-api-server-key" ).hasClass( "wpsl-validate-me" ) && !$( "#setting-error-server-key" ).length ) {
21
- validateServerKey();
22
- }
23
- }
24
-
25
- /**
26
- * Initialize the map with the correct settings.
27
- *
28
- * @since 1.0.0
29
- * @returns {void}
30
- */
31
- function initializeGmap() {
32
- var defaultLatLng = wpslSettings.defaultLatLng.split( "," ),
33
- latLng = new google.maps.LatLng( defaultLatLng[0], defaultLatLng[1] ),
34
- mapOptions = {};
35
-
36
- mapOptions = {
37
- zoom: parseInt( wpslSettings.defaultZoom ),
38
- center: latLng,
39
- mapTypeId: google.maps.MapTypeId[ wpslSettings.mapType.toUpperCase() ],
40
- mapTypeControl: false,
41
- streetViewControl: false,
42
- zoomControlOptions: {
43
- position: google.maps.ControlPosition.RIGHT_TOP
44
- }
45
- };
46
-
47
- geocoder = new google.maps.Geocoder();
48
- map = new google.maps.Map( document.getElementById( "wpsl-gmap-wrap" ), mapOptions );
49
-
50
- checkEditStoreMarker();
51
- }
52
-
53
- /**
54
- * Check if we have an existing latlng value.
55
- *
56
- * If there is an latlng value, then we add a marker to the map.
57
- * This can only happen on the edit store page.
58
- *
59
- * @since 1.0.0
60
- * @returns {void}
61
- */
62
- function checkEditStoreMarker() {
63
- var location,
64
- lat = $( "#wpsl-lat" ).val(),
65
- lng = $( "#wpsl-lng" ).val();
66
-
67
- if ( ( lat ) && ( lng ) ) {
68
- location = new google.maps.LatLng( lat, lng );
69
-
70
- map.setCenter( location );
71
- map.setZoom( 16 );
72
- addMarker( location );
73
- }
74
- }
75
 
76
  // If we have a city/country input field enable the autocomplete.
77
- if ( $( "#wpsl-start-name" ).length ) {
78
- activateAutoComplete();
79
- }
80
-
81
- /**
82
- * Activate the autocomplete function for the city/country field.
83
- *
84
- * @since 1.0.0
85
- * @returns {void}
86
- */
87
- function activateAutoComplete() {
88
- var latlng,
89
- input = document.getElementById( "wpsl-start-name" ),
90
- options = {
91
- types: ['geocode']
92
- },
93
- autocomplete = new google.maps.places.Autocomplete( input, options );
94
-
95
- google.maps.event.addListener( autocomplete, "place_changed", function() {
96
- latlng = autocomplete.getPlace().geometry.location;
97
- setLatlng( latlng, "zoom" );
98
- });
99
- }
100
-
101
- /**
102
- * Add a new marker to the map based on the provided location (latlng).
103
- *
104
- * @since 1.0.0
105
- * @param {object} location The latlng value
106
- * @returns {void}
107
- */
108
- function addMarker( location ) {
109
- var marker = new google.maps.Marker({
110
- position: location,
111
- map: map,
112
- draggable: true
113
- });
114
-
115
- markersArray.push( marker );
116
-
117
- // If the marker is dragged on the map, make sure the latlng values are updated.
118
- google.maps.event.addListener( marker, "dragend", function() {
119
- setLatlng( marker.getPosition(), "store" );
120
- });
121
- }
122
 
123
  // Lookup the provided location with the Google Maps API.
124
- $( "#wpsl-lookup-location" ).on( "click", function( e ) {
125
- e.preventDefault();
126
- codeAddress();
127
- });
128
-
129
- /**
130
- * Update the hidden input field with the current latlng values.
131
- *
132
- * @since 1.0.0
133
- * @param {object} latLng The latLng values
134
- * @param {string} target The location where we need to set the latLng
135
- * @returns {void}
136
- */
137
- function setLatlng( latLng, target ) {
138
- var coordinates = stripCoordinates( latLng ),
139
- lat = roundCoordinate( coordinates[0] ),
140
- lng = roundCoordinate( coordinates[1] );
141
-
142
- if ( target == "store" ) {
143
- $( "#wpsl-lat" ).val( lat );
144
- $( "#wpsl-lng" ).val( lng );
145
- } else if ( target == "zoom" ) {
146
- $( "#wpsl-latlng" ).val( lat + ',' + lng );
147
- }
148
- }
149
-
150
- /**
151
- * Geocode the user input.
152
- *
153
- * @since 1.0.0
154
- * @returns {void}
155
- */
156
- function codeAddress() {
157
- var filteredResponse, geocodeAddress;
158
-
159
- // Check if we have all the required data before attempting to geocode the address.
160
- if ( !validatePreviewFields() ) {
161
- geocodeAddress = createGeocodeAddress();
162
-
163
- geocoder.geocode( { 'address': geocodeAddress }, function( response, status ) {
164
- if ( status === google.maps.GeocoderStatus.OK ) {
165
-
166
- // If we have a previous marker on the map we remove it.
167
- if ( typeof( markersArray[0] ) !== "undefined" ) {
168
- if ( markersArray[0].draggable ) {
169
- markersArray[0].setMap( null );
170
- markersArray.splice(0, 1);
171
- }
172
- }
173
-
174
- // Center and zoom to the searched location.
175
- map.setCenter( response[0].geometry.location );
176
- map.setZoom( 16 );
177
-
178
- addMarker( response[0].geometry.location );
179
- setLatlng( response[0].geometry.location, "store" );
180
-
181
- filteredResponse = filterApiResponse( response );
182
-
183
- $( "#wpsl-country" ).val( filteredResponse.country.long_name );
184
- $( "#wpsl-country_iso" ).val( filteredResponse.country.short_name );
185
- } else {
186
- alert( wpslL10n.geocodeFail + ": " + status );
187
- }
188
- });
189
-
190
- return false;
191
- } else {
192
- activateStoreTab( "first" );
193
-
194
- alert( wpslL10n.missingGeoData );
195
-
196
- return true;
197
- }
198
- }
199
-
200
- /**
201
- * Check that all required fields for the map preview are there.
202
- *
203
- * @since 1.0.0
204
- * @returns {boolean} error Whether all the required fields contained data.
205
- */
206
- function validatePreviewFields() {
207
- var i, fieldData, requiredFields,
208
- error = false;
209
-
210
- $( ".wpsl-store-meta input" ).removeClass( "wpsl-error" );
211
-
212
- // Check which fields are required.
213
- if ( typeof wpslSettings.requiredFields !== "undefined" && _.isArray( wpslSettings.requiredFields ) ) {
214
- requiredFields = wpslSettings.requiredFields;
215
-
216
- // Check if all the required fields contain data.
217
- for ( i = 0; i < requiredFields.length; i++ ) {
218
- fieldData = $.trim( $( "#wpsl-" + requiredFields[i] ).val() );
219
-
220
- if ( !fieldData ) {
221
- $( "#wpsl-" + requiredFields[i] ).addClass( "wpsl-error" );
222
- error = true;
 
 
 
223
  }
 
224
 
225
- fieldData = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  }
228
 
229
- return error;
230
- }
231
-
232
- /**
233
- * Build the address that's send to the Geocode API.
234
- *
235
- * @since 2.1.0
236
- * @returns {string} geocodeAddress The address separated by , that's send to the Geocoder.
237
- */
238
- function createGeocodeAddress() {
239
- var i, part,
240
- address = [],
241
- addressParts = [ "address", "city", "state", "zip", "country" ];
242
-
243
- for ( i = 0; i < addressParts.length; i++ ) {
244
- part = $.trim( $( "#wpsl-" + addressParts[i] ).val() );
245
-
246
- /*
247
- * At this point we already know the address, city and country fields contain data.
248
- * But no need to include the zip and state if they are empty.
249
- */
250
- if ( part ) {
251
- address.push( part );
252
- }
253
-
254
- part = "";
255
- }
256
-
257
- return address.join();
258
- }
259
-
260
- /**
261
- * Filter out the country name from the API response.
262
- *
263
- * @since 1.0.0
264
- * @param {object} response The response of the geocode API
265
- * @returns {object} collectedData The short and long country name
266
- */
267
- function filterApiResponse( response ) {
268
- var i, responseType,
269
- country = {},
270
- collectedData = {},
271
- addressLength = response[0].address_components.length;
272
-
273
- // Loop over the API response.
274
- for ( i = 0; i < addressLength; i++ ) {
275
- responseType = response[0].address_components[i].types;
276
-
277
- // Filter out the country name.
278
- if ( /^country,political$/.test( responseType ) ) {
279
- country = {
280
- long_name: response[0].address_components[i].long_name,
281
- short_name: response[0].address_components[i].short_name
282
- };
283
- }
284
- }
285
-
286
- collectedData = {
287
- country: country
288
- };
289
-
290
- return collectedData;
291
- }
292
-
293
- /**
294
- * Round the coordinate to 6 digits after the comma.
295
- *
296
- * @since 1.0.0
297
- * @param {string} coordinate The coordinate
298
- * @returns {number} roundedCoord The rounded coordinate
299
- */
300
- function roundCoordinate( coordinate ) {
301
- var roundedCoord, decimals = 6;
302
-
303
- roundedCoord = Math.round( coordinate * Math.pow( 10, decimals ) ) / Math.pow( 10, decimals );
304
-
305
- return roundedCoord;
306
- }
307
-
308
- /**
309
- * Strip the '(' and ')' from the captured coordinates and split them.
310
- *
311
- * @since 1.0.0
312
- * @param {string} coordinates The coordinates
313
- * @returns {object} latLng The latlng coordinates
314
- */
315
- function stripCoordinates( coordinates ) {
316
- var latLng = [],
317
- selected = coordinates.toString(),
318
- latLngStr = selected.split( ",", 2 );
319
-
320
- latLng[0] = latLngStr[0].replace( "(", "" );
321
- latLng[1] = latLngStr[1].replace( ")", "" );
322
-
323
- return latLng;
324
- }
325
-
326
- $( ".wpsl-marker-list input[type=radio]" ).click( function() {
327
- $( this ).parents( ".wpsl-marker-list" ).find( "li" ).removeClass();
328
- $( this ).parent( "li" ).addClass( "wpsl-active-marker" );
329
- });
330
-
331
- $( ".wpsl-marker-list li" ).click( function() {
332
- $( this ).parents( ".wpsl-marker-list" ).find( "input" ).prop( "checked", false );
333
- $( this ).find( "input" ).prop( "checked", true );
334
- $( this ).siblings().removeClass();
335
- $( this ).addClass( "wpsl-active-marker" );
336
- });
337
-
338
- // Handle a click on the dismiss button. So that the warning msg that no starting point is set is disabled.
339
- $( ".wpsl-dismiss" ).click( function() {
340
- var $link = $( this ),
341
- data = {
342
- action: "disable_location_warning",
343
- _ajax_nonce: $link.attr( "data-nonce" )
344
- };
345
-
346
- $.post( ajaxurl, data );
347
-
348
- $( ".wpsl-dismiss" ).parents( ".error" ).remove();
349
-
350
- return false;
351
- });
352
 
353
  // Detect changes in checkboxes that have a conditional option.
354
- $( ".wpsl-has-conditional-option" ).on( "change", function() {
355
- $( this ).parent().next( ".wpsl-conditional-option" ).toggle();
356
- });
357
-
358
- /*
359
- * Detect changes to the store template dropdown. If the template is selected to
360
- * show the store list under the map then we show the option to hide the scrollbar.
361
- */
362
- $( "#wpsl-store-template" ).on( "change", function() {
363
- var $scrollOption = $( "#wpsl-listing-below-no-scroll" );
364
-
365
- if ( $( this ).val() == "below_map" ) {
366
- $scrollOption.show();
367
- } else {
368
- $scrollOption.hide();
369
- }
370
- });
371
-
372
- $( "#wpsl-api-region" ).on( "change", function() {
373
- var $geocodeComponent = $( "#wpsl-geocode-component" );
374
-
375
- if ( $( this ).val() ) {
376
- $geocodeComponent.show();
377
- } else {
378
- $geocodeComponent.hide();
379
- }
380
- });
381
 
382
  // Make sure the correct hour input format is visible.
383
- $( "#wpsl-editor-hour-input" ).on( "change", function() {
384
- $( ".wpsl-" + $( this ).val() + "-hours" ).show().siblings( "div" ).hide();
385
- $( ".wpsl-hour-notice" ).toggle();
386
- });
387
 
388
  // Set the correct tab to active and show the correct content block.
389
- $( "#wpsl-meta-nav li" ).on( "click", function( e ) {
390
- var activeClass = $( this ).attr( "class" );
391
- activeClass = activeClass.split( "-tab" );
 
 
392
 
393
- e.stopPropagation();
394
-
395
- // Set the correct tab and metabox to active.
396
- $( this ).addClass( "wpsl-active" ).siblings().removeClass( "wpsl-active" );
397
- $( ".wpsl-store-meta ." + activeClass[0] + "" ).show().addClass( "wpsl-active" ).siblings( "div" ).hide().removeClass( "wpsl-active" );
398
- });
399
 
400
  // Make sure the required store fields contain data.
401
- if ( $( "#wpsl-store-details" ).length ) {
402
- $( "#publish" ).click( function() {
403
- var firstErrorElem, currentTabClass, elemClass,
404
- errorMsg = '<div id="message" class="error"><p>' + wpslL10n.requiredFields + '</p></div>',
405
- missingData = false;
406
-
407
- // Remove error messages and css classes from previous submissions.
408
- $( "#wpbody-content .wrap #message" ).remove();
409
- $( ".wpsl-required" ).removeClass( "wpsl-error" );
410
-
411
- // Loop over the required fields and check for a value.
412
- $( ".wpsl-required" ).each( function() {
413
- if ( $( this ).val() == "" ) {
414
- $( this ).addClass( "wpsl-error" );
415
-
416
- if ( typeof firstErrorElem === "undefined" ) {
417
- firstErrorElem = getFirstErrorElemAttr( $( this ) );
418
- }
419
-
420
- missingData = true;
421
- }
422
- });
423
-
424
- // If one of the required fields are empty, then show the error msg and make sure the correct tab is visible.
425
- if ( missingData ) {
426
- $( "#wpbody-content .wrap > h2" ).after( errorMsg );
427
-
428
- if ( typeof firstErrorElem.val !== "undefined" ) {
429
- if ( firstErrorElem.type == "id" ) {
430
- currentTabClass = $( "#" + firstErrorElem.val + "" ).parents( ".wpsl-tab" ).attr( "class" );
431
- $( "html, body" ).scrollTop( Math.round( $( "#" + firstErrorElem.val + "" ).offset().top - 100 ) );
432
- } else if ( firstErrorElem.type == "class" ) {
433
- elemClass = firstErrorElem.val.replace( /wpsl-required|wpsl-error/g, "" );
434
- currentTabClass = $( "." + elemClass + "" ).parents( ".wpsl-tab" ).attr( "class" );
435
- $( "html, body" ).scrollTop( Math.round( $( "." + elemClass + "" ).offset().top - 100 ) );
436
- }
437
-
438
- currentTabClass = $.trim( currentTabClass.replace( /wpsl-tab|wpsl-active/g, "" ) );
439
- }
440
-
441
- // If we don't have a class of the tab that should be set to visible, we just show the first one.
442
- if ( !currentTabClass ) {
443
- activateStoreTab( 'first' );
444
- } else {
445
- activateStoreTab( currentTabClass );
446
- }
447
-
448
- /*
449
- * If not all required fields contains data, and the user has
450
- * clicked the submit button. Then an extra css class is added to the
451
- * button that will disabled it. This only happens in WP 3.8 or earlier.
452
- *
453
- * We need to make sure this css class doesn't exist otherwise
454
- * the user can never resubmit the page.
455
- */
456
- $( "#publish" ).removeClass( "button-primary-disabled" );
457
- $( ".spinner" ).hide();
458
-
459
- return false;
460
- } else {
461
- return true;
462
- }
463
- });
464
- }
465
-
466
- /**
467
- * Set the correct tab to visible, and hide all other metaboxes
468
- *
469
- * @since 2.0.0
470
- * @param {string} $target The name of the tab to show
471
- * @returns {void}
472
- */
473
- function activateStoreTab( $target ) {
474
- if ( $target == 'first' ) {
475
- $target = ':first-child';
476
- } else {
477
- $target = '.' + $target;
478
- }
479
-
480
- if ( !$( "#wpsl-meta-nav li" + $target + "-tab" ).hasClass( "wpsl-active" ) ) {
481
- $( "#wpsl-meta-nav li" + $target + "-tab" ).addClass( "wpsl-active" ).siblings().removeClass( "wpsl-active" );
482
- $( ".wpsl-store-meta > div" + $target + "" ).show().addClass( "wpsl-active" ).siblings( "div" ).hide().removeClass( "wpsl-active" );
483
- }
484
- }
485
-
486
- /**
487
- * Get the id or class of the first element that's an required field, but is empty.
488
- *
489
- * We need this to determine which tab we need to set active,
490
- * which will be the tab were the first error occured.
491
- *
492
- * @since 2.0.0
493
- * @param {object} elem The element the error occured on
494
- * @returns {object} firstErrorElem The id/class set on the first elem that an error occured on and the attr value
495
- */
496
- function getFirstErrorElemAttr( elem ) {
497
- var firstErrorElem = { "type": "id", "val" : elem.attr( "id" ) };
498
-
499
- // If no ID value exists, then check if we can get the class name.
500
- if ( typeof firstErrorElem.val === "undefined" ) {
501
- firstErrorElem = { "type": "class", "val" : elem.attr( "class" ) };
502
- }
503
-
504
- return firstErrorElem;
505
- }
506
 
507
  // If we have a store hours dropdown, init the event handler.
508
- if ( $( "#wpsl-store-hours" ).length ) {
509
- initHourEvents();
510
- }
511
-
512
- /**
513
- * Assign an event handler to the button that enables
514
- * users to remove an opening hour period.
515
- *
516
- * @since 2.0.0
517
- * @returns {void}
518
- */
519
- function initHourEvents() {
520
- $( "#wpsl-store-hours .wpsl-icon-cancel-circled" ).off();
521
- $( "#wpsl-store-hours .wpsl-icon-cancel-circled" ).on( "click", function() {
522
- removePeriod( $( this ) );
523
- });
524
- }
525
 
526
  // Add new openings period to the openings hours table.
527
- $( ".wpsl-add-period" ).on( "click", function( e ) {
528
- var newPeriod,
529
- hours = {},
530
- returnList = true,
531
- $tr = $( this ).parents( "tr" ),
532
- periodCount = currentPeriodCount( $( this ) ),
533
- periodCss = ( periodCount >= 1 ) ? "wpsl-current-period wpsl-multiple-periods" : "wpsl-current-period",
534
- day = $tr.find( ".wpsl-opening-hours" ).attr( "data-day" ),
535
- selectName = ( $( "#wpsl-settings-form" ).length ) ? "wpsl_editor[dropdown]" : "wpsl[hours]";
536
-
537
- newPeriod = '<div class="' + periodCss +'">';
538
- newPeriod += '<select autocomplete="off" name="' + selectName + '[' + day + '_open][]" class="wpsl-open-hour">' + createHourOptionList( returnList ) + '</select>';
539
- newPeriod += '<span> - </span>';
540
- newPeriod += '<select autocomplete="off" name="' + selectName + '[' + day + '_close][]" class="wpsl-close-hour">' + createHourOptionList( returnList ) + '</select>';
541
- newPeriod += '<div class="wpsl-icon-cancel-circled"></div>';
542
- newPeriod += '</div>';
543
-
544
- $tr.find( ".wpsl-store-closed" ).remove();
545
- $( "#wpsl-hours-" + day + "" ).append( newPeriod ).end();
546
-
547
- initHourEvents();
548
-
549
- if ( $( "#wpsl-editor-hour-format" ).val() == 24 ) {
550
- hours = {
551
- "open": "09:00",
552
- "close": "17:00"
553
- };
554
- } else {
555
- hours = {
556
- "open": "9:00 AM",
557
- "close": "5:00 PM"
558
- };
559
- }
560
-
561
- $tr.find( ".wpsl-open-hour:last option[value='" + hours.open + "']" ).attr( "selected", "selected" );
562
- $tr.find( ".wpsl-close-hour:last option[value='" + hours.close + "']" ).attr( "selected", "selected" );
563
-
564
- e.preventDefault();
565
- });
566
-
567
- /**
568
- * Remove an openings period
569
- *
570
- * @since 2.0.0
571
- * @param {object} elem The clicked element
572
- * @return {void}
573
- */
574
- function removePeriod( elem ) {
575
- var periodsLeft = currentPeriodCount( elem ),
576
- $tr = elem.parents( "tr" ),
577
- day = $tr.find( ".wpsl-opening-hours" ).attr( "data-day" );
578
-
579
- // If there was 1 opening hour left then we add the 'Closed' text.
580
- if ( periodsLeft == 1 ) {
581
- $tr.find( ".wpsl-opening-hours" ).html( "<p class='wpsl-store-closed'>" + wpslL10n.closedDate + "<input type='hidden' name='wpsl[hours][" + day + "_open]' value='' /></p>" );
582
- }
583
-
584
- // Remove the selected openings period.
585
- elem.parent().closest( ".wpsl-current-period" ).remove();
586
-
587
- // If the first element has the multiple class, then we need to remove it.
588
- if ( $tr.find( ".wpsl-opening-hours div:first-child" ).hasClass( "wpsl-multiple-periods" ) ) {
589
- $tr.find( ".wpsl-opening-hours div:first-child" ).removeClass( "wpsl-multiple-periods" );
590
- }
591
- }
592
-
593
- /**
594
- * Count the current opening periods in a day block
595
- *
596
- * @since 2.0.0
597
- * @param {object} elem The clicked element
598
- * @return {string} currentPeriods The ammount of period divs found
599
- */
600
- function currentPeriodCount( elem ) {
601
- var currentPeriods = elem.parents( "tr" ).find( ".wpsl-current-period" ).length;
602
-
603
- return currentPeriods;
604
- }
605
-
606
- /**
607
- * Create an option list with the correct opening hour format and interval
608
- *
609
- * @since 2.0.0
610
- * @param {string} returnList Whether to return the option list or call the setSelectedOpeningHours function
611
- * @return {mixed} optionList The html for the option list of or void
612
- */
613
- function createHourOptionList( returnList ) {
614
- var openingHours, openingHourInterval, hour, hrFormat,
615
- pm = false,
616
- twelveHrsAfternoon = false,
617
- pmOrAm = "",
618
- optionList = "",
619
- openingTimes = [],
620
- openingHourOptions = {
621
- "hours": {
622
- "hr12": [ 12, 1, 2, 3 ,4 ,5 ,6, 7, 8, 9, 10, 11, 12, 1, 2, 3 , 4, 5, 6, 7, 8, 9, 10, 11 ],
623
- "hr24": [ 0, 1, 2, 3 ,4 ,5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ,16, 17, 18, 19, 20, 21, 22, 23 ]
624
- },
625
- "interval": [ '00', '15', '30', '45' ]
626
- };
627
-
628
- if ( $( "#wpsl-editor-hour-format" ).length ) {
629
- hrFormat = $( "#wpsl-editor-hour-format" ).val();
630
- } else {
631
- hrFormat = wpslSettings.hourFormat;
632
- }
633
-
634
- $( "#wpsl-store-hours td" ).removeAttr( "style" );
635
-
636
- if ( hrFormat == 12 ) {
637
- $( "#wpsl-store-hours" ).removeClass().addClass( "wpsl-twelve-format" );
638
- openingHours = openingHourOptions.hours.hr12;
639
- } else {
640
- $( "#wpsl-store-hours" ).removeClass().addClass( "wpsl-twentyfour-format" );
641
- openingHours = openingHourOptions.hours.hr24;
642
- }
643
-
644
- openingHourInterval = openingHourOptions.interval;
645
-
646
- for ( var i = 0; i < openingHours.length; i++ ) {
647
- hour = openingHours[i];
648
-
649
- /*
650
- * If the 12hr format is selected, then check if we need to show AM or PM.
651
- *
652
- * If the 24hr format is selected and the hour is a single digit
653
- * then we add a 0 to the start so 5:00 becomes 05:00.
654
- */
655
- if ( hrFormat == 12 ) {
656
- if ( hour >= 12 ) {
657
- pm = ( twelveHrsAfternoon ) ? true : false;
658
-
659
- twelveHrsAfternoon = true;
660
- }
661
-
662
- pmOrAm = ( pm ) ? "PM" : "AM";
663
- } else if ( ( hrFormat == 24 ) && ( hour.toString().length == 1 ) ) {
664
- hour = "0" + hour;
665
- }
666
-
667
- // Collect the new opening hour format and interval.
668
- for ( var j = 0; j < openingHourInterval.length; j++ ) {
669
- openingTimes.push( hour + ":" + openingHourInterval[j] + " " + pmOrAm );
670
- }
671
- }
672
-
673
- // Create the <option> list.
674
- for ( var i = 0; i < openingTimes.length; i++ ) {
675
- optionList = optionList + '<option value="' + $.trim( openingTimes[i] ) + '">' + $.trim( openingTimes[i] ) + '</option>';
676
- }
677
-
678
- if ( returnList ) {
679
- return optionList;
680
- } else {
681
- setSelectedOpeningHours( optionList, hrFormat );
682
- }
683
- }
684
-
685
- /**
686
- * Set the correct selected opening hour in the dropdown
687
- *
688
- * @since 2.0.0
689
- * @param {string} optionList The html for the option list
690
- * @param {string} hrFormat The html for the option list
691
- * @return {void}
692
- */
693
- function setSelectedOpeningHours( optionList, hrFormat ) {
694
- var splitHour, hourType, periodBlock,
695
- hours = {};
696
-
697
- /*
698
- * Loop over each open/close block and make sure the selected
699
- * value is still set as selected after changing the hr format.
700
- */
701
- $( ".wpsl-current-period" ).each( function() {
702
- periodBlock = $( this ),
703
- hours = {
704
- "open": $( this ).find( ".wpsl-open-hour" ).val(),
705
- "close": $( this ).find( ".wpsl-close-hour" ).val()
706
- };
707
-
708
- // Set the new hour format for both dropdowns.
709
- $( this ).find( "select" ).html( optionList ).promise().done( function() {
710
-
711
- // Select the correct start/end hours as selected.
712
- for ( var key in hours ) {
713
- if ( hours.hasOwnProperty( key ) ) {
714
-
715
- // Breakup the hour, so we can check the part before and after the : separately.
716
- splitHour = hours[key].split( ":" );
717
-
718
- if ( hrFormat == 12 ) {
719
- hourType = "";
720
-
721
- // Change the hours to a 12hr format and add the correct AM or PM.
722
- if ( hours[key].charAt( 0 ) == 0 ) {
723
- hours[key] = hours[key].substr( 1 );
724
- hourType = " AM";
725
- } else if ( ( splitHour[0].length == 2 ) && ( splitHour[0] > 12 ) ) {
726
- hours[key] = ( splitHour[0] - 12 ) + ":" + splitHour[1];
727
- hourType = " PM";
728
- } else if ( splitHour[0] < 12 ) {
729
- hours[key] = splitHour[0] + ":" + splitHour[1];
730
- hourType = " AM";
731
- } else if ( splitHour[0] == 12 ) {
732
- hours[key] = splitHour[0] + ":" + splitHour[1];
733
- hourType = " PM";
734
- }
735
-
736
- // Add either AM or PM behind the time.
737
- if ( ( splitHour[1].indexOf( "PM" ) == -1 ) && ( splitHour[1].indexOf( "AM" ) == -1 ) ) {
738
- hours[key] = hours[key] + hourType;
739
- }
740
-
741
- } else if ( hrFormat == 24 ) {
742
-
743
- // Change the hours to a 24hr format and remove the AM or PM.
744
- if ( splitHour[1].indexOf( "PM" ) != -1 ) {
745
- if ( splitHour[0] == 12 ) {
746
- hours[key] = "12:" + splitHour[1].replace( " PM", "" );
747
- } else {
748
- hours[key] = ( + splitHour[0] + 12 ) + ":" + splitHour[1].replace( " PM", "" );
749
- }
750
- } else if ( splitHour[1].indexOf( "AM" ) != -1 ) {
751
- if ( splitHour[0].toString().length == 1 ) {
752
- hours[key] = "0" + splitHour[0] + ":" + splitHour[1].replace( " AM", "" );
753
- } else {
754
- hours[key] = splitHour[0] + ":" + splitHour[1].replace( " AM", "" );
755
- }
756
- } else {
757
- hours[key] = splitHour[0] + ":" + splitHour[1]; // When the interval is changed
758
- }
759
- }
760
-
761
- // Set the correct value as the selected one.
762
- periodBlock.find( ".wpsl-" + key + "-hour option[value='" + $.trim( hours[key] ) + "']" ).attr( "selected", "selected" );
763
- }
764
- }
765
-
766
- });
767
- });
768
- }
769
 
770
  // Update the opening hours format if one of the dropdown values change.
771
- $( "#wpsl-editor-hour-format, #wpsl-editor-hour-interval" ).on( "change", function() {
772
- createHourOptionList();
773
- });
774
 
775
  // Show the tooltips.
776
- $( ".wpsl-info" ).on( "mouseover", function() {
777
- $( this ).find( ".wpsl-info-text" ).show();
778
- });
779
 
780
- $( ".wpsl-info" ).on( "mouseout", function() {
781
- $( this ).find( ".wpsl-info-text" ).hide();
782
- });
783
 
784
  // If the start location is empty, then we color the info icon red instead of black.
785
- if ( $( "#wpsl-latlng" ).length && !$( "#wpsl-latlng" ).val() ) {
786
- $( "#wpsl-latlng" ).siblings( "label" ).find( ".wpsl-info" ).addClass( "wpsl-required-setting" );
787
- }
788
-
789
- /**
790
- * Try to apply the custom style data to the map.
791
- *
792
- * If the style data is invalid json we show an error.
793
- *
794
- * @since 2.0.0
795
- * @return {void}
796
- */
797
- function tryCustomMapStyle() {
798
- var validStyle = "",
799
- mapStyle = $.trim( $( "#wpsl-map-style" ).val() );
800
-
801
- $( ".wpsl-style-preview-error" ).remove();
802
-
803
- if ( mapStyle ) {
804
-
805
- // Make sure the data is valid json.
806
- validStyle = tryParseJSON( mapStyle );
807
-
808
- if ( !validStyle ) {
809
- $( "#wpsl-style-preview" ).after( "<div class='wpsl-style-preview-error'>" + wpslL10n.styleError + "</div>" );
810
- }
811
- }
812
-
813
- map.setOptions({ styles: validStyle });
814
- }
815
 
816
  // Handle the map style changes on the settings page.
817
- if ( $( "#wpsl-map-style" ).val() ) {
818
- tryCustomMapStyle();
819
- }
820
 
821
  // Handle clicks on the map style preview button.
822
- $( "#wpsl-style-preview" ).on( "click", function() {
823
- tryCustomMapStyle();
824
-
825
- return false;
826
- });
827
-
828
- /**
829
- * Make sure the JSON is valid.
830
- *
831
- * @link http://stackoverflow.com/a/20392392/1065294
832
- * @since 2.0.0
833
- * @param {string} jsonString The JSON data
834
- * @return {object|boolean} The JSON string or false if it's invalid json.
835
- */
836
- function tryParseJSON( jsonString ) {
837
-
838
- try {
839
- var o = JSON.parse( jsonString );
840
-
841
- /*
842
- * Handle non-exception-throwing cases:
843
- * Neither JSON.parse(false) or JSON.parse(1234) throw errors, hence the type-checking,
844
- * but... JSON.parse(null) returns 'null', and typeof null === "object",
845
- * so we must check for that, too.
846
- */
847
- if ( o && typeof o === "object" && o !== null ) {
848
- return o;
 
849
  }
 
 
 
850
  }
851
- catch ( e ) { }
852
-
853
- return false;
854
- }
855
-
856
- /**
857
- * Look for changes on the start location input field.
858
- *
859
- * If there's a problem with the browser API key,
860
- * then a 'gm-err-autocomplete' class is added to the input field.
861
- *
862
- * When this happens we create a notice explaining how to fix the issue.
863
- *
864
- * @since 2.2.10
865
- * @return void
866
- */
867
- function observeBrowserKeyErrors() {
868
- var observer,
869
- attributeValue = '',
870
- MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
871
- $startName = $( "#wpsl-start-name" );
872
-
873
- if ( typeof MutationObserver !== "undefined" ) {
874
- observer = new MutationObserver( function( mutations ) {
875
-
876
- // Loop over the mutations.
877
- mutations.forEach( function( mutation ) {
878
- if ( mutation.attributeName === "class" ) {
879
- attributeValue = $( mutation.target ).prop( mutation.attributeName );
880
-
881
- // Look for a specific class that's added when there's a problem with the browser API key
882
- if ( ( attributeValue.indexOf( "gm-err-autocomplete" ) !== -1 ) ) {
883
- createErrorNotice( wpslL10n.browserKeyError, "browser-key" );
884
- }
885
- }
886
- });
887
- });
888
-
889
- observer.observe( $startName[0], {
890
- attributes: true
891
- });
892
- }
893
- }
894
-
895
- /**
896
- * Make a request to the geocode API with the
897
- * provided server key to check for any errors.
898
- *
899
- * @since 2.2.10
900
- * @return void
901
- */
902
- function validateServerKey() {
903
- var ajaxData = {
904
- action: "validate_server_key",
905
- server_key: $( "#wpsl-api-server-key" ).val()
906
- };
907
-
908
- $.get( wpslSettings.ajaxurl, ajaxData, function( response ) {
909
- if ( !response.valid && typeof response.msg !== "undefined" ) {
910
- createErrorNotice( response.msg, "server-key" );
911
- } else {
912
- $( "#wpsl-api-server-key" ).removeClass( "wpsl-error" );
913
  }
914
- });
915
- }
916
-
917
- /**
918
- * Create the error notice.
919
- *
920
- * @since 2.2.10
921
- * @param {string} errorMsg The error message to show
922
- * @param {string} type The type of API key we need to show the notice for
923
- * @return void
924
- */
925
- function createErrorNotice( errorMsg, type ) {
926
- var errorNotice, noticeLocation;
927
-
928
- errorNotice = '<div id="setting-error-' + type + '" class="error settings-error notice is-dismissible">';
929
- errorNotice += '<p><strong>' + errorMsg + '</strong></p>';
930
- errorNotice += '<button type="button" class="notice-dismiss"><span class="screen-reader-text">' + wpslL10n.dismissNotice + '</span></button>';
931
- errorNotice += '</div>';
932
-
933
- noticeLocation = ( $( "#wpsl-tabs" ).length ) ? 'wpsl-tabs' : 'wpsl-settings-form';
934
-
935
- $( "#" + noticeLocation + "" ).before( errorNotice );
936
- $( "#wpsl-api-" + type + "").addClass( "wpsl-error" );
937
- }
938
 
939
- // Make sure the custom error notices can be removed
940
- $( "#wpsl-wrap" ).on( "click", "button.notice-dismiss", function() {
941
- $( this ).closest( 'div.notice' ).remove();
942
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
943
 
944
- });
945
 
 
 
 
 
 
 
 
 
946
 
 
1
  jQuery( document ).ready( function( $ ) {
2
+ var map, geocoder, markersArray = [];
3
 
4
+ if ( $( "#wpsl-gmap-wrap" ).length ) {
5
+ initializeGmap();
6
+ }
7
 
8
+ /*
9
+ * If we're on the settings page, then check for returned
10
+ * browser key errors from the autocomplete field
11
+ * and validate the server key when necessary.
12
+ */
13
+ if ( $( "#wpsl-map-settings").length ) {
14
+ observeBrowserKeyErrors();
15
+
16
+ /**
17
+ * Check if we need to validate the server key, and if no
18
+ * error message is already visible after saving the setting page.
19
+ */
20
+ if ( $( "#wpsl-api-server-key" ).hasClass( "wpsl-validate-me" ) && !$( "#setting-error-server-key" ).length ) {
21
+ validateServerKey();
22
+ }
23
+ }
24
 
25
  /**
26
+ * Initialize the map with the correct settings.
27
+ *
28
+ * @since 1.0.0
29
+ * @returns {void}
30
  */
31
+ function initializeGmap() {
32
+ var defaultLatLng = wpslSettings.defaultLatLng.split( "," ),
33
+ latLng = new google.maps.LatLng( defaultLatLng[0], defaultLatLng[1] ),
34
+ mapOptions = {};
35
+
36
+ mapOptions = {
37
+ zoom: parseInt( wpslSettings.defaultZoom ),
38
+ center: latLng,
39
+ mapTypeId: google.maps.MapTypeId[ wpslSettings.mapType.toUpperCase() ],
40
+ mapTypeControl: false,
41
+ streetViewControl: false,
42
+ zoomControlOptions: {
43
+ position: google.maps.ControlPosition.RIGHT_TOP
44
+ }
45
+ };
46
+
47
+ geocoder = new google.maps.Geocoder();
48
+ map = new google.maps.Map( document.getElementById( "wpsl-gmap-wrap" ), mapOptions );
49
+
50
+ checkEditStoreMarker();
51
+ }
52
+
53
+ /**
54
+ * Check if we have an existing latlng value.
55
+ *
56
+ * If there is an latlng value, then we add a marker to the map.
57
+ * This can only happen on the edit store page.
58
+ *
59
+ * @since 1.0.0
60
+ * @returns {void}
61
+ */
62
+ function checkEditStoreMarker() {
63
+ var location,
64
+ lat = $( "#wpsl-lat" ).val(),
65
+ lng = $( "#wpsl-lng" ).val();
66
+
67
+ if ( ( lat ) && ( lng ) ) {
68
+ location = new google.maps.LatLng( lat, lng );
69
+
70
+ map.setCenter( location );
71
+ map.setZoom( 16 );
72
+ addMarker( location );
73
+ }
74
+ }
 
 
 
 
 
 
 
 
 
 
 
75
 
76
  // If we have a city/country input field enable the autocomplete.
77
+ if ( $( "#wpsl-start-name" ).length ) {
78
+ activateAutoComplete();
79
+ }
80
+
81
+ /**
82
+ * Activate the autocomplete function for the city/country field.
83
+ *
84
+ * @since 1.0.0
85
+ * @returns {void}
86
+ */
87
+ function activateAutoComplete() {
88
+ var latlng,
89
+ input = document.getElementById( "wpsl-start-name" ),
90
+ options = {
91
+ types: ['geocode']
92
+ },
93
+ autocomplete = new google.maps.places.Autocomplete( input, options );
94
+
95
+ google.maps.event.addListener( autocomplete, "place_changed", function() {
96
+ latlng = autocomplete.getPlace().geometry.location;
97
+ setLatlng( latlng, "zoom" );
98
+ });
99
+ }
100
+
101
+ /**
102
+ * Add a new marker to the map based on the provided location (latlng).
103
+ *
104
+ * @since 1.0.0
105
+ * @param {object} location The latlng value
106
+ * @returns {void}
107
+ */
108
+ function addMarker( location ) {
109
+ var marker = new google.maps.Marker({
110
+ position: location,
111
+ map: map,
112
+ draggable: true
113
+ });
114
+
115
+ markersArray.push( marker );
116
+
117
+ // If the marker is dragged on the map, make sure the latlng values are updated.
118
+ google.maps.event.addListener( marker, "dragend", function() {
119
+ setLatlng( marker.getPosition(), "store" );
120
+ });
121
+ }
122
 
123
  // Lookup the provided location with the Google Maps API.
124
+ $( "#wpsl-lookup-location" ).on( "click", function( e ) {
125
+ e.preventDefault();
126
+ codeAddress();
127
+ });
128
+
129
+ /**
130
+ * Update the hidden input field with the current latlng values.
131
+ *
132
+ * @since 1.0.0
133
+ * @param {object} latLng The latLng values
134
+ * @param {string} target The location where we need to set the latLng
135
+ * @returns {void}
136
+ */
137
+ function setLatlng( latLng, target ) {
138
+ var coordinates = stripCoordinates( latLng ),
139
+ lat = roundCoordinate( coordinates[0] ),
140
+ lng = roundCoordinate( coordinates[1] );
141
+
142
+ if ( target == "store" ) {
143
+ $( "#wpsl-lat" ).val( lat );
144
+ $( "#wpsl-lng" ).val( lng );
145
+ } else if ( target == "zoom" ) {
146
+ $( "#wpsl-latlng" ).val( lat + ',' + lng );
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Geocode the user input.
152
+ *
153
+ * @since 1.0.0
154
+ * @returns {void}
155
+ */
156
+ function codeAddress() {
157
+ var filteredResponse, geocodeAddress;
158
+
159
+ // Check if we have all the required data before attempting to geocode the address.
160
+ if ( !validatePreviewFields() ) {
161
+ geocodeAddress = createGeocodeAddress();
162
+
163
+ geocoder.geocode( { 'address': geocodeAddress }, function( response, status ) {
164
+ if ( status === google.maps.GeocoderStatus.OK ) {
165
+
166
+ // If we have a previous marker on the map we remove it.
167
+ if ( typeof( markersArray[0] ) !== "undefined" ) {
168
+ if ( markersArray[0].draggable ) {
169
+ markersArray[0].setMap( null );
170
+ markersArray.splice(0, 1);
171
+ }
172
+ }
173
+
174
+ // Center and zoom to the searched location.
175
+ map.setCenter( response[0].geometry.location );
176
+ map.setZoom( 16 );
177
+
178
+ addMarker( response[0].geometry.location );
179
+ setLatlng( response[0].geometry.location, "store" );
180
+
181
+ filteredResponse = filterApiResponse( response );
182
+
183
+ $( "#wpsl-country" ).val( filteredResponse.country.long_name );
184
+ $( "#wpsl-country_iso" ).val( filteredResponse.country.short_name );
185
+ } else {
186
+ alert( wpslL10n.geocodeFail + ": " + status );
187
+ }
188
+ });
189
+
190
+ return false;
191
+ } else {
192
+ activateStoreTab( "first" );
193
+
194
+ alert( wpslL10n.missingGeoData );
195
+
196
+ return true;
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Check that all required fields for the map preview are there.
202
+ *
203
+ * @since 1.0.0
204
+ * @returns {boolean} error Whether all the required fields contained data.
205
+ */
206
+ function validatePreviewFields() {
207
+ var i, fieldData, requiredFields,
208
+ error = false;
209
+
210
+ $( ".wpsl-store-meta input" ).removeClass( "wpsl-error" );
211
+
212
+ // Check which fields are required.
213
+ if ( typeof wpslSettings.requiredFields !== "undefined" && _.isArray( wpslSettings.requiredFields ) ) {
214
+ requiredFields = wpslSettings.requiredFields;
215
+
216
+ // Check if all the required fields contain data.
217
+ for ( i = 0; i < requiredFields.length; i++ ) {
218
+ fieldData = $.trim( $( "#wpsl-" + requiredFields[i] ).val() );
219
+
220
+ if ( !fieldData ) {
221
+ $( "#wpsl-" + requiredFields[i] ).addClass( "wpsl-error" );
222
+ error = true;
223
+ }
224
+
225
+ fieldData = '';
226
  }
227
+ }
228
 
229
+ return error;
230
+ }
231
+
232
+ /**
233
+ * Build the address that's send to the Geocode API.
234
+ *
235
+ * @since 2.1.0
236
+ * @returns {string} geocodeAddress The address separated by , that's send to the Geocoder.
237
+ */
238
+ function createGeocodeAddress() {
239
+ var i, part,
240
+ address = [],
241
+ addressParts = [ "address", "city", "state", "zip", "country" ];
242
+
243
+ for ( i = 0; i < addressParts.length; i++ ) {
244
+ part = $.trim( $( "#wpsl-" + addressParts[i] ).val() );
245
+
246
+ /*
247
+ * At this point we already know the address, city and country fields contain data.
248
+ * But no need to include the zip and state if they are empty.
249
+ */
250
+ if ( part ) {
251
+ address.push( part );
252
+ }
253
+
254
+ part = "";
255
  }
256
+
257
+ return address.join();
258
+ }
259
+
260
+ /**
261
+ * Filter out the country name from the API response.
262
+ *
263
+ * @since 1.0.0
264
+ * @param {object} response The response of the geocode API
265
+ * @returns {object} collectedData The short and long country name
266
+ */
267
+ function filterApiResponse( response ) {
268
+ var i, responseType,
269
+ country = {},
270
+ collectedData = {},
271
+ addressLength = response[0].address_components.length;
272
+
273
+ // Loop over the API response.
274
+ for ( i = 0; i < addressLength; i++ ) {
275
+ responseType = response[0].address_components[i].types;
276
+
277
+ // Filter out the country name.
278
+ if ( /^country,political$/.test( responseType ) ) {
279
+ country = {
280
+ long_name: response[0].address_components[i].long_name,
281
+ short_name: response[0].address_components[i].short_name
282
+ };
283
+ }
284
+ }
285
+
286
+ collectedData = {
287
+ country: country
288
+ };
289
+
290
+ return collectedData;
291
+ }
292
+
293
+ /**
294
+ * Round the coordinate to 6 digits after the comma.
295
+ *
296
+ * @since 1.0.0
297
+ * @param {string} coordinate The coordinate
298
+ * @returns {number} roundedCoord The rounded coordinate
299
+ */
300
+ function roundCoordinate( coordinate ) {
301
+ var roundedCoord, decimals = 6;
302
+
303
+ roundedCoord = Math.round( coordinate * Math.pow( 10, decimals ) ) / Math.pow( 10, decimals );
304
+
305
+ return roundedCoord;
306
  }
307
 
308
+ /**
309
+ * Strip the '(' and ')' from the captured coordinates and split them.
310
+ *
311
+ * @since 1.0.0
312
+ * @param {string} coordinates The coordinates
313
+ * @returns {object} latLng The latlng coordinates
314
+ */
315
+ function stripCoordinates( coordinates ) {
316
+ var latLng = [],
317
+ selected = coordinates.toString(),
318
+ latLngStr = selected.split( ",", 2 );
319
+
320
+ latLng[0] = latLngStr[0].replace( "(", "" );
321
+ latLng[1] = latLngStr[1].replace( ")", "" );
322
+
323
+ return latLng;
324
+ }
325
+
326
+ $( ".wpsl-marker-list input[type=radio]" ).click( function() {
327
+ $( this ).parents( ".wpsl-marker-list" ).find( "li" ).removeClass();
328
+ $( this ).parent( "li" ).addClass( "wpsl-active-marker" );
329
+ });
330
+
331
+ $( ".wpsl-marker-list li" ).click( function() {
332
+ $( this ).parents( ".wpsl-marker-list" ).find( "input" ).prop( "checked", false );
333
+ $( this ).find( "input" ).prop( "checked", true );
334
+ $( this ).siblings().removeClass();
335
+ $( this ).addClass( "wpsl-active-marker" );
336
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
 
338
  // Detect changes in checkboxes that have a conditional option.
339
+ $( ".wpsl-has-conditional-option" ).on( "change", function() {
340
+ $( this ).parent().next( ".wpsl-conditional-option" ).toggle();
341
+ });
342
+
343
+ /*
344
+ * Detect changes to the store template dropdown. If the template is selected to
345
+ * show the store list under the map then we show the option to hide the scrollbar.
346
+ */
347
+ $( "#wpsl-store-template" ).on( "change", function() {
348
+ var $scrollOption = $( "#wpsl-listing-below-no-scroll" );
349
+
350
+ if ( $( this ).val() == "below_map" ) {
351
+ $scrollOption.show();
352
+ } else {
353
+ $scrollOption.hide();
354
+ }
355
+ });
356
+
357
+ $( "#wpsl-api-region" ).on( "change", function() {
358
+ var $geocodeComponent = $( "#wpsl-geocode-component" );
359
+
360
+ if ( $( this ).val() ) {
361
+ $geocodeComponent.show();
362
+ } else {
363
+ $geocodeComponent.hide();
364
+ }
365
+ });
366
 
367
  // Make sure the correct hour input format is visible.
368
+ $( "#wpsl-editor-hour-input" ).on( "change", function() {
369
+ $( ".wpsl-" + $( this ).val() + "-hours" ).show().siblings( "div" ).hide();
370
+ $( ".wpsl-hour-notice" ).toggle();
371
+ });
372
 
373
  // Set the correct tab to active and show the correct content block.
374
+ $( "#wpsl-meta-nav li" ).on( "click", function( e ) {
375
+ var activeClass = $( this ).attr( "class" );
376
+ activeClass = activeClass.split( "-tab" );
377
+
378
+ e.stopPropagation();
379
 
380
+ // Set the correct tab and metabox to active.
381
+ $( this ).addClass( "wpsl-active" ).siblings().removeClass( "wpsl-active" );
382
+ $( ".wpsl-store-meta ." + activeClass[0] + "" ).show().addClass( "wpsl-active" ).siblings( "div" ).hide().removeClass( "wpsl-active" );
383
+ });
 
 
384
 
385
  // Make sure the required store fields contain data.
386
+ if ( $( "#wpsl-store-details" ).length ) {
387
+ $( "#publish" ).click( function() {
388
+ var firstErrorElem, currentTabClass, elemClass,
389
+ errorMsg = '<div id="message" class="error"><p>' + wpslL10n.requiredFields + '</p></div>',
390
+ missingData = false;
391
+
392
+ // Remove error messages and css classes from previous submissions.
393
+ $( "#wpbody-content .wrap #message" ).remove();
394
+ $( ".wpsl-required" ).removeClass( "wpsl-error" );
395
+
396
+ // Loop over the required fields and check for a value.
397
+ $( ".wpsl-required" ).each( function() {
398
+ if ( $( this ).val() == "" ) {
399
+ $( this ).addClass( "wpsl-error" );
400
+
401
+ if ( typeof firstErrorElem === "undefined" ) {
402
+ firstErrorElem = getFirstErrorElemAttr( $( this ) );
403
+ }
404
+
405
+ missingData = true;
406
+ }
407
+ });
408
+
409
+ // If one of the required fields are empty, then show the error msg and make sure the correct tab is visible.
410
+ if ( missingData ) {
411
+ $( "#wpbody-content .wrap > h2" ).after( errorMsg );
412
+
413
+ if ( typeof firstErrorElem.val !== "undefined" ) {
414
+ if ( firstErrorElem.type == "id" ) {
415
+ currentTabClass = $( "#" + firstErrorElem.val + "" ).parents( ".wpsl-tab" ).attr( "class" );
416
+ $( "html, body" ).scrollTop( Math.round( $( "#" + firstErrorElem.val + "" ).offset().top - 100 ) );
417
+ } else if ( firstErrorElem.type == "class" ) {
418
+ elemClass = firstErrorElem.val.replace( /wpsl-required|wpsl-error/g, "" );
419
+ currentTabClass = $( "." + elemClass + "" ).parents( ".wpsl-tab" ).attr( "class" );
420
+ $( "html, body" ).scrollTop( Math.round( $( "." + elemClass + "" ).offset().top - 100 ) );
421
+ }
422
+
423
+ currentTabClass = $.trim( currentTabClass.replace( /wpsl-tab|wpsl-active/g, "" ) );
424
+ }
425
+
426
+ // If we don't have a class of the tab that should be set to visible, we just show the first one.
427
+ if ( !currentTabClass ) {
428
+ activateStoreTab( 'first' );
429
+ } else {
430
+ activateStoreTab( currentTabClass );
431
+ }
432
+
433
+ /*
434
+ * If not all required fields contains data, and the user has
435
+ * clicked the submit button. Then an extra css class is added to the
436
+ * button that will disabled it. This only happens in WP 3.8 or earlier.
437
+ *
438
+ * We need to make sure this css class doesn't exist otherwise
439
+ * the user can never resubmit the page.
440
+ */
441
+ $( "#publish" ).removeClass( "button-primary-disabled" );
442
+ $( ".spinner" ).hide();
443
+
444
+ return false;
445
+ } else {
446
+ return true;
447
+ }
448
+ });
449
+ }
450
+
451
+ /**
452
+ * Set the correct tab to visible, and hide all other metaboxes
453
+ *
454
+ * @since 2.0.0
455
+ * @param {string} $target The name of the tab to show
456
+ * @returns {void}
457
+ */
458
+ function activateStoreTab( $target ) {
459
+ if ( $target == 'first' ) {
460
+ $target = ':first-child';
461
+ } else {
462
+ $target = '.' + $target;
463
+ }
464
+
465
+ if ( !$( "#wpsl-meta-nav li" + $target + "-tab" ).hasClass( "wpsl-active" ) ) {
466
+ $( "#wpsl-meta-nav li" + $target + "-tab" ).addClass( "wpsl-active" ).siblings().removeClass( "wpsl-active" );
467
+ $( ".wpsl-store-meta > div" + $target + "" ).show().addClass( "wpsl-active" ).siblings( "div" ).hide().removeClass( "wpsl-active" );
468
+ }
469
+ }
470
+
471
+ /**
472
+ * Get the id or class of the first element that's an required field, but is empty.
473
+ *
474
+ * We need this to determine which tab we need to set active,
475
+ * which will be the tab were the first error occured.
476
+ *
477
+ * @since 2.0.0
478
+ * @param {object} elem The element the error occured on
479
+ * @returns {object} firstErrorElem The id/class set on the first elem that an error occured on and the attr value
480
+ */
481
+ function getFirstErrorElemAttr( elem ) {
482
+ var firstErrorElem = { "type": "id", "val" : elem.attr( "id" ) };
483
+
484
+ // If no ID value exists, then check if we can get the class name.
485
+ if ( typeof firstErrorElem.val === "undefined" ) {
486
+ firstErrorElem = { "type": "class", "val" : elem.attr( "class" ) };
487
+ }
488
+
489
+ return firstErrorElem;
490
+ }
491
 
492
  // If we have a store hours dropdown, init the event handler.
493
+ if ( $( "#wpsl-store-hours" ).length ) {
494
+ initHourEvents();
495
+ }
496
+
497
+ /**
498
+ * Assign an event handler to the button that enables
499
+ * users to remove an opening hour period.
500
+ *
501
+ * @since 2.0.0
502
+ * @returns {void}
503
+ */
504
+ function initHourEvents() {
505
+ $( "#wpsl-store-hours .wpsl-icon-cancel-circled" ).off();
506
+ $( "#wpsl-store-hours .wpsl-icon-cancel-circled" ).on( "click", function() {
507
+ removePeriod( $( this ) );
508
+ });
509
+ }
510
 
511
  // Add new openings period to the openings hours table.
512
+ $( ".wpsl-add-period" ).on( "click", function( e ) {
513
+ var newPeriod,
514
+ hours = {},
515
+ returnList = true,
516
+ $tr = $( this ).parents( "tr" ),
517
+ periodCount = currentPeriodCount( $( this ) ),
518
+ periodCss = ( periodCount >= 1 ) ? "wpsl-current-period wpsl-multiple-periods" : "wpsl-current-period",
519
+ day = $tr.find( ".wpsl-opening-hours" ).attr( "data-day" ),
520
+ selectName = ( $( "#wpsl-settings-form" ).length ) ? "wpsl_editor[dropdown]" : "wpsl[hours]";
521
+
522
+ newPeriod = '<div class="' + periodCss +'">';
523
+ newPeriod += '<select autocomplete="off" name="' + selectName + '[' + day + '_open][]" class="wpsl-open-hour">' + createHourOptionList( returnList ) + '</select>';
524
+ newPeriod += '<span> - </span>';
525
+ newPeriod += '<select autocomplete="off" name="' + selectName + '[' + day + '_close][]" class="wpsl-close-hour">' + createHourOptionList( returnList ) + '</select>';
526
+ newPeriod += '<div class="wpsl-icon-cancel-circled"></div>';
527
+ newPeriod += '</div>';
528
+
529
+ $tr.find( ".wpsl-store-closed" ).remove();
530
+ $( "#wpsl-hours-" + day + "" ).append( newPeriod ).end();
531
+
532
+ initHourEvents();
533
+
534
+ if ( $( "#wpsl-editor-hour-format" ).val() == 24 ) {
535
+ hours = {
536
+ "open": "09:00",
537
+ "close": "17:00"
538
+ };
539
+ } else {
540
+ hours = {
541
+ "open": "9:00 AM",
542
+ "close": "5:00 PM"
543
+ };
544
+ }
545
+
546
+ $tr.find( ".wpsl-open-hour:last option[value='" + hours.open + "']" ).attr( "selected", "selected" );
547
+ $tr.find( ".wpsl-close-hour:last option[value='" + hours.close + "']" ).attr( "selected", "selected" );
548
+
549
+ e.preventDefault();
550
+ });
551
+
552
+ /**
553
+ * Remove an openings period
554
+ *
555
+ * @since 2.0.0
556
+ * @param {object} elem The clicked element
557
+ * @return {void}
558
+ */
559
+ function removePeriod( elem ) {
560
+ var periodsLeft = currentPeriodCount( elem ),
561
+ $tr = elem.parents( "tr" ),
562
+ day = $tr.find( ".wpsl-opening-hours" ).attr( "data-day" );
563
+
564
+ // If there was 1 opening hour left then we add the 'Closed' text.
565
+ if ( periodsLeft == 1 ) {
566
+ $tr.find( ".wpsl-opening-hours" ).html( "<p class='wpsl-store-closed'>" + wpslL10n.closedDate + "<input type='hidden' name='wpsl[hours][" + day + "_open]' value='' /></p>" );
567
+ }
568
+
569
+ // Remove the selected openings period.
570
+ elem.parent().closest( ".wpsl-current-period" ).remove();
571
+
572
+ // If the first element has the multiple class, then we need to remove it.
573
+ if ( $tr.find( ".wpsl-opening-hours div:first-child" ).hasClass( "wpsl-multiple-periods" ) ) {
574
+ $tr.find( ".wpsl-opening-hours div:first-child" ).removeClass( "wpsl-multiple-periods" );
575
+ }
576
+ }
577
+
578
+ /**
579
+ * Count the current opening periods in a day block
580
+ *
581
+ * @since 2.0.0
582
+ * @param {object} elem The clicked element
583
+ * @return {string} currentPeriods The ammount of period divs found
584
+ */
585
+ function currentPeriodCount( elem ) {
586
+ var currentPeriods = elem.parents( "tr" ).find( ".wpsl-current-period" ).length;
587
+
588
+ return currentPeriods;
589
+ }
590
+
591
+ /**
592
+ * Create an option list with the correct opening hour format and interval
593
+ *
594
+ * @since 2.0.0
595
+ * @param {string} returnList Whether to return the option list or call the setSelectedOpeningHours function
596
+ * @return {mixed} optionList The html for the option list of or void
597
+ */
598
+ function createHourOptionList( returnList ) {
599
+ var openingHours, openingHourInterval, hour, hrFormat,
600
+ pm = false,
601
+ twelveHrsAfternoon = false,
602
+ pmOrAm = "",
603
+ optionList = "",
604
+ openingTimes = [],
605
+ openingHourOptions = {
606
+ "hours": {
607
+ "hr12": [ 12, 1, 2, 3 ,4 ,5 ,6, 7, 8, 9, 10, 11, 12, 1, 2, 3 , 4, 5, 6, 7, 8, 9, 10, 11 ],
608
+ "hr24": [ 0, 1, 2, 3 ,4 ,5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ,16, 17, 18, 19, 20, 21, 22, 23 ]
609
+ },
610
+ "interval": [ '00', '15', '30', '45' ]
611
+ };
612
+
613
+ if ( $( "#wpsl-editor-hour-format" ).length ) {
614
+ hrFormat = $( "#wpsl-editor-hour-format" ).val();
615
+ } else {
616
+ hrFormat = wpslSettings.hourFormat;
617
+ }
618
+
619
+ $( "#wpsl-store-hours td" ).removeAttr( "style" );
620
+
621
+ if ( hrFormat == 12 ) {
622
+ $( "#wpsl-store-hours" ).removeClass().addClass( "wpsl-twelve-format" );
623
+ openingHours = openingHourOptions.hours.hr12;
624
+ } else {
625
+ $( "#wpsl-store-hours" ).removeClass().addClass( "wpsl-twentyfour-format" );
626
+ openingHours = openingHourOptions.hours.hr24;
627
+ }
628
+
629
+ openingHourInterval = openingHourOptions.interval;
630
+
631
+ for ( var i = 0; i < openingHours.length; i++ ) {
632
+ hour = openingHours[i];
633
+
634
+ /*
635
+ * If the 12hr format is selected, then check if we need to show AM or PM.
636
+ *
637
+ * If the 24hr format is selected and the hour is a single digit
638
+ * then we add a 0 to the start so 5:00 becomes 05:00.
639
+ */
640
+ if ( hrFormat == 12 ) {
641
+ if ( hour >= 12 ) {
642
+ pm = ( twelveHrsAfternoon ) ? true : false;
643
+
644
+ twelveHrsAfternoon = true;
645
+ }
646
+
647
+ pmOrAm = ( pm ) ? "PM" : "AM";
648
+ } else if ( ( hrFormat == 24 ) && ( hour.toString().length == 1 ) ) {
649
+ hour = "0" + hour;
650
+ }
651
+
652
+ // Collect the new opening hour format and interval.
653
+ for ( var j = 0; j < openingHourInterval.length; j++ ) {
654
+ openingTimes.push( hour + ":" + openingHourInterval[j] + " " + pmOrAm );
655
+ }
656
+ }
657
+
658
+ // Create the <option> list.
659
+ for ( var i = 0; i < openingTimes.length; i++ ) {
660
+ optionList = optionList + '<option value="' + $.trim( openingTimes[i] ) + '">' + $.trim( openingTimes[i] ) + '</option>';
661
+ }
662
+
663
+ if ( returnList ) {
664
+ return optionList;
665
+ } else {
666
+ setSelectedOpeningHours( optionList, hrFormat );
667
+ }
668
+ }
669
+
670
+ /**
671
+ * Set the correct selected opening hour in the dropdown
672
+ *
673
+ * @since 2.0.0
674
+ * @param {string} optionList The html for the option list
675
+ * @param {string} hrFormat The html for the option list
676
+ * @return {void}
677
+ */
678
+ function setSelectedOpeningHours( optionList, hrFormat ) {
679
+ var splitHour, hourType, periodBlock,
680
+ hours = {};
681
+
682
+ /*
683
+ * Loop over each open/close block and make sure the selected
684
+ * value is still set as selected after changing the hr format.
685
+ */
686
+ $( ".wpsl-current-period" ).each( function() {
687
+ periodBlock = $( this ),
688
+ hours = {
689
+ "open": $( this ).find( ".wpsl-open-hour" ).val(),
690
+ "close": $( this ).find( ".wpsl-close-hour" ).val()
691
+ };
692
+
693
+ // Set the new hour format for both dropdowns.
694
+ $( this ).find( "select" ).html( optionList ).promise().done( function() {
695
+
696
+ // Select the correct start/end hours as selected.
697
+ for ( var key in hours ) {
698
+ if ( hours.hasOwnProperty( key ) ) {
699
+
700
+ // Breakup the hour, so we can check the part before and after the : separately.
701
+ splitHour = hours[key].split( ":" );
702
+
703
+ if ( hrFormat == 12 ) {
704
+ hourType = "";
705
+
706
+ // Change the hours to a 12hr format and add the correct AM or PM.
707
+ if ( hours[key].charAt( 0 ) == 0 ) {
708
+ hours[key] = hours[key].substr( 1 );
709
+ hourType = " AM";
710
+ } else if ( ( splitHour[0].length == 2 ) && ( splitHour[0] > 12 ) ) {
711
+ hours[key] = ( splitHour[0] - 12 ) + ":" + splitHour[1];
712
+ hourType = " PM";
713
+ } else if ( splitHour[0] < 12 ) {
714
+ hours[key] = splitHour[0] + ":" + splitHour[1];
715
+ hourType = " AM";
716
+ } else if ( splitHour[0] == 12 ) {
717
+ hours[key] = splitHour[0] + ":" + splitHour[1];
718
+ hourType = " PM";
719
+ }
720
+
721
+ // Add either AM or PM behind the time.
722
+ if ( ( splitHour[1].indexOf( "PM" ) == -1 ) && ( splitHour[1].indexOf( "AM" ) == -1 ) ) {
723
+ hours[key] = hours[key] + hourType;
724
+ }
725
+
726
+ } else if ( hrFormat == 24 ) {
727
+
728
+ // Change the hours to a 24hr format and remove the AM or PM.
729
+ if ( splitHour[1].indexOf( "PM" ) != -1 ) {
730
+ if ( splitHour[0] == 12 ) {
731
+ hours[key] = "12:" + splitHour[1].replace( " PM", "" );
732
+ } else {
733
+ hours[key] = ( + splitHour[0] + 12 ) + ":" + splitHour[1].replace( " PM", "" );
734
+ }
735
+ } else if ( splitHour[1].indexOf( "AM" ) != -1 ) {
736
+ if ( splitHour[0].toString().length == 1 ) {
737
+ hours[key] = "0" + splitHour[0] + ":" + splitHour[1].replace( " AM", "" );
738
+ } else {
739
+ hours[key] = splitHour[0] + ":" + splitHour[1].replace( " AM", "" );
740
+ }
741
+ } else {
742
+ hours[key] = splitHour[0] + ":" + splitHour[1]; // When the interval is changed
743
+ }
744
+ }
745
+
746
+ // Set the correct value as the selected one.
747
+ periodBlock.find( ".wpsl-" + key + "-hour option[value='" + $.trim( hours[key] ) + "']" ).attr( "selected", "selected" );
748
+ }
749
+ }
750
+
751
+ });
752
+ });
753
+ }
754
 
755
  // Update the opening hours format if one of the dropdown values change.
756
+ $( "#wpsl-editor-hour-format, #wpsl-editor-hour-interval" ).on( "change", function() {
757
+ createHourOptionList();
758
+ });
759
 
760
  // Show the tooltips.
761
+ $( ".wpsl-info" ).on( "mouseover", function() {
762
+ $( this ).find( ".wpsl-info-text" ).show();
763
+ });
764
 
765
+ $( ".wpsl-info" ).on( "mouseout", function() {
766
+ $( this ).find( ".wpsl-info-text" ).hide();
767
+ });
768
 
769
  // If the start location is empty, then we color the info icon red instead of black.
770
+ if ( $( "#wpsl-latlng" ).length && !$( "#wpsl-latlng" ).val() ) {
771
+ $( "#wpsl-latlng" ).siblings( "label" ).find( ".wpsl-info" ).addClass( "wpsl-required-setting" );
772
+ }
773
+
774
+ /**
775
+ * Try to apply the custom style data to the map.
776
+ *
777
+ * If the style data is invalid json we show an error.
778
+ *
779
+ * @since 2.0.0
780
+ * @return {void}
781
+ */
782
+ function tryCustomMapStyle() {
783
+ var validStyle = "",
784
+ mapStyle = $.trim( $( "#wpsl-map-style" ).val() );
785
+
786
+ $( ".wpsl-style-preview-error" ).remove();
787
+
788
+ if ( mapStyle ) {
789
+
790
+ // Make sure the data is valid json.
791
+ validStyle = tryParseJSON( mapStyle );
792
+
793
+ if ( !validStyle ) {
794
+ $( "#wpsl-style-preview" ).after( "<div class='wpsl-style-preview-error'>" + wpslL10n.styleError + "</div>" );
795
+ }
796
+ }
797
+
798
+ map.setOptions({ styles: validStyle });
799
+ }
800
 
801
  // Handle the map style changes on the settings page.
802
+ if ( $( "#wpsl-map-style" ).val() ) {
803
+ tryCustomMapStyle();
804
+ }
805
 
806
  // Handle clicks on the map style preview button.
807
+ $( "#wpsl-style-preview" ).on( "click", function() {
808
+ tryCustomMapStyle();
809
+
810
+ return false;
811
+ });
812
+
813
+ /**
814
+ * Make sure the JSON is valid.
815
+ *
816
+ * @link http://stackoverflow.com/a/20392392/1065294
817
+ * @since 2.0.0
818
+ * @param {string} jsonString The JSON data
819
+ * @return {object|boolean} The JSON string or false if it's invalid json.
820
+ */
821
+ function tryParseJSON( jsonString ) {
822
+
823
+ try {
824
+ var o = JSON.parse( jsonString );
825
+
826
+ /*
827
+ * Handle non-exception-throwing cases:
828
+ * Neither JSON.parse(false) or JSON.parse(1234) throw errors, hence the type-checking,
829
+ * but... JSON.parse(null) returns 'null', and typeof null === "object",
830
+ * so we must check for that, too.
831
+ */
832
+ if ( o && typeof o === "object" && o !== null ) {
833
+ return o;
834
+ }
835
  }
836
+ catch ( e ) { }
837
+
838
+ return false;
839
  }
840
+
841
+ /**
842
+ * Look for changes on the start location input field.
843
+ *
844
+ * If there's a problem with the browser API key,
845
+ * then a 'gm-err-autocomplete' class is added to the input field.
846
+ *
847
+ * When this happens we create a notice explaining how to fix the issue.
848
+ *
849
+ * @since 2.2.10
850
+ * @return void
851
+ */
852
+ function observeBrowserKeyErrors() {
853
+ var observer,
854
+ attributeValue = '',
855
+ MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
856
+ $startName = $( "#wpsl-start-name" );
857
+
858
+ if ( typeof MutationObserver !== "undefined" ) {
859
+ observer = new MutationObserver( function( mutations ) {
860
+
861
+ // Loop over the mutations.
862
+ mutations.forEach( function( mutation ) {
863
+ if ( mutation.attributeName === "class" ) {
864
+ attributeValue = $( mutation.target ).prop( mutation.attributeName );
865
+
866
+ // Look for a specific class that's added when there's a problem with the browser API key
867
+ if ( ( attributeValue.indexOf( "gm-err-autocomplete" ) !== -1 ) ) {
868
+ createErrorNotice( wpslL10n.browserKeyError, "browser-key" );
869
+ }
870
+ }
871
+ });
872
+ });
873
+
874
+ observer.observe( $startName[0], {
875
+ attributes: true
876
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
877
  }
878
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
879
 
880
+ /**
881
+ * Make a request to the geocode API with the
882
+ * provided server key to check for any errors.
883
+ *
884
+ * @since 2.2.10
885
+ * @return void
886
+ */
887
+ function validateServerKey() {
888
+ var ajaxData = {
889
+ action: "validate_server_key",
890
+ server_key: $( "#wpsl-api-server-key" ).val()
891
+ };
892
+
893
+ $.get( wpslSettings.ajaxurl, ajaxData, function( response ) {
894
+ if ( !response.valid && typeof response.msg !== "undefined" ) {
895
+ createErrorNotice( response.msg, "server-key" );
896
+ } else {
897
+ $( "#wpsl-api-server-key" ).removeClass( "wpsl-error" );
898
+ }
899
+ });
900
+ }
901
+
902
+ /**
903
+ * Create the error notice.
904
+ *
905
+ * @since 2.2.10
906
+ * @param {string} errorMsg The error message to show
907
+ * @param {string} type The type of API key we need to show the notice for
908
+ * @return void
909
+ */
910
+ function createErrorNotice( errorMsg, type ) {
911
+ var errorNotice, noticeLocation;
912
+
913
+ errorNotice = '<div id="setting-error-' + type + '" class="error settings-error notice is-dismissible">';
914
+ errorNotice += '<p><strong>' + errorMsg + '</strong></p>';
915
+ errorNotice += '<button type="button" class="notice-dismiss"><span class="screen-reader-text">' + wpslL10n.dismissNotice + '</span></button>';
916
+ errorNotice += '</div>';
917
 
918
+ noticeLocation = ( $( "#wpsl-tabs" ).length ) ? 'wpsl-tabs' : 'wpsl-settings-form';
919
 
920
+ $( "#" + noticeLocation + "" ).before( errorNotice );
921
+ $( "#wpsl-api-" + type + "").addClass( "wpsl-error" );
922
+ }
923
+
924
+ // Make sure the custom error notices can be removed
925
+ $( "#wpsl-wrap" ).on( "click", "button.notice-dismiss", function() {
926
+ $( this ).closest( 'div.notice' ).remove();
927
+ })
928
 
929
+ });
admin/js/wpsl-admin.min.js CHANGED
@@ -1 +1 @@
1
- jQuery(document).ready(function(e){function s(){var e=wpslSettings.defaultLatLng.split(","),s=new google.maps.LatLng(e[0],e[1]),o={};o={zoom:parseInt(wpslSettings.defaultZoom),center:s,mapTypeId:google.maps.MapTypeId[wpslSettings.mapType.toUpperCase()],mapTypeControl:!1,streetViewControl:!1,zoomControlOptions:{position:google.maps.ControlPosition.RIGHT_TOP}},O=new google.maps.Geocoder,L=new google.maps.Map(document.getElementById("wpsl-gmap-wrap"),o),t()}function t(){var s,t=e("#wpsl-lat").val(),o=e("#wpsl-lng").val();t&&o&&(s=new google.maps.LatLng(t,o),L.setCenter(s),L.setZoom(16),r(s))}function o(){var e,s=document.getElementById("wpsl-start-name"),t={types:["geocode"]},o=new google.maps.places.Autocomplete(s,t);google.maps.event.addListener(o,"place_changed",function(){e=o.getPlace().geometry.location,l(e,"zoom")})}function r(e){var s=new google.maps.Marker({position:e,map:L,draggable:!0});P.push(s),google.maps.event.addListener(s,"dragend",function(){l(s.getPosition(),"store")})}function l(s,t){var o=d(s),r=c(o[0]),l=c(o[1]);"store"==t?(e("#wpsl-lat").val(r),e("#wpsl-lng").val(l)):"zoom"==t&&e("#wpsl-latlng").val(r+","+l)}function n(){var s,t;return i()?(w("first"),alert(wpslL10n.missingGeoData),!0):(t=a(),O.geocode({address:t},function(t,o){o===google.maps.GeocoderStatus.OK?("undefined"!=typeof P[0]&&P[0].draggable&&(P[0].setMap(null),P.splice(0,1)),L.setCenter(t[0].geometry.location),L.setZoom(16),r(t[0].geometry.location),l(t[0].geometry.location,"store"),s=p(t),e("#wpsl-country").val(s.country.long_name),e("#wpsl-country_iso").val(s.country.short_name)):alert(wpslL10n.geocodeFail+": "+o)}),!1)}function i(){var s,t,o,r=!1;if(e(".wpsl-store-meta input").removeClass("wpsl-error"),"undefined"!=typeof wpslSettings.requiredFields&&_.isArray(wpslSettings.requiredFields))for(o=wpslSettings.requiredFields,s=0;s<o.length;s++)t=e.trim(e("#wpsl-"+o[s]).val()),t||(e("#wpsl-"+o[s]).addClass("wpsl-error"),r=!0),t="";return r}function a(){var s,t,o=[],r=["address","city","state","zip","country"];for(s=0;s<r.length;s++)t=e.trim(e("#wpsl-"+r[s]).val()),t&&o.push(t),t="";return o.join()}function p(e){var s,t,o={},r={},l=e[0].address_components.length;for(s=0;l>s;s++)t=e[0].address_components[s].types,/^country,political$/.test(t)&&(o={long_name:e[0].address_components[s].long_name,short_name:e[0].address_components[s].short_name});return r={country:o}}function c(e){var s,t=6;return s=Math.round(e*Math.pow(10,t))/Math.pow(10,t)}function d(e){var s=[],t=e.toString(),o=t.split(",",2);return s[0]=o[0].replace("(",""),s[1]=o[1].replace(")",""),s}function w(s){s="first"==s?":first-child":"."+s,e("#wpsl-meta-nav li"+s+"-tab").hasClass("wpsl-active")||(e("#wpsl-meta-nav li"+s+"-tab").addClass("wpsl-active").siblings().removeClass("wpsl-active"),e(".wpsl-store-meta > div"+s).show().addClass("wpsl-active").siblings("div").hide().removeClass("wpsl-active"))}function u(e){var s={type:"id",val:e.attr("id")};return"undefined"==typeof s.val&&(s={type:"class",val:e.attr("class")}),s}function v(){e("#wpsl-store-hours .wpsl-icon-cancel-circled").off(),e("#wpsl-store-hours .wpsl-icon-cancel-circled").on("click",function(){f(e(this))})}function f(e){var s=h(e),t=e.parents("tr"),o=t.find(".wpsl-opening-hours").attr("data-day");1==s&&t.find(".wpsl-opening-hours").html("<p class='wpsl-store-closed'>"+wpslL10n.closedDate+"<input type='hidden' name='wpsl[hours]["+o+"_open]' value='' /></p>"),e.parent().closest(".wpsl-current-period").remove(),t.find(".wpsl-opening-hours div:first-child").hasClass("wpsl-multiple-periods")&&t.find(".wpsl-opening-hours div:first-child").removeClass("wpsl-multiple-periods")}function h(e){var s=e.parents("tr").find(".wpsl-current-period").length;return s}function m(s){var t,o,r,l,n=!1,i=!1,a="",p="",c=[],d={hours:{hr12:[12,1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8,9,10,11],hr24:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]},interval:["00","15","30","45"]};l=e("#wpsl-editor-hour-format").length?e("#wpsl-editor-hour-format").val():wpslSettings.hourFormat,e("#wpsl-store-hours td").removeAttr("style"),12==l?(e("#wpsl-store-hours").removeClass().addClass("wpsl-twelve-format"),t=d.hours.hr12):(e("#wpsl-store-hours").removeClass().addClass("wpsl-twentyfour-format"),t=d.hours.hr24),o=d.interval;for(var w=0;w<t.length;w++){r=t[w],12==l?(r>=12&&(n=i?!0:!1,i=!0),a=n?"PM":"AM"):24==l&&1==r.toString().length&&(r="0"+r);for(var u=0;u<o.length;u++)c.push(r+":"+o[u]+" "+a)}for(var w=0;w<c.length;w++)p=p+'<option value="'+e.trim(c[w])+'">'+e.trim(c[w])+"</option>";return s?p:void g(p,l)}function g(s,t){var o,r,l,n={};e(".wpsl-current-period").each(function(){l=e(this),n={open:e(this).find(".wpsl-open-hour").val(),close:e(this).find(".wpsl-close-hour").val()},e(this).find("select").html(s).promise().done(function(){for(var s in n)n.hasOwnProperty(s)&&(o=n[s].split(":"),12==t?(r="",0==n[s].charAt(0)?(n[s]=n[s].substr(1),r=" AM"):2==o[0].length&&o[0]>12?(n[s]=o[0]-12+":"+o[1],r=" PM"):o[0]<12?(n[s]=o[0]+":"+o[1],r=" AM"):12==o[0]&&(n[s]=o[0]+":"+o[1],r=" PM"),-1==o[1].indexOf("PM")&&-1==o[1].indexOf("AM")&&(n[s]=n[s]+r)):24==t&&(-1!=o[1].indexOf("PM")?12==o[0]?n[s]="12:"+o[1].replace(" PM",""):n[s]=+o[0]+12+":"+o[1].replace(" PM",""):-1!=o[1].indexOf("AM")?1==o[0].toString().length?n[s]="0"+o[0]+":"+o[1].replace(" AM",""):n[s]=o[0]+":"+o[1].replace(" AM",""):n[s]=o[0]+":"+o[1]),l.find(".wpsl-"+s+"-hour option[value='"+e.trim(n[s])+"']").attr("selected","selected"))})})}function y(){var s="",t=e.trim(e("#wpsl-map-style").val());e(".wpsl-style-preview-error").remove(),t&&(s=b(t),s||e("#wpsl-style-preview").after("<div class='wpsl-style-preview-error'>"+wpslL10n.styleError+"</div>")),L.setOptions({styles:s})}function b(e){try{var s=JSON.parse(e);if(s&&"object"==typeof s&&null!==s)return s}catch(t){}return!1}function C(){var s,t="",o=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver,r=e("#wpsl-start-name");"undefined"!=typeof o&&(s=new o(function(s){s.forEach(function(s){"class"===s.attributeName&&(t=e(s.target).prop(s.attributeName),-1!==t.indexOf("gm-err-autocomplete")&&k(wpslL10n.browserKeyError,"browser-key"))})}),s.observe(r[0],{attributes:!0}))}function M(){var s={action:"validate_server_key",server_key:e("#wpsl-api-server-key").val()};e.get(wpslSettings.ajaxurl,s,function(s){s.valid||"undefined"==typeof s.msg?e("#wpsl-api-server-key").removeClass("wpsl-error"):k(s.msg,"server-key")})}function k(s,t){var o,r;o='<div id="setting-error-'+t+'" class="error settings-error notice is-dismissible">',o+="<p><strong>"+s+"</strong></p>",o+='<button type="button" class="notice-dismiss"><span class="screen-reader-text">'+wpslL10n.dismissNotice+"</span></button>",o+="</div>",r=e("#wpsl-tabs").length?"wpsl-tabs":"wpsl-settings-form",e("#"+r).before(o),e("#wpsl-api-"+t).addClass("wpsl-error")}var L,O,P=[];e("#wpsl-gmap-wrap").length&&s(),e("#wpsl-map-settings").length&&(C(),e("#wpsl-api-server-key").hasClass("wpsl-validate-me")&&!e("#setting-error-server-key").length&&M()),e("#wpsl-start-name").length&&o(),e("#wpsl-lookup-location").on("click",function(e){e.preventDefault(),n()}),e(".wpsl-marker-list input[type=radio]").click(function(){e(this).parents(".wpsl-marker-list").find("li").removeClass(),e(this).parent("li").addClass("wpsl-active-marker")}),e(".wpsl-marker-list li").click(function(){e(this).parents(".wpsl-marker-list").find("input").prop("checked",!1),e(this).find("input").prop("checked",!0),e(this).siblings().removeClass(),e(this).addClass("wpsl-active-marker")}),e(".wpsl-dismiss").click(function(){var s=e(this),t={action:"disable_location_warning",_ajax_nonce:s.attr("data-nonce")};return e.post(ajaxurl,t),e(".wpsl-dismiss").parents(".error").remove(),!1}),e(".wpsl-has-conditional-option").on("change",function(){e(this).parent().next(".wpsl-conditional-option").toggle()}),e("#wpsl-store-template").on("change",function(){var s=e("#wpsl-listing-below-no-scroll");"below_map"==e(this).val()?s.show():s.hide()}),e("#wpsl-api-region").on("change",function(){var s=e("#wpsl-geocode-component");e(this).val()?s.show():s.hide()}),e("#wpsl-editor-hour-input").on("change",function(){e(".wpsl-"+e(this).val()+"-hours").show().siblings("div").hide(),e(".wpsl-hour-notice").toggle()}),e("#wpsl-meta-nav li").on("click",function(s){var t=e(this).attr("class");t=t.split("-tab"),s.stopPropagation(),e(this).addClass("wpsl-active").siblings().removeClass("wpsl-active"),e(".wpsl-store-meta ."+t[0]).show().addClass("wpsl-active").siblings("div").hide().removeClass("wpsl-active")}),e("#wpsl-store-details").length&&e("#publish").click(function(){var s,t,o,r='<div id="message" class="error"><p>'+wpslL10n.requiredFields+"</p></div>",l=!1;return e("#wpbody-content .wrap #message").remove(),e(".wpsl-required").removeClass("wpsl-error"),e(".wpsl-required").each(function(){""==e(this).val()&&(e(this).addClass("wpsl-error"),"undefined"==typeof s&&(s=u(e(this))),l=!0)}),l?(e("#wpbody-content .wrap > h2").after(r),"undefined"!=typeof s.val&&("id"==s.type?(t=e("#"+s.val).parents(".wpsl-tab").attr("class"),e("html, body").scrollTop(Math.round(e("#"+s.val).offset().top-100))):"class"==s.type&&(o=s.val.replace(/wpsl-required|wpsl-error/g,""),t=e("."+o).parents(".wpsl-tab").attr("class"),e("html, body").scrollTop(Math.round(e("."+o).offset().top-100))),t=e.trim(t.replace(/wpsl-tab|wpsl-active/g,""))),w(t?t:"first"),e("#publish").removeClass("button-primary-disabled"),e(".spinner").hide(),!1):!0}),e("#wpsl-store-hours").length&&v(),e(".wpsl-add-period").on("click",function(s){var t,o={},r=!0,l=e(this).parents("tr"),n=h(e(this)),i=n>=1?"wpsl-current-period wpsl-multiple-periods":"wpsl-current-period",a=l.find(".wpsl-opening-hours").attr("data-day"),p=e("#wpsl-settings-form").length?"wpsl_editor[dropdown]":"wpsl[hours]";t='<div class="'+i+'">',t+='<select autocomplete="off" name="'+p+"["+a+'_open][]" class="wpsl-open-hour">'+m(r)+"</select>",t+="<span> - </span>",t+='<select autocomplete="off" name="'+p+"["+a+'_close][]" class="wpsl-close-hour">'+m(r)+"</select>",t+='<div class="wpsl-icon-cancel-circled"></div>',t+="</div>",l.find(".wpsl-store-closed").remove(),e("#wpsl-hours-"+a).append(t).end(),v(),o=24==e("#wpsl-editor-hour-format").val()?{open:"09:00",close:"17:00"}:{open:"9:00 AM",close:"5:00 PM"},l.find(".wpsl-open-hour:last option[value='"+o.open+"']").attr("selected","selected"),l.find(".wpsl-close-hour:last option[value='"+o.close+"']").attr("selected","selected"),s.preventDefault()}),e("#wpsl-editor-hour-format, #wpsl-editor-hour-interval").on("change",function(){m()}),e(".wpsl-info").on("mouseover",function(){e(this).find(".wpsl-info-text").show()}),e(".wpsl-info").on("mouseout",function(){e(this).find(".wpsl-info-text").hide()}),e("#wpsl-latlng").length&&!e("#wpsl-latlng").val()&&e("#wpsl-latlng").siblings("label").find(".wpsl-info").addClass("wpsl-required-setting"),e("#wpsl-map-style").val()&&y(),e("#wpsl-style-preview").on("click",function(){return y(),!1}),e("#wpsl-wrap").on("click","button.notice-dismiss",function(){e(this).closest("div.notice").remove()})});
1
+ jQuery(document).ready(function(e){var s,t,o,l,r,n,i,a,p,c,d,w,u,v=[];function h(e){var t=new google.maps.Marker({position:e,map:s,draggable:!0});v.push(t),google.maps.event.addListener(t,"dragend",function(){m(t.getPosition(),"store")})}function m(s,t){var o,l,r=(o=[],l=s.toString().split(",",2),o[0]=l[0].replace("(",""),o[1]=l[1].replace(")",""),o),n=g(r[0]),i=g(r[1]);"store"==t?(e("#wpsl-lat").val(n),e("#wpsl-lng").val(i)):"zoom"==t&&e("#wpsl-latlng").val(n+","+i)}function g(e){return Math.round(e*Math.pow(10,6))/Math.pow(10,6)}function f(s){e("#wpsl-meta-nav li"+(s="first"==s?":first-child":"."+s)+"-tab").hasClass("wpsl-active")||(e("#wpsl-meta-nav li"+s+"-tab").addClass("wpsl-active").siblings().removeClass("wpsl-active"),e(".wpsl-store-meta > div"+s).show().addClass("wpsl-active").siblings("div").hide().removeClass("wpsl-active"))}function y(){e("#wpsl-store-hours .wpsl-icon-cancel-circled").off(),e("#wpsl-store-hours .wpsl-icon-cancel-circled").on("click",function(){!function(e){var s=b(e),t=e.parents("tr"),o=t.find(".wpsl-opening-hours").attr("data-day");1==s&&t.find(".wpsl-opening-hours").html("<p class='wpsl-store-closed'>"+wpslL10n.closedDate+"<input type='hidden' name='wpsl[hours]["+o+"_open]' value='' /></p>");e.parent().closest(".wpsl-current-period").remove(),t.find(".wpsl-opening-hours div:first-child").hasClass("wpsl-multiple-periods")&&t.find(".wpsl-opening-hours div:first-child").removeClass("wpsl-multiple-periods")}(e(this))})}function b(e){return e.parents("tr").find(".wpsl-current-period").length}function C(s){var t,o,l,r,n,i,a,p,c,d,w=!1,u=!1,v="",h="",m=[],g={hr12:[12,1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8,9,10,11],hr24:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]},f=["00","15","30","45"];r=e("#wpsl-editor-hour-format").length?e("#wpsl-editor-hour-format").val():wpslSettings.hourFormat,e("#wpsl-store-hours td").removeAttr("style"),12==r?(e("#wpsl-store-hours").removeClass().addClass("wpsl-twelve-format"),t=g.hr12):(e("#wpsl-store-hours").removeClass().addClass("wpsl-twentyfour-format"),t=g.hr24),o=f;for(var y=0;y<t.length;y++){l=t[y],12==r?(l>=12&&(w=!!u,u=!0),v=w?"PM":"AM"):24==r&&1==l.toString().length&&(l="0"+l);for(var b=0;b<o.length;b++)m.push(l+":"+o[b]+" "+v)}for(y=0;y<m.length;y++)h=h+'<option value="'+e.trim(m[y])+'">'+e.trim(m[y])+"</option>";if(s)return h;n=h,i=r,d={},e(".wpsl-current-period").each(function(){c=e(this),d={open:e(this).find(".wpsl-open-hour").val(),close:e(this).find(".wpsl-close-hour").val()},e(this).find("select").html(n).promise().done(function(){for(var s in d)d.hasOwnProperty(s)&&(a=d[s].split(":"),12==i?(p="",0==d[s].charAt(0)?(d[s]=d[s].substr(1),p=" AM"):2==a[0].length&&a[0]>12?(d[s]=a[0]-12+":"+a[1],p=" PM"):a[0]<12?(d[s]=a[0]+":"+a[1],p=" AM"):12==a[0]&&(d[s]=a[0]+":"+a[1],p=" PM"),-1==a[1].indexOf("PM")&&-1==a[1].indexOf("AM")&&(d[s]=d[s]+p)):24==i&&(-1!=a[1].indexOf("PM")?12==a[0]?d[s]="12:"+a[1].replace(" PM",""):d[s]=+a[0]+12+":"+a[1].replace(" PM",""):-1!=a[1].indexOf("AM")?1==a[0].toString().length?d[s]="0"+a[0]+":"+a[1].replace(" AM",""):d[s]=a[0]+":"+a[1].replace(" AM",""):d[s]=a[0]+":"+a[1]),c.find(".wpsl-"+s+"-hour option[value='"+e.trim(d[s])+"']").attr("selected","selected"))})})}function M(){var t="",o=e.trim(e("#wpsl-map-style").val());e(".wpsl-style-preview-error").remove(),o&&((t=function(e){try{var s=JSON.parse(e);if(s&&"object"==typeof s&&null!==s)return s}catch(e){}return!1}(o))||e("#wpsl-style-preview").after("<div class='wpsl-style-preview-error'>"+wpslL10n.styleError+"</div>")),s.setOptions({styles:t})}function k(s,t){var o,l;o='<div id="setting-error-'+t+'" class="error settings-error notice is-dismissible">',o+="<p><strong>"+s+"</strong></p>",o+='<button type="button" class="notice-dismiss"><span class="screen-reader-text">'+wpslL10n.dismissNotice+"</span></button>",o+="</div>",l=e("#wpsl-tabs").length?"wpsl-tabs":"wpsl-settings-form",e("#"+l).before(o),e("#wpsl-api-"+t).addClass("wpsl-error")}e("#wpsl-gmap-wrap").length&&(o=wpslSettings.defaultLatLng.split(","),l=new google.maps.LatLng(o[0],o[1]),r={},r={zoom:parseInt(wpslSettings.defaultZoom),center:l,mapTypeId:google.maps.MapTypeId[wpslSettings.mapType.toUpperCase()],mapTypeControl:!1,streetViewControl:!1,zoomControlOptions:{position:google.maps.ControlPosition.RIGHT_TOP}},t=new google.maps.Geocoder,s=new google.maps.Map(document.getElementById("wpsl-gmap-wrap"),r),i=e("#wpsl-lat").val(),a=e("#wpsl-lng").val(),i&&a&&(n=new google.maps.LatLng(i,a),s.setCenter(n),s.setZoom(16),h(n))),e("#wpsl-map-settings").length&&(c=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver,d=e("#wpsl-start-name"),void 0!==c&&new c(function(s){s.forEach(function(s){"class"===s.attributeName&&-1!==e(s.target).prop(s.attributeName).indexOf("gm-err-autocomplete")&&k(wpslL10n.browserKeyError,"browser-key")})}).observe(d[0],{attributes:!0}),e("#wpsl-api-server-key").hasClass("wpsl-validate-me")&&!e("#setting-error-server-key").length&&(p={action:"validate_server_key",server_key:e("#wpsl-api-server-key").val()},e.get(wpslSettings.ajaxurl,p,function(s){s.valid||void 0===s.msg?e("#wpsl-api-server-key").removeClass("wpsl-error"):k(s.msg,"server-key")}))),e("#wpsl-start-name").length&&(w=document.getElementById("wpsl-start-name"),u=new google.maps.places.Autocomplete(w,{types:["geocode"]}),google.maps.event.addListener(u,"place_changed",function(){m(u.getPlace().geometry.location,"zoom")})),e("#wpsl-lookup-location").on("click",function(o){var l,r;o.preventDefault(),!function(){var s,t,o=!1;if(e(".wpsl-store-meta input").removeClass("wpsl-error"),void 0!==wpslSettings.requiredFields&&_.isArray(wpslSettings.requiredFields))for(t=wpslSettings.requiredFields,s=0;s<t.length;s++)e.trim(e("#wpsl-"+t[s]).val())||(e("#wpsl-"+t[s]).addClass("wpsl-error"),o=!0);return o}()?(r=function(){var s,t,o=[],l=["address","city","state","zip","country"];for(s=0;s<l.length;s++)(t=e.trim(e("#wpsl-"+l[s]).val()))&&o.push(t),t="";return o.join()}(),t.geocode({address:r},function(t,o){o===google.maps.GeocoderStatus.OK?(void 0!==v[0]&&v[0].draggable&&(v[0].setMap(null),v.splice(0,1)),s.setCenter(t[0].geometry.location),s.setZoom(16),h(t[0].geometry.location),m(t[0].geometry.location,"store"),l=function(e){var s,t,o={},l=e[0].address_components.length;for(s=0;s<l;s++)t=e[0].address_components[s].types,/^country,political$/.test(t)&&(o={long_name:e[0].address_components[s].long_name,short_name:e[0].address_components[s].short_name});return{country:o}}(t),e("#wpsl-country").val(l.country.long_name),e("#wpsl-country_iso").val(l.country.short_name)):alert(wpslL10n.geocodeFail+": "+o)})):(f("first"),alert(wpslL10n.missingGeoData))}),e(".wpsl-marker-list input[type=radio]").click(function(){e(this).parents(".wpsl-marker-list").find("li").removeClass(),e(this).parent("li").addClass("wpsl-active-marker")}),e(".wpsl-marker-list li").click(function(){e(this).parents(".wpsl-marker-list").find("input").prop("checked",!1),e(this).find("input").prop("checked",!0),e(this).siblings().removeClass(),e(this).addClass("wpsl-active-marker")}),e(".wpsl-has-conditional-option").on("change",function(){e(this).parent().next(".wpsl-conditional-option").toggle()}),e("#wpsl-store-template").on("change",function(){var s=e("#wpsl-listing-below-no-scroll");"below_map"==e(this).val()?s.show():s.hide()}),e("#wpsl-api-region").on("change",function(){var s=e("#wpsl-geocode-component");e(this).val()?s.show():s.hide()}),e("#wpsl-editor-hour-input").on("change",function(){e(".wpsl-"+e(this).val()+"-hours").show().siblings("div").hide(),e(".wpsl-hour-notice").toggle()}),e("#wpsl-meta-nav li").on("click",function(s){var t=e(this).attr("class");t=t.split("-tab"),s.stopPropagation(),e(this).addClass("wpsl-active").siblings().removeClass("wpsl-active"),e(".wpsl-store-meta ."+t[0]).show().addClass("wpsl-active").siblings("div").hide().removeClass("wpsl-active")}),e("#wpsl-store-details").length&&e("#publish").click(function(){var s,t,o,l='<div id="message" class="error"><p>'+wpslL10n.requiredFields+"</p></div>",r=!1;return e("#wpbody-content .wrap #message").remove(),e(".wpsl-required").removeClass("wpsl-error"),e(".wpsl-required").each(function(){""==e(this).val()&&(e(this).addClass("wpsl-error"),void 0===s&&(s=function(e){var s={type:"id",val:e.attr("id")};void 0===s.val&&(s={type:"class",val:e.attr("class")});return s}(e(this))),r=!0)}),!r||(e("#wpbody-content .wrap > h2").after(l),void 0!==s.val&&("id"==s.type?(t=e("#"+s.val).parents(".wpsl-tab").attr("class"),e("html, body").scrollTop(Math.round(e("#"+s.val).offset().top-100))):"class"==s.type&&(o=s.val.replace(/wpsl-required|wpsl-error/g,""),t=e("."+o).parents(".wpsl-tab").attr("class"),e("html, body").scrollTop(Math.round(e("."+o).offset().top-100))),t=e.trim(t.replace(/wpsl-tab|wpsl-active/g,""))),f(t||"first"),e("#publish").removeClass("button-primary-disabled"),e(".spinner").hide(),!1)}),e("#wpsl-store-hours").length&&y(),e(".wpsl-add-period").on("click",function(s){var t,o={},l=e(this).parents("tr"),r=b(e(this))>=1?"wpsl-current-period wpsl-multiple-periods":"wpsl-current-period",n=l.find(".wpsl-opening-hours").attr("data-day"),i=e("#wpsl-settings-form").length?"wpsl_editor[dropdown]":"wpsl[hours]";t='<div class="'+r+'">',t+='<select autocomplete="off" name="'+i+"["+n+'_open][]" class="wpsl-open-hour">'+C(!0)+"</select>",t+="<span> - </span>",t+='<select autocomplete="off" name="'+i+"["+n+'_close][]" class="wpsl-close-hour">'+C(!0)+"</select>",t+='<div class="wpsl-icon-cancel-circled"></div>',t+="</div>",l.find(".wpsl-store-closed").remove(),e("#wpsl-hours-"+n).append(t).end(),y(),o=24==e("#wpsl-editor-hour-format").val()?{open:"09:00",close:"17:00"}:{open:"9:00 AM",close:"5:00 PM"},l.find(".wpsl-open-hour:last option[value='"+o.open+"']").attr("selected","selected"),l.find(".wpsl-close-hour:last option[value='"+o.close+"']").attr("selected","selected"),s.preventDefault()}),e("#wpsl-editor-hour-format, #wpsl-editor-hour-interval").on("change",function(){C()}),e(".wpsl-info").on("mouseover",function(){e(this).find(".wpsl-info-text").show()}),e(".wpsl-info").on("mouseout",function(){e(this).find(".wpsl-info-text").hide()}),e("#wpsl-latlng").length&&!e("#wpsl-latlng").val()&&e("#wpsl-latlng").siblings("label").find(".wpsl-info").addClass("wpsl-required-setting"),e("#wpsl-map-style").val()&&M(),e("#wpsl-style-preview").on("click",function(){return M(),!1}),e("#wpsl-wrap").on("click","button.notice-dismiss",function(){e(this).closest("div.notice").remove()})});
admin/js/wpsl-shortcode-generator.js CHANGED
@@ -1,130 +1,130 @@
1
- /**
2
- * Insert the WPSL shortcode
3
- *
4
- * Grab the values from the thickbox form
5
- * and use them to set the wpsl shortcode attributes.
6
- *
7
- * @since 2.2.10
8
- */
9
- function WPSL_InsertShortcode() {
10
- var markers, shortcodeAtts, checkboxColumns, catSelectionID, catSelection,
11
- win = window.dialogArguments || opener || parent || top,
12
- startLocation = jQuery( "#wpsl-start-location" ).val(),
13
- catFilterType = jQuery( "#wpsl-cat-filter-types" ).val(),
14
- catRestriction = jQuery( "#wpsl-cat-restriction" ).val(),
15
- locateUser = ( jQuery( "#wpsl-auto-locate" ).is( ":checked" ) ) ? true : false;
16
-
17
- shortcodeAtts = 'template="' + jQuery( "#wpsl-store-template" ).val() + '" map_type="' + jQuery( "#wpsl-map-type" ).val() + '" auto_locate="' + locateUser + '"';
18
-
19
- // Grab the values for the selected markers
20
- markers = WPSL_Selected_Markers();
21
-
22
- if ( typeof markers.start !== "undefined" ) {
23
- shortcodeAtts += ' start_marker="' + markers.start + '"';
24
- }
25
-
26
- if ( typeof markers.store !== "undefined" ) {
27
- shortcodeAtts += ' store_marker="' + markers.store + '"';
28
- }
29
-
30
- if ( startLocation ) {
31
- shortcodeAtts += ' start_location="' + startLocation + '"';
32
- }
33
-
34
- if ( typeof catRestriction !== "undefined" && catRestriction !== null && !catFilterType ) {
35
- shortcodeAtts += ' category="' + catRestriction + '"';
36
- }
37
-
38
- // Make sure we target the correct ID based on the filter type selection.
39
- if ( catFilterType == "dropdown" ) {
40
- catSelectionID = "wpsl-cat-selection";
41
- } else {
42
- catSelectionID = "wpsl-checkbox-selection";
43
- }
44
-
45
- catSelection = jQuery( '#' + catSelectionID + '' ).val();
46
-
47
- if ( catSelection ) {
48
- shortcodeAtts += ' category_selection="' + catSelection + '"';
49
- }
50
-
51
- if ( catFilterType ) {
52
- shortcodeAtts += ' category_filter_type="' + catFilterType + '"';
53
- }
54
-
55
- if ( catFilterType == "checkboxes" ) {
56
- checkboxColumns = parseInt( jQuery( "#wpsl-checkbox-columns" ).val() );
57
-
58
- if ( typeof checkboxColumns === 'number' ) {
59
- shortcodeAtts += ' checkbox_columns="' + checkboxColumns + '"';
60
- }
61
- }
62
-
63
- // Send the collected shortcode attributes to the editor
64
- win.send_to_editor("[wpsl " + shortcodeAtts + "]");
65
- }
66
-
67
- function WPSL_Selected_Markers() {
68
- var startMarker, storeMarker,
69
- markers = [],
70
- selectedMarkers = {};
71
-
72
- jQuery( ".wpsl-marker-list ").each( function( i ) {
73
- markers.push( jQuery( ".wpsl-marker-list:eq(" + i + " ) .wpsl-active-marker input" ).val());
74
- });
75
-
76
- if ( markers.length == 2 ) {
77
- startMarker = markers[0].split( "." );
78
- storeMarker = markers[1].split( "." );
79
-
80
- if ( typeof startMarker[0] !== "undefined" ) {
81
- selectedMarkers.start = startMarker[0];
82
- }
83
-
84
- if ( typeof storeMarker[0] !== "undefined" ) {
85
- selectedMarkers.store = storeMarker[0];
86
- }
87
- }
88
-
89
- return selectedMarkers;
90
- }
91
-
92
- jQuery( document ).ready( function( $ ) {
93
- $( "#wpsl-media-tabs" ).tabs();
94
-
95
- // Show the tooltips.
96
- $( ".wpsl-info" ).on( "mouseover", function() {
97
- $(this).find(".wpsl-info-text").show();
98
- });
99
-
100
- $( ".wpsl-info" ).on( "mouseout", function() {
101
- $(this).find( ".wpsl-info-text" ).hide();
102
- });
103
-
104
- $( ".wpsl-marker-list input[type=radio]" ).click( function() {
105
- $( this ).parents( ".wpsl-marker-list" ).find( "li" ).removeClass();
106
- $( this ).parent( "li" ).addClass( "wpsl-active-marker" );
107
- });
108
-
109
- $( ".wpsl-marker-list li" ).click( function() {
110
- $( this ).parents( ".wpsl-marker-list" ).find( "input" ).prop( "checked", false );
111
- $( this ).find( "input" ).prop( "checked", true );
112
- $( this ).siblings().removeClass();
113
- $( this ).addClass( "wpsl-active-marker" );
114
- });
115
-
116
- $( "#wpsl-cat-filter-types" ).change( function() {
117
- var filterType = $( this ).val();
118
-
119
- if ( filterType == 'dropdown' ) {
120
- $( ".wpsl-cat-selection" ).show();
121
- $( ".wpsl-checkbox-options, .wpsl-cat-restriction, .wpsl-checkbox-selection" ).hide();
122
- } else if ( filterType == 'checkboxes' ) {
123
- $( ".wpsl-cat-selection, .wpsl-cat-restriction" ).hide();
124
- $( ".wpsl-checkbox-options, .wpsl-checkbox-selection" ).show();
125
- } else {
126
- $( ".wpsl-cat-restriction" ).show();
127
- $( ".wpsl-checkbox-options, .wpsl-cat-selection, .wpsl-checkbox-selection" ).hide();
128
- }
129
- });
130
  });
1
+ /**
2
+ * Insert the WPSL shortcode
3
+ *
4
+ * Grab the values from the thickbox form
5
+ * and use them to set the wpsl shortcode attributes.
6
+ *
7
+ * @since 2.2.10
8
+ */
9
+ function WPSL_InsertShortcode() {
10
+ var markers, shortcodeAtts, checkboxColumns, catSelectionID, catSelection,
11
+ win = window.dialogArguments || opener || parent || top,
12
+ startLocation = jQuery( "#wpsl-start-location" ).val(),
13
+ catFilterType = jQuery( "#wpsl-cat-filter-types" ).val(),
14
+ catRestriction = jQuery( "#wpsl-cat-restriction" ).val(),
15
+ locateUser = ( jQuery( "#wpsl-auto-locate" ).is( ":checked" ) ) ? true : false;
16
+
17
+ shortcodeAtts = 'template="' + jQuery( "#wpsl-store-template" ).val() + '" map_type="' + jQuery( "#wpsl-map-type" ).val() + '" auto_locate="' + locateUser + '"';
18
+
19
+ // Grab the values for the selected markers
20
+ markers = WPSL_Selected_Markers();
21
+
22
+ if ( typeof markers.start !== "undefined" ) {
23
+ shortcodeAtts += ' start_marker="' + markers.start + '"';
24
+ }
25
+
26
+ if ( typeof markers.store !== "undefined" ) {
27
+ shortcodeAtts += ' store_marker="' + markers.store + '"';
28
+ }
29
+
30
+ if ( startLocation ) {
31
+ shortcodeAtts += ' start_location="' + startLocation + '"';
32
+ }
33
+
34
+ if ( typeof catRestriction !== "undefined" && catRestriction !== null && !catFilterType ) {
35
+ shortcodeAtts += ' category="' + catRestriction + '"';
36
+ }
37
+
38
+ // Make sure we target the correct ID based on the filter type selection.
39
+ if ( catFilterType == "dropdown" ) {
40
+ catSelectionID = "wpsl-cat-selection";
41
+ } else {
42
+ catSelectionID = "wpsl-checkbox-selection";
43
+ }
44
+
45
+ catSelection = jQuery( '#' + catSelectionID + '' ).val();
46
+
47
+ if ( catSelection ) {
48
+ shortcodeAtts += ' category_selection="' + catSelection + '"';
49
+ }
50
+
51
+ if ( catFilterType ) {
52
+ shortcodeAtts += ' category_filter_type="' + catFilterType + '"';
53
+ }
54
+
55
+ if ( catFilterType == "checkboxes" ) {
56
+ checkboxColumns = parseInt( jQuery( "#wpsl-checkbox-columns" ).val() );
57
+
58
+ if ( typeof checkboxColumns === 'number' ) {
59
+ shortcodeAtts += ' checkbox_columns="' + checkboxColumns + '"';
60
+ }
61
+ }
62
+
63
+ // Send the collected shortcode attributes to the editor
64
+ win.send_to_editor("[wpsl " + shortcodeAtts + "]");
65
+ }
66
+
67
+ function WPSL_Selected_Markers() {
68
+ var startMarker, storeMarker,
69
+ markers = [],
70
+ selectedMarkers = {};
71
+
72
+ jQuery( ".wpsl-marker-list ").each( function( i ) {
73
+ markers.push( jQuery( ".wpsl-marker-list:eq(" + i + " ) .wpsl-active-marker input" ).val());
74
+ });
75
+
76
+ if ( markers.length == 2 ) {
77
+ startMarker = markers[0].split( "." );
78
+ storeMarker = markers[1].split( "." );
79
+
80
+ if ( typeof startMarker[0] !== "undefined" ) {
81
+ selectedMarkers.start = startMarker[0];
82
+ }
83
+
84
+ if ( typeof storeMarker[0] !== "undefined" ) {
85
+ selectedMarkers.store = storeMarker[0];
86
+ }
87
+ }
88
+
89
+ return selectedMarkers;
90
+ }
91
+
92
+ jQuery( document ).ready( function( $ ) {
93
+ $( "#wpsl-media-tabs" ).tabs();
94
+
95
+ // Show the tooltips.
96
+ $( ".wpsl-info" ).on( "mouseover", function() {
97
+ $(this).find(".wpsl-info-text").show();
98
+ });
99
+
100
+ $( ".wpsl-info" ).on( "mouseout", function() {
101
+ $(this).find( ".wpsl-info-text" ).hide();
102
+ });
103
+
104
+ $( ".wpsl-marker-list input[type=radio]" ).click( function() {
105
+ $( this ).parents( ".wpsl-marker-list" ).find( "li" ).removeClass();
106
+ $( this ).parent( "li" ).addClass( "wpsl-active-marker" );
107
+ });
108
+
109
+ $( ".wpsl-marker-list li" ).click( function() {
110
+ $( this ).parents( ".wpsl-marker-list" ).find( "input" ).prop( "checked", false );
111
+ $( this ).find( "input" ).prop( "checked", true );
112
+ $( this ).siblings().removeClass();
113
+ $( this ).addClass( "wpsl-active-marker" );
114
+ });
115
+
116
+ $( "#wpsl-cat-filter-types" ).change( function() {
117
+ var filterType = $( this ).val();
118
+
119
+ if ( filterType == 'dropdown' ) {
120
+ $( ".wpsl-cat-selection" ).show();
121
+ $( ".wpsl-checkbox-options, .wpsl-cat-restriction, .wpsl-checkbox-selection" ).hide();
122
+ } else if ( filterType == 'checkboxes' ) {
123
+ $( ".wpsl-cat-selection, .wpsl-cat-restriction" ).hide();
124
+ $( ".wpsl-checkbox-options, .wpsl-checkbox-selection" ).show();
125
+ } else {
126
+ $( ".wpsl-cat-restriction" ).show();
127
+ $( ".wpsl-checkbox-options, .wpsl-cat-selection, .wpsl-checkbox-selection" ).hide();
128
+ }
129
+ });
130
  });
admin/roles.php CHANGED
@@ -1,134 +1,134 @@
1
- <?php
2
-
3
- /**
4
- * Add WPSL Roles.
5
- *
6
- * @since 2.0.0
7
- * @return void
8
- */
9
- function wpsl_add_roles() {
10
-
11
- global $wp_roles;
12
-
13
- if ( class_exists( 'WP_Roles' ) ) {
14
- if ( !isset( $wp_roles ) ) {
15
- $wp_roles = new WP_Roles();
16
- }
17
- }
18
-
19
- if ( is_object( $wp_roles ) ) {
20
- add_role( 'wpsl_store_locator_manager', __( 'Store Locator Manager', 'wpsl' ), array(
21
- 'read' => true,
22
- 'edit_posts' => true,
23
- 'delete_posts' => true,
24
- 'unfiltered_html' => true,
25
- 'upload_files' => true,
26
- 'delete_others_pages' => true,
27
- 'delete_others_posts' => true,
28
- 'delete_pages' => true,
29
- 'delete_private_pages' => true,
30
- 'delete_private_posts' => true,
31
- 'delete_published_pages' => true,
32
- 'delete_published_posts' => true,
33
- 'edit_others_pages' => true,
34
- 'edit_others_posts' => true,
35
- 'edit_pages' => true,
36
- 'edit_private_pages' => true,
37
- 'edit_private_posts' => true,
38
- 'edit_published_pages' => true,
39
- 'edit_published_posts' => true,
40
- 'moderate_comments' => true,
41
- 'publish_pages' => true,
42
- 'publish_posts' => true,
43
- 'read_private_pages' => true,
44
- 'read_private_posts' => true
45
- ) );
46
- }
47
- }
48
-
49
- /**
50
- * Add WPSL user capabilities.
51
- *
52
- * @since 2.0.0
53
- * @return void
54
- */
55
- function wpsl_add_caps() {
56
-
57
- global $wp_roles;
58
-
59
- if ( class_exists( 'WP_Roles' ) ) {
60
- if ( !isset( $wp_roles ) ) {
61
- $wp_roles = new WP_Roles();
62
- }
63
- }
64
-
65
- if ( is_object( $wp_roles ) ) {
66
- $wp_roles->add_cap( 'administrator', 'manage_wpsl_settings' );
67
-
68
- $capabilities = wpsl_get_post_caps();
69
-
70
- foreach ( $capabilities as $cap ) {
71
- $wp_roles->add_cap( 'wpsl_store_locator_manager', $cap );
72
- $wp_roles->add_cap( 'administrator', $cap );
73
- }
74
- }
75
- }
76
-
77
- /**
78
- * Get the WPSL post type capabilities.
79
- *
80
- * @since 2.0.0
81
- * @return array $capabilities The post type capabilities
82
- */
83
- function wpsl_get_post_caps() {
84
-
85
- $capabilities = array(
86
- 'edit_store',
87
- 'read_store',
88
- 'delete_store',
89
- 'edit_stores',
90
- 'edit_others_stores',
91
- 'publish_stores',
92
- 'read_private_stores',
93
- 'delete_stores',
94
- 'delete_private_stores',
95
- 'delete_published_stores',
96
- 'delete_others_stores',
97
- 'edit_private_stores',
98
- 'edit_published_stores'
99
- );
100
-
101
- return $capabilities;
102
- }
103
-
104
- /**
105
- * Remove the WPSL caps and roles.
106
- *
107
- * Only called from uninstall.php
108
- *
109
- * @since 2.0.0
110
- * @return void
111
- */
112
- function wpsl_remove_caps_and_roles() {
113
-
114
- global $wp_roles;
115
-
116
- if ( class_exists( 'WP_Roles' ) ) {
117
- if ( !isset( $wp_roles ) ) {
118
- $wp_roles = new WP_Roles();
119
- }
120
- }
121
-
122
- if ( is_object( $wp_roles ) ) {
123
- $wp_roles->remove_cap( 'administrator', 'manage_wpsl_settings' );
124
-
125
- $capabilities = wpsl_get_post_caps();
126
-
127
- foreach ( $capabilities as $cap ) {
128
- $wp_roles->remove_cap( 'wpsl_store_locator_manager', $cap );
129
- $wp_roles->remove_cap( 'administrator', $cap );
130
- }
131
- }
132
-
133
- remove_role( 'wpsl_store_locator_manager' );
134
  }
1
+ <?php
2
+
3
+ /**
4
+ * Add WPSL Roles.
5
+ *
6
+ * @since 2.0.0
7
+ * @return void
8
+ */
9
+ function wpsl_add_roles() {
10
+
11
+ global $wp_roles;
12
+
13
+ if ( class_exists( 'WP_Roles' ) ) {
14
+ if ( !isset( $wp_roles ) ) {
15
+ $wp_roles = new WP_Roles();
16
+ }
17
+ }
18
+
19
+ if ( is_object( $wp_roles ) ) {
20
+ add_role( 'wpsl_store_locator_manager', __( 'Store Locator Manager', 'wpsl' ), array(
21
+ 'read' => true,
22
+ 'edit_posts' => true,
23
+ 'delete_posts' => true,
24
+ 'unfiltered_html' => true,
25
+ 'upload_files' => true,
26
+ 'delete_others_pages' => true,
27
+ 'delete_others_posts' => true,
28
+ 'delete_pages' => true,
29
+ 'delete_private_pages' => true,
30
+ 'delete_private_posts' => true,
31
+ 'delete_published_pages' => true,
32
+ 'delete_published_posts' => true,
33
+ 'edit_others_pages' => true,
34
+ 'edit_others_posts' => true,
35
+ 'edit_pages' => true,
36
+ 'edit_private_pages' => true,
37
+ 'edit_private_posts' => true,
38
+ 'edit_published_pages' => true,
39
+ 'edit_published_posts' => true,
40
+ 'moderate_comments' => true,
41
+ 'publish_pages' => true,
42
+ 'publish_posts' => true,
43
+ 'read_private_pages' => true,
44
+ 'read_private_posts' => true
45
+ ) );
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Add WPSL user capabilities.
51
+ *
52
+ * @since 2.0.0
53
+ * @return void
54
+ */
55
+ function wpsl_add_caps() {
56
+
57
+ global $wp_roles;
58
+
59
+ if ( class_exists( 'WP_Roles' ) ) {
60
+ if ( !isset( $wp_roles ) ) {
61
+ $wp_roles = new WP_Roles();
62
+ }
63
+ }
64
+
65
+ if ( is_object( $wp_roles ) ) {
66
+ $wp_roles->add_cap( 'administrator', 'manage_wpsl_settings' );
67
+
68
+ $capabilities = wpsl_get_post_caps();
69
+
70
+ foreach ( $capabilities as $cap ) {
71
+ $wp_roles->add_cap( 'wpsl_store_locator_manager', $cap );
72
+ $wp_roles->add_cap( 'administrator', $cap );
73
+ }
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Get the WPSL post type capabilities.
79
+ *
80
+ * @since 2.0.0
81
+ * @return array $capabilities The post type capabilities
82
+ */
83
+ function wpsl_get_post_caps() {
84
+
85
+ $capabilities = array(
86
+ 'edit_store',
87
+ 'read_store',
88
+ 'delete_store',
89
+ 'edit_stores',
90
+ 'edit_others_stores',
91
+ 'publish_stores',
92
+ 'read_private_stores',
93
+ 'delete_stores',
94
+ 'delete_private_stores',
95
+ 'delete_published_stores',
96
+ 'delete_others_stores',
97
+ 'edit_private_stores',
98
+ 'edit_published_stores'
99
+ );
100
+
101
+ return $capabilities;
102
+ }
103
+
104
+ /**
105
+ * Remove the WPSL caps and roles.
106
+ *
107
+ * Only called from uninstall.php
108
+ *
109
+ * @since 2.0.0
110
+ * @return void
111
+ */
112
+ function wpsl_remove_caps_and_roles() {
113
+
114
+ global $wp_roles;
115
+
116
+ if ( class_exists( 'WP_Roles' ) ) {
117
+ if ( !isset( $wp_roles ) ) {
118
+ $wp_roles = new WP_Roles();
119
+ }
120
+ }
121
+
122
+ if ( is_object( $wp_roles ) ) {
123
+ $wp_roles->remove_cap( 'administrator', 'manage_wpsl_settings' );
124
+
125
+ $capabilities = wpsl_get_post_caps();
126
+
127
+ foreach ( $capabilities as $cap ) {
128
+ $wp_roles->remove_cap( 'wpsl_store_locator_manager', $cap );
129
+ $wp_roles->remove_cap( 'administrator', $cap );
130
+ }
131
+ }
132
+
133
+ remove_role( 'wpsl_store_locator_manager' );
134
  }
admin/templates/add-ons.php CHANGED
@@ -1,59 +1,59 @@
1
- <?php
2
- if ( !defined( 'ABSPATH' ) ) exit;
3
-
4
- $campaign_params = '?utm_source=wpsl-add-ons&utm_medium=banner&utm_campaign=add-ons';
5
-
6
- // Load the add-on data from an existing transient, or grab new data from the remote URL.
7
- if ( false === ( $add_ons = get_transient( 'wpsl_addons' ) ) ) {
8
- $response = wp_remote_get( 'https://s3.amazonaws.com/wpsl/add-ons.json' );
9
-
10
- if ( !is_wp_error( $response ) ) {
11
- $add_ons = json_decode( wp_remote_retrieve_body( $response ) );
12
-
13
- if ( $add_ons ) {
14
- set_transient( 'wpsl_addons', $add_ons, WEEK_IN_SECONDS );
15
- }
16
- }
17
- }
18
- ?>
19
-
20
- <div class="wrap wpsl-add-ons">
21
- <h2><?php _e( 'WP Store Locator Add-Ons', 'wpsl' ); ?></h2>
22
-
23
- <?php
24
- if ( $add_ons ) {
25
- foreach ( $add_ons as $add_on ) {
26
- ?>
27
- <div class="wpsl-add-on">
28
- <?php if ( !empty( $add_on->url ) ) { ?>
29
- <a title="<?php echo esc_attr( $add_on->name ); ?>" href="<?php echo esc_url( $add_on->url ) . $campaign_params; ?>">
30
- <img src="<?php echo esc_url( $add_on->img ); ?>"/>
31
- </a>
32
- <?php } else { ?>
33
- <img src="<?php echo esc_url( $add_on->img ); ?>"/>
34
- <?php } ?>
35
-
36
- <div class="wpsl-add-on-desc">
37
- <p><?php echo esc_html( $add_on->desc ); ?></p>
38
-
39
- <div class="wpsl-add-on-status">
40
- <?php if ( !empty( $add_on->class ) && class_exists( $add_on->class ) ) { ?>
41
- <p><strong><?php _e( 'Already Installed.', 'wpsl' ); ?></strong></p>
42
- <?php } else if ( isset( $add_on->soon ) && $add_on->soon ) { ?>
43
- <p><strong><?php _e( 'Coming soon!', 'wpsl' ); ?></strong></p>
44
- <?php } else { ?>
45
- <a class="button-primary" href="<?php echo esc_url( $add_on->url ) . $campaign_params; ?>">
46
- <?php esc_html_e( 'Get This Add-On', 'wpsl' ); ?>
47
- </a>
48
- <?php } ?>
49
- </div>
50
- </div>
51
- </div>
52
- <?php
53
- }
54
- } else {
55
- echo '<p>'. __( 'Failed to load the add-on list from the server.', 'wpsl' ) . '</p>';
56
- echo '<p>'. __( 'Please try again later!', 'wpsl' ) . '</p>';
57
- }
58
- ?>
59
  </div>
1
+ <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
+
4
+ $campaign_params = '?utm_source=wpsl-add-ons&utm_medium=banner&utm_campaign=add-ons';
5
+
6
+ // Load the add-on data from an existing transient, or grab new data from the remote URL.
7
+ if ( false === ( $add_ons = get_transient( 'wpsl_addons' ) ) ) {
8
+ $response = wp_remote_get( 'https://s3.amazonaws.com/wpsl/add-ons.json' );
9
+
10
+ if ( !is_wp_error( $response ) ) {
11
+ $add_ons = json_decode( wp_remote_retrieve_body( $response ) );
12
+
13
+ if ( $add_ons ) {
14
+ set_transient( 'wpsl_addons', $add_ons, WEEK_IN_SECONDS );
15
+ }
16
+ }
17
+ }
18
+ ?>
19
+
20
+ <div class="wrap wpsl-add-ons">
21
+ <h2><?php _e( 'WP Store Locator Add-Ons', 'wpsl' ); ?></h2>
22
+
23
+ <?php
24
+ if ( $add_ons ) {
25
+ foreach ( $add_ons as $add_on ) {
26
+ ?>
27
+ <div class="wpsl-add-on">
28
+ <?php if ( !empty( $add_on->url ) ) { ?>
29
+ <a title="<?php echo esc_attr( $add_on->name ); ?>" href="<?php echo esc_url( $add_on->url ) . $campaign_params; ?>">
30
+ <img src="<?php echo esc_url( $add_on->img ); ?>"/>
31
+ </a>
32
+ <?php } else { ?>
33
+ <img src="<?php echo esc_url( $add_on->img ); ?>"/>
34
+ <?php } ?>
35
+
36
+ <div class="wpsl-add-on-desc">
37
+ <p><?php echo esc_html( $add_on->desc ); ?></p>
38
+
39
+ <div class="wpsl-add-on-status">
40
+ <?php if ( !empty( $add_on->class ) && class_exists( $add_on->class ) ) { ?>
41
+ <p><strong><?php _e( 'Already Installed.', 'wpsl' ); ?></strong></p>
42
+ <?php } else if ( isset( $add_on->soon ) && $add_on->soon ) { ?>
43
+ <p><strong><?php _e( 'Coming soon!', 'wpsl' ); ?></strong></p>
44
+ <?php } else { ?>
45
+ <a class="button-primary" href="<?php echo esc_url( $add_on->url ) . $campaign_params; ?>">
46
+ <?php esc_html_e( 'Get This Add-On', 'wpsl' ); ?>
47
+ </a>
48
+ <?php } ?>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ <?php
53
+ }
54
+ } else {
55
+ echo '<p>'. __( 'Failed to load the add-on list from the server.', 'wpsl' ) . '</p>';
56
+ echo '<p>'. __( 'Please try again later!', 'wpsl' ) . '</p>';
57
+ }
58
+ ?>
59
  </div>
admin/templates/map-settings.php CHANGED
@@ -1,625 +1,625 @@
1
- <?php
2
- if ( !defined( 'ABSPATH' ) ) exit;
3
-
4
- global $wpdb, $wpsl, $wpsl_admin, $wp_version, $wpsl_settings;
5
- ?>
6
-
7
- <div id="wpsl-wrap" class="wrap wpsl-settings <?php if ( floatval( $wp_version ) < 3.8 ) { echo 'wpsl-pre-38'; } // Fix CSS issue with < 3.8 versions ?>">
8
- <h2>WP Store Locator <?php _e( 'Settings', 'wpsl' ); ?></h2>
9
-
10
- <?php
11
- settings_errors();
12
-
13
- $tabs = apply_filters( 'wpsl_settings_tab', array( 'general' => __( 'General', 'wpsl' ) ) );
14
- $wpsl_licenses = apply_filters( 'wpsl_license_settings', array() );
15
- $current_tab = isset( $_GET['tab'] ) ? $_GET['tab'] : '';
16
-
17
- if ( $wpsl_licenses ) {
18
- $tabs['licenses'] = __( 'Licenses', 'wpsl' );
19
- }
20
-
21
- // Default to the general tab if an unknow tab value is set
22
- if ( !array_key_exists( $current_tab, $tabs ) ) {
23
- $current_tab = 'general';
24
- }
25
-
26
- if ( count( $tabs ) > 1 ) {
27
- echo '<h2 id="wpsl-tabs" class="nav-tab-wrapper">';
28
-
29
- foreach ( $tabs as $tab_key => $tab_name ) {
30
- if ( !$current_tab && $tab_key == 'general' || $current_tab == $tab_key ) {
31
- $active_tab = 'nav-tab-active';
32
- } else {
33
- $active_tab = '';
34
- }
35
-
36
- echo '<a class="nav-tab ' . $active_tab . '" title="' . esc_attr( $tab_name ) . '" href="' . admin_url( 'edit.php?post_type=wpsl_stores&page=wpsl_settings&tab=' . $tab_key ) . '">' . esc_attr( $tab_name ) . '</a>';
37
- }
38
-
39
- echo '</h2>';
40
- }
41
-
42
- if ( $wpsl_licenses && $current_tab == 'licenses' ) {
43
- ?>
44
-
45
- <form action="" method="post">
46
- <table class="wp-list-table widefat">
47
- <thead>
48
- <tr>
49
- <th scope="col"><?php _e( 'Add-On', 'wpsl' ); ?></th>
50
- <th scope="col"><?php _e( 'License Key', 'wpsl' ); ?></th>
51
- <th scope="col"><?php _e( 'License Expiry Date', 'wpsl' ); ?></th>
52
- </tr>
53
- </thead>
54
- <tbody id="the-list">
55
- <?php
56
- foreach ( $wpsl_licenses as $wpsl_license ) {
57
- $key = ( $wpsl_license['status'] == 'valid' ) ? esc_attr( $wpsl_license['key'] ) : '';
58
-
59
- echo '<tr>';
60
- echo '<td>' . esc_html( $wpsl_license['name'] ) . '</td>';
61
- echo '<td>';
62
- echo '<input type="text" value="' . $key . '" name="wpsl_licenses[' . esc_attr( $wpsl_license['short_name'] ) . ']" />';
63
-
64
- if ( $wpsl_license['status'] == 'valid' ) {
65
- echo '<input type="submit" class="button-secondary" name="' . esc_attr( $wpsl_license['short_name'] ) . '_license_key_deactivate" value="' . __( 'Deactivate License', 'wpsl' ) . '"/>';
66
- }
67
-
68
- wp_nonce_field( $wpsl_license['short_name'] . '_license-nonce', $wpsl_license['short_name'] . '_license-nonce' );
69
-
70
- echo '</td>';
71
- echo '<td>';
72
-
73
- if ( $wpsl_license['expiration'] && $wpsl_license['status'] == 'valid' ) {
74
- echo esc_html( date_i18n( get_option( 'date_format' ), strtotime( $wpsl_license['expiration'] ) ) );
75
- }
76
-
77
- echo '</td>';
78
- echo '</tr>';
79
- }
80
- ?>
81
- </tbody>
82
- </table>
83
-
84
- <p class="submit">
85
- <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button button-primary" id="submit" name="submit">
86
- </p>
87
- </form>
88
- <?php
89
- } else if ( $current_tab == 'general' || !$current_tab ) {
90
- ?>
91
-
92
- <div id="general">
93
- <form id="wpsl-settings-form" method="post" action="options.php" autocomplete="off" accept-charset="utf-8">
94
- <div class="postbox-container">
95
- <div class="metabox-holder">
96
- <div id="wpsl-api-settings" class="postbox">
97
- <h3 class="hndle"><span><?php _e( 'Google Maps API', 'wpsl' ); ?></span></h3>
98
- <div class="inside">
99
- <p>
100
- <label for="wpsl-api-server-key"><?php _e( 'Server key', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'A %sserver key%s allows you to monitor the usage of the Google Maps %sGeocoding API%s. %s %sRequired%s for %sapplications%s created after June 22, 2016.', 'wpsl' ), '<a href="https://wpstorelocator.co/document/create-google-api-keys/#server-key" target="_blank">', '</a>', '<a href="https://developers.google.com/maps/documentation/geocoding/intro">', '</a>', '<br><br>', '<strong>', '</strong>', '<a href="https://googlegeodevelopers.blogspot.nl/2016/06/building-for-scale-updates-to-google.html">', '</a>' ); ?></span></span></label>
101
- <input type="text" value="<?php echo esc_attr( $wpsl_settings['api_server_key'] ); ?>" name="wpsl_api[server_key]" class="textinput<?php if ( !get_option( 'wpsl_valid_server_key' ) ) { echo ' wpsl-validate-me wpsl-error'; } ?>" id="wpsl-api-server-key">
102
- </p>
103
- <p>
104
- <label for="wpsl-api-browser-key"><?php _e( 'Browser key', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'A %sbrowser key%s allows you to monitor the usage of the Google Maps %sJavaScript API%s. %s %sRequired%s for %sapplications%s created after June 22, 2016.', 'wpsl' ), '<a href="https://wpstorelocator.co/document/create-google-api-keys/#browser-key" target="_blank">', '</a>', '<a href="https://developers.google.com/maps/documentation/javascript/">', '</a>', '<br><br>', '<strong>', '</strong>', '<a href="https://googlegeodevelopers.blogspot.nl/2016/06/building-for-scale-updates-to-google.html">', '</a>' ); ?></span></span></label>
105
- <input type="text" value="<?php echo esc_attr( $wpsl_settings['api_browser_key'] ); ?>" name="wpsl_api[browser_key]" class="textinput" id="wpsl-api-browser-key">
106
- </p>
107
- <p>
108
- <label for="wpsl-api-language"><?php _e( 'Map language', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'If no map language is selected the browser\'s prefered language is used.', 'wpsl' ); ?></span></span></label>
109
- <select id="wpsl-api-language" name="wpsl_api[language]">
110
- <?php echo $wpsl_admin->settings_page->get_api_option_list( 'language' ); ?>
111
- </select>
112
- </p>
113
- <p>
114
- <label for="wpsl-api-region"><?php _e( 'Map region', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'This will bias the %sgeocoding%s results towards the selected region. %s If no region is selected the bias is set to the United States.', 'wpsl' ), '<a href="https://developers.google.com/maps/documentation/javascript/geocoding#Geocoding">', '</a>', '<br><br>' ); ?></span></span></label>
115
- <select id="wpsl-api-region" name="wpsl_api[region]">
116
- <?php echo $wpsl_admin->settings_page->get_api_option_list( 'region' ); ?>
117
- </select>
118
- </p>
119
- <p id="wpsl-geocode-component" <?php if ( !$wpsl_settings['api_region'] ) { echo 'style="display:none;"'; } ?>>
120
- <label for="wpsl-api-component"><?php _e( 'Restrict the geocoding results to the selected map region?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'If the %sgeocoding%s API finds more relevant results outside of the set map region ( some location names exist in multiple regions ), the user will likely see a "No results found" message. %s To rule this out you can restrict the results to the set map region. %s You can modify the used restrictions with %sthis%s filter.', 'wpsl' ), '<a href="https://developers.google.com/maps/documentation/javascript/geocoding#Geocoding">', '</a>', '<br><br>', '<br><br>', '<a href="http://wpstorelocator.co/document/wpsl_geocode_components">', '</a>' ); ?></span></span></label>
121
- <input type="checkbox" value="" <?php checked( $wpsl_settings['api_geocode_component'], true ); ?> name="wpsl_api[geocode_component]" id="wpsl-api-component">
122
- </p>
123
- <p class="submit">
124
- <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
125
- </p>
126
- </div>
127
- </div>
128
- </div>
129
- </div>
130
-
131
- <div class="postbox-container wpsl-search-settings">
132
- <div class="metabox-holder">
133
- <div id="wpsl-search-settings" class="postbox">
134
- <h3 class="hndle"><span><?php _e( 'Search', 'wpsl' ); ?></span></h3>
135
- <div class="inside">
136
- <p>
137
- <label for="wpsl-search-autocomplete"><?php _e( 'Enable autocomplete?', 'wpsl' ); ?></label>
138
- <input type="checkbox" value="" <?php checked( $wpsl_settings['autocomplete'], true ); ?> name="wpsl_search[autocomplete]" id="wpsl-search-autocomplete">
139
- </p>
140
- <p>
141
- <label for="wpsl-results-dropdown"><?php _e( 'Show the max results dropdown?', 'wpsl' ); ?></label>
142
- <input type="checkbox" value="" <?php checked( $wpsl_settings['results_dropdown'], true ); ?> name="wpsl_search[results_dropdown]" id="wpsl-results-dropdown">
143
- </p>
144
- <p>
145
- <label for="wpsl-radius-dropdown"><?php _e( 'Show the search radius dropdown?', 'wpsl' ); ?></label>
146
- <input type="checkbox" value="" <?php checked( $wpsl_settings['radius_dropdown'], true ); ?> name="wpsl_search[radius_dropdown]" id="wpsl-radius-dropdown">
147
- </p>
148
- <p>
149
- <label for="wpsl-category-filters"><?php _e( 'Enable category filters?', 'wpsl' ); ?></label>
150
- <input type="checkbox" value="" <?php checked( $wpsl_settings['category_filter'], true ); ?> name="wpsl_search[category_filter]" id="wpsl-category-filters" class="wpsl-has-conditional-option">
151
- </p>
152
- <div class="wpsl-conditional-option" <?php if ( !$wpsl_settings['category_filter'] ) { echo 'style="display:none;"'; } ?>>
153
- <p>
154
- <label for="wpsl-cat-filter-types"><?php _e( 'Filter type:', 'wpsl' ); ?></label>
155
- <?php echo $wpsl_admin->settings_page->create_dropdown( 'filter_types' ); ?>
156
- </p>
157
- </div>
158
- <p>
159
- <label for="wpsl-distance-unit"><?php _e( 'Distance unit', 'wpsl' ); ?>:</label>
160
- <span class="wpsl-radioboxes">
161
- <input type="radio" autocomplete="off" value="km" <?php checked( 'km', $wpsl_settings['distance_unit'] ); ?> name="wpsl_search[distance_unit]" id="wpsl-distance-km">
162
- <label for="wpsl-distance-km"><?php _e( 'km', 'wpsl' ); ?></label>
163
- <input type="radio" autocomplete="off" value="mi" <?php checked( 'mi', $wpsl_settings['distance_unit'] ); ?> name="wpsl_search[distance_unit]" id="wpsl-distance-mi">
164
- <label for="wpsl-distance-mi"><?php _e( 'mi', 'wpsl' ); ?></label>
165
- </span>
166
- </p>
167
- <p>
168
- <label for="wpsl-max-results"><?php _e( 'Max search results', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'The default value is set between the [ ].', 'wpsl' ); ?></span></span></label>
169
- <input type="text" value="<?php echo esc_attr( $wpsl_settings['max_results'] ); ?>" name="wpsl_search[max_results]" class="textinput" id="wpsl-max-results">
170
- </p>
171
- <p>
172
- <label for="wpsl-search-radius"><?php _e( 'Search radius options', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'The default value is set between the [ ].', 'wpsl' ); ?></span></span></label>
173
- <input type="text" value="<?php echo esc_attr( $wpsl_settings['search_radius'] ); ?>" name="wpsl_search[radius]" class="textinput" id="wpsl-search-radius">
174
- </p>
175
- <p class="submit">
176
- <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
177
- </p>
178
- </div>
179
- </div>
180
- </div>
181
- </div>
182
-
183
- <div class="postbox-container">
184
- <div class="metabox-holder">
185
- <div id="wpsl-map-settings" class="postbox">
186
- <h3 class="hndle"><span><?php _e( 'Map', 'wpsl' ); ?></span></h3>
187
- <div class="inside">
188
- <p>
189
- <label for="wpsl-auto-locate"><?php _e( 'Attempt to auto-locate the user', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'Most modern browsers %srequire%s a HTTPS connection before the Geolocation feature works.', 'wpsl' ), '<a href="https://wpstorelocator.co/document/html-5-geolocation-not-working/">', '</a>' ); ?></span></span></label>
190
- <input type="checkbox" value="" <?php checked( $wpsl_settings['auto_locate'], true ); ?> name="wpsl_map[auto_locate]" id="wpsl-auto-locate">
191
- </p>
192
- <p>
193
- <label for="wpsl-autoload"><?php _e( 'Load locations on page load', 'wpsl' ); ?>:</label>
194
- <input type="checkbox" value="" <?php checked( $wpsl_settings['autoload'], true ); ?> name="wpsl_map[autoload]" id="wpsl-autoload" class="wpsl-has-conditional-option">
195
- </p>
196
- <div class="wpsl-conditional-option" <?php if ( !$wpsl_settings['autoload'] ) { echo 'style="display:none;"'; } ?>>
197
- <p>
198
- <label for="wpsl-autoload-limit"><?php _e( 'Number of locations to show', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'Although the location data is cached after the first load, a lower number will result in the map being more responsive. %s If this field is left empty or set to 0, then all locations are loaded.', 'wpsl' ), '<br><br>' ); ?></span></span></label>
199
- <input type="text" value="<?php echo esc_attr( $wpsl_settings['autoload_limit'] ); ?>" name="wpsl_map[autoload_limit]" class="textinput" id="wpsl-autoload-limit">
200
- </p>
201
- </div>
202
- <p>
203
- <label for="wpsl-start-name"><?php _e( 'Start point', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( '%sRequired field.%s %s If auto-locating the user is disabled or fails, the center of the provided city or country will be used as the initial starting point for the user.', 'wpsl' ), '<strong>', '</strong>', '<br><br>' ); ?></span></span></label>
204
- <input type="text" value="<?php echo esc_attr( $wpsl_settings['start_name'] ); ?>" name="wpsl_map[start_name]" class="textinput" id="wpsl-start-name">
205
- <input type="hidden" value="<?php echo esc_attr( $wpsl_settings['start_latlng'] ); ?>" name="wpsl_map[start_latlng]" id="wpsl-latlng" />
206
- </p>
207
- <p>
208
- <label for="wpsl-run-fitbounds"><?php _e( 'Auto adjust the zoom level to make sure all markers are visible?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'This runs after a search is made, and makes sure all the returned locations are visible in the viewport.', 'wpsl' ); ?></span></span></label>
209
- <input type="checkbox" value="" <?php checked( $wpsl_settings['run_fitbounds'], true ); ?> name="wpsl_map[run_fitbounds]" id="wpsl-run-fitbounds">
210
- </p>
211
- <p>
212
- <label for="wpsl-zoom-level"><?php _e( 'Initial zoom level', 'wpsl' ); ?>:</label>
213
- <?php echo $wpsl_admin->settings_page->show_zoom_levels(); ?>
214
- </p>
215
- <p>
216
- <label for="wpsl-max-zoom-level"><?php _e( 'Max auto zoom level', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'This value sets the zoom level for the "Zoom here" link in the info window. %s It is also used to limit the zooming when the viewport of the map is changed to make all the markers fit on the screen.', 'wpsl' ), '<br><br>' ); ?></span></span></label>
217
- <?php echo $wpsl_admin->settings_page->create_dropdown( 'max_zoom_level' ); ?>
218
- </p>
219
- <p>
220
- <label for="wpsl-streetview"><?php _e( 'Show the street view controls?', 'wpsl' ); ?></label>
221
- <input type="checkbox" value="" <?php checked( $wpsl_settings['streetview'], true ); ?> name="wpsl_map[streetview]" id="wpsl-streetview">
222
- </p>
223
- <p>
224
- <label for="wpsl-type-control"><?php _e( 'Show the map type control?', 'wpsl' ); ?></label>
225
- <input type="checkbox" value="" <?php checked( $wpsl_settings['type_control'], true ); ?> name="wpsl_map[type_control]" id="wpsl-type-control">
226
- </p>
227
- <p>
228
- <label for="wpsl-scollwheel-zoom"><?php _e( 'Enable scroll wheel zooming?', 'wpsl' ); ?></label>
229
- <input type="checkbox" value="" <?php checked( $wpsl_settings['scrollwheel'], true ); ?> name="wpsl_map[scrollwheel]" id="wpsl-scollwheel-zoom">
230
- </p>
231
- <p>
232
- <label><?php _e( 'Zoom control position', 'wpsl' ); ?>:</label>
233
- <span class="wpsl-radioboxes">
234
- <input type="radio" autocomplete="off" value="left" <?php checked( 'left', $wpsl_settings['control_position'], true ); ?> name="wpsl_map[control_position]" id="wpsl-control-left">
235
- <label for="wpsl-control-left"><?php _e( 'Left', 'wpsl' ); ?></label>
236
- <input type="radio" autocomplete="off" value="right" <?php checked( 'right', $wpsl_settings['control_position'], true ); ?> name="wpsl_map[control_position]" id="wpsl-control-right">
237
- <label for="wpsl-control-right"><?php _e( 'Right', 'wpsl' ); ?></label>
238
- </span>
239
- </p>
240
- <p>
241
- <label for="wpsl-map-type"><?php _e( 'Map type', 'wpsl' ); ?>:</label>
242
- <?php echo $wpsl_admin->settings_page->create_dropdown( 'map_types' ); ?>
243
- </p>
244
- <p>
245
- <label for="wpsl-map-style"><?php _e( 'Map style', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'Custom map styles only work if the map type is set to "Roadmap" or "Terrain".', 'wpsl' ); ?></span></span></label>
246
- </p>
247
- <div class="wpsl-style-input">
248
- <p><?php echo sprintf( __( 'You can use existing map styles from %sSnazzy Maps%s or %sMap Stylr%s and paste it in the textarea below, or you can generate a custom map style through the %sMap Style Editor%s or %sStyled Maps Wizard%s.', 'wpsl' ), '<a target="_blank" href="http://snazzymaps.com">', '</a>', '<a target="_blank" href="http://mapstylr.com">', '</a>', '<a target="_blank" href="http://mapstylr.com/map-style-editor/">', '</a>', '<a target="_blank" href="http://gmaps-samples-v3.googlecode.com/svn/trunk/styledmaps/wizard/index.html">', '</a>' ); ?></p>
249
- <p><?php echo sprintf( __( 'If you like to write the style code yourself, then you can find the documentation from Google %shere%s.', 'wpsl' ), '<a target="_blank" href="https://developers.google.com/maps/documentation/javascript/styling">', '</a>' ); ?></p>
250
- <textarea id="wpsl-map-style" name="wpsl_map[map_style]"><?php echo strip_tags( stripslashes( json_decode( $wpsl_settings['map_style'] ) ) ); ?></textarea>
251
- <input type="submit" value="<?php _e( 'Preview Map Style', 'wpsl' ); ?>" class="button-primary" name="wpsl-style-preview" id="wpsl-style-preview">
252
- </div>
253
- <div id="wpsl-gmap-wrap" class="wpsl-styles-preview"></div>
254
- <p>
255
- <label for="wpsl-show-credits"><?php _e( 'Show credits?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'This will place a "Search provided by WP Store Locator" backlink below the map.', 'wpsl' ); ?></span></span></label>
256
- <input type="checkbox" value="" <?php checked( $wpsl_settings['show_credits'], true ); ?> name="wpsl_credits" id="wpsl-show-credits">
257
- </p>
258
- <p class="submit">
259
- <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
260
- </p>
261
- </div>
262
- </div>
263
- </div>
264
- </div>
265
-
266
- <div class="postbox-container">
267
- <div class="metabox-holder">
268
- <div id="wpsl-user-experience" class="postbox">
269
- <h3 class="hndle"><span><?php _e( 'User Experience', 'wpsl' ); ?></span></h3>
270
- <div class="inside">
271
- <p>
272
- <label for="wpsl-design-height"><?php _e( 'Store Locator height', 'wpsl' ); ?>:</label>
273
- <input size="3" value="<?php echo esc_attr( $wpsl_settings['height'] ); ?>" id="wpsl-design-height" name="wpsl_ux[height]"> px
274
- </p>
275
- <p>
276
- <label for="wpsl-infowindow-width"><?php _e( 'Max width for the info window content', 'wpsl' ); ?>:</label>
277
- <input size="3" value="<?php echo esc_attr( $wpsl_settings['infowindow_width'] ); ?>" id="wpsl-infowindow-width" name="wpsl_ux[infowindow_width]"> px
278
- </p>
279
- <p>
280
- <label for="wpsl-search-width"><?php _e( 'Search field width', 'wpsl' ); ?>:</label>
281
- <input size="3" value="<?php echo esc_attr( $wpsl_settings['search_width'] ); ?>" id="wpsl-search-width" name="wpsl_ux[search_width]"> px
282
- </p>
283
- <p>
284
- <label for="wpsl-label-width"><?php _e( 'Search and radius label width', 'wpsl' ); ?>:</label>
285
- <input size="3" value="<?php echo esc_attr( $wpsl_settings['label_width'] ); ?>" id="wpsl-label-width" name="wpsl_ux[label_width]"> px
286
- </p>
287
- <p>
288
- <label for="wpsl-store-template"><?php _e( 'Store Locator template', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'The selected template is used with the [wpsl] shortcode. %s You can add a custom template with the %swpsl_templates%s filter.', 'wpsl' ), '<br><br>', '<a href="http://wpstorelocator.co/document/wpsl_templates/">', '</a>' ); ?></span></span></label>
289
- <?php echo $wpsl_admin->settings_page->show_template_options(); ?>
290
- </p>
291
- <p id="wpsl-listing-below-no-scroll" <?php if ( $wpsl_settings['template_id'] != 'below_map' ) { echo 'style="display:none;"'; } ?>>
292
- <label for="wpsl-more-info-list"><?php _e( 'Hide the scrollbar?', 'wpsl' ); ?></label>
293
- <input type="checkbox" value="" <?php checked( $wpsl_settings['listing_below_no_scroll'], true ); ?> name="wpsl_ux[listing_below_no_scroll]" id="wpsl-listing-below-no-scroll">
294
- </p>
295
- <p>
296
- <label for="wpsl-new-window"><?php _e( 'Open links in a new window?', 'wpsl' ); ?></label>
297
- <input type="checkbox" value="" <?php checked( $wpsl_settings['new_window'], true ); ?> name="wpsl_ux[new_window]" id="wpsl-new-window">
298
- </p>
299
- <p>
300
- <label for="wpsl-reset-map"><?php _e( 'Show a reset map button?', 'wpsl' ); ?></label>
301
- <input type="checkbox" value="" <?php checked( $wpsl_settings['reset_map'], true ); ?> name="wpsl_ux[reset_map]" id="wpsl-reset-map">
302
- </p>
303
- <p>
304
- <label for="wpsl-direction-redirect"><?php _e( 'When a user clicks on "Directions", open a new window, and show the route on google.com/maps ?', 'wpsl' ); ?></label>
305
- <input type="checkbox" value="" <?php checked( $wpsl_settings['direction_redirect'], true ); ?> name="wpsl_ux[direction_redirect]" id="wpsl-direction-redirect">
306
- </p>
307
- <p>
308
- <label for="wpsl-more-info"><?php _e( 'Show a "More info" link in the store listings?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'This places a "More Info" link below the address and will show the phone, fax, email, opening hours and description once the link is clicked.', 'wpsl' ) ); ?></span></span></label>
309
- <input type="checkbox" value="" <?php checked( $wpsl_settings['more_info'], true ); ?> name="wpsl_ux[more_info]" id="wpsl-more-info" class="wpsl-has-conditional-option">
310
- </p>
311
- <div class="wpsl-conditional-option" <?php if ( !$wpsl_settings['more_info'] ) { echo 'style="display:none;"'; } ?>>
312
- <p>
313
- <label for="wpsl-more-info-list"><?php _e( 'Where do you want to show the "More info" details?', 'wpsl' ); ?></label>
314
- <?php echo $wpsl_admin->settings_page->create_dropdown( 'more_info' ); ?>
315
- </p>
316
- </div>
317
- <p>
318
- <label for="wpsl-contact-details"><?php _e( 'Always show the contact details below the address in the search results?', 'wpsl' ); ?></label>
319
- <input type="checkbox" value="" <?php checked( $wpsl_settings['show_contact_details'], true ); ?> name="wpsl_ux[show_contact_details]" id="wpsl-contact-details">
320
- </p>
321
- <p>
322
- <label for="wpsl-clickable-contact-details"><?php _e( 'Make the contact details always clickable?', 'wpsl' ); ?></label>
323
- <input type="checkbox" value="" <?php checked( $wpsl_settings['clickable_contact_details'], true ); ?> name="wpsl_ux[clickable_contact_details]" id="wpsl-clickable-contact-details">
324
- </p>
325
- <p>
326
- <label for="wpsl-store-url"><?php _e( 'Make the store name clickable if a store URL exists?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'If %spermalinks%s are enabled, the store name will always link to the store page.', 'wpsl' ), '<a href="' . admin_url( 'edit.php?post_type=wpsl_stores&page=wpsl_settings#wpsl-permalink-settings' ) . '">', '</a>' ); ?></span></span></label>
327
- <input type="checkbox" value="" <?php checked( $wpsl_settings['store_url'], true ); ?> name="wpsl_ux[store_url]" id="wpsl-store-url">
328
- </p>
329
- <p>
330
- <label for="wpsl-phone-url"><?php _e( 'Make the phone number clickable on mobile devices?', 'wpsl' ); ?></label>
331
- <input type="checkbox" value="" <?php checked( $wpsl_settings['phone_url'], true ); ?> name="wpsl_ux[phone_url]" id="wpsl-phone-url">
332
- </p>
333
- <p>
334
- <label for="wpsl-marker-streetview"><?php _e( 'If street view is available for the current location, then show a "Street view" link in the info window?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'Enabling this option can sometimes result in a small delay in the opening of the info window. %s This happens because an API request is made to Google Maps to check if street view is available for the current location.', 'wpsl' ), '<br><br>' ); ?></span></span></label>
335
- <input type="checkbox" value="" <?php checked( $wpsl_settings['marker_streetview'], true ); ?> name="wpsl_ux[marker_streetview]" id="wpsl-marker-streetview">
336
- </p>
337
- <p>
338
- <label for="wpsl-marker-zoom-to"><?php _e( 'Show a "Zoom here" link in the info window?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'Clicking this link will make the map zoom in to the %s max auto zoom level %s.', 'wpsl' ), '<a href="#wpsl-zoom-level">', '</a>' ); ?></span></span></label>
339
- <input type="checkbox" value="" <?php checked( $wpsl_settings['marker_zoom_to'], true ); ?> name="wpsl_ux[marker_zoom_to]" id="wpsl-marker-zoom-to">
340
- </p>
341
- <p>
342
- <label for="wpsl-mouse-focus"><?php _e( 'On page load move the mouse cursor to the search field?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'If the store locator is not placed at the top of the page, enabling this feature can result in the page scrolling down. %s %sThis option is disabled on mobile devices.%s', 'wpsl' ), '<br><br>', '<em>', '</em>' ); ?></span></span></label>
343
- <input type="checkbox" value="" <?php checked( $wpsl_settings['mouse_focus'], true ); ?> name="wpsl_ux[mouse_focus]" id="wpsl-mouse-focus">
344
- </p>
345
- <p>
346
- <label for="wpsl-infowindow-style"><?php _e( 'Use the default style for the info window?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'If the default style is disabled the %sInfoBox%s library will be used instead. %s This enables you to easily change the look and feel of the info window through the .wpsl-infobox css class.', 'wpsl' ), '<a href="http://google-maps-utility-library-v3.googlecode.com/svn/trunk/infobox/docs/reference.html" target="_blank">', '</a>', '<br><br>' ); ?></span></span></label>
347
- <input type="checkbox" value="default" <?php checked( $wpsl_settings['infowindow_style'], 'default' ); ?> name="wpsl_ux[infowindow_style]" id="wpsl-infowindow-style">
348
- </p>
349
- <p>
350
- <label for="wpsl-hide-country"><?php _e( 'Hide the country in the search results?', 'wpsl' ); ?></label>
351
- <input type="checkbox" value="" <?php checked( $wpsl_settings['hide_country'], true ); ?> name="wpsl_ux[hide_country]" id="wpsl-hide-country">
352
- </p>
353
- <p>
354
- <label for="wpsl-hide-distance"><?php _e( 'Hide the distance in the search results?', 'wpsl' ); ?></label>
355
- <input type="checkbox" value="" <?php checked( $wpsl_settings['hide_distance'], true ); ?> name="wpsl_ux[hide_distance]" id="wpsl-hide-distance">
356
- </p>
357
- <p>
358
- <label for="wpsl-bounce"><?php _e( 'If a user hovers over the search results the store marker', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'If marker clusters are enabled this option will not work as expected as long as the markers are clustered. %s The bouncing of the marker won\'t be visible at all unless a user zooms in far enough for the marker cluster to change back in to individual markers. %s The info window will open as expected, but it won\'t be clear to which marker it belongs to. ', 'wpsl' ), '<br><br>' , '<br><br>' ); ?></span></span></label>
359
- <?php echo $wpsl_admin->settings_page->create_dropdown( 'marker_effects' ); ?>
360
- </p>
361
- <p>
362
- <label for="wpsl-address-format"><?php _e( 'Address format', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'You can add custom address formats with the %swpsl_address_formats%s filter.', 'wpsl' ), '<a href="http://wpstorelocator.co/document/wpsl_address_formats/">', '</a>' ); ?></span></span></label>
363
- <?php echo $wpsl_admin->settings_page->create_dropdown( 'address_format' ); ?>
364
- </p>
365
- <p class="submit">
366
- <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
367
- </p>
368
- </div>
369
- </div>
370
- </div>
371
- </div>
372
-
373
- <div class="postbox-container">
374
- <div class="metabox-holder">
375
- <div id="wpsl-marker-settings" class="postbox">
376
- <h3 class="hndle"><span><?php _e( 'Markers', 'wpsl' ); ?></span></h3>
377
- <div class="inside">
378
- <?php echo $wpsl_admin->settings_page->show_marker_options(); ?>
379
- <p>
380
- <label for="wpsl-marker-clusters"><?php _e( 'Enable marker clusters?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'Recommended for maps with a large amount of markers.', 'wpsl' ); ?></span></span></label>
381
- <input type="checkbox" value="" <?php checked( $wpsl_settings['marker_clusters'], true ); ?> name="wpsl_map[marker_clusters]" id="wpsl-marker-clusters" class="wpsl-has-conditional-option">
382
- </p>
383
- <div class="wpsl-conditional-option" <?php if ( !$wpsl_settings['marker_clusters'] ) { echo 'style="display:none;"'; } ?>>
384
- <p>
385
- <label for="wpsl-marker-zoom"><?php _e( 'Max zoom level', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'If this zoom level is reached or exceeded, then all markers are moved out of the marker cluster and shown as individual markers.', 'wpsl' ); ?></span></span></label>
386
- <?php echo $wpsl_admin->settings_page->show_cluster_options( 'cluster_zoom' ); ?>
387
- </p>
388
- <p>
389
- <label for="wpsl-marker-cluster-size"><?php _e( 'Cluster size', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'The grid size of a cluster in pixels. %s A larger number will result in a lower amount of clusters and also make the algorithm run faster.', 'wpsl' ), '<br><br>' ); ?></span></span></label>
390
- <?php echo $wpsl_admin->settings_page->show_cluster_options( 'cluster_size' ); ?>
391
- </p>
392
- </div>
393
- <p class="submit">
394
- <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
395
- </p>
396
- </div>
397
- </div>
398
- </div>
399
- </div>
400
-
401
- <div class="postbox-container">
402
- <div class="metabox-holder">
403
- <div id="wpsl-store-editor-settings" class="postbox">
404
- <h3 class="hndle"><span><?php _e( 'Store Editor', 'wpsl' ); ?></span></h3>
405
- <div class="inside">
406
- <p>
407
- <label for="wpsl-editor-country"><?php _e( 'Default country', 'wpsl' ); ?>:</label>
408
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'editor_country', '' ) ); ?>" name="wpsl_editor[default_country]" class="textinput" id="wpsl-editor-country">
409
- </p>
410
- <p>
411
- <label for="wpsl-editor-map-type"><?php _e( 'Map type for the location preview', 'wpsl' ); ?>:</label>
412
- <?php echo $wpsl_admin->settings_page->create_dropdown( 'editor_map_types' ); ?>
413
- </p>
414
- <p>
415
- <label for="wpsl-editor-hide-hours"><?php _e( 'Hide the opening hours?', 'wpsl' ); ?></label>
416
- <input type="checkbox" value="" <?php checked( $wpsl_settings['hide_hours'], true ); ?> name="wpsl_editor[hide_hours]" id="wpsl-editor-hide-hours" class="wpsl-has-conditional-option">
417
- </p>
418
- <div class="wpsl-conditional-option" <?php if ( $wpsl_settings['hide_hours'] ) { echo 'style="display:none"'; } ?>>
419
- <?php if ( get_option( 'wpsl_legacy_support' ) ) { // Is only set for users who upgraded from 1.x ?>
420
- <p>
421
- <label for="wpsl-editor-hour-input"><?php _e( 'Opening hours input type', 'wpsl' ); ?>:</label>
422
- <?php echo $wpsl_admin->settings_page->create_dropdown( 'hour_input' ); ?>
423
- </p>
424
- <p class="wpsl-hour-notice <?php if ( $wpsl_settings['editor_hour_input'] !== 'dropdown' ) { echo 'style="display:none"'; } ?>">
425
- <em><?php echo sprintf( __( 'Opening hours created in version 1.x %sare not%s automatically converted to the new dropdown format.', 'wpsl' ), '<strong>', '</strong>' ); ?></em>
426
- </p>
427
- <div class="wpsl-textarea-hours" <?php if ( $wpsl_settings['editor_hour_input'] !== 'textarea' ) { echo 'style="display:none"'; } ?>>
428
- <p class="wpsl-default-hours"><strong><?php _e( 'The default opening hours', 'wpsl' ); ?></strong></p>
429
- <textarea rows="5" cols="5" name="wpsl_editor[textarea]" id="wpsl-textarea-hours"><?php if ( isset( $wpsl_settings['editor_hours']['textarea'] ) ) { echo esc_textarea( stripslashes( $wpsl_settings['editor_hours']['textarea'] ) ); } ?></textarea>
430
- </div>
431
- <?php } ?>
432
- <div class="wpsl-dropdown-hours" <?php if ( $wpsl_settings['editor_hour_input'] !== 'dropdown' ) { echo 'style="display:none"'; } ?>>
433
- <p>
434
- <label for="wpsl-editor-hour-format"><?php _e( 'Opening hours format', 'wpsl' ); ?>:</label>
435
- <?php echo $wpsl_admin->settings_page->show_opening_hours_format(); ?>
436
- </p>
437
- <p class="wpsl-default-hours"><strong><?php _e( 'The default opening hours', 'wpsl' ); ?></strong></p>
438
- <?php echo $wpsl_admin->metaboxes->opening_hours( 'settings' ); ?>
439
- </div>
440
- </div>
441
- <p><em><?php _e( 'The default country and opening hours are only used when a new store is created. So changing the default values will have no effect on existing store locations.', 'wpsl' ); ?></em></p>
442
-
443
- <p class="submit">
444
- <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
445
- </p>
446
- </div>
447
- </div>
448
- </div>
449
- </div>
450
-
451
- <div class="postbox-container">
452
- <div class="metabox-holder">
453
- <div id="wpsl-permalink-settings" class="postbox">
454
- <h3 class="hndle"><span><?php _e( 'Permalink', 'wpsl' ); ?></span></h3>
455
- <div class="inside">
456
- <p>
457
- <label for="wpsl-permalinks-active"><?php _e( 'Enable permalink?', 'wpsl' ); ?></label>
458
- <input type="checkbox" value="" <?php checked( $wpsl_settings['permalinks'], true ); ?> name="wpsl_permalinks[active]" id="wpsl-permalinks-active" class="wpsl-has-conditional-option">
459
- </p>
460
- <div class="wpsl-conditional-option" <?php if ( !$wpsl_settings['permalinks'] ) { echo 'style="display:none;"'; } ?>>
461
- <p>
462
- <label for="wpsl-permalinks-slug"><?php _e( 'Store slug', 'wpsl' ); ?>:</label>
463
- <input type="text" value="<?php echo esc_attr( $wpsl_settings['permalink_slug'] ); ?>" name="wpsl_permalinks[slug]" class="textinput" id="wpsl-permalinks-slug">
464
- </p>
465
- <p>
466
- <label for="wpsl-category-slug"><?php _e( 'Category slug', 'wpsl' ); ?>:</label>
467
- <input type="text" value="<?php echo esc_attr( $wpsl_settings['category_slug'] ); ?>" name="wpsl_permalinks[category_slug]" class="textinput" id="wpsl-category-slug">
468
- </p>
469
- <em><?php echo sprintf( __( 'The permalink slugs %smust be unique%s on your site.', 'wpsl' ), '<strong>', '</strong>' ); ?></em>
470
- </div>
471
- <p class="submit">
472
- <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
473
- </p>
474
- </div>
475
- </div>
476
- </div>
477
- </div>
478
-
479
- <div class="postbox-container">
480
- <div class="metabox-holder">
481
- <div id="wpsl-label-settings" class="postbox">
482
- <h3 class="hndle"><span><?php _e( 'Labels', 'wpsl' ); ?></span></h3>
483
- <div class="inside">
484
- <?php
485
- /*
486
- * Show a msg to make sure that when a WPML compatible plugin
487
- * is active users use the 'String Translations' page to change the labels,
488
- * instead of the 'Label' section.
489
- */
490
- if ( $wpsl->i18n->wpml_exists() ) {
491
- echo '<p>' . sprintf( __( '%sWarning!%s %sWPML%s, or a plugin using the WPML API is active.', 'wpsl' ), '<strong>', '</strong>', '<a href="https://wpml.org/">', '</a>' ) . '</p>';
492
- echo '<p>' . __( 'Please use the "String Translations" section in the used multilingual plugin to change the labels. Changing them here will have no effect as long as the multilingual plugin remains active.', 'wpsl' ) . '</p>';
493
- }
494
- ?>
495
- <p>
496
- <label for="wpsl-search"><?php _e( 'Your location', 'wpsl' ); ?>:</label>
497
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'search_label', __( 'Your location', 'wpsl' ) ) ); ?>" name="wpsl_label[search]" class="textinput" id="wpsl-search">
498
- </p>
499
- <p>
500
- <label for="wpsl-search-radius"><?php _e( 'Search radius', 'wpsl' ); ?>:</label>
501
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'radius_label', __( 'Search radius', 'wpsl' ) ) ); ?>" name="wpsl_label[radius]" class="textinput" id="wpsl-search-radius">
502
- </p>
503
- <p>
504
- <label for="wpsl-no-results"><?php _e( 'No results found', 'wpsl' ); ?>:</label>
505
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'no_results_label', __( 'No results found', 'wpsl' ) ) ); ?>" name="wpsl_label[no_results]" class="textinput" id="wpsl-no-results">
506
- </p>
507
- <p>
508
- <label for="wpsl-search-btn"><?php _e( 'Search', 'wpsl' ); ?>:</label>
509
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'search_btn_label', __( 'Search', 'wpsl' ) ) ); ?>" name="wpsl_label[search_btn]" class="textinput" id="wpsl-search-btn">
510
- </p>
511
- <p>
512
- <label for="wpsl-preloader"><?php _e( 'Searching (preloader text)', 'wpsl' ); ?>:</label>
513
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'preloader_label', __( 'Searching...', 'wpsl' ) ) ); ?>" name="wpsl_label[preloader]" class="textinput" id="wpsl-preloader">
514
- </p>
515
- <p>
516
- <label for="wpsl-results"><?php _e( 'Results', 'wpsl' ); ?>:</label>
517
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'results_label', __( 'Results', 'wpsl' ) ) ); ?>" name="wpsl_label[results]" class="textinput" id="wpsl-results">
518
- </p>
519
- <p>
520
- <label for="wpsl-category"><?php _e( 'Category filter', 'wpsl' ); ?>:</label>
521
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'category_label', __( 'Category', 'wpsl' ) ) ); ?>" name="wpsl_label[category]" class="textinput" id="wpsl-category">
522
- </p>
523
- <p>
524
- <label for="wpsl-category-default"><?php _e( 'Category first item', 'wpsl' ); ?>:</label>
525
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'category_default_label', __( 'Any', 'wpsl' ) ) ); ?>" name="wpsl_label[category_default]" class="textinput" id="wpsl-category-default">
526
- </p>
527
- <p>
528
- <label for="wpsl-more-info"><?php _e( 'More info', 'wpsl' ); ?>:</label>
529
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'more_label', __( 'More info', 'wpsl' ) ) ); ?>" name="wpsl_label[more]" class="textinput" id="wpsl-more-info">
530
- </p>
531
- <p>
532
- <label for="wpsl-phone"><?php _e( 'Phone', 'wpsl' ); ?>:</label>
533
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'phone_label', __( 'Phone', 'wpsl' ) ) ); ?>" name="wpsl_label[phone]" class="textinput" id="wpsl-phone">
534
- </p>
535
- <p>
536
- <label for="wpsl-fax"><?php _e( 'Fax', 'wpsl' ); ?>:</label>
537
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'fax_label', __( 'Fax', 'wpsl' ) ) ); ?>" name="wpsl_label[fax]" class="textinput" id="wpsl-fax">
538
- </p>
539
- <p>
540
- <label for="wpsl-email"><?php _e( 'Email', 'wpsl' ); ?>:</label>
541
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'email_label', __( 'Email', 'wpsl' ) ) ); ?>" name="wpsl_label[email]" class="textinput" id="wpsl-email">
542
- </p>
543
- <p>
544
- <label for="wpsl-url"><?php _e( 'Url', 'wpsl' ); ?>:</label>
545
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'url_label', __( 'Url', 'wpsl' ) ) ); ?>" name="wpsl_label[url]" class="textinput" id="wpsl-url">
546
- </p>
547
- <p>
548
- <label for="wpsl-hours"><?php _e( 'Hours', 'wpsl' ); ?>:</label>
549
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'hours_label', __( 'Hours', 'wpsl' ) ) ); ?>" name="wpsl_label[hours]" class="textinput" id="wpsl-hours">
550
- </p>
551
- <p>
552
- <label for="wpsl-start"><?php _e( 'Start location', 'wpsl' ); ?>:</label>
553
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'start_label', __( 'Start location', 'wpsl' ) ) ); ?>" name="wpsl_label[start]" class="textinput" id="wpsl-start">
554
- </p>
555
- <p>
556
- <label for="wpsl-directions"><?php _e( 'Get directions', 'wpsl' ); ?>:</label>
557
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'directions_label', __( 'Directions', 'wpsl' ) ) ); ?>" name="wpsl_label[directions]" class="textinput" id="wpsl-directions">
558
- </p>
559
- <p>
560
- <label for="wpsl-no-directions"><?php _e( 'No directions found', 'wpsl' ); ?>:</label>
561
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'no_directions_label', __( 'No route could be found between the origin and destination', 'wpsl' ) ) ); ?>" name="wpsl_label[no_directions]" class="textinput" id="wpsl-no-directions">
562
- </p>
563
- <p>
564
- <label for="wpsl-back"><?php _e( 'Back', 'wpsl' ); ?>:</label>
565
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'back_label', __( 'Back', 'wpsl' ) ) ); ?>" name="wpsl_label[back]" class="textinput" id="wpsl-back">
566
- </p>
567
- <p>
568
- <label for="wpsl-street-view"><?php _e( 'Street view', 'wpsl' ); ?>:</label>
569
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'street_view_label', __( 'Street view', 'wpsl' ) ) ); ?>" name="wpsl_label[street_view]" class="textinput" id="wpsl-street-view">
570
- </p>
571
- <p>
572
- <label for="wpsl-zoom-here"><?php _e( 'Zoom here', 'wpsl' ); ?>:</label>
573
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'zoom_here_label', __( 'Zoom here', 'wpsl' ) ) ); ?>" name="wpsl_label[zoom_here]" class="textinput" id="wpsl-zoom-here">
574
- </p>
575
- <p>
576
- <label for="wpsl-error"><?php _e( 'General error', 'wpsl' ); ?>:</label>
577
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'error_label', __( 'Something went wrong, please try again!', 'wpsl' ) ) ); ?>" name="wpsl_label[error]" class="textinput" id="wpsl-error">
578
- </p>
579
- <p>
580
- <label for="wpsl-limit"><?php _e( 'Query limit error', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'You can raise the %susage limit%s by obtaining an API %skey%s, and fill in the "API key" field at the top of this page.', 'wpsl' ), '<a href="https://developers.google.com/maps/documentation/javascript/usage#usage_limits" target="_blank">', '</a>' ,'<a href="https://developers.google.com/maps/documentation/javascript/tutorial#api_key" target="_blank">', '</a>' ); ?></span></span></label>
581
- <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'limit_label', __( 'API usage limit reached', 'wpsl' ) ) ); ?>" name="wpsl_label[limit]" class="textinput" id="wpsl-limit">
582
- </p>
583
- <p class="submit">
584
- <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
585
- </p>
586
- </div>
587
- </div>
588
- </div>
589
- </div>
590
-
591
- <div class="postbox-container">
592
- <div class="metabox-holder">
593
- <div id="wpsl-tools" class="postbox">
594
- <h3 class="hndle"><span><?php _e( 'Tools', 'wpsl' ); ?></span></h3>
595
- <div class="inside">
596
- <p>
597
- <label for="wpsl-debug"><?php _e( 'Enable store locator debug?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'This disables the WPSL transient cache. %sThe transient cache is only used if the %sLoad locations on page load%s option is enabled.', 'wpsl' ), '<br><br>', '<em>', '</em>' ); ?></span></span></label>
598
- <input type="checkbox" value="" <?php checked( $wpsl_settings['debug'], true ); ?> name="wpsl_tools[debug]" id="wpsl-debug">
599
- </p>
600
- <p>
601
- <label for="wpsl-deregister-gmaps"><?php _e( 'Enable compatibility mode?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'If the %sbrowser console%s shows the error below, then enabling this option should fix it. %s %sYou have included the Google Maps API multiple times on this page. This may cause unexpected errors.%s %s This error can in some situations break the store locator map.', 'wpsl' ), '<a href="https://codex.wordpress.org/Using_Your_Browser_to_Diagnose_JavaScript_Errors#Step_3:_Diagnosis">', '</a>', '<br><br>', '<em>', '</em>', '<br><br>' ); ?></span></span></label>
602
- <input type="checkbox" value="" <?php checked( $wpsl_settings['deregister_gmaps'], true ); ?> name="wpsl_tools[deregister_gmaps]" id="wpsl-deregister-gmaps">
603
- </p>
604
- <p>
605
- <label for="wpsl-transient"><?php _e( 'WPSL transients', 'wpsl' ); ?></label>
606
- <a class="button" href="<?php echo wp_nonce_url( admin_url( "edit.php?post_type=wpsl_stores&page=wpsl_settings&action=clear_wpsl_transients" ), 'clear_transients' ); ?>"><?php _e( 'Clear store locator transient cache', 'wpsl' ); ?></a>
607
- </p>
608
- <p class="submit">
609
- <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
610
- </p>
611
- </div>
612
- </div>
613
- </div>
614
- </div>
615
-
616
- <?php settings_fields( 'wpsl_settings' ); ?>
617
- </form>
618
- </div>
619
-
620
- <?php
621
- } else {
622
- do_action( 'wpsl_settings_section', $current_tab );
623
- }
624
- ?>
625
  </div>
1
+ <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
+
4
+ global $wpdb, $wpsl, $wpsl_admin, $wp_version, $wpsl_settings;
5
+ ?>
6
+
7
+ <div id="wpsl-wrap" class="wrap wpsl-settings <?php if ( floatval( $wp_version ) < 3.8 ) { echo 'wpsl-pre-38'; } // Fix CSS issue with < 3.8 versions ?>">
8
+ <h2>WP Store Locator <?php _e( 'Settings', 'wpsl' ); ?></h2>
9
+
10
+ <?php
11
+ settings_errors();
12
+
13
+ $tabs = apply_filters( 'wpsl_settings_tab', array( 'general' => __( 'General', 'wpsl' ) ) );
14
+ $wpsl_licenses = apply_filters( 'wpsl_license_settings', array() );
15
+ $current_tab = isset( $_GET['tab'] ) ? $_GET['tab'] : '';
16
+
17
+ if ( $wpsl_licenses ) {
18
+ $tabs['licenses'] = __( 'Licenses', 'wpsl' );
19
+ }
20
+
21
+ // Default to the general tab if an unknow tab value is set
22
+ if ( !array_key_exists( $current_tab, $tabs ) ) {
23
+ $current_tab = 'general';
24
+ }
25
+
26
+ if ( count( $tabs ) > 1 ) {
27
+ echo '<h2 id="wpsl-tabs" class="nav-tab-wrapper">';
28
+
29
+ foreach ( $tabs as $tab_key => $tab_name ) {
30
+ if ( !$current_tab && $tab_key == 'general' || $current_tab == $tab_key ) {
31
+ $active_tab = 'nav-tab-active';
32
+ } else {
33
+ $active_tab = '';
34
+ }
35
+
36
+ echo '<a class="nav-tab ' . $active_tab . '" title="' . esc_attr( $tab_name ) . '" href="' . admin_url( 'edit.php?post_type=wpsl_stores&page=wpsl_settings&tab=' . $tab_key ) . '">' . esc_attr( $tab_name ) . '</a>';
37
+ }
38
+
39
+ echo '</h2>';
40
+ }
41
+
42
+ if ( $wpsl_licenses && $current_tab == 'licenses' ) {
43
+ ?>
44
+
45
+ <form action="" method="post">
46
+ <table class="wp-list-table widefat">
47
+ <thead>
48
+ <tr>
49
+ <th scope="col"><?php _e( 'Add-On', 'wpsl' ); ?></th>
50
+ <th scope="col"><?php _e( 'License Key', 'wpsl' ); ?></th>
51
+ <th scope="col"><?php _e( 'License Expiry Date', 'wpsl' ); ?></th>
52
+ </tr>
53
+ </thead>
54
+ <tbody id="the-list">
55
+ <?php
56
+ foreach ( $wpsl_licenses as $wpsl_license ) {
57
+ $key = ( $wpsl_license['status'] == 'valid' ) ? esc_attr( $wpsl_license['key'] ) : '';
58
+
59
+ echo '<tr>';
60
+ echo '<td>' . esc_html( $wpsl_license['name'] ) . '</td>';
61
+ echo '<td>';
62
+ echo '<input type="text" value="' . $key . '" name="wpsl_licenses[' . esc_attr( $wpsl_license['short_name'] ) . ']" />';
63
+
64
+ if ( $wpsl_license['status'] == 'valid' ) {
65
+ echo '<input type="submit" class="button-secondary" name="' . esc_attr( $wpsl_license['short_name'] ) . '_license_key_deactivate" value="' . __( 'Deactivate License', 'wpsl' ) . '"/>';
66
+ }
67
+
68
+ wp_nonce_field( $wpsl_license['short_name'] . '_license-nonce', $wpsl_license['short_name'] . '_license-nonce' );
69
+
70
+ echo '</td>';
71
+ echo '<td>';
72
+
73
+ if ( $wpsl_license['expiration'] && $wpsl_license['status'] == 'valid' ) {
74
+ echo esc_html( date_i18n( get_option( 'date_format' ), strtotime( $wpsl_license['expiration'] ) ) );
75
+ }
76
+
77
+ echo '</td>';
78
+ echo '</tr>';
79
+ }
80
+ ?>
81
+ </tbody>
82
+ </table>
83
+
84
+ <p class="submit">
85
+ <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button button-primary" id="submit" name="submit">
86
+ </p>
87
+ </form>
88
+ <?php
89
+ } else if ( $current_tab == 'general' || !$current_tab ) {
90
+ ?>
91
+
92
+ <div id="general">
93
+ <form id="wpsl-settings-form" method="post" action="options.php" autocomplete="off" accept-charset="utf-8">
94
+ <div class="postbox-container">
95
+ <div class="metabox-holder">
96
+ <div id="wpsl-api-settings" class="postbox">
97
+ <h3 class="hndle"><span><?php _e( 'Google Maps API', 'wpsl' ); ?></span></h3>
98
+ <div class="inside">
99
+ <p>
100
+ <label for="wpsl-api-browser-key"><?php _e( 'Browser key', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'A %sbrowser key%s allows you to monitor the usage of the Google Maps %sJavaScript API%s. %s %sRequired%s for %sapplications%s created after June 22, 2016.', 'wpsl' ), '<a href="https://wpstorelocator.co/document/create-google-api-keys/#browser-key" target="_blank">', '</a>', '<a href="https://developers.google.com/maps/documentation/javascript/">', '</a>', '<br><br>', '<strong>', '</strong>', '<a href="https://googlegeodevelopers.blogspot.nl/2016/06/building-for-scale-updates-to-google.html">', '</a>' ); ?></span></span></label>
101
+ <input type="text" value="<?php echo esc_attr( $wpsl_settings['api_browser_key'] ); ?>" name="wpsl_api[browser_key]" class="textinput" id="wpsl-api-browser-key">
102
+ </p>
103
+ <p>
104
+ <label for="wpsl-api-server-key"><?php _e( 'Server key', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'A %sserver key%s allows you to monitor the usage of the Google Maps %sGeocoding API%s. %s %sRequired%s for %sapplications%s created after June 22, 2016.', 'wpsl' ), '<a href="https://wpstorelocator.co/document/create-google-api-keys/#server-key" target="_blank">', '</a>', '<a href="https://developers.google.com/maps/documentation/geocoding/intro">', '</a>', '<br><br>', '<strong>', '</strong>', '<a href="https://googlegeodevelopers.blogspot.nl/2016/06/building-for-scale-updates-to-google.html">', '</a>' ); ?></span></span></label>
105
+ <input type="text" value="<?php echo esc_attr( $wpsl_settings['api_server_key'] ); ?>" name="wpsl_api[server_key]" class="textinput<?php if ( !get_option( 'wpsl_valid_server_key' ) ) { echo ' wpsl-validate-me wpsl-error'; } ?>" id="wpsl-api-server-key">
106
+ </p>
107
+ <p>
108
+ <label for="wpsl-api-language"><?php _e( 'Map language', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'If no map language is selected the browser\'s prefered language is used.', 'wpsl' ); ?></span></span></label>
109
+ <select id="wpsl-api-language" name="wpsl_api[language]">
110
+ <?php echo $wpsl_admin->settings_page->get_api_option_list( 'language' ); ?>
111
+ </select>
112
+ </p>
113
+ <p>
114
+ <label for="wpsl-api-region"><?php _e( 'Map region', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'This will bias the %sgeocoding%s results towards the selected region. %s If no region is selected the bias is set to the United States.', 'wpsl' ), '<a href="https://developers.google.com/maps/documentation/javascript/geocoding#Geocoding">', '</a>', '<br><br>' ); ?></span></span></label>
115
+ <select id="wpsl-api-region" name="wpsl_api[region]">
116
+ <?php echo $wpsl_admin->settings_page->get_api_option_list( 'region' ); ?>
117
+ </select>
118
+ </p>
119
+ <p id="wpsl-geocode-component" <?php if ( !$wpsl_settings['api_region'] ) { echo 'style="display:none;"'; } ?>>
120
+ <label for="wpsl-api-component"><?php _e( 'Restrict the geocoding results to the selected map region?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'If the %sgeocoding%s API finds more relevant results outside of the set map region ( some location names exist in multiple regions ), the user will likely see a "No results found" message. %s To rule this out you can restrict the results to the set map region. %s You can modify the used restrictions with %sthis%s filter.', 'wpsl' ), '<a href="https://developers.google.com/maps/documentation/javascript/geocoding#Geocoding">', '</a>', '<br><br>', '<br><br>', '<a href="http://wpstorelocator.co/document/wpsl_geocode_components">', '</a>' ); ?></span></span></label>
121
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['api_geocode_component'], true ); ?> name="wpsl_api[geocode_component]" id="wpsl-api-component">
122
+ </p>
123
+ <p class="submit">
124
+ <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
125
+ </p>
126
+ </div>
127
+ </div>
128
+ </div>
129
+ </div>
130
+
131
+ <div class="postbox-container wpsl-search-settings">
132
+ <div class="metabox-holder">
133
+ <div id="wpsl-search-settings" class="postbox">
134
+ <h3 class="hndle"><span><?php _e( 'Search', 'wpsl' ); ?></span></h3>
135
+ <div class="inside">
136
+ <p>
137
+ <label for="wpsl-search-autocomplete"><?php _e( 'Enable autocomplete?', 'wpsl' ); ?></label>
138
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['autocomplete'], true ); ?> name="wpsl_search[autocomplete]" id="wpsl-search-autocomplete">
139
+ </p>
140
+ <p>
141
+ <label for="wpsl-results-dropdown"><?php _e( 'Show the max results dropdown?', 'wpsl' ); ?></label>
142
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['results_dropdown'], true ); ?> name="wpsl_search[results_dropdown]" id="wpsl-results-dropdown">
143
+ </p>
144
+ <p>
145
+ <label for="wpsl-radius-dropdown"><?php _e( 'Show the search radius dropdown?', 'wpsl' ); ?></label>
146
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['radius_dropdown'], true ); ?> name="wpsl_search[radius_dropdown]" id="wpsl-radius-dropdown">
147
+ </p>
148
+ <p>
149
+ <label for="wpsl-category-filters"><?php _e( 'Enable category filters?', 'wpsl' ); ?></label>
150
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['category_filter'], true ); ?> name="wpsl_search[category_filter]" id="wpsl-category-filters" class="wpsl-has-conditional-option">
151
+ </p>
152
+ <div class="wpsl-conditional-option" <?php if ( !$wpsl_settings['category_filter'] ) { echo 'style="display:none;"'; } ?>>
153
+ <p>
154
+ <label for="wpsl-cat-filter-types"><?php _e( 'Filter type:', 'wpsl' ); ?></label>
155
+ <?php echo $wpsl_admin->settings_page->create_dropdown( 'filter_types' ); ?>
156
+ </p>
157
+ </div>
158
+ <p>
159
+ <label for="wpsl-distance-unit"><?php _e( 'Distance unit', 'wpsl' ); ?>:</label>
160
+ <span class="wpsl-radioboxes">
161
+ <input type="radio" autocomplete="off" value="km" <?php checked( 'km', $wpsl_settings['distance_unit'] ); ?> name="wpsl_search[distance_unit]" id="wpsl-distance-km">
162
+ <label for="wpsl-distance-km"><?php _e( 'km', 'wpsl' ); ?></label>
163
+ <input type="radio" autocomplete="off" value="mi" <?php checked( 'mi', $wpsl_settings['distance_unit'] ); ?> name="wpsl_search[distance_unit]" id="wpsl-distance-mi">
164
+ <label for="wpsl-distance-mi"><?php _e( 'mi', 'wpsl' ); ?></label>
165
+ </span>
166
+ </p>
167
+ <p>
168
+ <label for="wpsl-max-results"><?php _e( 'Max search results', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'The default value is set between the [ ].', 'wpsl' ); ?></span></span></label>
169
+ <input type="text" value="<?php echo esc_attr( $wpsl_settings['max_results'] ); ?>" name="wpsl_search[max_results]" class="textinput" id="wpsl-max-results">
170
+ </p>
171
+ <p>
172
+ <label for="wpsl-search-radius"><?php _e( 'Search radius options', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'The default value is set between the [ ].', 'wpsl' ); ?></span></span></label>
173
+ <input type="text" value="<?php echo esc_attr( $wpsl_settings['search_radius'] ); ?>" name="wpsl_search[radius]" class="textinput" id="wpsl-search-radius">
174
+ </p>
175
+ <p class="submit">
176
+ <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
177
+ </p>
178
+ </div>
179
+ </div>
180
+ </div>
181
+ </div>
182
+
183
+ <div class="postbox-container">
184
+ <div class="metabox-holder">
185
+ <div id="wpsl-map-settings" class="postbox">
186
+ <h3 class="hndle"><span><?php _e( 'Map', 'wpsl' ); ?></span></h3>
187
+ <div class="inside">
188
+ <p>
189
+ <label for="wpsl-auto-locate"><?php _e( 'Attempt to auto-locate the user', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'Most modern browsers %srequire%s a HTTPS connection before the Geolocation feature works.', 'wpsl' ), '<a href="https://wpstorelocator.co/document/html-5-geolocation-not-working/">', '</a>' ); ?></span></span></label>
190
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['auto_locate'], true ); ?> name="wpsl_map[auto_locate]" id="wpsl-auto-locate">
191
+ </p>
192
+ <p>
193
+ <label for="wpsl-autoload"><?php _e( 'Load locations on page load', 'wpsl' ); ?>:</label>
194
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['autoload'], true ); ?> name="wpsl_map[autoload]" id="wpsl-autoload" class="wpsl-has-conditional-option">
195
+ </p>
196
+ <div class="wpsl-conditional-option" <?php if ( !$wpsl_settings['autoload'] ) { echo 'style="display:none;"'; } ?>>
197
+ <p>
198
+ <label for="wpsl-autoload-limit"><?php _e( 'Number of locations to show', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'Although the location data is cached after the first load, a lower number will result in the map being more responsive. %s If this field is left empty or set to 0, then all locations are loaded.', 'wpsl' ), '<br><br>' ); ?></span></span></label>
199
+ <input type="text" value="<?php echo esc_attr( $wpsl_settings['autoload_limit'] ); ?>" name="wpsl_map[autoload_limit]" class="textinput" id="wpsl-autoload-limit">
200
+ </p>
201
+ </div>
202
+ <p>
203
+ <label for="wpsl-start-name"><?php _e( 'Start point', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( '%sRequired field.%s %s If auto-locating the user is disabled or fails, the center of the provided city or country will be used as the initial starting point for the user.', 'wpsl' ), '<strong>', '</strong>', '<br><br>' ); ?></span></span></label>
204
+ <input type="text" value="<?php echo esc_attr( $wpsl_settings['start_name'] ); ?>" name="wpsl_map[start_name]" class="textinput" id="wpsl-start-name">
205
+ <input type="hidden" value="<?php echo esc_attr( $wpsl_settings['start_latlng'] ); ?>" name="wpsl_map[start_latlng]" id="wpsl-latlng" />
206
+ </p>
207
+ <p>
208
+ <label for="wpsl-run-fitbounds"><?php _e( 'Auto adjust the zoom level to make sure all markers are visible?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'This runs after a search is made, and makes sure all the returned locations are visible in the viewport.', 'wpsl' ); ?></span></span></label>
209
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['run_fitbounds'], true ); ?> name="wpsl_map[run_fitbounds]" id="wpsl-run-fitbounds">
210
+ </p>
211
+ <p>
212
+ <label for="wpsl-zoom-level"><?php _e( 'Initial zoom level', 'wpsl' ); ?>:</label>
213
+ <?php echo $wpsl_admin->settings_page->show_zoom_levels(); ?>
214
+ </p>
215
+ <p>
216
+ <label for="wpsl-max-zoom-level"><?php _e( 'Max auto zoom level', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'This value sets the zoom level for the "Zoom here" link in the info window. %s It is also used to limit the zooming when the viewport of the map is changed to make all the markers fit on the screen.', 'wpsl' ), '<br><br>' ); ?></span></span></label>
217
+ <?php echo $wpsl_admin->settings_page->create_dropdown( 'max_zoom_level' ); ?>
218
+ </p>
219
+ <p>
220
+ <label for="wpsl-streetview"><?php _e( 'Show the street view controls?', 'wpsl' ); ?></label>
221
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['streetview'], true ); ?> name="wpsl_map[streetview]" id="wpsl-streetview">
222
+ </p>
223
+ <p>
224
+ <label for="wpsl-type-control"><?php _e( 'Show the map type control?', 'wpsl' ); ?></label>
225
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['type_control'], true ); ?> name="wpsl_map[type_control]" id="wpsl-type-control">
226
+ </p>
227
+ <p>
228
+ <label for="wpsl-scollwheel-zoom"><?php _e( 'Enable scroll wheel zooming?', 'wpsl' ); ?></label>
229
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['scrollwheel'], true ); ?> name="wpsl_map[scrollwheel]" id="wpsl-scollwheel-zoom">
230
+ </p>
231
+ <p>
232
+ <label><?php _e( 'Zoom control position', 'wpsl' ); ?>:</label>
233
+ <span class="wpsl-radioboxes">
234
+ <input type="radio" autocomplete="off" value="left" <?php checked( 'left', $wpsl_settings['control_position'], true ); ?> name="wpsl_map[control_position]" id="wpsl-control-left">
235
+ <label for="wpsl-control-left"><?php _e( 'Left', 'wpsl' ); ?></label>
236
+ <input type="radio" autocomplete="off" value="right" <?php checked( 'right', $wpsl_settings['control_position'], true ); ?> name="wpsl_map[control_position]" id="wpsl-control-right">
237
+ <label for="wpsl-control-right"><?php _e( 'Right', 'wpsl' ); ?></label>
238
+ </span>
239
+ </p>
240
+ <p>
241
+ <label for="wpsl-map-type"><?php _e( 'Map type', 'wpsl' ); ?>:</label>
242
+ <?php echo $wpsl_admin->settings_page->create_dropdown( 'map_types' ); ?>
243
+ </p>
244
+ <p>
245
+ <label for="wpsl-map-style"><?php _e( 'Map style', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'Custom map styles only work if the map type is set to "Roadmap" or "Terrain".', 'wpsl' ); ?></span></span></label>
246
+ </p>
247
+ <div class="wpsl-style-input">
248
+ <p><?php echo sprintf( __( 'You can use existing map styles from %sSnazzy Maps%s or %sMap Stylr%s and paste it in the textarea below, or you can generate a custom map style through the %sMap Style Editor%s or %sStyled Maps Wizard%s.', 'wpsl' ), '<a target="_blank" href="http://snazzymaps.com">', '</a>', '<a target="_blank" href="http://mapstylr.com">', '</a>', '<a target="_blank" href="http://mapstylr.com/map-style-editor/">', '</a>', '<a target="_blank" href="http://gmaps-samples-v3.googlecode.com/svn/trunk/styledmaps/wizard/index.html">', '</a>' ); ?></p>
249
+ <p><?php echo sprintf( __( 'If you like to write the style code yourself, then you can find the documentation from Google %shere%s.', 'wpsl' ), '<a target="_blank" href="https://developers.google.com/maps/documentation/javascript/styling">', '</a>' ); ?></p>
250
+ <textarea id="wpsl-map-style" name="wpsl_map[map_style]"><?php echo strip_tags( stripslashes( json_decode( $wpsl_settings['map_style'] ) ) ); ?></textarea>
251
+ <input type="submit" value="<?php _e( 'Preview Map Style', 'wpsl' ); ?>" class="button-primary" name="wpsl-style-preview" id="wpsl-style-preview">
252
+ </div>
253
+ <div id="wpsl-gmap-wrap" class="wpsl-styles-preview"></div>
254
+ <p>
255
+ <label for="wpsl-show-credits"><?php _e( 'Show credits?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'This will place a "Search provided by WP Store Locator" backlink below the map.', 'wpsl' ); ?></span></span></label>
256
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['show_credits'], true ); ?> name="wpsl_credits" id="wpsl-show-credits">
257
+ </p>
258
+ <p class="submit">
259
+ <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
260
+ </p>
261
+ </div>
262
+ </div>
263
+ </div>
264
+ </div>
265
+
266
+ <div class="postbox-container">
267
+ <div class="metabox-holder">
268
+ <div id="wpsl-user-experience" class="postbox">
269
+ <h3 class="hndle"><span><?php _e( 'User Experience', 'wpsl' ); ?></span></h3>
270
+ <div class="inside">
271
+ <p>
272
+ <label for="wpsl-design-height"><?php _e( 'Store Locator height', 'wpsl' ); ?>:</label>
273
+ <input size="3" value="<?php echo esc_attr( $wpsl_settings['height'] ); ?>" id="wpsl-design-height" name="wpsl_ux[height]"> px
274
+ </p>
275
+ <p>
276
+ <label for="wpsl-infowindow-width"><?php _e( 'Max width for the info window content', 'wpsl' ); ?>:</label>
277
+ <input size="3" value="<?php echo esc_attr( $wpsl_settings['infowindow_width'] ); ?>" id="wpsl-infowindow-width" name="wpsl_ux[infowindow_width]"> px
278
+ </p>
279
+ <p>
280
+ <label for="wpsl-search-width"><?php _e( 'Search field width', 'wpsl' ); ?>:</label>
281
+ <input size="3" value="<?php echo esc_attr( $wpsl_settings['search_width'] ); ?>" id="wpsl-search-width" name="wpsl_ux[search_width]"> px
282
+ </p>
283
+ <p>
284
+ <label for="wpsl-label-width"><?php _e( 'Search and radius label width', 'wpsl' ); ?>:</label>
285
+ <input size="3" value="<?php echo esc_attr( $wpsl_settings['label_width'] ); ?>" id="wpsl-label-width" name="wpsl_ux[label_width]"> px
286
+ </p>
287
+ <p>
288
+ <label for="wpsl-store-template"><?php _e( 'Store Locator template', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'The selected template is used with the [wpsl] shortcode. %s You can add a custom template with the %swpsl_templates%s filter.', 'wpsl' ), '<br><br>', '<a href="http://wpstorelocator.co/document/wpsl_templates/">', '</a>' ); ?></span></span></label>
289
+ <?php echo $wpsl_admin->settings_page->show_template_options(); ?>
290
+ </p>
291
+ <p id="wpsl-listing-below-no-scroll" <?php if ( $wpsl_settings['template_id'] != 'below_map' ) { echo 'style="display:none;"'; } ?>>
292
+ <label for="wpsl-more-info-list"><?php _e( 'Hide the scrollbar?', 'wpsl' ); ?></label>
293
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['listing_below_no_scroll'], true ); ?> name="wpsl_ux[listing_below_no_scroll]" id="wpsl-listing-below-no-scroll">
294
+ </p>
295
+ <p>
296
+ <label for="wpsl-new-window"><?php _e( 'Open links in a new window?', 'wpsl' ); ?></label>
297
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['new_window'], true ); ?> name="wpsl_ux[new_window]" id="wpsl-new-window">
298
+ </p>
299
+ <p>
300
+ <label for="wpsl-reset-map"><?php _e( 'Show a reset map button?', 'wpsl' ); ?></label>
301
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['reset_map'], true ); ?> name="wpsl_ux[reset_map]" id="wpsl-reset-map">
302
+ </p>
303
+ <p>
304
+ <label for="wpsl-direction-redirect"><?php _e( 'When a user clicks on "Directions", open a new window, and show the route on google.com/maps ?', 'wpsl' ); ?></label>
305
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['direction_redirect'], true ); ?> name="wpsl_ux[direction_redirect]" id="wpsl-direction-redirect">
306
+ </p>
307
+ <p>
308
+ <label for="wpsl-more-info"><?php _e( 'Show a "More info" link in the store listings?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'This places a "More Info" link below the address and will show the phone, fax, email, opening hours and description once the link is clicked.', 'wpsl' ) ); ?></span></span></label>
309
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['more_info'], true ); ?> name="wpsl_ux[more_info]" id="wpsl-more-info" class="wpsl-has-conditional-option">
310
+ </p>
311
+ <div class="wpsl-conditional-option" <?php if ( !$wpsl_settings['more_info'] ) { echo 'style="display:none;"'; } ?>>
312
+ <p>
313
+ <label for="wpsl-more-info-list"><?php _e( 'Where do you want to show the "More info" details?', 'wpsl' ); ?></label>
314
+ <?php echo $wpsl_admin->settings_page->create_dropdown( 'more_info' ); ?>
315
+ </p>
316
+ </div>
317
+ <p>
318
+ <label for="wpsl-contact-details"><?php _e( 'Always show the contact details below the address in the search results?', 'wpsl' ); ?></label>
319
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['show_contact_details'], true ); ?> name="wpsl_ux[show_contact_details]" id="wpsl-contact-details">
320
+ </p>
321
+ <p>
322
+ <label for="wpsl-clickable-contact-details"><?php _e( 'Make the contact details always clickable?', 'wpsl' ); ?></label>
323
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['clickable_contact_details'], true ); ?> name="wpsl_ux[clickable_contact_details]" id="wpsl-clickable-contact-details">
324
+ </p>
325
+ <p>
326
+ <label for="wpsl-store-url"><?php _e( 'Make the store name clickable if a store URL exists?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'If %spermalinks%s are enabled, the store name will always link to the store page.', 'wpsl' ), '<a href="' . admin_url( 'edit.php?post_type=wpsl_stores&page=wpsl_settings#wpsl-permalink-settings' ) . '">', '</a>' ); ?></span></span></label>
327
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['store_url'], true ); ?> name="wpsl_ux[store_url]" id="wpsl-store-url">
328
+ </p>
329
+ <p>
330
+ <label for="wpsl-phone-url"><?php _e( 'Make the phone number clickable on mobile devices?', 'wpsl' ); ?></label>
331
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['phone_url'], true ); ?> name="wpsl_ux[phone_url]" id="wpsl-phone-url">
332
+ </p>
333
+ <p>
334
+ <label for="wpsl-marker-streetview"><?php _e( 'If street view is available for the current location, then show a "Street view" link in the info window?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'Enabling this option can sometimes result in a small delay in the opening of the info window. %s This happens because an API request is made to Google Maps to check if street view is available for the current location.', 'wpsl' ), '<br><br>' ); ?></span></span></label>
335
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['marker_streetview'], true ); ?> name="wpsl_ux[marker_streetview]" id="wpsl-marker-streetview">
336
+ </p>
337
+ <p>
338
+ <label for="wpsl-marker-zoom-to"><?php _e( 'Show a "Zoom here" link in the info window?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'Clicking this link will make the map zoom in to the %s max auto zoom level %s.', 'wpsl' ), '<a href="#wpsl-zoom-level">', '</a>' ); ?></span></span></label>
339
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['marker_zoom_to'], true ); ?> name="wpsl_ux[marker_zoom_to]" id="wpsl-marker-zoom-to">
340
+ </p>
341
+ <p>
342
+ <label for="wpsl-mouse-focus"><?php _e( 'On page load move the mouse cursor to the search field?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'If the store locator is not placed at the top of the page, enabling this feature can result in the page scrolling down. %s %sThis option is disabled on mobile devices.%s', 'wpsl' ), '<br><br>', '<em>', '</em>' ); ?></span></span></label>
343
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['mouse_focus'], true ); ?> name="wpsl_ux[mouse_focus]" id="wpsl-mouse-focus">
344
+ </p>
345
+ <p>
346
+ <label for="wpsl-infowindow-style"><?php _e( 'Use the default style for the info window?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'If the default style is disabled the %sInfoBox%s library will be used instead. %s This enables you to easily change the look and feel of the info window through the .wpsl-infobox css class.', 'wpsl' ), '<a href="http://google-maps-utility-library-v3.googlecode.com/svn/trunk/infobox/docs/reference.html" target="_blank">', '</a>', '<br><br>' ); ?></span></span></label>
347
+ <input type="checkbox" value="default" <?php checked( $wpsl_settings['infowindow_style'], 'default' ); ?> name="wpsl_ux[infowindow_style]" id="wpsl-infowindow-style">
348
+ </p>
349
+ <p>
350
+ <label for="wpsl-hide-country"><?php _e( 'Hide the country in the search results?', 'wpsl' ); ?></label>
351
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['hide_country'], true ); ?> name="wpsl_ux[hide_country]" id="wpsl-hide-country">
352
+ </p>
353
+ <p>
354
+ <label for="wpsl-hide-distance"><?php _e( 'Hide the distance in the search results?', 'wpsl' ); ?></label>
355
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['hide_distance'], true ); ?> name="wpsl_ux[hide_distance]" id="wpsl-hide-distance">
356
+ </p>
357
+ <p>
358
+ <label for="wpsl-bounce"><?php _e( 'If a user hovers over the search results the store marker', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'If marker clusters are enabled this option will not work as expected as long as the markers are clustered. %s The bouncing of the marker won\'t be visible at all unless a user zooms in far enough for the marker cluster to change back in to individual markers. %s The info window will open as expected, but it won\'t be clear to which marker it belongs to. ', 'wpsl' ), '<br><br>' , '<br><br>' ); ?></span></span></label>
359
+ <?php echo $wpsl_admin->settings_page->create_dropdown( 'marker_effects' ); ?>
360
+ </p>
361
+ <p>
362
+ <label for="wpsl-address-format"><?php _e( 'Address format', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'You can add custom address formats with the %swpsl_address_formats%s filter.', 'wpsl' ), '<a href="http://wpstorelocator.co/document/wpsl_address_formats/">', '</a>' ); ?></span></span></label>
363
+ <?php echo $wpsl_admin->settings_page->create_dropdown( 'address_format' ); ?>
364
+ </p>
365
+ <p class="submit">
366
+ <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
367
+ </p>
368
+ </div>
369
+ </div>
370
+ </div>
371
+ </div>
372
+
373
+ <div class="postbox-container">
374
+ <div class="metabox-holder">
375
+ <div id="wpsl-marker-settings" class="postbox">
376
+ <h3 class="hndle"><span><?php _e( 'Markers', 'wpsl' ); ?></span></h3>
377
+ <div class="inside">
378
+ <?php echo $wpsl_admin->settings_page->show_marker_options(); ?>
379
+ <p>
380
+ <label for="wpsl-marker-clusters"><?php _e( 'Enable marker clusters?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'Recommended for maps with a large amount of markers.', 'wpsl' ); ?></span></span></label>
381
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['marker_clusters'], true ); ?> name="wpsl_map[marker_clusters]" id="wpsl-marker-clusters" class="wpsl-has-conditional-option">
382
+ </p>
383
+ <div class="wpsl-conditional-option" <?php if ( !$wpsl_settings['marker_clusters'] ) { echo 'style="display:none;"'; } ?>>
384
+ <p>
385
+ <label for="wpsl-marker-zoom"><?php _e( 'Max zoom level', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php _e( 'If this zoom level is reached or exceeded, then all markers are moved out of the marker cluster and shown as individual markers.', 'wpsl' ); ?></span></span></label>
386
+ <?php echo $wpsl_admin->settings_page->show_cluster_options( 'cluster_zoom' ); ?>
387
+ </p>
388
+ <p>
389
+ <label for="wpsl-marker-cluster-size"><?php _e( 'Cluster size', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'The grid size of a cluster in pixels. %s A larger number will result in a lower amount of clusters and also make the algorithm run faster.', 'wpsl' ), '<br><br>' ); ?></span></span></label>
390
+ <?php echo $wpsl_admin->settings_page->show_cluster_options( 'cluster_size' ); ?>
391
+ </p>
392
+ </div>
393
+ <p class="submit">
394
+ <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
395
+ </p>
396
+ </div>
397
+ </div>
398
+ </div>
399
+ </div>
400
+
401
+ <div class="postbox-container">
402
+ <div class="metabox-holder">
403
+ <div id="wpsl-store-editor-settings" class="postbox">
404
+ <h3 class="hndle"><span><?php _e( 'Store Editor', 'wpsl' ); ?></span></h3>
405
+ <div class="inside">
406
+ <p>
407
+ <label for="wpsl-editor-country"><?php _e( 'Default country', 'wpsl' ); ?>:</label>
408
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'editor_country', '' ) ); ?>" name="wpsl_editor[default_country]" class="textinput" id="wpsl-editor-country">
409
+ </p>
410
+ <p>
411
+ <label for="wpsl-editor-map-type"><?php _e( 'Map type for the location preview', 'wpsl' ); ?>:</label>
412
+ <?php echo $wpsl_admin->settings_page->create_dropdown( 'editor_map_types' ); ?>
413
+ </p>
414
+ <p>
415
+ <label for="wpsl-editor-hide-hours"><?php _e( 'Hide the opening hours?', 'wpsl' ); ?></label>
416
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['hide_hours'], true ); ?> name="wpsl_editor[hide_hours]" id="wpsl-editor-hide-hours" class="wpsl-has-conditional-option">
417
+ </p>
418
+ <div class="wpsl-conditional-option" <?php if ( $wpsl_settings['hide_hours'] ) { echo 'style="display:none"'; } ?>>
419
+ <?php if ( get_option( 'wpsl_legacy_support' ) ) { // Is only set for users who upgraded from 1.x ?>
420
+ <p>
421
+ <label for="wpsl-editor-hour-input"><?php _e( 'Opening hours input type', 'wpsl' ); ?>:</label>
422
+ <?php echo $wpsl_admin->settings_page->create_dropdown( 'hour_input' ); ?>
423
+ </p>
424
+ <p class="wpsl-hour-notice <?php if ( $wpsl_settings['editor_hour_input'] !== 'dropdown' ) { echo 'style="display:none"'; } ?>">
425
+ <em><?php echo sprintf( __( 'Opening hours created in version 1.x %sare not%s automatically converted to the new dropdown format.', 'wpsl' ), '<strong>', '</strong>' ); ?></em>
426
+ </p>
427
+ <div class="wpsl-textarea-hours" <?php if ( $wpsl_settings['editor_hour_input'] !== 'textarea' ) { echo 'style="display:none"'; } ?>>
428
+ <p class="wpsl-default-hours"><strong><?php _e( 'The default opening hours', 'wpsl' ); ?></strong></p>
429
+ <textarea rows="5" cols="5" name="wpsl_editor[textarea]" id="wpsl-textarea-hours"><?php if ( isset( $wpsl_settings['editor_hours']['textarea'] ) ) { echo esc_textarea( stripslashes( $wpsl_settings['editor_hours']['textarea'] ) ); } ?></textarea>
430
+ </div>
431
+ <?php } ?>
432
+ <div class="wpsl-dropdown-hours" <?php if ( $wpsl_settings['editor_hour_input'] !== 'dropdown' ) { echo 'style="display:none"'; } ?>>
433
+ <p>
434
+ <label for="wpsl-editor-hour-format"><?php _e( 'Opening hours format', 'wpsl' ); ?>:</label>
435
+ <?php echo $wpsl_admin->settings_page->show_opening_hours_format(); ?>
436
+ </p>
437
+ <p class="wpsl-default-hours"><strong><?php _e( 'The default opening hours', 'wpsl' ); ?></strong></p>
438
+ <?php echo $wpsl_admin->metaboxes->opening_hours( 'settings' ); ?>
439
+ </div>
440
+ </div>
441
+ <p><em><?php _e( 'The default country and opening hours are only used when a new store is created. So changing the default values will have no effect on existing store locations.', 'wpsl' ); ?></em></p>
442
+
443
+ <p class="submit">
444
+ <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
445
+ </p>
446
+ </div>
447
+ </div>
448
+ </div>
449
+ </div>
450
+
451
+ <div class="postbox-container">
452
+ <div class="metabox-holder">
453
+ <div id="wpsl-permalink-settings" class="postbox">
454
+ <h3 class="hndle"><span><?php _e( 'Permalink', 'wpsl' ); ?></span></h3>
455
+ <div class="inside">
456
+ <p>
457
+ <label for="wpsl-permalinks-active"><?php _e( 'Enable permalink?', 'wpsl' ); ?></label>
458
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['permalinks'], true ); ?> name="wpsl_permalinks[active]" id="wpsl-permalinks-active" class="wpsl-has-conditional-option">
459
+ </p>
460
+ <div class="wpsl-conditional-option" <?php if ( !$wpsl_settings['permalinks'] ) { echo 'style="display:none;"'; } ?>>
461
+ <p>
462
+ <label for="wpsl-permalinks-slug"><?php _e( 'Store slug', 'wpsl' ); ?>:</label>
463
+ <input type="text" value="<?php echo esc_attr( $wpsl_settings['permalink_slug'] ); ?>" name="wpsl_permalinks[slug]" class="textinput" id="wpsl-permalinks-slug">
464
+ </p>
465
+ <p>
466
+ <label for="wpsl-category-slug"><?php _e( 'Category slug', 'wpsl' ); ?>:</label>
467
+ <input type="text" value="<?php echo esc_attr( $wpsl_settings['category_slug'] ); ?>" name="wpsl_permalinks[category_slug]" class="textinput" id="wpsl-category-slug">
468
+ </p>
469
+ <em><?php echo sprintf( __( 'The permalink slugs %smust be unique%s on your site.', 'wpsl' ), '<strong>', '</strong>' ); ?></em>
470
+ </div>
471
+ <p class="submit">
472
+ <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
473
+ </p>
474
+ </div>
475
+ </div>
476
+ </div>
477
+ </div>
478
+
479
+ <div class="postbox-container">
480
+ <div class="metabox-holder">
481
+ <div id="wpsl-label-settings" class="postbox">
482
+ <h3 class="hndle"><span><?php _e( 'Labels', 'wpsl' ); ?></span></h3>
483
+ <div class="inside">
484
+ <?php
485
+ /*
486
+ * Show a msg to make sure that when a WPML compatible plugin
487
+ * is active users use the 'String Translations' page to change the labels,
488
+ * instead of the 'Label' section.
489
+ */
490
+ if ( $wpsl->i18n->wpml_exists() ) {
491
+ echo '<p>' . sprintf( __( '%sWarning!%s %sWPML%s, or a plugin using the WPML API is active.', 'wpsl' ), '<strong>', '</strong>', '<a href="https://wpml.org/">', '</a>' ) . '</p>';
492
+ echo '<p>' . __( 'Please use the "String Translations" section in the used multilingual plugin to change the labels. Changing them here will have no effect as long as the multilingual plugin remains active.', 'wpsl' ) . '</p>';
493
+ }
494
+ ?>
495
+ <p>
496
+ <label for="wpsl-search"><?php _e( 'Your location', 'wpsl' ); ?>:</label>
497
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'search_label', __( 'Your location', 'wpsl' ) ) ); ?>" name="wpsl_label[search]" class="textinput" id="wpsl-search">
498
+ </p>
499
+ <p>
500
+ <label for="wpsl-search-radius"><?php _e( 'Search radius', 'wpsl' ); ?>:</label>
501
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'radius_label', __( 'Search radius', 'wpsl' ) ) ); ?>" name="wpsl_label[radius]" class="textinput" id="wpsl-search-radius">
502
+ </p>
503
+ <p>
504
+ <label for="wpsl-no-results"><?php _e( 'No results found', 'wpsl' ); ?>:</label>
505
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'no_results_label', __( 'No results found', 'wpsl' ) ) ); ?>" name="wpsl_label[no_results]" class="textinput" id="wpsl-no-results">
506
+ </p>
507
+ <p>
508
+ <label for="wpsl-search-btn"><?php _e( 'Search', 'wpsl' ); ?>:</label>
509
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'search_btn_label', __( 'Search', 'wpsl' ) ) ); ?>" name="wpsl_label[search_btn]" class="textinput" id="wpsl-search-btn">
510
+ </p>
511
+ <p>
512
+ <label for="wpsl-preloader"><?php _e( 'Searching (preloader text)', 'wpsl' ); ?>:</label>
513
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'preloader_label', __( 'Searching...', 'wpsl' ) ) ); ?>" name="wpsl_label[preloader]" class="textinput" id="wpsl-preloader">
514
+ </p>
515
+ <p>
516
+ <label for="wpsl-results"><?php _e( 'Results', 'wpsl' ); ?>:</label>
517
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'results_label', __( 'Results', 'wpsl' ) ) ); ?>" name="wpsl_label[results]" class="textinput" id="wpsl-results">
518
+ </p>
519
+ <p>
520
+ <label for="wpsl-category"><?php _e( 'Category filter', 'wpsl' ); ?>:</label>
521
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'category_label', __( 'Category', 'wpsl' ) ) ); ?>" name="wpsl_label[category]" class="textinput" id="wpsl-category">
522
+ </p>
523
+ <p>
524
+ <label for="wpsl-category-default"><?php _e( 'Category first item', 'wpsl' ); ?>:</label>
525
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'category_default_label', __( 'Any', 'wpsl' ) ) ); ?>" name="wpsl_label[category_default]" class="textinput" id="wpsl-category-default">
526
+ </p>
527
+ <p>
528
+ <label for="wpsl-more-info"><?php _e( 'More info', 'wpsl' ); ?>:</label>
529
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'more_label', __( 'More info', 'wpsl' ) ) ); ?>" name="wpsl_label[more]" class="textinput" id="wpsl-more-info">
530
+ </p>
531
+ <p>
532
+ <label for="wpsl-phone"><?php _e( 'Phone', 'wpsl' ); ?>:</label>
533
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'phone_label', __( 'Phone', 'wpsl' ) ) ); ?>" name="wpsl_label[phone]" class="textinput" id="wpsl-phone">
534
+ </p>
535
+ <p>
536
+ <label for="wpsl-fax"><?php _e( 'Fax', 'wpsl' ); ?>:</label>
537
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'fax_label', __( 'Fax', 'wpsl' ) ) ); ?>" name="wpsl_label[fax]" class="textinput" id="wpsl-fax">
538
+ </p>
539
+ <p>
540
+ <label for="wpsl-email"><?php _e( 'Email', 'wpsl' ); ?>:</label>
541
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'email_label', __( 'Email', 'wpsl' ) ) ); ?>" name="wpsl_label[email]" class="textinput" id="wpsl-email">
542
+ </p>
543
+ <p>
544
+ <label for="wpsl-url"><?php _e( 'Url', 'wpsl' ); ?>:</label>
545
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'url_label', __( 'Url', 'wpsl' ) ) ); ?>" name="wpsl_label[url]" class="textinput" id="wpsl-url">
546
+ </p>
547
+ <p>
548
+ <label for="wpsl-hours"><?php _e( 'Hours', 'wpsl' ); ?>:</label>
549
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'hours_label', __( 'Hours', 'wpsl' ) ) ); ?>" name="wpsl_label[hours]" class="textinput" id="wpsl-hours">
550
+ </p>
551
+ <p>
552
+ <label for="wpsl-start"><?php _e( 'Start location', 'wpsl' ); ?>:</label>
553
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'start_label', __( 'Start location', 'wpsl' ) ) ); ?>" name="wpsl_label[start]" class="textinput" id="wpsl-start">
554
+ </p>
555
+ <p>
556
+ <label for="wpsl-directions"><?php _e( 'Get directions', 'wpsl' ); ?>:</label>
557
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'directions_label', __( 'Directions', 'wpsl' ) ) ); ?>" name="wpsl_label[directions]" class="textinput" id="wpsl-directions">
558
+ </p>
559
+ <p>
560
+ <label for="wpsl-no-directions"><?php _e( 'No directions found', 'wpsl' ); ?>:</label>
561
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'no_directions_label', __( 'No route could be found between the origin and destination', 'wpsl' ) ) ); ?>" name="wpsl_label[no_directions]" class="textinput" id="wpsl-no-directions">
562
+ </p>
563
+ <p>
564
+ <label for="wpsl-back"><?php _e( 'Back', 'wpsl' ); ?>:</label>
565
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'back_label', __( 'Back', 'wpsl' ) ) ); ?>" name="wpsl_label[back]" class="textinput" id="wpsl-back">
566
+ </p>
567
+ <p>
568
+ <label for="wpsl-street-view"><?php _e( 'Street view', 'wpsl' ); ?>:</label>
569
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'street_view_label', __( 'Street view', 'wpsl' ) ) ); ?>" name="wpsl_label[street_view]" class="textinput" id="wpsl-street-view">
570
+ </p>
571
+ <p>
572
+ <label for="wpsl-zoom-here"><?php _e( 'Zoom here', 'wpsl' ); ?>:</label>
573
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'zoom_here_label', __( 'Zoom here', 'wpsl' ) ) ); ?>" name="wpsl_label[zoom_here]" class="textinput" id="wpsl-zoom-here">
574
+ </p>
575
+ <p>
576
+ <label for="wpsl-error"><?php _e( 'General error', 'wpsl' ); ?>:</label>
577
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'error_label', __( 'Something went wrong, please try again!', 'wpsl' ) ) ); ?>" name="wpsl_label[error]" class="textinput" id="wpsl-error">
578
+ </p>
579
+ <p>
580
+ <label for="wpsl-limit"><?php _e( 'Query limit error', 'wpsl' ); ?>:<span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'You can raise the %susage limit%s by obtaining an API %skey%s, and fill in the "API key" field at the top of this page.', 'wpsl' ), '<a href="https://developers.google.com/maps/documentation/javascript/usage#usage_limits" target="_blank">', '</a>' ,'<a href="https://developers.google.com/maps/documentation/javascript/tutorial#api_key" target="_blank">', '</a>' ); ?></span></span></label>
581
+ <input type="text" value="<?php echo esc_attr( $wpsl->i18n->get_translation( 'limit_label', __( 'API usage limit reached', 'wpsl' ) ) ); ?>" name="wpsl_label[limit]" class="textinput" id="wpsl-limit">
582
+ </p>
583
+ <p class="submit">
584
+ <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
585
+ </p>
586
+ </div>
587
+ </div>
588
+ </div>
589
+ </div>
590
+
591
+ <div class="postbox-container">
592
+ <div class="metabox-holder">
593
+ <div id="wpsl-tools" class="postbox">
594
+ <h3 class="hndle"><span><?php _e( 'Tools', 'wpsl' ); ?></span></h3>
595
+ <div class="inside">
596
+ <p>
597
+ <label for="wpsl-debug"><?php _e( 'Enable store locator debug?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'This disables the WPSL transient cache. %sThe transient cache is only used if the %sLoad locations on page load%s option is enabled.', 'wpsl' ), '<br><br>', '<em>', '</em>' ); ?></span></span></label>
598
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['debug'], true ); ?> name="wpsl_tools[debug]" id="wpsl-debug">
599
+ </p>
600
+ <p>
601
+ <label for="wpsl-deregister-gmaps"><?php _e( 'Enable compatibility mode?', 'wpsl' ); ?><span class="wpsl-info"><span class="wpsl-info-text wpsl-hide"><?php echo sprintf( __( 'If the %sbrowser console%s shows the error below, then enabling this option should fix it. %s %sYou have included the Google Maps API multiple times on this page. This may cause unexpected errors.%s %s This error can in some situations break the store locator map.', 'wpsl' ), '<a href="https://codex.wordpress.org/Using_Your_Browser_to_Diagnose_JavaScript_Errors#Step_3:_Diagnosis">', '</a>', '<br><br>', '<em>', '</em>', '<br><br>' ); ?></span></span></label>
602
+ <input type="checkbox" value="" <?php checked( $wpsl_settings['deregister_gmaps'], true ); ?> name="wpsl_tools[deregister_gmaps]" id="wpsl-deregister-gmaps">
603
+ </p>
604
+ <p>
605
+ <label for="wpsl-transient"><?php _e( 'WPSL transients', 'wpsl' ); ?></label>
606
+ <a class="button" href="<?php echo wp_nonce_url( admin_url( "edit.php?post_type=wpsl_stores&page=wpsl_settings&action=clear_wpsl_transients" ), 'clear_transients' ); ?>"><?php _e( 'Clear store locator transient cache', 'wpsl' ); ?></a>
607
+ </p>
608
+ <p class="submit">
609
+ <input type="submit" value="<?php _e( 'Save Changes', 'wpsl' ); ?>" class="button-primary">
610
+ </p>
611
+ </div>
612
+ </div>
613
+ </div>
614
+ </div>
615
+
616
+ <?php settings_fields( 'wpsl_settings' ); ?>
617
+ </form>
618
+ </div>
619
+
620
+ <?php
621
+ } else {
622
+ do_action( 'wpsl_settings_section', $current_tab );
623
+ }
624
+ ?>
625
  </div>
admin/upgrade.php CHANGED
@@ -1,695 +1,695 @@
1
- <?php
2
- add_action( 'admin_init', 'wpsl_check_upgrade' );
3
- add_action( 'admin_init', 'wpsl_cpt_update_state' );
4
-
5
- /**
6
- * If the db doesn't hold the current version, run the upgrade procedure
7
- *
8
- * @since 1.2
9
- * @return void
10
- */
11
- function wpsl_check_upgrade() {
12
-
13
- global $wpsl_settings;
14
-
15
- $current_version = get_option( 'wpsl_version' );
16
-
17
- if ( version_compare( $current_version, WPSL_VERSION_NUM, '===' ) )
18
- return;
19
-
20
- if ( version_compare( $current_version, '1.1', '<' ) ) {
21
- if ( is_array( $wpsl_settings ) ) {
22
- if ( empty( $wpsl_settings['reset_map'] ) ) {
23
- $wpsl_settings['reset_map'] = 0;
24
- }
25
-
26
- if ( empty( $wpsl_settings['auto_load'] ) ) {
27
- $wpsl_settings['auto_load'] = 1;
28
- }
29
-
30
- if ( empty( $wpsl_settings['new_window'] ) ) {
31
- $wpsl_settings['new_window'] = 0;
32
- }
33
-
34
- update_option( 'wpsl_settings', $wpsl_settings );
35
- }
36
- }
37
-
38
- if ( version_compare( $current_version, '1.2', '<' ) ) {
39
- if ( is_array( $wpsl_settings ) ) {
40
- if ( empty( $wpsl_settings['store_below'] ) ) {
41
- $wpsl_settings['store_below'] = 0;
42
- }
43
-
44
- if ( empty( $wpsl_settings['direction_redirect'] ) ) {
45
- $wpsl_settings['direction_redirect'] = 0;
46
- }
47
-
48
- update_option( 'wpsl_settings', $wpsl_settings );
49
- }
50
- }
51
-
52
- if ( version_compare( $current_version, '1.2.11', '<' ) ) {
53
- if ( is_array( $wpsl_settings ) ) {
54
- if ( empty( $wpsl_settings['more_info'] ) ) {
55
- $wpsl_settings['more_info'] = 0;
56
- }
57
-
58
- if ( empty( $wpsl_settings['more_label'] ) ) {
59
- $wpsl_settings['more_label'] = __( 'More info', 'wpsl' );
60
- }
61
-
62
- if ( empty( $wpsl_settings['mouse_focus'] ) ) {
63
- $wpsl_settings['mouse_focus'] = 1;
64
- }
65
-
66
- update_option( 'wpsl_settings', $wpsl_settings );
67
- }
68
- }
69
-
70
- if ( version_compare( $current_version, '1.2.12', '<' ) ) {
71
- if ( is_array( $wpsl_settings ) ) {
72
- if ( empty( $wpsl_settings['more_info_location'] ) ) {
73
- $wpsl_settings['more_info_location'] = __( 'info window', 'wpsl' );
74
- }
75
-
76
- if ( empty( $wpsl_settings['back_label'] ) ) {
77
- $wpsl_settings['back_label'] = __( 'Back', 'wpsl' );
78
- }
79
-
80
- if ( empty( $wpsl_settings['reset_label'] ) ) {
81
- $wpsl_settings['reset_label'] = __( 'Reset', 'wpsl' );
82
- }
83
-
84
- if ( empty( $wpsl_settings['store_below_scroll'] ) ) {
85
- $wpsl_settings['store_below_scroll'] = 0;
86
- }
87
-
88
- update_option( 'wpsl_settings', $wpsl_settings );
89
- }
90
- }
91
-
92
- if ( version_compare( $current_version, '1.2.20', '<' ) ) {
93
-
94
- global $wpdb;
95
-
96
- $wpsl_table = $wpdb->prefix . 'wpsl_stores';
97
-
98
- // Rename the street field to address.
99
- $wpdb->query( "ALTER TABLE $wpsl_table CHANGE street address VARCHAR(255)" );
100
-
101
- // Add the second address field.
102
- $wpdb->query( "ALTER TABLE $wpsl_table ADD address2 VARCHAR(255) NULL AFTER address" );
103
-
104
- if ( is_array( $wpsl_settings ) ) {
105
- if ( empty( $wpsl_settings['store_url'] ) ) {
106
- $wpsl_settings['store_url'] = 0;
107
- }
108
-
109
- if ( empty( $wpsl_settings['phone_url'] ) ) {
110
- $wpsl_settings['phone_url'] = 0;
111
- }
112
-
113
- if ( empty( $wpsl_settings['marker_clusters'] ) ) {
114
- $wpsl_settings['marker_clusters'] = 0;
115
- }
116
-
117
- if ( empty( $wpsl_settings['cluster_zoom'] ) ) {
118
- $wpsl_settings['cluster_zoom'] = 0;
119
- }
120
-
121
- if ( empty( $wpsl_settings['cluster_size'] ) ) {
122
- $wpsl_settings['cluster_size'] = 0;
123
- }
124
-
125
- if ( empty( $wpsl_settings['template_id'] ) ) {
126
- $wpsl_settings['template_id'] = ( $wpsl_settings['store_below'] ) ? 1 : 0;
127
- unset( $wpsl_settings['store_below'] );
128
- }
129
-
130
- if ( empty( $wpsl_settings['marker_streetview'] ) ) {
131
- $wpsl_settings['marker_streetview'] = 0;
132
- }
133
-
134
- if ( empty( $wpsl_settings['marker_zoom_to'] ) ) {
135
- $wpsl_settings['marker_zoom_to'] = 0;
136
- }
137
-
138
- if ( !isset( $wpsl_settings['editor_country'] ) ) {
139
- $wpsl_settings['editor_country'] = '';
140
- }
141
-
142
- if ( empty( $wpsl_settings['street_view_label'] ) ) {
143
- $wpsl_settings['street_view_label'] = __( 'Street view', 'wpsl' );
144
- }
145
-
146
- if ( empty( $wpsl_settings['zoom_here_label'] ) ) {
147
- $wpsl_settings['zoom_here_label'] = __( 'Zoom here', 'wpsl' );
148
- }
149
-
150
- if ( empty( $wpsl_settings['no_directions_label'] ) ) {
151
- $wpsl_settings['no_directions_label'] = __( 'No route could be found between the origin and destination', 'wpsl' );
152
- }
153
-
154
- update_option( 'wpsl_settings', $wpsl_settings );
155
- }
156
- }
157
-
158
- if ( version_compare( $current_version, '2.0', '<' ) ) {
159
-
160
- global $wpdb;
161
-
162
- $wpsl_table = $wpdb->prefix . 'wpsl_stores';
163
-
164
- if ( is_array( $wpsl_settings ) ) {
165
- if ( empty( $wpsl_settings['radius_dropdown'] ) ) {
166
- $wpsl_settings['radius_dropdown'] = 1;
167
- }
168
-
169
- if ( empty( $wpsl_settings['permalinks'] ) ) {
170
- $wpsl_settings['permalinks'] = 0;
171
- }
172
-
173
- if ( empty( $wpsl_settings['permalink_slug'] ) ) {
174
- $wpsl_settings['permalink_slug'] = __( 'stores', 'wpsl' );
175
- }
176
-
177
- if ( empty( $wpsl_settings['category_slug'] ) ) {
178
- $wpsl_settings['category_slug'] = __( 'store-category', 'wpsl' );
179
- }
180
-
181
- if ( empty( $wpsl_settings['editor_hours'] ) ) {
182
- $wpsl_settings['editor_hours'] = wpsl_default_opening_hours();
183
- }
184
-
185
- if ( empty( $wpsl_settings['editor_hour_format'] ) ) {
186
- $wpsl_settings['editor_hour_format'] = 12;
187
- }
188
-
189
- if ( empty( $wpsl_settings['editor_map_type'] ) ) {
190
- $wpsl_settings['editor_map_type'] = 'roadmap';
191
- }
192
-
193
- if ( empty( $wpsl_settings['infowindow_style'] ) ) {
194
- $wpsl_settings['infowindow_style'] = 'default';
195
- }
196
-
197
- if ( empty( $wpsl_settings['email_label'] ) ) {
198
- $wpsl_settings['email_label'] = __( 'Email', 'wpsl' );
199
- }
200
-
201
- if ( empty( $wpsl_settings['url_label'] ) ) {
202
- $wpsl_settings['url_label'] = __( 'Url', 'wpsl' );
203
- }
204
-
205
- if ( empty( $wpsl_settings['category_label'] ) ) {
206
- $wpsl_settings['category_label'] = __( 'Category filter', 'wpsl' );
207
- }
208
-
209
- if ( empty( $wpsl_settings['show_credits'] ) ) {
210
- $wpsl_settings['show_credits'] = 0;
211
- }
212
-
213
- if ( empty( $wpsl_settings['autoload_limit'] ) ) {
214
- $wpsl_settings['autoload_limit'] = 50;
215
- }
216
-
217
- if ( empty( $wpsl_settings['scrollwheel'] ) ) {
218
- $wpsl_settings['scrollwheel'] = 1;
219
- }
220
-
221
- if ( empty( $wpsl_settings['type_control'] ) ) {
222
- $wpsl_settings['type_control'] = 0;
223
- }
224
-
225
- if ( empty( $wpsl_settings['hide_hours'] ) ) {
226
- $wpsl_settings['hide_hours'] = 0;
227
- }
228
-
229
- // Either correct the existing map style format from the 2.0 beta or set it to empty.
230
- if ( isset( $wpsl_settings['map_style'] ) && is_array( $wpsl_settings['map_style'] ) && isset( $wpsl_settings['map_style']['id'] ) ) {
231
- switch( $wpsl_settings['map_style']['id'] ) {
232
- case 'custom':
233
- $map_style = $wpsl_settings['map_style']['custom_json'];
234
- break;
235
- case 'default':
236
- $map_style = '';
237
- break;
238
- default:
239
- $map_style = $wpsl_settings['map_style']['theme_json'];
240
- break;
241
- }
242
-
243
- $wpsl_settings['map_style'] = $map_style;
244
- } else {
245
- $wpsl_settings['map_style'] = '';
246
- }
247
-
248
- if ( empty( $wpsl_settings['autoload'] ) ) {
249
- $wpsl_settings['autoload'] = $wpsl_settings['auto_load'];
250
- unset( $wpsl_settings['auto_load'] );
251
- }
252
-
253
- if ( empty( $wpsl_settings['address_format'] ) ) {
254
- $wpsl_settings['address_format'] = 'city_state_zip';
255
- }
256
-
257
- if ( empty( $wpsl_settings['auto_zoom_level'] ) ) {
258
- $wpsl_settings['auto_zoom_level'] = 15;
259
- }
260
-
261
- if ( empty( $wpsl_settings['hide_distance'] ) ) {
262
- $wpsl_settings['hide_distance'] = 0;
263
- }
264
-
265
- if ( empty( $wpsl_settings['debug'] ) ) {
266
- $wpsl_settings['debug'] = 0;
267
- }
268
-
269
- if ( empty( $wpsl_settings['category_dropdown'] ) ) {
270
- $wpsl_settings['category_dropdown'] = 0;
271
- }
272
-
273
- /*
274
- * Replace marker_bounce with marker_effect to better reflect what the option contains.
275
- *
276
- * If a user hovers over the result list then either the corresponding marker will bounce,
277
- * the info window will open, or nothing will happen.
278
- *
279
- * The default behaviour is that the marker will bounce.
280
- */
281
- if ( empty( $wpsl_settings['marker_effect'] ) ) {
282
- $wpsl_settings['marker_effect'] = ( $wpsl_settings['marker_bounce'] ) ? 'bounce' : 'ignore';
283
- unset( $wpsl_settings['marker_bounce'] );
284
- }
285
-
286
- /*
287
- * The default input for the opening hours is set to textarea for current users,
288
- * 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 ).
289
- */
290
- if ( empty( $wpsl_settings['editor_hour_input'] ) ) {
291
- $wpsl_settings['editor_hour_input'] = 'textarea';
292
- }
293
-
294
- // Rename store_below_scroll to listing_below_no_scroll, it better reflects what it does.
295
- if ( empty( $wpsl_settings['listing_below_no_scroll'] ) && isset( $wpsl_settings['store_below_scroll'] ) ) {
296
- $wpsl_settings['listing_below_no_scroll'] = $wpsl_settings['store_below_scroll'];
297
- unset( $wpsl_settings['store_below_scroll'] );
298
- }
299
-
300
- // Change the template ids from number based to name based.
301
- if ( is_numeric( $wpsl_settings['template_id'] ) ) {
302
- $wpsl_settings['template_id'] = ( !$wpsl_settings['template_id'] ) ? 'default' : 'below_map';
303
- }
304
-
305
- $replace_data = array(
306
- 'max_results' => $wpsl_settings['max_results'],
307
- 'search_radius' => $wpsl_settings['search_radius']
308
- );
309
-
310
- /*
311
- * Replace the () with [], this fixes an issue with the mod_security module that is installed on some servers.
312
- * It triggerd a 'Possible SQL injection attack' warning probably because of the int,(int) format of the data.
313
- */
314
- foreach ( $replace_data as $index => $option_value ) {
315
- $wpsl_settings[$index] = str_replace( array( '(', ')' ), array( '[', ']' ), $option_value );
316
- }
317
-
318
- // The reset button now uses an icon instead of text, so no need for the label anymore.
319
- unset( $wpsl_settings['reset_label'] );
320
-
321
- update_option( 'wpsl_settings', $wpsl_settings );
322
-
323
- /*
324
- * Users upgrading from 1.x will be given the choice between the textarea or
325
- * dropdowns for the opening hours.
326
- *
327
- * New users don't get that choice, they will only get the dropdowns.
328
- *
329
- * The wpsl_legacy_support option is used to determine if we need to show both options.
330
- */
331
- update_option( 'wpsl_legacy_support', 1 );
332
-
333
- // Add the WPSL roles and caps.
334
- wpsl_add_roles();
335
- wpsl_add_caps();
336
-
337
- // If there is a wpsl_stores table, then we need to convert all the locations to the 'wpsl_stores' custom post type.
338
- if ( $wpdb->get_var( "SHOW TABLES LIKE '$wpsl_table'" ) && version_compare( $current_version, '1.9', '<' ) ) {
339
- if ( wpsl_remaining_cpt_count() ) {
340
- update_option( 'wpsl_convert_cpt', 'in_progress' );
341
- }
342
- }
343
- }
344
- }
345
-
346
- /*
347
- * Both map options are no longer supported in 3.22 of the Google Maps API.
348
- * See: https://developers.google.com/maps/articles/v322-controls-diff
349
- */
350
- if ( version_compare( $current_version, '2.0.3', '<' ) ) {
351
- unset( $wpsl_settings['control_style'] );
352
- unset( $wpsl_settings['pan_controls'] );
353
-
354
- update_option( 'wpsl_settings', $wpsl_settings );
355
- }
356
-
357
- if ( version_compare( $current_version, '2.1.0', '<' ) ) {
358
- if ( !isset( $wpsl_settings['api_geocode_component'] ) ) {
359
- $wpsl_settings['api_geocode_component'] = 0;
360
- }
361
-
362
- update_option( 'wpsl_settings', $wpsl_settings );
363
- }
364
-
365
- if ( version_compare( $current_version, '2.2', '<' ) ) {
366
- $wpsl_settings['autocomplete'] = 0;
367
- $wpsl_settings['category_default_label'] = __( 'Any', 'wpsl' );
368
-
369
- // Rename the 'zoom_name' and 'zoom_latlng' to 'start_name' and 'start_latlng'.
370
- if ( isset( $wpsl_settings['zoom_name'] ) ) {
371
- $wpsl_settings['start_name'] = $wpsl_settings['zoom_name'];
372
- unset( $wpsl_settings['zoom_name'] );
373
- }
374
-
375
- if ( isset( $wpsl_settings['zoom_latlng'] ) ) {
376
- $wpsl_settings['start_latlng'] = $wpsl_settings['zoom_latlng'];
377
- unset( $wpsl_settings['zoom_latlng'] );
378
- }
379
-
380
- if ( isset( $wpsl_settings['category_dropdown'] ) ) {
381
- $wpsl_settings['category_filter'] = $wpsl_settings['category_dropdown'];
382
- unset( $wpsl_settings['category_dropdown'] );
383
- }
384
-
385
- // We now have separate browser and server key fields, and assume the existing key is a server key.
386
- if ( isset( $wpsl_settings['api_key'] ) ) {
387
- $wpsl_settings['api_server_key'] = $wpsl_settings['api_key'];
388
- unset( $wpsl_settings['api_key'] );
389
- }
390
-
391
- $wpsl_settings['api_browser_key'] = '';
392
- $wpsl_settings['category_filter_type'] = 'dropdown';
393
- $wpsl_settings['hide_country'] = 0;
394
- $wpsl_settings['show_contact_details'] = 0;
395
-
396
- update_option( 'wpsl_settings', $wpsl_settings );
397
- }
398
-
399
- if ( version_compare( $current_version, '2.2.4', '<' ) ) {
400
- $wpsl_settings['deregister_gmaps'] = 0;
401
-
402
- update_option( 'wpsl_settings', $wpsl_settings );
403
- }
404
-
405
- if ( version_compare( $current_version, '2.2.9', '<' ) ) {
406
- $wpsl_settings['run_fitbounds'] = 1;
407
-
408
- update_option( 'wpsl_settings', $wpsl_settings );
409
- }
410
-
411
- if ( version_compare( $current_version, '2.2.13', '<' ) ) {
412
- $wpsl_settings['clickable_contact_details'] = 0;
413
-
414
- update_option( 'wpsl_settings', $wpsl_settings );
415
- }
416
-
417
- update_option( 'wpsl_version', WPSL_VERSION_NUM );
418
- }
419
-
420
- /**
421
- * Check if we need to show the notice that tells users that the store locations
422
- * need to be converted to custom post types before the update from 1.x to 2.x is complete.
423
- *
424
- * @since 2.0
425
- * @return void
426
- */
427
- function wpsl_cpt_update_state() {
428
-
429
- global $wpsl_admin;
430
-
431
- $conversion_state = get_option( 'wpsl_convert_cpt' );
432
-
433
- if ( $conversion_state == 'in_progress' ) {
434
- if ( ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ) ) {
435
- $remaining = wpsl_remaining_cpt_count();
436
- $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>" ) );
437
-
438
- add_action( 'admin_footer', 'wpsl_cpt_dialog_html' );
439
- }
440
-
441
- add_action( 'admin_enqueue_scripts', 'wpsl_convert_cpt_js' );
442
- add_action( 'wp_ajax_convert_cpt', 'wpsl_convert_cpt' );
443
- add_action( 'wp_ajax_convert_cpt_count', 'wpsl_convert_cpt_count' );
444
- }
445
- }
446
-
447
- /**
448
- * Include the js file that handles the ajax request to
449
- * start converting the 1.x store locations to custom post types.
450
- *
451
- * @since 2.0
452
- * @return void
453
- */
454
- function wpsl_convert_cpt_js() {
455
-
456
- $cpt_js_l10n = array(
457
- '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>' ),
458
- 'securityFail' => __( 'Security check failed, reload the page and try again.', 'wpsl' )
459
- );
460
-
461
- wp_enqueue_script( 'jquery-ui-dialog' );
462
- wp_enqueue_script( 'wpsl-queue', plugins_url( '/js/ajax-queue.js', __FILE__ ), array( 'jquery' ), false );
463
- wp_enqueue_script( 'wpsl-cpt-js', plugins_url( '/js/wpsl-cpt-upgrade.js', __FILE__ ), array( 'jquery' ), false );
464
- wp_localize_script( 'wpsl-cpt-js', 'wpslCptConversion', $cpt_js_l10n );
465
- }
466
-
467
- /**
468
- * The html for the lightbox
469
- *
470
- * @since 2.0
471
- * @return void
472
- */
473
- function wpsl_cpt_dialog_html() {
474
-
475
- ?>
476
- <div id="wpsl-cpt-lightbox" style="display:none;">
477
- <span class="tb-close-icon"></span>
478
- <p class="wpsl-cpt-remaining"><?php _e( 'Store locations to convert:', 'wpsl' ); echo '<span></span>'; ?></p>
479
- <div class="wslp-cpt-fix-wrap">
480
- <input id="wpsl-start-cpt-conversion" class="button-primary" type="submit" value="<?php _e( 'Start Converting', 'wpsl' ); ?>" >
481
- <img class="wpsl-preloader" alt="preloader" src="<?php echo WPSL_URL . 'img/ajax-loader.gif'; ?>" />
482
- </div>
483
- <input type="hidden" name="wpsl-cpt-fix-nonce" value="<?php echo wp_create_nonce( 'wpsl-cpt-fix' ); ?>" />
484
- <input type="hidden" name="wpsl-cpt-conversion-count" value="<?php echo wp_create_nonce( 'wpsl-cpt-count' ); ?>" />
485
- </div>
486
- <div id="wpsl-cpt-overlay" style="display:none;"></div>
487
- <style>
488
- .wslp-cpt-fix-wrap {
489
- float:left;
490
- clear:both;
491
- width:100%;
492
- margin:0 0 15px 0;
493
- }
494
-
495
- #wpsl-cpt-lightbox .wpsl-cpt-remaining span {
496
- margin-left:5px;
497
- }
498
-
499
- #wpsl-start-cpt-conversion {
500
- float:left;
501
- }
502
-
503
- .wslp-cpt-fix-wrap .wpsl-preloader,
504
- .wslp-cpt-fix-wrap span {
505
- float:left;
506
- margin:8px 0 0 10px;
507
- }
508
-
509
- .wslp-cpt-fix-wrap .wpsl-preloader {
510
- display: none;
511
- }
512
-
513
- #wpsl-cpt-lightbox {
514
- position:fixed;
515
- width:450px;
516
- left:50%;
517
- right:50%;
518
- top:3.8em;
519
- padding:15px;
520
- background:none repeat scroll 0 0 #fff;
521
- border-radius:3px;
522
- margin-left:-225px;
523
- z-index: 9999;
524
- }
525
-
526
- #wpsl-cpt-overlay {
527
- position:fixed;
528
- right:0;
529
- top:0;
530
- z-index:9998;
531
- background:none repeat scroll 0 0 #000;
532
- bottom:0;
533
- left:0;
534
- opacity:0.5;
535
- }
536
-
537
- .tb-close-icon {
538
- color: #666;
539
- text-align: center;
540
- line-height: 29px;
541
- width: 29px;
542
- height: 29px;
543
- position: absolute;
544
- top: 0;
545
- right: 0;
546
- }
547
-
548
- .tb-close-icon:before {
549
- content: '\f158';
550
- font: normal 20px/29px 'dashicons';
551
- speak: none;
552
- -webkit-font-smoothing: antialiased;
553
- -moz-osx-font-smoothing: grayscale;
554
- }
555
-
556
- .tb-close-icon:hover {
557
- color: #999 !important;
558
- cursor: pointer;
559
- }
560
- </style>
561
- <?php
562
- }
563
-
564
- /**
565
- * Handle the ajax call to start converting the
566
- * store locations to custom post types.
567
- *
568
- * @since 2.0
569
- * @return void|string json on completion
570
- */
571
- function wpsl_convert_cpt() {
572
-
573
- if ( !current_user_can( 'manage_options' ) )
574
- die( '-1' );
575
- check_ajax_referer( 'wpsl-cpt-fix' );
576
-
577
- // Start the cpt coversion.
578
- wpsl_cpt_conversion();
579
-
580
- exit();
581
- }
582
-
583
- /**
584
- * Get the amount of locations that still need to be converted.
585
- *
586
- * @since 2.0
587
- * @return string json The amount of locations that still need to be converted
588
- */
589
- function wpsl_convert_cpt_count() {
590
-
591
- if ( !current_user_can( 'manage_options' ) )
592
- die( '-1' );
593
- check_ajax_referer( 'wpsl-cpt-count' );
594
-
595
- $remaining_count = wpsl_remaining_cpt_count();
596
-
597
- $response['success'] = true;
598
-
599
- if ( $remaining_count ) {
600
- $response['count'] = $remaining_count;
601
- } else {
602
- $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>' );
603
-
604
- delete_option( 'wpsl_convert_cpt' );
605
- }
606
-
607
- wp_send_json( $response );
608
-
609
- exit();
610
- }
611
-
612
- /**
613
- * Return the difference between the number of existing wpsl custom post types,
614
- * and the number of records in the old wpsl_stores database.
615
- *
616
- * @since 2.0
617
- * @return int|boolean $remaining The amount of locations that still need to be converted
618
- */
619
- function wpsl_remaining_cpt_count() {
620
-
621
- global $wpdb;
622
-
623
- $table = $wpdb->prefix . 'wpsl_stores';
624
- $count = wp_count_posts( 'wpsl_stores' );
625
-
626
- if ( isset( $count->publish ) && isset( $count->draft ) ) {
627
- $cpt_count = $count->publish + $count->draft;
628
- } else {
629
- $cpt_count = 0;
630
- }
631
-
632
- $db_count = $wpdb->get_var( "SELECT COUNT(wpsl_id) FROM $table" );
633
- $difference = $db_count - $cpt_count;
634
-
635
- /*
636
- * This prevents users who used the 2.0 beta, and later added
637
- * more stores from seeing the upgrade notice again.
638
- */
639
- $remaining = ( $difference < 0 ) ? false : $difference;
640
-
641
- return $remaining;
642
- }
643
-
644
- /**
645
- * Convert the existing locations to custom post types.
646
- *
647
- * @since 2.0
648
- * @return void|boolean True if the conversion is completed
649
- */
650
- function wpsl_cpt_conversion() {
651
-
652
- global $wpdb;
653
-
654
- // Try to disable the time limit to prevent timeouts.
655
- @set_time_limit( 0 );
656
-
657
- $meta_keys = array( 'address', 'address2', 'city', 'state', 'zip', 'country', 'country_iso', 'lat', 'lng', 'phone', 'fax', 'url', 'email', 'hours' );
658
- $offset = wpsl_remaining_cpt_count();
659
- $wpsl_table = $wpdb->prefix . 'wpsl_stores';
660
- $stores = $wpdb->get_results( "(SELECT * FROM $wpsl_table ORDER BY wpsl_id DESC LIMIT $offset) ORDER BY wpsl_id ASC" );
661
-
662
- foreach ( $stores as $store ) {
663
-
664
- // Make sure we set the correct post status.
665
- if ( $store->active ) {
666
- $post_status = 'publish';
667
- } else {
668
- $post_status = 'draft';
669
- }
670
-
671
- $post = array (
672
- 'post_type' => 'wpsl_stores',
673
- 'post_status' => $post_status,
674
- 'post_title' => $store->store,
675
- 'post_content' => $store->description
676
- );
677
-
678
- $post_id = wp_insert_post( $post );
679
-
680
- if ( $post_id ) {
681
-
682
- // Save the data from the wpsl_stores db table as post meta data.
683
- foreach ( $meta_keys as $meta_key ) {
684
- if ( isset( $store->{$meta_key} ) && !empty( $store->{$meta_key} ) ) {
685
- update_post_meta( $post_id, 'wpsl_' . $meta_key, $store->{$meta_key} );
686
- }
687
- }
688
-
689
- // If we have a thumb ID set the post thumbnail for the inserted post.
690
- if ( $store->thumb_id ) {
691
- set_post_thumbnail( $post_id, $store->thumb_id );
692
- }
693
- }
694
- }
695
  }
1
+ <?php
2
+ add_action( 'admin_init', 'wpsl_check_upgrade' );
3
+ add_action( 'admin_init', 'wpsl_cpt_update_state' );
4
+
5
+ /**
6
+ * If the db doesn't hold the current version, run the upgrade procedure
7
+ *
8
+ * @since 1.2
9
+ * @return void
10
+ */
11
+ function wpsl_check_upgrade() {
12
+
13
+ global $wpsl_settings;
14
+
15
+ $current_version = get_option( 'wpsl_version' );
16
+
17
+ if ( version_compare( $current_version, WPSL_VERSION_NUM, '===' ) )
18
+ return;
19
+
20
+ if ( version_compare( $current_version, '1.1', '<' ) ) {
21
+ if ( is_array( $wpsl_settings ) ) {
22
+ if ( empty( $wpsl_settings['reset_map'] ) ) {
23
+ $wpsl_settings['reset_map'] = 0;
24
+ }
25
+
26
+ if ( empty( $wpsl_settings['auto_load'] ) ) {
27
+ $wpsl_settings['auto_load'] = 1;
28
+ }
29
+
30
+ if ( empty( $wpsl_settings['new_window'] ) ) {
31
+ $wpsl_settings['new_window'] = 0;
32
+ }
33
+
34
+ update_option( 'wpsl_settings', $wpsl_settings );
35
+ }
36
+ }
37
+
38
+ if ( version_compare( $current_version, '1.2', '<' ) ) {
39
+ if ( is_array( $wpsl_settings ) ) {
40
+ if ( empty( $wpsl_settings['store_below'] ) ) {
41
+ $wpsl_settings['store_below'] = 0;
42
+ }
43
+
44
+ if ( empty( $wpsl_settings['direction_redirect'] ) ) {
45
+ $wpsl_settings['direction_redirect'] = 0;
46
+ }
47
+
48
+ update_option( 'wpsl_settings', $wpsl_settings );
49
+ }
50
+ }
51
+
52
+ if ( version_compare( $current_version, '1.2.11', '<' ) ) {
53
+ if ( is_array( $wpsl_settings ) ) {
54
+ if ( empty( $wpsl_settings['more_info'] ) ) {
55
+ $wpsl_settings['more_info'] = 0;
56
+ }
57
+
58
+ if ( empty( $wpsl_settings['more_label'] ) ) {
59
+ $wpsl_settings['more_label'] = __( 'More info', 'wpsl' );
60
+ }
61
+
62
+ if ( empty( $wpsl_settings['mouse_focus'] ) ) {
63
+ $wpsl_settings['mouse_focus'] = 0;
64
+ }
65
+
66
+ update_option( 'wpsl_settings', $wpsl_settings );
67
+ }
68
+ }
69
+
70
+ if ( version_compare( $current_version, '1.2.12', '<' ) ) {
71
+ if ( is_array( $wpsl_settings ) ) {
72
+ if ( empty( $wpsl_settings['more_info_location'] ) ) {
73
+ $wpsl_settings['more_info_location'] = __( 'info window', 'wpsl' );
74
+ }
75
+
76
+ if ( empty( $wpsl_settings['back_label'] ) ) {
77
+ $wpsl_settings['back_label'] = __( 'Back', 'wpsl' );
78
+ }
79
+
80
+ if ( empty( $wpsl_settings['reset_label'] ) ) {
81
+ $wpsl_settings['reset_label'] = __( 'Reset', 'wpsl' );
82
+ }
83
+
84
+ if ( empty( $wpsl_settings['store_below_scroll'] ) ) {
85
+ $wpsl_settings['store_below_scroll'] = 0;
86
+ }
87
+
88
+ update_option( 'wpsl_settings', $wpsl_settings );
89
+ }
90
+ }
91
+
92
+ if ( version_compare( $current_version, '1.2.20', '<' ) ) {
93
+
94
+ global $wpdb;
95
+
96
+ $wpsl_table = $wpdb->prefix . 'wpsl_stores';
97
+
98
+ // Rename the street field to address.
99
+ $wpdb->query( "ALTER TABLE $wpsl_table CHANGE street address VARCHAR(255)" );
100
+
101
+ // Add the second address field.
102
+ $wpdb->query( "ALTER TABLE $wpsl_table ADD address2 VARCHAR(255) NULL AFTER address" );
103
+
104
+ if ( is_array( $wpsl_settings ) ) {
105
+ if ( empty( $wpsl_settings['store_url'] ) ) {
106
+ $wpsl_settings['store_url'] = 0;
107
+ }
108
+
109
+ if ( empty( $wpsl_settings['phone_url'] ) ) {
110
+ $wpsl_settings['phone_url'] = 0;
111
+ }
112
+
113
+ if ( empty( $wpsl_settings['marker_clusters'] ) ) {
114
+ $wpsl_settings['marker_clusters'] = 0;
115
+ }
116
+
117
+ if ( empty( $wpsl_settings['cluster_zoom'] ) ) {
118
+ $wpsl_settings['cluster_zoom'] = 0;
119
+ }
120
+
121
+ if ( empty( $wpsl_settings['cluster_size'] ) ) {
122
+ $wpsl_settings['cluster_size'] = 0;
123
+ }
124
+
125
+ if ( empty( $wpsl_settings['template_id'] ) ) {
126
+ $wpsl_settings['template_id'] = ( $wpsl_settings['store_below'] ) ? 1 : 0;
127
+ unset( $wpsl_settings['store_below'] );
128
+ }
129
+
130
+ if ( empty( $wpsl_settings['marker_streetview'] ) ) {
131
+ $wpsl_settings['marker_streetview'] = 0;
132
+ }
133
+
134
+ if ( empty( $wpsl_settings['marker_zoom_to'] ) ) {
135
+ $wpsl_settings['marker_zoom_to'] = 0;
136
+ }
137
+
138
+ if ( !isset( $wpsl_settings['editor_country'] ) ) {
139
+ $wpsl_settings['editor_country'] = '';
140
+ }
141
+
142
+ if ( empty( $wpsl_settings['street_view_label'] ) ) {
143
+ $wpsl_settings['street_view_label'] = __( 'Street view', 'wpsl' );
144
+ }
145
+
146
+ if ( empty( $wpsl_settings['zoom_here_label'] ) ) {
147
+ $wpsl_settings['zoom_here_label'] = __( 'Zoom here', 'wpsl' );
148
+ }
149
+
150
+ if ( empty( $wpsl_settings['no_directions_label'] ) ) {
151
+ $wpsl_settings['no_directions_label'] = __( 'No route could be found between the origin and destination', 'wpsl' );
152
+ }
153
+
154
+ update_option( 'wpsl_settings', $wpsl_settings );
155
+ }
156
+ }
157
+
158
+ if ( version_compare( $current_version, '2.0', '<' ) ) {
159
+
160
+ global $wpdb;
161
+
162
+ $wpsl_table = $wpdb->prefix . 'wpsl_stores';
163
+
164
+ if ( is_array( $wpsl_settings ) ) {
165
+ if ( empty( $wpsl_settings['radius_dropdown'] ) ) {
166
+ $wpsl_settings['radius_dropdown'] = 1;
167
+ }
168
+
169
+ if ( empty( $wpsl_settings['permalinks'] ) ) {
170
+ $wpsl_settings['permalinks'] = 0;
171
+ }
172
+
173
+ if ( empty( $wpsl_settings['permalink_slug'] ) ) {
174
+ $wpsl_settings['permalink_slug'] = __( 'stores', 'wpsl' );
175
+ }
176
+
177
+ if ( empty( $wpsl_settings['category_slug'] ) ) {
178
+ $wpsl_settings['category_slug'] = __( 'store-category', 'wpsl' );
179
+ }
180
+
181
+ if ( empty( $wpsl_settings['editor_hours'] ) ) {
182
+ $wpsl_settings['editor_hours'] = wpsl_default_opening_hours();
183
+ }
184
+
185
+ if ( empty( $wpsl_settings['editor_hour_format'] ) ) {
186
+ $wpsl_settings['editor_hour_format'] = 12;
187
+ }
188
+
189
+ if ( empty( $wpsl_settings['editor_map_type'] ) ) {
190
+ $wpsl_settings['editor_map_type'] = 'roadmap';
191
+ }
192
+
193
+ if ( empty( $wpsl_settings['infowindow_style'] ) ) {
194
+ $wpsl_settings['infowindow_style'] = 'default';
195
+ }
196
+
197
+ if ( empty( $wpsl_settings['email_label'] ) ) {
198
+ $wpsl_settings['email_label'] = __( 'Email', 'wpsl' );
199
+ }
200
+
201
+ if ( empty( $wpsl_settings['url_label'] ) ) {
202
+ $wpsl_settings['url_label'] = __( 'Url', 'wpsl' );
203
+ }
204
+
205
+ if ( empty( $wpsl_settings['category_label'] ) ) {
206
+ $wpsl_settings['category_label'] = __( 'Category filter', 'wpsl' );
207
+ }
208
+
209
+ if ( empty( $wpsl_settings['show_credits'] ) ) {
210
+ $wpsl_settings['show_credits'] = 0;
211
+ }
212
+
213
+ if ( empty( $wpsl_settings['autoload_limit'] ) ) {
214
+ $wpsl_settings['autoload_limit'] = 50;
215
+ }
216
+
217
+ if ( empty( $wpsl_settings['scrollwheel'] ) ) {
218
+ $wpsl_settings['scrollwheel'] = 1;
219
+ }
220
+
221
+ if ( empty( $wpsl_settings['type_control'] ) ) {
222
+ $wpsl_settings['type_control'] = 0;
223
+ }
224
+
225
+ if ( empty( $wpsl_settings['hide_hours'] ) ) {
226
+ $wpsl_settings['hide_hours'] = 0;
227
+ }
228
+
229
+ // Either correct the existing map style format from the 2.0 beta or set it to empty.
230
+ if ( isset( $wpsl_settings['map_style'] ) && is_array( $wpsl_settings['map_style'] ) && isset( $wpsl_settings['map_style']['id'] ) ) {
231
+ switch( $wpsl_settings['map_style']['id'] ) {
232
+ case 'custom':
233
+ $map_style = $wpsl_settings['map_style']['custom_json'];
234
+ break;
235
+ case 'default':
236
+ $map_style = '';
237
+ break;
238
+ default:
239
+ $map_style = $wpsl_settings['map_style']['theme_json'];
240
+ break;
241
+ }
242
+
243
+ $wpsl_settings['map_style'] = $map_style;
244
+ } else {
245
+ $wpsl_settings['map_style'] = '';
246
+ }
247
+
248
+ if ( empty( $wpsl_settings['autoload'] ) ) {
249
+ $wpsl_settings['autoload'] = $wpsl_settings['auto_load'];
250
+ unset( $wpsl_settings['auto_load'] );
251
+ }
252
+
253
+ if ( empty( $wpsl_settings['address_format'] ) ) {
254
+ $wpsl_settings['address_format'] = 'city_state_zip';
255
+ }
256
+
257
+ if ( empty( $wpsl_settings['auto_zoom_level'] ) ) {
258
+ $wpsl_settings['auto_zoom_level'] = 15;
259
+ }
260
+
261
+ if ( empty( $wpsl_settings['hide_distance'] ) ) {
262
+ $wpsl_settings['hide_distance'] = 0;
263
+ }
264
+
265
+ if ( empty( $wpsl_settings['debug'] ) ) {
266
+ $wpsl_settings['debug'] = 0;
267
+ }
268
+
269
+ if ( empty( $wpsl_settings['category_dropdown'] ) ) {
270
+ $wpsl_settings['category_dropdown'] = 0;
271
+ }
272
+
273
+ /*
274
+ * Replace marker_bounce with marker_effect to better reflect what the option contains.
275
+ *
276
+ * If a user hovers over the result list then either the corresponding marker will bounce,
277
+ * the info window will open, or nothing will happen.
278
+ *
279
+ * The default behaviour is that the marker will bounce.
280
+ */
281
+ if ( empty( $wpsl_settings['marker_effect'] ) ) {
282
+ $wpsl_settings['marker_effect'] = ( $wpsl_settings['marker_bounce'] ) ? 'bounce' : 'ignore';
283
+ unset( $wpsl_settings['marker_bounce'] );
284
+ }
285
+
286
+ /*
287
+ * The default input for the opening hours is set to textarea for current users,
288
+ * 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 ).
289
+ */
290
+ if ( empty( $wpsl_settings['editor_hour_input'] ) ) {
291
+ $wpsl_settings['editor_hour_input'] = 'textarea';
292
+ }
293
+
294
+ // Rename store_below_scroll to listing_below_no_scroll, it better reflects what it does.
295
+ if ( empty( $wpsl_settings['listing_below_no_scroll'] ) && isset( $wpsl_settings['store_below_scroll'] ) ) {
296
+ $wpsl_settings['listing_below_no_scroll'] = $wpsl_settings['store_below_scroll'];
297
+ unset( $wpsl_settings['store_below_scroll'] );
298
+ }
299
+
300
+ // Change the template ids from number based to name based.
301
+ if ( is_numeric( $wpsl_settings['template_id'] ) ) {
302
+ $wpsl_settings['template_id'] = ( !$wpsl_settings['template_id'] ) ? 'default' : 'below_map';
303
+ }
304
+
305
+ $replace_data = array(
306
+ 'max_results' => $wpsl_settings['max_results'],
307
+ 'search_radius' => $wpsl_settings['search_radius']
308
+ );
309
+
310
+ /*
311
+ * Replace the () with [], this fixes an issue with the mod_security module that is installed on some servers.
312
+ * It triggerd a 'Possible SQL injection attack' warning probably because of the int,(int) format of the data.
313
+ */
314
+ foreach ( $replace_data as $index => $option_value ) {
315
+ $wpsl_settings[$index] = str_replace( array( '(', ')' ), array( '[', ']' ), $option_value );
316
+ }
317
+
318
+ // The reset button now uses an icon instead of text, so no need for the label anymore.
319
+ unset( $wpsl_settings['reset_label'] );
320
+
321
+ update_option( 'wpsl_settings', $wpsl_settings );
322
+
323
+ /*
324
+ * Users upgrading from 1.x will be given the choice between the textarea or
325
+ * dropdowns for the opening hours.
326
+ *
327
+ * New users don't get that choice, they will only get the dropdowns.
328
+ *
329
+ * The wpsl_legacy_support option is used to determine if we need to show both options.
330
+ */
331
+ update_option( 'wpsl_legacy_support', 1 );
332
+
333
+ // Add the WPSL roles and caps.
334
+ wpsl_add_roles();
335
+ wpsl_add_caps();
336
+
337
+ // If there is a wpsl_stores table, then we need to convert all the locations to the 'wpsl_stores' custom post type.
338
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '$wpsl_table'" ) && version_compare( $current_version, '1.9', '<' ) ) {
339
+ if ( wpsl_remaining_cpt_count() ) {
340
+ update_option( 'wpsl_convert_cpt', 'in_progress' );
341
+ }
342
+ }
343
+ }
344
+ }
345
+
346
+ /*
347
+ * Both map options are no longer supported in 3.22 of the Google Maps API.
348
+ * See: https://developers.google.com/maps/articles/v322-controls-diff
349
+ */
350
+ if ( version_compare( $current_version, '2.0.3', '<' ) ) {
351
+ unset( $wpsl_settings['control_style'] );
352
+ unset( $wpsl_settings['pan_controls'] );
353
+
354
+ update_option( 'wpsl_settings', $wpsl_settings );
355
+ }
356
+
357
+ if ( version_compare( $current_version, '2.1.0', '<' ) ) {
358
+ if ( !isset( $wpsl_settings['api_geocode_component'] ) ) {
359
+ $wpsl_settings['api_geocode_component'] = 0;
360
+ }
361
+
362
+ update_option( 'wpsl_settings', $wpsl_settings );
363
+ }
364
+
365
+ if ( version_compare( $current_version, '2.2', '<' ) ) {
366
+ $wpsl_settings['autocomplete'] = 0;
367
+ $wpsl_settings['category_default_label'] = __( 'Any', 'wpsl' );
368
+
369
+ // Rename the 'zoom_name' and 'zoom_latlng' to 'start_name' and 'start_latlng'.
370
+ if ( isset( $wpsl_settings['zoom_name'] ) ) {
371
+ $wpsl_settings['start_name'] = $wpsl_settings['zoom_name'];
372
+ unset( $wpsl_settings['zoom_name'] );
373
+ }
374
+
375
+ if ( isset( $wpsl_settings['zoom_latlng'] ) ) {
376
+ $wpsl_settings['start_latlng'] = $wpsl_settings['zoom_latlng'];
377
+ unset( $wpsl_settings['zoom_latlng'] );
378
+ }
379
+
380
+ if ( isset( $wpsl_settings['category_dropdown'] ) ) {
381
+ $wpsl_settings['category_filter'] = $wpsl_settings['category_dropdown'];
382
+ unset( $wpsl_settings['category_dropdown'] );
383
+ }
384
+
385
+ // We now have separate browser and server key fields, and assume the existing key is a server key.
386
+ if ( isset( $wpsl_settings['api_key'] ) ) {
387
+ $wpsl_settings['api_server_key'] = $wpsl_settings['api_key'];
388
+ unset( $wpsl_settings['api_key'] );
389
+ }
390
+
391
+ $wpsl_settings['api_browser_key'] = '';
392
+ $wpsl_settings['category_filter_type'] = 'dropdown';
393
+ $wpsl_settings['hide_country'] = 0;
394
+ $wpsl_settings['show_contact_details'] = 0;
395
+
396
+ update_option( 'wpsl_settings', $wpsl_settings );
397
+ }
398
+
399
+ if ( version_compare( $current_version, '2.2.4', '<' ) ) {
400
+ $wpsl_settings['deregister_gmaps'] = 0;
401
+
402
+ update_option( 'wpsl_settings', $wpsl_settings );
403
+ }
404
+
405
+ if ( version_compare( $current_version, '2.2.9', '<' ) ) {
406
+ $wpsl_settings['run_fitbounds'] = 1;
407
+
408
+ update_option( 'wpsl_settings', $wpsl_settings );
409
+ }
410
+
411
+ if ( version_compare( $current_version, '2.2.13', '<' ) ) {
412
+ $wpsl_settings['clickable_contact_details'] = 0;
413
+
414
+ update_option( 'wpsl_settings', $wpsl_settings );
415
+ }
416
+
417
+ update_option( 'wpsl_version', WPSL_VERSION_NUM );
418
+ }
419
+
420
+ /**
421
+ * Check if we need to show the notice that tells users that the store locations
422
+ * need to be converted to custom post types before the update from 1.x to 2.x is complete.
423
+ *
424
+ * @since 2.0
425
+ * @return void
426
+ */
427
+ function wpsl_cpt_update_state() {
428
+
429
+ global $wpsl_admin;
430
+
431
+ $conversion_state = get_option( 'wpsl_convert_cpt' );
432
+
433
+ if ( $conversion_state == 'in_progress' ) {
434
+ if ( ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ) ) {
435
+ $remaining = wpsl_remaining_cpt_count();
436
+ $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>" ) );
437
+
438
+ add_action( 'admin_footer', 'wpsl_cpt_dialog_html' );
439
+ }
440
+
441
+ add_action( 'admin_enqueue_scripts', 'wpsl_convert_cpt_js' );
442
+ add_action( 'wp_ajax_convert_cpt', 'wpsl_convert_cpt' );
443
+ add_action( 'wp_ajax_convert_cpt_count', 'wpsl_convert_cpt_count' );
444
+ }
445
+ }
446
+
447
+ /**
448
+ * Include the js file that handles the ajax request to
449
+ * start converting the 1.x store locations to custom post types.
450
+ *
451
+ * @since 2.0
452
+ * @return void
453
+ */
454
+ function wpsl_convert_cpt_js() {
455
+
456
+ $cpt_js_l10n = array(
457
+ '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>' ),
458
+ 'securityFail' => __( 'Security check failed, reload the page and try again.', 'wpsl' )
459
+ );
460
+
461
+ wp_enqueue_script( 'jquery-ui-dialog' );
462
+ wp_enqueue_script( 'wpsl-queue', plugins_url( '/js/ajax-queue.js', __FILE__ ), array( 'jquery' ), false );
463
+ wp_enqueue_script( 'wpsl-cpt-js', plugins_url( '/js/wpsl-cpt-upgrade.js', __FILE__ ), array( 'jquery' ), false );
464
+ wp_localize_script( 'wpsl-cpt-js', 'wpslCptConversion', $cpt_js_l10n );
465
+ }
466
+
467
+ /**
468
+ * The html for the lightbox
469
+ *
470
+ * @since 2.0
471
+ * @return void
472
+ */
473
+ function wpsl_cpt_dialog_html() {
474
+
475
+ ?>
476
+ <div id="wpsl-cpt-lightbox" style="display:none;">
477
+ <span class="tb-close-icon"></span>
478
+ <p class="wpsl-cpt-remaining"><?php _e( 'Store locations to convert:', 'wpsl' ); echo '<span></span>'; ?></p>
479
+ <div class="wslp-cpt-fix-wrap">
480
+ <input id="wpsl-start-cpt-conversion" class="button-primary" type="submit" value="<?php _e( 'Start Converting', 'wpsl' ); ?>" >
481
+ <img class="wpsl-preloader" alt="preloader" src="<?php echo WPSL_URL . 'img/ajax-loader.gif'; ?>" />
482
+ </div>
483
+ <input type="hidden" name="wpsl-cpt-fix-nonce" value="<?php echo wp_create_nonce( 'wpsl-cpt-fix' ); ?>" />
484
+ <input type="hidden" name="wpsl-cpt-conversion-count" value="<?php echo wp_create_nonce( 'wpsl-cpt-count' ); ?>" />
485
+ </div>
486
+ <div id="wpsl-cpt-overlay" style="display:none;"></div>
487
+ <style>
488
+ .wslp-cpt-fix-wrap {
489
+ float:left;
490
+ clear:both;
491
+ width:100%;
492
+ margin:0 0 15px 0;
493
+ }
494
+
495
+ #wpsl-cpt-lightbox .wpsl-cpt-remaining span {
496
+ margin-left:5px;
497
+ }
498
+
499
+ #wpsl-start-cpt-conversion {
500
+ float:left;
501
+ }
502
+
503
+ .wslp-cpt-fix-wrap .wpsl-preloader,
504
+ .wslp-cpt-fix-wrap span {
505
+ float:left;
506
+ margin:8px 0 0 10px;
507
+ }
508
+
509
+ .wslp-cpt-fix-wrap .wpsl-preloader {
510
+ display: none;
511
+ }
512
+
513
+ #wpsl-cpt-lightbox {
514
+ position:fixed;
515
+ width:450px;
516
+ left:50%;
517
+ right:50%;
518
+ top:3.8em;
519
+ padding:15px;
520
+ background:none repeat scroll 0 0 #fff;
521
+ border-radius:3px;
522
+ margin-left:-225px;
523
+ z-index: 9999;
524
+ }
525
+
526
+ #wpsl-cpt-overlay {
527
+ position:fixed;
528
+ right:0;
529
+ top:0;
530
+ z-index:9998;
531
+ background:none repeat scroll 0 0 #000;
532
+ bottom:0;
533
+ left:0;
534
+ opacity:0.5;
535
+ }
536
+
537
+ .tb-close-icon {
538
+ color: #666;
539
+ text-align: center;
540
+ line-height: 29px;
541
+ width: 29px;
542
+ height: 29px;
543
+ position: absolute;
544
+ top: 0;
545
+ right: 0;
546
+ }
547
+
548
+ .tb-close-icon:before {
549
+ content: '\f158';
550
+ font: normal 20px/29px 'dashicons';
551
+ speak: none;
552
+ -webkit-font-smoothing: antialiased;
553
+ -moz-osx-font-smoothing: grayscale;
554
+ }
555
+
556
+ .tb-close-icon:hover {
557
+ color: #999 !important;
558
+ cursor: pointer;
559
+ }
560
+ </style>
561
+ <?php
562
+ }
563
+
564
+ /**
565
+ * Handle the ajax call to start converting the
566
+ * store locations to custom post types.
567
+ *
568
+ * @since 2.0
569
+ * @return void|string json on completion
570
+ */
571
+ function wpsl_convert_cpt() {
572
+
573
+ if ( !current_user_can( 'manage_options' ) )
574
+ die( '-1' );
575
+ check_ajax_referer( 'wpsl-cpt-fix' );
576
+
577
+ // Start the cpt coversion.
578
+ wpsl_cpt_conversion();
579
+
580
+ exit();
581
+ }
582
+
583
+ /**
584
+ * Get the amount of locations that still need to be converted.
585
+ *
586
+ * @since 2.0
587
+ * @return string json The amount of locations that still need to be converted
588
+ */
589
+ function wpsl_convert_cpt_count() {
590
+
591
+ if ( !current_user_can( 'manage_options' ) )
592
+ die( '-1' );
593
+ check_ajax_referer( 'wpsl-cpt-count' );
594
+
595
+ $remaining_count = wpsl_remaining_cpt_count();
596
+
597
+ $response['success'] = true;
598
+
599
+ if ( $remaining_count ) {
600
+ $response['count'] = $remaining_count;
601
+ } else {
602
+ $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>' );
603
+
604
+ delete_option( 'wpsl_convert_cpt' );
605
+ }
606
+
607
+ wp_send_json( $response );
608
+
609
+ exit();
610
+ }
611
+
612
+ /**
613
+ * Return the difference between the number of existing wpsl custom post types,
614
+ * and the number of records in the old wpsl_stores database.
615
+ *
616
+ * @since 2.0
617
+ * @return int|boolean $remaining The amount of locations that still need to be converted
618
+ */
619
+ function wpsl_remaining_cpt_count() {
620
+
621
+ global $wpdb;
622
+
623
+ $table = $wpdb->prefix . 'wpsl_stores';
624
+ $count = wp_count_posts( 'wpsl_stores' );
625
+
626
+ if ( isset( $count->publish ) && isset( $count->draft ) ) {
627
+ $cpt_count = $count->publish + $count->draft;
628
+ } else {
629
+ $cpt_count = 0;
630
+ }
631
+
632
+ $db_count = $wpdb->get_var( "SELECT COUNT(wpsl_id) FROM $table" );
633
+ $difference = $db_count - $cpt_count;
634
+
635
+ /*
636
+ * This prevents users who used the 2.0 beta, and later added
637
+ * more stores from seeing the upgrade notice again.
638
+ */
639
+ $remaining = ( $difference < 0 ) ? false : $difference;
640
+
641
+ return $remaining;
642
+ }
643
+
644
+ /**
645
+ * Convert the existing locations to custom post types.
646
+ *
647
+ * @since 2.0
648
+ * @return void|boolean True if the conversion is completed
649
+ */
650
+ function wpsl_cpt_conversion() {
651
+
652
+ global $wpdb;
653
+
654
+ // Try to disable the time limit to prevent timeouts.
655
+ @set_time_limit( 0 );
656
+
657
+ $meta_keys = array( 'address', 'address2', 'city', 'state', 'zip', 'country', 'country_iso', 'lat', 'lng', 'phone', 'fax', 'url', 'email', 'hours' );
658
+ $offset = wpsl_remaining_cpt_count();
659
+ $wpsl_table = $wpdb->prefix . 'wpsl_stores';
660
+ $stores = $wpdb->get_results( "(SELECT * FROM $wpsl_table ORDER BY wpsl_id DESC LIMIT $offset) ORDER BY wpsl_id ASC" );
661
+
662
+ foreach ( $stores as $store ) {
663
+
664
+ // Make sure we set the correct post status.
665
+ if ( $store->active ) {
666
+ $post_status = 'publish';
667
+ } else {
668
+ $post_status = 'draft';
669
+ }
670
+
671
+ $post = array (
672
+ 'post_type' => 'wpsl_stores',
673
+ 'post_status' => $post_status,
674
+ 'post_title' => $store->store,
675
+ 'post_content' => $store->description
676
+ );
677
+
678
+ $post_id = wp_insert_post( $post );
679
+
680
+ if ( $post_id ) {
681
+
682
+ // Save the data from the wpsl_stores db table as post meta data.
683
+ foreach ( $meta_keys as $meta_key ) {
684
+ if ( isset( $store->{$meta_key} ) && !empty( $store->{$meta_key} ) ) {
685
+ update_post_meta( $post_id, 'wpsl_' . $meta_key, $store->{$meta_key} );
686
+ }
687
+ }
688
+
689
+ // If we have a thumb ID set the post thumbnail for the inserted post.
690
+ if ( $store->thumb_id ) {
691
+ set_post_thumbnail( $post_id, $store->thumb_id );
692
+ }
693
+ }
694
+ }
695
  }
css/styles.css CHANGED
@@ -1,1058 +1,1079 @@
1
- @font-face {
2
- font-family: 'wpsl-fontello';
3
- src: url('../font/fontello.eot?28897909');
4
- src: url('../font/fontello.eot?28897909#iefix') format('embedded-opentype'),
5
- url('../font/fontello.woff?28897909') format('woff'),
6
- url('../font/fontello.ttf?28897909') format('truetype'),
7
- url('../font/fontello.svg?28897909#fontello') format('svg');
8
- font-weight: normal;
9
- font-style: normal;
10
- }
11
-
12
- #wpsl-gmap {
13
- float:right;
14
- width:66.5%;
15
- height:350px;
16
- margin-bottom:0;
17
- }
18
-
19
- .wpsl-store-below #wpsl-gmap {
20
- float:none;
21
- width:100%;
22
- }
23
-
24
- .wpsl-gmap-canvas {
25
- width:100%;
26
- height:300px;
27
- margin-bottom:20px;
28
- }
29
-
30
- #wpsl-reset-map:hover {
31
- cursor: pointer;
32
- }
33
-
34
- /*
35
- Some themes set a box-shadow or max-width for all image /
36
- div elements, we disable it to prevent it from messing up the map
37
-
38
- The .gv-iv- class is used in streetview, and they should not be included.
39
- */
40
- #wpsl-gmap div:not[class^="gv-iv"],
41
- #wpsl-gmap img,
42
- .wpsl-gmap-canvas div:not[class^="gv-iv"],
43
- .wpsl-gmap-canvas img {
44
- box-shadow: none !important;
45
- max-width: none !important;
46
- background: none;
47
- }
48
-
49
- #wpsl-gmap img,
50
- .wpsl-gmap-canvas img {
51
- display: inline !important;
52
- opacity: 1 !important;
53
- max-height: none !important;
54
- }
55
-
56
- /*
57
- Fix a problem where the background color used
58
- in street view mode doesn't cover the control area.
59
- */
60
- #wpsl-gmap * {
61
- box-sizing: content-box !important;
62
- -webkit-box-sizing: content-box !important;
63
- -moz-box-sizing: content-box !important;
64
- }
65
-
66
- #wpsl-gmap div.gm-iv-marker,
67
- .wpsl-gmap-canvas div.gm-iv-marker {
68
- backgroud-image: inherit;
69
- }
70
-
71
- #wpsl-wrap {
72
- position: relative;
73
- width: 100%;
74
- overflow: hidden;
75
- clear: both;
76
- margin-bottom: 20px;
77
- }
78
-
79
- #wpsl-search-wrap {
80
- float: left;
81
- width: 100%;
82
- }
83
-
84
- #wpsl-search-wrap form {
85
- margin: 0;
86
- padding: 0;
87
- border: none;
88
- outline: none;
89
- }
90
-
91
- /* Map Controls */
92
- #wpsl-gmap #wpsl-map-controls {
93
- position: absolute;
94
- height: 28px;
95
- right: 10px;
96
- bottom: 24px;
97
- border-radius: 2px;
98
- z-index: 3;
99
- font-size: 11px;
100
- white-space: nowrap;
101
- overflow: hidden;
102
- }
103
-
104
- #wpsl-gmap #wpsl-map-controls.wpsl-street-view-exists {
105
- right: 48px;
106
- }
107
-
108
- #wpsl-map-controls .wpsl-direction-preloader {
109
- margin: 5px 5px 0 5px;
110
- }
111
-
112
- #wpsl-map-controls div {
113
- float: left;
114
- background: #fff;
115
- border-radius: 2px;
116
- }
117
-
118
- #wpsl-map-controls div:hover {
119
- cursor: pointer;
120
- }
121
-
122
- #wpsl-wrap [class^="wpsl-icon-"],
123
- #wpsl-wrap [class*=" wpsl-icon-"] {
124
- position: relative;
125
- float: left;
126
- padding: 7px 9px 7px 8px;
127
- display: inline-block;
128
- font-family: "wpsl-fontello";
129
- font-style: normal;
130
- font-weight: normal;
131
- font-size: 1.3em;
132
- color: #737373;
133
- speak: none;
134
- text-decoration: inherit;
135
- text-align: center;
136
- font-variant: normal;
137
- text-transform: none;
138
- line-height: 1em;
139
- -webkit-font-smoothing: antialiased;
140
- -moz-osx-font-smoothing: grayscale;
141
- }
142
-
143
- /*
144
- * Make sure the CSS from a theme doesn't set a different font family, or font size
145
- * for the font icons. Otherwise the icons either don't show, or are to large.
146
- */
147
- #wpsl-map-controls span {
148
- font-family: inherit;
149
- font-size: inherit;
150
- }
151
-
152
- /* Fix the padding for the icon fonts in IE 8-11 :( */
153
- #wpsl-wrap .wpsl-ie [class^="wpsl-icon-"],
154
- #wpsl-wrap .wpsl-ie [class*=" wpsl-icon-"] {
155
- padding: 9px 8px 4px 8px;
156
- }
157
-
158
- /* Make the clickable area bigger for the buttons on mobile devices */
159
- #wpsl-wrap.wpsl-mobile [class^="wpsl-icon-"],
160
- #wpsl-wrap.wpsl-mobile [class*=" wpsl-icon-"] {
161
- padding: 8px 10px;
162
- }
163
-
164
- #wpsl-wrap .wpsl-icon-reset {
165
- border-radius: 2px 0 0 2px;
166
- z-index: 2;
167
- padding-left: 9px;
168
- padding-right: 4px;
169
- }
170
-
171
- #wpsl-wrap .wpsl-icon-direction {
172
- z-index: 1;
173
- }
174
-
175
- #wpsl-map-controls.wpsl-reset-exists .wpsl-icon-direction {
176
- border-radius: 0 2px 2px 0;
177
- }
178
-
179
- #wpsl-wrap .wpsl-active-icon,
180
- #wpsl-wrap [class^="wpsl-icon-"]:hover,
181
- #wpsl-wrap [class*=" wpsl-icon-"]:hover {
182
- color: #000;
183
- }
184
-
185
- #wpsl-wrap [class^="wpsl-icon-"]:active,
186
- #wpsl-wrap [class*=" wpsl-icon-"]:focus {
187
- outline: 0;
188
- }
189
-
190
- #wpsl-wrap .wpsl-in-progress:hover,
191
- #wpsl-wrap .wpsl-in-progress {
192
- color: #c6c6c6;
193
- }
194
-
195
- /* Map reset button */
196
- #wpsl-gmap #wpsl-reset-map {
197
- position: absolute;
198
- display: none;
199
- right: 37px;
200
- top: 37px;
201
- padding: 6px 14px;
202
- background: #fff !important;
203
- background-clip: padding-box;
204
- border: 1px solid rgba(0, 0, 0, 0.15);
205
- border-radius: 2px;
206
- z-index: 3;
207
- }
208
-
209
- #wpsl-reset-map:hover {
210
- cursor: pointer;
211
- }
212
-
213
- /* Possible fix for vertical text issue in IE9? */
214
- .gm-style-cc {
215
- word-wrap:normal;
216
- }
217
-
218
- #wpsl-search-wrap .wpsl-input,
219
- #wpsl-search-wrap .wpsl-select-wrap {
220
- display:table;
221
- }
222
-
223
- #wpsl-search-wrap .wpsl-input label,
224
- #wpsl-search-wrap .wpsl-input input,
225
- #wpsl-search-wrap #wpsl-radius,
226
- #wpsl-search-wrap #wpsl-results,
227
- #wpsl-search-btn {
228
- display:table-cell;
229
- }
230
-
231
- #wpsl-search-wrap label {
232
- margin-bottom:0;
233
- }
234
-
235
- #wpsl-search-input {
236
- width: 179px;
237
- height: auto;
238
- padding: 7px 12px;
239
- font-size: 100%;
240
- margin: 0;
241
- }
242
-
243
- #wpsl-search-wrap input,
244
- #wpsl-search-btn {
245
- border: 1px solid #d2d2d2;
246
- border-radius: 3px;
247
- }
248
-
249
- #wpsl-search-btn {
250
- padding: 7px 10px;
251
- line-height: 1.428571429;
252
- font-weight: normal;
253
- color: #7c7c7c;
254
- background-color: #e6e6e6;
255
- background-repeat: repeat-x;
256
- background-image: -moz-linear-gradient(top, #f4f4f4, #e6e6e6);
257
- background-image: -ms-linear-gradient(top, #f4f4f4, #e6e6e6);
258
- background-image: -webkit-linear-gradient(top, #f4f4f4, #e6e6e6);
259
- background-image: -o-linear-gradient(top, #f4f4f4, #e6e6e6);
260
- background-image: linear-gradient(top, #f4f4f4, #e6e6e6);
261
- box-shadow: 0 1px 2px rgba(64, 64, 64, 0.1);
262
- text-transform: none !important;
263
- }
264
-
265
- #wpsl-search-input.wpsl-error {
266
- border:1px solid #bd0028 !important;
267
- }
268
-
269
- .wpsl-search {
270
- margin-bottom:12px;
271
- padding:12px 12px 0 12px;
272
- background:#f4f3f3;
273
- }
274
-
275
- .wpsl-search.wpsl-checkboxes-enabled {
276
- padding: 12px;
277
- }
278
-
279
- /* Result list */
280
- .wpsl-back {
281
- display: inline-block;
282
- }
283
-
284
- #wpsl-result-list {
285
- width:33%;
286
- margin-right:0.5%;
287
- }
288
-
289
- .wpsl-store-below #wpsl-result-list {
290
- width:100%;
291
- margin:12px 0 0 0;
292
- }
293
-
294
- #wpsl-stores,
295
- #wpsl-direction-details {
296
- height:350px;
297
- overflow-y:auto;
298
- }
299
-
300
- .wpsl-hide,
301
- #wpsl-direction-details {
302
- display:none;
303
- }
304
-
305
- #wpsl-result-list p {
306
- padding-left:10px;
307
- }
308
- .wpsl-store-below #wpsl-result-list p {
309
- padding-left: 0;
310
- }
311
-
312
- #wpsl-result-list a {
313
- outline:none;
314
- }
315
-
316
- .wpsl-direction-before {
317
- margin: 14px 0 21px 0;
318
- padding-left: 10px;
319
- }
320
-
321
- .wpsl-store-below .wpsl-direction-before {
322
- padding-left: 0;
323
- }
324
-
325
- .wpsl-direction-before div {
326
- margin-top: 10px;
327
- }
328
-
329
- #wpsl-wrap #wpsl-result-list li {
330
- padding: 10px;
331
- border-bottom: 1px dotted #ccc;
332
- margin-left: 0;
333
- overflow: hidden;
334
- list-style: none outside none !important;
335
- text-indent: 0;
336
- }
337
-
338
- #wpsl-wrap #wpsl-result-list li li {
339
- padding: 0;
340
- border-bottom: 0;
341
- margin-left: 14px;
342
- overflow: visible;
343
- }
344
-
345
- #wpsl-wrap #wpsl-result-list ul li {
346
- list-style: none !important;
347
- }
348
-
349
- #wpsl-wrap #wpsl-result-list ol li {
350
- list-style: decimal !important;
351
- }
352
-
353
- #wpsl-wrap.wpsl-store-below #wpsl-result-list li {
354
- padding: 10px 10px 10px 0;
355
- }
356
-
357
- #wpsl-result-list li p {
358
- padding-left: 0;
359
- margin: 0 0 20px 0;
360
- }
361
-
362
- .wpsl-store-details.wpsl-store-listing {
363
- position: relative;
364
- padding-right: 20px;
365
- }
366
-
367
- .wpsl-store-details.wpsl-store-listing:before,
368
- .wpsl-store-details.wpsl-store-listing.wpsl-active-details:before {
369
- position: absolute;
370
- content: '';
371
- bottom:6px;
372
- right:0;
373
- border-top: 5px solid #000000;
374
- border-left: 6px solid rgba(0, 0, 0, 0);
375
- border-right: 6px solid rgba(0, 0, 0, 0);
376
- }
377
-
378
- .wpsl-store-details.wpsl-store-listing.wpsl-active-details:before {
379
- border-bottom: 5px solid #000000;
380
- border-top:none;
381
- border-left: 6px solid rgba(0, 0, 0, 0);
382
- border-right: 6px solid rgba(0, 0, 0, 0);
383
- }
384
-
385
- #wpsl-stores .wpsl-store-thumb {
386
- float:right;
387
- border-radius:3px;
388
- margin:7px 0 0 10px;
389
- padding:0;
390
- border:none;
391
- }
392
-
393
- .wpsl-direction-index {
394
- float:left;
395
- width:8%;
396
- margin:0 5% 0 0;
397
- }
398
-
399
- .wpsl-direction-txt {
400
- float:left;
401
- width:62%;
402
- }
403
-
404
- .wpsl-direction-distance {
405
- float:left;
406
- width:20%;
407
- margin:0 0 0 5%;
408
- }
409
-
410
- .wpsl-direction-txt span {
411
- display:block;
412
- margin-top:10px;
413
- }
414
-
415
- .wpsl-street,
416
- .wpsl-country {
417
- display: block;
418
- border-bottom: none !important;
419
- }
420
-
421
- .wpsl-directions {
422
- display: table;
423
- border-bottom: none !important;
424
- }
425
-
426
- /* Preloader */
427
- #wpsl-wrap #wpsl-result-list li.wpsl-preloader {
428
- position: relative;
429
- border-bottom: none;
430
- padding: 10px 10px 10px 35px;
431
- }
432
-
433
- .wpsl-preloader img {
434
- position: absolute;
435
- left: 10px;
436
- top: 50%;
437
- margin-top: -8px;
438
- box-shadow:none !important;
439
- border:none !important;
440
- }
441
-
442
- .wpsl-preloader span {
443
- float: left;
444
- margin: -5px 0 0 11px;
445
- }
446
-
447
- #wpsl-search-wrap div,
448
- #wpsl-search-btn {
449
- margin-right: 10px;
450
- float: left;
451
- }
452
-
453
- #wpsl-search-wrap .wpsl-select-wrap {
454
- position: relative;
455
- z-index: 2;
456
- margin-right: 0;
457
- }
458
-
459
- #wpsl-search-wrap .wpsl-input-field {
460
- position: relative;
461
- }
462
-
463
- #wpsl-radius, #wpsl-results {
464
- float: left;
465
- margin-right: 15px;
466
- }
467
-
468
- #wpsl-category {
469
- position: relative;
470
- z-index: 1;
471
- clear: both;
472
- }
473
-
474
- #wpsl-search-wrap .wpsl-dropdown div {
475
- position: absolute;
476
- float: none;
477
- margin: -1px 0 0 0;
478
- top: 100%;
479
- left: -1px;
480
- right: -1px;
481
- border: 1px solid #ccc;
482
- background: #fff;
483
- border-top: 1px solid #eee;
484
- border-radius: 0 0 3px 3px;
485
- opacity: 0;
486
- overflow: hidden;
487
- -webkit-transition: all 150ms ease-in-out;
488
- -moz-transition: all 150ms ease-in-out;
489
- -ms-transition: all 150ms ease-in-out;
490
- transition: all 150ms ease-in-out;
491
- }
492
-
493
- #wpsl-search-wrap .wpsl-dropdown.wpsl-active div {
494
- opacity: 1;
495
- }
496
-
497
- #wpsl-search-wrap .wpsl-input label {
498
- margin-right:0;
499
- }
500
-
501
- #wpsl-radius, #wpsl-results {
502
- display:inline;
503
- }
504
-
505
- #wpsl-radius {
506
- margin-right:10px;
507
- }
508
- #wpsl-search-btn:hover {
509
- cursor: pointer;
510
- }
511
-
512
- #wpsl-search-wrap select,
513
- #wpsl-search select {
514
- display:none;
515
- }
516
-
517
- #wpsl-search-wrap div label {
518
- float:left;
519
- margin-right:10px;
520
- line-height: 32px;
521
- }
522
-
523
- #wpsl-results label {
524
- width: auto;
525
- }
526
-
527
- #wpsl-result-list ul {
528
- list-style: none;
529
- margin: 0;
530
- padding: 0;
531
- }
532
- .wpsl-direction-details {
533
- display: none;
534
- }
535
-
536
- /* Infowindow */
537
- #wpsl-gmap .wpsl-info-window,
538
- .wpsl-gmap-canvas .wpsl-info-window {
539
- max-width:225px;
540
- }
541
-
542
- .wpsl-more-info-listings span,
543
- .wpsl-info-window span {
544
- display:block;
545
- }
546
-
547
- .wpsl-info-window .wpsl-no-margin {
548
- margin:0;
549
- }
550
-
551
- /* More info details in the store listings */
552
- .wpsl-more-info-listings {
553
- display:none;
554
- }
555
-
556
- /* Fix for Google Voice breaking the phone numbers */
557
- .wpsl-info-window span span {
558
- display:inline !important;
559
- }
560
-
561
- #wpsl-wrap .wpsl-info-window p {
562
- margin: 0 0 10px 0;
563
- }
564
-
565
- .wpsl-store-hours {
566
- margin-top:10px;
567
- }
568
-
569
- .wpsl-store-hours strong {
570
- display:block;
571
- }
572
-
573
- #wpsl-gmap .wpsl-info-actions {
574
- display:block;
575
- margin:10px 0 !important;
576
- }
577
-
578
- .wpsl-info-actions a {
579
- float:left;
580
- margin-right: 7px;
581
- }
582
-
583
- .wpsl-info-actions .wpsl-zoom-here {
584
- margin-right:0;
585
- }
586
-
587
- /* --- dropdowns --- */
588
- .wpsl-dropdown {
589
- position: relative;
590
- width: 90px;
591
- border: 1px solid #ccc;
592
- cursor: pointer;
593
- background: #fff;
594
- border-radius: 3px;
595
- -webkit-user-select: none;
596
- -moz-user-select: none;
597
- user-select: none;
598
- margin-right: 0 !important;
599
- z-index: 2;
600
- }
601
-
602
- #wpsl-results .wpsl-dropdown {
603
- width: 70px;
604
- }
605
-
606
- .wpsl-dropdown ul {
607
- position: absolute;
608
- left: 0;
609
- width: 100%;
610
- height: 100%;
611
- padding: 0 !important;
612
- margin: 0 !important;
613
- list-style: none;
614
- overflow: hidden;
615
- }
616
-
617
- .wpsl-dropdown:hover {
618
- box-shadow: 0 0 5px rgba( 0, 0, 0, 0.15 );
619
- }
620
-
621
- .wpsl-dropdown .wpsl-selected-item,
622
- .wpsl-dropdown li {
623
- position: relative;
624
- display: block;
625
- line-height: normal;
626
- color: #000;
627
- overflow: hidden;
628
- }
629
-
630
- #wpsl-radius .wpsl-dropdown .wpsl-selected-item,
631
- #wpsl-radius .wpsl-dropdown li,
632
- #wpsl-results .wpsl-dropdown .wpsl-selected-item,
633
- #wpsl-results .wpsl-dropdown li {
634
- white-space: nowrap;
635
- }
636
-
637
- .wpsl-selected-item:after {
638
- position: absolute;
639
- content: "";
640
- right: 12px;
641
- top: 50%;
642
- margin-top: -4px;
643
- border: 6px solid transparent;
644
- border-top: 8px solid #000;
645
- }
646
-
647
- .wpsl-active .wpsl-selected-item:after {
648
- margin-top: -10px;
649
- border: 6px solid transparent;
650
- border-bottom: 8px solid #000;
651
- }
652
-
653
- .wpsl-dropdown li:hover {
654
- background: #f8f9f8;
655
- position: relative;
656
- z-index: 3;
657
- color: #000;
658
- }
659
-
660
- .wpsl-dropdown .wpsl-selected-item,
661
- .wpsl-dropdown li,
662
- .wpsl-selected-item {
663
- list-style: none;
664
- padding: 9px 12px !important;
665
- margin:0 !important;
666
- }
667
-
668
- .wpsl-selected-dropdown {
669
- font-weight: bold;
670
- }
671
-
672
- .wpsl-clearfix:before,
673
- .wpsl-clearfix:after {
674
- content: " ";
675
- display: table;
676
- }
677
-
678
- .wpsl-clearfix:after {
679
- clear: both;
680
- }
681
-
682
- #wpsl-wrap .wpsl-selected-item {
683
- position: static;
684
- padding-right: 35px !important;
685
- }
686
-
687
- #wpsl-category,
688
- .wpsl-input,
689
- .wpsl-select-wrap {
690
- position: relative;
691
- margin-bottom: 10px;
692
- }
693
-
694
- #wpsl-search-wrap .wpsl-scroll-required div {
695
- overflow-y: scroll;
696
- }
697
-
698
- .wpsl-scroll-required ul {
699
- overflow: visible;
700
- }
701
-
702
- .wpsl-provided-by {
703
- float: right;
704
- padding: 5px 0;
705
- text-align: right;
706
- font-size: 12px;
707
- width: 100%;
708
- }
709
-
710
- #wpsl-wrap .wpsl-results-only label {
711
- width: auto;
712
- }
713
-
714
- /* wpsl custom post type pages */
715
- .wpsl-locations-details,
716
- .wpsl-location-address,
717
- .wpsl-contact-details {
718
- margin-bottom: 15px;
719
- }
720
-
721
- .wpsl-contact-details {
722
- clear: both;
723
- }
724
-
725
- table.wpsl-opening-hours td {
726
- vertical-align: top;
727
- padding: 0 15px 0 0;
728
- text-align: left;
729
- }
730
-
731
- table.wpsl-opening-hours time {
732
- display:block;
733
- }
734
-
735
- table.wpsl-opening-hours {
736
- width:auto !important;
737
- font-size:100% !important;
738
- }
739
-
740
- table.wpsl-opening-hours,
741
- table.wpsl-opening-hours td {
742
- border:none !important;
743
- }
744
-
745
- /* Custom Infobox */
746
- .wpsl-gmap-canvas .wpsl-infobox {
747
- min-width:155px;
748
- max-width:350px !important;
749
- padding:10px;
750
- border-radius:4px;
751
- font-size:13px;
752
- font-weight:300;
753
- border:1px solid #ccc;
754
- background:#fff !important;
755
- }
756
-
757
- .wpsl-gmap-canvas .wpsl-infobox:after,
758
- .wpsl-gmap-canvas .wpsl-infobox:before {
759
- position:absolute;
760
- content:"";
761
- left:40px;
762
- bottom:-11px;
763
- }
764
-
765
- .wpsl-gmap-canvas .wpsl-infobox:after {
766
- border-left:11px solid transparent;
767
- border-right:11px solid transparent;
768
- border-top:11px solid #fff;
769
- }
770
-
771
- .wpsl-gmap-canvas .wpsl-infobox:before {
772
- border-left:13px solid transparent;
773
- border-right:13px solid transparent;
774
- border-top:13px solid #ccc;
775
- bottom:-13px;
776
- left:38px;
777
- }
778
-
779
- #wpsl-checkbox-filter,
780
- .wpsl-custom-checkboxes {
781
- display: block;
782
- float: left;
783
- margin: 5px 0 15px;
784
- padding: 0;
785
- width: 100%;
786
- }
787
-
788
- #wpsl-checkbox-filter li,
789
- .wpsl-custom-checkboxes li {
790
- float: left;
791
- list-style: none;
792
- margin: 0 1% 0 0;
793
- }
794
-
795
- #wpsl-checkbox-filter.wpsl-checkbox-1-columns li,
796
- .wpsl-custom-checkboxes.wpsl-checkbox-1-columns li {
797
- width: 99%;
798
- }
799
-
800
- #wpsl-checkbox-filter.wpsl-checkbox-2-columns li,
801
- .wpsl-custom-checkboxes.wpsl-checkbox-2-columns li {
802
- width: 49%;
803
- }
804
-
805
- #wpsl-checkbox-filter.wpsl-checkbox-3-columns li,
806
- .wpsl-custom-checkboxes.wpsl-checkbox-3-columns li {
807
- width: 32%;
808
- }
809
-
810
- #wpsl-checkbox-filter.wpsl-checkbox-4-columns li,
811
- .wpsl-custom-checkboxes.wpsl-checkbox-4-columns li {
812
- width: 24%;
813
- }
814
-
815
- #wpsl-checkbox-filter input,
816
- .wpsl-custom-checkboxes input {
817
- margin-right: 5px;
818
- }
819
-
820
- #wpsl-result-list .wpsl-contact-details span {
821
- display: block !important;
822
- }
823
-
824
- /*
825
- Hide the select2 ( https://select2.org/ ) dropdowns to
826
- prevent duplicate dropdowns from showing up in the search bar.
827
- */
828
- #wpsl-search-wrap .select2 {
829
- display: none !important;
830
- }
831
-
832
- /*
833
- Make a few adjustments for themes that use RTL languages
834
- */
835
- .rtl #wpsl-result-list {
836
- float: left;
837
- }
838
-
839
- .rtl #wpsl-checkbox-filter input,
840
- .rtl .wpsl-custom-checkboxes input {
841
- margin-right: 0;
842
- margin-left: 5px;
843
- }
844
-
845
- .rtl .wpsl-info-actions a {
846
- float: right;
847
- margin: 0 0 0 7px;
848
- }
849
-
850
- .rtl #wpsl-gmap .wpsl-info-window {
851
- padding-right: 22px;
852
- }
853
-
854
- .rtl #wpsl-wrap #wpsl-result-list li.wpsl-preloader {
855
- padding: 10px 35px 10px 0;
856
- }
857
-
858
- .rtl .wpsl-preloader img {
859
- left: 0;
860
- right: 10px;
861
- }
862
-
863
- @media (max-width: 825px) {
864
- #wpsl-search-input {
865
- width: 348px;
866
- }
867
-
868
- .wpsl-results-only #wpsl-search-wrap .wpsl-dropdown {
869
- width: 70px;
870
- }
871
-
872
- #wpsl-search-wrap .wpsl-input {
873
- width: 100%;
874
- margin-bottom: 10px;
875
- }
876
-
877
- .wpsl-input label,
878
- #wpsl-radius label,
879
- #wpsl-category label,
880
- .wpsl-cat-results-filter #wpsl-search-wrap .wpsl-input,
881
- .wpsl-no-filters #wpsl-search-wrap .wpsl-input,
882
- .wpsl-results-only #wpsl-search-wrap .wpsl-input {
883
- width: auto;
884
- }
885
- }
886
-
887
- @media (max-width: 720px) {
888
- #wpsl-search-wrap .wpsl-dropdown {
889
- width: 114px;
890
- }
891
- }
892
-
893
- @media (max-width: 675px) {
894
- #wpsl-search-wrap #wpsl-search-btn {
895
- float: left;
896
- margin: 0 5px 0 0;
897
- }
898
-
899
- .wpsl-results-only #wpsl-search-wrap .wpsl-input,
900
- .wpsl-dropdown {
901
- width: 100%;
902
- }
903
-
904
- .wpsl-search {
905
- padding: 2%;
906
- }
907
-
908
- .wpsl-input {
909
- margin-right: 0;
910
- }
911
-
912
- #wpsl-result-list,
913
- #wpsl-gmap {
914
- width:49.75%;
915
- }
916
-
917
- #wpsl-result-list,
918
- #wpsl-gmap {
919
- float: none;
920
- width: 100%;
921
- }
922
-
923
- .wpsl-direction-before {
924
- padding-left: 0;
925
- }
926
-
927
- #wpsl-gmap {
928
- margin-bottom: 15px;
929
- }
930
-
931
- .wpsl-cat-results-filter .wpsl-select-wrap,
932
- .wpsl-filter .wpsl-select-wrap,
933
- #wpsl-result-list {
934
- margin-bottom: 10px;
935
- }
936
-
937
- #wpsl-result-list p,
938
- #wpsl-wrap #wpsl-result-list li {
939
- padding-left: 0;
940
- }
941
-
942
- #wpsl-wrap #wpsl-result-list li.wpsl-preloader {
943
- padding-left: 25px;
944
- }
945
-
946
- .wpsl-preloader img {
947
- left: 0;
948
- }
949
-
950
- #wpsl-stores.wpsl-not-loaded {
951
- height: 25px;
952
- }
953
-
954
- #wpsl-reset-map {
955
- top: 25px;
956
- }
957
-
958
- #wpsl-gmap {
959
- margin-top: 10px;
960
- }
961
-
962
- .wpsl-no-filters #wpsl-search-wrap .wpsl-input,
963
- #wpsl-category, .wpsl-input, .wpsl-select-wrap,
964
- .wpsl-input, #wpsl-search-btn {
965
- margin-bottom: 0;
966
- }
967
-
968
- #wpsl-stores.wpsl-no-autoload {
969
- height: auto !important;
970
- }
971
-
972
- #wpsl-checkbox-filter.wpsl-checkbox-3-columns li,
973
- #wpsl-checkbox-filter.wpsl-checkbox-4-columns li {
974
- width: 49%;
975
- }
976
- }
977
-
978
- @media (max-width: 570px) {
979
- #wpsl-search-wrap #wpsl-search-btn {
980
- margin-bottom: 5px;
981
- }
982
-
983
- .wpsl-search {
984
- padding: 4%;
985
- }
986
-
987
- #wpsl-search-input {
988
- width: 98% !important;
989
- }
990
-
991
- .wpsl-cat-results-filter #wpsl-search-wrap .wpsl-input,
992
- .wpsl-cat-results-filter #wpsl-search-input,
993
- .wpsl-no-results #wpsl-search-input,
994
- .wpsl-results-only #wpsl-search-input {
995
- width: 100% !important;
996
- }
997
-
998
- .wpsl-search-btn-wrap {
999
- margin-top: 15px;
1000
- }
1001
-
1002
- .wpsl-checkboxes-enabled .wpsl-search-btn-wrap {
1003
- margin-top: 0;
1004
- }
1005
-
1006
- #wpsl-search-wrap div,
1007
- #wpsl-search-btn {
1008
- margin-right: 0;
1009
- }
1010
-
1011
- #wpsl-search-wrap div label {
1012
- display: block;
1013
- width: 100%;
1014
- }
1015
-
1016
- #wpsl-results {
1017
- width:auto;
1018
- }
1019
-
1020
- .wpsl-select-wrap {
1021
- width: 100%;
1022
- }
1023
-
1024
- #wpsl-radius,
1025
- #wpsl-results {
1026
- width: 50%;
1027
- }
1028
-
1029
- #wpsl-radius {
1030
- margin-right: 4%;
1031
- }
1032
-
1033
- #wpsl-search-wrap .wpsl-dropdown {
1034
- width: 96% !important;
1035
- }
1036
-
1037
- .wpsl-search-btn-wrap {
1038
- clear: both;
1039
- }
1040
-
1041
- .wpsl-no-filters #wpsl-search-wrap .wpsl-input,
1042
- .wpsl-no-filters #wpsl-search-input {
1043
- width: 100% !important;
1044
- }
1045
- }
1046
-
1047
- @media (max-width: 420px) {
1048
- #wpsl-checkbox-filter li {
1049
- margin: 0;
1050
- }
1051
-
1052
- #wpsl-checkbox-filter.wpsl-checkbox-1-columns li,
1053
- #wpsl-checkbox-filter.wpsl-checkbox-2-columns li,
1054
- #wpsl-checkbox-filter.wpsl-checkbox-3-columns li,
1055
- #wpsl-checkbox-filter.wpsl-checkbox-4-columns li {
1056
- width: 100%;
1057
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1058
  }
1
+ @font-face {
2
+ font-family: 'wpsl-fontello';
3
+ src: url('../font/fontello.eot?28897909');
4
+ src: url('../font/fontello.eot?28897909#iefix') format('embedded-opentype'),
5
+ url('../font/fontello.woff?28897909') format('woff'),
6
+ url('../font/fontello.ttf?28897909') format('truetype'),
7
+ url('../font/fontello.svg?28897909#fontello') format('svg');
8
+ font-weight: normal;
9
+ font-style: normal;
10
+ }
11
+
12
+ #wpsl-gmap {
13
+ float:right;
14
+ width:66.5%;
15
+ height:350px;
16
+ margin-bottom:0;
17
+ }
18
+
19
+ .wpsl-store-below #wpsl-gmap {
20
+ float:none;
21
+ width:100%;
22
+ }
23
+
24
+ .wpsl-gmap-canvas {
25
+ width:100%;
26
+ height:300px;
27
+ margin-bottom:20px;
28
+ }
29
+
30
+ #wpsl-reset-map:hover {
31
+ cursor: pointer;
32
+ }
33
+
34
+ /*
35
+ Some themes set a box-shadow or max-width for all image /
36
+ div elements, we disable it to prevent it from messing up the map
37
+
38
+ The .gv-iv- class is used in streetview, and they should not be included.
39
+ */
40
+ #wpsl-gmap div:not[class^="gv-iv"],
41
+ #wpsl-gmap img,
42
+ .wpsl-gmap-canvas div:not[class^="gv-iv"],
43
+ .wpsl-gmap-canvas img {
44
+ box-shadow: none !important;
45
+ max-width: none !important;
46
+ background: none;
47
+ }
48
+
49
+ #wpsl-gmap img,
50
+ .wpsl-gmap-canvas img {
51
+ display: inline !important;
52
+ opacity: 1 !important;
53
+ max-height: none !important;
54
+ }
55
+
56
+ /*
57
+ Fix a problem where the background color used
58
+ in street view mode doesn't cover the control area.
59
+ */
60
+ #wpsl-gmap * {
61
+ box-sizing: content-box !important;
62
+ -webkit-box-sizing: content-box !important;
63
+ -moz-box-sizing: content-box !important;
64
+ }
65
+
66
+ #wpsl-gmap div.gm-iv-marker,
67
+ .wpsl-gmap-canvas div.gm-iv-marker {
68
+ backgroud-image: inherit;
69
+ }
70
+
71
+ #wpsl-wrap {
72
+ position: relative;
73
+ width: 100%;
74
+ overflow: hidden;
75
+ clear: both;
76
+ margin-bottom: 20px;
77
+ }
78
+
79
+ #wpsl-search-wrap {
80
+ float: left;
81
+ width: 100%;
82
+ }
83
+
84
+ #wpsl-search-wrap form {
85
+ margin: 0;
86
+ padding: 0;
87
+ border: none;
88
+ outline: none;
89
+ }
90
+
91
+ /* Map Controls */
92
+ #wpsl-gmap #wpsl-map-controls {
93
+ position: absolute;
94
+ height: 28px;
95
+ right: 10px;
96
+ bottom: 24px;
97
+ border-radius: 2px;
98
+ z-index: 3;
99
+ font-size: 11px;
100
+ white-space: nowrap;
101
+ overflow: hidden;
102
+ }
103
+
104
+ #wpsl-gmap #wpsl-map-controls.wpsl-street-view-exists {
105
+ right: 48px;
106
+ }
107
+
108
+ #wpsl-map-controls .wpsl-direction-preloader {
109
+ margin: 5px 5px 0 5px;
110
+ }
111
+
112
+ #wpsl-map-controls div {
113
+ float: left;
114
+ background: #fff;
115
+ border-radius: 2px;
116
+ }
117
+
118
+ #wpsl-map-controls div:hover {
119
+ cursor: pointer;
120
+ }
121
+
122
+ #wpsl-wrap [class^="wpsl-icon-"],
123
+ #wpsl-wrap [class*=" wpsl-icon-"] {
124
+ position: relative;
125
+ float: left;
126
+ padding: 7px 9px 7px 8px;
127
+ display: inline-block;
128
+ font-family: "wpsl-fontello";
129
+ font-style: normal;
130
+ font-weight: normal;
131
+ font-size: 1.3em;
132
+ color: #737373;
133
+ speak: none;
134
+ text-decoration: inherit;
135
+ text-align: center;
136
+ font-variant: normal;
137
+ text-transform: none;
138
+ line-height: 1em;
139
+ -webkit-font-smoothing: antialiased;
140
+ -moz-osx-font-smoothing: grayscale;
141
+ }
142
+
143
+ /*
144
+ * Make sure the CSS from a theme doesn't set a different font family, or font size
145
+ * for the font icons. Otherwise the icons either don't show, or are to large.
146
+ */
147
+ #wpsl-map-controls span {
148
+ font-family: inherit;
149
+ font-size: inherit;
150
+ }
151
+
152
+ /* Fix the padding for the icon fonts in IE 8-11 :( */
153
+ #wpsl-wrap .wpsl-ie [class^="wpsl-icon-"],
154
+ #wpsl-wrap .wpsl-ie [class*=" wpsl-icon-"] {
155
+ padding: 9px 8px 4px 8px;
156
+ }
157
+
158
+ /* Make the clickable area bigger for the buttons on mobile devices */
159
+ #wpsl-wrap.wpsl-mobile [class^="wpsl-icon-"],
160
+ #wpsl-wrap.wpsl-mobile [class*=" wpsl-icon-"] {
161
+ padding: 8px 10px;
162
+ }
163
+
164
+ #wpsl-wrap .wpsl-icon-reset {
165
+ border-radius: 2px 0 0 2px;
166
+ z-index: 2;
167
+ padding-left: 9px;
168
+ padding-right: 4px;
169
+ }
170
+
171
+ #wpsl-wrap .wpsl-icon-direction {
172
+ z-index: 1;
173
+ }
174
+
175
+ #wpsl-map-controls.wpsl-reset-exists .wpsl-icon-direction {
176
+ border-radius: 0 2px 2px 0;
177
+ }
178
+
179
+ #wpsl-wrap .wpsl-active-icon,
180
+ #wpsl-wrap [class^="wpsl-icon-"]:hover,
181
+ #wpsl-wrap [class*=" wpsl-icon-"]:hover {
182
+ color: #000;
183
+ }
184
+
185
+ #wpsl-wrap [class^="wpsl-icon-"]:active,
186
+ #wpsl-wrap [class*=" wpsl-icon-"]:focus {
187
+ outline: 0;
188
+ }
189
+
190
+ #wpsl-wrap .wpsl-in-progress:hover,
191
+ #wpsl-wrap .wpsl-in-progress {
192
+ color: #c6c6c6;
193
+ }
194
+
195
+ /* Map reset button */
196
+ #wpsl-gmap #wpsl-reset-map {
197
+ position: absolute;
198
+ display: none;
199
+ right: 37px;
200
+ top: 37px;
201
+ padding: 6px 14px;
202
+ background: #fff !important;
203
+ background-clip: padding-box;
204
+ border: 1px solid rgba(0, 0, 0, 0.15);
205
+ border-radius: 2px;
206
+ z-index: 3;
207
+ }
208
+
209
+ #wpsl-reset-map:hover {
210
+ cursor: pointer;
211
+ }
212
+
213
+ /* Possible fix for vertical text issue in IE9? */
214
+ .gm-style-cc {
215
+ word-wrap:normal;
216
+ }
217
+
218
+ #wpsl-search-wrap .wpsl-input,
219
+ #wpsl-search-wrap .wpsl-select-wrap {
220
+ display:table;
221
+ }
222
+
223
+ #wpsl-search-wrap .wpsl-input label,
224
+ #wpsl-search-wrap .wpsl-input input,
225
+ #wpsl-search-wrap #wpsl-radius,
226
+ #wpsl-search-wrap #wpsl-results,
227
+ #wpsl-search-btn {
228
+ display:table-cell;
229
+ }
230
+
231
+ #wpsl-search-wrap label {
232
+ margin-bottom:0;
233
+ }
234
+
235
+ #wpsl-search-input {
236
+ width: 179px;
237
+ height: auto;
238
+ padding: 7px 12px;
239
+ font-size: 100%;
240
+ margin: 0;
241
+ }
242
+
243
+ #wpsl-search-wrap input,
244
+ #wpsl-search-btn {
245
+ border: 1px solid #d2d2d2;
246
+ border-radius: 3px;
247
+ }
248
+
249
+ #wpsl-search-btn {
250
+ padding: 7px 10px;
251
+ line-height: 1.428571429;
252
+ font-weight: normal;
253
+ color: #7c7c7c;
254
+ background-color: #e6e6e6;
255
+ background-repeat: repeat-x;
256
+ background-image: -moz-linear-gradient(top, #f4f4f4, #e6e6e6);
257
+ background-image: -ms-linear-gradient(top, #f4f4f4, #e6e6e6);
258
+ background-image: -webkit-linear-gradient(top, #f4f4f4, #e6e6e6);
259
+ background-image: -o-linear-gradient(top, #f4f4f4, #e6e6e6);
260
+ background-image: linear-gradient(top, #f4f4f4, #e6e6e6);
261
+ box-shadow: 0 1px 2px rgba(64, 64, 64, 0.1);
262
+ text-transform: none !important;
263
+ }
264
+
265
+ #wpsl-search-input.wpsl-error {
266
+ border:1px solid #bd0028 !important;
267
+ }
268
+
269
+ .wpsl-search {
270
+ margin-bottom:12px;
271
+ padding:12px 12px 0 12px;
272
+ background:#f4f3f3;
273
+ }
274
+
275
+ .wpsl-search.wpsl-checkboxes-enabled {
276
+ padding: 12px;
277
+ }
278
+
279
+ /* Result list */
280
+ .wpsl-back {
281
+ display: inline-block;
282
+ }
283
+
284
+ #wpsl-result-list {
285
+ width:33%;
286
+ margin-right:0.5%;
287
+ }
288
+
289
+ .wpsl-store-below #wpsl-result-list {
290
+ width:100%;
291
+ margin:12px 0 0 0;
292
+ }
293
+
294
+ #wpsl-stores,
295
+ #wpsl-direction-details {
296
+ height:350px;
297
+ overflow-y:auto;
298
+ }
299
+
300
+ .wpsl-hide,
301
+ #wpsl-direction-details {
302
+ display:none;
303
+ }
304
+
305
+ #wpsl-result-list p {
306
+ padding-left:10px;
307
+ }
308
+ .wpsl-store-below #wpsl-result-list p {
309
+ padding-left: 0;
310
+ }
311
+
312
+ #wpsl-result-list a {
313
+ outline:none;
314
+ }
315
+
316
+ .wpsl-direction-before {
317
+ margin: 14px 0 21px 0;
318
+ padding-left: 10px;
319
+ }
320
+
321
+ .wpsl-store-below .wpsl-direction-before {
322
+ padding-left: 0;
323
+ }
324
+
325
+ .wpsl-direction-before div {
326
+ margin-top: 10px;
327
+ }
328
+
329
+ #wpsl-wrap #wpsl-result-list li {
330
+ padding: 10px;
331
+ border-bottom: 1px dotted #ccc;
332
+ margin-left: 0;
333
+ overflow: hidden;
334
+ list-style: none outside none !important;
335
+ text-indent: 0;
336
+ }
337
+
338
+ #wpsl-wrap #wpsl-result-list li li {
339
+ padding: 0;
340
+ border-bottom: 0;
341
+ margin-left: 14px;
342
+ overflow: visible;
343
+ }
344
+
345
+ #wpsl-wrap #wpsl-result-list ul li {
346
+ list-style: none !important;
347
+ }
348
+
349
+ #wpsl-wrap #wpsl-result-list ol li {
350
+ list-style: decimal !important;
351
+ }
352
+
353
+ #wpsl-wrap.wpsl-store-below #wpsl-result-list li {
354
+ padding: 10px 10px 10px 0;
355
+ }
356
+
357
+ #wpsl-result-list li p {
358
+ padding-left: 0;
359
+ margin: 0 0 20px 0;
360
+ }
361
+
362
+ .wpsl-store-details.wpsl-store-listing {
363
+ position: relative;
364
+ padding-right: 20px;
365
+ }
366
+
367
+ .wpsl-store-details.wpsl-store-listing:before,
368
+ .wpsl-store-details.wpsl-store-listing.wpsl-active-details:before {
369
+ position: absolute;
370
+ content: '';
371
+ bottom:6px;
372
+ right:0;
373
+ border-top: 5px solid #000000;
374
+ border-left: 6px solid rgba(0, 0, 0, 0);
375
+ border-right: 6px solid rgba(0, 0, 0, 0);
376
+ }
377
+
378
+ .wpsl-store-details.wpsl-store-listing.wpsl-active-details:before {
379
+ border-bottom: 5px solid #000000;
380
+ border-top:none;
381
+ border-left: 6px solid rgba(0, 0, 0, 0);
382
+ border-right: 6px solid rgba(0, 0, 0, 0);
383
+ }
384
+
385
+ #wpsl-stores .wpsl-store-thumb {
386
+ float:right;
387
+ border-radius:3px;
388
+ margin:7px 0 0 10px;
389
+ padding:0;
390
+ border:none;
391
+ }
392
+
393
+ .wpsl-direction-index {
394
+ float:left;
395
+ width:8%;
396
+ margin:0 5% 0 0;
397
+ }
398
+
399
+ .wpsl-direction-txt {
400
+ float:left;
401
+ width:62%;
402
+ }
403
+
404
+ .wpsl-direction-distance {
405
+ float:left;
406
+ width:20%;
407
+ margin:0 0 0 5%;
408
+ }
409
+
410
+ .wpsl-direction-txt span {
411
+ display:block;
412
+ margin-top:10px;
413
+ }
414
+
415
+ .wpsl-street,
416
+ .wpsl-country {
417
+ display: block;
418
+ border-bottom: none !important;
419
+ }
420
+
421
+ .wpsl-directions {
422
+ display: table;
423
+ border-bottom: none !important;
424
+ }
425
+
426
+ /* Preloader */
427
+ #wpsl-wrap #wpsl-result-list li.wpsl-preloader {
428
+ position: relative;
429
+ border-bottom: none;
430
+ padding: 10px 10px 10px 35px;
431
+ }
432
+
433
+ .wpsl-preloader img {
434
+ position: absolute;
435
+ left: 10px;
436
+ top: 50%;
437
+ margin-top: -8px;
438
+ box-shadow:none !important;
439
+ border:none !important;
440
+ }
441
+
442
+ .wpsl-preloader span {
443
+ float: left;
444
+ margin: -5px 0 0 11px;
445
+ }
446
+
447
+ #wpsl-search-wrap div,
448
+ #wpsl-search-btn {
449
+ margin-right: 10px;
450
+ float: left;
451
+ }
452
+
453
+ #wpsl-search-wrap .wpsl-select-wrap {
454
+ position: relative;
455
+ z-index: 2;
456
+ margin-right: 0;
457
+ }
458
+
459
+ #wpsl-search-wrap .wpsl-input-field {
460
+ position: relative;
461
+ }
462
+
463
+ #wpsl-radius, #wpsl-results {
464
+ float: left;
465
+ margin-right: 15px;
466
+ }
467
+
468
+ #wpsl-category {
469
+ position: relative;
470
+ z-index: 1;
471
+ clear: both;
472
+ }
473
+
474
+ #wpsl-search-wrap .wpsl-dropdown div {
475
+ position: absolute;
476
+ float: none;
477
+ margin: -1px 0 0 0;
478
+ top: 100%;
479
+ left: -1px;
480
+ right: -1px;
481
+ border: 1px solid #ccc;
482
+ background: #fff;
483
+ border-top: 1px solid #eee;
484
+ border-radius: 0 0 3px 3px;
485
+ opacity: 0;
486
+ overflow: hidden;
487
+ -webkit-transition: all 150ms ease-in-out;
488
+ -moz-transition: all 150ms ease-in-out;
489
+ -ms-transition: all 150ms ease-in-out;
490
+ transition: all 150ms ease-in-out;
491
+ }
492
+
493
+ #wpsl-search-wrap .wpsl-dropdown.wpsl-active div {
494
+ opacity: 1;
495
+ }
496
+
497
+ #wpsl-search-wrap .wpsl-input label {
498
+ margin-right:0;
499
+ }
500
+
501
+ #wpsl-radius, #wpsl-results {
502
+ display:inline;
503
+ }
504
+
505
+ #wpsl-radius {
506
+ margin-right:10px;
507
+ }
508
+ #wpsl-search-btn:hover {
509
+ cursor: pointer;
510
+ }
511
+
512
+ #wpsl-search-wrap select,
513
+ #wpsl-search select {
514
+ display:none;
515
+ }
516
+
517
+ #wpsl-search-wrap div label {
518
+ float:left;
519
+ margin-right:10px;
520
+ line-height: 32px;
521
+ }
522
+
523
+ #wpsl-results label {
524
+ width: auto;
525
+ }
526
+
527
+ #wpsl-result-list ul {
528
+ list-style: none;
529
+ margin: 0;
530
+ padding: 0;
531
+ }
532
+ .wpsl-direction-details {
533
+ display: none;
534
+ }
535
+
536
+ /* Infowindow */
537
+ #wpsl-gmap .wpsl-info-window,
538
+ .wpsl-gmap-canvas .wpsl-info-window {
539
+ max-width:225px;
540
+ }
541
+
542
+ .wpsl-more-info-listings span,
543
+ .wpsl-info-window span {
544
+ display:block;
545
+ }
546
+
547
+ .wpsl-info-window .wpsl-no-margin {
548
+ margin:0;
549
+ }
550
+
551
+ /* More info details in the store listings */
552
+ .wpsl-more-info-listings {
553
+ display:none;
554
+ }
555
+
556
+ /* Fix for Google Voice breaking the phone numbers */
557
+ .wpsl-info-window span span {
558
+ display:inline !important;
559
+ }
560
+
561
+ #wpsl-wrap .wpsl-info-window p {
562
+ margin: 0 0 10px 0;
563
+ }
564
+
565
+ .wpsl-store-hours {
566
+ margin-top:10px;
567
+ }
568
+
569
+ .wpsl-store-hours strong {
570
+ display:block;
571
+ }
572
+
573
+ #wpsl-gmap .wpsl-info-actions {
574
+ display:block;
575
+ margin:10px 0 !important;
576
+ }
577
+
578
+ .wpsl-info-actions a {
579
+ float:left;
580
+ margin-right: 7px;
581
+ }
582
+
583
+ .wpsl-info-actions .wpsl-zoom-here {
584
+ margin-right:0;
585
+ }
586
+
587
+ /* --- dropdowns --- */
588
+ .wpsl-dropdown {
589
+ position: relative;
590
+ width: 90px;
591
+ border: 1px solid #ccc;
592
+ cursor: pointer;
593
+ background: #fff;
594
+ border-radius: 3px;
595
+ -webkit-user-select: none;
596
+ -moz-user-select: none;
597
+ user-select: none;
598
+ margin-right: 0 !important;
599
+ z-index: 2;
600
+ }
601
+
602
+ #wpsl-results .wpsl-dropdown {
603
+ width: 70px;
604
+ }
605
+
606
+ .wpsl-dropdown ul {
607
+ position: absolute;
608
+ left: 0;
609
+ width: 100%;
610
+ height: 100%;
611
+ padding: 0 !important;
612
+ margin: 0 !important;
613
+ list-style: none;
614
+ overflow: hidden;
615
+ }
616
+
617
+ .wpsl-dropdown:hover {
618
+ box-shadow: 0 0 5px rgba( 0, 0, 0, 0.15 );
619
+ }
620
+
621
+ .wpsl-dropdown .wpsl-selected-item,
622
+ .wpsl-dropdown li {
623
+ position: relative;
624
+ display: block;
625
+ line-height: normal;
626
+ color: #000;
627
+ overflow: hidden;
628
+ }
629
+
630
+ #wpsl-radius .wpsl-dropdown .wpsl-selected-item,
631
+ #wpsl-radius .wpsl-dropdown li,
632
+ #wpsl-results .wpsl-dropdown .wpsl-selected-item,
633
+ #wpsl-results .wpsl-dropdown li {
634
+ white-space: nowrap;
635
+ }
636
+
637
+ .wpsl-selected-item:after {
638
+ position: absolute;
639
+ content: "";
640
+ right: 12px;
641
+ top: 50%;
642
+ margin-top: -4px;
643
+ border: 6px solid transparent;
644
+ border-top: 8px solid #000;
645
+ }
646
+
647
+ .wpsl-active .wpsl-selected-item:after {
648
+ margin-top: -10px;
649
+ border: 6px solid transparent;
650
+ border-bottom: 8px solid #000;
651
+ }
652
+
653
+ .wpsl-dropdown li:hover {
654
+ background: #f8f9f8;
655
+ position: relative;
656
+ z-index: 3;
657
+ color: #000;
658
+ }
659
+
660
+ .wpsl-dropdown .wpsl-selected-item,
661
+ .wpsl-dropdown li,
662
+ .wpsl-selected-item {
663
+ list-style: none;
664
+ padding: 9px 12px !important;
665
+ margin:0 !important;
666
+ }
667
+
668
+ .wpsl-selected-dropdown {
669
+ font-weight: bold;
670
+ }
671
+
672
+ .wpsl-clearfix:before,
673
+ .wpsl-clearfix:after {
674
+ content: " ";
675
+ display: table;
676
+ }
677
+
678
+ .wpsl-clearfix:after {
679
+ clear: both;
680
+ }
681
+
682
+ #wpsl-wrap .wpsl-selected-item {
683
+ position: static;
684
+ padding-right: 35px !important;
685
+ }
686
+
687
+ #wpsl-category,
688
+ .wpsl-input,
689
+ .wpsl-select-wrap {
690
+ position: relative;
691
+ margin-bottom: 10px;
692
+ }
693
+
694
+ #wpsl-search-wrap .wpsl-scroll-required div {
695
+ overflow-y: scroll;
696
+ }
697
+
698
+ .wpsl-scroll-required ul {
699
+ overflow: visible;
700
+ }
701
+
702
+ .wpsl-provided-by {
703
+ float: right;
704
+ padding: 5px 0;
705
+ text-align: right;
706
+ font-size: 12px;
707
+ width: 100%;
708
+ }
709
+
710
+ #wpsl-wrap .wpsl-results-only label {
711
+ width: auto;
712
+ }
713
+
714
+ /* wpsl custom post type pages */
715
+ .wpsl-locations-details,
716
+ .wpsl-location-address,
717
+ .wpsl-contact-details {
718
+ margin-bottom: 15px;
719
+ }
720
+
721
+ .wpsl-contact-details {
722
+ clear: both;
723
+ }
724
+
725
+ table.wpsl-opening-hours td {
726
+ vertical-align: top;
727
+ padding: 0 15px 0 0;
728
+ text-align: left;
729
+ }
730
+
731
+ table.wpsl-opening-hours time {
732
+ display:block;
733
+ }
734
+
735
+ table.wpsl-opening-hours {
736
+ width:auto !important;
737
+ font-size:100% !important;
738
+ }
739
+
740
+ table.wpsl-opening-hours,
741
+ table.wpsl-opening-hours td {
742
+ border:none !important;
743
+ }
744
+
745
+ /* Custom Infobox */
746
+ .wpsl-gmap-canvas .wpsl-infobox {
747
+ min-width:155px;
748
+ max-width:350px !important;
749
+ padding:10px;
750
+ border-radius:4px;
751
+ font-size:13px;
752
+ font-weight:300;
753
+ border:1px solid #ccc;
754
+ background:#fff !important;
755
+ }
756
+
757
+ .wpsl-gmap-canvas .wpsl-infobox:after,
758
+ .wpsl-gmap-canvas .wpsl-infobox:before {
759
+ position:absolute;
760
+ content:"";
761
+ left:40px;
762
+ bottom:-11px;
763
+ }
764
+
765
+ .wpsl-gmap-canvas .wpsl-infobox:after {
766
+ border-left:11px solid transparent;
767
+ border-right:11px solid transparent;
768
+ border-top:11px solid #fff;
769
+ }
770
+
771
+ .wpsl-gmap-canvas .wpsl-infobox:before {
772
+ border-left:13px solid transparent;
773
+ border-right:13px solid transparent;
774
+ border-top:13px solid #ccc;
775
+ bottom:-13px;
776
+ left:38px;
777
+ }
778
+
779
+ #wpsl-checkbox-filter,
780
+ .wpsl-custom-checkboxes {
781
+ display: block;
782
+ float: left;
783
+ margin: 5px 0 15px;
784
+ padding: 0;
785
+ width: 100%;
786
+ }
787
+
788
+ #wpsl-checkbox-filter li,
789
+ .wpsl-custom-checkboxes li {
790
+ float: left;
791
+ list-style: none;
792
+ margin: 0 1% 0 0;
793
+ }
794
+
795
+ #wpsl-checkbox-filter.wpsl-checkbox-1-columns li,
796
+ .wpsl-custom-checkboxes.wpsl-checkbox-1-columns li {
797
+ width: 99%;
798
+ }
799
+
800
+ #wpsl-checkbox-filter.wpsl-checkbox-2-columns li,
801
+ .wpsl-custom-checkboxes.wpsl-checkbox-2-columns li {
802
+ width: 49%;
803
+ }
804
+
805
+ #wpsl-checkbox-filter.wpsl-checkbox-3-columns li,
806
+ .wpsl-custom-checkboxes.wpsl-checkbox-3-columns li {
807
+ width: 32%;
808
+ }
809
+
810
+ #wpsl-checkbox-filter.wpsl-checkbox-4-columns li,
811
+ .wpsl-custom-checkboxes.wpsl-checkbox-4-columns li {
812
+ width: 24%;
813
+ }
814
+
815
+ #wpsl-checkbox-filter input,
816
+ .wpsl-custom-checkboxes input {
817
+ margin-right: 5px;
818
+ }
819
+
820
+ #wpsl-result-list .wpsl-contact-details span {
821
+ display: block !important;
822
+ }
823
+
824
+ /*
825
+ Hide the select2 ( https://select2.org/ ) dropdowns to
826
+ prevent duplicate dropdowns from showing up in the search bar.
827
+ */
828
+ #wpsl-search-wrap .select2 {
829
+ display: none !important;
830
+ }
831
+
832
+ /*
833
+ Make a few adjustments for themes that use RTL languages
834
+ */
835
+ .rtl #wpsl-result-list {
836
+ float: left;
837
+ }
838
+
839
+ .rtl #wpsl-checkbox-filter input,
840
+ .rtl .wpsl-custom-checkboxes input {
841
+ margin-right: 0;
842
+ margin-left: 5px;
843
+ }
844
+
845
+ .rtl .wpsl-info-actions a {
846
+ float: right;
847
+ margin: 0 0 0 7px;
848
+ }
849
+
850
+ .rtl #wpsl-gmap .wpsl-info-window {
851
+ padding-right: 22px;
852
+ }
853
+
854
+ .rtl #wpsl-wrap #wpsl-result-list li.wpsl-preloader {
855
+ padding: 10px 35px 10px 0;
856
+ }
857
+
858
+ .rtl .wpsl-preloader img {
859
+ left: 0;
860
+ right: 10px;
861
+ }
862
+
863
+ /* Only used when the TwentyNinteen theme is active */
864
+ .wpsl-twentynineteen .wpsl-input {
865
+ width: 100%;
866
+ }
867
+
868
+ .wpsl-twentynineteen #wpsl-search-input {
869
+ line-height: 1.3em;
870
+ }
871
+
872
+ .wpsl-twentynineteen #wpsl-search-wrap label {
873
+ margin-top: 6px;
874
+ }
875
+
876
+ .wpsl-twentynineteen .wpsl-dropdown {
877
+ width: 116px;
878
+ }
879
+
880
+ #wpsl-results .wpsl-dropdown {
881
+ width: 81px;
882
+ }
883
+
884
+ @media (max-width: 825px) {
885
+ #wpsl-search-input {
886
+ width: 348px;
887
+ }
888
+
889
+ .wpsl-results-only #wpsl-search-wrap .wpsl-dropdown {
890
+ width: 70px;
891
+ }
892
+
893
+ #wpsl-search-wrap .wpsl-input {
894
+ width: 100%;
895
+ margin-bottom: 10px;
896
+ }
897
+
898
+ .wpsl-input label,
899
+ #wpsl-radius label,
900
+ #wpsl-category label,
901
+ .wpsl-cat-results-filter #wpsl-search-wrap .wpsl-input,
902
+ .wpsl-no-filters #wpsl-search-wrap .wpsl-input,
903
+ .wpsl-results-only #wpsl-search-wrap .wpsl-input {
904
+ width: auto;
905
+ }
906
+ }
907
+
908
+ @media (max-width: 720px) {
909
+ #wpsl-search-wrap .wpsl-dropdown {
910
+ width: 114px;
911
+ }
912
+ }
913
+
914
+ @media (max-width: 675px) {
915
+ #wpsl-search-wrap #wpsl-search-btn {
916
+ float: left;
917
+ margin: 0 5px 0 0;
918
+ }
919
+
920
+ .wpsl-results-only #wpsl-search-wrap .wpsl-input,
921
+ .wpsl-dropdown {
922
+ width: 100%;
923
+ }
924
+
925
+ .wpsl-search {
926
+ padding: 2%;
927
+ }
928
+
929
+ .wpsl-input {
930
+ margin-right: 0;
931
+ }
932
+
933
+ #wpsl-result-list,
934
+ #wpsl-gmap {
935
+ width:49.75%;
936
+ }
937
+
938
+ #wpsl-result-list,
939
+ #wpsl-gmap {
940
+ float: none;
941
+ width: 100%;
942
+ }
943
+
944
+ .wpsl-direction-before {
945
+ padding-left: 0;
946
+ }
947
+
948
+ #wpsl-gmap {
949
+ margin-bottom: 15px;
950
+ }
951
+
952
+ .wpsl-cat-results-filter .wpsl-select-wrap,
953
+ .wpsl-filter .wpsl-select-wrap,
954
+ #wpsl-result-list {
955
+ margin-bottom: 10px;
956
+ }
957
+
958
+ #wpsl-result-list p,
959
+ #wpsl-wrap #wpsl-result-list li {
960
+ padding-left: 0;
961
+ }
962
+
963
+ #wpsl-wrap #wpsl-result-list li.wpsl-preloader {
964
+ padding-left: 25px;
965
+ }
966
+
967
+ .wpsl-preloader img {
968
+ left: 0;
969
+ }
970
+
971
+ #wpsl-stores.wpsl-not-loaded {
972
+ height: 25px;
973
+ }
974
+
975
+ #wpsl-reset-map {
976
+ top: 25px;
977
+ }
978
+
979
+ #wpsl-gmap {
980
+ margin-top: 10px;
981
+ }
982
+
983
+ .wpsl-no-filters #wpsl-search-wrap .wpsl-input,
984
+ #wpsl-category, .wpsl-input, .wpsl-select-wrap,
985
+ .wpsl-input, #wpsl-search-btn {
986
+ margin-bottom: 0;
987
+ }
988
+
989
+ #wpsl-stores.wpsl-no-autoload {
990
+ height: auto !important;
991
+ }
992
+
993
+ #wpsl-checkbox-filter.wpsl-checkbox-3-columns li,
994
+ #wpsl-checkbox-filter.wpsl-checkbox-4-columns li {
995
+ width: 49%;
996
+ }
997
+ }
998
+
999
+ @media (max-width: 570px) {
1000
+ #wpsl-search-wrap #wpsl-search-btn {
1001
+ margin-bottom: 5px;
1002
+ }
1003
+
1004
+ .wpsl-search {
1005
+ padding: 4%;
1006
+ }
1007
+
1008
+ #wpsl-search-input {
1009
+ width: 98% !important;
1010
+ }
1011
+
1012
+ .wpsl-cat-results-filter #wpsl-search-wrap .wpsl-input,
1013
+ .wpsl-cat-results-filter #wpsl-search-input,
1014
+ .wpsl-no-results #wpsl-search-input,
1015
+ .wpsl-results-only #wpsl-search-input {
1016
+ width: 100% !important;
1017
+ }
1018
+
1019
+ .wpsl-search-btn-wrap {
1020
+ margin-top: 15px;
1021
+ }
1022
+
1023
+ .wpsl-checkboxes-enabled .wpsl-search-btn-wrap {
1024
+ margin-top: 0;
1025
+ }
1026
+
1027
+ #wpsl-search-wrap div,
1028
+ #wpsl-search-btn {
1029
+ margin-right: 0;
1030
+ }
1031
+
1032
+ #wpsl-search-wrap div label {
1033
+ display: block;
1034
+ width: 100%;
1035
+ }
1036
+
1037
+ #wpsl-results {
1038
+ width:auto;
1039
+ }
1040
+
1041
+ .wpsl-select-wrap {
1042
+ width: 100%;
1043
+ }
1044
+
1045
+ #wpsl-radius,
1046
+ #wpsl-results {
1047
+ width: 50%;
1048
+ }
1049
+
1050
+ #wpsl-radius {
1051
+ margin-right: 4%;
1052
+ }
1053
+
1054
+ #wpsl-search-wrap .wpsl-dropdown {
1055
+ width: 96% !important;
1056
+ }
1057
+
1058
+ .wpsl-search-btn-wrap {
1059
+ clear: both;
1060
+ }
1061
+
1062
+ .wpsl-no-filters #wpsl-search-wrap .wpsl-input,
1063
+ .wpsl-no-filters #wpsl-search-input {
1064
+ width: 100% !important;
1065
+ }
1066
+ }
1067
+
1068
+ @media (max-width: 420px) {
1069
+ #wpsl-checkbox-filter li {
1070
+ margin: 0;
1071
+ }
1072
+
1073
+ #wpsl-checkbox-filter.wpsl-checkbox-1-columns li,
1074
+ #wpsl-checkbox-filter.wpsl-checkbox-2-columns li,
1075
+ #wpsl-checkbox-filter.wpsl-checkbox-3-columns li,
1076
+ #wpsl-checkbox-filter.wpsl-checkbox-4-columns li {
1077
+ width: 100%;
1078
+ }
1079
  }
css/styles.min.css CHANGED
@@ -1 +1 @@
1
- #wpsl-wrap,.wpsl-gmap-canvas{margin-bottom:20px;width:100%}#wpsl-result-list a,#wpsl-wrap [class*=" wpsl-icon-"]:focus,#wpsl-wrap [class^=wpsl-icon-]:active{outline:0}#wpsl-map-controls div:hover,#wpsl-reset-map:hover,#wpsl-search-btn:hover,.wpsl-dropdown{cursor:pointer}#wpsl-wrap,.wpsl-clearfix:after,.wpsl-contact-details{clear:both}@font-face{font-family:wpsl-fontello;src:url(../font/fontello.eot?28897909);src:url(../font/fontello.eot?28897909#iefix) format('embedded-opentype'),url(../font/fontello.woff?28897909) format('woff'),url(../font/fontello.ttf?28897909) format('truetype'),url(../font/fontello.svg?28897909#fontello) format('svg');font-weight:400;font-style:normal}#wpsl-gmap{float:right;width:66.5%;height:350px;margin-bottom:0}.wpsl-store-below #wpsl-gmap{float:none;width:100%}.wpsl-gmap-canvas{height:300px}#wpsl-gmap div:not[class^=gv-iv],#wpsl-gmap img,.wpsl-gmap-canvas div:not[class^=gv-iv],.wpsl-gmap-canvas img{box-shadow:none!important;max-width:none!important;background:0 0}#wpsl-gmap img,.wpsl-gmap-canvas img{display:inline!important;opacity:1!important;max-height:none!important}#wpsl-gmap *{box-sizing:content-box!important;-webkit-box-sizing:content-box!important;-moz-box-sizing:content-box!important}#wpsl-gmap div.gm-iv-marker,.wpsl-gmap-canvas div.gm-iv-marker{backgroud-image:inherit}#wpsl-wrap{position:relative;overflow:hidden}#wpsl-search-wrap{float:left;width:100%}#wpsl-search-wrap form{margin:0;padding:0;border:none;outline:0}#wpsl-gmap #wpsl-map-controls{position:absolute;height:28px;right:10px;bottom:24px;border-radius:2px;z-index:3;font-size:11px;white-space:nowrap;overflow:hidden}#wpsl-gmap #wpsl-map-controls.wpsl-street-view-exists{right:48px}#wpsl-map-controls .wpsl-direction-preloader{margin:5px 5px 0}#wpsl-map-controls div{float:left;background:#fff;border-radius:2px}#wpsl-wrap [class*=" wpsl-icon-"],#wpsl-wrap [class^=wpsl-icon-]{position:relative;float:left;padding:7px 9px 7px 8px;display:inline-block;font-family:wpsl-fontello;font-style:normal;font-weight:400;font-size:1.3em;color:#737373;speak:none;text-decoration:inherit;text-align:center;font-variant:normal;text-transform:none;line-height:1em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#wpsl-map-controls span{font-family:inherit;font-size:inherit}#wpsl-wrap .wpsl-ie [class*=" wpsl-icon-"],#wpsl-wrap .wpsl-ie [class^=wpsl-icon-]{padding:9px 8px 4px}#wpsl-wrap.wpsl-mobile [class*=" wpsl-icon-"],#wpsl-wrap.wpsl-mobile [class^=wpsl-icon-]{padding:8px 10px}#wpsl-wrap .wpsl-icon-reset{border-radius:2px 0 0 2px;z-index:2;padding-left:9px;padding-right:4px}#wpsl-wrap .wpsl-icon-direction{z-index:1}#wpsl-map-controls.wpsl-reset-exists .wpsl-icon-direction{border-radius:0 2px 2px 0}#wpsl-wrap .wpsl-active-icon,#wpsl-wrap [class*=" wpsl-icon-"]:hover,#wpsl-wrap [class^=wpsl-icon-]:hover{color:#000}#wpsl-wrap .wpsl-in-progress,#wpsl-wrap .wpsl-in-progress:hover{color:#c6c6c6}#wpsl-gmap #wpsl-reset-map{position:absolute;display:none;right:37px;top:37px;padding:6px 14px;background:#fff!important;border:1px solid rgba(0,0,0,.15);border-radius:2px;z-index:3}.gm-style-cc{word-wrap:normal}#wpsl-search-wrap .wpsl-input,#wpsl-search-wrap .wpsl-select-wrap{display:table}#wpsl-search-btn,#wpsl-search-wrap #wpsl-radius,#wpsl-search-wrap #wpsl-results,#wpsl-search-wrap .wpsl-input input,#wpsl-search-wrap .wpsl-input label{display:table-cell}#wpsl-search-wrap label{margin-bottom:0}#wpsl-search-input{width:179px;height:auto;padding:7px 12px;font-size:100%;margin:0}#wpsl-search-btn,#wpsl-search-wrap input{border:1px solid #d2d2d2;border-radius:3px}#wpsl-search-btn{padding:7px 10px;line-height:1.428571429;font-weight:400;color:#7c7c7c;background-color:#e6e6e6;background-repeat:repeat-x;background-image:-moz-linear-gradient(top,#f4f4f4,#e6e6e6);background-image:-ms-linear-gradient(top,#f4f4f4,#e6e6e6);background-image:-webkit-linear-gradient(top,#f4f4f4,#e6e6e6);background-image:-o-linear-gradient(top,#f4f4f4,#e6e6e6);background-image:linear-gradient(top,#f4f4f4,#e6e6e6);box-shadow:0 1px 2px rgba(64,64,64,.1);text-transform:none!important}#wpsl-search-input.wpsl-error{border:1px solid #bd0028!important}.wpsl-search{margin-bottom:12px;padding:12px 12px 0;background:#f4f3f3}.wpsl-search.wpsl-checkboxes-enabled{padding:12px}.wpsl-back{display:inline-block}#wpsl-result-list{width:33%;margin-right:.5%}.wpsl-store-below #wpsl-result-list{width:100%;margin:12px 0 0}#wpsl-direction-details,#wpsl-stores{height:350px;overflow-y:auto}#wpsl-direction-details,.wpsl-hide{display:none}#wpsl-result-list p{padding-left:10px}.wpsl-store-below #wpsl-result-list p{padding-left:0}.wpsl-direction-before{margin:14px 0 21px;padding-left:10px}.wpsl-store-below .wpsl-direction-before{padding-left:0}.wpsl-direction-before div{margin-top:10px}#wpsl-wrap #wpsl-result-list li{padding:10px;border-bottom:1px dotted #ccc;margin-left:0;overflow:hidden;list-style:none!important;text-indent:0}#wpsl-wrap #wpsl-result-list li li{padding:0;border-bottom:0;margin-left:14px;overflow:visible}#wpsl-wrap #wpsl-result-list ul li{list-style:none!important}#wpsl-wrap #wpsl-result-list ol li{list-style:decimal!important}#wpsl-wrap.wpsl-store-below #wpsl-result-list li{padding:10px 10px 10px 0}#wpsl-result-list li p{padding-left:0;margin:0 0 20px}.wpsl-store-details.wpsl-store-listing{position:relative;padding-right:20px}.wpsl-store-details.wpsl-store-listing.wpsl-active-details:before,.wpsl-store-details.wpsl-store-listing:before{position:absolute;content:'';bottom:6px;right:0;border-top:5px solid #000;border-left:6px solid transparent;border-right:6px solid transparent}.wpsl-store-details.wpsl-store-listing.wpsl-active-details:before{border-bottom:5px solid #000;border-top:none;border-left:6px solid transparent;border-right:6px solid transparent}#wpsl-stores .wpsl-store-thumb{float:right;border-radius:3px;margin:7px 0 0 10px;padding:0;border:none}.wpsl-direction-index{float:left;width:8%;margin:0 5% 0 0}.wpsl-direction-txt{float:left;width:62%}.wpsl-direction-distance{float:left;width:20%;margin:0 0 0 5%}.wpsl-direction-txt span{display:block;margin-top:10px}.wpsl-country,.wpsl-street{display:block;border-bottom:none!important}.wpsl-directions{display:table;border-bottom:none!important}#wpsl-wrap #wpsl-result-list li.wpsl-preloader{position:relative;border-bottom:none;padding:10px 10px 10px 35px}.wpsl-preloader img{position:absolute;left:10px;top:50%;margin-top:-8px;box-shadow:none!important;border:none!important}.wpsl-preloader span{float:left;margin:-5px 0 0 11px}#wpsl-search-btn,#wpsl-search-wrap div{margin-right:10px;float:left}#wpsl-search-wrap .wpsl-select-wrap{position:relative;z-index:2;margin-right:0}#wpsl-search-wrap .wpsl-input-field{position:relative}#wpsl-radius,#wpsl-results{float:left;margin-right:15px;display:inline}#wpsl-category{z-index:1;clear:both}#wpsl-search-wrap .wpsl-dropdown div{position:absolute;float:none;margin:-1px 0 0;top:100%;left:-1px;right:-1px;border:1px solid #ccc;background:#fff;border-top:1px solid #eee;border-radius:0 0 3px 3px;opacity:0;overflow:hidden;-webkit-transition:all 150ms ease-in-out;-moz-transition:all 150ms ease-in-out;-ms-transition:all 150ms ease-in-out;transition:all 150ms ease-in-out}#wpsl-search-wrap .wpsl-dropdown.wpsl-active div{opacity:1}#wpsl-search-wrap .wpsl-input label{margin-right:0}#wpsl-radius{margin-right:10px}#wpsl-search select,#wpsl-search-wrap select,.wpsl-direction-details{display:none}#wpsl-search-wrap div label{float:left;margin-right:10px;line-height:32px}#wpsl-results label{width:auto}#wpsl-result-list ul{list-style:none;margin:0;padding:0}#wpsl-gmap .wpsl-info-window,.wpsl-gmap-canvas .wpsl-info-window{max-width:225px}.wpsl-info-window span,.wpsl-more-info-listings span{display:block}.wpsl-info-window .wpsl-no-margin{margin:0}.wpsl-more-info-listings{display:none}.wpsl-info-window span span{display:inline!important}#wpsl-wrap .wpsl-info-window p{margin:0 0 10px}.wpsl-store-hours{margin-top:10px}.wpsl-store-hours strong{display:block}#wpsl-gmap .wpsl-info-actions{display:block;margin:10px 0!important}.wpsl-info-actions a{float:left;margin-right:7px}.wpsl-info-actions .wpsl-zoom-here{margin-right:0}.wpsl-dropdown{position:relative;width:90px;border:1px solid #ccc;background:#fff;border-radius:3px;-webkit-user-select:none;-moz-user-select:none;user-select:none;margin-right:0!important;z-index:2}#wpsl-results .wpsl-dropdown{width:70px}.wpsl-dropdown ul{position:absolute;left:0;width:100%;height:100%;padding:0!important;margin:0!important;list-style:none;overflow:hidden}.wpsl-dropdown:hover{box-shadow:0 0 5px rgba(0,0,0,.15)}.wpsl-dropdown .wpsl-selected-item,.wpsl-dropdown li{position:relative;display:block;line-height:normal;color:#000;overflow:hidden}#wpsl-radius .wpsl-dropdown .wpsl-selected-item,#wpsl-radius .wpsl-dropdown li,#wpsl-results .wpsl-dropdown .wpsl-selected-item,#wpsl-results .wpsl-dropdown li{white-space:nowrap}.wpsl-selected-item:after{position:absolute;content:"";right:12px;top:50%;margin-top:-4px;border:6px solid transparent;border-top:8px solid #000}.wpsl-active .wpsl-selected-item:after{margin-top:-10px;border:6px solid transparent;border-bottom:8px solid #000}.wpsl-dropdown li:hover{background:#f8f9f8;position:relative;z-index:3;color:#000}.wpsl-dropdown .wpsl-selected-item,.wpsl-dropdown li,.wpsl-selected-item{list-style:none;padding:9px 12px!important;margin:0!important}.wpsl-selected-dropdown{font-weight:700}.wpsl-clearfix:after,.wpsl-clearfix:before{content:" ";display:table}#wpsl-wrap .wpsl-selected-item{position:static;padding-right:35px!important}#wpsl-category,.wpsl-input,.wpsl-select-wrap{position:relative;margin-bottom:10px}#wpsl-search-wrap .wpsl-scroll-required div{overflow-y:scroll}.wpsl-scroll-required ul{overflow:visible}.wpsl-provided-by{float:right;padding:5px 0;text-align:right;font-size:12px;width:100%}#wpsl-wrap .wpsl-results-only label{width:auto}.wpsl-contact-details,.wpsl-location-address,.wpsl-locations-details{margin-bottom:15px}table.wpsl-opening-hours td{vertical-align:top;padding:0 15px 0 0;text-align:left}table.wpsl-opening-hours time{display:block}table.wpsl-opening-hours{width:auto!important;font-size:100%!important}table.wpsl-opening-hours,table.wpsl-opening-hours td{border:none!important}.wpsl-gmap-canvas .wpsl-infobox{min-width:155px;max-width:350px!important;padding:10px;border-radius:4px;font-size:13px;font-weight:300;border:1px solid #ccc;background:#fff!important}.wpsl-gmap-canvas .wpsl-infobox:after,.wpsl-gmap-canvas .wpsl-infobox:before{position:absolute;content:"";left:40px;bottom:-11px}.wpsl-gmap-canvas .wpsl-infobox:after{border-left:11px solid transparent;border-right:11px solid transparent;border-top:11px solid #fff}.wpsl-gmap-canvas .wpsl-infobox:before{border-left:13px solid transparent;border-right:13px solid transparent;border-top:13px solid #ccc;bottom:-13px;left:38px}#wpsl-checkbox-filter,.wpsl-custom-checkboxes{display:block;float:left;margin:5px 0 15px;padding:0;width:100%}#wpsl-checkbox-filter li,.wpsl-custom-checkboxes li{float:left;list-style:none;margin:0 1% 0 0}#wpsl-checkbox-filter.wpsl-checkbox-1-columns li,.wpsl-custom-checkboxes.wpsl-checkbox-1-columns li{width:99%}#wpsl-checkbox-filter.wpsl-checkbox-2-columns li,.wpsl-custom-checkboxes.wpsl-checkbox-2-columns li{width:49%}#wpsl-checkbox-filter.wpsl-checkbox-3-columns li,.wpsl-custom-checkboxes.wpsl-checkbox-3-columns li{width:32%}#wpsl-checkbox-filter.wpsl-checkbox-4-columns li,.wpsl-custom-checkboxes.wpsl-checkbox-4-columns li{width:24%}#wpsl-checkbox-filter input,.wpsl-custom-checkboxes input{margin-right:5px}#wpsl-result-list .wpsl-contact-details span{display:block!important}#wpsl-search-wrap .select2{display:none!important}.rtl #wpsl-result-list{float:left}.rtl #wpsl-checkbox-filter input,.rtl .wpsl-custom-checkboxes input{margin-right:0;margin-left:5px}.rtl .wpsl-info-actions a{float:right;margin:0 0 0 7px}.rtl #wpsl-gmap .wpsl-info-window{padding-right:22px}.rtl #wpsl-wrap #wpsl-result-list li.wpsl-preloader{padding:10px 35px 10px 0}.rtl .wpsl-preloader img{left:0;right:10px}@media (max-width:825px){#wpsl-search-input{width:348px}.wpsl-results-only #wpsl-search-wrap .wpsl-dropdown{width:70px}#wpsl-search-wrap .wpsl-input{width:100%;margin-bottom:10px}#wpsl-category label,#wpsl-radius label,.wpsl-cat-results-filter #wpsl-search-wrap .wpsl-input,.wpsl-input label,.wpsl-no-filters #wpsl-search-wrap .wpsl-input,.wpsl-results-only #wpsl-search-wrap .wpsl-input{width:auto}}@media (max-width:720px){#wpsl-search-wrap .wpsl-dropdown{width:114px}}@media (max-width:675px){#wpsl-search-wrap #wpsl-search-btn{float:left;margin:0 5px 0 0}.wpsl-dropdown,.wpsl-results-only #wpsl-search-wrap .wpsl-input{width:100%}.wpsl-search{padding:2%}#wpsl-result-list p,#wpsl-wrap #wpsl-result-list li,.wpsl-direction-before{padding-left:0}.wpsl-input{margin-right:0}#wpsl-gmap,#wpsl-result-list{float:none;width:100%}#wpsl-gmap{margin-bottom:15px;margin-top:10px}#wpsl-result-list,.wpsl-cat-results-filter .wpsl-select-wrap,.wpsl-filter .wpsl-select-wrap{margin-bottom:10px}#wpsl-wrap #wpsl-result-list li.wpsl-preloader{padding-left:25px}.wpsl-preloader img{left:0}#wpsl-stores.wpsl-not-loaded{height:25px}#wpsl-reset-map{top:25px}#wpsl-category,#wpsl-search-btn,.wpsl-input,.wpsl-no-filters #wpsl-search-wrap .wpsl-input,.wpsl-select-wrap{margin-bottom:0}#wpsl-stores.wpsl-no-autoload{height:auto!important}#wpsl-checkbox-filter.wpsl-checkbox-3-columns li,#wpsl-checkbox-filter.wpsl-checkbox-4-columns li{width:49%}}@media (max-width:570px){#wpsl-search-wrap #wpsl-search-btn{margin-bottom:5px}.wpsl-search{padding:4%}#wpsl-search-input{width:98%!important}.wpsl-cat-results-filter #wpsl-search-input,.wpsl-cat-results-filter #wpsl-search-wrap .wpsl-input,.wpsl-no-results #wpsl-search-input,.wpsl-results-only #wpsl-search-input{width:100%!important}.wpsl-search-btn-wrap{margin-top:15px;clear:both}.wpsl-checkboxes-enabled .wpsl-search-btn-wrap{margin-top:0}#wpsl-search-btn,#wpsl-search-wrap div{margin-right:0}#wpsl-search-wrap div label{display:block;width:100%}.wpsl-select-wrap{width:100%}#wpsl-radius,#wpsl-results{width:50%}#wpsl-radius{margin-right:4%}#wpsl-search-wrap .wpsl-dropdown{width:96%!important}.wpsl-no-filters #wpsl-search-input,.wpsl-no-filters #wpsl-search-wrap .wpsl-input{width:100%!important}}@media (max-width:420px){#wpsl-checkbox-filter li{margin:0}#wpsl-checkbox-filter.wpsl-checkbox-1-columns li,#wpsl-checkbox-filter.wpsl-checkbox-2-columns li,#wpsl-checkbox-filter.wpsl-checkbox-3-columns li,#wpsl-checkbox-filter.wpsl-checkbox-4-columns li{width:100%}}
1
+ #wpsl-wrap,.wpsl-gmap-canvas{margin-bottom:20px;width:100%}#wpsl-result-list a,#wpsl-wrap [class*=" wpsl-icon-"]:focus,#wpsl-wrap [class^=wpsl-icon-]:active{outline:0}#wpsl-map-controls div:hover,#wpsl-reset-map:hover,#wpsl-search-btn:hover,.wpsl-dropdown{cursor:pointer}#wpsl-wrap,.wpsl-clearfix:after,.wpsl-contact-details{clear:both}@font-face{font-family:wpsl-fontello;src:url(../font/fontello.eot?28897909);src:url(../font/fontello.eot?28897909#iefix) format('embedded-opentype'),url(../font/fontello.woff?28897909) format('woff'),url(../font/fontello.ttf?28897909) format('truetype'),url(../font/fontello.svg?28897909#fontello) format('svg');font-weight:400;font-style:normal}#wpsl-gmap{float:right;width:66.5%;height:350px;margin-bottom:0}.wpsl-store-below #wpsl-gmap{float:none;width:100%}.wpsl-gmap-canvas{height:300px}#wpsl-gmap div:not[class^=gv-iv],#wpsl-gmap img,.wpsl-gmap-canvas div:not[class^=gv-iv],.wpsl-gmap-canvas img{box-shadow:none!important;max-width:none!important;background:0 0}#wpsl-gmap img,.wpsl-gmap-canvas img{display:inline!important;opacity:1!important;max-height:none!important}#wpsl-gmap *{box-sizing:content-box!important;-webkit-box-sizing:content-box!important;-moz-box-sizing:content-box!important}#wpsl-gmap div.gm-iv-marker,.wpsl-gmap-canvas div.gm-iv-marker{backgroud-image:inherit}#wpsl-wrap{position:relative;overflow:hidden}#wpsl-search-wrap{float:left;width:100%}#wpsl-search-wrap form{margin:0;padding:0;border:none;outline:0}#wpsl-gmap #wpsl-map-controls{position:absolute;height:28px;right:10px;bottom:24px;border-radius:2px;z-index:3;font-size:11px;white-space:nowrap;overflow:hidden}#wpsl-gmap #wpsl-map-controls.wpsl-street-view-exists{right:48px}#wpsl-map-controls .wpsl-direction-preloader{margin:5px 5px 0}#wpsl-map-controls div{float:left;background:#fff;border-radius:2px}#wpsl-wrap [class*=" wpsl-icon-"],#wpsl-wrap [class^=wpsl-icon-]{position:relative;float:left;padding:7px 9px 7px 8px;display:inline-block;font-family:wpsl-fontello;font-style:normal;font-weight:400;font-size:1.3em;color:#737373;speak:none;text-decoration:inherit;text-align:center;font-variant:normal;text-transform:none;line-height:1em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#wpsl-map-controls span{font-family:inherit;font-size:inherit}#wpsl-wrap .wpsl-ie [class*=" wpsl-icon-"],#wpsl-wrap .wpsl-ie [class^=wpsl-icon-]{padding:9px 8px 4px}#wpsl-wrap.wpsl-mobile [class*=" wpsl-icon-"],#wpsl-wrap.wpsl-mobile [class^=wpsl-icon-]{padding:8px 10px}#wpsl-wrap .wpsl-icon-reset{border-radius:2px 0 0 2px;z-index:2;padding-left:9px;padding-right:4px}#wpsl-wrap .wpsl-icon-direction{z-index:1}#wpsl-map-controls.wpsl-reset-exists .wpsl-icon-direction{border-radius:0 2px 2px 0}#wpsl-wrap .wpsl-active-icon,#wpsl-wrap [class*=" wpsl-icon-"]:hover,#wpsl-wrap [class^=wpsl-icon-]:hover{color:#000}#wpsl-wrap .wpsl-in-progress,#wpsl-wrap .wpsl-in-progress:hover{color:#c6c6c6}#wpsl-gmap #wpsl-reset-map{position:absolute;display:none;right:37px;top:37px;padding:6px 14px;background:#fff!important;border:1px solid rgba(0,0,0,.15);border-radius:2px;z-index:3}.gm-style-cc{word-wrap:normal}#wpsl-search-wrap .wpsl-input,#wpsl-search-wrap .wpsl-select-wrap{display:table}#wpsl-search-btn,#wpsl-search-wrap #wpsl-radius,#wpsl-search-wrap #wpsl-results,#wpsl-search-wrap .wpsl-input input,#wpsl-search-wrap .wpsl-input label{display:table-cell}#wpsl-search-wrap label{margin-bottom:0}#wpsl-search-input{width:179px;height:auto;padding:7px 12px;font-size:100%;margin:0}#wpsl-search-btn,#wpsl-search-wrap input{border:1px solid #d2d2d2;border-radius:3px}#wpsl-search-btn{padding:7px 10px;line-height:1.428571429;font-weight:400;color:#7c7c7c;background-color:#e6e6e6;background-repeat:repeat-x;background-image:-moz-linear-gradient(top,#f4f4f4,#e6e6e6);background-image:-ms-linear-gradient(top,#f4f4f4,#e6e6e6);background-image:-webkit-linear-gradient(top,#f4f4f4,#e6e6e6);background-image:-o-linear-gradient(top,#f4f4f4,#e6e6e6);background-image:linear-gradient(top,#f4f4f4,#e6e6e6);box-shadow:0 1px 2px rgba(64,64,64,.1);text-transform:none!important}#wpsl-search-input.wpsl-error{border:1px solid #bd0028!important}.wpsl-search{margin-bottom:12px;padding:12px 12px 0;background:#f4f3f3}.wpsl-search.wpsl-checkboxes-enabled{padding:12px}.wpsl-back{display:inline-block}#wpsl-result-list{width:33%;margin-right:.5%}.wpsl-store-below #wpsl-result-list{width:100%;margin:12px 0 0}#wpsl-direction-details,#wpsl-stores{height:350px;overflow-y:auto}#wpsl-direction-details,.wpsl-hide{display:none}#wpsl-result-list p{padding-left:10px}.wpsl-store-below #wpsl-result-list p{padding-left:0}.wpsl-direction-before{margin:14px 0 21px;padding-left:10px}.wpsl-store-below .wpsl-direction-before{padding-left:0}.wpsl-direction-before div{margin-top:10px}#wpsl-wrap #wpsl-result-list li{padding:10px;border-bottom:1px dotted #ccc;margin-left:0;overflow:hidden;list-style:none!important;text-indent:0}#wpsl-wrap #wpsl-result-list li li{padding:0;border-bottom:0;margin-left:14px;overflow:visible}#wpsl-wrap #wpsl-result-list ul li{list-style:none!important}#wpsl-wrap #wpsl-result-list ol li{list-style:decimal!important}#wpsl-wrap.wpsl-store-below #wpsl-result-list li{padding:10px 10px 10px 0}#wpsl-result-list li p{padding-left:0;margin:0 0 20px}.wpsl-store-details.wpsl-store-listing{position:relative;padding-right:20px}.wpsl-store-details.wpsl-store-listing.wpsl-active-details:before,.wpsl-store-details.wpsl-store-listing:before{position:absolute;content:'';bottom:6px;right:0;border-top:5px solid #000;border-left:6px solid transparent;border-right:6px solid transparent}.wpsl-store-details.wpsl-store-listing.wpsl-active-details:before{border-bottom:5px solid #000;border-top:none;border-left:6px solid transparent;border-right:6px solid transparent}#wpsl-stores .wpsl-store-thumb{float:right;border-radius:3px;margin:7px 0 0 10px;padding:0;border:none}.wpsl-direction-index{float:left;width:8%;margin:0 5% 0 0}.wpsl-direction-txt{float:left;width:62%}.wpsl-direction-distance{float:left;width:20%;margin:0 0 0 5%}.wpsl-direction-txt span{display:block;margin-top:10px}.wpsl-country,.wpsl-street{display:block;border-bottom:none!important}.wpsl-directions{display:table;border-bottom:none!important}#wpsl-wrap #wpsl-result-list li.wpsl-preloader{position:relative;border-bottom:none;padding:10px 10px 10px 35px}.wpsl-preloader img{position:absolute;left:10px;top:50%;margin-top:-8px;box-shadow:none!important;border:none!important}.wpsl-preloader span{float:left;margin:-5px 0 0 11px}#wpsl-search-btn,#wpsl-search-wrap div{margin-right:10px;float:left}#wpsl-search-wrap .wpsl-select-wrap{position:relative;z-index:2;margin-right:0}#wpsl-search-wrap .wpsl-input-field{position:relative}#wpsl-radius,#wpsl-results{float:left;margin-right:15px;display:inline}#wpsl-category{z-index:1;clear:both}#wpsl-search-wrap .wpsl-dropdown div{position:absolute;float:none;margin:-1px 0 0;top:100%;left:-1px;right:-1px;border:1px solid #ccc;background:#fff;border-top:1px solid #eee;border-radius:0 0 3px 3px;opacity:0;overflow:hidden;-webkit-transition:all 150ms ease-in-out;-moz-transition:all 150ms ease-in-out;-ms-transition:all 150ms ease-in-out;transition:all 150ms ease-in-out}#wpsl-search-wrap .wpsl-dropdown.wpsl-active div{opacity:1}#wpsl-search-wrap .wpsl-input label{margin-right:0}#wpsl-radius{margin-right:10px}#wpsl-search select,#wpsl-search-wrap select,.wpsl-direction-details{display:none}#wpsl-search-wrap div label{float:left;margin-right:10px;line-height:32px}#wpsl-results label{width:auto}#wpsl-result-list ul{list-style:none;margin:0;padding:0}#wpsl-gmap .wpsl-info-window,.wpsl-gmap-canvas .wpsl-info-window{max-width:225px}.wpsl-info-window span,.wpsl-more-info-listings span{display:block}.wpsl-info-window .wpsl-no-margin{margin:0}.wpsl-more-info-listings{display:none}.wpsl-info-window span span{display:inline!important}#wpsl-wrap .wpsl-info-window p{margin:0 0 10px}.wpsl-store-hours{margin-top:10px}.wpsl-store-hours strong{display:block}#wpsl-gmap .wpsl-info-actions{display:block;margin:10px 0!important}.wpsl-info-actions a{float:left;margin-right:7px}.wpsl-info-actions .wpsl-zoom-here{margin-right:0}.wpsl-dropdown{position:relative;width:90px;border:1px solid #ccc;background:#fff;border-radius:3px;-webkit-user-select:none;-moz-user-select:none;user-select:none;margin-right:0!important;z-index:2}.wpsl-dropdown ul{position:absolute;left:0;width:100%;height:100%;padding:0!important;margin:0!important;list-style:none;overflow:hidden}.wpsl-dropdown:hover{box-shadow:0 0 5px rgba(0,0,0,.15)}.wpsl-dropdown .wpsl-selected-item,.wpsl-dropdown li{position:relative;display:block;line-height:normal;color:#000;overflow:hidden}#wpsl-radius .wpsl-dropdown .wpsl-selected-item,#wpsl-radius .wpsl-dropdown li,#wpsl-results .wpsl-dropdown .wpsl-selected-item,#wpsl-results .wpsl-dropdown li{white-space:nowrap}.wpsl-selected-item:after{position:absolute;content:"";right:12px;top:50%;margin-top:-4px;border:6px solid transparent;border-top:8px solid #000}.wpsl-active .wpsl-selected-item:after{margin-top:-10px;border:6px solid transparent;border-bottom:8px solid #000}.wpsl-dropdown li:hover{background:#f8f9f8;position:relative;z-index:3;color:#000}.wpsl-dropdown .wpsl-selected-item,.wpsl-dropdown li,.wpsl-selected-item{list-style:none;padding:9px 12px!important;margin:0!important}.wpsl-selected-dropdown{font-weight:700}.wpsl-clearfix:after,.wpsl-clearfix:before{content:" ";display:table}#wpsl-wrap .wpsl-selected-item{position:static;padding-right:35px!important}#wpsl-category,.wpsl-input,.wpsl-select-wrap{position:relative;margin-bottom:10px}#wpsl-search-wrap .wpsl-scroll-required div{overflow-y:scroll}.wpsl-scroll-required ul{overflow:visible}.wpsl-provided-by{float:right;padding:5px 0;text-align:right;font-size:12px;width:100%}#wpsl-wrap .wpsl-results-only label{width:auto}.wpsl-contact-details,.wpsl-location-address,.wpsl-locations-details{margin-bottom:15px}table.wpsl-opening-hours td{vertical-align:top;padding:0 15px 0 0;text-align:left}table.wpsl-opening-hours time{display:block}table.wpsl-opening-hours{width:auto!important;font-size:100%!important}table.wpsl-opening-hours,table.wpsl-opening-hours td{border:none!important}.wpsl-gmap-canvas .wpsl-infobox{min-width:155px;max-width:350px!important;padding:10px;border-radius:4px;font-size:13px;font-weight:300;border:1px solid #ccc;background:#fff!important}.wpsl-gmap-canvas .wpsl-infobox:after,.wpsl-gmap-canvas .wpsl-infobox:before{position:absolute;content:"";left:40px;bottom:-11px}.wpsl-gmap-canvas .wpsl-infobox:after{border-left:11px solid transparent;border-right:11px solid transparent;border-top:11px solid #fff}.wpsl-gmap-canvas .wpsl-infobox:before{border-left:13px solid transparent;border-right:13px solid transparent;border-top:13px solid #ccc;bottom:-13px;left:38px}#wpsl-checkbox-filter,.wpsl-custom-checkboxes{display:block;float:left;margin:5px 0 15px;padding:0;width:100%}#wpsl-checkbox-filter li,.wpsl-custom-checkboxes li{float:left;list-style:none;margin:0 1% 0 0}#wpsl-checkbox-filter.wpsl-checkbox-1-columns li,.wpsl-custom-checkboxes.wpsl-checkbox-1-columns li{width:99%}#wpsl-checkbox-filter.wpsl-checkbox-2-columns li,.wpsl-custom-checkboxes.wpsl-checkbox-2-columns li{width:49%}#wpsl-checkbox-filter.wpsl-checkbox-3-columns li,.wpsl-custom-checkboxes.wpsl-checkbox-3-columns li{width:32%}#wpsl-checkbox-filter.wpsl-checkbox-4-columns li,.wpsl-custom-checkboxes.wpsl-checkbox-4-columns li{width:24%}#wpsl-checkbox-filter input,.wpsl-custom-checkboxes input{margin-right:5px}#wpsl-result-list .wpsl-contact-details span{display:block!important}#wpsl-search-wrap .select2{display:none!important}.rtl #wpsl-result-list{float:left}.rtl #wpsl-checkbox-filter input,.rtl .wpsl-custom-checkboxes input{margin-right:0;margin-left:5px}.rtl .wpsl-info-actions a{float:right;margin:0 0 0 7px}.rtl #wpsl-gmap .wpsl-info-window{padding-right:22px}.rtl #wpsl-wrap #wpsl-result-list li.wpsl-preloader{padding:10px 35px 10px 0}.rtl .wpsl-preloader img{left:0;right:10px}.wpsl-twentynineteen .wpsl-input{width:100%}.wpsl-twentynineteen #wpsl-search-input{line-height:1.3em}.wpsl-twentynineteen #wpsl-search-wrap label{margin-top:6px}.wpsl-twentynineteen .wpsl-dropdown{width:116px}#wpsl-results .wpsl-dropdown{width:81px}@media (max-width:825px){#wpsl-search-input{width:348px}.wpsl-results-only #wpsl-search-wrap .wpsl-dropdown{width:70px}#wpsl-search-wrap .wpsl-input{width:100%;margin-bottom:10px}#wpsl-category label,#wpsl-radius label,.wpsl-cat-results-filter #wpsl-search-wrap .wpsl-input,.wpsl-input label,.wpsl-no-filters #wpsl-search-wrap .wpsl-input,.wpsl-results-only #wpsl-search-wrap .wpsl-input{width:auto}}@media (max-width:720px){#wpsl-search-wrap .wpsl-dropdown{width:114px}}@media (max-width:675px){#wpsl-search-wrap #wpsl-search-btn{float:left;margin:0 5px 0 0}.wpsl-dropdown,.wpsl-results-only #wpsl-search-wrap .wpsl-input{width:100%}.wpsl-search{padding:2%}#wpsl-result-list p,#wpsl-wrap #wpsl-result-list li,.wpsl-direction-before{padding-left:0}.wpsl-input{margin-right:0}#wpsl-gmap,#wpsl-result-list{float:none;width:100%}#wpsl-gmap{margin-bottom:15px;margin-top:10px}#wpsl-result-list,.wpsl-cat-results-filter .wpsl-select-wrap,.wpsl-filter .wpsl-select-wrap{margin-bottom:10px}#wpsl-wrap #wpsl-result-list li.wpsl-preloader{padding-left:25px}.wpsl-preloader img{left:0}#wpsl-stores.wpsl-not-loaded{height:25px}#wpsl-reset-map{top:25px}#wpsl-category,#wpsl-search-btn,.wpsl-input,.wpsl-no-filters #wpsl-search-wrap .wpsl-input,.wpsl-select-wrap{margin-bottom:0}#wpsl-stores.wpsl-no-autoload{height:auto!important}#wpsl-checkbox-filter.wpsl-checkbox-3-columns li,#wpsl-checkbox-filter.wpsl-checkbox-4-columns li{width:49%}}@media (max-width:570px){#wpsl-search-wrap #wpsl-search-btn{margin-bottom:5px}.wpsl-search{padding:4%}#wpsl-search-input{width:98%!important}.wpsl-cat-results-filter #wpsl-search-input,.wpsl-cat-results-filter #wpsl-search-wrap .wpsl-input,.wpsl-no-results #wpsl-search-input,.wpsl-results-only #wpsl-search-input{width:100%!important}.wpsl-search-btn-wrap{margin-top:15px;clear:both}.wpsl-checkboxes-enabled .wpsl-search-btn-wrap{margin-top:0}#wpsl-search-btn,#wpsl-search-wrap div{margin-right:0}#wpsl-search-wrap div label{display:block;width:100%}.wpsl-select-wrap{width:100%}#wpsl-radius,#wpsl-results{width:50%}#wpsl-radius{margin-right:4%}#wpsl-search-wrap .wpsl-dropdown{width:96%!important}.wpsl-no-filters #wpsl-search-input,.wpsl-no-filters #wpsl-search-wrap .wpsl-input{width:100%!important}}@media (max-width:420px){#wpsl-checkbox-filter li{margin:0}#wpsl-checkbox-filter.wpsl-checkbox-1-columns li,#wpsl-checkbox-filter.wpsl-checkbox-2-columns li,#wpsl-checkbox-filter.wpsl-checkbox-3-columns li,#wpsl-checkbox-filter.wpsl-checkbox-4-columns li{width:100%}}
frontend/class-frontend.php CHANGED
@@ -1,1867 +1,1887 @@
1
- <?php
2
- /**
3
- * Frontend class
4
- *
5
- * @author Tijmen Smit
6
- * @since 1.0.0
7
- */
8
-
9
- if ( !defined( 'ABSPATH' ) ) exit;
10
-
11
- if ( !class_exists( 'WPSL_Frontend' ) ) {
12
-
13
- /**
14
- * Handle the frontend of the store locator
15
- *
16
- * @since 1.0.0
17
- */
18
- class WPSL_Frontend {
19
-
20
- /**
21
- * Keep track which scripts we need to load
22
- *
23
- * @since 2.0.0
24
- */
25
- private $load_scripts = array();
26
-
27
- /**
28
- * Keep track of the amount of maps on the page
29
- *
30
- * @since 2.0.0
31
- */
32
- private static $map_count = 0;
33
-
34
- /*
35
- * Holds the shortcode atts for the [wpsl] shortcode.
36
- *
37
- * Used to overwrite the settings just before
38
- * they are send to wp_localize_script.
39
- *
40
- * @since 2.1.1
41
- */
42
- public $sl_shortcode_atts;
43
-
44
- private $store_map_data = array();
45
-
46
-
47
- /**
48
- * Class constructor
49
- */
50
- public function __construct() {
51
-
52
- $this->includes();
53
-
54
- add_action( 'wp_ajax_store_search', array( $this, 'store_search' ) );
55
- add_action( 'wp_ajax_nopriv_store_search', array( $this, 'store_search' ) );
56
- add_action( 'wp_enqueue_scripts', array( $this, 'add_frontend_styles' ) );
57
- add_action( 'wp_footer', array( $this, 'add_frontend_scripts' ) );
58
-
59
- add_filter( 'the_content', array( $this, 'cpt_template' ) );
60
-
61
- add_shortcode( 'wpsl', array( $this, 'show_store_locator' ) );
62
- add_shortcode( 'wpsl_address', array( $this, 'show_store_address' ) );
63
- add_shortcode( 'wpsl_hours', array( $this, 'show_opening_hours' ) );
64
- add_shortcode( 'wpsl_map', array( $this, 'show_store_map' ) );
65
- }
66
-
67
- /**
68
- * Include the required front-end files.
69
- *
70
- * @since 2.0.0
71
- * @return void
72
- */
73
- public function includes() {
74
- require_once( WPSL_PLUGIN_DIR . 'frontend/underscore-functions.php' );
75
- }
76
-
77
- /**
78
- * Handle the Ajax search on the frontend.
79
- *
80
- * @since 1.0.0
81
- * @return json A list of store locations that are located within the selected search radius
82
- */
83
- public function store_search() {
84
-
85
- global $wpsl_settings;
86
-
87
- /*
88
- * Check if auto loading the locations on page load is enabled.
89
- *
90
- * If so then we save the store data in a transient to prevent a long loading time
91
- * in case a large amount of locations need to be displayed.
92
- *
93
- * The SQL query that selects nearby locations doesn't take that long,
94
- * but collecting all the store meta data in get_store_meta_data() for hunderds,
95
- * or thousands of stores can make it really slow.
96
- */
97
- if ( $wpsl_settings['autoload'] && isset( $_GET['autoload'] ) && $_GET['autoload'] && !$wpsl_settings['debug'] && !isset( $_GET['skip_cache'] ) ) {
98
- $transient_name = $this->create_transient_name();
99
-
100
- if ( false === ( $store_data = get_transient( 'wpsl_autoload_' . $transient_name ) ) ) {
101
- $store_data = $this->find_nearby_locations();
102
-
103
- if ( $store_data ) {
104
- set_transient( 'wpsl_autoload_' . $transient_name, $store_data, 0 );
105
- }
106
- }
107
- } else {
108
- $store_data = $this->find_nearby_locations();
109
- }
110
-
111
- do_action( 'wpsl_store_search' );
112
-
113
- wp_send_json( $store_data );
114
-
115
- exit();
116
- }
117
-
118
- /**
119
- * Create the name used in the wpsl autoload transient.
120
- *
121
- * @since 2.1.1
122
- * @return string $transient_name The transient name.
123
- */
124
- public function create_transient_name() {
125
-
126
- global $wpsl, $wpsl_settings;
127
-
128
- $name_section = array();
129
-
130
- // Include the set autoload limit.
131
- if ( $wpsl_settings['autoload'] && $wpsl_settings['autoload_limit'] ) {
132
- $name_section[] = absint( $wpsl_settings['autoload_limit'] );
133
- }
134
-
135
- /*
136
- * Check if we need to include the cat id(s) in the transient name.
137
- *
138
- * This can only happen if the user used the
139
- * 'category' attr on the wpsl shortcode.
140
- */
141
- if ( isset( $_GET['filter'] ) && $_GET['filter'] ) {
142
- $name_section[] = absint( str_replace( ',', '', $_GET['filter'] ) );
143
- }
144
-
145
- // Include the lat value from the start location.
146
- if ( isset( $_GET['lat'] ) && $_GET['lat'] ) {
147
- $name_section[] = absint( str_replace( '.', '', $_GET['lat'] ) );
148
- }
149
-
150
- /*
151
- * If a multilingual plugin ( WPML or qTranslate X ) is active then we have
152
- * to make sure each language has his own unique transient. We do this by
153
- * including the lang code in the transient name.
154
- *
155
- * Otherwise if the language is for example set to German on page load,
156
- * and the user switches to Spanish, then he would get the incorrect
157
- * permalink structure ( /de/.. instead or /es/.. ) and translated
158
- * store details.
159
- */
160
- $lang_code = $wpsl->i18n->check_multilingual_code();
161
-
162
- if ( $lang_code ) {
163
- $name_section[] = $lang_code;
164
- }
165
-
166
- $transient_name = implode( '_', $name_section );
167
-
168
- /*
169
- * If the distance unit filter ( wpsl_distance_unit ) is used to change the km / mi unit based on
170
- * the location of the IP, then we include the km / mi in the transient name. This is done to
171
- * prevent users from seeing the wrong distances from the cached data.
172
- *
173
- * This way one data set can include the distance in km, and the other one the distance in miles.
174
- */
175
- if ( has_filter( 'wpsl_distance_unit' ) ) {
176
- $transient_name = $transient_name . '_' . wpsl_get_distance_unit();
177
- }
178
-
179
- return $transient_name;
180
- }
181
-
182
- /**
183
- * Find store locations that are located within the selected search radius.
184
- *
185
- * This happens by calculating the distance between the
186
- * latlng of the searched location, and the latlng from
187
- * the stores in the db.
188
- *
189
- * @since 2.0.0
190
- * @param array $args The arguments to use in the SQL query, only used by add-ons
191
- * @return void|array $store_data The list of stores that fall within the selected range.
192
- */
193
- public function find_nearby_locations( $args = array() ) {
194
-
195
- global $wpdb, $wpsl, $wpsl_settings;
196
-
197
- $store_data = array();
198
-
199
- /*
200
- * Set the correct earth radius in either km or miles.
201
- * We need this to calculate the distance between two coordinates.
202
- */
203
- $placeholder_values[] = ( wpsl_get_distance_unit() == 'km' ) ? 6371 : 3959;
204
-
205
- // The placeholder values for the prepared statement in the SQL query.
206
- if ( empty( $args ) ) {
207
- $args = $_GET;
208
- }
209
-
210
- array_push( $placeholder_values, $args['lat'], $args['lng'], $args['lat'] );
211
-
212
- // Check if we need to filter the results by category.
213
- if ( isset( $args['filter'] ) && $args['filter'] ) {
214
- $filter_ids = array_map( 'absint', explode( ',', $args['filter'] ) );
215
- $cat_filter = "INNER JOIN $wpdb->term_relationships AS term_rel ON posts.ID = term_rel.object_id
216
- INNER JOIN $wpdb->term_taxonomy AS term_tax ON term_rel.term_taxonomy_id = term_tax.term_taxonomy_id
217
- AND term_tax.taxonomy = 'wpsl_store_category'
218
- AND term_tax.term_id IN (" . implode( ',', $filter_ids ) . ")";
219
- } else {
220
- $cat_filter = '';
221
- }
222
-
223
- /*
224
- * If WPML is active we include 'GROUP BY lat' in the sql query
225
- * to prevent duplicate locations from showing up in the results.
226
- *
227
- * This is a problem when a store location for example
228
- * exists in 4 different languages. They would all fall within
229
- * the selected radius, but we only need one store ID for the 'icl_object_id'
230
- * function to get the correct store ID for the current language.
231
- */
232
- if ( $wpsl->i18n->wpml_exists() ) {
233
- $group_by = 'GROUP BY lat';
234
- } else {
235
- $group_by = 'GROUP BY posts.ID';
236
- }
237
-
238
- /*
239
- * If autoload is enabled we need to check if there is a limit to the
240
- * amount of locations we need to show.
241
- *
242
- * Otherwise include the radius and max results limit in the sql query.
243
- */
244
- if ( isset( $args['autoload'] ) && $args['autoload'] ) {
245
- $limit = '';
246
-
247
- if ( $wpsl_settings['autoload_limit'] ) {
248
- $limit = 'LIMIT %d';
249
- $placeholder_values[] = $wpsl_settings['autoload_limit'];
250
- }
251
-
252
- $sql_sort = 'ORDER BY distance '. $limit;
253
- } else {
254
- array_push( $placeholder_values, $this->check_store_filter( $args, 'search_radius' ), $this->check_store_filter( $args, 'max_results' ) );
255
- $sql_sort = 'HAVING distance < %d ORDER BY distance LIMIT 0, %d';
256
- }
257
-
258
- $placeholder_values = apply_filters( 'wpsl_sql_placeholder_values', $placeholder_values );
259
-
260
- /*
261
- * The sql that will check which store locations fall within
262
- * the selected radius based on the lat and lng values.
263
- */
264
- $sql = apply_filters( 'wpsl_sql',
265
- "SELECT post_lat.meta_value AS lat,
266
- post_lng.meta_value AS lng,
267
- posts.ID,
268
- ( %d * acos( cos( radians( %s ) ) * cos( radians( post_lat.meta_value ) ) * cos( radians( post_lng.meta_value ) - radians( %s ) ) + sin( radians( %s ) ) * sin( radians( post_lat.meta_value ) ) ) )
269
- AS distance
270
- FROM $wpdb->posts AS posts
271
- INNER JOIN $wpdb->postmeta AS post_lat ON post_lat.post_id = posts.ID AND post_lat.meta_key = 'wpsl_lat'
272
- INNER JOIN $wpdb->postmeta AS post_lng ON post_lng.post_id = posts.ID AND post_lng.meta_key = 'wpsl_lng'
273
- $cat_filter
274
- WHERE posts.post_type = 'wpsl_stores'
275
- AND posts.post_status = 'publish' $group_by $sql_sort"
276
- );
277
-
278
- $stores = $wpdb->get_results( $wpdb->prepare( $sql, $placeholder_values ) );
279
-
280
- if ( $stores ) {
281
- $store_data = apply_filters( 'wpsl_store_data', $this->get_store_meta_data( $stores ) );
282
- } else {
283
- $store_data = apply_filters( 'wpsl_no_results_sql', '' );
284
- }
285
-
286
- return $store_data;
287
- }
288
-
289
- /**
290
- * Get the post meta data for the selected stores.
291
- *
292
- * @since 2.0.0
293
- * @param object $stores
294
- * @return array $all_stores The stores that fall within the selected range with the post meta data.
295
- */
296
- public function get_store_meta_data( $stores ) {
297
-
298
- global $wpsl_settings, $wpsl;
299
-
300
- $all_stores = array();
301
-
302
- // Get the list of store fields that we need to filter out of the post meta data.
303
- $meta_field_map = $this->frontend_meta_fields();
304
-
305
- foreach ( $stores as $store_key => $store ) {
306
-
307
- // If WPML is active try to get the id of the translated page.
308
- if ( $wpsl->i18n->wpml_exists() ) {
309
- $store->ID = $wpsl->i18n->maybe_get_wpml_id( $store->ID );
310
-
311
- if ( !$store->ID ) {
312
- continue;
313
- }
314
- }
315
-
316
- // Get the post meta data for each store that was within the range of the search radius.
317
- $custom_fields = get_post_custom( $store->ID );
318
-
319
- foreach ( $meta_field_map as $meta_key => $meta_value ) {
320
-
321
- if ( isset( $custom_fields[$meta_key][0] ) ) {
322
- if ( ( isset( $meta_value['type'] ) ) && ( !empty( $meta_value['type'] ) ) ) {
323
- $meta_type = $meta_value['type'];
324
- } else {
325
- $meta_type = '';
326
- }
327
-
328
- // If we need to hide the opening hours, and the current meta type is set to hours we skip it.
329
- if ( $wpsl_settings['hide_hours'] && $meta_type == 'hours' ) {
330
- continue;
331
- }
332
-
333
- // Make sure the data is safe to use on the frontend and in the format we expect it to be.
334
- switch ( $meta_type ) {
335
- case 'numeric':
336
- $meta_data = ( is_numeric( $custom_fields[$meta_key][0] ) ) ? $custom_fields[$meta_key][0] : 0 ;
337
- break;
338
- case 'email':
339
- $meta_data = sanitize_email( $custom_fields[$meta_key][0] );
340
- break;
341
- case 'url':
342
- $meta_data = esc_url( $custom_fields[$meta_key][0] );
343
- break;
344
- case 'hours':
345
- $meta_data = $this->get_opening_hours( $custom_fields[$meta_key][0], apply_filters( 'wpsl_hide_closed_hours', false ) );
346
- break;
347
- case 'wp_editor':
348
- case 'textarea':
349
- $meta_data = wp_kses_post( wpautop( $custom_fields[$meta_key][0] ) );
350
- break;
351
- case 'text':
352
- default:
353
- $meta_data = sanitize_text_field( stripslashes( $custom_fields[$meta_key][0] ) );
354
- break;
355
- }
356
-
357
- $store_meta[$meta_value['name']] = $meta_data;
358
- } else {
359
- $store_meta[$meta_value['name']] = '';
360
- }
361
-
362
- /*
363
- * Include the post content if the "More info" option is enabled on the settings page,
364
- * or if $include_post_content is set to true through the 'wpsl_include_post_content' filter.
365
- */
366
- if ( ( $wpsl_settings['more_info'] && $wpsl_settings['more_info_location'] == 'store listings' ) || apply_filters( 'wpsl_include_post_content', false ) ) {
367
- $page_object = get_post( $store->ID );
368
- $store_meta['description'] = apply_filters( 'the_content', strip_shortcodes( $page_object->post_content ) );
369
- }
370
-
371
- $store_meta['store'] = get_the_title( $store->ID );
372
- $store_meta['thumb'] = $this->get_store_thumb( $store->ID, $store_meta['store'] );
373
- $store_meta['id'] = $store->ID;
374
-
375
- if ( !$wpsl_settings['hide_distance'] ) {
376
- $store_meta['distance'] = round( $store->distance, 1 );
377
- }
378
-
379
- if ( $wpsl_settings['permalinks'] ) {
380
- $store_meta['permalink'] = get_permalink( $store->ID );
381
- }
382
- }
383
-
384
- $all_stores[] = apply_filters( 'wpsl_store_meta', $store_meta, $store->ID );
385
- }
386
-
387
- return $all_stores;
388
- }
389
-
390
- /**
391
- * The store meta fields that are included in the json output.
392
- *
393
- * The wpsl_ is the name in db, the name value is used as the key in the json output.
394
- *
395
- * The type itself is used to determine how the value should be sanitized.
396
- * Text will go through sanitize_text_field, email through sanitize_email and so on.
397
- *
398
- * If no type is set it will default to sanitize_text_field.
399
- *
400
- * @since 2.0.0
401
- * @return array $store_fields The names of the meta fields used by the store
402
- */
403
- public function frontend_meta_fields() {
404
-
405
- $store_fields = array(
406
- 'wpsl_address' => array(
407
- 'name' => 'address'
408
- ),
409
- 'wpsl_address2' => array(
410
- 'name' => 'address2'
411
- ),
412
- 'wpsl_city' => array(
413
- 'name' => 'city'
414
- ),
415
- 'wpsl_state' => array(
416
- 'name' => 'state'
417
- ),
418
- 'wpsl_zip' => array(
419
- 'name' => 'zip'
420
- ),
421
- 'wpsl_country' => array(
422
- 'name' => 'country'
423
- ),
424
- 'wpsl_lat' => array(
425
- 'name' => 'lat',
426
- 'type' => 'numeric'
427
- ),
428
- 'wpsl_lng' => array(
429
- 'name' => 'lng',
430
- 'type' => 'numeric'
431
- ),
432
- 'wpsl_phone' => array(
433
- 'name' => 'phone'
434
- ),
435
- 'wpsl_fax' => array(
436
- 'name' => 'fax'
437
- ),
438
- 'wpsl_email' => array(
439
- 'name' => 'email',
440
- 'type' => 'email'
441
- ),
442
- 'wpsl_hours' => array(
443
- 'name' => 'hours',
444
- 'type' => 'hours'
445
- ),
446
- 'wpsl_url' => array(
447
- 'name' => 'url',
448
- 'type' => 'url'
449
- )
450
- );
451
-
452
- return apply_filters( 'wpsl_frontend_meta_fields', $store_fields );
453
- }
454
-
455
- /**
456
- * Get the store thumbnail.
457
- *
458
- * @since 2.0.0
459
- * @param string $post_id The post id of the store
460
- * @param string $store_name The name of the store
461
- * @return void|string $thumb The html img tag
462
- */
463
- public function get_store_thumb( $post_id, $store_name ) {
464
-
465
- $attr = array(
466
- 'class' => 'wpsl-store-thumb',
467
- 'alt' => $store_name
468
- );
469
-
470
- $thumb = get_the_post_thumbnail( $post_id, $this->get_store_thumb_size(), apply_filters( 'wpsl_thumb_attr', $attr ) );
471
-
472
- return $thumb;
473
- }
474
-
475
- /**
476
- * Get the store thumbnail size.
477
- *
478
- * @since 2.0.0
479
- * @return array $size The thumb format
480
- */
481
- public function get_store_thumb_size() {
482
-
483
- $size = apply_filters( 'wpsl_thumb_size', array( 45, 45 ) );
484
-
485
- return $size;
486
- }
487
-
488
- /**
489
- * Get the opening hours in the correct format.
490
- *
491
- * Either convert the hour values that are set through
492
- * a dropdown to a table, or wrap the textarea input in a <p>.
493
- *
494
- * Note: The opening hours can only be set in the textarea format by users who upgraded from 1.x.
495
- *
496
- * @since 2.0.0
497
- * @param array|string $hours The opening hours
498
- * @param boolean $hide_closed Hide the days were the location is closed
499
- * @return string $hours The formated opening hours
500
- */
501
- public function get_opening_hours( $hours, $hide_closed ) {
502
-
503
- $hours = maybe_unserialize( $hours );
504
-
505
- /*
506
- * If the hours are set through the dropdown then we create a table for the opening hours.
507
- * Otherwise we output the data entered in the textarea.
508
- */
509
- if ( is_array( $hours ) ) {
510
- $hours = $this->create_opening_hours_tabel( $hours, $hide_closed );
511
- } else {
512
- $hours = wp_kses_post( wpautop( $hours ) );
513
- }
514
-
515
- return $hours;
516
- }
517
-
518
- /**
519
- * Create a table for the opening hours.
520
- *
521
- * @since 2.0.0
522
- * @todo add schema.org support.
523
- * @param array $hours The opening hours
524
- * @param boolean $hide_closed Hide the days where the location is closed
525
- * @return string $hour_table The opening hours sorted in a table
526
- */
527
- public function create_opening_hours_tabel( $hours, $hide_closed ) {
528
-
529
- $opening_days = wpsl_get_weekdays();
530
-
531
- // Make sure that we have actual opening hours, and not every day is empty.
532
- if ( $this->not_always_closed( $hours ) ) {
533
- $hour_table = '<table class="wpsl-opening-hours">';
534
-
535
- foreach ( $opening_days as $index => $day ) {
536
- $i = 0;
537
- $hour_count = count( $hours[$index] );
538
-
539
- // If we need to hide days that are set to closed then skip them.
540
- if ( $hide_closed && !$hour_count ) {
541
- continue;
542
- }
543
-
544
- $hour_table .= '<tr>';
545
- $hour_table .= '<td>' . esc_html( $day ) . '</td>';
546
-
547
- // If we have opening hours we show them, otherwise just show 'Closed'.
548
- if ( $hour_count > 0 ) {
549
- $hour_table .= '<td>';
550
-
551
- while ( $i < $hour_count ) {
552
- $hour = explode( ',', $hours[$index][$i] );
553
- $hour_table .= '<time>' . esc_html( $hour[0] ) . ' - ' . esc_html( $hour[1] ) . '</time>';
554
-
555
- $i++;
556
- }
557
-
558
- $hour_table .= '</td>';
559
- } else {
560
- $hour_table .= '<td>' . __( 'Closed', 'wpsl' ) . '</td>';
561
- }
562
-
563
- $hour_table .= '</tr>';
564
- }
565
-
566
- $hour_table .= '</table>';
567
-
568
- return $hour_table;
569
- }
570
- }
571
-
572
- /**
573
- * Create the wpsl post type output.
574
- *
575
- * If you want to create a custom template you need to
576
- * create a single-wpsl_stores.php file in your theme folder.
577
- * You can see an example here https://wpstorelocator.co/document/create-custom-store-page-template/
578
- *
579
- * @since 2.0.0
580
- * @param string $content
581
- * @return string $content
582
- */
583
- public function cpt_template( $content ) {
584
-
585
- global $wpsl_settings, $post;
586
-
587
- $skip_cpt_template = apply_filters( 'wpsl_skip_cpt_template', false );
588
-
589
- if ( isset( $post->post_type ) && $post->post_type == 'wpsl_stores' && is_single() && in_the_loop() && !$skip_cpt_template ) {
590
- array_push( $this->load_scripts, 'wpsl_base' );
591
-
592
- $content .= '[wpsl_map]';
593
- $content .= '[wpsl_address]';
594
-
595
- if ( !$wpsl_settings['hide_hours'] ) {
596
- $content .= '[wpsl_hours]';
597
- }
598
- }
599
-
600
- return $content;
601
- }
602
-
603
- /**
604
- * Handle the [wpsl] shortcode attributes.
605
- *
606
- * @since 2.1.1
607
- * @param array $atts Shortcode attributes
608
- */
609
- public function check_sl_shortcode_atts( $atts ) {
610
-
611
- /*
612
- * Use a custom start location?
613
- *
614
- * If the provided location fails to geocode,
615
- * then the start location from the settings page is used.
616
- */
617
- if ( isset( $atts['start_location'] ) && $atts['start_location'] ) {
618
- $start_latlng = wpsl_check_latlng_transient( $atts['start_location'] );
619
-
620
- if ( isset( $start_latlng ) && $start_latlng ) {
621
- $this->sl_shortcode_atts['js']['startLatlng'] = $start_latlng;
622
- }
623
- }
624
-
625
- if ( isset( $atts['auto_locate'] ) && $atts['auto_locate'] ) {
626
- $this->sl_shortcode_atts['js']['autoLocate'] = ( $atts['auto_locate'] == 'true' ) ? 1 : 0;
627
- }
628
-
629
- // Change the category slugs into category ids.
630
- if ( isset( $atts['category'] ) && $atts['category'] ) {
631
- $term_ids = wpsl_get_term_ids( $atts['category'] );
632
-
633
- if ( $term_ids ) {
634
- $this->sl_shortcode_atts['js']['categoryIds'] = implode( ',', $term_ids );
635
- }
636
- }
637
-
638
- if ( isset( $atts['category_selection'] ) && $atts['category_selection'] ) {
639
- $this->sl_shortcode_atts['category_selection'] = wpsl_get_term_ids( $atts['category_selection'] );
640
- }
641
-
642
- if ( isset( $atts['category_filter_type'] ) && in_array( $atts['category_filter_type'], array( 'dropdown', 'checkboxes' ) ) ) {
643
- $this->sl_shortcode_atts['category_filter_type'] = $atts['category_filter_type'];
644
- }
645
-
646
- if ( isset( $atts['checkbox_columns'] ) && is_numeric( $atts['checkbox_columns'] ) ) {
647
- $this->sl_shortcode_atts['checkbox_columns'] = $atts['checkbox_columns'];
648
- }
649
-
650
- if ( isset( $atts['map_type'] ) && array_key_exists( $atts['map_type'], wpsl_get_map_types() ) ) {
651
- $this->sl_shortcode_atts['js']['mapType'] = $atts['map_type'];
652
- }
653
-
654
- if ( isset( $atts['start_marker'] ) && $atts['start_marker'] ) {
655
- $this->sl_shortcode_atts['js']['startMarker'] = $atts['start_marker'] . '@2x.png';
656
- }
657
-
658
- if ( isset( $atts['store_marker'] ) && $atts['store_marker'] ) {
659
- $this->sl_shortcode_atts['js']['storeMarker'] = $atts['store_marker'] . '@2x.png';
660
- }
661
- }
662
-
663
- /**
664
- * Handle the [wpsl] shortcode.
665
- *
666
- * @since 1.0.0
667
- * @param array $atts Shortcode attributes
668
- * @return string $output The wpsl template
669
- */
670
- public function show_store_locator( $atts ) {
671
-
672
- global $wpsl, $wpsl_settings;
673
-
674
- $atts = shortcode_atts( array(
675
- 'template' => $wpsl_settings['template_id'],
676
- 'start_location' => '',
677
- 'auto_locate' => '',
678
- 'category' => '',
679
- 'category_selection' => '',
680
- 'category_filter_type' => '',
681
- 'checkbox_columns' => '3',
682
- 'map_type' => '',
683
- 'start_marker' => '',
684
- 'store_marker' => ''
685
- ), $atts );
686
-
687
- $this->check_sl_shortcode_atts( $atts );
688
-
689
- // Make sure the required scripts are included for the wpsl shortcode.
690
- array_push( $this->load_scripts, 'wpsl_store_locator' );
691
-
692
- $template_details = $wpsl->templates->get_template_details( $atts['template'] );
693
-
694
- $output = include( $template_details['path'] );
695
-
696
- return $output;
697
- }
698
-
699
- /**
700
- * Handle the [wpsl_address] shortcode.
701
- *
702
- * @since 2.0.0
703
- * @todo add schema.org support.
704
- * @param array $atts Shortcode attributes
705
- * @return void|string $output The store address
706
- */
707
- public function show_store_address( $atts ) {
708
-
709
- global $post, $wpsl_settings, $wpsl;
710
-
711
- $atts = wpsl_bool_check( shortcode_atts( apply_filters( 'wpsl_address_shortcode_defaults', array(
712
- 'id' => '',
713
- 'name' => true,
714
- 'address' => true,
715
- 'address2' => true,
716
- 'city' => true,
717
- 'state' => true,
718
- 'zip' => true,
719
- 'country' => true,
720
- 'phone' => true,
721
- 'fax' => true,
722
- 'email' => true,
723
- 'url' => true,
724
- 'directions' => false,
725
- 'clickable_contact_details' => (bool) $wpsl_settings['clickable_contact_details']
726
- ) ), $atts ) );
727
-
728
- if ( get_post_type() == 'wpsl_stores' ) {
729
- if ( empty( $atts['id'] ) ) {
730
- if ( isset( $post->ID ) ) {
731
- $atts['id'] = $post->ID;
732
- } else {
733
- return;
734
- }
735
- }
736
- } else if ( empty( $atts['id'] ) ) {
737
- return __( 'If you use the [wpsl_address] shortcode outside a store page you need to set the ID attribute.', 'wpsl' );
738
- }
739
-
740
- $content = '<div class="wpsl-locations-details">';
741
-
742
- if ( $atts['name'] && $name = get_the_title( $atts['id'] ) ) {
743
- $content .= '<span><strong>' . esc_html( $name ) . '</strong></span>';
744
- }
745
-
746
- $content .= '<div class="wpsl-location-address">';
747
-
748
- if ( $atts['address'] && $address = get_post_meta( $atts['id'], 'wpsl_address', true ) ) {
749
- $content .= '<span>' . esc_html( $address ) . '</span><br/>';
750
- }
751
-
752
- if ( $atts['address2'] && $address2 = get_post_meta( $atts['id'], 'wpsl_address2', true ) ) {
753
- $content .= '<span>' . esc_html( $address2 ) . '</span><br/>';
754
- }
755
-
756
- $address_format = explode( '_', $wpsl_settings['address_format'] );
757
- $count = count( $address_format );
758
- $i = 1;
759
-
760
- // Loop over the address parts to make sure they are shown in the right order.
761
- foreach ( $address_format as $address_part ) {
762
-
763
- // Make sure the shortcode attribute is set to true for the $address_part, and it's not the 'comma' part.
764
- if ( $address_part != 'comma' && $atts[$address_part] ) {
765
- $post_meta = get_post_meta( $atts['id'], 'wpsl_' . $address_part, true );
766
-
767
- if ( $post_meta ) {
768
-
769
- /*
770
- * Check if the next part of the address is set to 'comma'.
771
- * If so add the, after the current address part, otherwise just show a space
772
- */
773
- if ( isset( $address_format[$i] ) && ( $address_format[$i] == 'comma' ) ) {
774
- $punctuation = ', ';
775
- } else {
776
- $punctuation = ' ';
777
- }
778
-
779
- // If we have reached the last item add a <br /> behind it.
780
- $br = ( $count == $i ) ? '<br />' : '';
781
-
782
- $content .= '<span>' . esc_html( $post_meta ) . $punctuation . '</span>' . $br;
783
- }
784
- }
785
-
786
- $i++;
787
- }
788
-
789
- if ( $atts['country'] && $country = get_post_meta( $atts['id'], 'wpsl_country', true ) ) {
790
- $content .= '<span>' . esc_html( $country ) . '</span>';
791
- }
792
-
793
- $content .= '</div>';
794
-
795
- // If either the phone, fax, email or url is set to true, then add the wrap div for the contact details.
796
- if ( $atts['phone'] || $atts['fax'] || $atts['email'] || $atts['url'] ) {
797
- $phone = get_post_meta( $atts['id'], 'wpsl_phone', true );
798
- $fax = get_post_meta( $atts['id'], 'wpsl_fax', true );
799
- $email = get_post_meta( $atts['id'], 'wpsl_email', true );
800
-
801
- if ( $atts['clickable_contact_details'] ) {
802
- $contact_details = array(
803
- 'phone' => '<a href="tel:' . esc_attr( $phone ) . '">' . esc_html( $phone ) . '</a>',
804
- 'fax' => '<a href="tel:' . esc_attr( $fax ) . '">' . esc_html( $fax ) . '</a>',
805
- 'email' => '<a href="mailto:' . sanitize_email( $email ) . '">' . sanitize_email( $email ) . '</a>'
806
- );
807
- } else {
808
- $contact_details = array(
809
- 'phone' => esc_html( $phone ),
810
- 'fax' => esc_html( $fax ),
811
- 'email' => sanitize_email( $email )
812
- );
813
- }
814
-
815
- $content .= '<div class="wpsl-contact-details">';
816
-
817
- if ( $atts['phone'] && $phone ) {
818
- $content .= esc_html( $wpsl->i18n->get_translation( 'phone_label', __( 'Phone', 'wpsl' ) ) ) . ': <span>' . $contact_details['phone'] . '</span><br/>';
819
- }
820
-
821
- if ( $atts['fax'] && $fax ) {
822
- $content .= esc_html( $wpsl->i18n->get_translation( 'fax_label', __( 'Fax', 'wpsl' ) ) ) . ': <span>' . $contact_details['fax'] . '</span><br/>';
823
- }
824
-
825
- if ( $atts['email'] && $email ) {
826
- $content .= esc_html( $wpsl->i18n->get_translation( 'email_label', __( 'Email', 'wpsl' ) ) ) . ': <span>' . $contact_details['email'] . '</span><br/>';
827
- }
828
-
829
- if ( $atts['url'] && $store_url = get_post_meta( $atts['id'], 'wpsl_url', true ) ) {
830
- $new_window = ( $wpsl_settings['new_window'] ) ? 'target="_blank"' : '' ;
831
- $content .= esc_html( $wpsl->i18n->get_translation( 'url_label', __( 'Url', 'wpsl' ) ) ) . ': <a ' . $new_window . ' href="' . esc_url( $store_url ) . '">' . esc_url( $store_url ) . '</a><br/>';
832
- }
833
-
834
- $content .= '</div>';
835
- }
836
-
837
- if ( $atts['directions'] && $address ) {
838
- if ( $wpsl_settings['new_window'] ) {
839
- $new_window = ' target="_blank"';
840
- } else {
841
- $new_window = '';
842
- }
843
-
844
- $content .= '<div class="wpsl-location-directions">';
845
-
846
- $city = get_post_meta( $atts['id'], 'wpsl_city', true );
847
- $country = get_post_meta( $atts['id'], 'wpsl_country', true );
848
- $destination = $address . ',' . $city . ',' . $country;
849
- $direction_url = "https://maps.google.com/maps?saddr=&daddr=" . urlencode( $destination ) . "&travelmode=" . strtolower( $this->get_directions_travel_mode() );
850
-
851
- $content .= '<p><a ' . $new_window . ' href="' . esc_url( $direction_url ) . '">' . __( 'Directions', 'wpsl' ) . '</a></p>';
852
- $content .= '</div>';
853
- }
854
-
855
- $content .= '</div>';
856
-
857
- return $content;
858
- }
859
-
860
- /**
861
- * Handle the [wpsl_hours] shortcode.
862
- *
863
- * @since 2.0.0
864
- * @param array $atts Shortcode attributes
865
- * @return void|string $output The opening hours
866
- */
867
- public function show_opening_hours( $atts ) {
868
-
869
- global $post;
870
-
871
- $hide_closed = apply_filters( 'wpsl_hide_closed_hours', false );
872
-
873
- $atts = wpsl_bool_check( shortcode_atts( apply_filters( 'wpsl_hour_shortcode_defaults', array(
874
- 'id' => '',
875
- 'hide_closed' => $hide_closed
876
- ) ), $atts ) );
877
-
878
- if ( get_post_type() == 'wpsl_stores' ) {
879
- if ( empty( $atts['id'] ) ) {
880
- if ( isset( $post->ID ) ) {
881
- $atts['id'] = $post->ID;
882
- } else {
883
- return;
884
- }
885
- }
886
- } else if ( empty( $atts['id'] ) ) {
887
- return __( 'If you use the [wpsl_hours] shortcode outside a store page you need to set the ID attribute.', 'wpsl' );
888
- }
889
-
890
- $opening_hours = get_post_meta( $atts['id'], 'wpsl_hours' );
891
-
892
- if ( $opening_hours ) {
893
- $output = $this->get_opening_hours( $opening_hours[0], $atts['hide_closed'] );
894
-
895
- return $output;
896
- }
897
- }
898
-
899
- /**
900
- * Handle the [wpsl_map] shortcode.
901
- *
902
- * @since 2.0.0
903
- * @param array $atts Shortcode attributes
904
- * @return string $output The html for the map
905
- */
906
- public function show_store_map( $atts ) {
907
-
908
- global $wpsl_settings, $post;
909
-
910
- $atts = shortcode_atts( apply_filters( 'wpsl_map_shortcode_defaults', array(
911
- 'id' => '',
912
- 'category' => '',
913
- 'width' => '',
914
- 'height' => $wpsl_settings['height'],
915
- 'zoom' => $wpsl_settings['zoom_level'],
916
- 'map_type' => $wpsl_settings['map_type'],
917
- 'map_type_control' => $wpsl_settings['type_control'],
918
- 'map_style' => '',
919
- 'street_view' => $wpsl_settings['streetview'],
920
- 'scrollwheel' => $wpsl_settings['scrollwheel'],
921
- 'control_position' => $wpsl_settings['control_position']
922
- ) ), $atts );
923
-
924
- array_push( $this->load_scripts, 'wpsl_base' );
925
-
926
- if ( get_post_type() == 'wpsl_stores' ) {
927
- if ( empty( $atts['id'] ) ) {
928
- if ( isset( $post->ID ) ) {
929
- $atts['id'] = $post->ID;
930
- } else {
931
- return;
932
- }
933
- }
934
- } else if ( empty( $atts['id'] ) && empty( $atts['category'] ) ) {
935
- return __( 'If you use the [wpsl_map] shortcode outside a store page, then you need to set the ID or category attribute.', 'wpsl' );
936
- }
937
-
938
- if ( $atts['category'] ) {
939
- $store_ids = get_posts( array(
940
- 'numberposts' => -1,
941
- 'post_type' => 'wpsl_stores',
942
- 'post_status' => 'publish',
943
- 'tax_query' => array(
944
- array(
945
- 'taxonomy' => 'wpsl_store_category',
946
- 'field' => 'slug',
947
- 'terms' => explode( ',', sanitize_text_field( $atts['category'] ) )
948
- ),
949
- ),
950
- 'fields' => 'ids'
951
- ) );
952
- } else {
953
- $store_ids = array_map( 'absint', explode( ',', $atts['id'] ) );
954
- $id_count = count( $store_ids );
955
- }
956
-
957
- /*
958
- * The location url is included if:
959
- *
960
- * - Multiple ids are set.
961
- * - The category attr is set.
962
- * - The shortcode is used on a post type other then 'wpsl_stores'. No point in showing a location
963
- * url to the user that links back to the page they are already on.
964
- */
965
- if ( $atts['category'] || isset( $id_count ) && $id_count > 1 || get_post_type() != 'wpsl_stores' && !empty( $atts['id'] ) ) {
966
- $incl_url = true;
967
- } else {
968
- $incl_url = false;
969
- }
970
-
971
- $store_meta = array();
972
- $i = 0;
973
-
974
- foreach ( $store_ids as $store_id ) {
975
- $lat = get_post_meta( $store_id, 'wpsl_lat', true );
976
- $lng = get_post_meta( $store_id, 'wpsl_lng', true );
977
-
978
- // Make sure the latlng is numeric before collecting the other meta data.
979
- if ( is_numeric( $lat ) && is_numeric( $lng ) ) {
980
- $store_meta[$i] = apply_filters( 'wpsl_cpt_info_window_meta_fields', array(
981
- 'store' => get_the_title( $store_id ),
982
- 'address' => get_post_meta( $store_id, 'wpsl_address', true ),
983
- 'address2' => get_post_meta( $store_id, 'wpsl_address2', true ),
984
- 'city' => get_post_meta( $store_id, 'wpsl_city', true ),
985
- 'state' => get_post_meta( $store_id, 'wpsl_state', true ),
986
- 'zip' => get_post_meta( $store_id, 'wpsl_zip', true ),
987
- 'country' => get_post_meta( $store_id, 'wpsl_country', true )
988
- ), $store_id );
989
-
990
- // Grab the permalink / url if necessary.
991
- if ( $incl_url ) {
992
- if ( $wpsl_settings['permalinks'] ) {
993
- $store_meta[$i]['permalink'] = get_permalink( $store_id );
994
- } else {
995
- $store_meta[$i]['url'] = get_post_meta( $store_id, 'wpsl_url', true );
996
- }
997
- }
998
-
999
- $store_meta[$i]['lat'] = $lat;
1000
- $store_meta[$i]['lng'] = $lng;
1001
- $store_meta[$i]['id'] = $store_id;
1002
-
1003
- $i++;
1004
- }
1005
- }
1006
-
1007
- $output = '<div id="wpsl-base-gmap_' . self::$map_count . '" class="wpsl-gmap-canvas"></div>' . "\r\n";
1008
-
1009
- // Make sure the shortcode attributes are valid.
1010
- $map_styles = $this->check_map_shortcode_atts( $atts );
1011
-
1012
- if ( $map_styles ) {
1013
- if ( isset( $map_styles['css'] ) && !empty( $map_styles['css'] ) ) {
1014
- $output .= '<style>' . $map_styles['css'] . '</style>' . "\r\n";
1015
- unset( $map_styles['css'] );
1016
- }
1017
-
1018
- if ( $map_styles ) {
1019
- $store_data['shortCode'] = $map_styles;
1020
- }
1021
- }
1022
-
1023
- $store_data['locations'] = $store_meta;
1024
-
1025
- $this->store_map_data[self::$map_count] = $store_data;
1026
-
1027
- self::$map_count++;
1028
-
1029
- return $output;
1030
- }
1031
-
1032
- /**
1033
- * Make sure the map style shortcode attributes are valid.
1034
- *
1035
- * The values are send to wp_localize_script in add_frontend_scripts.
1036
- *
1037
- * @since 2.0.0
1038
- * @param array $atts The map style shortcode attributes
1039
- * @return array $map_atts Validated map style shortcode attributes
1040
- */
1041
- public function check_map_shortcode_atts( $atts ) {
1042
-
1043
- $map_atts = array();
1044
-
1045
- if ( isset( $atts['width'] ) && is_numeric( $atts['width'] ) ) {
1046
- $width = 'width:' . $atts['width'] . 'px;';
1047
- } else {
1048
- $width = '';
1049
- }
1050
-
1051
- if ( isset( $atts['height'] ) && is_numeric( $atts['height'] ) ) {
1052
- $height = 'height:' . $atts['height'] . 'px;';
1053
- } else {
1054
- $height = '';
1055
- }
1056
-
1057
- if ( $width || $height ) {
1058
- $map_atts['css'] = '#wpsl-base-gmap_' . self::$map_count . ' {' . $width . $height . '}';
1059
- }
1060
-
1061
- if ( isset( $atts['zoom'] ) && !empty( $atts['zoom'] ) ) {
1062
- $map_atts['zoomLevel'] = wpsl_valid_zoom_level( $atts['zoom'] );
1063
- }
1064
-
1065
- if ( isset( $atts['map_type'] ) && !empty( $atts['map_type'] ) ) {
1066
- $map_atts['mapType'] = wpsl_valid_map_type( $atts['map_type'] );
1067
- }
1068
-
1069
- if ( isset( $atts['map_type_control'] ) ) {
1070
- $map_atts['mapTypeControl'] = $this->shortcode_atts_boolean( $atts['map_type_control'] );
1071
- }
1072
-
1073
- if ( isset( $atts['map_style'] ) && $atts['map_style'] == 'default' ) {
1074
- $map_atts['mapStyle'] = '';
1075
- }
1076
-
1077
- if ( isset( $atts['street_view'] ) ) {
1078
- $map_atts['streetView'] = $this->shortcode_atts_boolean( $atts['street_view'] );
1079
- }
1080
-
1081
- if ( isset( $atts['scrollwheel'] ) ) {
1082
- $map_atts['scrollWheel'] = $this->shortcode_atts_boolean( $atts['scrollwheel'] );
1083
- }
1084
-
1085
- if ( isset( $atts['control_position'] ) && !empty( $atts['control_position'] ) && ( $atts['control_position'] == 'left' || $atts['control_position'] == 'right' ) ) {
1086
- $map_atts['controlPosition'] = $atts['control_position'];
1087
- }
1088
-
1089
- return $map_atts;
1090
- }
1091
-
1092
- /**
1093
- * Set the shortcode attribute to either 1 or 0.
1094
- *
1095
- * @since 2.0.0
1096
- * @param string $att The shortcode attribute val
1097
- * @return int $att_val Either 1 or 0
1098
- */
1099
- public function shortcode_atts_boolean( $att ) {
1100
-
1101
- if ( $att === 'true' || absint( $att ) ) {
1102
- $att_val = 1;
1103
- } else {
1104
- $att_val = 0;
1105
- }
1106
-
1107
- return $att_val;
1108
- }
1109
-
1110
- /**
1111
- * Make sure the filter contains a valid value, otherwise use the default value.
1112
- *
1113
- * @since 2.0.0
1114
- * @param array $args The values used in the SQL query to find nearby locations
1115
- * @param string $filter The name of the filter
1116
- * @return string $filter_value The filter value
1117
- */
1118
- public function check_store_filter( $args, $filter ) {
1119
-
1120
- if ( isset( $args[$filter] ) && absint( $args[$filter] ) && $this->check_allowed_filter_value( $args, $filter ) ) {
1121
- $filter_value = $args[$filter];
1122
- } else {
1123
- $filter_value = $this->get_default_filter_value( $filter );
1124
- }
1125
-
1126
- return $filter_value;
1127
- }
1128
-
1129
- /**
1130
- * Make sure the used filter value isn't bigger
1131
- * then the value that's set on the settings page.
1132
- *
1133
- * @since 2.2.9
1134
- * @param array $args The values used in the SQL query to find nearby locations
1135
- * @param string $filter The name of the filter
1136
- * @return bool $allowed True if the value is equal or smaller then the value from the settings page
1137
- */
1138
- public function check_allowed_filter_value( $args, $filter ) {
1139
-
1140
- global $wpsl_settings;
1141
-
1142
- $allowed = false;
1143
-
1144
- $max_filter_val = max( explode(',', str_replace( array( '[',']' ), '', $wpsl_settings[$filter] ) ) );
1145
-
1146
- if ( (int) $args[$filter] <= (int) $max_filter_val ) {
1147
- $allowed = true;
1148
- }
1149
-
1150
- return $allowed;
1151
- }
1152
-
1153
- /**
1154
- * Get the default selected value for a dropdown.
1155
- *
1156
- * @since 1.0.0
1157
- * @param string $type The request list type
1158
- * @return string $response The default list value
1159
- */
1160
- public function get_default_filter_value( $type ) {
1161
-
1162
- $settings = get_option( 'wpsl_settings' );
1163
- $list_values = explode( ',', $settings[$type] );
1164
-
1165
- foreach ( $list_values as $k => $list_value ) {
1166
-
1167
- // The default radius has a [] wrapped around it, so we check for that and filter out the [].
1168
- if ( strpos( $list_value, '[' ) !== false ) {
1169
- $response = filter_var( $list_value, FILTER_SANITIZE_NUMBER_INT );
1170
- break;
1171
- }
1172
- }
1173
-
1174
- return $response;
1175
- }
1176
-
1177
- /**
1178
- * Check if we have a opening day that has an value, if not they are all set to closed.
1179
- *
1180
- * @since 2.0.0
1181
- * @param array $opening_hours The opening hours
1182
- * @return boolean True if a day is found that isn't empty
1183
- */
1184
- public function not_always_closed( $opening_hours ) {
1185
-
1186
- foreach ( $opening_hours as $hours => $hour ) {
1187
- if ( !empty( $hour ) ) {
1188
- return true;
1189
- }
1190
- }
1191
- }
1192
-
1193
- /**
1194
- * Create the css rules based on the height / max-width that is set on the settings page.
1195
- *
1196
- * @since 1.0.0
1197
- * @return string $css The custom css rules
1198
- */
1199
- public function get_custom_css() {
1200
-
1201
- global $wpsl_settings;
1202
-
1203
- $thumb_size = $this->get_store_thumb_size();
1204
-
1205
- $css = '<style>' . "\r\n";
1206
-
1207
- if ( isset( $thumb_size[0] ) && is_numeric( $thumb_size[0] ) && isset( $thumb_size[1] ) && is_numeric( $thumb_size[1] ) ) {
1208
- $css .= "\t" . "#wpsl-stores .wpsl-store-thumb {height:" . esc_attr( $thumb_size[0] ) . "px !important; width:" . esc_attr( $thumb_size[1] ) . "px !important;}" . "\r\n";
1209
- }
1210
-
1211
- if ( $wpsl_settings['template_id'] == 'below_map' && $wpsl_settings['listing_below_no_scroll'] ) {
1212
- $css .= "\t" . "#wpsl-gmap {height:" . esc_attr( $wpsl_settings['height'] ) . "px !important;}" . "\r\n";
1213
- $css .= "\t" . "#wpsl-stores, #wpsl-direction-details {height:auto !important;}";
1214
- } else {
1215
- $css .= "\t" . "#wpsl-stores, #wpsl-direction-details, #wpsl-gmap {height:" . esc_attr( $wpsl_settings['height'] ) . "px !important;}" . "\r\n";
1216
- }
1217
-
1218
- /*
1219
- * If the category dropdowns are enabled then we make it
1220
- * the same width as the search input field.
1221
- */
1222
- if ( $wpsl_settings['category_filter'] && $wpsl_settings['category_filter_type'] == 'dropdown' || isset( $this->sl_shortcode_atts['category_filter_type'] ) && $this->sl_shortcode_atts['category_filter_type'] == 'dropdown' ) {
1223
- $cat_elem = ',#wpsl-category .wpsl-dropdown';
1224
- } else {
1225
- $cat_elem = '';
1226
- }
1227
-
1228
- $css .= "\t" . "#wpsl-gmap .wpsl-info-window {max-width:" . esc_attr( $wpsl_settings['infowindow_width'] ) . "px !important;}" . "\r\n";
1229
- $css .= "\t" . ".wpsl-input label, #wpsl-radius label, #wpsl-category label {width:" . esc_attr( $wpsl_settings['label_width'] ) . "px;}" . "\r\n";
1230
- $css .= "\t" . "#wpsl-search-input " . $cat_elem . " {width:" . esc_attr( $wpsl_settings['search_width'] ) . "px;}" . "\r\n";
1231
- $css .= '</style>' . "\r\n";
1232
-
1233
- return $css;
1234
- }
1235
-
1236
- /**
1237
- * Collect the CSS classes that are placed on the outer store locator div.
1238
- *
1239
- * @since 2.0.0
1240
- * @return string $classes The custom CSS rules
1241
- */
1242
- public function get_css_classes() {
1243
-
1244
- global $wpsl_settings;
1245
-
1246
- $classes = array();
1247
-
1248
- if ( $wpsl_settings['category_filter'] && $wpsl_settings['results_dropdown'] && !$wpsl_settings['radius_dropdown'] ) {
1249
- $classes[] = 'wpsl-cat-results-filter';
1250
- } else if ( $wpsl_settings['category_filter'] && ( $wpsl_settings['results_dropdown'] || $wpsl_settings['radius_dropdown'] ) ) {
1251
- $classes[] = 'wpsl-filter';
1252
- }
1253
- // checkboxes class toevoegen?
1254
- if ( !$wpsl_settings['category_filter'] && !$wpsl_settings['results_dropdown'] && !$wpsl_settings['radius_dropdown'] ) {
1255
- $classes[] = 'wpsl-no-filters';
1256
- }
1257
-
1258
- if ( $wpsl_settings['category_filter'] && $wpsl_settings['category_filter_type'] == 'checkboxes' ) {
1259
- $classes[] = 'wpsl-checkboxes-enabled';
1260
- }
1261
-
1262
- if ( $wpsl_settings['results_dropdown'] && !$wpsl_settings['category_filter'] && !$wpsl_settings['radius_dropdown'] ) {
1263
- $classes[] = 'wpsl-results-only';
1264
- }
1265
-
1266
- $classes = apply_filters( 'wpsl_template_css_classes', $classes );
1267
-
1268
- if ( !empty( $classes ) ) {
1269
- return join( ' ', $classes );
1270
- }
1271
- }
1272
-
1273
- /**
1274
- * Create a dropdown list holding the search radius or
1275
- * max search results options.
1276
- *
1277
- * @since 1.0.0
1278
- * @param string $list_type The name of the list we need to load data for
1279
- * @return string $dropdown_list A list with the available options for the dropdown list
1280
- */
1281
- public function get_dropdown_list( $list_type ) {
1282
-
1283
- global $wpsl_settings;
1284
-
1285
- $dropdown_list = '';
1286
- $settings = explode( ',', $wpsl_settings[$list_type] );
1287
-
1288
- // Only show the distance unit if we are dealing with the search radius.
1289
- if ( $list_type == 'search_radius' ) {
1290
- $distance_unit = ' '. esc_attr( wpsl_get_distance_unit() );
1291
- } else {
1292
- $distance_unit = '';
1293
- }
1294
-
1295
- foreach ( $settings as $index => $setting_value ) {
1296
-
1297
- // The default radius has a [] wrapped around it, so we check for that and filter out the [].
1298
- if ( strpos( $setting_value, '[' ) !== false ) {
1299
- $setting_value = filter_var( $setting_value, FILTER_SANITIZE_NUMBER_INT );
1300
- $selected = 'selected="selected" ';
1301
- } else {
1302
- $selected = '';
1303
- }
1304
-
1305
- $dropdown_list .= '<option ' . $selected . 'value="'. absint( $setting_value ) .'">'. absint( $setting_value ) . $distance_unit .'</option>';
1306
- }
1307
-
1308
- return $dropdown_list;
1309
- }
1310
-
1311
- /**
1312
- * Check if we need to use a dropdown or checkboxes
1313
- * to filter the search results by categories.
1314
- *
1315
- * @since 2.2.10
1316
- * @return bool $use_filter
1317
- */
1318
- public function use_category_filter() {
1319
-
1320
- global $wpsl_settings;
1321
-
1322
- $use_filter = false;
1323
-
1324
- // Is a filter type set through the shortcode, or is the filter option enable on the settings page?
1325
- if ( isset( $this->sl_shortcode_atts['category_filter_type'] ) || $wpsl_settings['category_filter'] ) {
1326
- $use_filter = true;
1327
- }
1328
-
1329
- return $use_filter;
1330
- }
1331
-
1332
- /**
1333
- * Create the category filter.
1334
- *
1335
- * @todo create another func that accepts a meta key param to generate
1336
- * a dropdown with unique values. So for example create_filter( 'restaurant' ) will output a
1337
- * filter with all restaurant types. This can be used in a custom theme template.
1338
- *
1339
- * @since 2.0.0
1340
- * @return string|void $category The HTML for the category dropdown, or nothing if no terms exist.
1341
- */
1342
- public function create_category_filter() {
1343
-
1344
- global $wpsl, $wpsl_settings;
1345
-
1346
- /*
1347
- * If the category attr is set on the wpsl shortcode, then
1348
- * there is no need to ouput an extra category dropdown.
1349
- */
1350
- if ( isset( $this->sl_shortcode_atts['js']['categoryIds'] ) ) {
1351
- return;
1352
- }
1353
-
1354
- $terms = get_terms( 'wpsl_store_category' );
1355
-
1356
- if ( count( $terms ) > 0 ) {
1357
-
1358
- // Either use the shortcode atts filter type or the one from the settings page.
1359
- if ( isset( $this->sl_shortcode_atts['category_filter_type'] ) ) {
1360
- $filter_type = $this->sl_shortcode_atts['category_filter_type'];
1361
- } else {
1362
- $filter_type = $wpsl_settings['category_filter_type'];
1363
- }
1364
-
1365
- // Check if we need to show the filter as checkboxes or a dropdown list
1366
- if ( $filter_type == 'checkboxes' ) {
1367
- if ( isset( $this->sl_shortcode_atts['checkbox_columns'] ) ) {
1368
- $checkbox_columns = absint( $this->sl_shortcode_atts['checkbox_columns'] );
1369
- }
1370
-
1371
- if ( isset( $checkbox_columns ) && $checkbox_columns ) {
1372
- $column_count = $checkbox_columns;
1373
- } else {
1374
- $column_count = 3;
1375
- }
1376
-
1377
- $category = '<ul id="wpsl-checkbox-filter" class="wpsl-checkbox-' . $column_count . '-columns">';
1378
-
1379
- foreach ( $terms as $term ) {
1380
- $category .= '<li>';
1381
- $category .= '<label>';
1382
- $category .= '<input type="checkbox" value="' . esc_attr( $term->term_id ) . '" ' . $this->set_selected_category( $filter_type, $term->term_id ) . ' />';
1383
- $category .= esc_html( $term->name );
1384
- $category .= '</label>';
1385
- $category .= '</li>';
1386
- }
1387
-
1388
- $category .= '</ul>';
1389
- } else {
1390
- $category = '<div id="wpsl-category">' . "\r\n";
1391
- $category .= '<label for="wpsl-category-list">' . esc_html( $wpsl->i18n->get_translation( 'category_label', __( 'Category', 'wpsl' ) ) ) . '</label>' . "\r\n";
1392
-
1393
- $args = apply_filters( 'wpsl_dropdown_category_args', array(
1394
- 'show_option_none' => $wpsl->i18n->get_translation( 'category_default_label', __( 'Any', 'wpsl' ) ),
1395
- 'option_none_value' => '0',
1396
- 'orderby' => 'NAME',
1397
- 'order' => 'ASC',
1398
- 'echo' => 0,
1399
- 'selected' => $this->set_selected_category( $filter_type ),
1400
- 'hierarchical' => 1,
1401
- 'name' => 'wpsl-category',
1402
- 'id' => 'wpsl-category-list',
1403
- 'class' => 'wpsl-dropdown',
1404
- 'taxonomy' => 'wpsl_store_category',
1405
- 'hide_if_empty' => true
1406
- )
1407
- );
1408
-
1409
- $category .= wp_dropdown_categories( $args );
1410
-
1411
- $category .= '</div>' . "\r\n";
1412
- }
1413
-
1414
- return $category;
1415
- }
1416
- }
1417
-
1418
- /**
1419
- * Set the selected category item.
1420
- *
1421
- * @since 2.1.2
1422
- * @param string $filter_type The type of filter being used ( dropdown or checkbox )
1423
- * @param int|string $term_id The term id ( checkbox only )
1424
- * @return string|void $category The ID of the selected option, or checked='checked' if it's for a checkbox
1425
- */
1426
- public function set_selected_category( $filter_type, $term_id = '' ) {
1427
-
1428
- $selected_id = '';
1429
-
1430
- // Check if the ID for the selected cat is either passed through the widget, or shortcode
1431
- if ( isset( $_REQUEST['wpsl-widget-categories'] ) ) {
1432
- $selected_id = absint( $_REQUEST['wpsl-widget-categories'] );
1433
- } else if ( isset( $this->sl_shortcode_atts['category_selection'] ) ) {
1434
-
1435
- /*
1436
- * When the term_id is set, then it's a checkbox.
1437
- *
1438
- * Otherwise select the first value from the provided list since
1439
- * multiple selections are not supported in dropdowns.
1440
- */
1441
- if ( $term_id ) {
1442
-
1443
- // Check if the passed term id exists in the set shortcode value.
1444
- $key = array_search( $term_id, $this->sl_shortcode_atts['category_selection'] );
1445
-
1446
- if ( $key !== false ) {
1447
- $selected_id = $this->sl_shortcode_atts['category_selection'][$key];
1448
- }
1449
- } else {
1450
- $selected_id = $this->sl_shortcode_atts['category_selection'][0];
1451
- }
1452
- }
1453
-
1454
- if ( $selected_id ) {
1455
-
1456
- /*
1457
- * Based on the filter type, either return the ID of the selected category,
1458
- * or check if the checkbox needs to be set to checked="checked".
1459
- */
1460
- if ( $filter_type == 'dropdown' ) {
1461
- return $selected_id;
1462
- } else {
1463
- return checked( $selected_id, $term_id, false );
1464
- }
1465
- }
1466
- }
1467
-
1468
- /**
1469
- * Create a filename with @2x in it for the selected marker color.
1470
- *
1471
- * So when a user selected green.png in the admin panel. The JS on the front-end will end up
1472
- * loading green@2x.png to provide support for retina compatible devices.
1473
- *
1474
- * @since 1.0.0
1475
- * @param string $filename The name of the seleted marker
1476
- * @return string $filename The filename with @2x added to the end
1477
- */
1478
- public function create_retina_filename( $filename ) {
1479
-
1480
- $filename = explode( '.', $filename );
1481
- $filename = $filename[0] . '@2x.' . $filename[1];
1482
-
1483
- return $filename;
1484
- }
1485
-
1486
- /**
1487
- * Get the default values for the max_results and the search_radius dropdown.
1488
- *
1489
- * @since 1.0.2
1490
- * @return array $output The default dropdown values
1491
- */
1492
- public function get_dropdown_defaults() {
1493
-
1494
- global $wpsl_settings;
1495
-
1496
- $required_defaults = array(
1497
- 'max_results',
1498
- 'search_radius'
1499
- );
1500
-
1501
- // Strip out the default values that are wrapped in [].
1502
- foreach ( $required_defaults as $required_default ) {
1503
- preg_match_all( '/\[([0-9]+?)\]/', $wpsl_settings[$required_default], $match, PREG_PATTERN_ORDER );
1504
- $output[$required_default] = ( isset( $match[1][0] ) ) ? $match[1][0] : '25';
1505
- }
1506
-
1507
- return $output;
1508
- }
1509
-
1510
- /**
1511
- * Load the required css styles.
1512
- *
1513
- * @since 2.0.0
1514
- * @return void
1515
- */
1516
- public function add_frontend_styles() {
1517
-
1518
- global $wpsl_settings;
1519
-
1520
- /**
1521
- * Check if we need to deregister other Google Maps scripts loaded
1522
- * by other plugins, or the current theme?
1523
- *
1524
- * This in some cases can break the store locator map.
1525
- */
1526
- if ( $wpsl_settings['deregister_gmaps'] ) {
1527
- wpsl_deregister_other_gmaps();
1528
- }
1529
-
1530
- $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
1531
-
1532
- wp_enqueue_style( 'wpsl-styles', WPSL_URL . 'css/styles'. $min .'.css', '', WPSL_VERSION_NUM );
1533
- }
1534
-
1535
- /**
1536
- * Get the HTML for the map controls.
1537
- *
1538
- * The '&#xe800;' and '&#xe801;' code is for the icon font from fontello.com
1539
- *
1540
- * @since 2.0.0
1541
- * @return string The HTML for the map controls
1542
- */
1543
- public function get_map_controls() {
1544
-
1545
- global $wpsl_settings, $is_IE;
1546
-
1547
- $classes = array();
1548
-
1549
- if ( $wpsl_settings['reset_map'] ) {
1550
- $reset_button = '<div class="wpsl-icon-reset"><span>&#xe801;</span></div>';
1551
- } else {
1552
- $reset_button = '';
1553
- }
1554
-
1555
- /*
1556
- * IE messes up the top padding for the icon fonts from fontello >_<.
1557
- *
1558
- * Luckily it's the same in all IE version ( 8-11 ),
1559
- * so adjusting the padding just for IE fixes it.
1560
- */
1561
- if ( $is_IE ) {
1562
- $classes[] = 'wpsl-ie';
1563
- }
1564
-
1565
- // If the street view option is enabled, then we need to adjust the right margin for the map control div.
1566
- if ( $wpsl_settings['streetview'] ) {
1567
- $classes[] = 'wpsl-street-view-exists';
1568
- }
1569
-
1570
- if ( !empty( $classes ) ) {
1571
- $class = 'class="' . join( ' ', $classes ) . '"';
1572
- } else {
1573
- $class = '';
1574
- }
1575
-
1576
- $map_controls = '<div id="wpsl-map-controls" ' . $class . '>' . $reset_button . '<div class="wpsl-icon-direction"><span>&#xe800;</span></div></div>';
1577
-
1578
- return apply_filters( 'wpsl_map_controls', $map_controls );
1579
- }
1580
-
1581
- /**
1582
- * The different geolocation errors.
1583
- *
1584
- * They are shown when the Geolocation API returns an error.
1585
- *
1586
- * @since 2.0.0
1587
- * @return array $geolocation_errors
1588
- */
1589
- public function geolocation_errors() {
1590
-
1591
- $geolocation_errors = array(
1592
- 'denied' => __( 'The application does not have permission to use the Geolocation API.', 'wpsl' ),
1593
- 'unavailable' => __( 'Location information is unavailable.', 'wpsl' ),
1594
- 'timeout' => __( 'The geolocation request timed out.', 'wpsl' ),
1595
- 'generalError' => __( 'An unknown error occurred.', 'wpsl' )
1596
- );
1597
-
1598
- return $geolocation_errors;
1599
- }
1600
-
1601
- /**
1602
- * Get the used marker properties.
1603
- *
1604
- * @since 2.1.0
1605
- * @link https://developers.google.com/maps/documentation/javascript/3.exp/reference#Icon
1606
- * @return array $marker_props The marker properties.
1607
- */
1608
- public function get_marker_props() {
1609
-
1610
- $marker_props = array(
1611
- 'scaledSize' => '24,35', // 50% of the normal image to make it work on retina screens.
1612
- 'origin' => '0,0',
1613
- 'anchor' => '12,35'
1614
- );
1615
-
1616
- /*
1617
- * If this is not defined, the url path will default to
1618
- * the url path of the WPSL plugin folder + /img/markers/
1619
- * in the wpsl-gmap.js.
1620
- */
1621
- if ( defined( 'WPSL_MARKER_URI' ) ) {
1622
- $marker_props['url'] = WPSL_MARKER_URI;
1623
- }
1624
-
1625
- return apply_filters( 'wpsl_marker_props', $marker_props );
1626
- }
1627
-
1628
- /**
1629
- * Get the used travel direction mode.
1630
- *
1631
- * @since 2.2.8
1632
- * @return string $travel_mode The used travel mode for the travel direcions
1633
- */
1634
- public function get_directions_travel_mode() {
1635
-
1636
- $default = 'driving';
1637
-
1638
- $travel_mode = apply_filters( 'wpsl_direction_travel_mode', $default );
1639
- $allowed_modes = array( 'driving', 'bicycling', 'transit', 'walking' );
1640
-
1641
- if ( !in_array( $travel_mode, $allowed_modes ) ) {
1642
- $travel_mode = $default;
1643
- }
1644
-
1645
- return strtoupper( $travel_mode );
1646
- }
1647
-
1648
- /**
1649
- * Get the map tab anchors.
1650
- *
1651
- * If the wpsl/wpsl_map shortcode is used in one or more tabs,
1652
- * then a JS fix ( the fixGreyTabMap function ) needs to run
1653
- * to make sure the map doesn't turn grey.
1654
- *
1655
- * For the fix to work need to know the used anchor(s).
1656
- *
1657
- * @since 2.2.10
1658
- * @return string|array $map_tab_anchor One or more anchors used to show the map(s)
1659
- */
1660
- public function get_map_tab_anchor() {
1661
-
1662
- $map_tab_anchor = apply_filters( 'wpsl_map_tab_anchor', 'wpsl-map-tab' );
1663
-
1664
- return $map_tab_anchor;
1665
- }
1666
-
1667
- /**
1668
- * Load the required JS scripts.
1669
- *
1670
- * @since 1.0.0
1671
- * @return void
1672
- */
1673
- public function add_frontend_scripts() {
1674
-
1675
- global $wpsl_settings, $wpsl;
1676
-
1677
- // Only load the required js files on the store locator page or individual store pages.
1678
- if ( empty( $this->load_scripts ) ) {
1679
- return;
1680
- }
1681
-
1682
- $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
1683
-
1684
- $dropdown_defaults = $this->get_dropdown_defaults();
1685
-
1686
- /**
1687
- * Check if we need to deregister other Google Maps scripts loaded
1688
- * by other plugins, or the current theme?
1689
- *
1690
- * This in some cases can break the store locator map.
1691
- */
1692
- if ( $wpsl_settings['deregister_gmaps'] ) {
1693
- wpsl_deregister_other_gmaps();
1694
- }
1695
-
1696
- wp_enqueue_script( 'wpsl-gmap', ( 'https://maps.google.com/maps/api/js' . wpsl_get_gmap_api_params( 'browser_key' ) . '' ), '', null, true );
1697
-
1698
- $base_settings = array(
1699
- 'storeMarker' => $this->create_retina_filename( $wpsl_settings['store_marker'] ),
1700
- 'mapType' => $wpsl_settings['map_type'],
1701
- 'mapTypeControl' => $wpsl_settings['type_control'],
1702
- 'zoomLevel' => $wpsl_settings['zoom_level'],
1703
- 'startLatlng' => $wpsl_settings['start_latlng'],
1704
- 'autoZoomLevel' => $wpsl_settings['auto_zoom_level'],
1705
- 'scrollWheel' => $wpsl_settings['scrollwheel'],
1706
- 'controlPosition' => $wpsl_settings['control_position'],
1707
- 'url' => WPSL_URL,
1708
- 'markerIconProps' => $this->get_marker_props(),
1709
- 'storeUrl' => $wpsl_settings['store_url'],
1710
- 'maxDropdownHeight' => apply_filters( 'wpsl_max_dropdown_height', 300 ),
1711
- 'enableStyledDropdowns' => apply_filters( 'wpsl_enable_styled_dropdowns', true ),
1712
- 'mapTabAnchor' => $this->get_map_tab_anchor(),
1713
- 'mapTabAnchorReturn' => apply_filters( 'wpsl_map_tab_anchor_return', false ),
1714
- 'gestureHandling' => apply_filters( 'wpsl_gesture_handling', 'auto' ),
1715
- 'directionsTravelMode' => $this->get_directions_travel_mode(),
1716
- 'runFitBounds' => $wpsl_settings['run_fitbounds']
1717
- );
1718
-
1719
- $locator_map_settings = array(
1720
- 'startMarker' => $this->create_retina_filename( $wpsl_settings['start_marker'] ),
1721
- 'markerClusters' => $wpsl_settings['marker_clusters'],
1722
- 'streetView' => $wpsl_settings['streetview'],
1723
- 'autoComplete' => $wpsl_settings['autocomplete'],
1724
- 'autoLocate' => $wpsl_settings['auto_locate'],
1725
- 'autoLoad' => $wpsl_settings['autoload'],
1726
- 'markerEffect' => $wpsl_settings['marker_effect'],
1727
- 'markerStreetView' => $wpsl_settings['marker_streetview'],
1728
- 'markerZoomTo' => $wpsl_settings['marker_zoom_to'],
1729
- 'newWindow' => $wpsl_settings['new_window'],
1730
- 'resetMap' => $wpsl_settings['reset_map'],
1731
- 'directionRedirect' => $wpsl_settings['direction_redirect'],
1732
- 'phoneUrl' => $wpsl_settings['phone_url'],
1733
- 'clickableDetails' => $wpsl_settings['clickable_contact_details'],
1734
- 'moreInfoLocation' => $wpsl_settings['more_info_location'],
1735
- 'mouseFocus' => $wpsl_settings['mouse_focus'],
1736
- 'templateId' => $wpsl_settings['template_id'],
1737
- 'maxResults' => $dropdown_defaults['max_results'],
1738
- 'searchRadius' => $dropdown_defaults['search_radius'],
1739
- 'distanceUnit' => wpsl_get_distance_unit(),
1740
- 'geoLocationTimeout' => apply_filters( 'wpsl_geolocation_timeout', 7500 ),
1741
- 'ajaxurl' => wpsl_get_ajax_url(),
1742
- 'mapControls' => $this->get_map_controls()
1743
- );
1744
-
1745
- /*
1746
- * If no results are found then by default it will just show the
1747
- * "No results found" text. This filter makes it possible to show
1748
- * a custom HTML block instead of the "No results found" text.
1749
- */
1750
- $no_results_msg = apply_filters( 'wpsl_no_results', '' );
1751
-
1752
- if ( $no_results_msg ) {
1753
- $locator_map_settings['noResults'] = $no_results_msg;
1754
- }
1755
-
1756
- /*
1757
- * If enabled, include the component filter settings.
1758
- * @todo see https://developers.google.com/maps/documentation/javascript/releases#327
1759
- * See https://developers.google.com/maps/documentation/javascript/geocoding#ComponentFiltering
1760
- */
1761
- if ( $wpsl_settings['api_region'] && $wpsl_settings['api_geocode_component'] ) {
1762
- $locator_map_settings['geocodeComponents'] = apply_filters( 'wpsl_geocode_components', array(
1763
- 'country' => strtoupper( $wpsl_settings['api_region'] )
1764
- ) );
1765
- }
1766
-
1767
- // If the marker clusters are enabled, include the js file and marker settings.
1768
- if ( $wpsl_settings['marker_clusters'] ) {
1769
- wp_enqueue_script( 'wpsl-cluster', WPSL_URL . 'js/markerclusterer'. $min .'.js', array( 'wpsl-js' ), WPSL_VERSION_NUM, true ); //not minified version is in the /js folder
1770
-
1771
- $base_settings['clusterZoom'] = $wpsl_settings['cluster_zoom'];
1772
- $base_settings['clusterSize'] = $wpsl_settings['cluster_size'];
1773
- $base_settings['clusterImagePath'] = 'https://cdn.rawgit.com/googlemaps/js-marker-clusterer/gh-pages/images/m';
1774
- }
1775
-
1776
- // Check if we need to include the infobox script and settings.
1777
- if ( $wpsl_settings['infowindow_style'] == 'infobox' ) {
1778
- wp_enqueue_script( 'wpsl-infobox', WPSL_URL . 'js/infobox'. $min .'.js', array( 'wpsl-gmap' ), WPSL_VERSION_NUM, true ); // Not minified version is in the /js folder
1779
-
1780
- $base_settings['infoWindowStyle'] = $wpsl_settings['infowindow_style'];
1781
- $base_settings = $this->get_infobox_settings( $base_settings );
1782
- }
1783
-
1784
- // Include the map style.
1785
- if ( !empty( $wpsl_settings['map_style'] ) ) {
1786
- $base_settings['mapStyle'] = strip_tags( stripslashes( json_decode( $wpsl_settings['map_style'] ) ) );
1787
- }
1788
-
1789
- wp_enqueue_script( 'wpsl-js', apply_filters( 'wpsl_gmap_js', WPSL_URL . 'js/wpsl-gmap'. $min .'.js' ), array( 'jquery' ), WPSL_VERSION_NUM, true );
1790
- wp_enqueue_script( 'underscore' );
1791
-
1792
- // Check if we need to include all the settings and labels or just a part of them.
1793
- if ( in_array( 'wpsl_store_locator', $this->load_scripts ) ) {
1794
- $settings = wp_parse_args( $base_settings, $locator_map_settings );
1795
- $template = 'wpsl_store_locator';
1796
- $labels = array(
1797
- 'preloader' => $wpsl->i18n->get_translation( 'preloader_label', __( 'Searching...', 'wpsl' ) ),
1798
- 'noResults' => $wpsl->i18n->get_translation( 'no_results_label', __( 'No results found', 'wpsl' ) ),
1799
- 'moreInfo' => $wpsl->i18n->get_translation( 'more_label', __( 'More info', 'wpsl' ) ),
1800
- 'generalError' => $wpsl->i18n->get_translation( 'error_label', __( 'Something went wrong, please try again!', 'wpsl' ) ),
1801
- 'queryLimit' => $wpsl->i18n->get_translation( 'limit_label', __( 'API usage limit reached', 'wpsl' ) ),
1802
- 'directions' => $wpsl->i18n->get_translation( 'directions_label', __( 'Directions', 'wpsl' ) ),
1803
- 'noDirectionsFound' => $wpsl->i18n->get_translation( 'no_directions_label', __( 'No route could be found between the origin and destination', 'wpsl' ) ),
1804
- 'startPoint' => $wpsl->i18n->get_translation( 'start_label', __( 'Start location', 'wpsl' ) ),
1805
- 'back' => $wpsl->i18n->get_translation( 'back_label', __( 'Back', 'wpsl' ) ),
1806
- 'streetView' => $wpsl->i18n->get_translation( 'street_view_label', __( 'Street view', 'wpsl' ) ),
1807
- 'zoomHere' => $wpsl->i18n->get_translation( 'zoom_here_label', __( 'Zoom here', 'wpsl' ) )
1808
- );
1809
-
1810
- wp_localize_script( 'wpsl-js', 'wpslLabels', $labels );
1811
- wp_localize_script( 'wpsl-js', 'wpslGeolocationErrors', $this->geolocation_errors() );
1812
- } else {
1813
- $template = '';
1814
- $settings = $base_settings;
1815
- }
1816
-
1817
- // Check if we need to overwrite JS settings that are set through the [wpsl] shortcode.
1818
- if ( $this->sl_shortcode_atts && isset( $this->sl_shortcode_atts['js'] ) ) {
1819
- foreach ( $this->sl_shortcode_atts['js'] as $shortcode_key => $shortcode_val ) {
1820
- $settings[$shortcode_key] = $shortcode_val;
1821
- }
1822
- }
1823
-
1824
- wp_localize_script( 'wpsl-js', 'wpslSettings', apply_filters( 'wpsl_js_settings', $settings ) );
1825
-
1826
- wpsl_create_underscore_templates( $template );
1827
-
1828
- if ( !empty( $this->store_map_data ) ) {
1829
- $i = 0;
1830
-
1831
- foreach ( $this->store_map_data as $map ) {
1832
- wp_localize_script( 'wpsl-js', 'wpslMap_' . $i, $map );
1833
-
1834
- $i++;
1835
- }
1836
- }
1837
- }
1838
-
1839
- /**
1840
- * Get the infobox settings.
1841
- *
1842
- * @since 2.0.0
1843
- * @see http://google-maps-utility-library-v3.googlecode.com/svn/trunk/infobox/docs/reference.html
1844
- * @param array $settings The plugin settings used on the front-end in js
1845
- * @return array $settings The plugin settings including the infobox settings
1846
- */
1847
- public function get_infobox_settings( $settings ) {
1848
-
1849
- $infobox_settings = apply_filters( 'wpsl_infobox_settings', array(
1850
- 'infoBoxClass' => 'wpsl-infobox',
1851
- 'infoBoxCloseMargin' => '2px', // The margin can be written in css style, so 2px 2px 4px 2px for top, right, bottom, left
1852
- 'infoBoxCloseUrl' => '//www.google.com/intl/en_us/mapfiles/close.gif',
1853
- 'infoBoxClearance' => '40,40',
1854
- 'infoBoxDisableAutoPan' => 0,
1855
- 'infoBoxEnableEventPropagation' => 0,
1856
- 'infoBoxPixelOffset' => '-52,-45',
1857
- 'infoBoxZindex' => 1500
1858
- ) );
1859
-
1860
- foreach ( $infobox_settings as $infobox_key => $infobox_setting ) {
1861
- $settings[$infobox_key] = $infobox_setting;
1862
- }
1863
-
1864
- return $settings;
1865
- }
1866
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1867
  }
1
+ <?php
2
+ /**
3
+ * Frontend class
4
+ *
5
+ * @author Tijmen Smit
6
+ * @since 1.0.0
7
+ */
8
+
9
+ if ( !defined( 'ABSPATH' ) ) exit;
10
+
11
+ if ( !class_exists( 'WPSL_Frontend' ) ) {
12
+
13
+ /**
14
+ * Handle the frontend of the store locator
15
+ *
16
+ * @since 1.0.0
17
+ */
18
+ class WPSL_Frontend {
19
+
20
+ /**
21
+ * Keep track which scripts we need to load
22
+ *
23
+ * @since 2.0.0
24
+ */
25
+ private $load_scripts = array();
26
+
27
+ /**
28
+ * Keep track of the amount of maps on the page
29
+ *
30
+ * @since 2.0.0
31
+ */
32
+ private static $map_count = 0;
33
+
34
+ /*
35
+ * Holds the shortcode atts for the [wpsl] shortcode.
36
+ *
37
+ * Used to overwrite the settings just before
38
+ * they are send to wp_localize_script.
39
+ *
40
+ * @since 2.1.1
41
+ */
42
+ public $sl_shortcode_atts;
43
+
44
+ private $store_map_data = array();
45
+
46
+
47
+ /**
48
+ * Class constructor
49
+ */
50
+ public function __construct() {
51
+
52
+ $this->includes();
53
+
54
+ add_action( 'wp_ajax_store_search', array( $this, 'store_search' ) );
55
+ add_action( 'wp_ajax_nopriv_store_search', array( $this, 'store_search' ) );
56
+ add_action( 'wp_enqueue_scripts', array( $this, 'add_frontend_styles' ) );
57
+ add_action( 'wp_footer', array( $this, 'add_frontend_scripts' ) );
58
+
59
+ add_filter( 'the_content', array( $this, 'cpt_template' ) );
60
+
61
+ add_shortcode( 'wpsl', array( $this, 'show_store_locator' ) );
62
+ add_shortcode( 'wpsl_address', array( $this, 'show_store_address' ) );
63
+ add_shortcode( 'wpsl_hours', array( $this, 'show_opening_hours' ) );
64
+ add_shortcode( 'wpsl_map', array( $this, 'show_store_map' ) );
65
+ }
66
+
67
+ /**
68
+ * Include the required front-end files.
69
+ *
70
+ * @since 2.0.0
71
+ * @return void
72
+ */
73
+ public function includes() {
74
+ require_once( WPSL_PLUGIN_DIR . 'frontend/underscore-functions.php' );
75
+ }
76
+
77
+ /**
78
+ * Handle the Ajax search on the frontend.
79
+ *
80
+ * @since 1.0.0
81
+ * @return json A list of store locations that are located within the selected search radius
82
+ */
83
+ public function store_search() {
84
+
85
+ global $wpsl_settings;
86
+
87
+ /*
88
+ * Check if auto loading the locations on page load is enabled.
89
+ *
90
+ * If so then we save the store data in a transient to prevent a long loading time
91
+ * in case a large amount of locations need to be displayed.
92
+ *
93
+ * The SQL query that selects nearby locations doesn't take that long,
94
+ * but collecting all the store meta data in get_store_meta_data() for hunderds,
95
+ * or thousands of stores can make it really slow.
96
+ */
97
+ if ( $wpsl_settings['autoload'] && isset( $_GET['autoload'] ) && $_GET['autoload'] && !$wpsl_settings['debug'] && !isset( $_GET['skip_cache'] ) ) {
98
+ $transient_name = $this->create_transient_name();
99
+
100
+ if ( false === ( $store_data = get_transient( 'wpsl_autoload_' . $transient_name ) ) ) {
101
+ $store_data = $this->find_nearby_locations();
102
+
103
+ if ( $store_data ) {
104
+ set_transient( 'wpsl_autoload_' . $transient_name, $store_data, 0 );
105
+ }
106
+ }
107
+ } else {
108
+ $store_data = $this->find_nearby_locations();
109
+ }
110
+
111
+ do_action( 'wpsl_store_search' );
112
+
113
+ wp_send_json( $store_data );
114
+
115
+ exit();
116
+ }
117
+
118
+ /**
119
+ * Create the name used in the wpsl autoload transient.
120
+ *
121
+ * @since 2.1.1
122
+ * @return string $transient_name The transient name.
123
+ */
124
+ public function create_transient_name() {
125
+
126
+ global $wpsl, $wpsl_settings;
127
+
128
+ $name_section = array();
129
+
130
+ // Include the set autoload limit.
131
+ if ( $wpsl_settings['autoload'] && $wpsl_settings['autoload_limit'] ) {
132
+ $name_section[] = absint( $wpsl_settings['autoload_limit'] );
133
+ }
134
+
135
+ /*
136
+ * Check if we need to include the cat id(s) in the transient name.
137
+ *
138
+ * This can only happen if the user used the
139
+ * 'category' attr on the wpsl shortcode.
140
+ */
141
+ if ( isset( $_GET['filter'] ) && $_GET['filter'] ) {
142
+ $name_section[] = absint( str_replace( ',', '', $_GET['filter'] ) );
143
+ }
144
+
145
+ // Include the lat value from the start location.
146
+ if ( isset( $_GET['lat'] ) && $_GET['lat'] ) {
147
+ $name_section[] = absint( str_replace( '.', '', $_GET['lat'] ) );
148
+ }
149
+
150
+ /*
151
+ * If a multilingual plugin ( WPML or qTranslate X ) is active then we have
152
+ * to make sure each language has his own unique transient. We do this by
153
+ * including the lang code in the transient name.
154
+ *
155
+ * Otherwise if the language is for example set to German on page load,
156
+ * and the user switches to Spanish, then he would get the incorrect
157
+ * permalink structure ( /de/.. instead or /es/.. ) and translated
158
+ * store details.
159
+ */
160
+ $lang_code = $wpsl->i18n->check_multilingual_code();
161
+
162
+ if ( $lang_code ) {
163
+ $name_section[] = $lang_code;
164
+ }
165
+
166
+ $transient_name = implode( '_', $name_section );
167
+
168
+ /*
169
+ * If the distance unit filter ( wpsl_distance_unit ) is used to change the km / mi unit based on
170
+ * the location of the IP, then we include the km / mi in the transient name. This is done to
171
+ * prevent users from seeing the wrong distances from the cached data.
172
+ *
173
+ * This way one data set can include the distance in km, and the other one the distance in miles.
174
+ */
175
+ if ( has_filter( 'wpsl_distance_unit' ) ) {
176
+ $transient_name = $transient_name . '_' . wpsl_get_distance_unit();
177
+ }
178
+
179
+ return $transient_name;
180
+ }
181
+
182
+ /**
183
+ * Find store locations that are located within the selected search radius.
184
+ *
185
+ * This happens by calculating the distance between the
186
+ * latlng of the searched location, and the latlng from
187
+ * the stores in the db.
188
+ *
189
+ * @since 2.0.0
190
+ * @param array $args The arguments to use in the SQL query, only used by add-ons
191
+ * @return void|array $store_data The list of stores that fall within the selected range.
192
+ */
193
+ public function find_nearby_locations( $args = array() ) {
194
+
195
+ global $wpdb, $wpsl, $wpsl_settings;
196
+
197
+ $store_data = array();
198
+
199
+ /*
200
+ * Set the correct earth radius in either km or miles.
201
+ * We need this to calculate the distance between two coordinates.
202
+ */
203
+ $placeholder_values[] = ( wpsl_get_distance_unit() == 'km' ) ? 6371 : 3959;
204
+
205
+ // The placeholder values for the prepared statement in the SQL query.
206
+ if ( empty( $args ) ) {
207
+ $args = $_GET;
208
+ }
209
+
210
+ array_push( $placeholder_values, $args['lat'], $args['lng'], $args['lat'] );
211
+
212
+ // Check if we need to filter the results by category.
213
+ if ( isset( $args['filter'] ) && $args['filter'] ) {
214
+ $filter_ids = array_map( 'absint', explode( ',', $args['filter'] ) );
215
+ $cat_filter = "INNER JOIN $wpdb->term_relationships AS term_rel ON posts.ID = term_rel.object_id
216
+ INNER JOIN $wpdb->term_taxonomy AS term_tax ON term_rel.term_taxonomy_id = term_tax.term_taxonomy_id
217
+ AND term_tax.taxonomy = 'wpsl_store_category'
218
+ AND term_tax.term_id IN (" . implode( ',', $filter_ids ) . ")";
219
+ } else {
220
+ $cat_filter = '';
221
+ }
222
+
223
+ /*
224
+ * If WPML is active we include 'GROUP BY lat' in the sql query
225
+ * to prevent duplicate locations from showing up in the results.
226
+ *
227
+ * This is a problem when a store location for example
228
+ * exists in 4 different languages. They would all fall within
229
+ * the selected radius, but we only need one store ID for the 'icl_object_id'
230
+ * function to get the correct store ID for the current language.
231
+ */
232
+ if ( $wpsl->i18n->wpml_exists() ) {
233
+ $group_by = 'GROUP BY lat';
234
+ } else {
235
+ $group_by = 'GROUP BY posts.ID';
236
+ }
237
+
238
+ /*
239
+ * If autoload is enabled we need to check if there is a limit to the
240
+ * amount of locations we need to show.
241
+ *
242
+ * Otherwise include the radius and max results limit in the sql query.
243
+ */
244
+ if ( isset( $args['autoload'] ) && $args['autoload'] ) {
245
+ $limit = '';
246
+
247
+ if ( $wpsl_settings['autoload_limit'] ) {
248
+ $limit = 'LIMIT %d';
249
+ $placeholder_values[] = $wpsl_settings['autoload_limit'];
250
+ }
251
+
252
+ $sql_sort = 'ORDER BY distance '. $limit;
253
+ } else {
254
+ array_push( $placeholder_values, $this->check_store_filter( $args, 'search_radius' ), $this->check_store_filter( $args, 'max_results' ) );
255
+ $sql_sort = 'HAVING distance < %d ORDER BY distance LIMIT 0, %d';
256
+ }
257
+
258
+ $placeholder_values = apply_filters( 'wpsl_sql_placeholder_values', $placeholder_values );
259
+
260
+ /*
261
+ * The sql that will check which store locations fall within
262
+ * the selected radius based on the lat and lng values.
263
+ */
264
+ $sql = apply_filters( 'wpsl_sql',
265
+ "SELECT post_lat.meta_value AS lat,
266
+ post_lng.meta_value AS lng,
267
+ posts.ID,
268
+ ( %d * acos( cos( radians( %s ) ) * cos( radians( post_lat.meta_value ) ) * cos( radians( post_lng.meta_value ) - radians( %s ) ) + sin( radians( %s ) ) * sin( radians( post_lat.meta_value ) ) ) )
269
+ AS distance
270
+ FROM $wpdb->posts AS posts
271
+ INNER JOIN $wpdb->postmeta AS post_lat ON post_lat.post_id = posts.ID AND post_lat.meta_key = 'wpsl_lat'
272
+ INNER JOIN $wpdb->postmeta AS post_lng ON post_lng.post_id = posts.ID AND post_lng.meta_key = 'wpsl_lng'
273
+ $cat_filter
274
+ WHERE posts.post_type = 'wpsl_stores'
275
+ AND posts.post_status = 'publish' $group_by $sql_sort"
276
+ );
277
+
278
+ $stores = $wpdb->get_results( $wpdb->prepare( $sql, $placeholder_values ) );
279
+
280
+ if ( $stores ) {
281
+ $store_data = apply_filters( 'wpsl_store_data', $this->get_store_meta_data( $stores ) );
282
+ } else {
283
+ $store_data = apply_filters( 'wpsl_no_results_sql', '' );
284
+ }
285
+
286
+ return $store_data;
287
+ }
288
+
289
+ /**
290
+ * Get the post meta data for the selected stores.
291
+ *
292
+ * @since 2.0.0
293
+ * @param object $stores
294
+ * @return array $all_stores The stores that fall within the selected range with the post meta data.
295
+ */
296
+ public function get_store_meta_data( $stores ) {
297
+
298
+ global $wpsl_settings, $wpsl;
299
+
300
+ $all_stores = array();
301
+
302
+ // Get the list of store fields that we need to filter out of the post meta data.
303
+ $meta_field_map = $this->frontend_meta_fields();
304
+
305
+ foreach ( $stores as $store_key => $store ) {
306
+
307
+ // If WPML is active try to get the id of the translated page.
308
+ if ( $wpsl->i18n->wpml_exists() ) {
309
+ $store->ID = $wpsl->i18n->maybe_get_wpml_id( $store->ID );
310
+
311
+ if ( !$store->ID ) {
312
+ continue;
313
+ }
314
+ }
315
+
316
+ // Get the post meta data for each store that was within the range of the search radius.
317
+ $custom_fields = get_post_custom( $store->ID );
318
+
319
+ foreach ( $meta_field_map as $meta_key => $meta_value ) {
320
+
321
+ if ( isset( $custom_fields[$meta_key][0] ) ) {
322
+ if ( ( isset( $meta_value['type'] ) ) && ( !empty( $meta_value['type'] ) ) ) {
323
+ $meta_type = $meta_value['type'];
324
+ } else {
325
+ $meta_type = '';
326
+ }
327
+
328
+ // If we need to hide the opening hours, and the current meta type is set to hours we skip it.
329
+ if ( $wpsl_settings['hide_hours'] && $meta_type == 'hours' ) {
330
+ continue;
331
+ }
332
+
333
+ // Make sure the data is safe to use on the frontend and in the format we expect it to be.
334
+ switch ( $meta_type ) {
335
+ case 'numeric':
336
+ $meta_data = ( is_numeric( $custom_fields[$meta_key][0] ) ) ? $custom_fields[$meta_key][0] : 0 ;
337
+ break;
338
+ case 'email':
339
+ $meta_data = sanitize_email( $custom_fields[$meta_key][0] );
340
+ break;
341
+ case 'url':
342
+ $meta_data = esc_url( $custom_fields[$meta_key][0] );
343
+ break;
344
+ case 'hours':
345
+ $meta_data = $this->get_opening_hours( $custom_fields[$meta_key][0], apply_filters( 'wpsl_hide_closed_hours', false ) );
346
+ break;
347
+ case 'wp_editor':
348
+ case 'textarea':
349
+ $meta_data = wp_kses_post( wpautop( $custom_fields[$meta_key][0] ) );
350
+ break;
351
+ case 'text':
352
+ default:
353
+ $meta_data = sanitize_text_field( stripslashes( $custom_fields[$meta_key][0] ) );
354
+ break;
355
+ }
356
+
357
+ $store_meta[$meta_value['name']] = $meta_data;
358
+ } else {
359
+ $store_meta[$meta_value['name']] = '';
360
+ }
361
+
362
+ /*
363
+ * Include the post content if the "More info" option is enabled on the settings page,
364
+ * or if $include_post_content is set to true through the 'wpsl_include_post_content' filter.
365
+ */
366
+ if ( ( $wpsl_settings['more_info'] && $wpsl_settings['more_info_location'] == 'store listings' ) || apply_filters( 'wpsl_include_post_content', false ) ) {
367
+ $page_object = get_post( $store->ID );
368
+
369
+ // Check if we need to strip the shortcode from the post content.
370
+ if ( apply_filters( 'wpsl_strip_content_shortcode', true ) ) {
371
+ $post_content = strip_shortcodes( $page_object->post_content );
372
+ } else {
373
+ $post_content = $page_object->post_content;
374
+ }
375
+
376
+ $store_meta['description'] = apply_filters( 'the_content', $post_content );
377
+ }
378
+
379
+ $store_meta['store'] = get_the_title( $store->ID );
380
+ $store_meta['thumb'] = $this->get_store_thumb( $store->ID, $store_meta['store'] );
381
+ $store_meta['id'] = $store->ID;
382
+
383
+ if ( !$wpsl_settings['hide_distance'] ) {
384
+ $store_meta['distance'] = round( $store->distance, 1 );
385
+ }
386
+
387
+ if ( $wpsl_settings['permalinks'] ) {
388
+ $store_meta['permalink'] = get_permalink( $store->ID );
389
+ }
390
+ }
391
+
392
+ $all_stores[] = apply_filters( 'wpsl_store_meta', $store_meta, $store->ID );
393
+ }
394
+
395
+ return $all_stores;
396
+ }
397
+
398
+ /**
399
+ * The store meta fields that are included in the json output.
400
+ *
401
+ * The wpsl_ is the name in db, the name value is used as the key in the json output.
402
+ *
403
+ * The type itself is used to determine how the value should be sanitized.
404
+ * Text will go through sanitize_text_field, email through sanitize_email and so on.
405
+ *
406
+ * If no type is set it will default to sanitize_text_field.
407
+ *
408
+ * @since 2.0.0
409
+ * @return array $store_fields The names of the meta fields used by the store
410
+ */
411
+ public function frontend_meta_fields() {
412
+
413
+ $store_fields = array(
414
+ 'wpsl_address' => array(
415
+ 'name' => 'address'
416
+ ),
417
+ 'wpsl_address2' => array(
418
+ 'name' => 'address2'
419
+ ),
420
+ 'wpsl_city' => array(
421
+ 'name' => 'city'
422
+ ),
423
+ 'wpsl_state' => array(
424
+ 'name' => 'state'
425
+ ),
426
+ 'wpsl_zip' => array(
427
+ 'name' => 'zip'
428
+ ),
429
+ 'wpsl_country' => array(
430
+ 'name' => 'country'
431
+ ),
432
+ 'wpsl_lat' => array(
433
+ 'name' => 'lat',
434
+ 'type' => 'numeric'
435
+ ),
436
+ 'wpsl_lng' => array(
437
+ 'name' => 'lng',
438
+ 'type' => 'numeric'
439
+ ),
440
+ 'wpsl_phone' => array(
441
+ 'name' => 'phone'
442
+ ),
443
+ 'wpsl_fax' => array(
444
+ 'name' => 'fax'
445
+ ),
446
+ 'wpsl_email' => array(
447
+ 'name' => 'email',
448
+ 'type' => 'email'
449
+ ),
450
+ 'wpsl_hours' => array(
451
+ 'name' => 'hours',
452
+ 'type' => 'hours'
453
+ ),
454
+ 'wpsl_url' => array(
455
+ 'name' => 'url',
456
+ 'type' => 'url'
457
+ )
458
+ );
459
+
460
+ return apply_filters( 'wpsl_frontend_meta_fields', $store_fields );
461
+ }
462
+
463
+ /**
464
+ * Get the store thumbnail.
465
+ *
466
+ * @since 2.0.0
467
+ * @param string $post_id The post id of the store
468
+ * @param string $store_name The name of the store
469
+ * @return void|string $thumb The html img tag
470
+ */
471
+ public function get_store_thumb( $post_id, $store_name ) {
472
+
473
+ $attr = array(
474
+ 'class' => 'wpsl-store-thumb',
475
+ 'alt' => $store_name
476
+ );
477
+
478
+ $thumb = get_the_post_thumbnail( $post_id, $this->get_store_thumb_size(), apply_filters( 'wpsl_thumb_attr', $attr ) );
479
+
480
+ return $thumb;
481
+ }
482
+
483
+ /**
484
+ * Get the store thumbnail size.
485
+ *
486
+ * @since 2.0.0
487
+ * @return array $size The thumb format
488
+ */
489
+ public function get_store_thumb_size() {
490
+
491
+ $size = apply_filters( 'wpsl_thumb_size', array( 45, 45 ) );
492
+
493
+ return $size;
494
+ }
495
+
496
+ /**
497
+ * Get the opening hours in the correct format.
498
+ *
499
+ * Either convert the hour values that are set through
500
+ * a dropdown to a table, or wrap the textarea input in a <p>.
501
+ *
502
+ * Note: The opening hours can only be set in the textarea format by users who upgraded from 1.x.
503
+ *
504
+ * @since 2.0.0
505
+ * @param array|string $hours The opening hours
506
+ * @param boolean $hide_closed Hide the days were the location is closed
507
+ * @return string $hours The formated opening hours
508
+ */
509
+ public function get_opening_hours( $hours, $hide_closed ) {
510
+
511
+ $hours = maybe_unserialize( $hours );
512
+
513
+ /*
514
+ * If the hours are set through the dropdown then we create a table for the opening hours.
515
+ * Otherwise we output the data entered in the textarea.
516
+ */
517
+ if ( is_array( $hours ) ) {
518
+ $hours = $this->create_opening_hours_tabel( $hours, $hide_closed );
519
+ } else {
520
+ $hours = wp_kses_post( wpautop( $hours ) );
521
+ }
522
+
523
+ return $hours;
524
+ }
525
+
526
+ /**
527
+ * Create a table for the opening hours.
528
+ *
529
+ * @since 2.0.0
530
+ * @todo add schema.org support.
531
+ * @param array $hours The opening hours
532
+ * @param boolean $hide_closed Hide the days where the location is closed
533
+ * @return string $hour_table The opening hours sorted in a table
534
+ */
535
+ public function create_opening_hours_tabel( $hours, $hide_closed ) {
536
+
537
+ $opening_days = wpsl_get_weekdays();
538
+
539
+ // Make sure that we have actual opening hours, and not every day is empty.
540
+ if ( $this->not_always_closed( $hours ) ) {
541
+ $hour_table = '<table role="presentation" class="wpsl-opening-hours">';
542
+
543
+ foreach ( $opening_days as $index => $day ) {
544
+ $i = 0;
545
+ $hour_count = count( $hours[$index] );
546
+
547
+ // If we need to hide days that are set to closed then skip them.
548
+ if ( $hide_closed && !$hour_count ) {
549
+ continue;
550
+ }
551
+
552
+ $hour_table .= '<tr>';
553
+ $hour_table .= '<td>' . esc_html( $day ) . '</td>';
554
+
555
+ // If we have opening hours we show them, otherwise just show 'Closed'.
556
+ if ( $hour_count > 0 ) {
557
+ $hour_table .= '<td>';
558
+
559
+ while ( $i < $hour_count ) {
560
+ $hour = explode( ',', $hours[$index][$i] );
561
+ $hour_table .= '<time>' . esc_html( $hour[0] ) . ' - ' . esc_html( $hour[1] ) . '</time>';
562
+
563
+ $i++;
564
+ }
565
+
566
+ $hour_table .= '</td>';
567
+ } else {
568
+ $hour_table .= '<td>' . __( 'Closed', 'wpsl' ) . '</td>';
569
+ }
570
+
571
+ $hour_table .= '</tr>';
572
+ }
573
+
574
+ $hour_table .= '</table>';
575
+
576
+ return $hour_table;
577
+ }
578
+ }
579
+
580
+ /**
581
+ * Create the wpsl post type output.
582
+ *
583
+ * If you want to create a custom template you need to
584
+ * create a single-wpsl_stores.php file in your theme folder.
585
+ * You can see an example here https://wpstorelocator.co/document/create-custom-store-page-template/
586
+ *
587
+ * @since 2.0.0
588
+ * @param string $content
589
+ * @return string $content
590
+ */
591
+ public function cpt_template( $content ) {
592
+
593
+ global $wpsl_settings, $post;
594
+
595
+ // Prevent duplicate output when the Twenty Nineteen theme is active.
596
+ $skip_status = ( get_option( 'template' ) === 'twentynineteen' ) ? true : false;
597
+ $skip_cpt_template = apply_filters( 'wpsl_skip_cpt_template', $skip_status );
598
+
599
+ if ( isset( $post->post_type ) && $post->post_type == 'wpsl_stores' && is_single() && in_the_loop() && !$skip_cpt_template ) {
600
+ array_push( $this->load_scripts, 'wpsl_base' );
601
+
602
+ $content .= '[wpsl_map]';
603
+ $content .= '[wpsl_address]';
604
+
605
+ if ( !$wpsl_settings['hide_hours'] ) {
606
+ $content .= '[wpsl_hours]';
607
+ }
608
+ }
609
+
610
+ return $content;
611
+ }
612
+
613
+ /**
614
+ * Handle the [wpsl] shortcode attributes.
615
+ *
616
+ * @since 2.1.1
617
+ * @param array $atts Shortcode attributes
618
+ */
619
+ public function check_sl_shortcode_atts( $atts ) {
620
+
621
+ /*
622
+ * Use a custom start location?
623
+ *
624
+ * If the provided location fails to geocode,
625
+ * then the start location from the settings page is used.
626
+ */
627
+ if ( isset( $atts['start_location'] ) && $atts['start_location'] ) {
628
+ $start_latlng = wpsl_check_latlng_transient( $atts['start_location'] );
629
+
630
+ if ( isset( $start_latlng ) && $start_latlng ) {
631
+ $this->sl_shortcode_atts['js']['startLatlng'] = $start_latlng;
632
+ }
633
+ }
634
+
635
+ if ( isset( $atts['auto_locate'] ) && $atts['auto_locate'] ) {
636
+ $this->sl_shortcode_atts['js']['autoLocate'] = ( $atts['auto_locate'] == 'true' ) ? 1 : 0;
637
+ }
638
+
639
+ // Change the category slugs into category ids.
640
+ if ( isset( $atts['category'] ) && $atts['category'] ) {
641
+ $term_ids = wpsl_get_term_ids( $atts['category'] );
642
+
643
+ if ( $term_ids ) {
644
+ $this->sl_shortcode_atts['js']['categoryIds'] = implode( ',', $term_ids );
645
+ }
646
+ }
647
+
648
+ if ( isset( $atts['category_selection'] ) && $atts['category_selection'] ) {
649
+ $this->sl_shortcode_atts['category_selection'] = wpsl_get_term_ids( $atts['category_selection'] );
650
+ }
651
+
652
+ if ( isset( $atts['category_filter_type'] ) && in_array( $atts['category_filter_type'], array( 'dropdown', 'checkboxes' ) ) ) {
653
+ $this->sl_shortcode_atts['category_filter_type'] = $atts['category_filter_type'];
654
+ }
655
+
656
+ if ( isset( $atts['checkbox_columns'] ) && is_numeric( $atts['checkbox_columns'] ) ) {
657
+ $this->sl_shortcode_atts['checkbox_columns'] = $atts['checkbox_columns'];
658
+ }
659
+
660
+ if ( isset( $atts['map_type'] ) && array_key_exists( $atts['map_type'], wpsl_get_map_types() ) ) {
661
+ $this->sl_shortcode_atts['js']['mapType'] = $atts['map_type'];
662
+ }
663
+
664
+ if ( isset( $atts['start_marker'] ) && $atts['start_marker'] ) {
665
+ $this->sl_shortcode_atts['js']['startMarker'] = $atts['start_marker'] . '@2x.png';
666
+ }
667
+
668
+ if ( isset( $atts['store_marker'] ) && $atts['store_marker'] ) {
669
+ $this->sl_shortcode_atts['js']['storeMarker'] = $atts['store_marker'] . '@2x.png';
670
+ }
671
+ }
672
+
673
+ /**
674
+ * Handle the [wpsl] shortcode.
675
+ *
676
+ * @since 1.0.0
677
+ * @param array $atts Shortcode attributes
678
+ * @return string $output The wpsl template
679
+ */
680
+ public function show_store_locator( $atts ) {
681
+
682
+ global $wpsl, $wpsl_settings;
683
+
684
+ $atts = shortcode_atts( array(
685
+ 'template' => $wpsl_settings['template_id'],
686
+ 'start_location' => '',
687
+ 'auto_locate' => '',
688
+ 'category' => '',
689
+ 'category_selection' => '',
690
+ 'category_filter_type' => '',
691
+ 'checkbox_columns' => '3',
692
+ 'map_type' => '',
693
+ 'start_marker' => '',
694
+ 'store_marker' => ''
695
+ ), $atts );
696
+
697
+ $this->check_sl_shortcode_atts( $atts );
698
+
699
+ // Make sure the required scripts are included for the wpsl shortcode.
700
+ array_push( $this->load_scripts, 'wpsl_store_locator' );
701
+
702
+ $template_details = $wpsl->templates->get_template_details( $atts['template'] );
703
+
704
+ $output = include( $template_details['path'] );
705
+
706
+ return $output;
707
+ }
708
+
709
+ /**
710
+ * Handle the [wpsl_address] shortcode.
711
+ *
712
+ * @since 2.0.0
713
+ * @todo add schema.org support.
714
+ * @param array $atts Shortcode attributes
715
+ * @return void|string $output The store address
716
+ */
717
+ public function show_store_address( $atts ) {
718
+
719
+ global $post, $wpsl_settings, $wpsl;
720
+
721
+ $atts = wpsl_bool_check( shortcode_atts( apply_filters( 'wpsl_address_shortcode_defaults', array(
722
+ 'id' => '',
723
+ 'name' => true,
724
+ 'address' => true,
725
+ 'address2' => true,
726
+ 'city' => true,
727
+ 'state' => true,
728
+ 'zip' => true,
729
+ 'country' => true,
730
+ 'phone' => true,
731
+ 'fax' => true,
732
+ 'email' => true,
733
+ 'url' => true,
734
+ 'directions' => false,
735
+ 'clickable_contact_details' => (bool) $wpsl_settings['clickable_contact_details']
736
+ ) ), $atts ) );
737
+
738
+ if ( get_post_type() == 'wpsl_stores' ) {
739
+ if ( empty( $atts['id'] ) ) {
740
+ if ( isset( $post->ID ) ) {
741
+ $atts['id'] = $post->ID;
742
+ } else {
743
+ return;
744
+ }
745
+ }
746
+ } else if ( empty( $atts['id'] ) ) {
747
+ return __( 'If you use the [wpsl_address] shortcode outside a store page you need to set the ID attribute.', 'wpsl' );
748
+ }
749
+
750
+ $content = '<div class="wpsl-locations-details">';
751
+
752
+ if ( $atts['name'] && $name = get_the_title( $atts['id'] ) ) {
753
+ $content .= '<span><strong>' . esc_html( $name ) . '</strong></span>';
754
+ }
755
+
756
+ $content .= '<div class="wpsl-location-address">';
757
+
758
+ if ( $atts['address'] && $address = get_post_meta( $atts['id'], 'wpsl_address', true ) ) {
759
+ $content .= '<span>' . esc_html( $address ) . '</span><br/>';
760
+ }
761
+
762
+ if ( $atts['address2'] && $address2 = get_post_meta( $atts['id'], 'wpsl_address2', true ) ) {
763
+ $content .= '<span>' . esc_html( $address2 ) . '</span><br/>';
764
+ }
765
+
766
+ $address_format = explode( '_', $wpsl_settings['address_format'] );
767
+ $count = count( $address_format );
768
+ $i = 1;
769
+
770
+ // Loop over the address parts to make sure they are shown in the right order.
771
+ foreach ( $address_format as $address_part ) {
772
+
773
+ // Make sure the shortcode attribute is set to true for the $address_part, and it's not the 'comma' part.
774
+ if ( $address_part != 'comma' && $atts[$address_part] ) {
775
+ $post_meta = get_post_meta( $atts['id'], 'wpsl_' . $address_part, true );
776
+
777
+ if ( $post_meta ) {
778
+
779
+ /*
780
+ * Check if the next part of the address is set to 'comma'.
781
+ * If so add the, after the current address part, otherwise just show a space
782
+ */
783
+ if ( isset( $address_format[$i] ) && ( $address_format[$i] == 'comma' ) ) {
784
+ $punctuation = ', ';
785
+ } else {
786
+ $punctuation = ' ';
787
+ }
788
+
789
+ // If we have reached the last item add a <br /> behind it.
790
+ $br = ( $count == $i ) ? '<br />' : '';
791
+
792
+ $content .= '<span>' . esc_html( $post_meta ) . $punctuation . '</span>' . $br;
793
+ }
794
+ }
795
+
796
+ $i++;
797
+ }
798
+
799
+ if ( $atts['country'] && $country = get_post_meta( $atts['id'], 'wpsl_country', true ) ) {
800
+ $content .= '<span>' . esc_html( $country ) . '</span>';
801
+ }
802
+
803
+ $content .= '</div>';
804
+
805
+ // If either the phone, fax, email or url is set to true, then add the wrap div for the contact details.
806
+ if ( $atts['phone'] || $atts['fax'] || $atts['email'] || $atts['url'] ) {
807
+ $phone = get_post_meta( $atts['id'], 'wpsl_phone', true );
808
+ $fax = get_post_meta( $atts['id'], 'wpsl_fax', true );
809
+ $email = get_post_meta( $atts['id'], 'wpsl_email', true );
810
+
811
+ if ( $atts['clickable_contact_details'] ) {
812
+ $contact_details = array(
813
+ 'phone' => '<a href="tel:' . esc_attr( $phone ) . '">' . esc_html( $phone ) . '</a>',
814
+ 'fax' => '<a href="tel:' . esc_attr( $fax ) . '">' . esc_html( $fax ) . '</a>',
815
+ 'email' => '<a href="mailto:' . sanitize_email( $email ) . '">' . sanitize_email( $email ) . '</a>'
816
+ );
817
+ } else {
818
+ $contact_details = array(
819
+ 'phone' => esc_html( $phone ),
820
+ 'fax' => esc_html( $fax ),
821
+ 'email' => sanitize_email( $email )
822
+ );
823
+ }
824
+
825
+ $content .= '<div class="wpsl-contact-details">';
826
+
827
+ if ( $atts['phone'] && $phone ) {
828
+ $content .= esc_html( $wpsl->i18n->get_translation( 'phone_label', __( 'Phone', 'wpsl' ) ) ) . ': <span>' . $contact_details['phone'] . '</span><br/>';
829
+ }
830
+
831
+ if ( $atts['fax'] && $fax ) {
832
+ $content .= esc_html( $wpsl->i18n->get_translation( 'fax_label', __( 'Fax', 'wpsl' ) ) ) . ': <span>' . $contact_details['fax'] . '</span><br/>';
833
+ }
834
+
835
+ if ( $atts['email'] && $email ) {
836
+ $content .= esc_html( $wpsl->i18n->get_translation( 'email_label', __( 'Email', 'wpsl' ) ) ) . ': <span>' . $contact_details['email'] . '</span><br/>';
837
+ }
838
+
839
+ if ( $atts['url'] && $store_url = get_post_meta( $atts['id'], 'wpsl_url', true ) ) {
840
+ $new_window = ( $wpsl_settings['new_window'] ) ? 'target="_blank"' : '' ;
841
+ $content .= esc_html( $wpsl->i18n->get_translation( 'url_label', __( 'Url', 'wpsl' ) ) ) . ': <a ' . $new_window . ' href="' . esc_url( $store_url ) . '">' . esc_url( $store_url ) . '</a><br/>';
842
+ }
843
+
844
+ $content .= '</div>';
845
+ }
846
+
847
+ if ( $atts['directions'] && $address ) {
848
+ if ( $wpsl_settings['new_window'] ) {
849
+ $new_window = ' target="_blank"';
850
+ } else {
851
+ $new_window = '';
852
+ }
853
+
854
+ $content .= '<div class="wpsl-location-directions">';
855
+
856
+ $city = get_post_meta( $atts['id'], 'wpsl_city', true );
857
+ $country = get_post_meta( $atts['id'], 'wpsl_country', true );
858
+ $destination = $address . ',' . $city . ',' . $country;
859
+ $direction_url = "https://maps.google.com/maps?saddr=&daddr=" . urlencode( $destination ) . "&travelmode=" . strtolower( $this->get_directions_travel_mode() );
860
+
861
+ $content .= '<p><a ' . $new_window . ' href="' . esc_url( $direction_url ) . '">' . __( 'Directions', 'wpsl' ) . '</a></p>';
862
+ $content .= '</div>';
863
+ }
864
+
865
+ $content .= '</div>';
866
+
867
+ return $content;
868
+ }
869
+
870
+ /**
871
+ * Handle the [wpsl_hours] shortcode.
872
+ *
873
+ * @since 2.0.0
874
+ * @param array $atts Shortcode attributes
875
+ * @return void|string $output The opening hours
876
+ */
877
+ public function show_opening_hours( $atts ) {
878
+
879
+ global $wpsl_settings, $post;
880
+
881
+ // If the hours are set to hidden on the settings page, then respect that and don't continue.
882
+ if ( $wpsl_settings['hide_hours'] ) {
883
+ return;
884
+ }
885
+
886
+ $hide_closed = apply_filters( 'wpsl_hide_closed_hours', false );
887
+
888
+ $atts = wpsl_bool_check( shortcode_atts( apply_filters( 'wpsl_hour_shortcode_defaults', array(
889
+ 'id' => '',
890
+ 'hide_closed' => $hide_closed
891
+ ) ), $atts ) );
892
+
893
+ if ( get_post_type() == 'wpsl_stores' ) {
894
+ if ( empty( $atts['id'] ) ) {
895
+ if ( isset( $post->ID ) ) {
896
+ $atts['id'] = $post->ID;
897
+ } else {
898
+ return;
899
+ }
900
+ }
901
+ } else if ( empty( $atts['id'] ) ) {
902
+ return __( 'If you use the [wpsl_hours] shortcode outside a store page you need to set the ID attribute.', 'wpsl' );
903
+ }
904
+
905
+ $opening_hours = get_post_meta( $atts['id'], 'wpsl_hours' );
906
+
907
+ if ( $opening_hours ) {
908
+ $output = $this->get_opening_hours( $opening_hours[0], $atts['hide_closed'] );
909
+
910
+ return $output;
911
+ }
912
+ }
913
+
914
+ /**
915
+ * Handle the [wpsl_map] shortcode.
916
+ *
917
+ * @since 2.0.0
918
+ * @param array $atts Shortcode attributes
919
+ * @return string $output The html for the map
920
+ */
921
+ public function show_store_map( $atts ) {
922
+
923
+ global $wpsl_settings, $post;
924
+
925
+ $atts = shortcode_atts( apply_filters( 'wpsl_map_shortcode_defaults', array(
926
+ 'id' => '',
927
+ 'category' => '',
928
+ 'width' => '',
929
+ 'height' => $wpsl_settings['height'],
930
+ 'zoom' => $wpsl_settings['zoom_level'],
931
+ 'map_type' => $wpsl_settings['map_type'],
932
+ 'map_type_control' => $wpsl_settings['type_control'],
933
+ 'map_style' => '',
934
+ 'street_view' => $wpsl_settings['streetview'],
935
+ 'scrollwheel' => $wpsl_settings['scrollwheel'],
936
+ 'control_position' => $wpsl_settings['control_position']
937
+ ) ), $atts );
938
+
939
+ array_push( $this->load_scripts, 'wpsl_base' );
940
+
941
+ if ( get_post_type() == 'wpsl_stores' ) {
942
+ if ( empty( $atts['id'] ) ) {
943
+ if ( isset( $post->ID ) ) {
944
+ $atts['id'] = $post->ID;
945
+ } else {
946
+ return;
947
+ }
948
+ }
949
+ } else if ( empty( $atts['id'] ) && empty( $atts['category'] ) ) {
950
+ return __( 'If you use the [wpsl_map] shortcode outside a store page, then you need to set the ID or category attribute.', 'wpsl' );
951
+ }
952
+
953
+ if ( $atts['category'] ) {
954
+ $store_ids = get_posts( array(
955
+ 'numberposts' => -1,
956
+ 'post_type' => 'wpsl_stores',
957
+ 'post_status' => 'publish',
958
+ 'tax_query' => array(
959
+ array(
960
+ 'taxonomy' => 'wpsl_store_category',
961
+ 'field' => 'slug',
962
+ 'terms' => explode( ',', sanitize_text_field( $atts['category'] ) )
963
+ ),
964
+ ),
965
+ 'fields' => 'ids'
966
+ ) );
967
+ } else {
968
+ $store_ids = array_map( 'absint', explode( ',', $atts['id'] ) );
969
+ $id_count = count( $store_ids );
970
+ }
971
+
972
+ /*
973
+ * The location url is included if:
974
+ *
975
+ * - Multiple ids are set.
976
+ * - The category attr is set.
977
+ * - The shortcode is used on a post type other then 'wpsl_stores'. No point in showing a location
978
+ * url to the user that links back to the page they are already on.
979
+ */
980
+ if ( $atts['category'] || isset( $id_count ) && $id_count > 1 || get_post_type() != 'wpsl_stores' && !empty( $atts['id'] ) ) {
981
+ $incl_url = true;
982
+ } else {
983
+ $incl_url = false;
984
+ }
985
+
986
+ $store_meta = array();
987
+ $i = 0;
988
+
989
+ foreach ( $store_ids as $store_id ) {
990
+ $lat = get_post_meta( $store_id, 'wpsl_lat', true );
991
+ $lng = get_post_meta( $store_id, 'wpsl_lng', true );
992
+
993
+ // Make sure the latlng is numeric before collecting the other meta data.
994
+ if ( is_numeric( $lat ) && is_numeric( $lng ) ) {
995
+ $store_meta[$i] = apply_filters( 'wpsl_cpt_info_window_meta_fields', array(
996
+ 'store' => get_the_title( $store_id ),
997
+ 'address' => get_post_meta( $store_id, 'wpsl_address', true ),
998
+ 'address2' => get_post_meta( $store_id, 'wpsl_address2', true ),
999
+ 'city' => get_post_meta( $store_id, 'wpsl_city', true ),
1000
+ 'state' => get_post_meta( $store_id, 'wpsl_state', true ),
1001
+ 'zip' => get_post_meta( $store_id, 'wpsl_zip', true ),
1002
+ 'country' => get_post_meta( $store_id, 'wpsl_country', true )
1003
+ ), $store_id );
1004
+
1005
+ // Grab the permalink / url if necessary.
1006
+ if ( $incl_url ) {
1007
+ if ( $wpsl_settings['permalinks'] ) {
1008
+ $store_meta[$i]['permalink'] = get_permalink( $store_id );
1009
+ } else {
1010
+ $store_meta[$i]['url'] = get_post_meta( $store_id, 'wpsl_url', true );
1011
+ }
1012
+ }
1013
+
1014
+ $store_meta[$i]['lat'] = $lat;
1015
+ $store_meta[$i]['lng'] = $lng;
1016
+ $store_meta[$i]['id'] = $store_id;
1017
+
1018
+ $i++;
1019
+ }
1020
+ }
1021
+
1022
+ $output = '<div id="wpsl-base-gmap_' . self::$map_count . '" class="wpsl-gmap-canvas"></div>' . "\r\n";
1023
+
1024
+ // Make sure the shortcode attributes are valid.
1025
+ $map_styles = $this->check_map_shortcode_atts( $atts );
1026
+
1027
+ if ( $map_styles ) {
1028
+ if ( isset( $map_styles['css'] ) && !empty( $map_styles['css'] ) ) {
1029
+ $output .= '<style>' . $map_styles['css'] . '</style>' . "\r\n";
1030
+ unset( $map_styles['css'] );
1031
+ }
1032
+
1033
+ if ( $map_styles ) {
1034
+ $store_data['shortCode'] = $map_styles;
1035
+ }
1036
+ }
1037
+
1038
+ $store_data['locations'] = $store_meta;
1039
+
1040
+ $this->store_map_data[self::$map_count] = $store_data;
1041
+
1042
+ self::$map_count++;
1043
+
1044
+ return $output;
1045
+ }
1046
+
1047
+ /**
1048
+ * Make sure the map style shortcode attributes are valid.
1049
+ *
1050
+ * The values are send to wp_localize_script in add_frontend_scripts.
1051
+ *
1052
+ * @since 2.0.0
1053
+ * @param array $atts The map style shortcode attributes
1054
+ * @return array $map_atts Validated map style shortcode attributes
1055
+ */
1056
+ public function check_map_shortcode_atts( $atts ) {
1057
+
1058
+ $map_atts = array();
1059
+
1060
+ if ( isset( $atts['width'] ) && is_numeric( $atts['width'] ) ) {
1061
+ $width = 'width:' . $atts['width'] . 'px;';
1062
+ } else {
1063
+ $width = '';
1064
+ }
1065
+
1066
+ if ( isset( $atts['height'] ) && is_numeric( $atts['height'] ) ) {
1067
+ $height = 'height:' . $atts['height'] . 'px;';
1068
+ } else {
1069
+ $height = '';
1070
+ }
1071
+
1072
+ if ( $width || $height ) {
1073
+ $map_atts['css'] = '#wpsl-base-gmap_' . self::$map_count . ' {' . $width . $height . '}';
1074
+ }
1075
+
1076
+ if ( isset( $atts['zoom'] ) && !empty( $atts['zoom'] ) ) {
1077
+ $map_atts['zoomLevel'] = wpsl_valid_zoom_level( $atts['zoom'] );
1078
+ }
1079
+
1080
+ if ( isset( $atts['map_type'] ) && !empty( $atts['map_type'] ) ) {
1081
+ $map_atts['mapType'] = wpsl_valid_map_type( $atts['map_type'] );
1082
+ }
1083
+
1084
+ if ( isset( $atts['map_type_control'] ) ) {
1085
+ $map_atts['mapTypeControl'] = $this->shortcode_atts_boolean( $atts['map_type_control'] );
1086
+ }
1087
+
1088
+ if ( isset( $atts['map_style'] ) && $atts['map_style'] == 'default' ) {
1089
+ $map_atts['mapStyle'] = '';
1090
+ }
1091
+
1092
+ if ( isset( $atts['street_view'] ) ) {
1093
+ $map_atts['streetView'] = $this->shortcode_atts_boolean( $atts['street_view'] );
1094
+ }
1095
+
1096
+ if ( isset( $atts['scrollwheel'] ) ) {
1097
+ $map_atts['scrollWheel'] = $this->shortcode_atts_boolean( $atts['scrollwheel'] );
1098
+ }
1099
+
1100
+ if ( isset( $atts['control_position'] ) && !empty( $atts['control_position'] ) && ( $atts['control_position'] == 'left' || $atts['control_position'] == 'right' ) ) {
1101
+ $map_atts['controlPosition'] = $atts['control_position'];
1102
+ }
1103
+
1104
+ return $map_atts;
1105
+ }
1106
+
1107
+ /**
1108
+ * Set the shortcode attribute to either 1 or 0.
1109
+ *
1110
+ * @since 2.0.0
1111
+ * @param string $att The shortcode attribute val
1112
+ * @return int $att_val Either 1 or 0
1113
+ */
1114
+ public function shortcode_atts_boolean( $att ) {
1115
+
1116
+ if ( $att === 'true' || absint( $att ) ) {
1117
+ $att_val = 1;
1118
+ } else {
1119
+ $att_val = 0;
1120
+ }
1121
+
1122
+ return $att_val;
1123
+ }
1124
+
1125
+ /**
1126
+ * Make sure the filter contains a valid value, otherwise use the default value.
1127
+ *
1128
+ * @since 2.0.0
1129
+ * @param array $args The values used in the SQL query to find nearby locations
1130
+ * @param string $filter The name of the filter
1131
+ * @return string $filter_value The filter value
1132
+ */
1133
+ public function check_store_filter( $args, $filter ) {
1134
+
1135
+ if ( isset( $args[$filter] ) && absint( $args[$filter] ) && $this->check_allowed_filter_value( $args, $filter ) ) {
1136
+ $filter_value = $args[$filter];
1137
+ } else {
1138
+ $filter_value = $this->get_default_filter_value( $filter );
1139
+ }
1140
+
1141
+ return $filter_value;
1142
+ }
1143
+
1144
+ /**
1145
+ * Make sure the used filter value isn't bigger
1146
+ * then the value that's set on the settings page.
1147
+ *
1148
+ * @since 2.2.9
1149
+ * @param array $args The values used in the SQL query to find nearby locations
1150
+ * @param string $filter The name of the filter
1151
+ * @return bool $allowed True if the value is equal or smaller then the value from the settings page
1152
+ */
1153
+ public function check_allowed_filter_value( $args, $filter ) {
1154
+
1155
+ global $wpsl_settings;
1156
+
1157
+ $allowed = false;
1158
+
1159
+ $max_filter_val = max( explode(',', str_replace( array( '[',']' ), '', $wpsl_settings[$filter] ) ) );
1160
+
1161
+ if ( (int) $args[$filter] <= (int) $max_filter_val ) {
1162
+ $allowed = true;
1163
+ }
1164
+
1165
+ return $allowed;
1166
+ }
1167
+
1168
+ /**
1169
+ * Get the default selected value for a dropdown.
1170
+ *
1171
+ * @since 1.0.0
1172
+ * @param string $type The request list type
1173
+ * @return string $response The default list value
1174
+ */
1175
+ public function get_default_filter_value( $type ) {
1176
+
1177
+ $settings = get_option( 'wpsl_settings' );
1178
+ $list_values = explode( ',', $settings[$type] );
1179
+
1180
+ foreach ( $list_values as $k => $list_value ) {
1181
+
1182
+ // The default radius has a [] wrapped around it, so we check for that and filter out the [].
1183
+ if ( strpos( $list_value, '[' ) !== false ) {
1184
+ $response = filter_var( $list_value, FILTER_SANITIZE_NUMBER_INT );
1185
+ break;
1186
+ }
1187
+ }
1188
+
1189
+ return $response;
1190
+ }
1191
+
1192
+ /**
1193
+ * Check if we have a opening day that has an value, if not they are all set to closed.
1194
+ *
1195
+ * @since 2.0.0
1196
+ * @param array $opening_hours The opening hours
1197
+ * @return boolean True if a day is found that isn't empty
1198
+ */
1199
+ public function not_always_closed( $opening_hours ) {
1200
+
1201
+ foreach ( $opening_hours as $hours => $hour ) {
1202
+ if ( !empty( $hour ) ) {
1203
+ return true;
1204
+ }
1205
+ }
1206
+ }
1207
+
1208
+ /**
1209
+ * Create the css rules based on the height / max-width that is set on the settings page.
1210
+ *
1211
+ * @since 1.0.0
1212
+ * @return string $css The custom css rules
1213
+ */
1214
+ public function get_custom_css() {
1215
+
1216
+ global $wpsl_settings;
1217
+
1218
+ $thumb_size = $this->get_store_thumb_size();
1219
+
1220
+ $css = '<style>' . "\r\n";
1221
+
1222
+ if ( isset( $thumb_size[0] ) && is_numeric( $thumb_size[0] ) && isset( $thumb_size[1] ) && is_numeric( $thumb_size[1] ) ) {
1223
+ $css .= "\t" . "#wpsl-stores .wpsl-store-thumb {height:" . esc_attr( $thumb_size[0] ) . "px !important; width:" . esc_attr( $thumb_size[1] ) . "px !important;}" . "\r\n";
1224
+ }
1225
+
1226
+ if ( $wpsl_settings['template_id'] == 'below_map' && $wpsl_settings['listing_below_no_scroll'] ) {
1227
+ $css .= "\t" . "#wpsl-gmap {height:" . esc_attr( $wpsl_settings['height'] ) . "px !important;}" . "\r\n";
1228
+ $css .= "\t" . "#wpsl-stores, #wpsl-direction-details {height:auto !important;}";
1229
+ } else {
1230
+ $css .= "\t" . "#wpsl-stores, #wpsl-direction-details, #wpsl-gmap {height:" . esc_attr( $wpsl_settings['height'] ) . "px !important;}" . "\r\n";
1231
+ }
1232
+
1233
+ /*
1234
+ * If the category dropdowns are enabled then we make it
1235
+ * the same width as the search input field.
1236
+ */
1237
+ if ( $wpsl_settings['category_filter'] && $wpsl_settings['category_filter_type'] == 'dropdown' || isset( $this->sl_shortcode_atts['category_filter_type'] ) && $this->sl_shortcode_atts['category_filter_type'] == 'dropdown' ) {
1238
+ $cat_elem = ',#wpsl-category .wpsl-dropdown';
1239
+ } else {
1240
+ $cat_elem = '';
1241
+ }
1242
+
1243
+ $css .= "\t" . "#wpsl-gmap .wpsl-info-window {max-width:" . esc_attr( $wpsl_settings['infowindow_width'] ) . "px !important;}" . "\r\n";
1244
+ $css .= "\t" . ".wpsl-input label, #wpsl-radius label, #wpsl-category label {width:" . esc_attr( $wpsl_settings['label_width'] ) . "px;}" . "\r\n";
1245
+ $css .= "\t" . "#wpsl-search-input " . $cat_elem . " {width:" . esc_attr( $wpsl_settings['search_width'] ) . "px;}" . "\r\n";
1246
+ $css .= '</style>' . "\r\n";
1247
+
1248
+ return $css;
1249
+ }
1250
+
1251
+ /**
1252
+ * Collect the CSS classes that are placed on the outer store locator div.
1253
+ *
1254
+ * @since 2.0.0
1255
+ * @return string $classes The custom CSS rules
1256
+ */
1257
+ public function get_css_classes() {
1258
+
1259
+ global $wpsl_settings;
1260
+
1261
+ $classes = array();
1262
+
1263
+ if ( $wpsl_settings['category_filter'] && $wpsl_settings['results_dropdown'] && !$wpsl_settings['radius_dropdown'] ) {
1264
+ $classes[] = 'wpsl-cat-results-filter';
1265
+ } else if ( $wpsl_settings['category_filter'] && ( $wpsl_settings['results_dropdown'] || $wpsl_settings['radius_dropdown'] ) ) {
1266
+ $classes[] = 'wpsl-filter';
1267
+ }
1268
+ // checkboxes class toevoegen?
1269
+ if ( !$wpsl_settings['category_filter'] && !$wpsl_settings['results_dropdown'] && !$wpsl_settings['radius_dropdown'] ) {
1270
+ $classes[] = 'wpsl-no-filters';
1271
+ }
1272
+
1273
+ if ( $wpsl_settings['category_filter'] && $wpsl_settings['category_filter_type'] == 'checkboxes' ) {
1274
+ $classes[] = 'wpsl-checkboxes-enabled';
1275
+ }
1276
+
1277
+ if ( $wpsl_settings['results_dropdown'] && !$wpsl_settings['category_filter'] && !$wpsl_settings['radius_dropdown'] ) {
1278
+ $classes[] = 'wpsl-results-only';
1279
+ }
1280
+
1281
+ // Adjust the styling of the store locator for the default WP 5.0 theme.
1282
+ if ( get_option( 'template' ) === 'twentynineteen' ) {
1283
+ $classes[] = 'wpsl-twentynineteen';
1284
+ }
1285
+
1286
+ $classes = apply_filters( 'wpsl_template_css_classes', $classes );
1287
+
1288
+ if ( !empty( $classes ) ) {
1289
+ return join( ' ', $classes );
1290
+ }
1291
+ }
1292
+
1293
+ /**
1294
+ * Create a dropdown list holding the search radius or
1295
+ * max search results options.
1296
+ *
1297
+ * @since 1.0.0
1298
+ * @param string $list_type The name of the list we need to load data for
1299
+ * @return string $dropdown_list A list with the available options for the dropdown list
1300
+ */
1301
+ public function get_dropdown_list( $list_type ) {
1302
+
1303
+ global $wpsl_settings;
1304
+
1305
+ $dropdown_list = '';
1306
+ $settings = explode( ',', $wpsl_settings[$list_type] );
1307
+
1308
+ // Only show the distance unit if we are dealing with the search radius.
1309
+ if ( $list_type == 'search_radius' ) {
1310
+ $distance_unit = ' '. esc_attr( wpsl_get_distance_unit() );
1311
+ } else {
1312
+ $distance_unit = '';
1313
+ }
1314
+
1315
+ foreach ( $settings as $index => $setting_value ) {
1316
+
1317
+ // The default radius has a [] wrapped around it, so we check for that and filter out the [].
1318
+ if ( strpos( $setting_value, '[' ) !== false ) {
1319
+ $setting_value = filter_var( $setting_value, FILTER_SANITIZE_NUMBER_INT );
1320
+ $selected = 'selected="selected" ';
1321
+ } else {
1322
+ $selected = '';
1323
+ }
1324
+
1325
+ $dropdown_list .= '<option ' . $selected . 'value="'. absint( $setting_value ) .'">'. absint( $setting_value ) . $distance_unit .'</option>';
1326
+ }
1327
+
1328
+ return $dropdown_list;
1329
+ }
1330
+
1331
+ /**
1332
+ * Check if we need to use a dropdown or checkboxes
1333
+ * to filter the search results by categories.
1334
+ *
1335
+ * @since 2.2.10
1336
+ * @return bool $use_filter
1337
+ */
1338
+ public function use_category_filter() {
1339
+
1340
+ global $wpsl_settings;
1341
+
1342
+ $use_filter = false;
1343
+
1344
+ // Is a filter type set through the shortcode, or is the filter option enable on the settings page?
1345
+ if ( isset( $this->sl_shortcode_atts['category_filter_type'] ) || $wpsl_settings['category_filter'] ) {
1346
+ $use_filter = true;
1347
+ }
1348
+
1349
+ return $use_filter;
1350
+ }
1351
+
1352
+ /**
1353
+ * Create the category filter.
1354
+ *
1355
+ * @todo create another func that accepts a meta key param to generate
1356
+ * a dropdown with unique values. So for example create_filter( 'restaurant' ) will output a
1357
+ * filter with all restaurant types. This can be used in a custom theme template.
1358
+ *
1359
+ * @since 2.0.0
1360
+ * @return string|void $category The HTML for the category dropdown, or nothing if no terms exist.
1361
+ */
1362
+ public function create_category_filter() {
1363
+
1364
+ global $wpsl, $wpsl_settings;
1365
+
1366
+ /*
1367
+ * If the category attr is set on the wpsl shortcode, then
1368
+ * there is no need to ouput an extra category dropdown.
1369
+ */
1370
+ if ( isset( $this->sl_shortcode_atts['js']['categoryIds'] ) ) {
1371
+ return;
1372
+ }
1373
+
1374
+ $terms = get_terms( 'wpsl_store_category' );
1375
+
1376
+ if ( count( $terms ) > 0 ) {
1377
+
1378
+ // Either use the shortcode atts filter type or the one from the settings page.
1379
+ if ( isset( $this->sl_shortcode_atts['category_filter_type'] ) ) {
1380
+ $filter_type = $this->sl_shortcode_atts['category_filter_type'];
1381
+ } else {
1382
+ $filter_type = $wpsl_settings['category_filter_type'];
1383
+ }
1384
+
1385
+ // Check if we need to show the filter as checkboxes or a dropdown list
1386
+ if ( $filter_type == 'checkboxes' ) {
1387
+ if ( isset( $this->sl_shortcode_atts['checkbox_columns'] ) ) {
1388
+ $checkbox_columns = absint( $this->sl_shortcode_atts['checkbox_columns'] );
1389
+ }
1390
+
1391
+ if ( isset( $checkbox_columns ) && $checkbox_columns ) {
1392
+ $column_count = $checkbox_columns;
1393
+ } else {
1394
+ $column_count = 3;
1395
+ }
1396
+
1397
+ $category = '<ul id="wpsl-checkbox-filter" class="wpsl-checkbox-' . $column_count . '-columns">';
1398
+
1399
+ foreach ( $terms as $term ) {
1400
+ $category .= '<li>';
1401
+ $category .= '<label>';
1402
+ $category .= '<input type="checkbox" value="' . esc_attr( $term->term_id ) . '" ' . $this->set_selected_category( $filter_type, $term->term_id ) . ' />';
1403
+ $category .= esc_html( $term->name );
1404
+ $category .= '</label>';
1405
+ $category .= '</li>';
1406
+ }
1407
+
1408
+ $category .= '</ul>';
1409
+ } else {
1410
+ $category = '<div id="wpsl-category">' . "\r\n";
1411
+ $category .= '<label for="wpsl-category-list">' . esc_html( $wpsl->i18n->get_translation( 'category_label', __( 'Category', 'wpsl' ) ) ) . '</label>' . "\r\n";
1412
+
1413
+ $args = apply_filters( 'wpsl_dropdown_category_args', array(
1414
+ 'show_option_none' => $wpsl->i18n->get_translation( 'category_default_label', __( 'Any', 'wpsl' ) ),
1415
+ 'option_none_value' => '0',
1416
+ 'orderby' => 'NAME',
1417
+ 'order' => 'ASC',
1418
+ 'echo' => 0,
1419
+ 'selected' => $this->set_selected_category( $filter_type ),
1420
+ 'hierarchical' => 1,
1421
+ 'name' => 'wpsl-category',
1422
+ 'id' => 'wpsl-category-list',
1423
+ 'class' => 'wpsl-dropdown',
1424
+ 'taxonomy' => 'wpsl_store_category',
1425
+ 'hide_if_empty' => true
1426
+ )
1427
+ );
1428
+
1429
+ $category .= wp_dropdown_categories( $args );
1430
+
1431
+ $category .= '</div>' . "\r\n";
1432
+ }
1433
+
1434
+ return $category;
1435
+ }
1436
+ }
1437
+
1438
+ /**
1439
+ * Set the selected category item.
1440
+ *
1441
+ * @since 2.1.2
1442
+ * @param string $filter_type The type of filter being used ( dropdown or checkbox )
1443
+ * @param int|string $term_id The term id ( checkbox only )
1444
+ * @return string|void $category The ID of the selected option, or checked='checked' if it's for a checkbox
1445
+ */
1446
+ public function set_selected_category( $filter_type, $term_id = '' ) {
1447
+
1448
+ $selected_id = '';
1449
+
1450
+ // Check if the ID for the selected cat is either passed through the widget, or shortcode
1451
+ if ( isset( $_REQUEST['wpsl-widget-categories'] ) ) {
1452
+ $selected_id = absint( $_REQUEST['wpsl-widget-categories'] );
1453
+ } else if ( isset( $this->sl_shortcode_atts['category_selection'] ) ) {
1454
+
1455
+ /*
1456
+ * When the term_id is set, then it's a checkbox.
1457
+ *
1458
+ * Otherwise select the first value from the provided list since
1459
+ * multiple selections are not supported in dropdowns.
1460
+ */
1461
+ if ( $term_id ) {
1462
+
1463
+ // Check if the passed term id exists in the set shortcode value.
1464
+ $key = array_search( $term_id, $this->sl_shortcode_atts['category_selection'] );
1465
+
1466
+ if ( $key !== false ) {
1467
+ $selected_id = $this->sl_shortcode_atts['category_selection'][$key];
1468
+ }
1469
+ } else {
1470
+ $selected_id = $this->sl_shortcode_atts['category_selection'][0];
1471
+ }
1472
+ }
1473
+
1474
+ if ( $selected_id ) {
1475
+
1476
+ /*
1477
+ * Based on the filter type, either return the ID of the selected category,
1478
+ * or check if the checkbox needs to be set to checked="checked".
1479
+ */
1480
+ if ( $filter_type == 'dropdown' ) {
1481
+ return $selected_id;
1482
+ } else {
1483
+ return checked( $selected_id, $term_id, false );
1484
+ }
1485
+ }
1486
+ }
1487
+
1488
+ /**
1489
+ * Create a filename with @2x in it for the selected marker color.
1490
+ *
1491
+ * So when a user selected green.png in the admin panel. The JS on the front-end will end up
1492
+ * loading green@2x.png to provide support for retina compatible devices.
1493
+ *
1494
+ * @since 1.0.0
1495
+ * @param string $filename The name of the seleted marker
1496
+ * @return string $filename The filename with @2x added to the end
1497
+ */
1498
+ public function create_retina_filename( $filename ) {
1499
+
1500
+ $filename = explode( '.', $filename );
1501
+ $filename = $filename[0] . '@2x.' . $filename[1];
1502
+
1503
+ return $filename;
1504
+ }
1505
+
1506
+ /**
1507
+ * Get the default values for the max_results and the search_radius dropdown.
1508
+ *
1509
+ * @since 1.0.2
1510
+ * @return array $output The default dropdown values
1511
+ */
1512
+ public function get_dropdown_defaults() {
1513
+
1514
+ global $wpsl_settings;
1515
+
1516
+ $required_defaults = array(
1517
+ 'max_results',
1518
+ 'search_radius'
1519
+ );
1520
+
1521
+ // Strip out the default values that are wrapped in [].
1522
+ foreach ( $required_defaults as $required_default ) {
1523
+ preg_match_all( '/\[([0-9]+?)\]/', $wpsl_settings[$required_default], $match, PREG_PATTERN_ORDER );
1524
+ $output[$required_default] = ( isset( $match[1][0] ) ) ? $match[1][0] : '25';
1525
+ }
1526
+
1527
+ return $output;
1528
+ }
1529
+
1530
+ /**
1531
+ * Load the required css styles.
1532
+ *
1533
+ * @since 2.0.0
1534
+ * @return void
1535
+ */
1536
+ public function add_frontend_styles() {
1537
+
1538
+ global $wpsl_settings;
1539
+
1540
+ /**
1541
+ * Check if we need to deregister other Google Maps scripts loaded
1542
+ * by other plugins, or the current theme?
1543
+ *
1544
+ * This in some cases can break the store locator map.
1545
+ */
1546
+ if ( $wpsl_settings['deregister_gmaps'] ) {
1547
+ wpsl_deregister_other_gmaps();
1548
+ }
1549
+
1550
+ $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
1551
+
1552
+ wp_enqueue_style( 'wpsl-styles', WPSL_URL . 'css/styles'. $min .'.css', '', WPSL_VERSION_NUM );
1553
+ }
1554
+
1555
+ /**
1556
+ * Get the HTML for the map controls.
1557
+ *
1558
+ * The '&#xe800;' and '&#xe801;' code is for the icon font from fontello.com
1559
+ *
1560
+ * @since 2.0.0
1561
+ * @return string The HTML for the map controls
1562
+ */
1563
+ public function get_map_controls() {
1564
+
1565
+ global $wpsl_settings, $is_IE;
1566
+
1567
+ $classes = array();
1568
+
1569
+ if ( $wpsl_settings['reset_map'] ) {
1570
+ $reset_button = '<div class="wpsl-icon-reset"><span>&#xe801;</span></div>';
1571
+ } else {
1572
+ $reset_button = '';
1573
+ }
1574
+
1575
+ /*
1576
+ * IE messes up the top padding for the icon fonts from fontello >_<.
1577
+ *
1578
+ * Luckily it's the same in all IE version ( 8-11 ),
1579
+ * so adjusting the padding just for IE fixes it.
1580
+ */
1581
+ if ( $is_IE ) {
1582
+ $classes[] = 'wpsl-ie';
1583
+ }
1584
+
1585
+ // If the street view option is enabled, then we need to adjust the right margin for the map control div.
1586
+ if ( $wpsl_settings['streetview'] ) {
1587
+ $classes[] = 'wpsl-street-view-exists';
1588
+ }
1589
+
1590
+ if ( !empty( $classes ) ) {
1591
+ $class = 'class="' . join( ' ', $classes ) . '"';
1592
+ } else {
1593
+ $class = '';
1594
+ }
1595
+
1596
+ $map_controls = '<div id="wpsl-map-controls" ' . $class . '>' . $reset_button . '<div class="wpsl-icon-direction"><span>&#xe800;</span></div></div>';
1597
+
1598
+ return apply_filters( 'wpsl_map_controls', $map_controls );
1599
+ }
1600
+
1601
+ /**
1602
+ * The different geolocation errors.
1603
+ *
1604
+ * They are shown when the Geolocation API returns an error.
1605
+ *
1606
+ * @since 2.0.0
1607
+ * @return array $geolocation_errors
1608
+ */
1609
+ public function geolocation_errors() {
1610
+
1611
+ $geolocation_errors = array(
1612
+ 'denied' => __( 'The application does not have permission to use the Geolocation API.', 'wpsl' ),
1613
+ 'unavailable' => __( 'Location information is unavailable.', 'wpsl' ),
1614
+ 'timeout' => __( 'The geolocation request timed out.', 'wpsl' ),
1615
+ 'generalError' => __( 'An unknown error occurred.', 'wpsl' )
1616
+ );
1617
+
1618
+ return $geolocation_errors;
1619
+ }
1620
+
1621
+ /**
1622
+ * Get the used marker properties.
1623
+ *
1624
+ * @since 2.1.0
1625
+ * @link https://developers.google.com/maps/documentation/javascript/3.exp/reference#Icon
1626
+ * @return array $marker_props The marker properties.
1627
+ */
1628
+ public function get_marker_props() {
1629
+
1630
+ $marker_props = array(
1631
+ 'scaledSize' => '24,35', // 50% of the normal image to make it work on retina screens.
1632
+ 'origin' => '0,0',
1633
+ 'anchor' => '12,35'
1634
+ );
1635
+
1636
+ /*
1637
+ * If this is not defined, the url path will default to
1638
+ * the url path of the WPSL plugin folder + /img/markers/
1639
+ * in the wpsl-gmap.js.
1640
+ */
1641
+ if ( defined( 'WPSL_MARKER_URI' ) ) {
1642
+ $marker_props['url'] = WPSL_MARKER_URI;
1643
+ }
1644
+
1645
+ return apply_filters( 'wpsl_marker_props', $marker_props );
1646
+ }
1647
+
1648
+ /**
1649
+ * Get the used travel direction mode.
1650
+ *
1651
+ * @since 2.2.8
1652
+ * @return string $travel_mode The used travel mode for the travel direcions
1653
+ */
1654
+ public function get_directions_travel_mode() {
1655
+
1656
+ $default = 'driving';
1657
+
1658
+ $travel_mode = apply_filters( 'wpsl_direction_travel_mode', $default );
1659
+ $allowed_modes = array( 'driving', 'bicycling', 'transit', 'walking' );
1660
+
1661
+ if ( !in_array( $travel_mode, $allowed_modes ) ) {
1662
+ $travel_mode = $default;
1663
+ }
1664
+
1665
+ return strtoupper( $travel_mode );
1666
+ }
1667
+
1668
+ /**
1669
+ * Get the map tab anchors.
1670
+ *
1671
+ * If the wpsl/wpsl_map shortcode is used in one or more tabs,
1672
+ * then a JS fix ( the fixGreyTabMap function ) needs to run
1673
+ * to make sure the map doesn't turn grey.
1674
+ *
1675
+ * For the fix to work need to know the used anchor(s).
1676
+ *
1677
+ * @since 2.2.10
1678
+ * @return string|array $map_tab_anchor One or more anchors used to show the map(s)
1679
+ */
1680
+ public function get_map_tab_anchor() {
1681
+
1682
+ $map_tab_anchor = apply_filters( 'wpsl_map_tab_anchor', 'wpsl-map-tab' );
1683
+
1684
+ return $map_tab_anchor;
1685
+ }
1686
+
1687
+ /**
1688
+ * Load the required JS scripts.
1689
+ *
1690
+ * @since 1.0.0
1691
+ * @return void
1692
+ */
1693
+ public function add_frontend_scripts() {
1694
+
1695
+ global $wpsl_settings, $wpsl;
1696
+
1697
+ // Only load the required js files on the store locator page or individual store pages.
1698
+ if ( empty( $this->load_scripts ) ) {
1699
+ return;
1700
+ }
1701
+
1702
+ $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
1703
+
1704
+ $dropdown_defaults = $this->get_dropdown_defaults();
1705
+
1706
+ /**
1707
+ * Check if we need to deregister other Google Maps scripts loaded
1708
+ * by other plugins, or the current theme?
1709
+ *
1710
+ * This in some cases can break the store locator map.
1711
+ */
1712
+ if ( $wpsl_settings['deregister_gmaps'] ) {
1713
+ wpsl_deregister_other_gmaps();
1714
+ }
1715
+
1716
+ wp_enqueue_script( 'wpsl-gmap', ( 'https://maps.google.com/maps/api/js' . wpsl_get_gmap_api_params( 'browser_key' ) . '' ), '', null, true );
1717
+
1718
+ $base_settings = array(
1719
+ 'storeMarker' => $this->create_retina_filename( $wpsl_settings['store_marker'] ),
1720
+ 'mapType' => $wpsl_settings['map_type'],
1721
+ 'mapTypeControl' => $wpsl_settings['type_control'],
1722
+ 'zoomLevel' => $wpsl_settings['zoom_level'],
1723
+ 'startLatlng' => $wpsl_settings['start_latlng'],
1724
+ 'autoZoomLevel' => $wpsl_settings['auto_zoom_level'],
1725
+ 'scrollWheel' => $wpsl_settings['scrollwheel'],
1726
+ 'controlPosition' => $wpsl_settings['control_position'],
1727
+ 'url' => WPSL_URL,
1728
+ 'markerIconProps' => $this->get_marker_props(),
1729
+ 'storeUrl' => $wpsl_settings['store_url'],
1730
+ 'maxDropdownHeight' => apply_filters( 'wpsl_max_dropdown_height', 300 ),
1731
+ 'enableStyledDropdowns' => apply_filters( 'wpsl_enable_styled_dropdowns', true ),
1732
+ 'mapTabAnchor' => $this->get_map_tab_anchor(),
1733
+ 'mapTabAnchorReturn' => apply_filters( 'wpsl_map_tab_anchor_return', false ),
1734
+ 'gestureHandling' => apply_filters( 'wpsl_gesture_handling', 'auto' ),
1735
+ 'directionsTravelMode' => $this->get_directions_travel_mode(),
1736
+ 'runFitBounds' => $wpsl_settings['run_fitbounds']
1737
+ );
1738
+
1739
+ $locator_map_settings = array(
1740
+ 'startMarker' => $this->create_retina_filename( $wpsl_settings['start_marker'] ),
1741
+ 'markerClusters' => $wpsl_settings['marker_clusters'],
1742
+ 'streetView' => $wpsl_settings['streetview'],
1743
+ 'autoComplete' => $wpsl_settings['autocomplete'],
1744
+ 'autoLocate' => $wpsl_settings['auto_locate'],
1745
+ 'autoLoad' => $wpsl_settings['autoload'],
1746
+ 'markerEffect' => $wpsl_settings['marker_effect'],
1747
+ 'markerStreetView' => $wpsl_settings['marker_streetview'],
1748
+ 'markerZoomTo' => $wpsl_settings['marker_zoom_to'],
1749
+ 'newWindow' => $wpsl_settings['new_window'],
1750
+ 'resetMap' => $wpsl_settings['reset_map'],
1751
+ 'directionRedirect' => $wpsl_settings['direction_redirect'],
1752
+ 'phoneUrl' => $wpsl_settings['phone_url'],
1753
+ 'clickableDetails' => $wpsl_settings['clickable_contact_details'],
1754
+ 'moreInfoLocation' => $wpsl_settings['more_info_location'],
1755
+ 'mouseFocus' => $wpsl_settings['mouse_focus'],
1756
+ 'templateId' => $wpsl_settings['template_id'],
1757
+ 'maxResults' => $dropdown_defaults['max_results'],
1758
+ 'searchRadius' => $dropdown_defaults['search_radius'],
1759
+ 'distanceUnit' => wpsl_get_distance_unit(),
1760
+ 'geoLocationTimeout' => apply_filters( 'wpsl_geolocation_timeout', 7500 ),
1761
+ 'ajaxurl' => wpsl_get_ajax_url(),
1762
+ 'mapControls' => $this->get_map_controls()
1763
+ );
1764
+
1765
+ /*
1766
+ * If no results are found then by default it will just show the
1767
+ * "No results found" text. This filter makes it possible to show
1768
+ * a custom HTML block instead of the "No results found" text.
1769
+ */
1770
+ $no_results_msg = apply_filters( 'wpsl_no_results', '' );
1771
+
1772
+ if ( $no_results_msg ) {
1773
+ $locator_map_settings['noResults'] = $no_results_msg;
1774
+ }
1775
+
1776
+ /*
1777
+ * If enabled, include the component filter settings.
1778
+ * @todo see https://developers.google.com/maps/documentation/javascript/releases#327
1779
+ * See https://developers.google.com/maps/documentation/javascript/geocoding#ComponentFiltering
1780
+ */
1781
+ if ( $wpsl_settings['api_region'] && $wpsl_settings['api_geocode_component'] ) {
1782
+ $locator_map_settings['geocodeComponents'] = apply_filters( 'wpsl_geocode_components', array(
1783
+ 'country' => strtoupper( $wpsl_settings['api_region'] )
1784
+ ) );
1785
+ }
1786
+
1787
+ // If the marker clusters are enabled, include the js file and marker settings.
1788
+ if ( $wpsl_settings['marker_clusters'] ) {
1789
+ wp_enqueue_script( 'wpsl-cluster', WPSL_URL . 'js/markerclusterer'. $min .'.js', array( 'wpsl-js' ), WPSL_VERSION_NUM, true ); //not minified version is in the /js folder
1790
+
1791
+ $base_settings['clusterZoom'] = $wpsl_settings['cluster_zoom'];
1792
+ $base_settings['clusterSize'] = $wpsl_settings['cluster_size'];
1793
+ $base_settings['clusterImagePath'] = 'https://cdn.rawgit.com/googlemaps/js-marker-clusterer/gh-pages/images/m';
1794
+ }
1795
+
1796
+ // Check if we need to include the infobox script and settings.
1797
+ if ( $wpsl_settings['infowindow_style'] == 'infobox' ) {
1798
+ wp_enqueue_script( 'wpsl-infobox', WPSL_URL . 'js/infobox'. $min .'.js', array( 'wpsl-gmap' ), WPSL_VERSION_NUM, true ); // Not minified version is in the /js folder
1799
+
1800
+ $base_settings['infoWindowStyle'] = $wpsl_settings['infowindow_style'];
1801
+ $base_settings = $this->get_infobox_settings( $base_settings );
1802
+ }
1803
+
1804
+ // Include the map style.
1805
+ if ( !empty( $wpsl_settings['map_style'] ) ) {
1806
+ $base_settings['mapStyle'] = strip_tags( stripslashes( json_decode( $wpsl_settings['map_style'] ) ) );
1807
+ }
1808
+
1809
+ wp_enqueue_script( 'wpsl-js', apply_filters( 'wpsl_gmap_js', WPSL_URL . 'js/wpsl-gmap'. $min .'.js' ), array( 'jquery' ), WPSL_VERSION_NUM, true );
1810
+ wp_enqueue_script( 'underscore' );
1811
+
1812
+ // Check if we need to include all the settings and labels or just a part of them.
1813
+ if ( in_array( 'wpsl_store_locator', $this->load_scripts ) ) {
1814
+ $settings = wp_parse_args( $base_settings, $locator_map_settings );
1815
+ $template = 'wpsl_store_locator';
1816
+ $labels = array(
1817
+ 'preloader' => $wpsl->i18n->get_translation( 'preloader_label', __( 'Searching...', 'wpsl' ) ),
1818
+ 'noResults' => $wpsl->i18n->get_translation( 'no_results_label', __( 'No results found', 'wpsl' ) ),
1819
+ 'moreInfo' => $wpsl->i18n->get_translation( 'more_label', __( 'More info', 'wpsl' ) ),
1820
+ 'generalError' => $wpsl->i18n->get_translation( 'error_label', __( 'Something went wrong, please try again!', 'wpsl' ) ),
1821
+ 'queryLimit' => $wpsl->i18n->get_translation( 'limit_label', __( 'API usage limit reached', 'wpsl' ) ),
1822
+ 'directions' => $wpsl->i18n->get_translation( 'directions_label', __( 'Directions', 'wpsl' ) ),
1823
+ 'noDirectionsFound' => $wpsl->i18n->get_translation( 'no_directions_label', __( 'No route could be found between the origin and destination', 'wpsl' ) ),
1824
+ 'startPoint' => $wpsl->i18n->get_translation( 'start_label', __( 'Start location', 'wpsl' ) ),
1825
+ 'back' => $wpsl->i18n->get_translation( 'back_label', __( 'Back', 'wpsl' ) ),
1826
+ 'streetView' => $wpsl->i18n->get_translation( 'street_view_label', __( 'Street view', 'wpsl' ) ),
1827
+ 'zoomHere' => $wpsl->i18n->get_translation( 'zoom_here_label', __( 'Zoom here', 'wpsl' ) )
1828
+ );
1829
+
1830
+ wp_localize_script( 'wpsl-js', 'wpslLabels', $labels );
1831
+ wp_localize_script( 'wpsl-js', 'wpslGeolocationErrors', $this->geolocation_errors() );
1832
+ } else {
1833
+ $template = '';
1834
+ $settings = $base_settings;
1835
+ }
1836
+
1837
+ // Check if we need to overwrite JS settings that are set through the [wpsl] shortcode.
1838
+ if ( $this->sl_shortcode_atts && isset( $this->sl_shortcode_atts['js'] ) ) {
1839
+ foreach ( $this->sl_shortcode_atts['js'] as $shortcode_key => $shortcode_val ) {
1840
+ $settings[$shortcode_key] = $shortcode_val;
1841
+ }
1842
+ }
1843
+
1844
+ wp_localize_script( 'wpsl-js', 'wpslSettings', apply_filters( 'wpsl_js_settings', $settings ) );
1845
+
1846
+ wpsl_create_underscore_templates( $template );
1847
+
1848
+ if ( !empty( $this->store_map_data ) ) {
1849
+ $i = 0;
1850
+
1851
+ foreach ( $this->store_map_data as $map ) {
1852
+ wp_localize_script( 'wpsl-js', 'wpslMap_' . $i, $map );
1853
+
1854
+ $i++;
1855
+ }
1856
+ }
1857
+ }
1858
+
1859
+ /**
1860
+ * Get the infobox settings.
1861
+ *
1862
+ * @since 2.0.0
1863
+ * @see http://google-maps-utility-library-v3.googlecode.com/svn/trunk/infobox/docs/reference.html
1864
+ * @param array $settings The plugin settings used on the front-end in js
1865
+ * @return array $settings The plugin settings including the infobox settings
1866
+ */
1867
+ public function get_infobox_settings( $settings ) {
1868
+
1869
+ $infobox_settings = apply_filters( 'wpsl_infobox_settings', array(
1870
+ 'infoBoxClass' => 'wpsl-infobox',
1871
+ 'infoBoxCloseMargin' => '2px', // The margin can be written in css style, so 2px 2px 4px 2px for top, right, bottom, left
1872
+ 'infoBoxCloseUrl' => '//www.google.com/intl/en_us/mapfiles/close.gif',
1873
+ 'infoBoxClearance' => '40,40',
1874
+ 'infoBoxDisableAutoPan' => 0,
1875
+ 'infoBoxEnableEventPropagation' => 0,
1876
+ 'infoBoxPixelOffset' => '-52,-45',
1877
+ 'infoBoxZindex' => 1500
1878
+ ) );
1879
+
1880
+ foreach ( $infobox_settings as $infobox_key => $infobox_setting ) {
1881
+ $settings[$infobox_key] = $infobox_setting;
1882
+ }
1883
+
1884
+ return $settings;
1885
+ }
1886
+ }
1887
  }
frontend/templates/default.php CHANGED
@@ -1,67 +1,67 @@
1
- <?php
2
- global $wpsl_settings, $wpsl;
3
-
4
- $output = $this->get_custom_css();
5
- $autoload_class = ( !$wpsl_settings['autoload'] ) ? 'class="wpsl-not-loaded"' : '';
6
-
7
- $output .= '<div id="wpsl-wrap">' . "\r\n";
8
- $output .= "\t" . '<div class="wpsl-search wpsl-clearfix ' . $this->get_css_classes() . '">' . "\r\n";
9
- $output .= "\t\t" . '<div id="wpsl-search-wrap">' . "\r\n";
10
- $output .= "\t\t\t" . '<form autocomplete="off">' . "\r\n";
11
- $output .= "\t\t\t" . '<div class="wpsl-input">' . "\r\n";
12
- $output .= "\t\t\t\t" . '<div><label for="wpsl-search-input">' . esc_html( $wpsl->i18n->get_translation( 'search_label', __( 'Your location', 'wpsl' ) ) ) . '</label></div>' . "\r\n";
13
- $output .= "\t\t\t\t" . '<input id="wpsl-search-input" type="text" value="' . apply_filters( 'wpsl_search_input', '' ) . '" name="wpsl-search-input" placeholder="" aria-required="true" />' . "\r\n";
14
- $output .= "\t\t\t" . '</div>' . "\r\n";
15
-
16
- if ( $wpsl_settings['radius_dropdown'] || $wpsl_settings['results_dropdown'] ) {
17
- $output .= "\t\t\t" . '<div class="wpsl-select-wrap">' . "\r\n";
18
-
19
- if ( $wpsl_settings['radius_dropdown'] ) {
20
- $output .= "\t\t\t\t" . '<div id="wpsl-radius">' . "\r\n";
21
- $output .= "\t\t\t\t\t" . '<label for="wpsl-radius-dropdown">' . esc_html( $wpsl->i18n->get_translation( 'radius_label', __( 'Search radius', 'wpsl' ) ) ) . '</label>' . "\r\n";
22
- $output .= "\t\t\t\t\t" . '<select id="wpsl-radius-dropdown" class="wpsl-dropdown" name="wpsl-radius">' . "\r\n";
23
- $output .= "\t\t\t\t\t\t" . $this->get_dropdown_list( 'search_radius' ) . "\r\n";
24
- $output .= "\t\t\t\t\t" . '</select>' . "\r\n";
25
- $output .= "\t\t\t\t" . '</div>' . "\r\n";
26
- }
27
-
28
- if ( $wpsl_settings['results_dropdown'] ) {
29
- $output .= "\t\t\t\t" . '<div id="wpsl-results">' . "\r\n";
30
- $output .= "\t\t\t\t\t" . '<label for="wpsl-results-dropdown">' . esc_html( $wpsl->i18n->get_translation( 'results_label', __( 'Results', 'wpsl' ) ) ) . '</label>' . "\r\n";
31
- $output .= "\t\t\t\t\t" . '<select id="wpsl-results-dropdown" class="wpsl-dropdown" name="wpsl-results">' . "\r\n";
32
- $output .= "\t\t\t\t\t\t" . $this->get_dropdown_list( 'max_results' ) . "\r\n";
33
- $output .= "\t\t\t\t\t" . '</select>' . "\r\n";
34
- $output .= "\t\t\t\t" . '</div>' . "\r\n";
35
- }
36
-
37
- $output .= "\t\t\t" . '</div>' . "\r\n";
38
- }
39
-
40
- if ( $this->use_category_filter() ) {
41
- $output .= $this->create_category_filter();
42
- }
43
-
44
- $output .= "\t\t\t\t" . '<div class="wpsl-search-btn-wrap"><input id="wpsl-search-btn" type="submit" value="' . esc_attr( $wpsl->i18n->get_translation( 'search_btn_label', __( 'Search', 'wpsl' ) ) ) . '"></div>' . "\r\n";
45
-
46
- $output .= "\t\t" . '</form>' . "\r\n";
47
- $output .= "\t\t" . '</div>' . "\r\n";
48
- $output .= "\t" . '</div>' . "\r\n";
49
-
50
- $output .= "\t" . '<div id="wpsl-gmap" class="wpsl-gmap-canvas"></div>' . "\r\n";
51
-
52
- $output .= "\t" . '<div id="wpsl-result-list">' . "\r\n";
53
- $output .= "\t\t" . '<div id="wpsl-stores" '. $autoload_class .'>' . "\r\n";
54
- $output .= "\t\t\t" . '<ul></ul>' . "\r\n";
55
- $output .= "\t\t" . '</div>' . "\r\n";
56
- $output .= "\t\t" . '<div id="wpsl-direction-details">' . "\r\n";
57
- $output .= "\t\t\t" . '<ul></ul>' . "\r\n";
58
- $output .= "\t\t" . '</div>' . "\r\n";
59
- $output .= "\t" . '</div>' . "\r\n";
60
-
61
- if ( $wpsl_settings['show_credits'] ) {
62
- $output .= "\t" . '<div class="wpsl-provided-by">'. sprintf( __( "Search provided by %sWP Store Locator%s", "wpsl" ), "<a target='_blank' href='https://wpstorelocator.co'>", "</a>" ) .'</div>' . "\r\n";
63
- }
64
-
65
- $output .= '</div>' . "\r\n";
66
-
67
  return $output;
1
+ <?php
2
+ global $wpsl_settings, $wpsl;
3
+
4
+ $output = $this->get_custom_css();
5
+ $autoload_class = ( !$wpsl_settings['autoload'] ) ? 'class="wpsl-not-loaded"' : '';
6
+
7
+ $output .= '<div id="wpsl-wrap">' . "\r\n";
8
+ $output .= "\t" . '<div class="wpsl-search wpsl-clearfix ' . $this->get_css_classes() . '">' . "\r\n";
9
+ $output .= "\t\t" . '<div id="wpsl-search-wrap">' . "\r\n";
10
+ $output .= "\t\t\t" . '<form autocomplete="off">' . "\r\n";
11
+ $output .= "\t\t\t" . '<div class="wpsl-input">' . "\r\n";
12
+ $output .= "\t\t\t\t" . '<div><label for="wpsl-search-input">' . esc_html( $wpsl->i18n->get_translation( 'search_label', __( 'Your location', 'wpsl' ) ) ) . '</label></div>' . "\r\n";
13
+ $output .= "\t\t\t\t" . '<input id="wpsl-search-input" type="text" value="' . apply_filters( 'wpsl_search_input', '' ) . '" name="wpsl-search-input" placeholder="" aria-required="true" />' . "\r\n";
14
+ $output .= "\t\t\t" . '</div>' . "\r\n";
15
+
16
+ if ( $wpsl_settings['radius_dropdown'] || $wpsl_settings['results_dropdown'] ) {
17
+ $output .= "\t\t\t" . '<div class="wpsl-select-wrap">' . "\r\n";
18
+
19
+ if ( $wpsl_settings['radius_dropdown'] ) {
20
+ $output .= "\t\t\t\t" . '<div id="wpsl-radius">' . "\r\n";
21
+ $output .= "\t\t\t\t\t" . '<label for="wpsl-radius-dropdown">' . esc_html( $wpsl->i18n->get_translation( 'radius_label', __( 'Search radius', 'wpsl' ) ) ) . '</label>' . "\r\n";
22
+ $output .= "\t\t\t\t\t" . '<select id="wpsl-radius-dropdown" class="wpsl-dropdown" name="wpsl-radius">' . "\r\n";
23
+ $output .= "\t\t\t\t\t\t" . $this->get_dropdown_list( 'search_radius' ) . "\r\n";
24
+ $output .= "\t\t\t\t\t" . '</select>' . "\r\n";
25
+ $output .= "\t\t\t\t" . '</div>' . "\r\n";
26
+ }
27
+
28
+ if ( $wpsl_settings['results_dropdown'] ) {
29
+ $output .= "\t\t\t\t" . '<div id="wpsl-results">' . "\r\n";
30
+ $output .= "\t\t\t\t\t" . '<label for="wpsl-results-dropdown">' . esc_html( $wpsl->i18n->get_translation( 'results_label', __( 'Results', 'wpsl' ) ) ) . '</label>' . "\r\n";
31
+ $output .= "\t\t\t\t\t" . '<select id="wpsl-results-dropdown" class="wpsl-dropdown" name="wpsl-results">' . "\r\n";
32
+ $output .= "\t\t\t\t\t\t" . $this->get_dropdown_list( 'max_results' ) . "\r\n";
33
+ $output .= "\t\t\t\t\t" . '</select>' . "\r\n";
34
+ $output .= "\t\t\t\t" . '</div>' . "\r\n";
35
+ }
36
+
37
+ $output .= "\t\t\t" . '</div>' . "\r\n";
38
+ }
39
+
40
+ if ( $this->use_category_filter() ) {
41
+ $output .= $this->create_category_filter();
42
+ }
43
+
44
+ $output .= "\t\t\t\t" . '<div class="wpsl-search-btn-wrap"><input id="wpsl-search-btn" type="submit" value="' . esc_attr( $wpsl->i18n->get_translation( 'search_btn_label', __( 'Search', 'wpsl' ) ) ) . '"></div>' . "\r\n";
45
+
46
+ $output .= "\t\t" . '</form>' . "\r\n";
47
+ $output .= "\t\t" . '</div>' . "\r\n";
48
+ $output .= "\t" . '</div>' . "\r\n";
49
+
50
+ $output .= "\t" . '<div id="wpsl-gmap" class="wpsl-gmap-canvas"></div>' . "\r\n";
51
+
52
+ $output .= "\t" . '<div id="wpsl-result-list">' . "\r\n";
53
+ $output .= "\t\t" . '<div id="wpsl-stores" '. $autoload_class .'>' . "\r\n";
54
+ $output .= "\t\t\t" . '<ul></ul>' . "\r\n";
55
+ $output .= "\t\t" . '</div>' . "\r\n";
56
+ $output .= "\t\t" . '<div id="wpsl-direction-details">' . "\r\n";
57
+ $output .= "\t\t\t" . '<ul></ul>' . "\r\n";
58
+ $output .= "\t\t" . '</div>' . "\r\n";
59
+ $output .= "\t" . '</div>' . "\r\n";
60
+
61
+ if ( $wpsl_settings['show_credits'] ) {
62
+ $output .= "\t" . '<div class="wpsl-provided-by">'. sprintf( __( "Search provided by %sWP Store Locator%s", "wpsl" ), "<a target='_blank' href='https://wpstorelocator.co'>", "</a>" ) .'</div>' . "\r\n";
63
+ }
64
+
65
+ $output .= '</div>' . "\r\n";
66
+
67
  return $output;
frontend/templates/store-listings-below.php CHANGED
@@ -1,73 +1,73 @@
1
- <?php
2
- global $wpsl_settings, $wpsl;
3
-
4
- $output = $this->get_custom_css();
5
- $autoload_class = ( !$wpsl_settings['autoload'] ) ? 'class="wpsl-not-loaded"' : '';
6
-
7
- $output .= '<div id="wpsl-wrap" class="wpsl-store-below">' . "\r\n";
8
- $output .= "\t" . '<div class="wpsl-search wpsl-clearfix ' . $this->get_css_classes() . '">' . "\r\n";
9
- $output .= "\t\t" . '<div id="wpsl-search-wrap">' . "\r\n";
10
- $output .= "\t\t\t" . '<form autocomplete="off">' . "\r\n";
11
- $output .= "\t\t\t" . '<div class="wpsl-input">' . "\r\n";
12
- $output .= "\t\t\t\t" . '<div><label for="wpsl-search-input">' . esc_html( $wpsl->i18n->get_translation( 'search_label', __( 'Your location', 'wpsl' ) ) ) . '</label></div>' . "\r\n";
13
- $output .= "\t\t\t\t" . '<input id="wpsl-search-input" type="text" value="' . apply_filters( 'wpsl_search_input', '' ) . '" name="wpsl-search-input" placeholder="" aria-required="true" />' . "\r\n";
14
- $output .= "\t\t\t" . '</div>' . "\r\n";
15
-
16
- if ( $wpsl_settings['radius_dropdown'] || $wpsl_settings['results_dropdown'] ) {
17
- $output .= "\t\t\t" . '<div class="wpsl-select-wrap">' . "\r\n";
18
-
19
- if ( $wpsl_settings['radius_dropdown'] ) {
20
- $output .= "\t\t\t\t" . '<div id="wpsl-radius">' . "\r\n";
21
- $output .= "\t\t\t\t\t" . '<label for="wpsl-radius-dropdown">' . esc_html( $wpsl->i18n->get_translation( 'radius_label', __( 'Search radius', 'wpsl' ) ) ) . '</label>' . "\r\n";
22
- $output .= "\t\t\t\t\t" . '<select id="wpsl-radius-dropdown" class="wpsl-dropdown" name="wpsl-radius">' . "\r\n";
23
- $output .= "\t\t\t\t\t\t" . $this->get_dropdown_list( 'search_radius' ) . "\r\n";
24
- $output .= "\t\t\t\t\t" . '</select>' . "\r\n";
25
- $output .= "\t\t\t\t" . '</div>' . "\r\n";
26
- }
27
-
28
- if ( $wpsl_settings['results_dropdown'] ) {
29
- $output .= "\t\t\t\t" . '<div id="wpsl-results">' . "\r\n";
30
- $output .= "\t\t\t\t\t" . '<label for="wpsl-results-dropdown">' . esc_html( $wpsl->i18n->get_translation( 'results_label', __( 'Results', 'wpsl' ) ) ) . '</label>' . "\r\n";
31
- $output .= "\t\t\t\t\t" . '<select id="wpsl-results-dropdown" class="wpsl-dropdown" name="wpsl-results">' . "\r\n";
32
- $output .= "\t\t\t\t\t\t" . $this->get_dropdown_list( 'max_results' ) . "\r\n";
33
- $output .= "\t\t\t\t\t" . '</select>' . "\r\n";
34
- $output .= "\t\t\t\t" . '</div>' . "\r\n";
35
- }
36
-
37
- $output .= "\t\t\t" . '</div>' . "\r\n";
38
- }
39
-
40
- if ( $this->use_category_filter() ) {
41
- $output .= $this->create_category_filter();
42
- }
43
-
44
- $output .= "\t\t\t\t" . '<div class="wpsl-search-btn-wrap"><input id="wpsl-search-btn" type="submit" value="' . esc_attr( $wpsl->i18n->get_translation( 'search_btn_label', __( 'Search', 'wpsl' ) ) ) . '"></div>' . "\r\n";
45
-
46
- $output .= "\t\t" . '</form>' . "\r\n";
47
- $output .= "\t\t" . '</div>' . "\r\n";
48
- $output .= "\t" . '</div>' . "\r\n";
49
-
50
- if ( $wpsl_settings['reset_map'] ) {
51
- $output .= "\t" . '<div class="wpsl-gmap-wrap">' . "\r\n";
52
- $output .= "\t\t" . '<div id="wpsl-gmap" class="wpsl-gmap-canvas"></div>' . "\r\n";
53
- $output .= "\t" . '</div>' . "\r\n";
54
- } else {
55
- $output .= "\t" . '<div id="wpsl-gmap" class="wpsl-gmap-canvas"></div>' . "\r\n";
56
- }
57
-
58
- $output .= "\t" . '<div id="wpsl-result-list">' . "\r\n";
59
- $output .= "\t\t" . '<div id="wpsl-stores" '. $autoload_class .'>' . "\r\n";
60
- $output .= "\t\t\t" . '<ul></ul>' . "\r\n";
61
- $output .= "\t\t" . '</div>' . "\r\n";
62
- $output .= "\t\t" . '<div id="wpsl-direction-details">' . "\r\n";
63
- $output .= "\t\t\t" . '<ul></ul>' . "\r\n";
64
- $output .= "\t\t" . '</div>' . "\r\n";
65
- $output .= "\t" . '</div>' . "\r\n";
66
-
67
- if ( $wpsl_settings['show_credits'] ) {
68
- $output .= "\t" . '<div class="wpsl-provided-by">'. sprintf( __( "Search provided by %sWP Store Locator%s", "wpsl" ), "<a target='_blank' href='https://wpstorelocator.co'>", "</a>" ) .'</div>' . "\r\n";
69
- }
70
-
71
- $output .= '</div>' . "\r\n";
72
-
73
  return $output;
1
+ <?php
2
+ global $wpsl_settings, $wpsl;
3
+
4
+ $output = $this->get_custom_css();
5
+ $autoload_class = ( !$wpsl_settings['autoload'] ) ? 'class="wpsl-not-loaded"' : '';
6
+
7
+ $output .= '<div id="wpsl-wrap" class="wpsl-store-below">' . "\r\n";
8
+ $output .= "\t" . '<div class="wpsl-search wpsl-clearfix ' . $this->get_css_classes() . '">' . "\r\n";
9
+ $output .= "\t\t" . '<div id="wpsl-search-wrap">' . "\r\n";
10
+ $output .= "\t\t\t" . '<form autocomplete="off">' . "\r\n";
11
+ $output .= "\t\t\t" . '<div class="wpsl-input">' . "\r\n";
12
+ $output .= "\t\t\t\t" . '<div><label for="wpsl-search-input">' . esc_html( $wpsl->i18n->get_translation( 'search_label', __( 'Your location', 'wpsl' ) ) ) . '</label></div>' . "\r\n";
13
+ $output .= "\t\t\t\t" . '<input id="wpsl-search-input" type="text" value="' . apply_filters( 'wpsl_search_input', '' ) . '" name="wpsl-search-input" placeholder="" aria-required="true" />' . "\r\n";
14
+ $output .= "\t\t\t" . '</div>' . "\r\n";
15
+
16
+ if ( $wpsl_settings['radius_dropdown'] || $wpsl_settings['results_dropdown'] ) {
17
+ $output .= "\t\t\t" . '<div class="wpsl-select-wrap">' . "\r\n";
18
+
19
+ if ( $wpsl_settings['radius_dropdown'] ) {
20
+ $output .= "\t\t\t\t" . '<div id="wpsl-radius">' . "\r\n";
21
+ $output .= "\t\t\t\t\t" . '<label for="wpsl-radius-dropdown">' . esc_html( $wpsl->i18n->get_translation( 'radius_label', __( 'Search radius', 'wpsl' ) ) ) . '</label>' . "\r\n";
22
+ $output .= "\t\t\t\t\t" . '<select id="wpsl-radius-dropdown" class="wpsl-dropdown" name="wpsl-radius">' . "\r\n";
23
+ $output .= "\t\t\t\t\t\t" . $this->get_dropdown_list( 'search_radius' ) . "\r\n";
24
+ $output .= "\t\t\t\t\t" . '</select>' . "\r\n";
25
+ $output .= "\t\t\t\t" . '</div>' . "\r\n";
26
+ }
27
+
28
+ if ( $wpsl_settings['results_dropdown'] ) {
29
+ $output .= "\t\t\t\t" . '<div id="wpsl-results">' . "\r\n";
30
+ $output .= "\t\t\t\t\t" . '<label for="wpsl-results-dropdown">' . esc_html( $wpsl->i18n->get_translation( 'results_label', __( 'Results', 'wpsl' ) ) ) . '</label>' . "\r\n";
31
+ $output .= "\t\t\t\t\t" . '<select id="wpsl-results-dropdown" class="wpsl-dropdown" name="wpsl-results">' . "\r\n";
32
+ $output .= "\t\t\t\t\t\t" . $this->get_dropdown_list( 'max_results' ) . "\r\n";
33
+ $output .= "\t\t\t\t\t" . '</select>' . "\r\n";
34
+ $output .= "\t\t\t\t" . '</div>' . "\r\n";
35
+ }
36
+
37
+ $output .= "\t\t\t" . '</div>' . "\r\n";
38
+ }
39
+
40
+ if ( $this->use_category_filter() ) {
41
+ $output .= $this->create_category_filter();
42
+ }
43
+
44
+ $output .= "\t\t\t\t" . '<div class="wpsl-search-btn-wrap"><input id="wpsl-search-btn" type="submit" value="' . esc_attr( $wpsl->i18n->get_translation( 'search_btn_label', __( 'Search', 'wpsl' ) ) ) . '"></div>' . "\r\n";
45
+
46
+ $output .= "\t\t" . '</form>' . "\r\n";
47
+ $output .= "\t\t" . '</div>' . "\r\n";
48
+ $output .= "\t" . '</div>' . "\r\n";
49
+
50
+ if ( $wpsl_settings['reset_map'] ) {
51
+ $output .= "\t" . '<div class="wpsl-gmap-wrap">' . "\r\n";
52
+ $output .= "\t\t" . '<div id="wpsl-gmap" class="wpsl-gmap-canvas"></div>' . "\r\n";
53
+ $output .= "\t" . '</div>' . "\r\n";
54
+ } else {
55
+ $output .= "\t" . '<div id="wpsl-gmap" class="wpsl-gmap-canvas"></div>' . "\r\n";
56
+ }
57
+
58
+ $output .= "\t" . '<div id="wpsl-result-list">' . "\r\n";
59
+ $output .= "\t\t" . '<div id="wpsl-stores" '. $autoload_class .'>' . "\r\n";
60
+ $output .= "\t\t\t" . '<ul></ul>' . "\r\n";
61
+ $output .= "\t\t" . '</div>' . "\r\n";
62
+ $output .= "\t\t" . '<div id="wpsl-direction-details">' . "\r\n";
63
+ $output .= "\t\t\t" . '<ul></ul>' . "\r\n";
64
+ $output .= "\t\t" . '</div>' . "\r\n";
65
+ $output .= "\t" . '</div>' . "\r\n";
66
+
67
+ if ( $wpsl_settings['show_credits'] ) {
68
+ $output .= "\t" . '<div class="wpsl-provided-by">'. sprintf( __( "Search provided by %sWP Store Locator%s", "wpsl" ), "<a target='_blank' href='https://wpstorelocator.co'>", "</a>" ) .'</div>' . "\r\n";
69
+ }
70
+
71
+ $output .= '</div>' . "\r\n";
72
+
73
  return $output;
frontend/underscore-functions.php CHANGED
@@ -1,267 +1,267 @@
1
- <?php
2
- /**
3
- * Create the store data templates.
4
- *
5
- * The templates are created in JS with _.template, see http://underscorejs.org/#template
6
- *
7
- * @since 2.0.0
8
- * @param string $template The type of template we need to create
9
- * @return void
10
- */
11
- function wpsl_create_underscore_templates( $template ) {
12
-
13
- global $wpsl_settings, $wpsl;
14
-
15
- if ( $template == 'wpsl_store_locator' ) {
16
- ?>
17
- <script id="wpsl-info-window-template" type="text/template">
18
- <?php
19
- $info_window_template = '<div data-store-id="<%= id %>" class="wpsl-info-window">' . "\r\n";
20
- $info_window_template .= "\t\t" . '<p>' . "\r\n";
21
- $info_window_template .= "\t\t\t" . wpsl_store_header_template() . "\r\n"; // Check which header format we use
22
- $info_window_template .= "\t\t\t" . '<span><%= address %></span>' . "\r\n";
23
- $info_window_template .= "\t\t\t" . '<% if ( address2 ) { %>' . "\r\n";
24
- $info_window_template .= "\t\t\t" . '<span><%= address2 %></span>' . "\r\n";
25
- $info_window_template .= "\t\t\t" . '<% } %>' . "\r\n";
26
- $info_window_template .= "\t\t\t" . '<span>' . wpsl_address_format_placeholders() . '</span>' . "\r\n"; // Use the correct address format
27
- $info_window_template .= "\t\t" . '</p>' . "\r\n";
28
- $info_window_template .= "\t\t" . '<% if ( phone ) { %>' . "\r\n";
29
- $info_window_template .= "\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'phone_label', __( 'Phone', 'wpsl' ) ) ) . '</strong>: <%= formatPhoneNumber( phone ) %></span>' . "\r\n";
30
- $info_window_template .= "\t\t" . '<% } %>' . "\r\n";
31
- $info_window_template .= "\t\t" . '<% if ( fax ) { %>' . "\r\n";
32
- $info_window_template .= "\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'fax_label', __( 'Fax', 'wpsl' ) ) ) . '</strong>: <%= fax %></span>' . "\r\n";
33
- $info_window_template .= "\t\t" . '<% } %>' . "\r\n";
34
- $info_window_template .= "\t\t" . '<% if ( email ) { %>' . "\r\n";
35
- $info_window_template .= "\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'email_label', __( 'Email', 'wpsl' ) ) ) . '</strong>: <%= formatEmail( email ) %></span>' . "\r\n";
36
- $info_window_template .= "\t\t" . '<% } %>' . "\r\n";
37
- $info_window_template .= "\t\t" . '<%= createInfoWindowActions( id ) %>' . "\r\n";
38
- $info_window_template .= "\t" . '</div>';
39
-
40
- echo apply_filters( 'wpsl_info_window_template', $info_window_template . "\n" );
41
- ?>
42
- </script>
43
- <script id="wpsl-listing-template" type="text/template">
44
- <?php
45
- $listing_template = '<li data-store-id="<%= id %>">' . "\r\n";
46
- $listing_template .= "\t\t" . '<div class="wpsl-store-location">' . "\r\n";
47
- $listing_template .= "\t\t\t" . '<p><%= thumb %>' . "\r\n";
48
- $listing_template .= "\t\t\t\t" . wpsl_store_header_template( 'listing' ) . "\r\n"; // Check which header format we use
49
- $listing_template .= "\t\t\t\t" . '<span class="wpsl-street"><%= address %></span>' . "\r\n";
50
- $listing_template .= "\t\t\t\t" . '<% if ( address2 ) { %>' . "\r\n";
51
- $listing_template .= "\t\t\t\t" . '<span class="wpsl-street"><%= address2 %></span>' . "\r\n";
52
- $listing_template .= "\t\t\t\t" . '<% } %>' . "\r\n";
53
- $listing_template .= "\t\t\t\t" . '<span>' . wpsl_address_format_placeholders() . '</span>' . "\r\n"; // Use the correct address format
54
-
55
- if ( !$wpsl_settings['hide_country'] ) {
56
- $listing_template .= "\t\t\t\t" . '<span class="wpsl-country"><%= country %></span>' . "\r\n";
57
- }
58
-
59
- $listing_template .= "\t\t\t" . '</p>' . "\r\n";
60
-
61
- // Show the phone, fax or email data if they exist.
62
- if ( $wpsl_settings['show_contact_details'] ) {
63
- $listing_template .= "\t\t\t" . '<p class="wpsl-contact-details">' . "\r\n";
64
- $listing_template .= "\t\t\t" . '<% if ( phone ) { %>' . "\r\n";
65
- $listing_template .= "\t\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'phone_label', __( 'Phone', 'wpsl' ) ) ) . '</strong>: <%= formatPhoneNumber( phone ) %></span>' . "\r\n";
66
- $listing_template .= "\t\t\t" . '<% } %>' . "\r\n";
67
- $listing_template .= "\t\t\t" . '<% if ( fax ) { %>' . "\r\n";
68
- $listing_template .= "\t\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'fax_label', __( 'Fax', 'wpsl' ) ) ) . '</strong>: <%= fax %></span>' . "\r\n";
69
- $listing_template .= "\t\t\t" . '<% } %>' . "\r\n";
70
- $listing_template .= "\t\t\t" . '<% if ( email ) { %>' . "\r\n";
71
- $listing_template .= "\t\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'email_label', __( 'Email', 'wpsl' ) ) ) . '</strong>: <%= formatEmail( email ) %></span>' . "\r\n";
72
- $listing_template .= "\t\t\t" . '<% } %>' . "\r\n";
73
- $listing_template .= "\t\t\t" . '</p>' . "\r\n";
74
- }
75
-
76
- $listing_template .= "\t\t\t" . wpsl_more_info_template() . "\r\n"; // Check if we need to show the 'More Info' link and info
77
- $listing_template .= "\t\t" . '</div>' . "\r\n";
78
- $listing_template .= "\t\t" . '<div class="wpsl-direction-wrap">' . "\r\n";
79
-
80
- if ( !$wpsl_settings['hide_distance'] ) {
81
- $listing_template .= "\t\t\t" . '<%= distance %> ' . esc_html( wpsl_get_distance_unit() ) . '' . "\r\n";
82
- }
83
-
84
- $listing_template .= "\t\t\t" . '<%= createDirectionUrl() %>' . "\r\n";
85
- $listing_template .= "\t\t" . '</div>' . "\r\n";
86
- $listing_template .= "\t" . '</li>';
87
-
88
- echo apply_filters( 'wpsl_listing_template', $listing_template . "\n" );
89
- ?>
90
- </script>
91
- <?php
92
- } else {
93
- ?>
94
- <script id="wpsl-cpt-info-window-template" type="text/template">
95
- <?php
96
- $cpt_info_window_template = '<div class="wpsl-info-window">' . "\r\n";
97
- $cpt_info_window_template .= "\t\t" . '<p class="wpsl-no-margin">' . "\r\n";
98
- $cpt_info_window_template .= "\t\t\t" . wpsl_store_header_template( 'wpsl_map' ) . "\r\n";
99
- $cpt_info_window_template .= "\t\t\t" . '<span><%= address %></span>' . "\r\n";
100
- $cpt_info_window_template .= "\t\t\t" . '<% if ( address2 ) { %>' . "\r\n";
101
- $cpt_info_window_template .= "\t\t\t" . '<span><%= address2 %></span>' . "\r\n";
102
- $cpt_info_window_template .= "\t\t\t" . '<% } %>' . "\r\n";
103
- $cpt_info_window_template .= "\t\t\t" . '<span>' . wpsl_address_format_placeholders() . '</span>' . "\r\n"; // Use the correct address format
104
-
105
- if ( !$wpsl_settings['hide_country'] ) {
106
- $cpt_info_window_template .= "\t\t\t" . '<span class="wpsl-country"><%= country %></span>' . "\r\n";
107
- }
108
-
109
- $cpt_info_window_template .= "\t\t" . '</p>' . "\r\n";
110
- $cpt_info_window_template .= "\t" . '</div>';
111
-
112
- echo apply_filters( 'wpsl_cpt_info_window_template', $cpt_info_window_template . "\n" );
113
- ?>
114
- </script>
115
- <?php
116
- }
117
- }
118
-
119
- /**
120
- * Create the more info template.
121
- *
122
- * @since 2.0.0
123
- * @return string $more_info_template The template that is used to show the "More info" content
124
- */
125
- function wpsl_more_info_template() {
126
-
127
- global $wpsl_settings, $wpsl;
128
-
129
- if ( $wpsl_settings['more_info'] ) {
130
- $more_info_url = '#';
131
-
132
- if ( $wpsl_settings['template_id'] == 'default' && $wpsl_settings['more_info_location'] == 'info window' ) {
133
- $more_info_url = '#wpsl-search-wrap';
134
- }
135
-
136
- if ( $wpsl_settings['more_info_location'] == 'store listings' ) {
137
- $more_info_template = '<% if ( !_.isEmpty( phone ) || !_.isEmpty( fax ) || !_.isEmpty( email ) ) { %>' . "\r\n";
138
- $more_info_template .= "\t\t\t" . '<p><a class="wpsl-store-details wpsl-store-listing" href="#wpsl-id-<%= id %>">' . esc_html( $wpsl->i18n->get_translation( 'more_label', __( 'More info', 'wpsl' ) ) ) . '</a></p>' . "\r\n";
139
- $more_info_template .= "\t\t\t" . '<div id="wpsl-id-<%= id %>" class="wpsl-more-info-listings">' . "\r\n";
140
- $more_info_template .= "\t\t\t\t" . '<% if ( description ) { %>' . "\r\n";
141
- $more_info_template .= "\t\t\t\t" . '<%= description %>' . "\r\n";
142
- $more_info_template .= "\t\t\t\t" . '<% } %>' . "\r\n";
143
-
144
- if ( !$wpsl_settings['show_contact_details'] ) {
145
- $more_info_template .= "\t\t\t\t" . '<p>' . "\r\n";
146
- $more_info_template .= "\t\t\t\t" . '<% if ( phone ) { %>' . "\r\n";
147
- $more_info_template .= "\t\t\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'phone_label', __( 'Phone', 'wpsl' ) ) ) . '</strong>: <%= formatPhoneNumber( phone ) %></span>' . "\r\n";
148
- $more_info_template .= "\t\t\t\t" . '<% } %>' . "\r\n";
149
- $more_info_template .= "\t\t\t\t" . '<% if ( fax ) { %>' . "\r\n";
150
- $more_info_template .= "\t\t\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'fax_label', __( 'Fax', 'wpsl' ) ) ) . '</strong>: <%= fax %></span>' . "\r\n";
151
- $more_info_template .= "\t\t\t\t" . '<% } %>' . "\r\n";
152
- $more_info_template .= "\t\t\t\t" . '<% if ( email ) { %>' . "\r\n";
153
- $more_info_template .= "\t\t\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'email_label', __( 'Email', 'wpsl' ) ) ) . '</strong>: <%= formatEmail( email ) %></span>' . "\r\n";
154
- $more_info_template .= "\t\t\t\t" . '<% } %>' . "\r\n";
155
- $more_info_template .= "\t\t\t\t" . '</p>' . "\r\n";
156
- }
157
-
158
- if ( !$wpsl_settings['hide_hours'] ) {
159
- $more_info_template .= "\t\t\t\t" . '<% if ( hours ) { %>' . "\r\n";
160
- $more_info_template .= "\t\t\t\t" . '<div class="wpsl-store-hours"><strong>' . esc_html( $wpsl->i18n->get_translation( 'hours_label', __( 'Hours', 'wpsl' ) ) ) . '</strong><%= hours %></div>' . "\r\n";
161
- $more_info_template .= "\t\t\t\t" . '<% } %>' . "\r\n";
162
- }
163
-
164
- $more_info_template .= "\t\t\t" . '</div>' . "\r\n";
165
- $more_info_template .= "\t\t\t" . '<% } %>';
166
-
167
- } else {
168
- $more_info_template = '<p><a class="wpsl-store-details" href="' . $more_info_url . '">' . esc_html( $wpsl->i18n->get_translation( 'more_label', __( 'More info', 'wpsl' ) ) ) . '</a></p>';
169
- }
170
-
171
- return apply_filters( 'wpsl_more_info_template', $more_info_template );
172
- }
173
- }
174
-
175
- /**
176
- * Create the store header template.
177
- *
178
- * @since 2.0.0
179
- * @param string $location The location where the header is shown ( info_window / listing / wpsl_map shortcode )
180
- * @return string $header_template The template for the store header
181
- */
182
- function wpsl_store_header_template( $location = 'info_window' ) {
183
-
184
- global $wpsl_settings;
185
-
186
- if ( $wpsl_settings['new_window'] ) {
187
- $new_window = ' target="_blank"';
188
- } else {
189
- $new_window = '';
190
- }
191
-
192
- /*
193
- * To keep the code readable in the HTML source we ( unfortunately ) need to adjust the
194
- * amount of tabs in front of it based on the location were it is shown.
195
- */
196
- if ( $location == 'listing') {
197
- $tab = "\t\t\t\t";
198
- } else {
199
- $tab = "\t\t\t";
200
- }
201
-
202
- if ( $wpsl_settings['permalinks'] ) {
203
-
204
- /**
205
- * It's possible the permalinks are enabled, but not included in the location data on
206
- * pages where the [wpsl_map] shortcode is used.
207
- *
208
- * So we need to check for undefined, which isn't necessary in all other cases.
209
- */
210
- if ( $location == 'wpsl_map') {
211
- $header_template = '<% if ( typeof permalink !== "undefined" ) { %>' . "\r\n";
212
- $header_template .= $tab . '<strong><a' . $new_window . ' href="<%= permalink %>"><%= store %></a></strong>' . "\r\n";
213
- $header_template .= $tab . '<% } else { %>' . "\r\n";
214
- $header_template .= $tab . '<strong><%= store %></strong>' . "\r\n";
215
- $header_template .= $tab . '<% } %>';
216
- } else {
217
- $header_template = '<strong><a' . $new_window . ' href="<%= permalink %>"><%= store %></a></strong>';
218
- }
219
- } else {
220
- $header_template = '<% if ( wpslSettings.storeUrl == 1 && url ) { %>' . "\r\n";
221
- $header_template .= $tab . '<strong><a' . $new_window . ' href="<%= url %>"><%= store %></a></strong>' . "\r\n";
222
- $header_template .= $tab . '<% } else { %>' . "\r\n";
223
- $header_template .= $tab . '<strong><%= store %></strong>' . "\r\n";
224
- $header_template .= $tab . '<% } %>';
225
- }
226
-
227
- return apply_filters( 'wpsl_store_header_template', $header_template );
228
- }
229
-
230
- /**
231
- * Create the address placeholders based on the structure defined on the settings page.
232
- *
233
- * @since 2.0.0
234
- * @return string $address_placeholders A list of address placeholders in the correct order
235
- */
236
- function wpsl_address_format_placeholders() {
237
-
238
- global $wpsl_settings;
239
-
240
- $address_format = explode( '_', $wpsl_settings['address_format'] );
241
- $placeholders = '';
242
- $part_count = count( $address_format ) - 1;
243
- $i = 0;
244
-
245
- foreach ( $address_format as $address_part ) {
246
- if ( $address_part != 'comma' ) {
247
-
248
- /*
249
- * Don't add a space after the placeholder if the next part
250
- * is going to be a comma or if it is the last part.
251
- */
252
- if ( $i == $part_count || $address_format[$i + 1] == 'comma' ) {
253
- $space = '';
254
- } else {
255
- $space = ' ';
256
- }
257
-
258
- $placeholders .= '<%= ' . $address_part . ' %>' . $space;
259
- } else {
260
- $placeholders .= ', ';
261
- }
262
-
263
- $i++;
264
- }
265
-
266
- return $placeholders;
267
  }
1
+ <?php
2
+ /**
3
+ * Create the store data templates.
4
+ *
5
+ * The templates are created in JS with _.template, see http://underscorejs.org/#template
6
+ *
7
+ * @since 2.0.0
8
+ * @param string $template The type of template we need to create
9
+ * @return void
10
+ */
11
+ function wpsl_create_underscore_templates( $template ) {
12
+
13
+ global $wpsl_settings, $wpsl;
14
+
15
+ if ( $template == 'wpsl_store_locator' ) {
16
+ ?>
17
+ <script id="wpsl-info-window-template" type="text/template">
18
+ <?php
19
+ $info_window_template = '<div data-store-id="<%= id %>" class="wpsl-info-window">' . "\r\n";
20
+ $info_window_template .= "\t\t" . '<p>' . "\r\n";
21
+ $info_window_template .= "\t\t\t" . wpsl_store_header_template() . "\r\n"; // Check which header format we use
22
+ $info_window_template .= "\t\t\t" . '<span><%= address %></span>' . "\r\n";
23
+ $info_window_template .= "\t\t\t" . '<% if ( address2 ) { %>' . "\r\n";
24
+ $info_window_template .= "\t\t\t" . '<span><%= address2 %></span>' . "\r\n";
25
+ $info_window_template .= "\t\t\t" . '<% } %>' . "\r\n";
26
+ $info_window_template .= "\t\t\t" . '<span>' . wpsl_address_format_placeholders() . '</span>' . "\r\n"; // Use the correct address format
27
+ $info_window_template .= "\t\t" . '</p>' . "\r\n";
28
+ $info_window_template .= "\t\t" . '<% if ( phone ) { %>' . "\r\n";
29
+ $info_window_template .= "\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'phone_label', __( 'Phone', 'wpsl' ) ) ) . '</strong>: <%= formatPhoneNumber( phone ) %></span>' . "\r\n";
30
+ $info_window_template .= "\t\t" . '<% } %>' . "\r\n";
31
+ $info_window_template .= "\t\t" . '<% if ( fax ) { %>' . "\r\n";
32
+ $info_window_template .= "\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'fax_label', __( 'Fax', 'wpsl' ) ) ) . '</strong>: <%= fax %></span>' . "\r\n";
33
+ $info_window_template .= "\t\t" . '<% } %>' . "\r\n";
34
+ $info_window_template .= "\t\t" . '<% if ( email ) { %>' . "\r\n";
35
+ $info_window_template .= "\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'email_label', __( 'Email', 'wpsl' ) ) ) . '</strong>: <%= formatEmail( email ) %></span>' . "\r\n";
36
+ $info_window_template .= "\t\t" . '<% } %>' . "\r\n";
37
+ $info_window_template .= "\t\t" . '<%= createInfoWindowActions( id ) %>' . "\r\n";
38
+ $info_window_template .= "\t" . '</div>';
39
+
40
+ echo apply_filters( 'wpsl_info_window_template', $info_window_template . "\n" );
41
+ ?>
42
+ </script>
43
+ <script id="wpsl-listing-template" type="text/template">
44
+ <?php
45
+ $listing_template = '<li data-store-id="<%= id %>">' . "\r\n";
46
+ $listing_template .= "\t\t" . '<div class="wpsl-store-location">' . "\r\n";
47
+ $listing_template .= "\t\t\t" . '<p><%= thumb %>' . "\r\n";
48
+ $listing_template .= "\t\t\t\t" . wpsl_store_header_template( 'listing' ) . "\r\n"; // Check which header format we use
49
+ $listing_template .= "\t\t\t\t" . '<span class="wpsl-street"><%= address %></span>' . "\r\n";
50
+ $listing_template .= "\t\t\t\t" . '<% if ( address2 ) { %>' . "\r\n";
51
+ $listing_template .= "\t\t\t\t" . '<span class="wpsl-street"><%= address2 %></span>' . "\r\n";
52
+ $listing_template .= "\t\t\t\t" . '<% } %>' . "\r\n";
53
+ $listing_template .= "\t\t\t\t" . '<span>' . wpsl_address_format_placeholders() . '</span>' . "\r\n"; // Use the correct address format
54
+
55
+ if ( !$wpsl_settings['hide_country'] ) {
56
+ $listing_template .= "\t\t\t\t" . '<span class="wpsl-country"><%= country %></span>' . "\r\n";
57
+ }
58
+
59
+ $listing_template .= "\t\t\t" . '</p>' . "\r\n";
60
+
61
+ // Show the phone, fax or email data if they exist.
62
+ if ( $wpsl_settings['show_contact_details'] ) {
63
+ $listing_template .= "\t\t\t" . '<p class="wpsl-contact-details">' . "\r\n";
64
+ $listing_template .= "\t\t\t" . '<% if ( phone ) { %>' . "\r\n";
65
+ $listing_template .= "\t\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'phone_label', __( 'Phone', 'wpsl' ) ) ) . '</strong>: <%= formatPhoneNumber( phone ) %></span>' . "\r\n";
66
+ $listing_template .= "\t\t\t" . '<% } %>' . "\r\n";
67
+ $listing_template .= "\t\t\t" . '<% if ( fax ) { %>' . "\r\n";
68
+ $listing_template .= "\t\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'fax_label', __( 'Fax', 'wpsl' ) ) ) . '</strong>: <%= fax %></span>' . "\r\n";
69
+ $listing_template .= "\t\t\t" . '<% } %>' . "\r\n";
70
+ $listing_template .= "\t\t\t" . '<% if ( email ) { %>' . "\r\n";
71
+ $listing_template .= "\t\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'email_label', __( 'Email', 'wpsl' ) ) ) . '</strong>: <%= formatEmail( email ) %></span>' . "\r\n";
72
+ $listing_template .= "\t\t\t" . '<% } %>' . "\r\n";
73
+ $listing_template .= "\t\t\t" . '</p>' . "\r\n";
74
+ }
75
+
76
+ $listing_template .= "\t\t\t" . wpsl_more_info_template() . "\r\n"; // Check if we need to show the 'More Info' link and info
77
+ $listing_template .= "\t\t" . '</div>' . "\r\n";
78
+ $listing_template .= "\t\t" . '<div class="wpsl-direction-wrap">' . "\r\n";
79
+
80
+ if ( !$wpsl_settings['hide_distance'] ) {
81
+ $listing_template .= "\t\t\t" . '<%= distance %> ' . esc_html( wpsl_get_distance_unit() ) . '' . "\r\n";
82
+ }
83
+
84
+ $listing_template .= "\t\t\t" . '<%= createDirectionUrl() %>' . "\r\n";
85
+ $listing_template .= "\t\t" . '</div>' . "\r\n";
86
+ $listing_template .= "\t" . '</li>';
87
+
88
+ echo apply_filters( 'wpsl_listing_template', $listing_template . "\n" );
89
+ ?>
90
+ </script>
91
+ <?php
92
+ } else {
93
+ ?>
94
+ <script id="wpsl-cpt-info-window-template" type="text/template">
95
+ <?php
96
+ $cpt_info_window_template = '<div class="wpsl-info-window">' . "\r\n";
97
+ $cpt_info_window_template .= "\t\t" . '<p class="wpsl-no-margin">' . "\r\n";
98
+ $cpt_info_window_template .= "\t\t\t" . wpsl_store_header_template( 'wpsl_map' ) . "\r\n";
99
+ $cpt_info_window_template .= "\t\t\t" . '<span><%= address %></span>' . "\r\n";
100
+ $cpt_info_window_template .= "\t\t\t" . '<% if ( address2 ) { %>' . "\r\n";
101
+ $cpt_info_window_template .= "\t\t\t" . '<span><%= address2 %></span>' . "\r\n";
102
+ $cpt_info_window_template .= "\t\t\t" . '<% } %>' . "\r\n";
103
+ $cpt_info_window_template .= "\t\t\t" . '<span>' . wpsl_address_format_placeholders() . '</span>' . "\r\n"; // Use the correct address format
104
+
105
+ if ( !$wpsl_settings['hide_country'] ) {
106
+ $cpt_info_window_template .= "\t\t\t" . '<span class="wpsl-country"><%= country %></span>' . "\r\n";
107
+ }
108
+
109
+ $cpt_info_window_template .= "\t\t" . '</p>' . "\r\n";
110
+ $cpt_info_window_template .= "\t" . '</div>';
111
+
112
+ echo apply_filters( 'wpsl_cpt_info_window_template', $cpt_info_window_template . "\n" );
113
+ ?>
114
+ </script>
115
+ <?php
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Create the more info template.
121
+ *
122
+ * @since 2.0.0
123
+ * @return string $more_info_template The template that is used to show the "More info" content
124
+ */
125
+ function wpsl_more_info_template() {
126
+
127
+ global $wpsl_settings, $wpsl;
128
+
129
+ if ( $wpsl_settings['more_info'] ) {
130
+ $more_info_url = '#';
131
+
132
+ if ( $wpsl_settings['template_id'] == 'default' && $wpsl_settings['more_info_location'] == 'info window' ) {
133
+ $more_info_url = '#wpsl-search-wrap';
134
+ }
135
+
136
+ if ( $wpsl_settings['more_info_location'] == 'store listings' ) {
137
+ $more_info_template = '<% if ( !_.isEmpty( phone ) || !_.isEmpty( fax ) || !_.isEmpty( email ) ) { %>' . "\r\n";
138
+ $more_info_template .= "\t\t\t" . '<p><a class="wpsl-store-details wpsl-store-listing" href="#wpsl-id-<%= id %>">' . esc_html( $wpsl->i18n->get_translation( 'more_label', __( 'More info', 'wpsl' ) ) ) . '</a></p>' . "\r\n";
139
+ $more_info_template .= "\t\t\t" . '<div id="wpsl-id-<%= id %>" class="wpsl-more-info-listings">' . "\r\n";
140
+ $more_info_template .= "\t\t\t\t" . '<% if ( description ) { %>' . "\r\n";
141
+ $more_info_template .= "\t\t\t\t" . '<%= description %>' . "\r\n";
142
+ $more_info_template .= "\t\t\t\t" . '<% } %>' . "\r\n";
143
+
144
+ if ( !$wpsl_settings['show_contact_details'] ) {
145
+ $more_info_template .= "\t\t\t\t" . '<p>' . "\r\n";
146
+ $more_info_template .= "\t\t\t\t" . '<% if ( phone ) { %>' . "\r\n";
147
+ $more_info_template .= "\t\t\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'phone_label', __( 'Phone', 'wpsl' ) ) ) . '</strong>: <%= formatPhoneNumber( phone ) %></span>' . "\r\n";
148
+ $more_info_template .= "\t\t\t\t" . '<% } %>' . "\r\n";
149
+ $more_info_template .= "\t\t\t\t" . '<% if ( fax ) { %>' . "\r\n";
150
+ $more_info_template .= "\t\t\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'fax_label', __( 'Fax', 'wpsl' ) ) ) . '</strong>: <%= fax %></span>' . "\r\n";
151
+ $more_info_template .= "\t\t\t\t" . '<% } %>' . "\r\n";
152
+ $more_info_template .= "\t\t\t\t" . '<% if ( email ) { %>' . "\r\n";
153
+ $more_info_template .= "\t\t\t\t" . '<span><strong>' . esc_html( $wpsl->i18n->get_translation( 'email_label', __( 'Email', 'wpsl' ) ) ) . '</strong>: <%= formatEmail( email ) %></span>' . "\r\n";
154
+ $more_info_template .= "\t\t\t\t" . '<% } %>' . "\r\n";
155
+ $more_info_template .= "\t\t\t\t" . '</p>' . "\r\n";
156
+ }
157
+
158
+ if ( !$wpsl_settings['hide_hours'] ) {
159
+ $more_info_template .= "\t\t\t\t" . '<% if ( hours ) { %>' . "\r\n";
160
+ $more_info_template .= "\t\t\t\t" . '<div class="wpsl-store-hours"><strong>' . esc_html( $wpsl->i18n->get_translation( 'hours_label', __( 'Hours', 'wpsl' ) ) ) . '</strong><%= hours %></div>' . "\r\n";
161
+ $more_info_template .= "\t\t\t\t" . '<% } %>' . "\r\n";
162
+ }
163
+
164
+ $more_info_template .= "\t\t\t" . '</div>' . "\r\n";
165
+ $more_info_template .= "\t\t\t" . '<% } %>';
166
+
167
+ } else {
168
+ $more_info_template = '<p><a class="wpsl-store-details" href="' . $more_info_url . '">' . esc_html( $wpsl->i18n->get_translation( 'more_label', __( 'More info', 'wpsl' ) ) ) . '</a></p>';
169
+ }
170
+
171
+ return apply_filters( 'wpsl_more_info_template', $more_info_template );
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Create the store header template.
177
+ *
178
+ * @since 2.0.0
179
+ * @param string $location The location where the header is shown ( info_window / listing / wpsl_map shortcode )
180
+ * @return string $header_template The template for the store header
181
+ */
182
+ function wpsl_store_header_template( $location = 'info_window' ) {
183
+
184
+ global $wpsl_settings;
185
+
186
+ if ( $wpsl_settings['new_window'] ) {
187
+ $new_window = ' target="_blank"';
188
+ } else {
189
+ $new_window = '';
190
+ }
191
+
192
+ /*
193
+ * To keep the code readable in the HTML source we ( unfortunately ) need to adjust the
194
+ * amount of tabs in front of it based on the location were it is shown.
195
+ */
196
+ if ( $location == 'listing') {
197
+ $tab = "\t\t\t\t";
198
+ } else {
199
+ $tab = "\t\t\t";
200
+ }
201
+
202
+ if ( $wpsl_settings['permalinks'] ) {
203
+
204
+ /**
205
+ * It's possible the permalinks are enabled, but not included in the location data on
206
+ * pages where the [wpsl_map] shortcode is used.
207
+ *
208
+ * So we need to check for undefined, which isn't necessary in all other cases.
209
+ */
210
+ if ( $location == 'wpsl_map') {
211
+ $header_template = '<% if ( typeof permalink !== "undefined" ) { %>' . "\r\n";
212
+ $header_template .= $tab . '<strong><a' . $new_window . ' href="<%= permalink %>"><%= store %></a></strong>' . "\r\n";
213
+ $header_template .= $tab . '<% } else { %>' . "\r\n";
214
+ $header_template .= $tab . '<strong><%= store %></strong>' . "\r\n";
215
+ $header_template .= $tab . '<% } %>';
216
+ } else {
217
+ $header_template = '<strong><a' . $new_window . ' href="<%= permalink %>"><%= store %></a></strong>';
218
+ }
219
+ } else {
220
+ $header_template = '<% if ( wpslSettings.storeUrl == 1 && url ) { %>' . "\r\n";
221
+ $header_template .= $tab . '<strong><a' . $new_window . ' href="<%= url %>"><%= store %></a></strong>' . "\r\n";
222
+ $header_template .= $tab . '<% } else { %>' . "\r\n";
223
+ $header_template .= $tab . '<strong><%= store %></strong>' . "\r\n";
224
+ $header_template .= $tab . '<% } %>';
225
+ }
226
+
227
+ return apply_filters( 'wpsl_store_header_template', $header_template );
228
+ }
229
+
230
+ /**
231
+ * Create the address placeholders based on the structure defined on the settings page.
232
+ *
233
+ * @since 2.0.0
234
+ * @return string $address_placeholders A list of address placeholders in the correct order
235
+ */
236
+ function wpsl_address_format_placeholders() {
237
+
238
+ global $wpsl_settings;
239
+
240
+ $address_format = explode( '_', $wpsl_settings['address_format'] );
241
+ $placeholders = '';
242
+ $part_count = count( $address_format ) - 1;
243
+ $i = 0;
244
+
245
+ foreach ( $address_format as $address_part ) {
246
+ if ( $address_part != 'comma' ) {
247
+
248
+ /*
249
+ * Don't add a space after the placeholder if the next part
250
+ * is going to be a comma or if it is the last part.
251
+ */
252
+ if ( $i == $part_count || $address_format[$i + 1] == 'comma' ) {
253
+ $space = '';
254
+ } else {
255
+ $space = ' ';
256
+ }
257
+
258
+ $placeholders .= '<%= ' . $address_part . ' %>' . $space;
259
+ } else {
260
+ $placeholders .= ', ';
261
+ }
262
+
263
+ $i++;
264
+ }
265
+
266
+ return $placeholders;
267
  }
inc/class-i18n.php CHANGED
@@ -1,160 +1,160 @@
1
- <?php
2
- /**
3
- * i18n class
4
- *
5
- * @author Tijmen Smit
6
- * @since 2.0.0
7
- */
8
-
9
- if ( !defined( 'ABSPATH' ) ) exit;
10
-
11
- if ( !class_exists( 'WPSL_i18n' ) ) {
12
-
13
- class WPSL_i18n {
14
-
15
- private $wpml_active = null;
16
-
17
- private $qtrans_active = null;
18
-
19
- /**
20
- * Class constructor
21
- */
22
- function __construct() {
23
- add_action( 'plugins_loaded', array( $this, 'load_plugin_textdomain' ) );
24
- }
25
-
26
- /**
27
- * Load the translations from the language folder
28
- *
29
- * @since 2.0.0
30
- * @return void
31
- */
32
- public function load_plugin_textdomain() {
33
-
34
- $domain = 'wpsl';
35
- $locale = apply_filters( 'plugin_locale', get_locale(), $domain );
36
-
37
- // Load the language file from the /wp-content/languages/wp-store-locator folder, custom + update proof translations.
38
- load_textdomain( $domain, WP_LANG_DIR . '/wp-store-locator/' . $domain . '-' . $locale . '.mo' );
39
-
40
- // Load the language file from the /wp-content/plugins/wp-store-locator/languages/ folder.
41
- load_plugin_textdomain( $domain, false, dirname( WPSL_BASENAME ) . '/languages/' );
42
- }
43
-
44
- /**
45
- * Check if WPML is active
46
- *
47
- * @since 2.0.0
48
- * @return boolean|null
49
- */
50
- public function wpml_exists() {
51
-
52
- if ( $this->wpml_active == null ) {
53
- $this->wpml_active = function_exists( 'icl_register_string' );
54
- }
55
-
56
- return $this->wpml_active;
57
- }
58
-
59
- /**
60
- * Check if a qTranslate compatible plugin is active.
61
- *
62
- * @since 2.0.0
63
- * @return boolean|null
64
- */
65
- public function qtrans_exists() {
66
-
67
- if ( $this->qtrans_active == null ) {
68
- $this->qtrans_active = ( function_exists( 'qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage' ) || function_exists( 'qtranxf_useCurrentLanguageIfNotFoundUseDefaultLanguage' ) );
69
- }
70
-
71
- return $this->qtrans_active;
72
- }
73
-
74
- /**
75
- * See if there is a translated page available for the provided store ID.
76
- *
77
- * @since 2.0.0
78
- * @see https://wpml.org/documentation/support/creating-multilingual-wordpress-themes/language-dependent-ids/#2
79
- * @param string $store_id
80
- * @return string empty or the id of the translated store
81
- */
82
- public function maybe_get_wpml_id( $store_id ) {
83
-
84
- $return_original_id = apply_filters( 'wpsl_return_original_wpml_id', true );
85
-
86
- // icl_object_id is deprecated as of 3.2
87
- if ( defined( 'ICL_SITEPRESS_VERSION' ) && version_compare( ICL_SITEPRESS_VERSION, 3.2, '>=' ) ) {
88
- $translated_id = apply_filters( 'wpml_object_id', $store_id, 'wpsl_stores', $return_original_id, ICL_LANGUAGE_CODE );
89
- } else {
90
- $translated_id = icl_object_id( $store_id, 'wpsl_stores', $return_original_id, ICL_LANGUAGE_CODE );
91
- }
92
-
93
- // If '$return_original_id' is set to false, NULL is returned if no translation exists.
94
- if ( is_null( $translated_id ) ) {
95
- $translated_id = '';
96
- }
97
-
98
- return $translated_id;
99
- }
100
-
101
- /**
102
- * Get the correct translation.
103
- *
104
- * Return the translated text from WPML or the translation
105
- * that was set on the settings page.
106
- *
107
- * @since 2.0.0
108
- * @param string $name The name of the translated string
109
- * @param string $text The text of the translated string
110
- * @return string The translation
111
- */
112
- public function get_translation( $name, $text ) {
113
-
114
- global $wpsl_settings;
115
-
116
- if ( defined( 'WPML_ST_VERSION' ) ) {
117
- $translation = $text;
118
- } elseif ( defined( 'POLYLANG_VERSION' ) && defined( 'PLL_INC' ) ) {
119
-
120
- if ( !function_exists( 'pll__' ) ) {
121
- require_once PLL_INC . '/api.php';
122
- }
123
-
124
- $translation = pll__( $text );
125
- } else {
126
- $translation = stripslashes( $wpsl_settings[$name] );
127
- }
128
-
129
- return $translation;
130
- }
131
-
132
- /**
133
- * If a multilingual plugin like WPML or qTranslate X is active
134
- * we return the active language code.
135
- *
136
- * @since 2.0.0
137
- * @return string Empty or the current language code
138
- */
139
- public function check_multilingual_code() {
140
-
141
- $language_code = '';
142
-
143
- if ( $this->wpml_exists() && defined( 'ICL_LANGUAGE_CODE' ) ) {
144
- $language_code = ICL_LANGUAGE_CODE;
145
- } else if ( $this->qtrans_exists() ) {
146
-
147
- if ( function_exists( 'qtranxf_getLanguage' ) ) {
148
- $language_code = qtranxf_getLanguage();
149
- } else if ( function_exists( 'qtrans_getLanguage' ) ) {
150
- $language_code = qtrans_getLanguage();
151
- }
152
- }
153
-
154
- return $language_code;
155
- }
156
-
157
- }
158
-
159
- new WPSL_i18n();
160
  }
1
+ <?php
2
+ /**
3
+ * i18n class
4
+ *
5
+ * @author Tijmen Smit
6
+ * @since 2.0.0
7
+ */
8
+
9
+ if ( !defined( 'ABSPATH' ) ) exit;
10
+
11
+ if ( !class_exists( 'WPSL_i18n' ) ) {
12
+
13
+ class WPSL_i18n {
14
+
15
+ private $wpml_active = null;
16
+
17
+ private $qtrans_active = null;
18
+
19
+ /**
20
+ * Class constructor
21
+ */
22
+ function __construct() {
23
+ add_action( 'plugins_loaded', array( $this, 'load_plugin_textdomain' ) );
24
+ }
25
+
26
+ /**
27
+ * Load the translations from the language folder
28
+ *
29
+ * @since 2.0.0
30
+ * @return void
31
+ */
32
+ public function load_plugin_textdomain() {
33
+
34
+ $domain = 'wpsl';
35
+ $locale = apply_filters( 'plugin_locale', get_locale(), $domain );
36
+
37
+ // Load the language file from the /wp-content/languages/wp-store-locator folder, custom + update proof translations.
38
+ load_textdomain( $domain, WP_LANG_DIR . '/wp-store-locator/' . $domain . '-' . $locale . '.mo' );
39
+
40
+ // Load the language file from the /wp-content/plugins/wp-store-locator/languages/ folder.
41
+ load_plugin_textdomain( $domain, false, dirname( WPSL_BASENAME ) . '/languages/' );
42
+ }
43
+
44
+ /**
45
+ * Check if WPML is active
46
+ *
47
+ * @since 2.0.0
48
+ * @return boolean|null
49
+ */
50
+ public function wpml_exists() {
51
+
52
+ if ( $this->wpml_active == null ) {
53
+ $this->wpml_active = function_exists( 'icl_register_string' );
54
+ }
55
+
56
+ return $this->wpml_active;
57
+ }
58
+
59
+ /**
60
+ * Check if a qTranslate compatible plugin is active.
61
+ *
62
+ * @since 2.0.0
63
+ * @return boolean|null
64
+ */
65
+ public function qtrans_exists() {
66
+
67
+ if ( $this->qtrans_active == null ) {
68
+ $this->qtrans_active = ( function_exists( 'qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage' ) || function_exists( 'qtranxf_useCurrentLanguageIfNotFoundUseDefaultLanguage' ) );
69
+ }
70
+
71
+ return $this->qtrans_active;
72
+ }
73
+
74
+ /**
75
+ * See if there is a translated page available for the provided store ID.
76
+ *
77
+ * @since 2.0.0
78
+ * @see https://wpml.org/documentation/support/creating-multilingual-wordpress-themes/language-dependent-ids/#2
79
+ * @param string $store_id
80
+ * @return string empty or the id of the translated store
81
+ */
82
+ public function maybe_get_wpml_id( $store_id ) {
83
+
84
+ $return_original_id = apply_filters( 'wpsl_return_original_wpml_id', true );
85
+
86
+ // icl_object_id is deprecated as of 3.2
87
+ if ( defined( 'ICL_SITEPRESS_VERSION' ) && version_compare( ICL_SITEPRESS_VERSION, 3.2, '>=' ) ) {
88
+ $translated_id = apply_filters( 'wpml_object_id', $store_id, 'wpsl_stores', $return_original_id, ICL_LANGUAGE_CODE );
89
+ } else {
90
+ $translated_id = icl_object_id( $store_id, 'wpsl_stores', $return_original_id, ICL_LANGUAGE_CODE );
91
+ }
92
+
93
+ // If '$return_original_id' is set to false, NULL is returned if no translation exists.
94
+ if ( is_null( $translated_id ) ) {
95
+ $translated_id = '';
96
+ }
97
+
98
+ return $translated_id;
99
+ }
100
+
101
+ /**
102
+ * Get the correct translation.
103
+ *
104
+ * Return the translated text from WPML or the translation
105
+ * that was set on the settings page.
106
+ *
107
+ * @since 2.0.0
108
+ * @param string $name The name of the translated string
109
+ * @param string $text The text of the translated string
110
+ * @return string The translation
111
+ */
112
+ public function get_translation( $name, $text ) {
113
+
114
+ global $wpsl_settings;
115
+
116
+ if ( defined( 'WPML_ST_VERSION' ) ) {
117
+ $translation = $text;
118
+ } elseif ( defined( 'POLYLANG_VERSION' ) && defined( 'PLL_INC' ) ) {
119
+
120
+ if ( !function_exists( 'pll__' ) ) {
121
+ require_once PLL_INC . '/api.php';
122
+ }
123
+
124
+ $translation = pll__( $text );
125
+ } else {
126
+ $translation = stripslashes( $wpsl_settings[$name] );
127
+ }
128
+
129
+ return $translation;
130
+ }
131
+
132
+ /**
133
+ * If a multilingual plugin like WPML or qTranslate X is active
134
+ * we return the active language code.
135
+ *
136
+ * @since 2.0.0
137
+ * @return string Empty or the current language code
138
+ */
139
+ public function check_multilingual_code() {
140
+
141
+ $language_code = '';
142
+
143
+ if ( $this->wpml_exists() && defined( 'ICL_LANGUAGE_CODE' ) ) {
144
+ $language_code = ICL_LANGUAGE_CODE;
145
+ } else if ( $this->qtrans_exists() ) {
146
+
147
+ if ( function_exists( 'qtranxf_getLanguage' ) ) {
148
+ $language_code = qtranxf_getLanguage();
149
+ } else if ( function_exists( 'qtrans_getLanguage' ) ) {
150
+ $language_code = qtrans_getLanguage();
151
+ }
152
+ }
153
+
154
+ return $language_code;
155
+ }
156
+
157
+ }
158
+
159
+ new WPSL_i18n();
160
  }
inc/class-post-types.php CHANGED
@@ -34,7 +34,7 @@ if ( !class_exists( 'WPSL_Post_Types' ) ) {
34
  */
35
  public function register_post_types() {
36
 
37
- global $wpsl_settings;
38
 
39
  // Enable permalinks for the post type?
40
  if ( isset( $wpsl_settings['permalinks'] ) && $wpsl_settings['permalinks'] ) {
@@ -47,6 +47,9 @@ if ( !class_exists( 'WPSL_Post_Types' ) ) {
47
  $rewrite = false;
48
  }
49
 
 
 
 
50
  // The labels for the wpsl_stores post type.
51
  $labels = apply_filters( 'wpsl_post_type_labels', array(
52
  'name' => __( 'Store Locator', 'wpsl' ),
@@ -74,7 +77,8 @@ if ( !class_exists( 'WPSL_Post_Types' ) ) {
74
  'map_meta_cap' => true,
75
  'rewrite' => $rewrite,
76
  'query_var' => 'wpsl_stores',
77
- 'supports' => array( 'title', 'editor', 'author', 'excerpt', 'revisions', 'thumbnail' )
 
78
  )
79
  );
80
 
34
  */
35
  public function register_post_types() {
36
 
37
+ global $wpsl_settings, $wp_version;
38
 
39
  // Enable permalinks for the post type?
40
  if ( isset( $wpsl_settings['permalinks'] ) && $wpsl_settings['permalinks'] ) {
47
  $rewrite = false;
48
  }
49
 
50
+ // For WP 5.x set it to true to enable the Gutenberg editor.
51
+ $show_in_rest = ( floatval( $wp_version ) >= 5 ) ? true : false;
52
+
53
  // The labels for the wpsl_stores post type.
54
  $labels = apply_filters( 'wpsl_post_type_labels', array(
55
  'name' => __( 'Store Locator', 'wpsl' ),
77
  'map_meta_cap' => true,
78
  'rewrite' => $rewrite,
79
  'query_var' => 'wpsl_stores',
80
+ 'supports' => array( 'title', 'editor', 'author', 'excerpt', 'revisions', 'thumbnail' ),
81
+ 'show_in_rest' => $show_in_rest
82
  )
83
  );
84
 
inc/class-templates.php CHANGED
@@ -1,153 +1,153 @@
1
- <?php
2
- /**
3
- * Handle the WPSL and Add-on templates
4
- *
5
- * @author Tijmen Smit
6
- * @since 2.2.11
7
- */
8
-
9
- if ( !defined( 'ABSPATH' ) ) exit;
10
-
11
- if ( !class_exists( 'WPSL_Templates' ) ) {
12
-
13
- class WPSL_Templates {
14
-
15
- /**
16
- * Get the list of available templates
17
- *
18
- * @since 2.2.11
19
- * @param string $type The template type to return
20
- * @return array|void
21
- */
22
- public function get_template_list( $type = 'store_locator' ) {
23
-
24
- $template_list = array();
25
-
26
- // Add the WPSL templates or the add-on templates.
27
- if ( $type == 'store_locator' ) {
28
- $template_list['store_locator'] = wpsl_get_templates();
29
- } else {
30
- $template_list = apply_filters( 'wpsl_template_list', $template_list );
31
- }
32
-
33
- if ( isset( $template_list[$type] ) && !empty( $template_list[$type] ) ) {
34
- return $template_list[$type];
35
- }
36
- }
37
-
38
- /**
39
- * Get the template details
40
- *
41
- * @since 2.2.11
42
- * @param string $used_template The name of the template
43
- * @param string $type The type of template data to load
44
- * @return array $template_data The template data ( id, name, path )
45
- */
46
- public function get_template_details( $used_template, $type = 'store_locator' ) {
47
-
48
- $used_template = ( empty( $used_template ) ) ? 'default' : $used_template;
49
- $templates = $this->get_template_list( $type );
50
- $template_data = '';
51
- $template_path = '';
52
-
53
- if ( $templates ) {
54
- // Grab the the correct template data from the available templates.
55
- foreach ( $templates as $template ) {
56
- if ( $used_template == $template['id'] ) {
57
- $template_data = $template;
58
- break;
59
- }
60
- }
61
- }
62
-
63
- // Old structure ( WPSL only ) was only the path, new structure ( add-ons ) expects the file name as well.
64
- if ( isset( $template_data['path'] ) && isset( $template_data['file_name'] ) ) {
65
- $template_path = $template_data['path'] . $template_data['file_name'];
66
- } else if ( isset( $template_data['path'] ) ) {
67
- $template_path = $template_data['path'];
68
- }
69
-
70
- // If no match exists, or the template file doesnt exist, then use the default template.
71
- if ( !$template_data || ( !file_exists( $template_path ) ) ) {
72
- $template_data = $this->get_default_template( $type );
73
-
74
- // If no template can be loaded, then show a msg to the admin user.
75
- if ( !$template_data && current_user_can( 'administrator' ) ) {
76
- echo '<p>' . sprintf( __( 'No template found for %s', 'wpsl' ), $type ) . '</p>';
77
- echo '<p>' . sprintf( __( 'Make sure you call the %sget_template_details%s function with the correct parameters.', 'wpsl' ), '<code>', '</code>' ) . '</p>';
78
- }
79
- }
80
-
81
- return $template_data;
82
- }
83
-
84
- /**
85
- * Locate the default template
86
- *
87
- * @since 2.2.11
88
- * @param string $type The type of default template to return
89
- * @return array $default The default template data
90
- */
91
- public function get_default_template( $type = 'store_locator' ) {
92
-
93
- $template_list = $this->get_template_list( $type );
94
- $default = '';
95
-
96
- if ( $template_list ) {
97
- foreach ( $template_list as $template ) {
98
- if ( $template['id'] == 'default' ) {
99
- $default = $template;
100
- break;
101
- }
102
- }
103
- }
104
-
105
- return $default;
106
- }
107
-
108
- /**
109
- * Include the template file.
110
- *
111
- * @since 2.2.11
112
- * @param array $args The template path details
113
- * @param array $template_data The template data ( address, phone, fax etc ).
114
- * @return string The location template.
115
- */
116
- function get_template( $args, $template_data ) {
117
-
118
- // Don't continue if not path and file name is set.
119
- if ( !isset( $args['path'] ) || !isset( $args['file_name'] ) ) {
120
- return;
121
- }
122
-
123
- ob_start();
124
-
125
- include( $this->find_template_path( $args ) );
126
-
127
- return ob_get_clean();
128
- }
129
-
130
- /**
131
- * Locate the template file in either the
132
- * theme folder or the plugin folder itself.
133
- *
134
- * @since 2.2.11
135
- * @param array $args The template data
136
- * @return string $template The path to the template.
137
- */
138
- function find_template_path( $args ) {
139
-
140
- // Look for the template in the theme folder.
141
- $template = locate_template(
142
- array( trailingslashit( 'wpsl-templates' ) . $args['file_name'], $args['file_name'] )
143
- );
144
-
145
- // If the template doesn't exist in the theme folder load the one from the plugin dir.
146
- if ( !$template ) {
147
- $template = $args['path'] . $args['file_name'];
148
- }
149
-
150
- return $template;
151
- }
152
- }
153
  }
1
+ <?php
2
+ /**
3
+ * Handle the WPSL and Add-on templates
4
+ *
5
+ * @author Tijmen Smit
6
+ * @since 2.2.11
7
+ */
8
+
9
+ if ( !defined( 'ABSPATH' ) ) exit;
10
+
11
+ if ( !class_exists( 'WPSL_Templates' ) ) {
12
+
13
+ class WPSL_Templates {
14
+
15
+ /**
16
+ * Get the list of available templates
17
+ *
18
+ * @since 2.2.11
19
+ * @param string $type The template type to return
20
+ * @return array|void
21
+ */
22
+ public function get_template_list( $type = 'store_locator' ) {
23
+
24
+ $template_list = array();
25
+
26
+ // Add the WPSL templates or the add-on templates.
27
+ if ( $type == 'store_locator' ) {
28
+ $template_list['store_locator'] = wpsl_get_templates();
29
+ } else {
30
+ $template_list = apply_filters( 'wpsl_template_list', $template_list );
31
+ }
32
+
33
+ if ( isset( $template_list[$type] ) && !empty( $template_list[$type] ) ) {
34
+ return $template_list[$type];
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Get the template details
40
+ *
41
+ * @since 2.2.11
42
+ * @param string $used_template The name of the template
43
+ * @param string $type The type of template data to load
44
+ * @return array $template_data The template data ( id, name, path )
45
+ */
46
+ public function get_template_details( $used_template, $type = 'store_locator' ) {
47
+
48
+ $used_template = ( empty( $used_template ) ) ? 'default' : $used_template;
49
+ $templates = $this->get_template_list( $type );
50
+ $template_data = '';
51
+ $template_path = '';
52
+
53
+ if ( $templates ) {
54
+ // Grab the the correct template data from the available templates.
55
+ foreach ( $templates as $template ) {
56
+ if ( $used_template == $template['id'] ) {
57
+ $template_data = $template;
58
+ break;
59
+ }
60
+ }
61
+ }
62
+
63
+ // Old structure ( WPSL only ) was only the path, new structure ( add-ons ) expects the file name as well.
64
+ if ( isset( $template_data['path'] ) && isset( $template_data['file_name'] ) ) {
65
+ $template_path = $template_data['path'] . $template_data['file_name'];
66
+ } else if ( isset( $template_data['path'] ) ) {
67
+ $template_path = $template_data['path'];
68
+ }
69
+
70
+ // If no match exists, or the template file doesnt exist, then use the default template.
71
+ if ( !$template_data || ( !file_exists( $template_path ) ) ) {
72
+ $template_data = $this->get_default_template( $type );
73
+
74
+ // If no template can be loaded, then show a msg to the admin user.
75
+ if ( !$template_data && current_user_can( 'administrator' ) ) {
76
+ echo '<p>' . sprintf( __( 'No template found for %s', 'wpsl' ), $type ) . '</p>';
77
+ echo '<p>' . sprintf( __( 'Make sure you call the %sget_template_details%s function with the correct parameters.', 'wpsl' ), '<code>', '</code>' ) . '</p>';
78
+ }
79
+ }
80
+
81
+ return $template_data;
82
+ }
83
+
84
+ /**
85
+ * Locate the default template
86
+ *
87
+ * @since 2.2.11
88
+ * @param string $type The type of default template to return
89
+ * @return array $default The default template data
90
+ */
91
+ public function get_default_template( $type = 'store_locator' ) {
92
+
93
+ $template_list = $this->get_template_list( $type );
94
+ $default = '';
95
+
96
+ if ( $template_list ) {
97
+ foreach ( $template_list as $template ) {
98
+ if ( $template['id'] == 'default' ) {
99
+ $default = $template;
100
+ break;
101
+ }
102
+ }
103
+ }
104
+
105
+ return $default;
106
+ }
107
+
108
+ /**
109
+ * Include the template file.
110
+ *
111
+ * @since 2.2.11
112
+ * @param array $args The template path details
113
+ * @param array $template_data The template data ( address, phone, fax etc ).
114
+ * @return string The location template.
115
+ */
116
+ function get_template( $args, $template_data ) {
117
+
118
+ // Don't continue if not path and file name is set.
119
+ if ( !isset( $args['path'] ) || !isset( $args['file_name'] ) ) {
120
+ return;
121
+ }
122
+
123
+ ob_start();
124
+
125
+ include( $this->find_template_path( $args ) );
126
+
127
+ return ob_get_clean();
128
+ }
129
+
130
+ /**
131
+ * Locate the template file in either the
132
+ * theme folder or the plugin folder itself.
133
+ *
134
+ * @since 2.2.11
135
+ * @param array $args The template data
136
+ * @return string $template The path to the template.
137
+ */
138
+ function find_template_path( $args ) {
139
+
140
+ // Look for the template in the theme folder.
141
+ $template = locate_template(
142
+ array( trailingslashit( 'wpsl-templates' ) . $args['file_name'], $args['file_name'] )
143
+ );
144
+
145
+ // If the template doesn't exist in the theme folder load the one from the plugin dir.
146
+ if ( !$template ) {
147
+ $template = $args['path'] . $args['file_name'];
148
+ }
149
+
150
+ return $template;
151
+ }
152
+ }
153
  }
inc/install.php CHANGED
@@ -1,65 +1,65 @@
1
- <?php
2
- /**
3
- * WPSL Install
4
- *
5
- * @author Tijmen Smit
6
- * @since 2.0.0
7
- */
8
-
9
- if ( !defined( 'ABSPATH' ) ) exit;
10
-
11
- /**
12
- * Run the install.
13
- *
14
- * @since 1.2.20
15
- * @return void
16
- */
17
- function wpsl_install( $network_wide ) {
18
-
19
- global $wpdb;
20
-
21
- if ( function_exists( 'is_multisite' ) && is_multisite() ) {
22
-
23
- if ( $network_wide ) {
24
- $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
25
-
26
- foreach ( $blog_ids as $blog_id ) {
27
- switch_to_blog( $blog_id );
28
- wpsl_install_data();
29
- }
30
-
31
- restore_current_blog();
32
- } else {
33
- wpsl_install_data();
34
- }
35
- } else {
36
- wpsl_install_data();
37
- }
38
- }
39
-
40
- /**
41
- * Install the required data.
42
- *
43
- * @since 1.2.20
44
- * @return void
45
- */
46
- function wpsl_install_data() {
47
-
48
- global $wpsl;
49
-
50
- // Register the post type and flush the permalinks.
51
- $wpsl->post_types->register_post_types();
52
- flush_rewrite_rules();
53
-
54
- // Create the default settings.
55
- wpsl_set_default_settings();
56
-
57
- // Set the correct version.
58
- update_option( 'wpsl_version', WPSL_VERSION_NUM );
59
-
60
- // Add user roles.
61
- wpsl_add_roles();
62
-
63
- // Add user capabilities.
64
- wpsl_add_caps();
65
  }
1
+ <?php
2
+ /**
3
+ * WPSL Install
4
+ *
5
+ * @author Tijmen Smit
6
+ * @since 2.0.0
7
+ */
8
+
9
+ if ( !defined( 'ABSPATH' ) ) exit;
10
+
11
+ /**
12
+ * Run the install.
13
+ *
14
+ * @since 1.2.20
15
+ * @return void
16
+ */
17
+ function wpsl_install( $network_wide ) {
18
+
19
+ global $wpdb;
20
+
21
+ if ( function_exists( 'is_multisite' ) && is_multisite() ) {
22
+
23
+ if ( $network_wide ) {
24
+ $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
25
+
26
+ foreach ( $blog_ids as $blog_id ) {
27
+ switch_to_blog( $blog_id );
28
+ wpsl_install_data();
29
+ }
30
+
31
+ restore_current_blog();
32
+ } else {
33
+ wpsl_install_data();
34
+ }
35
+ } else {
36
+ wpsl_install_data();
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Install the required data.
42
+ *
43
+ * @since 1.2.20
44
+ * @return void
45
+ */
46
+ function wpsl_install_data() {
47
+
48
+ global $wpsl;
49
+
50
+ // Register the post type and flush the permalinks.
51
+ $wpsl->post_types->register_post_types();
52
+ flush_rewrite_rules();
53
+
54
+ // Create the default settings.
55
+ wpsl_set_default_settings();
56
+
57
+ // Set the correct version.
58
+ update_option( 'wpsl_version', WPSL_VERSION_NUM );
59
+
60
+ // Add user roles.
61
+ wpsl_add_roles();
62
+
63
+ // Add user capabilities.
64
+ wpsl_add_caps();
65
  }
inc/wpsl-functions.php CHANGED
@@ -1,661 +1,673 @@
1
- <?php
2
-
3
- /**
4
- * Collect all the parameters ( language, key, region )
5
- * we need before making a request to the Google Maps API.
6
- *
7
- * @since 1.0.0
8
- * @param string $api_key_type The type of API key we need to include ( server_key or browser_key ).
9
- * @param boolean $geocode_params
10
- * @return string $api_params The API parameters.
11
- */
12
- function wpsl_get_gmap_api_params( $api_key_type, $geocode_params = false ) {
13
-
14
- global $wpsl, $wpsl_settings;
15
-
16
- $api_params = '';
17
- $param_keys = array( 'language', 'region', 'key' );
18
-
19
- /*
20
- * The geocode params are included after the address so we need to
21
- * use a '&' as the first char, but when the maps script is included on
22
- * the front-end it does need to start with a '?'.
23
- */
24
- $first_sep = ( $geocode_params ) ? '&' : '?';
25
-
26
- foreach ( $param_keys as $param_key ) {
27
- $option_key = ( $param_key == 'key' ) ? $api_key_type : $param_key;
28
-
29
- /*
30
- * Get the current language code if WPML or qTranslate-X is active.
31
- * Otherwise get the param value from the settings var.
32
- */
33
- if ( $option_key == 'language' && ( $wpsl->i18n->wpml_exists() || $wpsl->i18n->qtrans_exists() ) ) {
34
- $param_val = $wpsl->i18n->check_multilingual_code();
35
- } else {
36
- $param_val = $wpsl_settings['api_' . $option_key];
37
- }
38
-
39
- if ( !empty( $param_val ) ) {
40
- $api_params .= $param_key . '=' . $param_val . '&';
41
- }
42
- }
43
-
44
- if ( $api_params ) {
45
- $api_params = $first_sep . rtrim( $api_params, '&' );
46
- }
47
-
48
- // Do we need to include the autocomplete library?
49
- if ( ( $wpsl_settings['autocomplete'] && $api_key_type == 'browser_key' ) || is_admin() ) {
50
- $api_params .= '&libraries=places';
51
- }
52
-
53
- if ( $api_key_type == 'browser_key' ) {
54
- $api_version = apply_filters( 'wpsl_gmap_api_version', '3.33' );
55
- $api_params .= '&v=' . $api_version;
56
- }
57
-
58
- return apply_filters( 'wpsl_gmap_api_params', $api_params );
59
- }
60
-
61
- /**
62
- * Get the default plugin settings.
63
- *
64
- * @since 1.0.0
65
- * @return array $default_settings The default settings
66
- */
67
- function wpsl_get_default_settings() {
68
-
69
- $default_settings = array(
70
- 'api_browser_key' => '',
71
- 'api_server_key' => '',
72
- 'api_language' => 'en',
73
- 'api_region' => '',
74
- 'api_geocode_component' => 0,
75
- 'distance_unit' => 'km',
76
- 'max_results' => '[25],50,75,100',
77
- 'search_radius' => '10,25,[50],100,200,500',
78
- 'marker_effect' => 'bounce',
79
- 'address_format' => 'city_state_zip',
80
- 'hide_distance' => 0,
81
- 'hide_country' => 0,
82
- 'show_contact_details' => 0,
83
- 'clickable_contact_details' => 0,
84
- 'auto_locate' => 1,
85
- 'autocomplete' => 0,
86
- 'autoload' => 1,
87
- 'autoload_limit' => 50,
88
- 'run_fitbounds' => 1,
89
- 'zoom_level' => 3,
90
- 'auto_zoom_level' => 15,
91
- 'start_name' => '',
92
- 'start_latlng' => '',
93
- 'height' => 350,
94
- 'map_type' => 'roadmap',
95
- 'map_style' => '',
96
- 'type_control' => 0,
97
- 'streetview' => 0,
98
- 'results_dropdown' => 1,
99
- 'radius_dropdown' => 1,
100
- 'category_filter' => 0,
101
- 'category_filter_type' => 'dropdown',
102
- 'infowindow_width' => 225,
103
- 'search_width' => 179,
104
- 'label_width' => 95,
105
- 'control_position' => 'left',
106
- 'scrollwheel' => 1,
107
- 'marker_clusters' => 0,
108
- 'cluster_zoom' => 0,
109
- 'cluster_size' => 0,
110
- 'new_window' => 0,
111
- 'reset_map' => 0,
112
- 'template_id' => 'default',
113
- 'listing_below_no_scroll' => 0,
114
- 'direction_redirect' => 0,
115
- 'more_info' => 0,
116
- 'store_url' => 0,
117
- 'phone_url' => 0,
118
- 'marker_streetview' => 0,
119
- 'marker_zoom_to' => 0,
120
- 'more_info_location' => 'info window',
121
- 'mouse_focus' => 1,
122
- 'start_marker' => 'red.png',
123
- 'store_marker' => 'blue.png',
124
- 'editor_country' => '',
125
- 'editor_hours' => wpsl_default_opening_hours(),
126
- 'editor_hour_input' => 'dropdown',
127
- 'editor_hour_format' => 12,
128
- 'editor_map_type' => 'roadmap',
129
- 'hide_hours' => 0,
130
- 'permalinks' => 0,
131
- 'permalink_slug' => __( 'stores', 'wpsl' ),
132
- 'category_slug' => __( 'store-category', 'wpsl' ),
133
- 'infowindow_style' => 'default',
134
- 'show_credits' => 0,
135
- 'debug' => 0,
136
- 'deregister_gmaps' => 0,
137
- 'start_label' => __( 'Start location', 'wpsl' ),
138
- 'search_label' => __( 'Your location', 'wpsl' ),
139
- 'search_btn_label' => __( 'Search', 'wpsl' ),
140
- 'preloader_label' => __( 'Searching...', 'wpsl' ),
141
- 'radius_label' => __( 'Search radius', 'wpsl' ),
142
- 'no_results_label' => __( 'No results found', 'wpsl' ),
143
- 'results_label' => __( 'Results', 'wpsl' ),
144
- 'more_label' => __( 'More info', 'wpsl' ),
145
- 'directions_label' => __( 'Directions', 'wpsl' ),
146
- 'no_directions_label' => __( 'No route could be found between the origin and destination', 'wpsl' ),
147
- 'back_label' => __( 'Back', 'wpsl' ),
148
- 'street_view_label' => __( 'Street view', 'wpsl' ),
149
- 'zoom_here_label' => __( 'Zoom here', 'wpsl' ),
150
- 'error_label' => __( 'Something went wrong, please try again!', 'wpsl' ),
151
- 'limit_label' => __( 'API usage limit reached', 'wpsl' ),
152
- 'phone_label' => __( 'Phone', 'wpsl' ),
153
- 'fax_label' => __( 'Fax', 'wpsl' ),
154
- 'email_label' => __( 'Email', 'wpsl' ),
155
- 'url_label' => __( 'Url', 'wpsl' ),
156
- 'hours_label' => __( 'Hours', 'wpsl' ),
157
- 'category_label' => __( 'Category filter', 'wpsl' ),
158
- 'category_default_label' => __( 'Any', 'wpsl' )
159
- );
160
-
161
- return $default_settings;
162
- }
163
-
164
- /**
165
- * Get the current plugin settings.
166
- *
167
- * @since 1.0.0
168
- * @return array $setting The current plugin settings
169
- */
170
- function wpsl_get_settings() {
171
-
172
- $settings = get_option( 'wpsl_settings' );
173
-
174
- if ( !$settings ) {
175
- update_option( 'wpsl_settings', wpsl_get_default_settings() );
176
- $settings = wpsl_get_default_settings();
177
- }
178
-
179
- return $settings;
180
- }
181
-
182
- /**
183
- * Get a single value from the default settings.
184
- *
185
- * @since 1.0.0
186
- * @param string $setting The value that should be restored
187
- * @return string $wpsl_default_settings The default setting value
188
- */
189
- function wpsl_get_default_setting( $setting ) {
190
-
191
- global $wpsl_default_settings;
192
-
193
- return $wpsl_default_settings[$setting];
194
- }
195
-
196
- /**
197
- * Set the default plugin settings.
198
- *
199
- * @since 1.0.0
200
- * @return void
201
- */
202
- function wpsl_set_default_settings() {
203
-
204
- $settings = get_option( 'wpsl_settings' );
205
-
206
- if ( !$settings ) {
207
- update_option( 'wpsl_settings', wpsl_get_default_settings() );
208
- }
209
- }
210
-
211
- /**
212
- * Return a list of the store templates.
213
- *
214
- * @since 1.2.20
215
- * @return array $templates The list of default store templates
216
- */
217
- function wpsl_get_templates() {
218
-
219
- $templates = array(
220
- array(
221
- 'id' => 'default',
222
- 'name' => __( 'Default', 'wpsl' ),
223
- 'path' => WPSL_PLUGIN_DIR . 'frontend/templates/default.php'
224
- ),
225
- array(
226
- 'id' => 'below_map',
227
- 'name' => __( 'Show the store list below the map', 'wpsl' ),
228
- 'path' => WPSL_PLUGIN_DIR . 'frontend/templates/store-listings-below.php'
229
- )
230
- );
231
-
232
- return apply_filters( 'wpsl_templates', $templates );
233
- }
234
-
235
- /**
236
- * Return the days of the week.
237
- *
238
- * @since 2.0.0
239
- * @return array $weekdays The days of the week
240
- */
241
- function wpsl_get_weekdays() {
242
-
243
- $weekdays = array(
244
- 'monday' => __( 'Monday', 'wpsl' ),
245
- 'tuesday' => __( 'Tuesday', 'wpsl' ),
246
- 'wednesday' => __( 'Wednesday', 'wpsl' ),
247
- 'thursday' => __( 'Thursday', 'wpsl' ),
248
- 'friday' => __( 'Friday', 'wpsl' ),
249
- 'saturday' => __( 'Saturday', 'wpsl' ),
250
- 'sunday' => __( 'Sunday' , 'wpsl' )
251
- );
252
-
253
- return $weekdays;
254
- }
255
-
256
- /**
257
- * Get the default opening hours.
258
- *
259
- * @since 2.0.0
260
- * @return array $opening_hours The default opening hours
261
- */
262
- function wpsl_default_opening_hours() {
263
-
264
- $current_version = get_option( 'wpsl_version' );
265
-
266
- $opening_hours = array(
267
- 'dropdown' => array(
268
- 'monday' => array( '9:00 AM,5:00 PM' ),
269
- 'tuesday' => array( '9:00 AM,5:00 PM' ),
270
- 'wednesday' => array( '9:00 AM,5:00 PM' ),
271
- 'thursday' => array( '9:00 AM,5:00 PM' ),
272
- 'friday' => array( '9:00 AM,5:00 PM' ),
273
- 'saturday' => '',
274
- 'sunday' => ''
275
- )
276
- );
277
-
278
- /* Only add the textarea defaults for users that upgraded from 1.x */
279
- if ( version_compare( $current_version, '2.0', '<' ) ) {
280
- $opening_hours['textarea'] = sprintf( __( 'Mon %sTue %sWed %sThu %sFri %sSat Closed %sSun Closed', 'wpsl' ), '9:00 AM - 5:00 PM' . "\n", '9:00 AM - 5:00 PM' . "\n", '9:00 AM - 5:00 PM' . "\n", '9:00 AM - 5:00 PM' . "\n", '9:00 AM - 5:00 PM' . "\n", "\n" ); //cleaner way without repeating it 5 times??
281
- }
282
-
283
- return $opening_hours;
284
- }
285
-
286
- /**
287
- * Get the available map types.
288
- *
289
- * @since 2.0.0
290
- * @return array $map_types The available map types
291
- */
292
- function wpsl_get_map_types() {
293
-
294
- $map_types = array(
295
- 'roadmap' => __( 'Roadmap', 'wpsl' ),
296
- 'satellite' => __( 'Satellite', 'wpsl' ),
297
- 'hybrid' => __( 'Hybrid', 'wpsl' ),
298
- 'terrain' => __( 'Terrain', 'wpsl' )
299
- );
300
-
301
- return $map_types;
302
- }
303
-
304
- /**
305
- * Get the address formats.
306
- *
307
- * @since 2.0.0
308
- * @return array $address_formats The address formats
309
- */
310
- function wpsl_get_address_formats() {
311
-
312
- $address_formats = array(
313
- 'city_state_zip' => __( '(city) (state) (zip code)', 'wpsl' ),
314
- 'city_comma_state_zip' => __( '(city), (state) (zip code)', 'wpsl' ),
315
- 'city_zip' => __( '(city) (zip code)', 'wpsl' ),
316
- 'city_comma_zip' => __( '(city), (zip code)', 'wpsl' ),
317
- 'zip_city_state' => __( '(zip code) (city) (state)', 'wpsl' ),
318
- 'zip_city' => __( '(zip code) (city)', 'wpsl' )
319
- );
320
-
321
- return apply_filters( 'wpsl_address_formats', $address_formats );
322
- }
323
-
324
- /**
325
- * Make sure the provided map type is valid.
326
- *
327
- * If the map type is invalid the default is used ( roadmap ).
328
- *
329
- * @since 2.0.0
330
- * @param string $map_type The provided map type
331
- * @return string $map_type A valid map type
332
- */
333
- function wpsl_valid_map_type( $map_type ) {
334
-
335
- $allowed_map_types = wpsl_get_map_types();
336
-
337
- if ( !array_key_exists( $map_type, $allowed_map_types ) ) {
338
- $map_type = wpsl_get_default_setting( 'map_type' );
339
- }
340
-
341
- return $map_type;
342
- }
343
-
344
- /**
345
- * Make sure the provided zoom level is valid.
346
- *
347
- * If the zoom level is invalid the default is used ( 3 ).
348
- *
349
- * @since 2.0.0
350
- * @param string $zoom_level The provided zoom level
351
- * @return string $zoom_level A valid zoom level
352
- */
353
- function wpsl_valid_zoom_level( $zoom_level ) {
354
-
355
- $zoom_level = absint( $zoom_level );
356
-
357
- if ( ( $zoom_level < 1 ) || ( $zoom_level > 21 ) ) {
358
- $zoom_level = wpsl_get_default_setting( 'zoom_level' );
359
- }
360
-
361
- return $zoom_level;
362
- }
363
-
364
- /**
365
- * Get the max auto zoom levels for the map.
366
- *
367
- * @since 2.0.0
368
- * @return array $max_zoom_levels The array holding the min - max zoom levels
369
- */
370
- function wpsl_get_max_zoom_levels() {
371
-
372
- $max_zoom_levels = array();
373
- $zoom_level = array(
374
- 'min' => 10,
375
- 'max' => 21
376
- );
377
-
378
- $i = $zoom_level['min'];
379
-
380
- while ( $i <= $zoom_level['max'] ) {
381
- $max_zoom_levels[$i] = $i;
382
- $i++;
383
- }
384
-
385
- return $max_zoom_levels;
386
- }
387
-
388
- /**
389
- * The labels and the values that can be set through the settings page.
390
- *
391
- * @since 2.0.0
392
- * @return array $labels The label names from the settings page.
393
- */
394
- function wpsl_labels() {
395
-
396
- $labels = array(
397
- 'search',
398
- 'search_btn',
399
- 'preloader',
400
- 'radius',
401
- 'no_results',
402
- 'results',
403
- 'more',
404
- 'directions',
405
- 'no_directions',
406
- 'back',
407
- 'street_view',
408
- 'zoom_here',
409
- 'error',
410
- 'phone',
411
- 'fax',
412
- 'email',
413
- 'url',
414
- 'hours',
415
- 'start',
416
- 'limit',
417
- 'category',
418
- 'category_default'
419
- );
420
-
421
- return $labels;
422
- }
423
-
424
- /**
425
- * Callback for array_walk_recursive, sanitize items in a multidimensional array.
426
- *
427
- * @since 2.0.0
428
- * @param string $item The value
429
- * @param integer $key The key
430
- */
431
- function wpsl_sanitize_multi_array( &$item, $key ) {
432
- $item = sanitize_text_field( $item );
433
- }
434
-
435
- /**
436
- * Check whether the array is multidimensional.
437
- *
438
- * @since 2.0.0
439
- * @param array $array The array to check
440
- * @return boolean
441
- */
442
- function wpsl_is_multi_array( $array ) {
443
-
444
- foreach ( $array as $value ) {
445
- if ( is_array( $value ) ) return true;
446
- }
447
-
448
- return false;
449
- }
450
-
451
- /**
452
- * @since 2.1.1
453
- * @param string $address The address to geocode.
454
- * @return array $response Either a WP_Error or the response from the Geocode API.
455
- */
456
- function wpsl_call_geocode_api( $address ) {
457
-
458
- $url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' . urlencode( $address ) . wpsl_get_gmap_api_params( 'server_key', true );
459
- $response = wp_remote_get( $url );
460
-
461
- return $response;
462
- }
463
-
464
- /**
465
- * Get the latlng for the provided address.
466
- *
467
- * This is used to geocode the address set as the start point on
468
- * the settings page in case the autocomplete fails
469
- * ( only happens when there is a JS error on the page ),
470
- * or to get the latlng when the 'start_location' attr is set
471
- * on the wpsl shortcode.
472
- *
473
- * @since 2.2
474
- * @param string $address The address to geocode.
475
- * @return array|void $latlng The returned latlng or nothing if there was an error.
476
- */
477
- function wpsl_get_address_latlng( $address ) {
478
-
479
- $latlng = '';
480
- $response = wpsl_call_geocode_api( $address );
481
-
482
- if ( !is_wp_error( $response ) ) {
483
- $response = json_decode( $response['body'], true );
484
-
485
- if ( $response['status'] == 'OK' ) {
486
- $latlng = $response['results'][0]['geometry']['location']['lat'] . ',' . $response['results'][0]['geometry']['location']['lng'];
487
- }
488
- }
489
-
490
- return $latlng;
491
- }
492
-
493
- /**
494
- * Check if there's a transient that holds
495
- * the coordinates for the passed address.
496
- *
497
- * If not, then we geocode the address and
498
- * set the returned value in the transient.
499
- *
500
- * @since 2.2.11
501
- * @param string $address The location to geocode
502
- * @return string $latlng The coordinates of the geocoded location
503
- */
504
- function wpsl_check_latlng_transient( $address ) {
505
-
506
- $name_section = explode( ',', $address );
507
- $transient_name = 'wpsl_' . trim( strtolower( $name_section[0] ) ) . '_latlng';
508
-
509
- if ( false === ( $latlng = get_transient( $transient_name ) ) ) {
510
- $latlng = wpsl_get_address_latlng( $address );
511
-
512
- if ( $latlng ) {
513
- set_transient( $transient_name, $latlng, 0 );
514
- }
515
- }
516
-
517
- return $latlng;
518
- }
519
-
520
- /**
521
- * Make sure the shortcode attributes are booleans
522
- * when they are expected to be.
523
- *
524
- * @since 2.0.4
525
- * @param array $atts Shortcode attributes
526
- * @return array $atts Shortcode attributes
527
- */
528
- function wpsl_bool_check( $atts ) {
529
-
530
- foreach ( $atts as $key => $val ) {
531
- if ( in_array( $val, array( 'true', '1', 'yes', 'on' ) ) ) {
532
- $atts[$key] = true;
533
- } else if ( in_array( $val, array( 'false', '0', 'no', 'off' ) ) ) {
534
- $atts[$key] = false;
535
- }
536
- }
537
-
538
- return $atts;
539
- }
540
-
541
- /**
542
- * Create a string with random characters.
543
- *
544
- * @since 2.2.4
545
- * @param int $length Used length
546
- * @return string $random_chars Random characters
547
- */
548
- function wpsl_random_chars( $length = 5 ) {
549
-
550
- $random_chars = substr( str_shuffle( "abcdefghijklmnopqrstuvwxyz" ), 0, $length );
551
-
552
- return $random_chars;
553
- }
554
-
555
- /**
556
- * Deregister other Google Maps scripts.
557
- *
558
- * If plugins / themes also include the Google Maps library, then it can cause
559
- * problems with the autocomplete function on the settings page and break
560
- * the store locator on the front-end.
561
- *
562
- * @since 2.2.4
563
- * @return void
564
- */
565
- function wpsl_deregister_other_gmaps() {
566
-
567
- global $wp_scripts;
568
-
569
- foreach ( $wp_scripts->registered as $index => $script ) {
570
- if ( ( strpos( $script->src, 'maps.google.com' ) !== false ) || ( strpos( $script->src, 'maps.googleapis.com' ) !== false ) && ( $script->handle !== 'wpsl-gmap' ) ) {
571
- wp_deregister_script( $script->handle );
572
- }
573
- }
574
- }
575
-
576
- /**
577
- * Return the used distance unit.
578
- *
579
- * @since 2.2.8
580
- * @return string Either km or mi
581
- */
582
- function wpsl_get_distance_unit() {
583
-
584
- global $wpsl_settings;
585
-
586
- return apply_filters( 'wpsl_distance_unit', $wpsl_settings['distance_unit'] );
587
- }
588
-
589
- /**
590
- * Find the term ids for the provided term slugs.
591
- *
592
- * @since 2.2.10
593
- * @param array $cat_list List of term slugs
594
- * @return array $term_ids The term ids
595
- */
596
- function wpsl_get_term_ids( $cat_list ) {
597
-
598
- $term_ids = array();
599
- $cats = explode( ',', $cat_list );
600
-
601
- foreach ( $cats as $key => $term_slug ) {
602
- $term_data = get_term_by( 'slug', $term_slug, 'wpsl_store_category' );
603
-
604
- if ( isset( $term_data->term_id ) && $term_data->term_id ) {
605
- $term_ids[] = $term_data->term_id;
606
- }
607
- }
608
-
609
- return $term_ids;
610
- }
611
-
612
- /**
613
- * Get the url to the admin-ajax.php
614
- *
615
- * @since 2.2.3
616
- * @return string $ajax_url URL to the admin-ajax.php possibly with the WPML lang param included.
617
- */
618
- function wpsl_get_ajax_url() {
619
-
620
- $i18n = new WPSL_i18n();
621
-
622
- $param = '';
623
-
624
- if ( $i18n->wpml_exists() && defined( 'ICL_LANGUAGE_CODE' ) ) {
625
- $param = '?lang=' . ICL_LANGUAGE_CODE;
626
- }
627
-
628
- $ajax_url = admin_url( 'admin-ajax.php' . $param );
629
-
630
- return $ajax_url;
631
- }
632
-
633
- /**
634
- * Get the used location fields
635
- *
636
- * @since 2.2.14
637
- * @return array $fields
638
- */
639
- function wpsl_get_location_fields() {
640
-
641
- global $wpsl_admin;
642
-
643
- $fields = array();
644
-
645
- $meta_fields = $wpsl_admin->metaboxes->meta_box_fields();
646
-
647
- $fields['id'] = 'id';
648
- $fields['distance'] = 'distance';
649
-
650
- foreach ( $meta_fields as $k => $field_section ) {
651
- foreach ( $field_section as $field_name => $field_value ) {
652
- if ( in_array( $field_name, array( 'lat', 'lng', 'country_iso' ) ) ) {
653
- continue;
654
- }
655
-
656
- $fields[$field_name] = $field_name;
657
- }
658
- }
659
-
660
- return $fields;
 
 
 
 
 
 
 
 
 
 
 
 
661
  }
1
+ <?php
2
+
3
+ /**
4
+ * Collect all the parameters ( language, key, region )
5
+ * we need before making a request to the Google Maps API.
6
+ *
7
+ * @since 1.0.0
8
+ * @param string $api_key_type The type of API key we need to include ( server_key or browser_key ).
9
+ * @param boolean $geocode_params
10
+ * @return string $api_params The API parameters.
11
+ */
12
+ function wpsl_get_gmap_api_params( $api_key_type, $geocode_params = false ) {
13
+
14
+ global $wpsl, $wpsl_settings;
15
+
16
+ $api_params = '';
17
+ $param_keys = array( 'language', 'region', 'key' );
18
+
19
+ /*
20
+ * The geocode params are included after the address so we need to
21
+ * use a '&' as the first char, but when the maps script is included on
22
+ * the front-end it does need to start with a '?'.
23
+ */
24
+ $first_sep = ( $geocode_params ) ? '&' : '?';
25
+
26
+ foreach ( $param_keys as $param_key ) {
27
+ $option_key = ( $param_key == 'key' ) ? $api_key_type : $param_key;
28
+
29
+ /*
30
+ * Get the current language code if WPML or qTranslate-X is active.
31
+ * Otherwise get the param value from the settings var.
32
+ */
33
+ if ( $option_key == 'language' && ( $wpsl->i18n->wpml_exists() || $wpsl->i18n->qtrans_exists() ) ) {
34
+ $param_val = $wpsl->i18n->check_multilingual_code();
35
+ } else {
36
+ $param_val = $wpsl_settings['api_' . $option_key];
37
+ }
38
+
39
+ if ( !empty( $param_val ) ) {
40
+ $api_params .= $param_key . '=' . $param_val . '&';
41
+ }
42
+ }
43
+
44
+ if ( $api_params ) {
45
+ $api_params = $first_sep . rtrim( $api_params, '&' );
46
+ }
47
+
48
+ // Do we need to include the autocomplete library?
49
+ if ( ( $wpsl_settings['autocomplete'] && $api_key_type == 'browser_key' ) || is_admin() ) {
50
+ $api_params .= '&libraries=places';
51
+ }
52
+
53
+ if ( $api_key_type == 'browser_key' ) {
54
+ $api_version = apply_filters( 'wpsl_gmap_api_version', '3.33' );
55
+ $api_params .= '&v=' . $api_version;
56
+ }
57
+
58
+ return apply_filters( 'wpsl_gmap_api_params', $api_params );
59
+ }
60
+
61
+ /**
62
+ * Get the default plugin settings.
63
+ *
64
+ * @since 1.0.0
65
+ * @return array $default_settings The default settings
66
+ */
67
+ function wpsl_get_default_settings() {
68
+
69
+ $default_settings = array(
70
+ 'api_browser_key' => '',
71
+ 'api_server_key' => '',
72
+ 'api_language' => 'en',
73
+ 'api_region' => '',
74
+ 'api_geocode_component' => 0,
75
+ 'distance_unit' => 'km',
76
+ 'max_results' => '[25],50,75,100',
77
+ 'search_radius' => '10,25,[50],100,200,500',
78
+ 'marker_effect' => 'bounce',
79
+ 'address_format' => 'city_state_zip',
80
+ 'hide_distance' => 0,
81
+ 'hide_country' => 0,
82
+ 'show_contact_details' => 0,
83
+ 'clickable_contact_details' => 0,
84
+ 'auto_locate' => 1,
85
+ 'autocomplete' => 0,
86
+ 'autoload' => 1,
87
+ 'autoload_limit' => 50,
88
+ 'run_fitbounds' => 1,
89
+ 'zoom_level' => 3,
90
+ 'auto_zoom_level' => 15,
91
+ 'start_name' => '',
92
+ 'start_latlng' => '',
93
+ 'height' => 350,
94
+ 'map_type' => 'roadmap',
95
+ 'map_style' => '',
96
+ 'type_control' => 0,
97
+ 'streetview' => 0,
98
+ 'results_dropdown' => 1,
99
+ 'radius_dropdown' => 1,
100
+ 'category_filter' => 0,
101
+ 'category_filter_type' => 'dropdown',
102
+ 'infowindow_width' => 225,
103
+ 'search_width' => 179,
104
+ 'label_width' => 95,
105
+ 'control_position' => 'left',
106
+ 'scrollwheel' => 1,
107
+ 'marker_clusters' => 0,
108
+ 'cluster_zoom' => 0,
109
+ 'cluster_size' => 0,
110
+ 'new_window' => 0,
111
+ 'reset_map' => 0,
112
+ 'template_id' => 'default',
113
+ 'listing_below_no_scroll' => 0,
114
+ 'direction_redirect' => 0,
115
+ 'more_info' => 0,
116
+ 'store_url' => 0,
117
+ 'phone_url' => 0,
118
+ 'marker_streetview' => 0,
119
+ 'marker_zoom_to' => 0,
120
+ 'more_info_location' => 'info window',
121
+ 'mouse_focus' => 0,
122
+ 'start_marker' => 'red.png',
123
+ 'store_marker' => 'blue.png',
124
+ 'editor_country' => '',
125
+ 'editor_hours' => wpsl_default_opening_hours(),
126
+ 'editor_hour_input' => 'dropdown',
127
+ 'editor_hour_format' => 12,
128
+ 'editor_map_type' => 'roadmap',
129
+ 'hide_hours' => 0,
130
+ 'permalinks' => 0,
131
+ 'permalink_slug' => __( 'stores', 'wpsl' ),
132
+ 'category_slug' => __( 'store-category', 'wpsl' ),
133
+ 'infowindow_style' => 'default',
134
+ 'show_credits' => 0,
135
+ 'debug' => 0,
136
+ 'deregister_gmaps' => 0,
137
+ 'start_label' => __( 'Start location', 'wpsl' ),
138
+ 'search_label' => __( 'Your location', 'wpsl' ),
139
+ 'search_btn_label' => __( 'Search', 'wpsl' ),
140
+ 'preloader_label' => __( 'Searching...', 'wpsl' ),
141
+ 'radius_label' => __( 'Search radius', 'wpsl' ),
142
+ 'no_results_label' => __( 'No results found', 'wpsl' ),
143
+ 'results_label' => __( 'Results', 'wpsl' ),
144
+ 'more_label' => __( 'More info', 'wpsl' ),
145
+ 'directions_label' => __( 'Directions', 'wpsl' ),
146
+ 'no_directions_label' => __( 'No route could be found between the origin and destination', 'wpsl' ),
147
+ 'back_label' => __( 'Back', 'wpsl' ),
148
+ 'street_view_label' => __( 'Street view', 'wpsl' ),
149
+ 'zoom_here_label' => __( 'Zoom here', 'wpsl' ),
150
+ 'error_label' => __( 'Something went wrong, please try again!', 'wpsl' ),
151
+ 'limit_label' => __( 'API usage limit reached', 'wpsl' ),
152
+ 'phone_label' => __( 'Phone', 'wpsl' ),
153
+ 'fax_label' => __( 'Fax', 'wpsl' ),
154
+ 'email_label' => __( 'Email', 'wpsl' ),
155
+ 'url_label' => __( 'Url', 'wpsl' ),
156
+ 'hours_label' => __( 'Hours', 'wpsl' ),
157
+ 'category_label' => __( 'Category filter', 'wpsl' ),
158
+ 'category_default_label' => __( 'Any', 'wpsl' )
159
+ );
160
+
161
+ return $default_settings;
162
+ }
163
+
164
+ /**
165
+ * Get the current plugin settings.
166
+ *
167
+ * @since 1.0.0
168
+ * @return array $setting The current plugin settings
169
+ */
170
+ function wpsl_get_settings() {
171
+
172
+ $settings = get_option( 'wpsl_settings' );
173
+
174
+ if ( !$settings ) {
175
+ update_option( 'wpsl_settings', wpsl_get_default_settings() );
176
+ $settings = wpsl_get_default_settings();
177
+ }
178
+
179
+ return $settings;
180
+ }
181
+
182
+ /**
183
+ * Get a single value from the default settings.
184
+ *
185
+ * @since 1.0.0
186
+ * @param string $setting The value that should be restored
187
+ * @return string $wpsl_default_settings The default setting value
188
+ */
189
+ function wpsl_get_default_setting( $setting ) {
190
+
191
+ global $wpsl_default_settings;
192
+
193
+ return $wpsl_default_settings[$setting];
194
+ }
195
+
196
+ /**
197
+ * Set the default plugin settings.
198
+ *
199
+ * @since 1.0.0
200
+ * @return void
201
+ */
202
+ function wpsl_set_default_settings() {
203
+
204
+ $settings = get_option( 'wpsl_settings' );
205
+
206
+ if ( !$settings ) {
207
+ update_option( 'wpsl_settings', wpsl_get_default_settings() );
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Return a list of the store templates.
213
+ *
214
+ * @since 1.2.20
215
+ * @return array $templates The list of default store templates
216
+ */
217
+ function wpsl_get_templates() {
218
+
219
+ $templates = array(
220
+ array(
221
+ 'id' => 'default',
222
+ 'name' => __( 'Default', 'wpsl' ),
223
+ 'path' => WPSL_PLUGIN_DIR . 'frontend/templates/default.php'
224
+ ),
225
+ array(
226
+ 'id' => 'below_map',
227
+ 'name' => __( 'Show the store list below the map', 'wpsl' ),
228
+ 'path' => WPSL_PLUGIN_DIR . 'frontend/templates/store-listings-below.php'
229
+ )
230
+ );
231
+
232
+ return apply_filters( 'wpsl_templates', $templates );
233
+ }
234
+
235
+ /**
236
+ * Return the days of the week.
237
+ *
238
+ * @since 2.0.0
239
+ * @return array $weekdays The days of the week
240
+ */
241
+ function wpsl_get_weekdays() {
242
+
243
+ $weekdays = array(
244
+ 'monday' => __( 'Monday', 'wpsl' ),
245
+ 'tuesday' => __( 'Tuesday', 'wpsl' ),
246
+ 'wednesday' => __( 'Wednesday', 'wpsl' ),
247
+ 'thursday' => __( 'Thursday', 'wpsl' ),
248
+ 'friday' => __( 'Friday', 'wpsl' ),
249
+ 'saturday' => __( 'Saturday', 'wpsl' ),
250
+ 'sunday' => __( 'Sunday' , 'wpsl' )
251
+ );
252
+
253
+ return $weekdays;
254
+ }
255
+
256
+ /**
257
+ * Get the default opening hours.
258
+ *
259
+ * @since 2.0.0
260
+ * @return array $opening_hours The default opening hours
261
+ */
262
+ function wpsl_default_opening_hours() {
263
+
264
+ $current_version = get_option( 'wpsl_version' );
265
+
266
+ $opening_hours = array(
267
+ 'dropdown' => array(
268
+ 'monday' => array( '9:00 AM,5:00 PM' ),
269
+ 'tuesday' => array( '9:00 AM,5:00 PM' ),
270
+ 'wednesday' => array( '9:00 AM,5:00 PM' ),
271
+ 'thursday' => array( '9:00 AM,5:00 PM' ),
272
+ 'friday' => array( '9:00 AM,5:00 PM' ),
273
+ 'saturday' => '',
274
+ 'sunday' => ''
275
+ )
276
+ );
277
+
278
+ /* Only add the textarea defaults for users that upgraded from 1.x */
279
+ if ( version_compare( $current_version, '2.0', '<' ) ) {
280
+ $opening_hours['textarea'] = sprintf( __( 'Mon %sTue %sWed %sThu %sFri %sSat Closed %sSun Closed', 'wpsl' ), '9:00 AM - 5:00 PM' . "\n", '9:00 AM - 5:00 PM' . "\n", '9:00 AM - 5:00 PM' . "\n", '9:00 AM - 5:00 PM' . "\n", '9:00 AM - 5:00 PM' . "\n", "\n" ); //cleaner way without repeating it 5 times??
281
+ }
282
+
283
+ return $opening_hours;
284
+ }
285
+
286
+ /**
287
+ * Get the available map types.
288
+ *
289
+ * @since 2.0.0
290
+ * @return array $map_types The available map types
291
+ */
292
+ function wpsl_get_map_types() {
293
+
294
+ $map_types = array(
295
+ 'roadmap' => __( 'Roadmap', 'wpsl' ),
296
+ 'satellite' => __( 'Satellite', 'wpsl' ),
297
+ 'hybrid' => __( 'Hybrid', 'wpsl' ),
298
+ 'terrain' => __( 'Terrain', 'wpsl' )
299
+ );
300
+
301
+ return $map_types;
302
+ }
303
+
304
+ /**
305
+ * Get the address formats.
306
+ *
307
+ * @since 2.0.0
308
+ * @return array $address_formats The address formats
309
+ */
310
+ function wpsl_get_address_formats() {
311
+
312
+ $address_formats = array(
313
+ 'city_state_zip' => __( '(city) (state) (zip code)', 'wpsl' ),
314
+ 'city_comma_state_zip' => __( '(city), (state) (zip code)', 'wpsl' ),
315
+ 'city_zip' => __( '(city) (zip code)', 'wpsl' ),
316
+ 'city_comma_zip' => __( '(city), (zip code)', 'wpsl' ),
317
+ 'zip_city_state' => __( '(zip code) (city) (state)', 'wpsl' ),
318
+ 'zip_city' => __( '(zip code) (city)', 'wpsl' )
319
+ );
320
+
321
+ return apply_filters( 'wpsl_address_formats', $address_formats );
322
+ }
323
+
324
+ /**
325
+ * Make sure the provided map type is valid.
326
+ *
327
+ * If the map type is invalid the default is used ( roadmap ).
328
+ *
329
+ * @since 2.0.0
330
+ * @param string $map_type The provided map type
331
+ * @return string $map_type A valid map type
332
+ */
333
+ function wpsl_valid_map_type( $map_type ) {
334
+
335
+ $allowed_map_types = wpsl_get_map_types();
336
+
337
+ if ( !array_key_exists( $map_type, $allowed_map_types ) ) {
338
+ $map_type = wpsl_get_default_setting( 'map_type' );
339
+ }
340
+
341
+ return $map_type;
342
+ }
343
+
344
+ /**
345
+ * Make sure the provided zoom level is valid.
346
+ *
347
+ * If the zoom level is invalid the default is used ( 3 ).
348
+ *
349
+ * @since 2.0.0
350
+ * @param string $zoom_level The provided zoom level
351
+ * @return string $zoom_level A valid zoom level
352
+ */
353
+ function wpsl_valid_zoom_level( $zoom_level ) {
354
+
355
+ $zoom_level = absint( $zoom_level );
356
+
357
+ if ( ( $zoom_level < 1 ) || ( $zoom_level > 21 ) ) {
358
+ $zoom_level = wpsl_get_default_setting( 'zoom_level' );
359
+ }
360
+
361
+ return $zoom_level;
362
+ }
363
+
364
+ /**
365
+ * Get the max auto zoom levels for the map.
366
+ *
367
+ * @since 2.0.0
368
+ * @return array $max_zoom_levels The array holding the min - max zoom levels
369
+ */
370
+ function wpsl_get_max_zoom_levels() {
371
+
372
+ $max_zoom_levels = array();
373
+ $zoom_level = array(
374
+ 'min' => 10,
375
+ 'max' => 21
376
+ );
377
+
378
+ $i = $zoom_level['min'];
379
+
380
+ while ( $i <= $zoom_level['max'] ) {
381
+ $max_zoom_levels[$i] = $i;
382
+ $i++;
383
+ }
384
+
385
+ return $max_zoom_levels;
386
+ }
387
+
388
+ /**
389
+ * The labels and the values that can be set through the settings page.
390
+ *
391
+ * @since 2.0.0
392
+ * @return array $labels The label names from the settings page.
393
+ */
394
+ function wpsl_labels() {
395
+
396
+ $labels = array(
397
+ 'search',
398
+ 'search_btn',
399
+ 'preloader',
400
+ 'radius',
401
+ 'no_results',
402
+ 'results',
403
+ 'more',
404
+ 'directions',
405
+ 'no_directions',
406
+ 'back',
407
+ 'street_view',
408
+ 'zoom_here',
409
+ 'error',
410
+ 'phone',
411
+ 'fax',
412
+ 'email',
413
+ 'url',
414
+ 'hours',
415
+ 'start',
416
+ 'limit',
417
+ 'category',
418
+ 'category_default'
419
+ );
420
+
421
+ return $labels;
422
+ }
423
+
424
+ /**
425
+ * Callback for array_walk_recursive, sanitize items in a multidimensional array.
426
+ *
427
+ * @since 2.0.0
428
+ * @param string $item The value
429
+ * @param integer $key The key
430
+ */
431
+ function wpsl_sanitize_multi_array( &$item, $key ) {
432
+ $item = sanitize_text_field( $item );
433
+ }
434
+
435
+ /**
436
+ * Check whether the array is multidimensional.
437
+ *
438
+ * @since 2.0.0
439
+ * @param array $array The array to check
440
+ * @return boolean
441
+ */
442
+ function wpsl_is_multi_array( $array ) {
443
+
444
+ foreach ( $array as $value ) {
445
+ if ( is_array( $value ) ) return true;
446
+ }
447
+
448
+ return false;
449
+ }
450
+
451
+ /**
452
+ * @since 2.1.1
453
+ * @param string $address The address to geocode.
454
+ * @return array $response Either a WP_Error or the response from the Geocode API.
455
+ */
456
+ function wpsl_call_geocode_api( $address ) {
457
+
458
+ $url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' . urlencode( $address ) . wpsl_get_gmap_api_params( 'server_key', true );
459
+ $response = wp_remote_get( $url );
460
+
461
+ return $response;
462
+ }
463
+
464
+ /**
465
+ * Get the latlng for the provided address.
466
+ *
467
+ * This is used to geocode the address set as the start point on
468
+ * the settings page in case the autocomplete fails
469
+ * ( only happens when there is a JS error on the page ),
470
+ * or to get the latlng when the 'start_location' attr is set
471
+ * on the wpsl shortcode.
472
+ *
473
+ * @since 2.2
474
+ * @param string $address The address to geocode.
475
+ * @return array|void $latlng The returned latlng or nothing if there was an error.
476
+ */
477
+ function wpsl_get_address_latlng( $address ) {
478
+
479
+ $latlng = '';
480
+ $response = wpsl_call_geocode_api( $address );
481
+
482
+ if ( !is_wp_error( $response ) ) {
483
+ $response = json_decode( $response['body'], true );
484
+
485
+ if ( $response['status'] == 'OK' ) {
486
+ $latlng = $response['results'][0]['geometry']['location']['lat'] . ',' . $response['results'][0]['geometry']['location']['lng'];
487
+ }
488
+ }
489
+
490
+ return $latlng;
491
+ }
492
+
493
+ /**
494
+ * Check if there's a transient that holds
495
+ * the coordinates for the passed address.
496
+ *
497
+ * If not, then we geocode the address and
498
+ * set the returned value in the transient.
499
+ *
500
+ * @since 2.2.11
501
+ * @param string $address The location to geocode
502
+ * @return string $latlng The coordinates of the geocoded location
503
+ */
504
+ function wpsl_check_latlng_transient( $address ) {
505
+
506
+ $name_section = explode( ',', $address );
507
+ $transient_name = 'wpsl_' . trim( strtolower( $name_section[0] ) ) . '_latlng';
508
+
509
+ if ( false === ( $latlng = get_transient( $transient_name ) ) ) {
510
+ $latlng = wpsl_get_address_latlng( $address );
511
+
512
+ if ( $latlng ) {
513
+ set_transient( $transient_name, $latlng, 0 );
514
+ }
515
+ }
516
+
517
+ return $latlng;
518
+ }
519
+
520
+ /**
521
+ * Make sure the shortcode attributes are booleans
522
+ * when they are expected to be.
523
+ *
524
+ * @since 2.0.4
525
+ * @param array $atts Shortcode attributes
526
+ * @return array $atts Shortcode attributes
527
+ */
528
+ function wpsl_bool_check( $atts ) {
529
+
530
+ foreach ( $atts as $key => $val ) {
531
+ if ( in_array( $val, array( 'true', '1', 'yes', 'on' ) ) ) {
532
+ $atts[$key] = true;
533
+ } else if ( in_array( $val, array( 'false', '0', 'no', 'off' ) ) ) {
534
+ $atts[$key] = false;
535
+ }
536
+ }
537
+
538
+ return $atts;
539
+ }
540
+
541
+ /**
542
+ * Create a string with random characters.
543
+ *
544
+ * @since 2.2.4
545
+ * @param int $length Used length
546
+ * @return string $random_chars Random characters
547
+ */
548
+ function wpsl_random_chars( $length = 5 ) {
549
+
550
+ $random_chars = substr( str_shuffle( "abcdefghijklmnopqrstuvwxyz" ), 0, $length );
551
+
552
+ return $random_chars;
553
+ }
554
+
555
+ /**
556
+ * Deregister other Google Maps scripts.
557
+ *
558
+ * If plugins / themes also include the Google Maps library, then it can cause
559
+ * problems with the autocomplete function on the settings page and break
560
+ * the store locator on the front-end.
561
+ *
562
+ * @since 2.2.4
563
+ * @return void
564
+ */
565
+ function wpsl_deregister_other_gmaps() {
566
+
567
+ global $wp_scripts;
568
+
569
+ foreach ( $wp_scripts->registered as $index => $script ) {
570
+ if ( ( strpos( $script->src, 'maps.google.com' ) !== false ) || ( strpos( $script->src, 'maps.googleapis.com' ) !== false ) && ( $script->handle !== 'wpsl-gmap' ) ) {
571
+ wp_deregister_script( $script->handle );
572
+ }
573
+ }
574
+ }
575
+
576
+ /**
577
+ * Return the used distance unit.
578
+ *
579
+ * @since 2.2.8
580
+ * @return string Either km or mi
581
+ */
582
+ function wpsl_get_distance_unit() {
583
+
584
+ global $wpsl_settings;
585
+
586
+ return apply_filters( 'wpsl_distance_unit', $wpsl_settings['distance_unit'] );
587
+ }
588
+
589
+ /**
590
+ * Find the term ids for the provided term slugs.
591
+ *
592
+ * @since 2.2.10
593
+ * @param array $cat_list List of term slugs
594
+ * @return array $term_ids The term ids
595
+ */
596
+ function wpsl_get_term_ids( $cat_list ) {
597
+
598
+ $term_ids = array();
599
+ $cats = explode( ',', $cat_list );
600
+
601
+ foreach ( $cats as $key => $term_slug ) {
602
+ $term_data = get_term_by( 'slug', $term_slug, 'wpsl_store_category' );
603
+
604
+ if ( isset( $term_data->term_id ) && $term_data->term_id ) {
605
+ $term_ids[] = $term_data->term_id;
606
+ }
607
+ }
608
+
609
+ return $term_ids;
610
+ }
611
+
612
+ /**
613
+ * Get the url to the admin-ajax.php
614
+ *
615
+ * @since 2.2.3
616
+ * @return string $ajax_url URL to the admin-ajax.php possibly with the WPML lang param included.
617
+ */
618
+ function wpsl_get_ajax_url() {
619
+
620
+ $i18n = new WPSL_i18n();
621
+
622
+ $param = '';
623
+
624
+ if ( $i18n->wpml_exists() && defined( 'ICL_LANGUAGE_CODE' ) ) {
625
+ $param = '?lang=' . ICL_LANGUAGE_CODE;
626
+ }
627
+
628
+ $ajax_url = admin_url( 'admin-ajax.php' . $param );
629
+
630
+ return $ajax_url;
631
+ }
632
+
633
+ /**
634
+ * Get a list of the used meta fields.
635
+ *
636
+ * Used by add-ons and the REST-API.
637
+ *
638
+ * @since 2.2.14
639
+ * @param array $args Argument to grab the locations field. See the $defaults structure.
640
+ * @return array $fields
641
+ */
642
+ function wpsl_get_location_fields( $args = array() ) {
643
+
644
+ // Required to make sure it works with API calls.
645
+ if ( !class_exists( 'WPSL_Metaboxes' ) ) {
646
+ require_once( WPSL_PLUGIN_DIR . 'admin/class-metaboxes.php' );
647
+ }
648
+
649
+ $metaboxes = new WPSL_Metaboxes();
650
+ $meta_fields = $metaboxes->meta_box_fields();
651
+
652
+ $fields = array();
653
+ $defaults = array(
654
+ 'exclude' => array( 'country_iso' ),
655
+ 'prefix' => '',
656
+ 'set_values' => true
657
+ );
658
+
659
+ /**
660
+ * Parse incoming $args into an array and merge it with $defaults
661
+ */
662
+ $args = wp_parse_args( $args, $defaults );
663
+
664
+ foreach ( $meta_fields as $k => $field_section ) {
665
+ foreach ( $field_section as $field_name => $field_value ) {
666
+ if ( in_array( $field_name, $args['exclude'] ) ) {
667
+ continue;
668
+ }
669
+ $fields[$args['prefix'] . $field_name] = ( $args['set_values'] ) ? $field_name : '';
670
+ }
671
+ }
672
+ return $fields;
673
  }
js/wpsl-gmap.js CHANGED
@@ -1,2442 +1,2604 @@
1
- jQuery( document ).ready( function( $ ) {
2
- var geocoder, map, directionsDisplay, directionsService, autoCompleteLatLng,
3
- activeWindowMarkerId, infoWindow, markerClusterer, startMarkerData, startAddress,
4
- openInfoWindow = [],
5
- markersArray = [],
6
- mapsArray = [],
7
- markerSettings = {},
8
- directionMarkerPosition = {},
9
- mapDefaults = {},
10
- resetMap = false,
11
- streetViewAvailable = false,
12
- autoLoad = ( typeof wpslSettings !== "undefined" ) ? wpslSettings.autoLoad : "",
13
- userGeolocation = {},
14
- statistics = {
15
- enabled: ( typeof wpslSettings.collectStatistics !== "undefined" ) ? true : false,
16
- address_components: ''
17
- };
18
-
19
- /**
20
- * Set the underscore template settings.
21
- *
22
- * Defining them here prevents other plugins
23
- * that also use underscore / backbone, and defined a
24
- * different _.templateSettings from breaking the
25
- * rendering of the store locator template.
26
- *
27
- * @link http://underscorejs.org/#template
28
- * @requires underscore.js
29
- * @since 2.0.0
30
- */
31
- _.templateSettings = {
32
- evaluate: /\<\%(.+?)\%\>/g,
33
- interpolate: /\<\%=(.+?)\%\>/g,
34
- escape: /\<\%-(.+?)\%\>/g
35
- };
36
-
37
- // Only continue if a map is present.
38
- if ( $( ".wpsl-gmap-canvas" ).length ) {
39
- $( "<img />" ).attr( "src", wpslSettings.url + "img/ajax-loader.gif" );
40
-
41
- /*
42
- * The [wpsl] shortcode can only exist once on a page,
43
- * but the [wpsl_map] shortcode can exist multiple times.
44
- *
45
- * So to make sure we init all the maps we loop over them.
46
- */
47
- $( ".wpsl-gmap-canvas" ).each( function( mapIndex ) {
48
- var mapId = $( this ).attr( "id" );
49
-
50
- initializeGmap( mapId, mapIndex );
51
- });
52
-
53
- /*
54
- * Check if we are dealing with a map that's placed in a tab,
55
- * if so run a fix to prevent the map from showing up grey.
56
- */
57
- maybeApplyTabFix();
58
- }
59
-
60
- /**
61
- * Initialize the map with the correct settings.
62
- *
63
- * @since 1.0.0
64
- * @param {string} mapId The id of the map div
65
- * @param {number} mapIndex Number of the map
66
- * @returns {void}
67
- */
68
- function initializeGmap( mapId, mapIndex ) {
69
- var mapOptions, mapDetails, settings, infoWindow, latLng,
70
- bounds, mapData, zoomLevel,
71
- defaultZoomLevel = Number( wpslSettings.zoomLevel ),
72
- maxZoom = Number( wpslSettings.autoZoomLevel );
73
-
74
- // Get the settings that belongs to the current map.
75
- settings = getMapSettings( mapIndex );
76
-
77
- /*
78
- * This is the value from either the settings page,
79
- * or the zoom level set through the shortcode.
80
- */
81
- zoomLevel = Number( settings.zoomLevel );
82
-
83
- /*
84
- * If they are not equal, then the zoom value is set through the shortcode.
85
- * If this is the case, then we use that as the max zoom level.
86
- */
87
- if ( zoomLevel !== defaultZoomLevel ) {
88
- maxZoom = zoomLevel;
89
- }
90
-
91
- // Create a new infoWindow, either with the infobox libray or use the default one.
92
- infoWindow = newInfoWindow();
93
-
94
- geocoder = new google.maps.Geocoder();
95
- directionsDisplay = new google.maps.DirectionsRenderer();
96
- directionsService = new google.maps.DirectionsService();
97
-
98
- // Set the map options.
99
- mapOptions = {
100
- zoom: zoomLevel,
101
- center: settings.startLatLng,
102
- mapTypeId: google.maps.MapTypeId[ settings.mapType.toUpperCase() ],
103
- mapTypeControl: Number( settings.mapTypeControl ) ? true : false,
104
- scrollwheel: Number( settings.scrollWheel ) ? true : false,
105
- streetViewControl: Number( settings.streetView ) ? true : false,
106
- gestureHandling: settings.gestureHandling,
107
- zoomControlOptions: {
108
- position: google.maps.ControlPosition[ settings.controlPosition.toUpperCase() + '_TOP' ]
109
- }
110
- };
111
-
112
- // Get the correct marker path & properties.
113
- markerSettings = getMarkerSettings();
114
-
115
- map = new google.maps.Map( document.getElementById( mapId ), mapOptions );
116
-
117
- // Check if we need to apply a map style.
118
- maybeApplyMapStyle( settings.mapStyle );
119
-
120
- if ( ( typeof window[ "wpslMap_" + mapIndex ] !== "undefined" ) && ( typeof window[ "wpslMap_" + mapIndex ].locations !== "undefined" ) ) {
121
- bounds = new google.maps.LatLngBounds(),
122
- mapData = window[ "wpslMap_" + mapIndex ].locations;
123
-
124
- // Loop over the map data, create the infowindow object and add each marker.
125
- $.each( mapData, function( index ) {
126
- latLng = new google.maps.LatLng( mapData[index].lat, mapData[index].lng );
127
- addMarker( latLng, mapData[index].id, mapData[index], false, infoWindow );
128
- bounds.extend( latLng );
129
- });
130
-
131
- // If we have more then one location on the map, then make sure to not zoom to far.
132
- if ( mapData.length > 1 ) {
133
- // Make sure we don't zoom to far when fitBounds runs.
134
- attachBoundsChangedListener( map, maxZoom );
135
-
136
- // Make all the markers fit on the map.
137
- map.fitBounds( bounds );
138
- }
139
-
140
- /*
141
- * If we need to apply the fix for the map showing up grey because
142
- * it's used in a tabbed nav multiple times, then collect the active maps.
143
- *
144
- * See the fixGreyTabMap function.
145
- */
146
- if ( _.isArray( wpslSettings.mapTabAnchor ) ) {
147
- mapDetails = {
148
- map: map,
149
- bounds: bounds,
150
- maxZoom: maxZoom
151
- };
152
-
153
- mapsArray.push( mapDetails );
154
- }
155
- }
156
-
157
- // Only run this part if the store locator exist and we don't just have a basic map.
158
- if ( $( "#wpsl-gmap" ).length ) {
159
-
160
- if ( wpslSettings.autoComplete == 1 ) {
161
- activateAutocomplete();
162
- }
163
-
164
- /*
165
- * Not the most optimal solution, but we check the useragent if we should enable the styled dropdowns.
166
- *
167
- * We do this because several people have reported issues with the styled dropdowns on
168
- * iOS and Android devices. So on mobile devices the dropdowns will be styled according
169
- * to the browser styles on that device.
170
- */
171
- if ( !checkMobileUserAgent() && $( ".wpsl-dropdown" ).length && wpslSettings.enableStyledDropdowns == 1 ) {
172
- createDropdowns();
173
- } else {
174
- $( "#wpsl-search-wrap select" ).show();
175
-
176
- if ( checkMobileUserAgent() ) {
177
- $( "#wpsl-wrap" ).addClass( "wpsl-mobile" );
178
- } else {
179
- $( "#wpsl-wrap" ).addClass( "wpsl-default-filters" );
180
- }
181
- }
182
-
183
- // Check if we need to autolocate the user, or autoload the store locations.
184
- if ( !$( ".wpsl-search" ).hasClass( "wpsl-widget" ) ) {
185
- if ( wpslSettings.autoLocate == 1 ) {
186
- checkGeolocation( settings.startLatLng, infoWindow );
187
- } else if ( wpslSettings.autoLoad == 1 ) {
188
- showStores( settings.startLatLng, infoWindow );
189
- }
190
- }
191
-
192
- // Move the mousecursor to the store search field if the focus option is enabled.
193
- if ( wpslSettings.mouseFocus == 1 && !checkMobileUserAgent() ) {
194
- $( "#wpsl-search-input" ).focus();
195
- }
196
-
197
- // Bind store search button.
198
- searchLocationBtn( infoWindow );
199
-
200
- // Add the 'reload' and 'find location' icon to the map.
201
- mapControlIcons( settings, map, infoWindow );
202
-
203
- // Check if the user submitted a search through a search widget.
204
- checkWidgetSubmit();
205
- }
206
-
207
- // Bind the zoom_changed listener.
208
- zoomChangedListener();
209
- }
210
-
211
- /**
212
- * Activate the autocomplete for the store search.
213
- *
214
- * @since 2.2.0
215
- * @link https://developers.google.com/maps/documentation/javascript/places-autocomplete
216
- * @returns {void}
217
- */
218
- function activateAutocomplete() {
219
- var input, autocomplete, place,
220
- options = {};
221
-
222
- // Check if we need to set the geocode component restrictions.
223
- if ( typeof wpslSettings.geocodeComponents !== "undefined" && !$.isEmptyObject( wpslSettings.geocodeComponents ) ) {
224
- options.componentRestrictions = wpslSettings.geocodeComponents;
225
- }
226
-
227
- input = document.getElementById( "wpsl-search-input" );
228
- autocomplete = new google.maps.places.Autocomplete( input, options );
229
-
230
- autocomplete.addListener( "place_changed", function() {
231
- place = autocomplete.getPlace();
232
-
233
- /*
234
- * Assign the returned latlng to the autoCompleteLatLng var.
235
- * This var is used when the users submits the search.
236
- */
237
- if ( place.geometry ) {
238
- autoCompleteLatLng = place.geometry.location;
239
- }
240
- });
241
- }
242
-
243
- /**
244
- * Make sure that the 'Zoom here' link in the info window
245
- * doesn't zoom past the max auto zoom level.
246
- *
247
- * The 'max auto zoom level' is set on the settings page.
248
- *
249
- * @since 2.0.0
250
- * @returns {void}
251
- */
252
- function zoomChangedListener() {
253
- if ( typeof wpslSettings.markerZoomTo !== "undefined" && wpslSettings.markerZoomTo == 1 ) {
254
- google.maps.event.addListener( map, "zoom_changed", function() {
255
- checkMaxZoomLevel();
256
- });
257
- }
258
- }
259
-
260
- /**
261
- * Get the correct map settings.
262
- *
263
- * @since 2.0.0
264
- * @param {number} mapIndex Number of the map
265
- * @returns {object} mapSettings The map settings either set through a shortcode or the default settings
266
- */
267
- function getMapSettings( mapIndex ) {
268
- var j, len, shortCodeVal,
269
- settingOptions = [ "zoomLevel", "mapType", "mapTypeControl", "mapStyle", "streetView", "scrollWheel", "controlPosition" ],
270
- mapSettings = {
271
- zoomLevel: wpslSettings.zoomLevel,
272
- mapType: wpslSettings.mapType,
273
- mapTypeControl: wpslSettings.mapTypeControl,
274
- mapStyle: wpslSettings.mapStyle,
275
- streetView: wpslSettings.streetView,
276
- scrollWheel: wpslSettings.scrollWheel,
277
- controlPosition: wpslSettings.controlPosition,
278
- gestureHandling: wpslSettings.gestureHandling
279
- };
280
-
281
- // If there are settings that are set through the shortcode, then we use them instead of the default ones.
282
- if ( ( typeof window[ "wpslMap_" + mapIndex ] !== "undefined" ) && ( typeof window[ "wpslMap_" + mapIndex ].shortCode !== "undefined" ) ) {
283
- for ( j = 0, len = settingOptions.length; j < len; j++ ) {
284
- shortCodeVal = window[ "wpslMap_" + mapIndex ].shortCode[ settingOptions[j] ];
285
-
286
- // If the value is set through the shortcode, we overwrite the default value.
287
- if ( typeof shortCodeVal !== "undefined" ) {
288
- mapSettings[ settingOptions[j] ] = shortCodeVal;
289
- }
290
- }
291
- }
292
-
293
- mapSettings.startLatLng = getStartLatlng( mapIndex );
294
-
295
- return mapSettings;
296
- }
297
-
298
- /**
299
- * Get the latlng coordinates that are used to init the map.
300
- *
301
- * @since 2.0.0
302
- * @param {number} mapIndex Number of the map
303
- * @returns {object} startLatLng The latlng value where the map will initially focus on
304
- */
305
- function getStartLatlng( mapIndex ) {
306
- var startLatLng, latLng,
307
- firstLocation = "";
308
-
309
- /*
310
- * Maps that are added with the [wpsl_map] shortcode will have the locations key set.
311
- * If it exists we use the coordinates from the first location to center the map on.
312
- */
313
- if ( ( typeof window[ "wpslMap_" + mapIndex ] !== "undefined" ) && ( typeof window[ "wpslMap_" + mapIndex ].locations !== "undefined" ) ) {
314
- firstLocation = window[ "wpslMap_" + mapIndex ].locations[0];
315
- }
316
-
317
- /*
318
- * Either use the coordinates from the first location as the start coordinates
319
- * or the default start point defined on the settings page.
320
- *
321
- * If both are not available we set it to 0,0
322
- */
323
- if ( ( typeof firstLocation !== "undefined" && typeof firstLocation.lat !== "undefined" ) && ( typeof firstLocation.lng !== "undefined" ) ) {
324
- startLatLng = new google.maps.LatLng( firstLocation.lat, firstLocation.lng );
325
- } else if ( wpslSettings.startLatlng !== "" ) {
326
- latLng = wpslSettings.startLatlng.split( "," );
327
- startLatLng = new google.maps.LatLng( latLng[0], latLng[1] );
328
- } else {
329
- startLatLng = new google.maps.LatLng( 0,0 );
330
- }
331
-
332
- return startLatLng;
333
- }
334
-
335
- /**
336
- * Create a new infoWindow object.
337
- *
338
- * Either use the default infoWindow or use the infobox library.
339
- *
340
- * @since 2.0.0
341
- * @return {object} infoWindow The infoWindow object
342
- */
343
- function newInfoWindow() {
344
- var boxClearance, boxPixelOffset,
345
- infoBoxOptions = {};
346
-
347
- // Do we need to use the infobox script or use the default info windows?
348
- if ( ( typeof wpslSettings.infoWindowStyle !== "undefined" ) && ( wpslSettings.infoWindowStyle == "infobox" ) ) {
349
-
350
- // See http://google-maps-utility-library-v3.googlecode.com/svn/trunk/infobox/docs/reference.html.
351
- boxClearance = wpslSettings.infoBoxClearance.split( "," );
352
- boxPixelOffset = wpslSettings.infoBoxPixelOffset.split( "," );
353
- infoBoxOptions = {
354
- alignBottom: true,
355
- boxClass: wpslSettings.infoBoxClass,
356
- closeBoxMargin: wpslSettings.infoBoxCloseMargin,
357
- closeBoxURL: wpslSettings.infoBoxCloseUrl,
358
- content: "",
359
- disableAutoPan: ( Number( wpslSettings.infoBoxDisableAutoPan ) ) ? true : false,
360
- enableEventPropagation: ( Number( wpslSettings.infoBoxEnableEventPropagation ) ) ? true : false,
361
- infoBoxClearance: new google.maps.Size( Number( boxClearance[0] ), Number( boxClearance[1] ) ),
362
- pixelOffset: new google.maps.Size( Number( boxPixelOffset[0] ), Number( boxPixelOffset[1] ) ),
363
- zIndex: Number( wpslSettings.infoBoxZindex )
364
- };
365
-
366
- infoWindow = new InfoBox( infoBoxOptions );
367
- } else {
368
- infoWindow = new google.maps.InfoWindow();
369
- }
370
-
371
- return infoWindow;
372
- }
373
-
374
- /**
375
- * Get the required marker settings.
376
- *
377
- * @since 2.1.0
378
- * @return {object} settings The marker settings.
379
- */
380
- function getMarkerSettings() {
381
- var markerProp,
382
- markerProps = wpslSettings.markerIconProps,
383
- settings = {};
384
-
385
- // Use the correct marker path.
386
- if ( typeof markerProps.url !== "undefined" ) {
387
- settings.url = markerProps.url;
388
- } else if ( typeof markerProps.categoryMarkerUrl !== "undefined" ) {
389
- settings.categoryMarkerUrl = markerProps.categoryMarkerUrl;
390
- } else if ( typeof markerProps.alternateMarkerUrl !== "undefined" ) {
391
- settings.alternateMarkerUrl = markerProps.alternateMarkerUrl;
392
- } else {
393
- settings.url = wpslSettings.url + "img/markers/";
394
- }
395
-
396
- for ( var key in markerProps ) {
397
- if ( markerProps.hasOwnProperty( key ) ) {
398
- markerProp = markerProps[key].split( "," );
399
-
400
- if ( markerProp.length == 2 ) {
401
- settings[key] = markerProp;
402
- }
403
- }
404
- }
405
-
406
- return settings;
407
- }
408
-
409
- /**
410
- * Check if we have a map style that we need to apply to the map.
411
- *
412
- * @since 2.0.0
413
- * @param {string} mapStyle The id of the map
414
- * @return {void}
415
- */
416
- function maybeApplyMapStyle( mapStyle ) {
417
-
418
- // Make sure the JSON is valid before applying it as a map style.
419
- mapStyle = tryParseJSON( mapStyle );
420
-
421
- if ( mapStyle ) {
422
- map.setOptions({ styles: mapStyle });
423
- }
424
- }
425
-
426
- /**
427
- * Make sure the JSON is valid.
428
- *
429
- * @link http://stackoverflow.com/a/20392392/1065294
430
- * @since 2.0.0
431
- * @param {string} jsonString The JSON data
432
- * @return {object|boolean} The JSON string or false if it's invalid json.
433
- */
434
- function tryParseJSON( jsonString ) {
435
-
436
- try {
437
- var o = JSON.parse( jsonString );
438
-
439
- /*
440
- * Handle non-exception-throwing cases:
441
- * Neither JSON.parse(false) or JSON.parse(1234) throw errors, hence the type-checking,
442
- * but... JSON.parse(null) returns 'null', and typeof null === "object",
443
- * so we must check for that, too.
444
- */
445
- if ( o && typeof o === "object" && o !== null ) {
446
- return o;
447
- }
448
- }
449
- catch ( e ) { }
450
-
451
- return false;
452
- }
453
-
454
- /**
455
- * Add the start marker and call the function that inits the store search.
456
- *
457
- * @since 1.1.0
458
- * @param {object} startLatLng The start coordinates
459
- * @param {object} infoWindow The infoWindow object
460
- * @returns {void}
461
- */
462
- function showStores( startLatLng, infoWindow ) {
463
- addMarker( startLatLng, 0, '', true, infoWindow ); // This marker is the 'start location' marker. With a storeId of 0, no name and is draggable
464
- findStoreLocations( startLatLng, resetMap, autoLoad, infoWindow );
465
- }
466
-
467
- /**
468
- * Compare the current useragent to a list of known mobile useragents ( not optimal, I know ).
469
- *
470
- * @