{"id":278012,"date":"2026-02-03T17:16:55","date_gmt":"2026-02-03T17:16:55","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/a1-tools\/"},"modified":"2026-05-26T12:58:20","modified_gmt":"2026-05-26T12:58:20","slug":"a1-tools","status":"publish","type":"plugin","link":"https:\/\/mya.wordpress.org\/plugins\/a1-tools\/","author":23444128,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"2.5.26","stable_tag":"2.5.26","tested":"6.9.4","requires":"5.0","requires_php":"7.4","requires_plugins":null,"header_name":"A1 Tools","header_author":"A1 Chimney","header_description":"Connects your WordPress site to the A1 Tools platform for centralized management of contact information, social media links, and business details.","assets_banners_color":"825b2d","last_updated":"2026-05-26 12:58:20","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/tools.a-1chimney.com","header_author_uri":"https:\/\/a-1chimney.com","rating":0,"author_block_rating":0,"active_installs":100,"downloads":4266,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.10.0":{"tag":"1.10.0","author":"a1tools","date":"2026-03-09 15:03:13"},"1.3.2":{"tag":"1.3.2","author":"a1tools","date":"2026-02-03 17:24:12"},"1.4.0":{"tag":"1.4.0","author":"a1tools","date":"2026-02-03 17:25:21"},"1.4.1":{"tag":"1.4.1","author":"a1tools","date":"2026-02-03 17:40:13"},"1.4.10":{"tag":"1.4.10","author":"a1tools","date":"2026-02-10 16:11:21"},"1.4.11":{"tag":"1.4.11","author":"a1tools","date":"2026-02-16 17:37:16"},"1.4.12":{"tag":"1.4.12","author":"a1tools","date":"2026-02-16 20:06:07"},"1.4.13":{"tag":"1.4.13","author":"a1tools","date":"2026-02-20 17:56:41"},"1.4.14":{"tag":"1.4.14","author":"a1tools","date":"2026-02-23 15:51:48"},"1.4.2":{"tag":"1.4.2","author":"a1tools","date":"2026-02-04 15:57:56"},"1.4.3":{"tag":"1.4.3","author":"a1tools","date":"2026-02-04 20:46:16"},"1.4.4":{"tag":"1.4.4","author":"a1tools","date":"2026-02-09 18:00:42"},"1.4.5":{"tag":"1.4.5","author":"a1tools","date":"2026-02-09 18:16:44"},"1.4.6":{"tag":"1.4.6","author":"a1tools","date":"2026-02-10 15:46:54"},"1.4.7":{"tag":"1.4.7","author":"a1tools","date":"2026-02-10 15:49:54"},"1.4.8":{"tag":"1.4.8","author":"a1tools","date":"2026-02-10 16:00:35"},"1.4.9":{"tag":"1.4.9","author":"a1tools","date":"2026-02-10 16:05:44"},"1.5.0":{"tag":"1.5.0","author":"a1tools","date":"2026-02-23 15:52:56"},"1.5.1":{"tag":"1.5.1","author":"a1tools","date":"2026-02-23 17:03:16"},"1.5.2":{"tag":"1.5.2","author":"a1tools","date":"2026-02-24 17:11:54"},"1.5.3":{"tag":"1.5.3","author":"a1tools","date":"2026-02-24 17:52:42"},"1.5.4":{"tag":"1.5.4","author":"a1tools","date":"2026-02-24 17:58:29"},"1.5.5":{"tag":"1.5.5","author":"a1tools","date":"2026-02-27 15:45:15"},"1.5.6":{"tag":"1.5.6","author":"a1tools","date":"2026-02-27 16:03:10"},"1.5.7":{"tag":"1.5.7","author":"a1tools","date":"2026-02-27 16:18:25"},"1.5.8":{"tag":"1.5.8","author":"a1tools","date":"2026-02-27 16:28:37"},"1.5.9":{"tag":"1.5.9","author":"a1tools","date":"2026-02-27 16:39:05"},"1.6.0":{"tag":"1.6.0","author":"a1tools","date":"2026-02-27 16:53:29"},"1.6.1":{"tag":"1.6.1","author":"a1tools","date":"2026-02-27 17:03:50"},"1.6.2":{"tag":"1.6.2","author":"a1tools","date":"2026-02-27 17:24:39"},"1.6.3":{"tag":"1.6.3","author":"a1tools","date":"2026-02-27 17:45:25"},"1.6.4":{"tag":"1.6.4","author":"a1tools","date":"2026-02-27 20:53:11"},"1.6.5":{"tag":"1.6.5","author":"a1tools","date":"2026-03-02 18:48:04"},"1.6.6":{"tag":"1.6.6","author":"a1tools","date":"2026-03-02 19:35:33"},"1.6.7":{"tag":"1.6.7","author":"a1tools","date":"2026-03-02 19:43:09"},"1.6.8":{"tag":"1.6.8","author":"a1tools","date":"2026-03-02 19:47:07"},"1.6.9":{"tag":"1.6.9","author":"a1tools","date":"2026-03-02 19:54:53"},"1.7.0":{"tag":"1.7.0","author":"a1tools","date":"2026-03-03 13:02:35"},"1.7.1":{"tag":"1.7.1","author":"a1tools","date":"2026-03-03 16:20:27"},"1.7.2":{"tag":"1.7.2","author":"a1tools","date":"2026-03-03 16:29:51"},"1.7.3":{"tag":"1.7.3","author":"a1tools","date":"2026-03-03 16:50:49"},"1.7.4":{"tag":"1.7.4","author":"a1tools","date":"2026-03-03 17:06:19"},"1.7.5":{"tag":"1.7.5","author":"a1tools","date":"2026-03-03 19:51:42"},"1.7.6":{"tag":"1.7.6","author":"a1tools","date":"2026-03-03 20:20:51"},"1.7.7":{"tag":"1.7.7","author":"a1tools","date":"2026-03-03 20:42:19"},"1.7.8":{"tag":"1.7.8","author":"a1tools","date":"2026-03-03 20:53:24"},"1.7.9":{"tag":"1.7.9","author":"a1tools","date":"2026-03-04 17:00:58"},"1.8.0":{"tag":"1.8.0","author":"a1tools","date":"2026-03-04 17:04:28"},"1.8.1":{"tag":"1.8.1","author":"a1tools","date":"2026-03-04 18:14:16"},"1.8.2":{"tag":"1.8.2","author":"a1tools","date":"2026-03-04 18:54:01"},"1.8.3":{"tag":"1.8.3","author":"a1tools","date":"2026-03-04 18:58:06"},"1.8.4":{"tag":"1.8.4","author":"a1tools","date":"2026-03-04 19:29:43"},"1.8.5":{"tag":"1.8.5","author":"a1tools","date":"2026-03-04 19:44:22"},"1.8.6":{"tag":"1.8.6","author":"a1tools","date":"2026-03-04 19:57:58"},"1.9.0":{"tag":"1.9.0","author":"a1tools","date":"2026-03-05 14:27:39"},"1.9.1":{"tag":"1.9.1","author":"a1tools","date":"2026-03-05 14:37:58"},"1.9.2":{"tag":"1.9.2","author":"a1tools","date":"2026-03-06 18:48:23"},"1.9.3":{"tag":"1.9.3","author":"a1tools","date":"2026-03-06 20:19:02"},"1.9.4":{"tag":"1.9.4","author":"a1tools","date":"2026-03-09 14:01:25"},"1.9.5":{"tag":"1.9.5","author":"a1tools","date":"2026-03-09 14:02:55"},"1.9.6":{"tag":"1.9.6","author":"a1tools","date":"2026-03-09 14:14:20"},"1.9.7":{"tag":"1.9.7","author":"a1tools","date":"2026-03-09 14:30:38"},"1.9.8":{"tag":"1.9.8","author":"a1tools","date":"2026-03-09 14:47:56"},"1.9.9":{"tag":"1.9.9","author":"a1tools","date":"2026-03-09 14:55:31"},"2.0.0":{"tag":"2.0.0","author":"a1tools","date":"2026-03-09 15:07:11"},"2.0.1":{"tag":"2.0.1","author":"a1tools","date":"2026-03-09 15:17:22"},"2.0.2":{"tag":"2.0.2","author":"a1tools","date":"2026-03-09 17:02:31"},"2.0.4":{"tag":"2.0.4","author":"a1tools","date":"2026-03-10 12:43:56"},"2.0.5":{"tag":"2.0.5","author":"a1tools","date":"2026-03-13 13:33:23"},"2.0.6":{"tag":"2.0.6","author":"a1tools","date":"2026-03-13 15:13:40"},"2.0.7":{"tag":"2.0.7","author":"a1tools","date":"2026-03-13 16:09:56"},"2.1.5":{"tag":"2.1.5","author":"a1tools","date":"2026-03-23 16:50:50"},"2.1.6":{"tag":"2.1.6","author":"a1tools","date":"2026-03-24 13:41:45"},"2.2.0":{"tag":"2.2.0","author":"a1tools","date":"2026-03-24 17:07:35"},"2.3.0":{"tag":"2.3.0","author":"a1tools","date":"2026-04-01 18:25:34"},"2.4.0":{"tag":"2.4.0","author":"a1tools","date":"2026-04-30 13:51:04"},"2.4.1":{"tag":"2.4.1","author":"a1tools","date":"2026-04-30 13:55:49"},"2.4.2":{"tag":"2.4.2","author":"a1tools","date":"2026-05-06 17:06:44"},"2.4.3":{"tag":"2.4.3","author":"a1tools","date":"2026-05-06 17:50:26"},"2.5.0":{"tag":"2.5.0","author":"a1tools","date":"2026-05-12 17:33:59"},"2.5.10":{"tag":"2.5.10","author":"a1tools","date":"2026-05-13 18:59:22"},"2.5.2":{"tag":"2.5.2","author":"a1tools","date":"2026-05-12 19:48:48"},"2.5.20":{"tag":"2.5.20","author":"a1tools","date":"2026-05-14 13:53:05"},"2.5.26":{"tag":"2.5.26","author":"a1tools","date":"2026-05-26 12:58:20"},"2.5.3":{"tag":"2.5.3","author":"a1tools","date":"2026-05-13 12:45:55"},"2.5.4":{"tag":"2.5.4","author":"a1tools","date":"2026-05-13 13:24:14"},"2.5.5":{"tag":"2.5.5","author":"a1tools","date":"2026-05-13 13:24:14"},"2.5.6":{"tag":"2.5.6","author":"a1tools","date":"2026-05-13 16:24:57"},"2.5.7":{"tag":"2.5.7","author":"a1tools","date":"2026-05-13 16:34:18"},"2.5.8":{"tag":"2.5.8","author":"a1tools","date":"2026-05-13 16:56:36"},"2.5.9":{"tag":"2.5.9","author":"a1tools","date":"2026-05-13 17:06:49"}},"upgrade_notice":{"2.5.2":"<p>Major upgrade for both A1 Web Form and A1 Live Chat widgets. The form widget gains full visual styling controls, a standard-fields repeater (toggle\/relabel\/reorder the 10 CRM fields), a custom-fields repeater (add arbitrary fields of any type), an SMS-consent block with TCPA-friendly legal-trail storage, and an optional &quot;I&#039;m not a robot&quot; checkbox. The chat widget gains pre-chat form configurability and full window-container styling. The CRM client renders all of the new data in the dispatcher detail panel. Requires A1 Tools 5.3.166 or later on the server side (the DB schema adds three new columns).<\/p>","2.5.1":"<p>Critical fix for 2.5.0: the Web Form + Live Chat widgets were reading the wrong WordPress option for the CRM company ID, so on sites that already had the legacy form integration configured, the new widgets rendered with company_id=0 and the chat button was clickable but unresponsive. Update immediately if you&#039;ve placed the new widgets on any live page.<\/p>","2.5.0":"<p>Adds two new Elementor widgets: A1 Web Form (structured lead intake) and A1 Web Chat (live chat with floating + inline modes that share one conversation). Submissions and chats flow into the reworked Web Leads tab in the CRM. Requires A1 Tools 5.3.160 or later on the server side.<\/p>","2.4.3":"<p>Franchise widget now uses stored coordinates correctly and aligns cards in the grid even when titles wrap to a second line.<\/p>","2.4.2":"<p>Fixes a transient TTL bug where &quot;No caching&quot; stored a permanent transient and every other duration was 60\u00d7 too long. Cached payloads (city pages, site variables, franchises, etc.) are flushed automatically on upgrade.<\/p>","1.4.0":"<p>Added auto-updates support for WordPress.<\/p>","1.3.2":"<p>Fixed WordPress.org compliance issues with inline styles and added Google Maps documentation.<\/p>","1.3.1":"<p>Fixed Plugin Check errors. Font Awesome is no longer auto-loaded - ensure your theme includes it or install a Font Awesome plugin.<\/p>","1.3.0":"<p><strong>Important:<\/strong> All shortcode names have changed from <code>[a1_*]<\/code> to <code>[a1tools_*]<\/code>. Please update your shortcodes after upgrading. Example: <code>[a1_var]<\/code> is now <code>[a1tools_var]<\/code>.<\/p>","1.2.0":"<p>New Social Icons Widget with full styling customization. Enhanced shortcode with style, shape, size, and hover effect options.<\/p>","1.1.0":"<p>Added 6 new social media platforms: Pinterest, BBB, Nextdoor, Houzz, Angi, Thumbtack.<\/p>","1.0.9":"<p>Added settings page with configurable cache duration and manual cache clear button. Default cache reduced to 5 minutes.<\/p>","1.0.8":"<p>Added city name, state, and Google Maps location fields with new shortcodes.<\/p>","1.0.0":"<p>Initial release of A1 Tools plugin.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3453106,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3453106,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-772x250.png":{"filename":"banner-772x250.png","revision":3453106,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.10.0","1.3.2","1.4.0","1.4.1","1.4.10","1.4.11","1.4.12","1.4.13","1.4.14","1.4.2","1.4.3","1.4.4","1.4.5","1.4.6","1.4.7","1.4.8","1.4.9","1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.5.8","1.5.9","1.6.0","1.6.1","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.6.9","1.7.0","1.7.1","1.7.2","1.7.3","1.7.4","1.7.5","1.7.6","1.7.7","1.7.8","1.7.9","1.8.0","1.8.1","1.8.2","1.8.3","1.8.4","1.8.5","1.8.6","1.9.0","1.9.1","1.9.2","1.9.3","1.9.4","1.9.5","1.9.6","1.9.7","1.9.8","1.9.9","2.0.0","2.0.1","2.0.2","2.0.4","2.0.5","2.0.6","2.0.7","2.1.5","2.1.6","2.2.0","2.3.0","2.4.0","2.4.1","2.4.2","2.4.3","2.5.0","2.5.10","2.5.2","2.5.20","2.5.26","2.5.3","2.5.4","2.5.5","2.5.6","2.5.7","2.5.8","2.5.9"],"block_files":[],"assets_screenshots":[],"screenshots":{"1":"Site Variables management in A1 Tools dashboard","2":"Using shortcodes in the WordPress editor","3":"Elementor dynamic tags integration"}},"plugin_section":[],"plugin_tags":[25998,1092,2686,357,255157],"plugin_category":[43,51],"plugin_contributors":[255158],"plugin_business_model":[],"class_list":["post-278012","plugin","type-plugin","status-publish","hentry","plugin_tags-business-information","plugin_tags-contact-info","plugin_tags-multi-site","plugin_tags-shortcodes","plugin_tags-site-variables","plugin_category-customization","plugin_category-multisite","plugin_contributors-a1tools","plugin_committers-a1tools"],"banners":{"banner":"https:\/\/ps.w.org\/a1-tools\/assets\/banner-772x250.png?rev=3453106","banner_2x":false,"banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/a1-tools\/assets\/icon-128x128.png?rev=3453106","icon_2x":"https:\/\/ps.w.org\/a1-tools\/assets\/icon-256x256.png?rev=3453106","generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p>A1 Tools connects your WordPress site to the A1 Tools platform, enabling centralized management of business information that can be displayed anywhere on your site using simple shortcodes.<\/p>\n\n<p><strong>Perfect for businesses with multiple websites<\/strong> - update your phone number, address, or social media links once in the A1 Tools dashboard, and all your connected sites update automatically.<\/p>\n\n<h4>Features<\/h4>\n\n<ul>\n<li><strong>Centralized Management<\/strong> - Manage all your site variables from one dashboard<\/li>\n<li><strong>Simple Shortcodes<\/strong> - Display any variable with <code>[a1tools_var key=\"phone_primary\"]<\/code><\/li>\n<li><strong>Address Formatting<\/strong> - Multiple address display formats available<\/li>\n<li><strong>Operating Hours<\/strong> - Display business hours as tables or lists<\/li>\n<li><strong>Social Media Links<\/strong> - Output all social icons with one shortcode or widget<\/li>\n<li><strong>Social Icons Widget<\/strong> - Customizable widget with style, shape, size, and hover effects<\/li>\n<li><strong>Elementor Support<\/strong> - Dynamic tags for Elementor page builder<\/li>\n<li><strong>Performance Optimized<\/strong> - Configurable caching (default 5 minutes)<\/li>\n<li><strong>Developer Friendly<\/strong> - PHP functions available for theme developers<\/li>\n<\/ul>\n\n<h4>Available Variables<\/h4>\n\n<ul>\n<li>Business name, city name, state\/location name, tagline<\/li>\n<li>Google Maps URL for location<\/li>\n<li>Primary and secondary phone numbers<\/li>\n<li>Primary and secondary email addresses<\/li>\n<li>Full address (line 1, line 2, city, state, ZIP, country)<\/li>\n<li>Social media URLs (Facebook, Instagram, YouTube, Twitter, LinkedIn, TikTok, Yelp, Google Business)<\/li>\n<li>Operating hours for each day of the week<\/li>\n<\/ul>\n\n<h4>Shortcode Examples<\/h4>\n\n<pre><code>[a1tools_var key=\"phone_primary\"] - Display primary phone number\n\n[a1tools_var key=\"facebook_url\" link=\"true\"] - Display Facebook URL as clickable link\n\n[a1tools_address format=\"full\"] - Display complete formatted address (multi-line)\n\n[a1tools_full_address] - Display full address in a single line (e.g., \"123 Main St, Suite 101, Miami, FL 30001\")\n\n[a1tools_hours format=\"table\"] - Display operating hours as a table\n\n[a1tools_social_links] - Display all configured social media icons\n\n[a1tools_social_links style=\"default\" shape=\"circle\" size=\"50\"] - Styled social icons\n\n[a1tools_city_name] - Display the city name for geo-targeting\n\n[a1tools_state] - Display the state name for geo-targeting\n\n[a1tools_google_map type=\"link\"] - Display Google Maps link\n\n[a1tools_google_map type=\"embed\" width=\"100%\" height=\"400\"] - Embed Google Maps iframe\n<\/code><\/pre>\n\n<h4>Requirements<\/h4>\n\n<ul>\n<li>Your WordPress site must be registered in the A1 Tools platform<\/li>\n<li>An active A1 Tools account with site variables configured<\/li>\n<li>Font Awesome 6 for social icons (most themes include this; if not, use a Font Awesome plugin)<\/li>\n<\/ul>\n\n<h3>External Services<\/h3>\n\n<p>This plugin relies on a third-party service to retrieve site variables (business information, contact details, social media URLs, etc.) that you configure in the A1 Tools dashboard.<\/p>\n\n<h4>A1 Tools API<\/h4>\n\n<p><strong>What it is:<\/strong> A1 Tools is a business management platform operated by A1 Chimney Service that allows businesses to centrally manage their contact information, addresses, operating hours, and social media links across multiple websites.<\/p>\n\n<p><strong>What data is sent:<\/strong> When this plugin is activated and the site loads (or when a shortcode is used), the plugin sends your WordPress site URL to the A1 Tools API to retrieve the site variables you have configured for that specific site.<\/p>\n\n<p><strong>When data is sent:<\/strong>\n- When a page containing A1 Tools shortcodes is loaded\n- When the plugin's admin settings page is accessed\n- When the cache expires and fresh data is needed (configurable, default 5 minutes)<\/p>\n\n<p><strong>What data is received:<\/strong> The plugin receives only the site variables you have configured in your A1 Tools dashboard, such as business name, phone numbers, email addresses, physical address, operating hours, and social media URLs.<\/p>\n\n<p><strong>Service Provider:<\/strong> A1 Chimney Service\n<strong>API Endpoint:<\/strong> https:\/\/tools.a-1chimney.com\/api\/website_variables.php\n<strong>Terms of Service:<\/strong> https:\/\/a-1chimney.com\/terms-of-service\/\n<strong>Privacy Policy:<\/strong> https:\/\/a-1chimney.com\/privacy-policy\/<\/p>\n\n<h4>Google Maps Embed API<\/h4>\n\n<p><strong>What it is:<\/strong> Google Maps is a web mapping service developed by Google. This plugin can embed Google Maps iframes to display your business location.<\/p>\n\n<p><strong>What data is sent:<\/strong> When using the <code>[a1tools_google_map type=\"embed\"]<\/code> shortcode, your visitor's browser loads an iframe from Google Maps containing your business address or place ID that you configured in the A1 Tools dashboard.<\/p>\n\n<p><strong>When data is sent:<\/strong>\n- Only when a page containing the <code>[a1tools_google_map type=\"embed\"]<\/code> shortcode is loaded\n- The embed is loaded client-side by the visitor's browser directly from Google<\/p>\n\n<p><strong>What data is received:<\/strong> Google Maps returns the embedded map showing your business location. Google may collect visitor data according to their privacy policy.<\/p>\n\n<p><strong>Service Provider:<\/strong> Google LLC\n<strong>Embed URL:<\/strong> https:\/\/www.google.com\/maps\/embed\/v1\/place\n<strong>Terms of Service:<\/strong> https:\/\/www.google.com\/intl\/en_us\/help\/terms_maps\/\n<strong>Privacy Policy:<\/strong> https:\/\/policies.google.com\/privacy<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>a1-tools<\/code> folder to the <code>\/wp-content\/plugins\/<\/code> directory<\/li>\n<li>Activate the plugin through the 'Plugins' menu in WordPress<\/li>\n<li>Ensure your site is registered in your A1 Tools dashboard under Integrations &gt; WordPress Sites<\/li>\n<li>Configure your site variables in A1 Tools under Marketing Tools &gt; Web Management &gt; Site Variables<\/li>\n<li>Use the shortcodes in your pages, posts, or widgets<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"how%20do%20i%20get%20an%20a1%20tools%20account%3F\"><h3>How do I get an A1 Tools account?<\/h3><\/dt>\n<dd><p>A1 Tools is a business management platform. Contact A1 Chimney Service for access information.<\/p><\/dd>\n<dt id=\"why%20are%20my%20variables%20not%20showing%3F\"><h3>Why are my variables not showing?<\/h3><\/dt>\n<dd><ol>\n<li>Verify your site URL in A1 Tools matches your WordPress site URL exactly (including https:\/\/)<\/li>\n<li>Check that you have saved variables for this site in A1 Tools<\/li>\n<li>Go to A1 Tools in your admin menu and click \"Clear Cache Now\" to fetch fresh data<\/li>\n<\/ol><\/dd>\n<dt id=\"how%20often%20do%20variables%20update%3F\"><h3>How often do variables update?<\/h3><\/dt>\n<dd><p>Variables are cached based on your settings (default 5 minutes). You can configure the cache duration or disable caching entirely in the A1 Tools settings page. To force an immediate update, use the \"Clear Cache Now\" button on the settings page.<\/p><\/dd>\n<dt id=\"can%20i%20use%20this%20with%20elementor%3F\"><h3>Can I use this with Elementor?<\/h3><\/dt>\n<dd><p>Yes! The plugin registers dynamic tags that appear under the \"A1 Tools\" group in Elementor. You can also use the Shortcode widget with any of the available shortcodes.<\/p><\/dd>\n<dt id=\"is%20this%20plugin%20free%3F\"><h3>Is this plugin free?<\/h3><\/dt>\n<dd><p>The plugin is free and open source. However, it requires an A1 Tools account to function, as it retrieves data from the A1 Tools platform.<\/p><\/dd>\n<dt id=\"what%20data%20does%20this%20plugin%20send%20to%20external%20services%3F\"><h3>What data does this plugin send to external services?<\/h3><\/dt>\n<dd><p>See the \"External Services\" section above for complete details. In summary, the plugin sends your site URL to the A1 Tools API to retrieve the site variables you have configured. No personal user data from your visitors is collected or transmitted.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>2.5.26<\/h4>\n\n<ul>\n<li>New: real-time cache invalidation via webhook (Layer C of the 2026-05-25 outage hardening pass). On activation\/upgrade the plugin now generates a per-site 256-bit HMAC secret, registers it with the central A1 Tools server, and exposes <code>POST \/wp-json\/a1-tools\/v1\/invalidate-cache<\/code>. When an admin saves any variable \/ store \/ franchise \/ city page \/ service \/ review \/ before-after \/ team member \/ FAQ \/ SEO location on the central server, that server pushes an HMAC-signed ping here within milliseconds and the matching transient is cleared on the spot. End result: variable edits propagate to live customer pages in seconds instead of waiting up to 30 minutes for the cache to expire. The 30-min cache TTL + 5-min CDN cache remain in place as the safety net if a ping is missed.<\/li>\n<\/ul>\n\n<h4>2.5.25<\/h4>\n\n<ul>\n<li>Performance \/ reliability: hardening pass on the data-fetch layer triggered by the 2026-05-25 central-server outage. The outage was caused by sites with caching disabled hammering the upstream API on every page render until the central PHP-FPM pool exhausted. Three coordinated changes here:\n\n<ul>\n<li>Stampede protection in <code>a1tools_fetch_api_data()<\/code>: when N concurrent Apache workers on the same site hit an expired transient at the same instant, a short lock transient now lets exactly one worker refill the cache while the others return the stale fallback. Lock TTL (20s) &gt; <code>wp_remote_get<\/code> timeout (15s) so a dying request can't hold the lock indefinitely.<\/li>\n<li>Per-request memoization via a static array in the same function. Pages with many shortcodes\/widgets that each call into the data layer (a header with 8 <code>[a1tools_var]<\/code> tags, for example) now hit the transient backend exactly once per page render instead of once per shortcode.<\/li>\n<li>The \"No caching (always fresh)\" and \"1 minute\" options were removed from the Cache Duration dropdown. They were the root cause of the upstream overload \u2014 a few sites with caching disabled generated enough volume to bring the central server down. Default cache duration bumped 5 min \u2192 30 min. A migration coerces any existing <code>a1tools_cache_expiry = 0<\/code> to the new default on plugin upgrade.<\/li>\n<\/ul><\/li>\n<\/ul>\n\n<h4>2.5.24<\/h4>\n\n<ul>\n<li>New: Web Form widget \u2192 Title &amp; Subtitle style section gains per-breakpoint Alignment controls (Desktop \/ Tablet \/ Mobile, with Left \/ Center \/ Right options). Lets editors center the hero title on desktop while keeping it left-aligned on mobile (or any combination). Default empty inherits whatever the parent column dictates, so existing forms render unchanged.<\/li>\n<\/ul>\n\n<h4>2.5.23<\/h4>\n\n<ul>\n<li>New: Web Form widget \u2192 Container style section gains a \"Border color (hover)\" control, sitting directly below the normal Border group. Pairs a fade transition (<code>border-color 180ms ease<\/code>) on the wrap so the swap is smooth instead of snapping. Matches the existing Submit Button's Normal \/ Hover pattern so editors expecting hover states on bordered elements get a consistent experience.<\/li>\n<\/ul>\n\n<h4>2.5.22<\/h4>\n\n<ul>\n<li>Fix: Web Form widget \u2014 Submit Button \"Full width\" alignment now actually renders full-width. The 2.5.2 implementation used <code>justify-content: stretch<\/code> (which isn't a valid value for <code>justify-content<\/code> \u2014 only <code>align-items<\/code>\/<code>align-content<\/code> accept it, so browsers silently dropped it and fell back to <code>flex-end<\/code>) paired with a <code>[data-align=\"full\"]<\/code> attribute selector for the <code>width: 100%<\/code> rule (which never matched in Elementor's live editor preview because the editor only re-injects CSS, not HTML, until you save + reload). Both alignment and width now flow through CSS variables (<code>--submit-justify<\/code>, <code>--submit-width<\/code>) that Elementor emits on the <code>.a1tools-web-form__actions<\/code> wrapper, so the button responds in real time to the alignment toggle in the editor sidebar AND on the published page. Left \/ Center \/ Right also pick up the same variable plumbing for consistency.<\/li>\n<\/ul>\n\n<h4>2.5.21<\/h4>\n\n<ul>\n<li>New: Web Form widget \u2014 Custom Fields type whitelist gains a \"Time\" option (renders an <code>&lt;input type=\"time\"&gt;<\/code>). Useful alongside the existing Date type for appointment-preference or booking-window forms. Server-side validator in the A1 Tools CRM API extended in lock-step so the new type round-trips into <code>crm_web_form_submissions.custom_fields<\/code> cleanly.<\/li>\n<li>New: Web Form widget \u2014 per-field responsive Column Width control on BOTH Standard Fields and Custom Fields repeaters. Each row exposes a Desktop \/ Tablet \/ Mobile dropdown (100% \/ 75% \/ 66% \/ 50% \/ 33% \/ 25%) so editors can lay out e.g. First Name + Last Name side-by-side on desktop while remaining stacked on mobile. Tablet inherits Desktop, Mobile inherits Tablet, unless explicitly set. Under the hood the form switches from single-column flex to a 12-column CSS grid; existing forms render unchanged (each field defaults to full-width).<\/li>\n<\/ul>\n\n<h4>2.5.20<\/h4>\n\n<ul>\n<li>Maintenance: WP.org publish baseline catch-up. The wordpress.org SVN tags had drifted behind by 9 versions (2.5.11\u20132.5.19 were never tagged on the public plugin marketplace; sites updating from wordpress.org therefore still saw 2.5.10 as the latest available build). 2.5.20 republishes the current trunk so customer sites pick up everything between 2.5.10 and current in a single update. No behavioral changes vs 2.5.18 \u2014 see prior entries below for what shipped over the catch-up window (Web Leads \/ Live Chat overhaul, multi-tenant CRM scoping, franchise grid stabilization, etc.).<\/li>\n<\/ul>\n\n<h4>2.5.18<\/h4>\n\n<ul>\n<li>Improve: Franchise grid shortcode \u2014 when a franchise card has no street\/city\/state\/zip fields populated, the embedded map now falls back to <code>lat,lng<\/code> coordinates instead of using the franchise name as the search query (which would otherwise resolve to an unrelated \"A1 Chimney\" listing in another state). If neither address nor coordinates exist, the map is omitted entirely.<\/li>\n<li>Fix: Franchise grid card layout \u2014 cards become flex columns with <code>min-height: 2.5em<\/code> + <code>line-height: 1.25<\/code> on the title and <code>margin-top: auto<\/code> on the directions link. Stops the map iframe from jumping rows when one card has a single-line title and the neighbor has two lines.<\/li>\n<\/ul>\n\n<h4>2.5.17<\/h4>\n\n<ul>\n<li>Tweak: Live Chat default greeting copy \u2014 \"Hi! Thanks for reaching out \u2014 a team member will be with you in just a moment.\" \u2192 \"Hi! Thanks for reaching out, a team member will be with you as soon as possible.\" Dropped the em-dash for a comma-joined single thought, and softened the \"moment\" promise into \"as soon as possible.\" Per-widget override still wins \u2014 existing Elementor placements that already saved the old wording need to be edited (or cleared) in the widget admin to pick up the new default. A one-time migration on plugin activation also rewrites any saved Elementor postmeta values still carrying the old phrase to the new one.<\/li>\n<\/ul>\n\n<h4>2.5.16<\/h4>\n\n<ul>\n<li>Fix: Live Chat pre-chat form \u2014 name + phone-or-email fields rendered with the customer site's theme-default \"dark form\" styling (black background, white text) instead of the plugin's intended white-with-dark-text. Some Astra \/ Hello-Elementor child themes ship a <code>body input { background: #000; color: #fff }<\/code> rule that loads after the plugin's stylesheet and wins on cascade order. Hardened <code>.a1tools-web-chat-window__prechat input, textarea<\/code> with <code>!important<\/code> on background + color + border to defeat the theme cascade (same pattern as the 2.5.7 SVG-fill fix). The new \"How can we help?\" textarea was visually correct out of the box in 2.5.15 only because no theme had targeted a bare <code>textarea<\/code> element yet; it's now under the same shield for safety.<\/li>\n<\/ul>\n\n<h4>2.5.15<\/h4>\n\n<ul>\n<li>New: Live Chat pre-chat form \u2014 added a third field, \"How can we help?\" (label + required-flag + placeholder all configurable in the Elementor widget admin). The visitor types their first question or message before starting the chat. Server-side: the question is stored as the first <code>sender='visitor'<\/code> message in the conversation, between \"Chat started by \u2026\" and the auto-greeting, so the flow reads as: (1) system header, (2) visitor's question, (3) auto-reply \/ \"team member will be with you shortly.\" Dispatchers viewing the chat in the CRM Web Leads inbox see the visitor's intent immediately instead of having to ask \"what brings you here?\" once they pick up.<\/li>\n<\/ul>\n\n<h4>2.5.14<\/h4>\n\n<ul>\n<li>Improve: Live Chat \u2014 pre-chat greeting now renders as a real left-aligned chat bubble (matching the look of an actual dispatcher message), instead of the centered italic system notice 2.5.12 used. The visitor's eye reads it immediately as \"someone is replying\" rather than as session metadata. The hairline divider between session metadata and the conversation now lands directly under \"Chat started by \u2026\", with the greeting on the <em>conversation<\/em> side of the divider where it belongs.<\/li>\n<li>New: server-side <code>auto<\/code> sender type added to <code>crm_web_chat_messages.sender<\/code> ENUM. Distinct from <code>system<\/code> so the dispatcher's CRM inbox and the visitor widget can both render auto-replies as chat bubbles while keeping the centered-italic styling exclusively for true session metadata (\"Chat started by \u2026\", \"Chat marked resolved by \u2026\", etc.).<\/li>\n<\/ul>\n\n<h4>2.5.13<\/h4>\n\n<ul>\n<li>Fix: Live Chat \u2014 business-hours cutoff was 4:00 PM in 2.5.12 but should have been 5:00 PM Florida time. Widget now renders 9:00 AM \u2013 5:00 PM (<code>$hour &gt;= 9 &amp;&amp; $hour &lt; 17<\/code>).<\/li>\n<\/ul>\n\n<h4>2.5.12<\/h4>\n\n<ul>\n<li>Improve: Live Chat \u2014 pre-chat greeting (the \"Hi! Thanks for reaching out \u2014 a team member will be with you in just a moment.\" message configured per-widget) is now persisted on the server as a system message immediately after \"Chat started by \u2026\", instead of being a local-only bubble in the visitor's browser. Two consequences: (1) the greeting always renders AFTER the session header in proper chronological order (the 2.5.10 local bubble raced the polling loop and frequently won, putting the greeting ABOVE \"Chat started by \u2026\"); (2) dispatchers viewing the chat in the CRM Web Leads inbox now see the exact same flow the visitor sees \u2014 previously the dispatcher saw only \"Chat started by \u2026\" and had no idea what auto-reply the visitor had already received.<\/li>\n<li>New: Live Chat \u2014 business-hours gate. The chat widget now only renders between 9:00 AM and 5:00 PM Florida time (America\/New_York, automatically respecting EST\/EDT DST). Outside those hours visitors don't see the chat option at all (no button, no JS init, no <code>.a1tools-web-chat<\/code> placement), so they fall through to the contact form \/ phone instead of pinging an unstaffed inbox. The cutoff is hard-coded for now \u2014 if you need configurable hours per widget, let us know.<\/li>\n<\/ul>\n\n<h4>2.5.11<\/h4>\n\n<ul>\n<li>Cache-bust re-tag of 2.5.10. No functional changes \u2014 the 2.5.10 fixes (greeting-as-dispatcher-bubble, system-message divider, raised floating button) were correct, but Elementor's \"Improved Asset Loading\" experiment had baked the widget's CSS\/JS inline into per-page generated CSS files at the time those pages were first rendered (under 2.5.9), and those cached snapshots weren't regenerating when the plugin updated. Bumping the version forces a new <code>?ver=<\/code> query string on the asset URLs so browsers fetch the new JS\/CSS, alongside an Elementor CSS-cache flush + SiteGround dynamic-cache purge as part of this deploy.<\/li>\n<\/ul>\n\n<h4>2.5.10<\/h4>\n\n<ul>\n<li>Improve: Live Chat \u2014 pre-chat greeting (the \"Hi! Thanks for reaching out \u2014 a team member will be with you in just a moment.\" message configured per-widget) now renders as a dispatcher-style chat bubble instead of an italic system notice at the top of the thread. Reads as an instant auto-reply so the visitor gets immediate \"someone heard me\" feedback before a real dispatcher picks up.<\/li>\n<li>Improve: Live Chat \u2014 visual divider between system metadata lines (like \"Chat started by Visitor (5551234567)\") and the actual conversation. A faded hairline now draws automatically at the boundary between the last system message and the first real message, separating session metadata from the conversation flow without adding manual UI clutter.<\/li>\n<li>Improve: Live Chat \u2014 floating button raised 50px on bottom-corner placements (br\/bl) from <code>bottom: 20px<\/code> to <code>bottom: 70px<\/code> so it doesn't collide with sticky footers, floating CTAs, or mobile safe-area indicators. Top-corner placements unchanged.<\/li>\n<\/ul>\n\n<h4>2.5.9<\/h4>\n\n<ul>\n<li>Fix: A1 City List widget on customer sites only listed pages by authors with \"city\" in their display name (e.g., \"Single City\", \"Location City\"), silently dropping pages authored by \"Single Location\" users. Mirrors the same broadening that landed server-side in 2.5.8 for the CRM Cities tab: <code>a1tools_get_wp_city_pages()<\/code> now matches <code>display_name LIKE '%city%' OR LIKE '%location%'<\/code> (still excluding <code>%seo%<\/code>). Sites that use the <code>Single Location<\/code> naming convention (e.g., ct.a-1chimney.com) will now surface ~all location pages alongside city pages. The page-type detection further down keys off \"location\" in the name, so the <code>location_city<\/code> vs <code>single_city<\/code> classification still works correctly regardless of which keyword matched.<\/li>\n<\/ul>\n\n<h4>2.5.8<\/h4>\n\n<ul>\n<li>Fix: Live Chat \u2014 \"Jose Demasi is helping you\" banner persisted even after the dispatcher resolved\/abandoned the session. The 2.5.7 fix correctly hid the banner inside <code>handleSessionClosed()<\/code>, but the same poll payload that signals the close ALSO carries the <code>attended_by_dispatcher<\/code> field (the server keeps it populated on the row for record-keeping), and the subsequent <code>updateAttended()<\/code> call would immediately un-hide the banner that was just hidden. Now <code>updateAttended()<\/code> is gated on <code>session_status === 'open'<\/code>; when the session is closed the banner is forcibly cleared and stays cleared.<\/li>\n<li>Fix: Live Chat \u2014 visitor-facing strings now show the dispatcher's <strong>first name only<\/strong> (\"Jose is helping you\" \/ \"Jose \u2022 12:42 PM\") instead of the full \"Jose Demasi\". The CRM dispatcher view (Flutter app) is unchanged \u2014 it keeps the full display name. Applied to the attended-by banner AND every dispatcher message bubble's meta line.<\/li>\n<li>Fix: Live Chat \u2014 \"Start chat\" (pre-chat submit) and \"Start a new chat\" (resolved-state restart) rendered as thin yellow strips instead of proper buttons. Caused by 2.5.7's chat-window button reset (specificity 0,1,1) winning over those buttons' specific style rules (specificity 0,1,0) and zeroing their padding. Fixed by adding the <code>.a1tools-web-chat-window<\/code> parent prefix to both selectors so they reach specificity 0,2,0 and keep their padding + shadow + transitions. Restart button is now a primary orange button (was an outlined secondary) to match the call-to-action role.<\/li>\n<\/ul>\n\n<h4>2.5.7<\/h4>\n\n<ul>\n<li>Fix: Live Chat \u2014 pre-chat form, active thread, and \"chat closed\" banner all rendered stacked on top of each other on themes that override <code>[hidden]<\/code> (most do \u2014 <code>div { display: block }<\/code> etc. at higher specificity than HTML5's user-agent default). Visitor saw the \"Start chat\" button alongside an active conversation and the \"Start a new chat\" button at the same time. Fixed with a scoped <code>.a1tools-web-chat-window [hidden] { display: none !important }<\/code> rule so theme cascade can't defeat the state machine.<\/li>\n<li>Fix: Live Chat \u2014 close \/ attach \/ send icons rendered as solid yellow blocks (again) on sites where the theme also overrides <code>svg { fill: &lt;brand-color&gt; }<\/code> at higher specificity than our scoped <code>fill: currentColor<\/code>. Same-color-on-same-color = invisible icon. Strengthened the chat-window SVG rule with <code>!important<\/code> on <code>fill: currentColor<\/code>, <code>visibility<\/code>, and <code>opacity<\/code>.<\/li>\n<li>Fix: Live Chat \u2014 stale \"Jose is helping you\" banner stayed visible after the dispatcher resolved\/abandoned the session. <code>handleSessionClosed()<\/code> now clears the attended-by banner + typing-dots + <code>state.attendedBy<\/code> alongside flipping the resolved view on. The banner re-renders fresh if the visitor starts a new chat that gets attended.<\/li>\n<\/ul>\n\n<h4>2.5.6<\/h4>\n\n<ul>\n<li>Fix: Live Chat \/ Web Form \u2014 widgets rendered with <code>data-company-id=\"0\"<\/code> on every site that hadn't been manually configured, so the chat button did nothing and form submissions rejected. The CRM company id is now <strong>auto-resolved from the server<\/strong> when the WP-Admin option is still at the default 0. The plugin hits <code>tools.a-1chimney.com\/api\/website_variables.php?action=public&amp;url=&lt;site_url&gt;<\/code> (which already does the <code>wordpress_sites \u2192 wordpress_groups.company_id<\/code> JOIN), reads the returned <code>company_id<\/code>, and persists it back into the option so subsequent renders don't re-fetch. Admin overrides win \u2014 manual edits in the settings UI are never overwritten. The 41 customer sites running 2.5.5 were back-filled directly via wp-cli during this release; new installs from here on configure themselves on first widget render.<\/li>\n<\/ul>\n\n<h4>2.5.5<\/h4>\n\n<ul>\n<li>Fix: Live Chat \u2014 page reload wiped the existing thread. The visitor saw an empty chat (or worse, the pre-chat form re-appeared with a \"Start a new chat\" button) even though the session was still open server-side. Caused by the bootstrap restoring <code>since_message_id<\/code> from <code>localStorage<\/code> and then polling <code>id &gt; since_id<\/code>, which returned no rows because the watermark already covered every prior message. Resolved by forcing <code>since_message_id=0<\/code> + an in-memory <code>isInitialRestore<\/code> flag on the page-load fetch so the entire thread reloads \u2014 including the visitor's own bubbles (which were skipped during live polling as \"already optimistically rendered\" but ARE legitimately needed after a reload because the optimistic DOM is gone).<\/li>\n<li>Fix: Live Chat \u2014 empty white bar with a non-functional X above the composer. The attachment-preview wrapper used <code>display: flex<\/code> which beat HTML5's default <code>[hidden] { display: none }<\/code> when site themes overrode the hidden default. Added explicit <code>[hidden] !important<\/code> rule.<\/li>\n<li>Fix: Live Chat \u2014 visitor saw the raw dispatcher username (\"Aghaji2025 is helping you\" \/ \"Aghaji2025 \u2022 1:04 PM\") instead of the dispatcher's real name. Server now resolves usernames to <code>first_name last_name<\/code> via a batched lookup against <code>a1tools_users<\/code> in poll \/ resume \/ list-sessions \/ session-messages responses (single SQL query per request, no N+1). Visitor JS prefers the new <code>sender_display_name<\/code> \/ <code>attended_by_dispatcher_display_name<\/code> fields and falls back to username transparently.<\/li>\n<\/ul>\n\n<h4>2.5.4<\/h4>\n\n<ul>\n<li>Re-tag of 2.5.3 to force the WordPress.org plugin directory to re-index after the 2.5.3 hotfix landed. No functional changes between 2.5.3 and 2.5.4 \u2014 the chat-window icon visibility fix and cross-origin CORS fix from 2.5.3 are the substantive changes shipping here. Bumping the version forces customer-site auto-updaters to re-pull cleanly.<\/li>\n<\/ul>\n\n<h4>2.5.3<\/h4>\n\n<ul>\n<li>Fix: Live Chat \u2014 missing icons in the chat window (close \/ attach \/ send buttons rendered as solid color blocks with the SVG icons invisible). Caused by site themes overriding the chat window's button styles (e.g. theme rule <code>button { background: gold; color: gold; }<\/code> made every SVG with <code>fill: currentColor<\/code> disappear into the same-coloured background). Resolved by introducing a chat-window-scoped CSS reset and bumping selector specificity on the close \/ attach \/ send buttons so theme styles can't leak in.<\/li>\n<li>Fix: Live Chat \/ Web Form \u2014 cross-origin requests blocked by the server's CORS allowlist on every customer site except <code>a-1chimney.com<\/code> (and its <code>www.<\/code> subdomain). The widget POSTs to <code>tools.a-1chimney.com\/api\/web_chat.php<\/code> \/ <code>web_forms.php<\/code> from <code>dc.a-1chimney.com<\/code>, <code>ct.a-1chimney.com<\/code>, and ~40 other A1 Chimney subdomain sites, plus the 14 portfolio sites; none were on the static allowlist, so the browser refused to read responses \u2192 \"Network error \u2014 please try again\" on Start Chat \/ form submit. Resolved by introducing <code>handlePublicWidgetCors()<\/code> in <code>api\/config.php<\/code> (echoes any origin with <code>Access-Control-Allow-Credentials: false<\/code>) and opting the two widget endpoints into permissive CORS via a new <code>$publicCors<\/code> parameter on <code>initApi()<\/code>. Safety preserved by the HMAC form-secret (Web Form) and visitor_token + Bearer-token authentication (Web Chat) \u2014 neither relies on cookies, so allowing any origin doesn't open a CSRF surface.<\/li>\n<\/ul>\n\n<h4>2.5.2<\/h4>\n\n<ul>\n<li>New: Web Form widget \u2014 full visual styling controls. Container background + border (style\/width\/color) + border radius + box-shadow + padding. Label typography (family\/size\/weight\/color). Input typography + separate placeholder vs user-typed colors + input background + input border + focus border. Required-asterisk color (default red <code>#dc2626<\/code>) + weight. Submit button alignment (left \/ center \/ right \/ full-width).<\/li>\n<li>New: Web Form widget \u2014 Standard Fields repeater. Toggle, relabel, reorder the ten CRM lead-intake fields (First Name, Last Name, Phone, Email, Address, City, State, Zip, Commentary, Preferred Contact). Hidden fields are no longer sent to the server; the server in turn accepts missing fields and defaults them to empty string (soft-requires at least one of phone \/ email \/ commentary so a contentless submission still rejects).<\/li>\n<li>New: Web Form widget \u2014 Custom Fields repeater. Add arbitrary user-defined fields beyond the ten standard ones. Types: text, textarea, email, tel, url, number, date, dropdown (select), checkbox, radio. Per-field label \/ placeholder \/ help text \/ required-flag \/ options. Stored as a self-describing JSON array on the submission row (new <code>custom_fields<\/code> column on <code>crm_web_form_submissions<\/code>) so each submission carries its own field schema and the CRM detail card renders the exact labels the visitor saw at submit time.<\/li>\n<li>New: Web Form widget \u2014 SMS Consent section. Toggle-gated; WYSIWYG consent text with <code>{{privacy_url}}<\/code>, <code>{{sms_terms_url}}<\/code>, <code>{{help_phone}}<\/code> placeholders auto-resolved to site-relative defaults when the URL fields are left blank. Optional Help phone field. \"Require check to submit\" toggle. The verbatim consent text the visitor saw plus the checked\/unchecked flag are stored as first-class columns (<code>consent_text<\/code>, <code>consent_checked<\/code>) for downstream Twilio \/ SMS pipeline TCPA-compliance audits.<\/li>\n<li>New: Web Form widget \u2014 Optional \"I'm not a robot\" checkbox gate. Simple in-form click-through (no external CAPTCHA service); blocks naive automation alongside the existing honeypot + minimum-fill-time guards.<\/li>\n<li>New: Web Chat widget \u2014 Pre-chat Form section. Configurable label + required-flag for the Name and Phone-or-Email fields, plus the Start-chat button label.<\/li>\n<li>New: Web Chat widget \u2014 Window container styling. Background color, border color\/width, border radius, box-shadow, plus body typography (matches the Web Form widget's level of control).<\/li>\n<\/ul>\n\n<h4>2.5.1<\/h4>\n\n<ul>\n<li>Fixed: A1 Web Form and A1 Web Chat widgets read the wrong WordPress option for the CRM company ID (<code>a1tools_form_company_id<\/code> instead of <code>a1tools_form_crm_company_id<\/code> \u2014 the latter being the option the existing admin UI + form pipeline have always used). On any site that had configured the legacy contact-form CRM integration, both new widgets rendered with <code>data-company-id=\"0\"<\/code>. The chat widget's JS module bailed at the <code>if (!config.companyId)<\/code> check, leaving the floating Live Chat button visible but completely unresponsive \u2014 clicks did nothing, no errors in the WP admin, just silence. Both widgets now read the same option the rest of the plugin uses.<\/li>\n<li>Improved: \"CRM Integration\" section heading and description in the admin Settings page now explicitly mention BOTH the contact form AND the Live Chat widget. Same Company ID drives both; setting it to 0 disables both. Visual styling for the chat widget (button color, padding, placement, dispatcher\/visitor bubble colors) lives in Elementor's edit panel, not in WP admin \u2014 the admin page only carries the global Company ID + form secret.<\/li>\n<\/ul>\n\n<h4>2.5.0<\/h4>\n\n<ul>\n<li>New: A1 Web Form Elementor widget \u2014 structured lead intake form (First name, Last name, Phone, Email, Address, City, State (searchable US-state dropdown with \"Outside the US\" fallback), Zip, Tell-us-about-your-project commentary, Preferred contact method radio). All fields required. Honeypot + minimum-fill-time anti-spam. Submissions land in <code>crm_web_form_submissions<\/code> and surface in the CRM's reworked Web Leads tab.<\/li>\n<li>New: A1 Web Chat Elementor widget \u2014 live chat with both floating-corner and inline placements. Multiple placements on the same page share one conversation via a <code>visitor_token<\/code> stored in <code>localStorage<\/code>. Pre-chat captures Name + Phone-or-Email before the conversation opens so the CRM has a usable identity even if the visitor closes the tab mid-conversation. Image attachments (JPEG\/PNG\/GIF\/WEBP, 5 MB cap), typing indicator, read receipts, and an \"Attended by X\" indicator showing which dispatcher is currently working the thread. Chats only close when a CRM dispatcher resolves or abandons them \u2014 page reload, browser close, or device switch all restore the open session.<\/li>\n<li>New: Public REST proxy <code>\/wp-json\/a1-tools\/v1\/submit-web-form<\/code> that signs the form payload server-side (HMAC-SHA256) and forwards to <code>tools.a-1chimney.com\/api\/web_forms.php<\/code>. Visitor never sees the shared secret.<\/li>\n<li>Performance: Web Form + Web Chat assets are versioned with <code>A1TOOLS_VERSION<\/code> so a plugin upgrade busts the browser cache cleanly. Scripts only mount when a corresponding placement exists in the DOM.<\/li>\n<\/ul>\n\n<h4>2.4.3<\/h4>\n\n<ul>\n<li>Fixed: A1 Franchise Location widget rendered the wrong map for franchises that had no street address \u2014 Google's iframe embed was being asked to resolve the franchise <em>name<\/em> alone (e.g. <code>q=A1+Chimney+-+Trenton<\/code>), and Google's first match was often an unrelated business with the same name in another state (Hallandale FL was a frequent culprit). Even franchises that had correct latitude\/longitude in the database were affected, because the embed builder never actually used the stored coordinates. The widget now prefers <code>name + full address<\/code> when an address exists (rich Google business card), falls back to the stored <code>latitude,longitude<\/code> when only coordinates are available (deterministic pin), and renders no map at all when neither is available \u2014 better than misleading visitors with the wrong location.<\/li>\n<li>Fixed: Franchise grid alignment \u2014 when one card's title wrapped to two lines (e.g. \"A1 Chimney - Mount Laurel Township\") and the others were single-line, the map iframes shifted to different y-coordinates across the row. Cards are now flex columns with a 2-line <code>min-height<\/code> reserved for the title and the \"Get Directions\" button pinned to the bottom (<code>margin-top: auto<\/code>), so maps line up horizontally regardless of title length.<\/li>\n<\/ul>\n\n<h4>2.4.2<\/h4>\n\n<ul>\n<li>Fixed: City List widget (and every other A1 Tools data widget) could be stuck rendering an outdated payload \u2014 most visibly, all city pages collapsing into a single \"Ungrouped\" section when their group assignments had changed in the A1 Tools dashboard. Root cause: <code>a1tools_cache_expiry<\/code> is stored in seconds by the settings page, but <code>a1tools_fetch_api_data()<\/code> was multiplying it by <code>MINUTE_IN_SECONDS<\/code> again, making every cached payload live 60\u00d7 longer than the admin had configured. Worse, picking \"No caching (always fresh)\" stored <code>0<\/code>, which WordPress's <code>set_transient()<\/code> interprets as \"no expiration\" \u2014 so the transient persisted forever, forever serving stale data.<\/li>\n<li>Fixed: \"No caching (always fresh)\" now actually means no caching \u2014 the plugin no longer writes a transient when caching is disabled, and proactively deletes any pre-existing transient on the first request after the upgrade so legacy forever-caches are evicted.<\/li>\n<li>Migration: On upgrade, all <code>a1tools_*<\/code> transients are flushed automatically via <code>a1tools_clear_cache()<\/code> so any payload poisoned by the pre-2.4.2 TTL bug is replaced with a fresh fetch on the next page load. No admin action is required.<\/li>\n<li>Security: Blog-sync REST routes (<code>\/blog-posts<\/code>, <code>\/blog-sync\/create<\/code>, <code>\/blog-sync\/update<\/code>) now authenticate against a dedicated <code>a1tools_blog_sync_secret<\/code> option, falling back to the legacy <code>a1tools_form_secret<\/code> only for backwards compatibility (with an error_log notice when the legacy secret is used). Operators should provision a fresh per-purpose secret in wp-admin \u2192 Settings \u2192 A1 Tools and then rotate the form secret; one leaked secret no longer grants read+write access to every post on every connected site.<\/li>\n<\/ul>\n\n<h4>2.4.1<\/h4>\n\n<ul>\n<li>Fixed: X (Twitter) icon rendering as a blank tile in the Social Icons widget and the Elementor social-icons widget. The icon ships as inline SVG (since X's logo isn't in Font Awesome 5), but the render path was passing the markup through wp_kses_post() which strips \/ tags. Now uses a dedicated allowed-tags array that permits both Font Awesome  and inline SVG.<\/li>\n<\/ul>\n\n<h4>2.4.0<\/h4>\n\n<ul>\n<li>Security: Form submissions now sign each request with HMAC-SHA256 (X-Form-Signature header) and a timestamp (X-Form-Timestamp) for replay protection<\/li>\n<li>Security: Removed redundant form_secret from JSON body \u2014 secret is now only sent in the X-Form-Secret header to avoid exposure in logs<\/li>\n<li>Removed: Unused \"Target Board ID\" and \"Target Group ID\" admin fields (these were never actually sent to the server; routing is owned by the A1 Tools app's Sunday forms mapping table)<\/li>\n<li>Added: \"Test Connection\" button on the Form Submission Integration settings page \u2014 verifies the secret, CRM company ID, and CRM franchise ID resolve correctly against the A1 Tools server<\/li>\n<li>Added: \"Last Submission\" status panel showing the most recent submission outcome (success\/failure + reason + timestamp) so admins can spot integration issues without enabling WP_DEBUG<\/li>\n<li>Improved: Form submission API call now uses fastcgi_finish_request() when available so the visitor's browser receives the thank-you response before the API call fires, while still capturing the actual response for diagnostics<\/li>\n<\/ul>\n\n<h4>2.3.1<\/h4>\n\n<ul>\n<li>Performance: Fixed N+1 query in \/blog-posts endpoint by priming post meta and term caches once per batch (up to ~200x fewer DB calls on a 50-post page)<\/li>\n<\/ul>\n\n<h4>2.3.0<\/h4>\n\n<ul>\n<li>Added: licence field to site variables<\/li>\n<li>Added: [a1tools_licence] shortcode for displaying licence number<\/li>\n<\/ul>\n\n<h4>2.2.0<\/h4>\n\n<ul>\n<li>New: Blog Sync REST endpoints for cross-site post management<\/li>\n<li>New: \/blog-posts endpoint for listing posts with metadata and Yoast SEO<\/li>\n<li>New: \/blog-posts\/{id} endpoint for full post content with SEO data<\/li>\n<li>New: \/blog-sync\/create endpoint for creating synced posts with categories, tags, featured images, and Yoast SEO<\/li>\n<li>New: \/blog-sync\/update\/{id} endpoint for updating synced posts<\/li>\n<li>New: Image sideloading helper for featured image sync across sites<\/li>\n<\/ul>\n\n<h4>2.1.6<\/h4>\n\n<ul>\n<li>Fixed: SEO location shortcode names in app reference had incorrect <em>seo<\/em> prefix<\/li>\n<li>New: SEO Location Shortcodes section on A1 Tools settings page with sample values and copy buttons<\/li>\n<li>New: Expandable \"View all locations\" table on settings page for multi-location sites<\/li>\n<\/ul>\n\n<h4>2.1.3<\/h4>\n\n<ul>\n<li>Fixed: All Plugin Check errors and warnings resolved<\/li>\n<li>Fixed: Output escaping on social icon HTML<\/li>\n<li>Fixed: Added translators comment for Elementor widget<\/li>\n<li>Fixed: Replaced rename() with WP_Filesystem::move()<\/li>\n<li>Fixed: Added wp_unslash() to all $_POST\/$_SERVER inputs<\/li>\n<li>Fixed: Prefixed global variables in uninstall.php<\/li>\n<\/ul>\n\n<h4>2.1.2<\/h4>\n\n<ul>\n<li>Fixed: Plugin zip packaging for WordPress compatibility<\/li>\n<\/ul>\n\n<h4>2.1.1<\/h4>\n\n<ul>\n<li>Fixed: Syntax error in media management module<\/li>\n<\/ul>\n\n<h4>2.1.0<\/h4>\n\n<ul>\n<li>Security: Full security and standards audit per WordPress Plugin Directory review<\/li>\n<li>Removed: Update URI header (per WordPress.org guidelines)<\/li>\n<li>Fixed: Output escaping in city address inline format<\/li>\n<li>Fixed: REST API permission callback now uses WP_REST_Request instead of getallheaders()<\/li>\n<li>Fixed: All database queries now use $wpdb-&gt;prepare()<\/li>\n<li>Fixed: Replaced @unlink() with wp_delete_file()<\/li>\n<li>Fixed: error_log() calls now wrapped in WP_DEBUG checks<\/li>\n<li>Improved: Uninstall handler now cleans up all plugin options and transients<\/li>\n<li>Updated: Tested up to WordPress 6.9<\/li>\n<\/ul>\n\n<h4>2.0.10<\/h4>\n\n<ul>\n<li>New: City, Full Address, and State fields for SEO location pages<\/li>\n<li>New: [a1tools_location_city], [a1tools_location_address], [a1tools_location_state] shortcodes<\/li>\n<li>Improved: [a1tools_location_web] link text now uses full address, [a1tools_city_web] uses city name, [a1tools_state_web] uses state name<\/li>\n<\/ul>\n\n<h4>2.0.9<\/h4>\n\n<ul>\n<li>New: SEO Locations system \u2014 manage phone, URLs, map address, and Street View embed per SEO City page<\/li>\n<li>New: 7 shortcodes \u2014 [a1tools_location_phone], [a1tools_location_web], [a1tools_city_web], [a1tools_state_web], [a1tools_map_address], [a1tools_viewpoint], [a1tools_location_list]<\/li>\n<li>New: Data fetchers for SEO City author pages (separate from regular City Pages)<\/li>\n<\/ul>\n\n<h4>2.0.8<\/h4>\n\n<ul>\n<li>Refactor: Split monolithic plugin file into logical modules for maintainability<\/li>\n<li>New: Cache invalidation REST endpoint for programmatic cache busting<\/li>\n<li>Improved: Plugin architecture \u2014 shortcodes, data fetching, admin, forms, REST API, Elementor, and Yoast integration now in separate include files<\/li>\n<\/ul>\n\n<h4>2.0.7<\/h4>\n\n<ul>\n<li>New: [a1tools_post_title] shortcode \u2014 outputs the current post\/page title with optional tag, class, before\/after text<\/li>\n<\/ul>\n\n<h4>2.0.6<\/h4>\n\n<ul>\n<li>Improved: CRM leads now capture service requested, appointment date\/time, and state from form submissions<\/li>\n<li>Improved: Leads stored with separate service_need, preferred_date, preferred_time fields<\/li>\n<\/ul>\n\n<h4>2.0.5<\/h4>\n\n<ul>\n<li>Maintenance: Version bump to sync latest plugin updates<\/li>\n<\/ul>\n\n<h4>2.0.2<\/h4>\n\n<ul>\n<li>New: CRM  &hellip;<\/li>\n<\/ul>","raw_excerpt":"Centrally manage contact information, social media links, and business details across your WordPress sites from the A1 Tools platform.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/mya.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/278012","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mya.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/mya.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/mya.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=278012"}],"author":[{"embeddable":true,"href":"https:\/\/mya.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/a1tools"}],"wp:attachment":[{"href":"https:\/\/mya.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=278012"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/mya.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=278012"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/mya.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=278012"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/mya.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=278012"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/mya.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=278012"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/mya.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=278012"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}