{"id":333489,"date":"2026-07-16T09:24:47","date_gmt":"2026-07-16T09:24:47","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/turnkey-directories\/"},"modified":"2026-10-28T22:48:30","modified_gmt":"2026-10-28T22:48:30","slug":"turnkey-directories","status":"publish","type":"plugin","link":"https:\/\/brx.wordpress.org\/plugins\/turnkey-directories\/","author":23524724,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"3.1.6","stable_tag":"3.1.6","tested":"7.1.4","requires":"5.9","requires_php":"7.4","requires_plugins":null,"header_name":"TurnKey Directories","header_author":"turnkey.directories","header_description":"Build a high-performance business directory: create listings manually, dynamic directory pages, LocalBusiness schema, and XML + HTML sitemaps.","assets_banners_color":"6b7b92","last_updated":"2026-10-28 22:48:30","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/turnkeydirectories.com\/getting-started\/","header_author_uri":"https:\/\/turnkeydirectories.com\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":466,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"3.1.1":{"tag":"3.1.1","author":"turnkeydirectories","date":"2026-07-21 19:55:04","revision":3617661},"3.1.2":{"tag":"3.1.2","author":"turnkeydirectories","date":"2026-07-27 18:03:22","revision":3624999},"3.1.3":{"tag":"3.1.3","author":"turnkeydirectories","date":"2026-10-02 17:07:43","revision":3725171},"3.1.6":{"tag":"3.1.6","author":"turnkeydirectories","date":"2026-10-28 22:48:30","revision":3771806}},"upgrade_notice":{"3.1.6":"<p>Fixes the street-view search crawling along with &quot;Mapillary is busy&quot; messages. A search that is already running picks up speed by itself after the update.<\/p>","3.1.5":"<p>Fixes the Plugin Check warnings. The street-view box at the top is gone: its progress, with Pause and Resume, is now in Settings \u2192 Integrations, and the search carries on by itself until every listing is checked. Install this before Pro 2.1.1.<\/p>","3.1.4":"<p>Fixes normal WordPress pages returning &quot;Page not found&quot;, puts listings with a picture first, finds street-view photos much faster and makes setup easier. After updating, a one-time image update and the street-view search run in the background. Clear your page cache after updating.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.gif":{"filename":"icon-128x128.gif","revision":3771846,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.gif":{"filename":"icon-256x256.gif","revision":3771846,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3771846,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3771846,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":{"turnkeydir\/directory":{"name":"turnkeydir\/directory","title":"Directory Listings"}},"tagged_versions":["3.1.1","3.1.2","3.1.3","3.1.6"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3617661,"resolution":"1","location":"assets","locale":"","width":1280,"height":720},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3617661,"resolution":"2","location":"assets","locale":"","width":1280,"height":720},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3617661,"resolution":"3","location":"assets","locale":"","width":1280,"height":720},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3617661,"resolution":"4","location":"assets","locale":"","width":1280,"height":720},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3617661,"resolution":"5","location":"assets","locale":"","width":1280,"height":720}},"screenshots":{"1":"Setup wizard \u2014 connect integrations and add your first listings.","2":"Add a Listing \u2014 the manual listing editor.","3":"Dynamic directory pages, cross-linked to nearby cities.","4":"Single business listing with photos, hours, and contact details.","5":"Admin dashboard with analytics."}},"plugin_section":[],"plugin_tags":[4526,2466,4528,1122,125621],"plugin_category":[],"plugin_contributors":[271840],"plugin_business_model":[],"class_list":["post-333489","plugin","type-plugin","status-publish","hentry","plugin_tags-business-directory","plugin_tags-directory","plugin_tags-listings","plugin_tags-local-seo","plugin_tags-schema-markup","plugin_contributors-turnkeydirectories","plugin_committers-turnkeydirectories"],"banners":{"banner":"https:\/\/ps.w.org\/turnkey-directories\/assets\/banner-772x250.png?rev=3771846","banner_2x":"https:\/\/ps.w.org\/turnkey-directories\/assets\/banner-1544x500.png?rev=3771846","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/turnkey-directories\/assets\/icon-128x128.gif?rev=3771846","icon_2x":"https:\/\/ps.w.org\/turnkey-directories\/assets\/icon-256x256.gif?rev=3771846","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/turnkey-directories\/assets\/screenshot-1.png?rev=3617661","caption":"Setup wizard \u2014 connect integrations and add your first listings."},{"src":"https:\/\/ps.w.org\/turnkey-directories\/assets\/screenshot-2.png?rev=3617661","caption":"Add a Listing \u2014 the manual listing editor."},{"src":"https:\/\/ps.w.org\/turnkey-directories\/assets\/screenshot-3.png?rev=3617661","caption":"Dynamic directory pages, cross-linked to nearby cities."},{"src":"https:\/\/ps.w.org\/turnkey-directories\/assets\/screenshot-4.png?rev=3617661","caption":"Single business listing with photos, hours, and contact details."},{"src":"https:\/\/ps.w.org\/turnkey-directories\/assets\/screenshot-5.png?rev=3617661","caption":"Admin dashboard with analytics."}],"raw_content":"<!--section=description-->\n<p>TurnKey Directories turns a WordPress install into a structured business directory. The free plugin lets you create unlimited listings manually \u2014 one at a time through a simple \"Add a Listing\" form \u2014 and ships with a setup wizard, a public-facing search and listing UI, dynamic virtual directory pages, LocalBusiness and directory-level schema, and XML + HTML sitemaps. Need to populate a directory in bulk? Bulk CSV\/XLSX import and business-data import via the Outscraper API are available in the optional Pro add-on.<\/p>\n\n<h4>Watch it in action<\/h4>\n\n<p>https:\/\/youtu.be\/P7u6VxR8YuU<\/p>\n\n<p>Prefer shorter chapters? The same walkthrough is split into individual clips in <a href=\"https:\/\/www.youtube.com\/playlist?list=PLGGOM5Ysk_ulJtMMcZpfgNEEZIxUPJOZA\">this YouTube playlist<\/a>.<\/p>\n\n<h4>Core Features<\/h4>\n\n<ul>\n<li><strong>Setup wizard<\/strong> that walks through the optional integrations (Google Search Console\/GA4, Mapillary), adding your first listings, and sitemap generation.<\/li>\n<li><strong>Manual listing creation<\/strong> \u2014 add unlimited listings one at a time through an \"Add a Listing\" form, with a full editor for photos, working hours, and contact details.<\/li>\n<li><strong>Dynamic virtual directory pages<\/strong> across niche, country, state, city, and business levels, generated on the fly from your listings.<\/li>\n<li><strong>Faceted search + Leaflet map<\/strong> with filtering, search, and pagination.<\/li>\n<li><strong>Schema.org JSON-LD<\/strong>: LocalBusiness and BreadcrumbList on every listing page, directory-level CollectionPage \/ ItemList schema on category (niche\/country\/state\/city) pages, plus WebSite + SearchAction site-wide.<\/li>\n<li><strong>XML and HTML sitemaps<\/strong> so newly added listings and directory pages are discoverable. The XML sitemap is an index at <code>\/turnkeydir-sitemap.xml<\/code> (files of up to 10,000 addresses), listed in your robots.txt and, when active, in Yoast SEO's or Rank Math's own sitemap.<\/li>\n<li><strong>Works with SEO plugins<\/strong> \u2014 with Yoast SEO, Rank Math, All in One SEO or SEOPress active, directory pages hand their title, description and canonical URL to that plugin; without one, the plugin prints them itself.<\/li>\n<li><strong>Accent-color and typography customization<\/strong> (Settings \u2192 Appearance) to match your brand.<\/li>\n<li><strong>Two shortcodes<\/strong> \u2014 <code>[turnkeydir_dynamic_directory]<\/code> renders the category overview anywhere; <code>[turnkeydir_city_directory category=\"\" state=\"\" city=\"\"]<\/code> embeds a city listing block.<\/li>\n<li><strong>Mapillary integration<\/strong> to pull street-view imagery for listings with coordinates, found automatically in the background (also right after a bulk import).<\/li>\n<li><strong>Listings with a picture first<\/strong> \u2014 on every list, listings with their own image come first, then street view, then the imported photo, then the rest (paid listings from the Pro add-on stay on top).<\/li>\n<li><strong>Google Search Console + Analytics (GA4)<\/strong> connection with per-page analytics.<\/li>\n<li><strong>Optional \"Powered by\" credit<\/strong> \u2014 opt-in (off by default).<\/li>\n<li><strong>Performance-first architecture<\/strong>: object-cache-aware caching layer, prepared statements throughout, encrypted secrets at rest, rate-limited customer-facing AJAX endpoints.<\/li>\n<\/ul>\n\n<h4>Upgrade to Pro<\/h4>\n\n<p><a href=\"https:\/\/turnkeydirectories.com\/\">TurnKey Directories Pro<\/a> adds bulk CSV\/XLSX import, business-data import via the Outscraper API, business-owner claims and a customer portal, on-site reviews, multi-engine indexing (Google Indexing API + IndexNow), keyword-rank tracking, Stripe subscription billing, display ad zones, and the cross-site directory link network (Network Hub). The free plugin runs fully on its own; Pro is an optional add-on.<\/p>\n\n<h4>External services<\/h4>\n\n<p>This plugin can talk to a number of third-party services. No service is contacted on its own: the Mapillary and Google integrations are opt-in via the setup wizard or the relevant settings tab, and the admin-side Nominatim geocoding runs only when you save an address in the listing editor. On the visitor side, directory pages load OpenStreetMap map tiles (the maps are a core feature of the directory pages), and a listing page embeds a video or street-view frame only when that listing has one. Listing images load from wherever they are stored (see \"Listing images from other websites\" below).<\/p>\n\n<ul>\n<li><strong>OpenStreetMap tile server<\/strong> (<code>tile.openstreetmap.org<\/code>) \u2014 whenever a directory page renders its Leaflet map (and likewise for the map preview in the wp-admin listing editor), the browser requests the map-tile images for the viewed area directly from OpenStreetMap. As with any image request, the viewer's IP address and user agent reach the tile server, along with the tile coordinates of the map area being viewed. Operated by the OpenStreetMap Foundation. <a href=\"https:\/\/operations.osmfoundation.org\/policies\/tiles\/\">Tile usage policy<\/a> \u00b7 <a href=\"https:\/\/osmfoundation.org\/wiki\/Privacy_Policy\">Privacy policy<\/a>. The maps always show the required OpenStreetMap credit; busy sites can switch to another tile provider (see the FAQ).<\/li>\n<li><strong>OpenStreetMap Nominatim<\/strong> (<code>nominatim.openstreetmap.org<\/code>) \u2014 geocoding lookups (forward and reverse). Used in two places: (1) the admin listing editor sends the address text or coordinates you enter; (2) on the public directory search, when a visitor chooses to use their current location, their browser sends those coordinates to resolve them to a place name (user-initiated; the browser also asks for geolocation permission first). <a href=\"https:\/\/operations.osmfoundation.org\/policies\/nominatim\/\">Usage policy<\/a> \u00b7 <a href=\"https:\/\/osmfoundation.org\/wiki\/Privacy_Policy\">Privacy policy<\/a>.<\/li>\n<li><strong>Mapillary \/ Meta<\/strong> (<code>graph.mapillary.com<\/code>) \u2014 opt-in; when a Mapillary client token is configured, your server (not the visitor's browser) contacts Mapillary to look up and renew street-view photos: in the background after a listing is added or imported, when you save the token, when you use \"Find street-view photos\", \"Refresh Images\" or \"Look up now\", and in the regular background task that renews photo links. Each lookup sends a listing's latitude\/longitude plus your token. Visitors' pages never wait for these lookups. The returned images are then loaded by visitors' browsers from Mapillary's image CDN, which is run by Facebook (<code>tiles.mapillary.com<\/code>, <code>scontent-*.fbcdn.net<\/code>), and where a listing shows an interactive street-view embed the visitor's browser loads Mapillary's embed viewer from <code>www.mapillary.com<\/code> (the visitor's IP address and user agent reach Meta's servers). <a href=\"https:\/\/www.mapillary.com\/terms\">Terms of use<\/a> \u00b7 <a href=\"https:\/\/www.mapillary.com\/privacy\">Privacy policy<\/a>.<\/li>\n<li><strong>Listing images from other websites<\/strong> \u2014 a listing's image is loaded by the visitor's browser from wherever it is stored. Images you upload come from your own site. An imported photo (for example the \"Image URL\" in a Google Maps export, hosted by Google on <code>googleusercontent.com<\/code>) or an image address you paste in the listing editor loads from that website, and street-view photos load from Facebook's CDN for Mapillary (see above). As with any image, the visitor's IP address and user agent reach that website. Google Maps and Google API links (<code>google.com<\/code>, <code>googleapis.com<\/code>) are never shown as images; developers can block more hosts with the <code>turnkeydir_blocked_image_hosts<\/code> filter.<\/li>\n<li><strong>Google<\/strong> \u2014 opt-in: when you connect Google integration, the admin requests Google Search Console and Analytics (GA4) reporting on your behalf, sending your OAuth tokens and your site URL (<code>accounts.google.com<\/code>, <code>oauth2.googleapis.com<\/code>, <code>googleapis.com<\/code>). This connection is used only to read your own reporting data into the admin Analytics dashboard; the plugin does not add any Google tracking tag to your public pages. <a href=\"https:\/\/policies.google.com\/terms\">Terms of service<\/a> \u00b7 <a href=\"https:\/\/policies.google.com\/privacy\">Privacy policy<\/a>.<\/li>\n<li><strong>YouTube \/ Vimeo video embeds<\/strong> \u2014 when a listing has a video URL on record, its listing page embeds that video player in the visitor's browser (<code>youtube.com<\/code> \/ <code>player.vimeo.com<\/code>); the embed provider receives the visitor's IP address and user agent, as with any embedded player. No video services are contacted on pages without a listing video. <a href=\"https:\/\/www.youtube.com\/t\/terms\">YouTube terms<\/a> \u00b7 <a href=\"https:\/\/policies.google.com\/privacy\">Google privacy<\/a> \u00b7 <a href=\"https:\/\/vimeo.com\/terms\">Vimeo terms<\/a> \u00b7 <a href=\"https:\/\/vimeo.com\/privacy\">Vimeo privacy<\/a>.<\/li>\n<li><strong>Directions links<\/strong> \u2014 the \"Get Directions\" buttons are plain user-initiated links that open the visitor's maps application (Google Maps or the platform's geo: handler); nothing is contacted until the visitor taps them.<\/li>\n<li><strong>Share links<\/strong> \u2014 the share buttons on directory pages are likewise plain user-initiated links to the visitor's chosen network (Facebook, X\/Twitter, LinkedIn, or an email draft); nothing is contacted until the visitor taps one, at which point the shared page URL and title travel to that network as part of the link.<\/li>\n<\/ul>\n\n<h4>Bundled third-party libraries<\/h4>\n\n<p>All front-end libraries are bundled locally \u2014 the plugin loads none of its own assets from any CDN. (The only external scripts are the per-listing video\/street-view embeds \u2014 see the external-services list above.) Bundled (with links to their readable source): <a href=\"https:\/\/github.com\/Leaflet\/Leaflet\">Leaflet 1.9.4<\/a> (BSD-2), <a href=\"https:\/\/github.com\/chartjs\/Chart.js\">Chart.js 4.5.1<\/a> (MIT), <a href=\"https:\/\/github.com\/FortAwesome\/Font-Awesome\">Font Awesome Free 6.7.2<\/a> (icons CC BY 4.0, code MIT, fonts OFL), <a href=\"https:\/\/github.com\/themustafaomar\/jsvectormap\">jsVectorMap 1.7.0<\/a> (MIT), and the <a href=\"https:\/\/github.com\/rsms\/inter\">Inter<\/a> typeface (OFL). Each library's full license text ships alongside it under <code>public\/vendor\/<\/code>.<\/p>\n\n<h4>Source Code &amp; Bug Reports<\/h4>\n\n<p>Source code, issue tracker, and contribution guidelines are available at <a href=\"https:\/\/turnkeydirectories.com\/\">turnkeydirectories.com<\/a>.<\/p>\n\n<h3>Recommended hosting setup<\/h3>\n\n<p>The plugin schedules daily background tasks for Google Analytics \/ Search Console sync and Mapillary image fetching. By default WordPress's <code>wp_cron<\/code> piggybacks on regular page loads \u2014 meaning a logged-in admin viewing a public page can be the one that synchronously runs an overdue background task. If a Google API call is slow that day, that page load is slow too.<\/p>\n\n<p>For best front-end performance, offload <code>wp_cron<\/code> to a real server cron:<\/p>\n\n<ol>\n<li><p>In <code>wp-config.php<\/code>, above the \"stop editing\" comment, add:<\/p>\n\n<p>define('DISABLE_WP_CRON', true);<\/p><\/li>\n<li><p>In your hosting control panel's cron-job manager (Hostinger hPanel \u2192 Advanced \u2192 Cron Jobs, or cPanel \u2192 Cron Jobs), add an every-five-minute job that hits <code>wp-cron.php<\/code>:<\/p>\n\n<p>*\/5 * * * * curl -s https:\/\/YOUR-DOMAIN.com\/wp-cron.php?doing_wp_cron &gt;\/dev\/null 2&gt;&amp;1<\/p>\n\n<p>(replace <code>YOUR-DOMAIN.com<\/code> with your actual site)<\/p><\/li>\n<\/ol>\n\n<p>After this, all background tasks run in their own short-lived process and never block a visitor's page render.<\/p>\n\n<p>As a defense-in-depth, each scheduled callback also has a <code>DOING_CRON<\/code> guard and an in-flight transient lock so even with default <code>wp_cron<\/code> enabled, a slow Google API call can't be re-entered by a concurrent page load and is bounded by a short HTTP timeout. With both the server-cron offload and the in-plugin guards in place, the impact of any third-party API outage on your front-end response time is effectively zero.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>turnkey-directories<\/code> folder to <code>\/wp-content\/plugins\/<\/code>, or install the plugin through the WordPress Plugins screen.<\/li>\n<li>Activate the plugin through the <strong>Plugins<\/strong> screen in WordPress.<\/li>\n<li>Visit <strong>TurnKey Directories<\/strong> in the admin sidebar to launch the setup wizard.<\/li>\n<li>Follow the wizard to connect the optional integrations (Google, Mapillary), add your first listings, and generate the sitemap. Branding (accent color, typography) lives under <strong>Settings \u2192 Appearance<\/strong>.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20this%20plugin%20work%20without%20any%20third-party%20services%3F\"><h3>Does this plugin work without any third-party services?<\/h3><\/dt>\n<dd><p>Yes. The core directory (manual listing creation, public pages, schema, sitemap) works without any external integrations. Mapillary and Google are opt-in.<\/p><\/dd>\n<dt id=\"how%20many%20listings%20can%20the%20plugin%20handle%3F\"><h3>How many listings can the plugin handle?<\/h3><\/dt>\n<dd><p>Architecturally targeted at large directories on standard shared hosting. Performance scales with your DB, hosting tier, and whether you have an external object cache (Redis or Memcached) configured. The dynamic-routing layer caches city\/state\/country queries through <code>wp_cache_*<\/code> (with a transient fallback) so high-traffic pages do not hammer MySQL. The free plugin supports an unlimited number of manually created listings; to populate a directory in bulk, the Pro add-on adds CSV\/XLSX import and business-data import via the Outscraper API.<\/p><\/dd>\n<dt id=\"are%20stored%20api%20keys%20encrypted%3F\"><h3>Are stored API keys encrypted?<\/h3><\/dt>\n<dd><p>Yes. All third-party credentials (Mapillary, Google OAuth refresh\/access tokens) are encrypted at rest using <code>sodium_crypto_secretbox<\/code> (XSalsa20-Poly1305) with a key derived from <code>wp_salt('auth')<\/code>. Existing plaintext options are migrated transparently on first read.<\/p><\/dd>\n<dt id=\"what%20visitor%20data%20does%20the%20plugin%20store%3F\"><h3>What visitor data does the plugin store?<\/h3><\/dt>\n<dd><p>The directory stores the business listings you create. For abuse prevention, the built-in security log records the IP address, user agent and request path of requests that trip a rate limit or look malicious, keeping only a rolling window of the most recent entries so repeat offenders can be blocked. No cookies are set on visitors, and the plugin adds no analytics or tracking tag to your public pages. You can clear the security log at any time from <strong>Dashboard \u2192 Health \u2192 Security Logs<\/strong>.<\/p><\/dd>\n<dt id=\"which%20xml%20sitemap%20address%20do%20i%20submit%20to%20google%3F\"><h3>Which XML sitemap address do I submit to Google?<\/h3><\/dt>\n<dd><p>Submit <code>https:\/\/your-site.com\/turnkeydir-sitemap.xml<\/code>. It is a sitemap index that lists your listing pages and your niche, country, state and city pages, split into files of up to 10,000 addresses. It is built when requested, so it is always current. If no SEO plugin serves <code>\/sitemap.xml<\/code>, that address shows the same index. With Yoast SEO or Rank Math, the directory sitemap is also listed inside their sitemap, so submitting theirs is enough.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20yoast%20seo%20or%20rank%20math%3F\"><h3>Does it work with Yoast SEO or Rank Math?<\/h3><\/dt>\n<dd><p>Yes. On directory and listing pages the plugin passes its title, meta description, canonical URL and social tags to Yoast SEO, Rank Math, All in One SEO or SEOPress, so each page has exactly one set of tags. Without an SEO plugin, the plugin prints them itself.<\/p><\/dd>\n<dt id=\"can%20i%20use%20a%20different%20map%20tile%20provider%3F\"><h3>Can I use a different map tile provider?<\/h3><\/dt>\n<dd><p>Yes. The maps use OpenStreetMap's public tile server, which is meant for light use. Busy sites should use a commercial tile provider; add this to a small plugin or your theme's functions.php and fill in your provider's details:<\/p>\n\n<pre><code>add_filter( 'turnkeydir_map_tiles', function ( $tiles ) {\n    $tiles['url']         = 'https:\/\/tiles.example.com\/{z}\/{x}\/{y}.png?key=YOUR_KEY';\n    $tiles['attribution'] = '&amp;copy; Example Maps &amp;copy; &lt;a href=\"https:\/\/www.openstreetmap.org\/copyright\"&gt;OpenStreetMap&lt;\/a&gt; contributors';\n    return $tiles;\n} );\n<\/code><\/pre><\/dd>\n<dt id=\"can%20developers%20extend%20the%20plugin%3F\"><h3>Can developers extend the plugin?<\/h3><\/dt>\n<dd><p>Yes. Every action and filter is listed with its arguments in <code>includes\/class-turnkeydir-hooks.php<\/code>. New in 3.1.4:<\/p>\n\n<ul>\n<li><strong>Listing images:<\/strong> <code>turnkeydir_listing_image( $row, $context )<\/code> (or <code>TURNKEYDIR_Listing_Image::resolve()<\/code>) returns the image a listing shows \u2014 owner image, else street view, else imported photo, else none \u2014 and the <code>turnkeydir_listing_image<\/code> filter changes it. <code>TURNKEYDIR_Listing_Image::tier_for_row()<\/code> gives a listing's image group (0 own image, 1 street view, 2 imported photo, 3 none).<\/li>\n<li><strong>Changing images:<\/strong> <code>TURNKEYDIR_Listing_Image::set_custom_image( $id, $url, $by )<\/code> and <code>clear_custom_image( $id, $by )<\/code> set or remove a listing's own image. Code that writes <code>custom_featured_image<\/code>, <code>mapillary_image_url<\/code> or <code>photo<\/code> itself must call <code>TURNKEYDIR_Listing_Image::touch( $ids )<\/code> afterwards; importers fire the <code>turnkeydir_listings_imported<\/code> action with the new listing IDs instead. The <code>turnkeydir_image_tiers_ready<\/code> action fires when the one-time image update has finished.<\/li>\n<li><strong>Image hosts:<\/strong> the <code>turnkeydir_blocked_image_hosts<\/code> filter lists hosts whose images are never shown (default <code>google.com<\/code>, <code>googleapis.com<\/code>).<\/li>\n<li><strong>Order of listings:<\/strong> <code>TURNKEYDIR_Listing_Image::order_by( $sort, $geo, $alias )<\/code> builds the order used on every list; the <code>turnkeydir_listing_order_by<\/code> filter changes it (keep a unique last key so paging stays correct).<\/li>\n<li><strong>Street view:<\/strong> <code>TURNKEYDIR_Mapillary_API::refresh_listing( $id )<\/code> looks up one listing now; <code>TURNKEYDIR_Mapillary_API::start_fast_pass()<\/code> starts the background search. Filters <code>turnkeydir_mapillary_fast_concurrency<\/code>, <code>turnkeydir_mapillary_fast_rpm<\/code>, <code>turnkeydir_mapillary_fast_purge_interval<\/code> and <code>turnkeydir_mapillary_api_base<\/code> tune it.<\/li>\n<li><strong>Edit Listing:<\/strong> the <code>turnkeydir_edit_listing_sections<\/code> action adds admin-only panels below the listing fields (they are posted with Save, so don't reuse listing field names).<\/li>\n<li><strong>Setup wizard:<\/strong> the <code>turnkeydir_wizard_leave<\/code> action and the <code>turnkeydir_wizard_activities<\/code>, <code>turnkeydir_wizard_sections<\/code>, <code>turnkeydir_wizard_substeps<\/code> and <code>turnkeydir_wizard_exit_url<\/code> filters let add-ons add their own sections, steps and running work. <code>TURNKEYDIR_Installation_Wizard::instance()<\/code> offers <code>get_step_url()<\/code>, <code>get_step_number()<\/code>, <code>get_resume_slug()<\/code>, <code>next_url()<\/code>, <code>prev_url()<\/code>, <code>leave_url()<\/code> and <code>reset_progress()<\/code>; in the browser, <code>TURNKEYDIRWizard.setBusy()<\/code> \/ <code>clearBusy()<\/code>.<\/li>\n<li><strong>Admin helpers:<\/strong> <code>turnkeydir_admin_current_tab()<\/code> (the dashboard tab being shown), <code>turnkeydir_asset_version( $file )<\/code> (a version that changes whenever the file changes) and <code>turnkeydir_admin_send_nocache()<\/code>.<\/li>\n<\/ul>\n\n<p>Add-ons should check that a function or class exists before calling it, so they keep working with older versions of the plugin.<\/p><\/dd>\n<dt id=\"how%20do%20i%20delete%20all%20plugin%20data%20on%20uninstall%3F\"><h3>How do I delete all plugin data on uninstall?<\/h3><\/dt>\n<dd><p>Visit <strong>Settings \u2192 Danger Zone \u2192 Delete all plugin data on uninstall<\/strong> and check the box BEFORE deleting the plugin from the Plugins page. The default behavior preserves your data so you can reinstall without loss.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>3.1.6<\/h4>\n\n<ul>\n<li>Fixed: the street-view search kept saying \"Mapillary is busy or can't be reached\" and was very slow (days instead of hours on a large directory). Mapillary wasn't busy. A few listings it can't answer for (mostly in very dense areas) made the search think Mapillary was down, so it slowed to one request at a time and waited 2 minutes, again and again. Now, when every request in a round fails, it first checks whether Mapillary answers at all. If it does, those listings are left to the hourly street-view job and the search carries on at full speed. It only waits when Mapillary is really down or asks it to slow down.<\/li>\n<li>Changed: when there is no street-view photo within 50 m of a listing, the search now looks about 200 m and 500 m around it before searching wider areas. Dense areas answer much faster, and the photo found is usually closer to the business.<\/li>\n<li>The street-view card in Settings \u2192 Integrations shows how many listings are left for a later try. While the search waits because Mapillary is busy or can't be reached, the card also shows the last error (usually Mapillary's own message), so you can see what is going on.<\/li>\n<li>Fixed: one listing that Mapillary kept answering with a temporary error could hold back the street-view search and the hourly street-view job. That listing is now tried again later instead.<\/li>\n<\/ul>\n\n<h4>3.1.5<\/h4>\n\n<ul>\n<li>Fixed: the 4 warnings Plugin Check reported. It now shows 0 errors and 0 warnings.<\/li>\n<li>Changed: the street-view box at the top of the plugin's screens is gone. The search's progress (bar, counts, time left) now shows in Settings \u2192 Integrations, in the street-view card (next to Street-view coverage and Refresh Images), with Pause and Resume. A search paused in 3.1.4 shows Resume there, even if you dismissed its box. \"Find street-view photos\" on Manage Listings says the search has started, with a link to its progress.<\/li>\n<li>Fixed: the street-view search could stop before every listing was checked, mostly when nobody was logged in (some hosts, such as LiteSpeed, cut off its background requests). It now carries on by itself until every listing is checked, also after you leave the page or log out.<\/li>\n<li>Fixed: when Mapillary was busy or couldn't be reached, the search paused until someone opened an admin page. It now waits a few minutes and tries again by itself, and a short Mapillary outage no longer marks listings as failed. If the problem lasts more than an hour, the search pauses for 30 minutes and then carries on; listings that still fail are retried by the hourly street-view job.<\/li>\n<li>If your server blocks the search's background requests, the Settings card says so. The search then moves on when WordPress runs its scheduled tasks, or while a TurnKey Directories page is open.<\/li>\n<li>Changed: the setup wizard's top bar shows the TurnKey Directories logo, the same as the dashboard.<\/li>\n<li>Fixed on phones: the Health tab no longer scrolls sideways, and the close button of Manage Listings messages is bigger and easier to tap.<\/li>\n<\/ul>\n\n<h4>3.1.4<\/h4>\n\n<p>A reliability, SEO and performance release based on a full code audit. It also puts listings with a picture first, finds street-view photos much faster and makes the setup wizard easier to use. No settings need to change; existing listing URLs keep working.<\/p>\n\n<p><strong>Your website<\/strong>\n* Fixed: while the plugin was active, many normal WordPress pages returned \"Page not found\" \u2014 blog categories, tags, author pages, date archives, comment feeds, sub-pages and WooCommerce products. Directory pages no longer take over those addresses.\n* Fixed: a visitor could change a listing's web address just by opening a made-up URL. Listing addresses now only change when you edit the listing; old addresses redirect (301) to the current one.\n* Fixed: the plugin could adopt, rename or delete pages it didn't create (for example your own \"Business Directory\" page, or your customised directory page after the top category changed). It now only touches its own page, renames it in place, and moves it to the trash instead of deleting it.\n* Fixed: \/directory\/ crashed with a fatal error. It now redirects to your directory page.\n* Fixed: pages 2, 3 \u2026 of the directory search page redirected back to page 1.<\/p>\n\n<p><strong>Street-view photos<\/strong>\n* Fixed: street-view photos broke about a month after they were fetched, and listing pages then showed a blank white box or a broken-image icon. Mapillary's photo links expire after about 30 days, and nothing renewed them. The plugin now stores each photo's Mapillary ID and renews its link automatically in the background about two weeks before it expires (up to 50 links per request to Mapillary, renewals first). No reinstalling or \"Refresh Images\" needed. After updating, the plugin finds a photo again for every listing that needs one: once the one-time image update (see \"Listing images and order\") has finished, the fast street-view search below looks them up in the background.\n* Pages never show a photo link that is about to expire, and if a photo still can't load (for example a privacy filter blocks it) the listing shows a dark street-view panel instead of a broken image, with or without JavaScript.\n* Listings added later get their street-view photo automatically (after a bulk import straight away, see below), and listings marked \"no coverage\" are checked again every 90 days.\n* New: after a Pro bulk import, street-view photos for the new listings are looked up straight away in the background, many at a time, instead of one listing at a time over hours or days. On a test site with simulated Mapillary answers, 18,000 listings took about 16 minutes. The same fast search starts when you save a Mapillary token (also when you switch Mapillary on for the first time), when the one-time image update has finished, and from the new \"Find street-view photos\" button on Manage Listings.\n* A notice at the top of the plugin's screens shows the search's progress (bar, counts, time left) with Pause and Resume. If your server blocks background requests, it says \"Keep this tab open\" and the search carries on while a TurnKey Directories page is open.\n* The search stays under Mapillary's limits and slows down when Mapillary asks it to. If Mapillary refuses your token, it stops for 6 hours, shows one red notice and then tries again by itself (or at once when you save the token or press Resume).\n* Without a Mapillary token, the dashboard says after an import: \"Add a Mapillary token to show street-view listings first.\"\n* Settings \u2192 Refresh Images no longer empties every photo first: it checks every listing again in the background and keeps the photos shown until a newer one is found. Its Stop button is now Pause.\n* Settings \u2192 Integrations: the street-view card shows the same numbers on load as while it refreshes, and its progress can reach 100%.\n* While new photos turn up, the page cache is cleared fully at most every 10 minutes, and once at the end.\n* Photos are chosen better: the best photo within 50 m of the business first (Mapillary weighs distance, recency and 360\u00b0), otherwise the nearest one, instead of whichever photo came first in a 2 km box. The largest search box also respects Mapillary's current size limit, which made those lookups fail.\n* Photo pages credit Mapillary with a link to the photo (CC BY-SA licence).\n* The dashboard warns when photos can't be renewed (no Mapillary token, token rejected, or WordPress background tasks not running). While an administrator has the dashboard open, renewals also run from there, for sites whose WP-Cron doesn't run.\n* With LiteSpeed Cache, a listing page is never cached longer than its photo link stays valid.\n* Fixed: on iPhones and Macs (Safari), the map on listing pages didn't load because of a script error.<\/p>\n\n<p><strong>Listing images and order<\/strong>\n* New: one rule picks a listing's image everywhere (pages, cards, share previews and search-engine tags): the listing's own image, set by you or (with Pro) the business owner, first; then the street-view photo; then the photo from your imported data; else the placeholder. Before, each page chose differently.\n* New: every list of listings uses one order. Paid listings (Sponsored or Priority, with Pro) come first, then listings with their own image, then street view, then the imported photo, then no image. Inside each group the visitor's choice (best rated, most reviews, A\u2013Z, newest, nearest) decides. This covers category pages, \"Load more\", city, state and country pages, related listings on a listing page, the city directory shortcode and Manage Listings.\n* Fixed: paging through a list could show a listing twice or skip one. Every listing now appears exactly once.\n* \"Nearest first\" puts listings without a location at the end of their group. The \"Top Rated\" box on city and state pages is still sorted by rating only.\n* Imported photos hosted by Google (the usual \"Image URL\" in Google Maps exports) now show on listing pages too, not only on cards. Google Maps and Street View API links are still never shown.\n* Fixed: the \"Street View\" badge showed on every image; it now only shows on street-view photos. City pages now also show street-view photos that were saved without their photo ID.\n* Mapillary viewer pages saved as an image, and broken links that only look like street-view photos, are never shown as an image.\n* After updating, a one-time background update moves images that older versions kept in other places to where cards and the order of listings see them: uploaded images become the listing's own image, other image links its imported photo. On a test site with 50,000 listings it took about 2 seconds. It clears the page cache once at the end.<\/p>\n\n<p><strong>Edit Listing and Manage Listings<\/strong>\n* Fixed: in Manage Listings, \"Select all N listings\" followed by Delete or Export acted on the whole directory, even with a search or filter. Deleting the listings found with a filter could delete every listing. It now acts only on the matching listings, large deletes run in steps with a progress message, and the question before deleting says how many listings it affects.\n* Edit Listing has a new image panel. It shows which image the site shows now (Owner image, Street view, Imported photo or Placeholder), lets you upload or paste your own image and remove it with \"Remove custom image\", shows the street-view status (found, none nearby, not checked yet, not set up) with a \"Look up now\" button, and shows the imported photo.\n* Fixed: an image uploaded or pasted in Edit Listing was missing from directory cards and Manage Listings. It is now saved on the listing itself. An uploaded file that is replaced or removed is deleted when no other listing uses it.\n* Fixed: pasting something that isn't a picture's web address (a \"data:\" link, a file path from your computer, a Google search result or Maps link) silently removed the listing's image. It now shows a message and saves nothing.\n* Fixed: a page left open while the image was changed elsewhere (another tab, the business owner) brought the old image back when you saved another field.\n* Manage Listings: new Image filter (own image, street view, imported photo, no image, no location) with the number of listings for each. The image icon shows where each image comes from (star = own image, camera = street view, picture = imported photo, crossed out = none), and the \"With Images\" card also shows how many listings show a street-view photo.\n* Manage Listings: the default order is now the order of your directory pages. Column headers sort A\u2013Z, then Z\u2013A, then back to the default order; the Image column sorts by image group.\n* Manage Listings: a search or filter with no matches says so, with a \"Show all listings\" button.<\/p>\n\n<p><strong>Setup wizard<\/strong>\n* Every setup step has a bar at the top that jumps straight to Google, Mapillary or Listings (each shows done, not set up yet or running) and has \"Exit setup\" to leave at any time. If an import or scrape is still running, Exit setup asks whether to keep it running or stop it.\n* The setup remembers where you stopped: \"Continue where you left off\" and the dashboard's \"Continue setup\" go back there. Old bookmarks to a step open it instead of failing, and a page left open overnight refreshes itself instead of ending on \"The link you followed has expired.\"\n* Next and Skip no longer land on an empty column-mapping page. The data step's button is now \"Finish setup\".\n* The dashboard's Setup tab is rebuilt: Google, Mapillary and Listings cards with their status and a Set up \/ Change button. \"Run the full setup again\" restarts the steps but no longer deletes your Google connection, Mapillary key, listings or scrape results.\n* \"Add your first listing manually\" stays inside the setup: after saving you see \"Listing created\" with Continue setup, Add another and Edit this listing.\n* Fixed: the wizard's own colours, borders and shadows were faint or missing, the Google Maps scrape window could open off-centre with a second dark layer and a squeezed form, and the import cards didn't line up. Four import cards now fit side by side on a laptop; on phones the wizard's buttons are full width and its bar scrolls to the current section.<\/p>\n\n<p><strong>Directory pages<\/strong>\n* Fixed: city, state and country pages were empty for names with punctuation or accents (St. Louis, Winston-Salem, O'Fallon, Coeur d'Alene, S\u00e3o Paulo) and for countries stored by full name (United Kingdom).\n* Fixed: two businesses with the same name in the same city could not both be reached; each now has its own address.\n* Fixed: \"Open now\" and the Open\/Closed badges were wrong for late-night hours (e.g. 6 PM \u2013 2 AM), hours written without AM\/PM (8-6), and several import formats, and used UTC instead of your site's timezone.\n* Fixed: made-up state, city or business addresses showed an empty page instead of a proper \"Page not found\".\n* Fixed: maps now show the OpenStreetMap credit and use OpenStreetMap's current tile address, as their usage policy requires (sites that don't comply can be blocked). High-traffic sites can switch map provider with the <code>turnkeydir_map_tiles<\/code> filter.\n* Fixed on listing pages: the Website, Directions and Book buttons could stay stuck on a spinner after a click, and \"Read more\" was hidden under the next card on phones.\n* Fixed on city pages: search suggestions stopped working after the first click on the page; statistics counted up twice and could end on a wrong number; a \"Map\" button on phones did nothing.\n* Fixed: the search bar on the directory search page flickered between sticky and not sticky while scrolling.<\/p>\n\n<p><strong>SEO<\/strong>\n* Each directory and listing page now has exactly one title, description and canonical URL. Before, some pages had several descriptions and robots tags (some inside the page body) and most had no canonical URL.\n* Works with Yoast SEO, Rank Math, All in One SEO and SEOPress: the plugin hands its titles and descriptions to your SEO plugin. With Yoast in 3.1.3, directory pages were titled with just the site name and pointed their canonical URL at the homepage.\n* Listing pages are titled with the business name, and the description comes from the listing's About text.\n* New XML sitemap: a sitemap index at \/turnkeydir-sitemap.xml with files of up to 10,000 addresses, now including your country, state and city pages. It is built on request (nothing is rebuilt when you save a listing) and is added to your robots.txt and to Yoast's \/ Rank Math's sitemap. \/sitemap.xml is left to your SEO plugin when you have one.\n* New setting: \"Include imported ratings in structured data\" (on by default, same as before).<\/p>\n\n<p><strong>Speed<\/strong>\n* Directory pages load about half as many stylesheet files (the search page 9 instead of 19): the plugin's CSS is served as a few combined, minified files, with exactly the same styling. A script that rearranged the layout on phones after the page had loaded is replaced by plain CSS, so the page no longer jumps.\n* About a third fewer database queries on directory pages, and cached statistics.\n* Street view no longer slows pages down: visitors never wait for Mapillary. Photos are looked up and renewed in the background, and pages are refreshed in page caches when a photo arrives. Before, a slow Mapillary response delayed city and listing pages by up to 15 seconds and then left a broken image in the cached page.\n* Caches now clear correctly on hosts with Redis or Memcached, and saving settings no longer empties your whole site's object cache.\n* Search results and visitor input are no longer stored in the database.\n* The new order of listings stays fast: each listing stores its image group, and on a test site with 50,000 listings a category page loads its listings in about 25 ms.\n* Country pages no longer run two listing searches they never showed.\n* When one listing's image changes, only that listing's page and its category and city pages are cleared from the page cache. Bulk changes clear the whole cache at most once every 10 minutes.<\/p>\n\n<p><strong>Security<\/strong>\n* The plugin's REST API lock could be bypassed; it now checks the actual API route.\n* Fixed a setting that told browsers and CDNs to cache some admin pages publicly for a year.\n* Visitor IP addresses are detected correctly behind Cloudflare and other proxies, so rate limits no longer treat every visitor as one; you can no longer accidentally block your own IP; the security log is capped and no longer slows every page.\n* Listing fields are checked by type when saved (web addresses, e-mail, colours, numbers).\n* Business names are escaped in admin tables and tooltips.\n* A crafted Settings address can no longer cause an error on older WordPress versions.<\/p>\n\n<p><strong>Translations and multisite<\/strong>\n* Nearly all visible text \u2014 directory pages, listing pages, messages shown by the page scripts and the admin screens \u2014 is now translatable.\n* Network activation sets up every site in a multisite network, and new sites are set up automatically. Uninstall cleans up each site.<\/p>\n\n<p><strong>Admin<\/strong>\n* Fixed: saving Settings switched off \"Include column headers\" for CSV exports, because that option is not on the form. Options that are not on the submitted form now keep their value.\n* Fixed: the \"You have unsaved changes\" bar appeared without any change on screens with radio buttons or colour pickers, and stayed after a change was undone.\n* The analytics page table has an index for page lookups, so analytics syncs on large directories are faster.\n* Fixed: when Google answered a token renewal with an error page instead of a token (an outage or a proxy error), the stored Google connection was damaged. The connection is now kept and renewed on the next try.\n* Fixed: pop-up windows inside a dashboard tab (for example Manage Listings \u2192 Bulk Upload) could open below the bottom of the screen; on a phone, Bulk Upload looked like it did nothing. They now cover the whole screen and open in the middle.\n* Fixed on phones: with nothing to save, the hidden \"You have unsaved changes\" bar still caught taps meant for buttons under it, such as Save Ad Banners. While the bar shows, the page now leaves room for it, so the last fields and buttons can be scrolled above it.\n* Fixed: opening the dashboard from the menu could load a tab without its styles (the Pro Ad Banners panel showed as plain text), and a tab name with capital letters in the address showed a blank panel.\n* Settings remembers the open section after a refresh.\n* Admin styles and scripts are fetched fresh whenever a file changes, so a browser or host cache can't serve an old copy. The setup wizard and the Add Listing page are never cached, so the Back button can't show an out-of-date screen.\n* Security Logs keeps its own look without changing other screens, and the Export All \/ Add New Listing \/ Bulk Upload buttons on Manage Listings wrap onto a second line on phones.<\/p>\n\n<p><strong>For the Pro add-on<\/strong>\n* Listing pages render the Pro photo gallery through a new <code>turnkeydir_listing_gallery<\/code> action.\n* A listing address that no longer exists (for example after the business owner renamed the listing in the owner portal) can be redirected to the listing's current address through the <code>turnkeydir_listing_redirect_slug<\/code> filter.\n* Listings that must be kept, for example ones whose owner has an active paid subscription, are skipped by Delete and Delete all (<code>turnkeydir_deletable_listing_ids<\/code> and <code>turnkeydir_protected_listing_ids<\/code> filters) and the message says how many were kept. New <code>turnkeydir_before_listings_delete<\/code> action.\n* The optional \"delete all data\" uninstall keeps the options and photo table that belong to the Pro add-on (its licence and payment settings); the add-on removes them with its own uninstall.\n* Edit Listing has a spot below the listing fields for Pro panels (<code>turnkeydir_edit_listing_sections<\/code> action); Pro uses it for its private \"Contacts &amp; details\" panel. Without Pro, or with an older Pro, the page is unchanged.\n* Pro imports report the listings they wrote (<code>turnkeydir_listings_imported<\/code> action), so imported listings get their place in the order straight away and their street-view search as soon as the import ends.\n* The Pro add-on can add its own sections, steps and running work (imports, scrapes) to the setup wizard. Older versions of the add-on keep working.\n* Developer notes for all new hooks and functions are in the FAQ (\"Can developers extend the plugin?\").<\/p>\n\n<p><strong>Under the hood<\/strong>\n* Removed unused code, duplicate scripts and developer notes that were printed into the page HTML.\n* Plugin debug logging is now opt-in with the <code>TURNKEYDIR_DEBUG<\/code> constant instead of following <code>WP_DEBUG<\/code>.\n* Database version 3.0: a new column stores each listing's image group. An hourly check keeps it right when an older add-on changes an image or a street-view link expires.\n* Removed an unused block of old card code and the old importer's leftover styles.\n* Uninstalling always removes the street-view search's progress record; your listings and settings stay unless you chose \"Delete all plugin data on uninstall\".<\/p>\n\n<h4>3.1.3<\/h4>\n\n<ul>\n<li>WordPress 7.1 compatibility: tested against the 7.1 release candidate.<\/li>\n<li>Changed: the Directory Listings editor block now registers with block API version 3, matching WordPress 7.1's always-iframed post editor.<\/li>\n<li>Fixed: the block's editor placeholder icon could render as an empty box in the iframed editor, where the admin icon font isn't loaded. The placeholder now uses a self-contained inline icon.<\/li>\n<li>Removed: an unused media-library enqueue on the Edit Listing screen (the screen uses its own uploader), so no unneeded media scripts are loaded there.<\/li>\n<\/ul>\n\n<h4>3.1.2<\/h4>\n\n<ul>\n<li>Fixed: the directory landing page (your search page) 404'd after a bulk import. On a new install the page is created before any listings exist, so it falls back to a generic \"\/business\/\" slug; if the post-import refresh didn't reach it, the page stayed there while every link pointed at \"\/your-niche\/\". It now self-heals: once real listings exist, the landing page is regenerated at the correct slug.<\/li>\n<li>Fixed: PHP warning \"Undefined variable $accent_color\" on state pages, which also emitted empty theme-color meta tags.<\/li>\n<li>Fixed: bulk imports silently dropped several mapped columns (review counts, website, email, description, opening hours, image and booking links) whenever the source column names differed from the internal database names. All mapped fields are now resolved to their real columns before saving, on every import path (CSV, XLSX, scraped data, and the manual Add-a-Listing form). Re-import your data file to backfill listings imported before this release.<\/li>\n<li>Fixed: the Google review link on a listing pointed at the on-page review section instead of the business's Google reviews URL whenever the review count was zero. The link target is now chosen from the stored reviews URL independently of the count.<\/li>\n<li>Fixed: filtering a category search by rating (or any non-search filter) still showed the \"All Businesses\" heading and full count, so the filter looked like it did nothing. The results header now reflects the active filters and the filtered total.<\/li>\n<li>Fixed: opening-hours import corrupted day names that were already spelled out (e.g. \"Monday\" became \"Mondayday\"). Day-abbreviation expansion is now word-boundary aware.<\/li>\n<li>Fixed: the interactive street view showed only sky with its controls cut off. The embed was forced 100px taller than its container (which clips overflow), so it was cropped to the top of the image. It now fills the container exactly \u2014 the view is centred on the street and every control is visible. Also relabelled \"360\u00b0 View\" to \"Interactive Street View\" (most coverage is flat perspective).<\/li>\n<\/ul>\n\n<h4>3.1.1<\/h4>\n\n<ul>\n<li>Initial release.<\/li>\n<\/ul>","raw_excerpt":"High-performance business directory. Create listings manually, dynamic directory pages, LocalBusiness schema, and XML + HTML sitemaps.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/brx.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/333489","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/brx.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/brx.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/brx.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=333489"}],"author":[{"embeddable":true,"href":"https:\/\/brx.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/turnkeydirectories"}],"wp:attachment":[{"href":"https:\/\/brx.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=333489"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/brx.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=333489"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/brx.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=333489"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/brx.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=333489"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/brx.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=333489"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/brx.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=333489"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}