| 1 |
<?php |
| 2 |
|
| 3 |
/** |
| 4 |
* Plugin Name: Store Locator |
| 5 |
* Version: 1.2.2 |
| 6 |
* Plugin URI: https://locatestore.com/ |
| 7 |
* Description: Create a store locator for your website in minutes. Add all the store locations in google sheets and embed map on your website. There is no coding involved here. |
| 8 |
* Author: Micro.company |
| 9 |
* Author URI: https://micro.company/ |
| 10 |
* License: GPLv2 or later |
| 11 |
*/ |
| 12 |
|
| 13 |
/** |
| 14 |
* Display instructional notice on top of dashboard pages. |
| 15 |
* Removed when user dismisses the notice. |
| 16 |
*/ |
| 17 |
// exit if accessed directly |
| 18 |
if (!defined('ABSPATH')) { |
| 19 |
exit; |
| 20 |
} |
| 21 |
|
| 22 |
$storelocator_active_plugin = []; |
| 23 |
$base = plugin_basename(__FILE__); |
| 24 |
$wp_plugins_dir = defined('WP_PLUGIN_DIR') ? WP_PLUGIN_DIR : $wp_content_dir . '/plugins'; |
| 25 |
if (is_admin()) { |
| 26 |
if (!function_exists('get_plugin_data')) { |
| 27 |
require_once(ABSPATH . 'wp-admin/includes/plugin.php'); |
| 28 |
} |
| 29 |
$storelocator_active_plugin[$base] = get_plugin_data($wp_plugins_dir . '/' . $base, true, false); |
| 30 |
} |
| 31 |
|
| 32 |
define('STLR_PLUGIN_DIR', str_replace('\\', '/', dirname(__FILE__))); |
| 33 |
define('STLR_DIR_URL', plugin_dir_url(__FILE__)); |
| 34 |
|
| 35 |
function storelocator_settings_link($links) |
| 36 |
{ |
| 37 |
$settings_link = '<a href="options-general.php?page=storelocator">Settings</a>'; |
| 38 |
$support_link = '<a href="https://reach.at/storelocator" target="_blank">Support</a>'; |
| 39 |
|
| 40 |
array_push($links, $settings_link); |
| 41 |
array_push($links, $support_link); |
| 42 |
|
| 43 |
return $links; |
| 44 |
} |
| 45 |
|
| 46 |
$plugin = plugin_basename(__FILE__); |
| 47 |
add_filter( |
| 48 |
"plugin_action_links_$plugin", |
| 49 |
'storelocator_settings_link' |
| 50 |
); |
| 51 |
|
| 52 |
|
| 53 |
|
| 54 |
function storelocator_activation_redirect($plugin) |
| 55 |
{ |
| 56 |
if ($plugin == plugin_basename(__FILE__)) { |
| 57 |
exit(wp_redirect(admin_url('admin.php?page=storelocator'))); |
| 58 |
} |
| 59 |
} |
| 60 |
add_action( |
| 61 |
'activated_plugin', |
| 62 |
'storelocator_activation_redirect' |
| 63 |
); |
| 64 |
|
| 65 |
|
| 66 |
|
| 67 |
function storelocator_getting_started_notice() |
| 68 |
{ |
| 69 |
|
| 70 |
global $current_user; |
| 71 |
$user_id = $current_user->ID; |
| 72 |
/* Check that the user hasn't already clicked to ignore the message */ |
| 73 |
if (!get_user_meta($user_id, 'storelocator_getting_started_notice') && !(isset($_GET['page']) && $_GET['page'] == 'storelocator')) { |
| 74 |
|
| 75 |
printf(__('<div class="notice" style="display: flex;flex-direction:column;gap:10px;padding:20px;"> |
| 76 |
<a href="https://locatestore.com" class="logo" ><img src="%2$s" width="150px" alt="Storelocator logo"/></a> |
| 77 |
<div> |
| 78 |
<h4 style="margin: 0;">Getting started with your Store Locator 🚀</h4> |
| 79 |
|
| 80 |
<ol> |
| 81 |
<li>Install the <a href="https://workspace.google.com/marketplace/app/store_locator/734551689349" target="_blank" rel="noreferrer">Google marketplace addon.</a></li> |
| 82 |
<li><a href="https://sheets.new" target="_blank" rel="noreferrer">Create a new Google Sheet</a> and go to <em>Extensions > Store Locator > Get started</em>.</li> |
| 83 |
<li>Copy the Store Locator URL / shortcode and visit <a href="options-general.php?page=storelocator">plugin settings</a>.</li> |
| 84 |
</ol> |
| 85 |
</div> |
| 86 |
<a href="%1$s">Dismiss</a> |
| 87 |
</div>'), '?storelocator_notice_ignore=1',STLR_DIR_URL . "storelocator-logo.png"); |
| 88 |
} |
| 89 |
} |
| 90 |
add_action('admin_notices', 'storelocator_getting_started_notice'); |
| 91 |
|
| 92 |
|
| 93 |
function storelocator_notice_ignore() |
| 94 |
{ |
| 95 |
global $current_user; |
| 96 |
$user_id = $current_user->ID; |
| 97 |
/* If user clicks to ignore the notice, add that to their user meta */ |
| 98 |
if (isset($_GET['storelocator_notice_ignore']) && '1' == $_GET['storelocator_notice_ignore']) { |
| 99 |
add_user_meta($user_id, 'storelocator_getting_started_notice', 'true', true); |
| 100 |
} |
| 101 |
} |
| 102 |
add_action('admin_init', 'storelocator_notice_ignore'); |
| 103 |
|
| 104 |
/** |
| 105 |
* Convert shortcode into embedded iframe. |
| 106 |
*/ |
| 107 |
|
| 108 |
|
| 109 |
function storelocator_embed_shortcode($atts) |
| 110 |
{ |
| 111 |
$atts = array_change_key_case((array) $atts, CASE_LOWER); |
| 112 |
extract(shortcode_atts(array( |
| 113 |
'id' => '', |
| 114 |
'width' => '100%', |
| 115 |
'height' => "600" |
| 116 |
), $atts)); |
| 117 |
if (true || !($id == '')) { |
| 118 |
// add source = wp |
| 119 |
return '<iframe src="' . esc_url("https://locatestore.com/" . $id . "?source=wordpress") . '" allow="geolocation" width="' . esc_attr($width) . '" height="' . esc_attr($height) . '" frameBorder="0" allowfullscreen style="width:100% !important;max-width:100% !important;margin:10px;"></iframe>'; |
| 120 |
} else { |
| 121 |
return "⚠ Please enter a valid storelocator id."; |
| 122 |
} |
| 123 |
} |
| 124 |
add_shortcode('storelocator', 'storelocator_embed_shortcode'); |
| 125 |
|
| 126 |
/** |
| 127 |
* Register OEmbed config. This automatically loads the iframe from plain url. |
| 128 |
*/ |
| 129 |
|
| 130 |
wp_oembed_add_provider('https://locatestore.com/*', "https://locatestore.com/services/oembed"); |
| 131 |
|
| 132 |
/** |
| 133 |
* Automatically generate storelocator page. |
| 134 |
*/ |
| 135 |
|
| 136 |
function storelocator_check_page_existence($page_slug) |
| 137 |
{ |
| 138 |
$page = get_page_by_path($page_slug, OBJECT, 'page'); |
| 139 |
|
| 140 |
return isset($page); |
| 141 |
} |
| 142 |
function add_storelocator_page($url, $path) |
| 143 |
{ |
| 144 |
try { |
| 145 |
if (storelocator_check_page_existence($path)) throw new Exception("Page already exists"); |
| 146 |
$storelocator_page = array( |
| 147 |
'post_title' => 'Store Locator', |
| 148 |
'post_name' => $path, |
| 149 |
'post_content' => '[storelocator id="' . substr($url, 24) . '"]', |
| 150 |
'post_status' => 'publish', |
| 151 |
'post_author' => 1, |
| 152 |
'post_type' => 'page', |
| 153 |
); |
| 154 |
wp_insert_post($storelocator_page, true); |
| 155 |
update_option("storelocator_url", $url); |
| 156 |
update_option("storelocator_path", $path); |
| 157 |
|
| 158 |
return true; |
| 159 |
} catch (Exception $e) { |
| 160 |
return false; |
| 161 |
} |
| 162 |
} |
| 163 |
|
| 164 |
/** |
| 165 |
* Create settings menu |
| 166 |
*/ |
| 167 |
|
| 168 |
// Refer below link to understand how to add multiple settings section to same page |
| 169 |
// http://www.mendoweb.be/blog/wordpress-settings-api-multiple-forms-on-same-page/ |
| 170 |
function storelocator_add_menu() |
| 171 |
{ |
| 172 |
|
| 173 |
add_menu_page( |
| 174 |
'StoreLocator', // page <title>Title</title> |
| 175 |
'StoreLocator', // menu link text |
| 176 |
'manage_options', // capability to access the page |
| 177 |
'storelocator', // page URL slug |
| 178 |
'storelocator_settings_template', // callback function /w content |
| 179 |
'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDIwMDEwOTA0Ly9FTiIKICJodHRwOi8vd3d3LnczLm9yZy9UUi8yMDAxL1JFQy1TVkctMjAwMTA5MDQvRFREL3N2ZzEwLmR0ZCI+CjxzdmcgdmVyc2lvbj0iMS4wIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiB3aWR0aD0iNTEyLjAwMDAwMHB0IiBoZWlnaHQ9IjUxMi4wMDAwMDBwdCIgdmlld0JveD0iMCAwIDUxMi4wMDAwMDAgNTEyLjAwMDAwMCIKIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIG1lZXQiPgoKPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsNTEyLjAwMDAwMCkgc2NhbGUoMC4xMDAwMDAsLTAuMTAwMDAwKSIKZmlsbD0iIzAwMDAwMCIgc3Ryb2tlPSJub25lIj4KPHBhdGggZD0iTTIzODcgNTA0OSBjLTQ4NCAtNDcgLTk2NCAtMjg1IC0xMjYzIC02MjcgLTI0MyAtMjc4IC0zODcgLTYxOSAtNDM0Ci0xMDMxIC0xNiAtMTM1IC03IC00ODkgMTUgLTYzMSAxMDAgLTYzOSA0MTMgLTEzMTAgODkxIC0xOTA4IDI2NyAtMzM1IDYyNAotNjc3IDc4MiAtNzUwIDc3IC0zNSAxNzcgLTQ4IDI1NCAtMzQgMTM1IDI1IDIzOSA5NyA0ODIgMzMzIDczNCA3MTEgMTIyMQoxNjI2IDEzMTYgMjQ3NCAxNCAxMzAgMTIgNDMyIC00IDU1NSAtNTQgMzk5IC0xOTQgNzIyIC00MzAgOTkyIC0zNzggNDMzCi0xMDIxIDY4MyAtMTYwOSA2Mjd6IG00NDMgLTMxNSBjNjM2IC0xMDYgMTE1OSAtNTg5IDEzMTQgLTEyMTQgMzcgLTE1MSA0NgotMjI1IDQ2IC00MDIgMCAtMTEwIC02IC0xOTAgLTIwIC0yNzEgLTEwMCAtNTg5IC01MjggLTEwODcgLTEwOTUgLTEyNzYgLTE4NgotNjIgLTI3OCAtNzUgLTUxNSAtNzUgLTIzNyAwIC0zMjkgMTMgLTUxNSA3NSAtNTcwIDE5MCAtOTk1IDY4NyAtMTA5NiAxMjgyCi0xOCAxMDMgLTIwIDE1MSAtMTcgMzEyIDQgMTYxIDkgMjA4IDMyIDMwOSAxMTEgNDc5IDQwNyA4NjcgODM2IDEwOTQgMzEzIDE2NQo2NzcgMjI0IDEwMzAgMTY2eiIvPgo8cGF0aCBkPSJNMTU2NyA0MDg4IGMtMTkgLTE1IC0xOTUgLTM4NSAtMjA0IC00MjkgLTE1IC03OCAzNyAtMTU5IDEzMCAtMjAwCjQzIC0xOSA2OSAtMjMgMTUyIC0yMyAxNjEgMCAyMjggNDAgMjk4IDE3OSBsMzYgNzAgMSAtNDggYzAgLTI2IDcgLTYxIDE0IC03NgoxOSAtMzYgODAgLTkwIDEyMyAtMTA3IDkwIC0zOCAyMzYgLTMzIDMyMSAxMCA1MSAyNiAxMTIgODYgMTEyIDExMSAwIDggNSAxNQoxMCAxNSA2IDAgMTAgLTcgMTAgLTE1IDAgLTI0IDYxIC04NSAxMTAgLTExMCAxMDMgLTUyIDI0NSAtNTEgMzQ0IDMgNjkgMzgKMTA2IDg3IDExNSAxNTQgbDcgNTMgMzIgLTY1IGMxOCAtMzUgNDUgLTc3IDYwIC05MyA1NiAtNTkgMTY1IC05MyAyNjcgLTg0CjE1NSAxNSAyNTUgOTMgMjU1IDIwMCAwIDM4IC0xODEgNDM2IC0yMDcgNDU1IC0xMyA5IC0yMjggMTIgLTk5MyAxMiAtNzY1IDAKLTk4MCAtMyAtOTkzIC0xMnoiLz4KPHBhdGggZD0iTTE3NjAgMzMyMCBjLTE0IC00IC00OSAtOCAtNzcgLTkgbC01MyAtMSAtMiAtNDIyIC0zIC00MjMgLTgyIC0zCi04MyAtMyAwIC04OSAwIC05MCAxMTAwIDAgMTEwMCAwIDAgOTAgMCA5MCAtODAgMCAtODAgMCAtMiA0MjMgLTMgNDIyIC02NSA2CmMtMzYgMyAtNzcgMTAgLTkyIDE0IGwtMjggNyAtMiAtNDMzIC0zIC00MzQgLTc0NSAwIC03NDUgMCAtMyA0MzMgYy0yIDMzNiAtNQo0MzIgLTE1IDQzMSAtNiAtMSAtMjMgLTUgLTM3IC05eiIvPgo8L2c+Cjwvc3ZnPgo=', // menu icon |
| 180 |
null // priority |
| 181 |
); |
| 182 |
} |
| 183 |
add_action('admin_menu', 'storelocator_add_menu'); |
| 184 |
|
| 185 |
|
| 186 |
function storelocator_settings_template() |
| 187 |
{ |
| 188 |
$storelocator_url = sanitize_text_field(get_option('storelocator_url_input')); |
| 189 |
$storelocator_path = sanitize_text_field(get_option('storelocator_path_input')); |
| 190 |
|
| 191 |
$current_url = sanitize_text_field(get_option("storelocator_url")); |
| 192 |
$current_path = sanitize_text_field(get_option("storelocator_path")); |
| 193 |
|
| 194 |
|
| 195 |
$valid_url_pattern = "/^https:\/\/locatestore.com\/.{3,}$/i"; |
| 196 |
$valid_old_url_pattern = "/^https:\/\/storelocator.site\/.{3,}$/i"; |
| 197 |
$valid_path_pattern = "/^([A-Za-z0-9-_])+$/"; |
| 198 |
|
| 199 |
$is_valid_url = (preg_match($valid_url_pattern, $storelocator_url) == 1) || (preg_match($valid_old_url_pattern, $storelocator_url) == 1); |
| 200 |
$is_valid_path = preg_match($valid_path_pattern, $storelocator_path) == 1; |
| 201 |
$has_path_changed = ($current_path !== $storelocator_path); |
| 202 |
$has_url_changed = ($current_url !== $storelocator_url); |
| 203 |
|
| 204 |
// Defaults to true. Is false only when page generation fails. |
| 205 |
$page_generated = true; |
| 206 |
|
| 207 |
|
| 208 |
if ($is_valid_url && $is_valid_path) { |
| 209 |
try { |
| 210 |
if ($has_path_changed || !storelocator_check_page_existence($current_path)) { |
| 211 |
$page_generated = add_storelocator_page($storelocator_url, $storelocator_path); |
| 212 |
} elseif ($has_url_changed) { |
| 213 |
$page = get_page_by_path($current_path, OBJECT, 'page'); |
| 214 |
$page->post_content = '[storelocator id="' . substr($storelocator_url, 24) . '"]'; |
| 215 |
wp_update_post($page, true); |
| 216 |
update_option("storelocator_url", $storelocator_url); |
| 217 |
} |
| 218 |
} catch (Exception $e) { |
| 219 |
} |
| 220 |
} |
| 221 |
|
| 222 |
$errors = array(); |
| 223 |
|
| 224 |
if (!$is_valid_url && false !== get_option('storelocator_url_input')) array_push($errors, "Invalid store locator URL. Please enter a valid URL of the form <code>https://locatestore.com/<id></code>"); |
| 225 |
if (!$is_valid_path && false !== get_option('storelocator_path_input')) array_push($errors, "Invalid store locator path. Path names can only contain alphabets, digits and the characters <code>-</code> and <code>_</code> "); |
| 226 |
if (!$page_generated) array_push($errors, "Couldn't generate the store locator page. Are you sure the page doesn't already exist ?"); |
| 227 |
|
| 228 |
|
| 229 |
|
| 230 |
require_once(STLR_PLUGIN_DIR . '/settings.php'); |
| 231 |
} |
| 232 |
|
| 233 |
/* |
| 234 |
* Settings template |
| 235 |
*/ |
| 236 |
|
| 237 |
function storelocator_plugin_settings_init() |
| 238 |
{ |
| 239 |
|
| 240 |
add_settings_section('storelocator-settings-section-generate-page', '', '', 'storelocator'); |
| 241 |
|
| 242 |
add_settings_field('storelocator-url-input', 'Store Locator URL', 'storelocator_url_callback', 'storelocator', 'storelocator-settings-section-generate-page'); |
| 243 |
register_setting('storelocator-settings-generate-page', 'storelocator_url_input', array('type' => 'string', 'sanitize_callback' => 'sanitize_text_field')); |
| 244 |
|
| 245 |
add_settings_field('storelocator-path-input', 'Wordpress page URL', 'storelocator_path_callback', 'storelocator', 'storelocator-settings-section-generate-page'); |
| 246 |
register_setting('storelocator-settings-generate-page', 'storelocator_path_input', array('type' => 'string', 'sanitize_callback' => 'sanitize_text_field')); |
| 247 |
} |
| 248 |
add_action('admin_init', 'storelocator_plugin_settings_init'); |
| 249 |
|
| 250 |
|
| 251 |
|
| 252 |
function storelocator_url_callback() |
| 253 |
{ |
| 254 |
$storelocator_url = get_option('storelocator_url_input'); |
| 255 |
|
| 256 |
?> |
| 257 |
<input name="storelocator_url_input" class="regular-text" type="text" style="margin:0" placeholder="https://locatestore.com/<id>" value="<?php echo isset($storelocator_url) ? esc_attr($storelocator_url) : '' ?>" /> |
| 258 |
<?php |
| 259 |
} |
| 260 |
|
| 261 |
function storelocator_path_callback() |
| 262 |
{ |
| 263 |
$storelocator_path = get_option('storelocator_path_input'); |
| 264 |
|
| 265 |
?> <div style="display:flex;flex-wrap:wrap;"> <code style="display:flex;flex-direction:column;justify-content:center;border:1px solid gray;border-right:0;margin:0;"> |
| 266 |
<div><?php echo get_site_url(); ?>/</div> |
| 267 |
</code> |
| 268 |
<input name="storelocator_path_input" class="regular-text" id="storelocator_path_input" type="text" style="max-width:147px;margin:0;" placeholder="storelocator" value="<?php echo esc_attr(get_option('storelocator_path_input') === false ? 'storelocator' : get_option('storelocator_path_input')); ?>" /> |
| 269 |
</div> |
| 270 |
<?php |
| 271 |
} |
| 272 |
|
| 273 |
// For reset. Will be removed in production. |
| 274 |
|
| 275 |
function storelocator_reset_handler() |
| 276 |
{ |
| 277 |
delete_option('storelocator_path_input'); |
| 278 |
delete_option('storelocator_url_input'); |
| 279 |
delete_option('storelocator_url'); |
| 280 |
delete_option('storelocator_path'); |
| 281 |
wp_die(); |
| 282 |
} |
| 283 |
add_action('wp_ajax_reset', 'storelocatorreset_handler'); |
| 284 |
|
| 285 |
|
| 286 |
|
| 287 |
|
| 288 |
function storelocator_admin_enqueue_scripts() |
| 289 |
{ |
| 290 |
require_once(STLR_PLUGIN_DIR . '/feedback-form.php'); |
| 291 |
wp_enqueue_style('storelocator-modal-css', plugin_dir_url(__FILE__) . 'css/modal.css'); |
| 292 |
storelocator_add_feedback_form(); |
| 293 |
} |
| 294 |
add_action('admin_enqueue_scripts', 'storelocator_admin_enqueue_scripts'); |
| 295 |
|
| 296 |
function storelocator_submit_uninstall_reason_action() |
| 297 |
{ |
| 298 |
global $wp_version, $storelocator_active_plugin, $current_user; |
| 299 |
|
| 300 |
if (!isset($_REQUEST['storelocator_ajax_nonce']) || !wp_verify_nonce($_REQUEST['storelocator_ajax_nonce'], 'storelocator_ajax_nonce')) { |
| 301 |
wp_die('Invalid request', 'Invalid request', array('response' => 403)); |
| 302 |
} |
| 303 |
|
| 304 |
if (!current_user_can('manage_options')) { |
| 305 |
wp_die('Unauthorized', 'Unauthorized', array('response' => 403)); |
| 306 |
} |
| 307 |
|
| 308 |
$reason_id = isset($_REQUEST['reason_id']) ? stripcslashes(sanitize_text_field($_REQUEST['reason_id'])) : ''; |
| 309 |
$basename = isset($_REQUEST['plugin']) ? stripcslashes(sanitize_text_field($_REQUEST['plugin'])) : ''; |
| 310 |
|
| 311 |
if (empty($reason_id) || empty($basename)) { |
| 312 |
exit; |
| 313 |
} |
| 314 |
|
| 315 |
$reason_info = isset($_REQUEST['reason_info']) ? stripcslashes(sanitize_textarea_field($_REQUEST['reason_info'])) : ''; |
| 316 |
if (!empty($reason_info)) { |
| 317 |
$reason_info = substr($reason_info, 0, 255); |
| 318 |
} |
| 319 |
$is_anonymous = isset($_REQUEST['is_anonymous']) && 1 == $_REQUEST['is_anonymous']; |
| 320 |
|
| 321 |
$options = array( |
| 322 |
'product' => 'STORELOCATOR_WP_PLUGIN', |
| 323 |
'reason_id' => $reason_id, |
| 324 |
'reason_info' => $reason_info, |
| 325 |
); |
| 326 |
|
| 327 |
if (!$is_anonymous) { |
| 328 |
|
| 329 |
|
| 330 |
$options['url'] = get_site_url(); |
| 331 |
$options['wp_version'] = $wp_version; |
| 332 |
$options['plugin_version'] = $storelocator_active_plugin[$basename]['Version']; |
| 333 |
|
| 334 |
$options['email'] = $current_user->data->user_email; |
| 335 |
} |
| 336 |
|
| 337 |
/* send data */ |
| 338 |
|
| 339 |
wp_remote_post( |
| 340 |
"https://locatestore.com/services/wordpress/churn", |
| 341 |
array( |
| 342 |
'method' => 'POST', |
| 343 |
'body' => $options, |
| 344 |
'timeout' => 15, |
| 345 |
) |
| 346 |
); |
| 347 |
exit; |
| 348 |
} |
| 349 |
add_action('wp_ajax_storelocator_submit_uninstall_reason_action', 'storelocator_submit_uninstall_reason_action'); |
| 350 |
|
| 351 |
|
| 352 |
/** |
| 353 |
* Proper ob_end_flush() for all levels |
| 354 |
* |
| 355 |
* This replaces the WordPress `wp_ob_end_flush_all()` function |
| 356 |
* with a replacement that doesn't cause PHP notices. |
| 357 |
*/ |
| 358 |
remove_action('shutdown', 'wp_ob_end_flush_all', 1); |
| 359 |
add_action('shutdown', function () { |
| 360 |
while (@ob_end_flush()); |
| 361 |
}); |
| 362 |
|