{"id":325362,"date":"2026-06-19T08:20:23","date_gmt":"2026-06-19T08:20:23","guid":{"rendered":"https:\/\/en-gb.wordpress.org\/plugins\/townsmith-local-pages-engine\/"},"modified":"2026-07-03T13:17:11","modified_gmt":"2026-07-03T13:17:11","slug":"townsmith-local-pages-engine","status":"publish","type":"plugin","link":"https:\/\/fuc.wordpress.org\/plugins\/townsmith-local-pages-engine\/","author":23515379,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.1.2","stable_tag":"1.1.2","tested":"7.0","requires":"6.4","requires_php":"8.0","requires_plugins":null,"header_name":"Townsmith Local Pages Engine \u2013 Service Area & Location Page Generator","header_author":"Stephen Evans","header_description":"Generate and manage quality-controlled service-area landing pages \u2014 services \u00d7 areas from one blueprint, as real, editable WordPress pages.","assets_banners_color":"585c75","last_updated":"2026-07-03 13:17:11","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/townsmithwp.com\/","header_author_uri":"https:\/\/townsmithwp.com\/about\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":234,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","faq","changelog"],"tags":{"1.0.1":{"tag":"1.0.1","author":"thestephenevans","date":"2026-06-19 09:08:38"},"1.0.2":{"tag":"1.0.2","author":"thestephenevans","date":"2026-06-19 15:00:42"},"1.0.3":{"tag":"1.0.3","author":"thestephenevans","date":"2026-06-22 08:17:13"},"1.0.4":{"tag":"1.0.4","author":"thestephenevans","date":"2026-06-22 13:00:08"},"1.1.0":{"tag":"1.1.0","author":"thestephenevans","date":"2026-06-22 14:15:48"},"1.1.1":{"tag":"1.1.1","author":"thestephenevans","date":"2026-07-03 10:46:42"},"1.1.2":{"tag":"1.1.2","author":"thestephenevans","date":"2026-07-03 13:17:11"}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3578234,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3578234,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256},"icon.svg":{"filename":"icon.svg","revision":3578234,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3578234,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3578234,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{"blueprint.json":{"filename":"blueprint.json","revision":3598078,"resolution":false,"location":"assets","locale":"","contents":"{\"landingPage\":\"\\\/wp-admin\\\/admin.php?page=townsmith-quality\",\"preferredVersions\":{\"php\":\"8.2\",\"wp\":\"latest\"},\"phpExtensionBundles\":[\"kitchen-sink\"],\"steps\":[{\"step\":\"login\",\"username\":\"admin\",\"password\":\"password\"},{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"townsmith-local-pages-engine\"},\"options\":{\"activate\":true}},{\"step\":\"installTheme\",\"themeZipFile\":{\"resource\":\"wordpress.org\\\/themes\",\"slug\":\"astra\"}},{\"step\":\"writeFile\",\"path\":\"\\\/wordpress\\\/townsmith-demo-seed.php\",\"data\":\"<?php\\n\\\/**\\n * Townsmith Local Pages Engine - demo seed.\\n *\\n * Creates a believable trades company, 3 service \\\"templates\\\" (blueprints),\\n * 32 service areas, then generates the full matrix (~96 location pages) and\\n * scores them. A subset of areas is given genuine local detail so the Quality\\n * Score and Safety Check show a realistic mixed verdict, not an artificial\\n * all-green or all-red.\\n *\\n * Run on any WordPress with the plugin active:\\n *     wp eval-file seed.php --user=admin\\n * or it is embedded into the WordPress Playground blueprint (blueprint.json).\\n *\\n * Safe to read: this only creates demo content on the current site.\\n *\\n * @package Townsmith\\\\LPE\\\\Demo\\n *\\\/\\n\\nif ( ! defined( 'ABSPATH' ) ) {\\n\\tfwrite( STDERR, \\\"Run inside WordPress (wp eval-file seed.php --user=admin).\\\\n\\\" );\\n\\treturn;\\n}\\n\\nif ( ! class_exists( '\\\\\\\\Townsmith\\\\\\\\LPE\\\\\\\\Engine\\\\\\\\GenerationPlanner' ) ) {\\n\\techo \\\"Townsmith Local Pages Engine is not active. Activate the plugin first.\\\\n\\\";\\n\\treturn;\\n}\\n\\n\\\/\\\/ Belt and braces: generation needs a user with edit_pages for content filtering.\\nif ( 0 === get_current_user_id() ) {\\n\\t$admin = get_users( array( 'role' => 'administrator', 'number' => 1, 'fields' => 'ID' ) );\\n\\tif ( ! empty( $admin ) ) {\\n\\t\\twp_set_current_user( (int) $admin[0] );\\n\\t}\\n}\\n\\n\\\/\\\/ The activation redirect would otherwise hijack the first admin load.\\ndelete_transient( 'townsmith_lpe_activation_redirect' );\\n\\n\\\/\\\/ Don't double-seed.\\n$existing = get_posts( array( 'post_type' => \\\\Townsmith\\\\LPE\\\\Data\\\\Services::POST_TYPE, 'posts_per_page' => 1, 'fields' => 'ids', 'post_status' => 'any' ) );\\nif ( ! empty( $existing ) ) {\\n\\techo \\\"Demo already seeded (services exist). Nothing to do.\\\\n\\\";\\n\\treturn;\\n}\\n\\n\\\/* -------------------------------------------------------------------------\\n * 1. Business profile (feeds merge tokens + LocalBusiness schema)\\n * ---------------------------------------------------------------------- *\\\/\\nupdate_option(\\n\\t\\\\Townsmith\\\\LPE\\\\Data\\\\BusinessProfile::OPTION,\\n\\tarray(\\n\\t\\t'business_name'   => 'Meridian Plumbing & Heating',\\n\\t\\t'phone'           => '01273 555 0142',\\n\\t\\t'address_line1'   => '14 Trafalgar Street',\\n\\t\\t'address_line2'   => '',\\n\\t\\t'city'            => 'Brighton',\\n\\t\\t'postcode'        => 'BN1 4EQ',\\n\\t\\t'country'         => 'United Kingdom',\\n\\t\\t'opening_hours'   => 'Mon-Fri 08:00-18:00, Sat 09:00-13:00',\\n\\t\\t'gbp_url'         => 'https:\\\/\\\/maps.google.com\\\/?cid=demo',\\n\\t\\t'primary_cta_url' => '#request-a-quote',\\n\\t\\t'logo_id'         => 0,\\n\\t)\\n);\\n\\n\\\/* -------------------------------------------------------------------------\\n * 2. Services (each becomes one \\\"template\\\" via its own blueprint)\\n * ---------------------------------------------------------------------- *\\\/\\n$service_defs = array(\\n\\t'boiler-installation' => array(\\n\\t\\t'title'   => 'Boiler Installation',\\n\\t\\t'excerpt' => 'new boiler supply and installation by Gas Safe registered engineers',\\n\\t),\\n\\t'boiler-repair' => array(\\n\\t\\t'title'   => 'Boiler Repair',\\n\\t\\t'excerpt' => 'same-day boiler repair, fault finding and breakdown cover',\\n\\t),\\n\\t'central-heating' => array(\\n\\t\\t'title'   => 'Central Heating Installation',\\n\\t\\t'excerpt' => 'full central heating design, power flushing and installation',\\n\\t),\\n);\\n\\n$service_ids = array();\\nforeach ( $service_defs as $key => $def ) {\\n\\t$service_ids[ $key ] = (int) wp_insert_post(\\n\\t\\tarray(\\n\\t\\t\\t'post_type'    => \\\\Townsmith\\\\LPE\\\\Data\\\\Services::POST_TYPE,\\n\\t\\t\\t'post_title'   => $def['title'],\\n\\t\\t\\t'post_excerpt' => $def['excerpt'],\\n\\t\\t\\t'post_status'  => 'publish',\\n\\t\\t)\\n\\t);\\n}\\n\\n\\\/* -------------------------------------------------------------------------\\n * 3. Service areas (32 towns). A dozen carry a genuine local descriptor;\\n *    the rest are left blank on purpose so the Safety Check has something\\n *    to flag - which is exactly the differentiation story.\\n * ---------------------------------------------------------------------- *\\\/\\n$areas = array(\\n\\t'Brighton'          => 'A dense coastal city where most homes are Victorian terraces and flats, so flue routing and combi swaps are the usual jobs. Parking permits in the central zones need arranging before a fitting day.',\\n\\t'Hove'              => 'Wide Regency townhouses and mansion-block flats dominate Hove, where older system boilers and airing-cupboard cylinders are common and often due replacement.',\\n\\t'Worthing'          => 'A large seaside town with a high proportion of retired homeowners on system boilers, so straight swaps and heating upgrades are the bread and butter here.',\\n\\t'Eastbourne'        => 'Sheltered by the Downs, Eastbourne has many 1930s semis with original pipework, where a power flush usually comes before a new boiler goes in.',\\n\\t'Hastings'          => 'Steep streets and older housing stock in the Old Town make access and flue positioning the first thing we check on a Hastings survey.',\\n\\t'Crawley'           => 'A post-war new town of consistent estate housing, so Crawley jobs are often quick like-for-like combi replacements with predictable pipework.',\\n\\t'Horsham'           => 'A prosperous market town with a mix of period cottages and newer executive homes, where full central heating designs are common.',\\n\\t'Lewes'             => 'Listed and conservation-area properties are frequent in Lewes, so flue and siting approvals matter and we plan them into the quote.',\\n\\t'Uckfield'          => 'A Wealden town with many rural and off-mains-gas homes nearby, where LPG and heat-only systems come up more than average.',\\n\\t'Burgess Hill'      => 'Steady 1960s-90s estate housing makes Burgess Hill straightforward for combi swaps and heating upgrades.',\\n\\t'Haywards Heath'    => 'Commuter-belt family homes around Haywards Heath often want larger-output system boilers and extra zoning for busy households.',\\n\\t'Shoreham-by-Sea'   => 'Riverside and harbour properties in Shoreham can sit in flood-risk zones, so boiler siting above ground-floor level is a regular consideration.',\\n\\t'Littlehampton'     => '', 'Bognor Regis' => '', 'Chichester' => '', 'Arundel' => '',\\n\\t'Steyning'          => '', 'Henfield' => '', 'Hurstpierpoint' => '', 'Newhaven' => '',\\n\\t'Seaford'           => '', 'Peacehaven' => '', 'Polegate' => '', 'Hailsham' => '',\\n\\t'Bexhill-on-Sea'    => '', 'Rye' => '', 'Battle' => '', 'Heathfield' => '',\\n\\t'Crowborough'       => '', 'East Grinstead' => '', 'Redhill' => '', 'Reigate' => '',\\n);\\n\\n\\\/\\\/ The wp.org Live Preview uses a smaller, faster set. Keep a mix of\\n\\\/\\\/ descriptor-rich and blank areas so the Safety Check still shows a real\\n\\\/\\\/ verdict. Define TOWNSMITH_DEMO_MAX_AREAS before requiring this file to cap.\\n$max_areas = defined( 'TOWNSMITH_DEMO_MAX_AREAS' ) ? (int) TOWNSMITH_DEMO_MAX_AREAS : 0;\\nif ( $max_areas > 0 && count( $areas ) > $max_areas ) {\\n\\t$keys  = array_keys( $areas );\\n\\t$front = (int) ceil( $max_areas \\\/ 2 );                 \\\/\\\/ descriptor-rich (first towns)\\n\\t$keep  = array_merge( array_slice( $keys, 0, $front ), array_slice( $keys, -( $max_areas - $front ) ) );\\n\\t$areas = array_intersect_key( $areas, array_flip( $keep ) );\\n}\\n\\n$area_ids = array();\\nforeach ( $areas as $town => $descriptor ) {\\n\\t$id = (int) wp_insert_post(\\n\\t\\tarray(\\n\\t\\t\\t'post_type'   => \\\\Townsmith\\\\LPE\\\\Data\\\\Areas::POST_TYPE,\\n\\t\\t\\t'post_title'  => $town,\\n\\t\\t\\t'post_status' => 'publish',\\n\\t\\t)\\n\\t);\\n\\tupdate_post_meta( $id, \\\\Townsmith\\\\LPE\\\\Data\\\\Areas::META_TYPE, 'town' );\\n\\tif ( '' !== $descriptor ) {\\n\\t\\tupdate_post_meta( $id, \\\\Townsmith\\\\LPE\\\\Data\\\\Areas::META_DESCRIPTOR, $descriptor );\\n\\t}\\n\\t$area_ids[] = $id;\\n}\\n\\n\\\/* -------------------------------------------------------------------------\\n * 4. Three blueprints (one per service) - deliberately different templates\\n * ---------------------------------------------------------------------- *\\\/\\n$local_slot = '<!-- wp:townsmith\\\/local-slot {\\\"lpeId\\\":\\\"slot-local\\\",\\\"name\\\":\\\"local-intro\\\",\\\"label\\\":\\\"Add something specific about {{area}} here\\\"} -->' . \\\"\\\\n\\\"\\n\\t. '<div class=\\\"wp-block-townsmith-local-slot lpe-local-slot\\\"><\\\/div>' . \\\"\\\\n\\\"\\n\\t. '<!-- \\\/wp:townsmith\\\/local-slot -->';\\n\\n$related = '<!-- wp:townsmith\\\/related-areas {\\\"heading\\\":\\\"Areas we cover\\\"} \\\/-->';\\n\\n$blueprint_markup = array();\\n\\n$blueprint_markup['boiler-installation'] = <<<BLOCKS\\n<!-- wp:heading -->\\n<h2>{{service}} in {{area}}<\\\/h2>\\n<!-- \\\/wp:heading -->\\n\\n<!-- wp:paragraph -->\\n<p>{{business_name}} provides {{service}} across {{area}} and the surrounding villages. We are a local team offering {{service_description}}.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\n\\n<!-- wp:townsmith\\\/variation {\\\"lpeId\\\":\\\"var-intro\\\"} -->\\n<!-- wp:townsmith\\\/variant -->\\n<!-- wp:paragraph -->\\n<p>Looking for a new boiler in {{area}}? Our Gas Safe engineers size, supply and fit the right unit for your home, with tidy workmanship and a clear fixed price before we start.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\n<!-- \\\/wp:townsmith\\\/variant -->\\n<!-- wp:townsmith\\\/variant -->\\n<!-- wp:paragraph -->\\n<p>Planning a boiler upgrade in {{area}}? We handle the whole installation from survey to commissioning, and leave your home clean, warm and running efficiently.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\n<!-- \\\/wp:townsmith\\\/variant -->\\n<!-- \\\/wp:townsmith\\\/variation -->\\n\\n{$local_slot}\\n\\n{$related}\\n\\n<!-- wp:paragraph -->\\n<p>Call {{business_name}} on {{phone}} for a free {{service}} quote in {{area}}.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\nBLOCKS;\\n\\n$blueprint_markup['boiler-repair'] = <<<BLOCKS\\n<!-- wp:heading -->\\n<h2>{{service}} in {{area}} - fast, Gas Safe cover<\\\/h2>\\n<!-- \\\/wp:heading -->\\n\\n<!-- wp:paragraph -->\\n<p>Boiler broken down in {{area}}? {{business_name}} offers {{service_description}}, often the same day.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\n\\n<!-- wp:townsmith\\\/variation {\\\"lpeId\\\":\\\"var-repair\\\"} -->\\n<!-- wp:townsmith\\\/variant -->\\n<!-- wp:paragraph -->\\n<p>No heating or hot water in {{area}}? We diagnose the fault, quote before we fix, and carry common parts so most repairs are done in a single visit.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\n<!-- \\\/wp:townsmith\\\/variant -->\\n<!-- wp:townsmith\\\/variant -->\\n<!-- wp:paragraph -->\\n<p>A cold home in {{area}} is never convenient. Our engineers get to you quickly, find the fault properly rather than guessing, and get you back up and running.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\n<!-- \\\/wp:townsmith\\\/variant -->\\n<!-- \\\/wp:townsmith\\\/variation -->\\n\\n{$local_slot}\\n\\n{$related}\\n\\n<!-- wp:paragraph -->\\n<p>Need a repair now? Call {{business_name}} on {{phone}}.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\nBLOCKS;\\n\\n$blueprint_markup['central-heating'] = <<<BLOCKS\\n<!-- wp:heading -->\\n<h2>{{service}} in {{area}}<\\\/h2>\\n<!-- \\\/wp:heading -->\\n\\n<!-- wp:paragraph -->\\n<p>{{business_name}} designs and installs central heating across {{area}}, offering {{service_description}}.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\n\\n<!-- wp:townsmith\\\/variation {\\\"lpeId\\\":\\\"var-ch\\\"} -->\\n<!-- wp:townsmith\\\/variant -->\\n<!-- wp:paragraph -->\\n<p>Upgrading the heating in your {{area}} home? We design the system around how you actually live, balance the radiators, and power flush so it runs clean from day one.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\n<!-- \\\/wp:townsmith\\\/variant -->\\n<!-- wp:townsmith\\\/variant -->\\n<!-- wp:paragraph -->\\n<p>New radiators, smart controls or a full system in {{area}}? We plan the whole job properly and install it with minimal disruption to your home.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\n<!-- \\\/wp:townsmith\\\/variant -->\\n<!-- \\\/wp:townsmith\\\/variation -->\\n\\n{$local_slot}\\n\\n{$related}\\n\\n<!-- wp:paragraph -->\\n<p>Ask {{business_name}} for a central heating design in {{area}}. Call {{phone}}.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\nBLOCKS;\\n\\n$blueprint_ids = array();\\nforeach ( $blueprint_markup as $key => $markup ) {\\n\\t$blueprint_ids[ $key ] = (int) wp_insert_post(\\n\\t\\tarray(\\n\\t\\t\\t'post_type'    => \\\\Townsmith\\\\LPE\\\\Data\\\\Blueprints::POST_TYPE,\\n\\t\\t\\t'post_title'   => $service_defs[ $key ]['title'] . ' Blueprint',\\n\\t\\t\\t'post_content' => $markup,\\n\\t\\t\\t'post_status'  => 'publish',\\n\\t\\t)\\n\\t);\\n}\\n\\n\\\/* -------------------------------------------------------------------------\\n * 5. Generate: each service x all areas, with that service's blueprint\\n * ---------------------------------------------------------------------- *\\\/\\n@set_time_limit( 0 );\\n$planner = new \\\\Townsmith\\\\LPE\\\\Engine\\\\GenerationPlanner();\\n$runner  = new \\\\Townsmith\\\\LPE\\\\Engine\\\\QueueRunner();\\n$total_enqueued = 0;\\n\\nforeach ( $service_ids as $key => $sid ) {\\n\\t$summary = $planner->plan_and_enqueue( array( $sid ), $area_ids, $blueprint_ids[ $key ], get_current_user_id(), true );\\n\\tif ( is_wp_error( $summary ) ) {\\n\\t\\techo 'Planner error for ' . $service_defs[ $key ]['title'] . ': ' . $summary->get_error_message() . \\\"\\\\n\\\";\\n\\t\\tcontinue;\\n\\t}\\n\\t$total_enqueued += (int) $summary['enqueued'];\\n\\techo sprintf( \\\"Enqueued %d pages for %s.\\\\n\\\", $summary['enqueued'], $service_defs[ $key ]['title'] );\\n}\\n\\n\\\/\\\/ Drain the queue.\\n$guard = 0;\\ndo {\\n\\t$progress = $runner->run( 15.0 );\\n\\techo sprintf( \\\"  processed %d, failed %d, remaining %d\\\\n\\\", $progress['processed'], $progress['failed'], $progress['remaining'] );\\n\\t$guard++;\\n} while ( $progress['remaining'] > 0 && $guard < 200 && ( $progress['processed'] > 0 || $progress['failed'] > 0 ) );\\n\\n\\\/* -------------------------------------------------------------------------\\n * 6. Publish two pages per service so there are live front-end examples\\n *    (by default every generated page stays a draft until a human approves).\\n * ---------------------------------------------------------------------- *\\\/\\n$published = 0;\\nforeach ( $service_ids as $sid ) {\\n\\t$pages = get_posts(\\n\\t\\tarray(\\n\\t\\t\\t'post_type'      => 'page',\\n\\t\\t\\t'post_status'    => 'draft',\\n\\t\\t\\t'posts_per_page' => 2,\\n\\t\\t\\t'fields'         => 'ids',\\n\\t\\t\\t\\\/\\\/ phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query\\n\\t\\t\\t'meta_query'     => array(\\n\\t\\t\\t\\tarray( 'key' => \\\\Townsmith\\\\LPE\\\\Engine\\\\Generator::META_SERVICE_ID, 'value' => $sid ),\\n\\t\\t\\t),\\n\\t\\t)\\n\\t);\\n\\tforeach ( $pages as $pid ) {\\n\\t\\twp_update_post( array( 'ID' => (int) $pid, 'post_status' => 'publish' ) );\\n\\t\\t$published++;\\n\\t}\\n}\\n\\n\\\/* -------------------------------------------------------------------------\\n * 7. Pretty permalinks so the generated URLs read properly\\n * ---------------------------------------------------------------------- *\\\/\\nif ( '' === get_option( 'permalink_structure' ) ) {\\n\\tupdate_option( 'permalink_structure', '\\\/%postname%\\\/' );\\n\\tflush_rewrite_rules( false );\\n}\\n\\n$generated = get_posts(\\n\\tarray(\\n\\t\\t'post_type'      => 'page',\\n\\t\\t'post_status'    => 'any',\\n\\t\\t'posts_per_page' => -1,\\n\\t\\t'fields'         => 'ids',\\n\\t\\t'no_found_rows'  => true,\\n\\t\\t\\\/\\\/ phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query\\n\\t\\t'meta_query'     => array( array( 'key' => \\\\Townsmith\\\\LPE\\\\Engine\\\\Generator::META_MANAGED, 'value' => '1' ) ),\\n\\t)\\n);\\n\\necho \\\"\\\\n=== Demo seeded ===\\\\n\\\";\\necho sprintf( \\\"Services: %d   Areas: %d   Blueprints: %d\\\\n\\\", count( $service_ids ), count( $area_ids ), count( $blueprint_ids ) );\\necho sprintf( \\\"Generated location pages: %d  (published live: %d, the rest are drafts)\\\\n\\\", count( $generated ), $published );\\necho \\\"Open Local Pages > Quality to see the Safety Check verdict.\\\\n\\\";\\n\"},{\"step\":\"runPHP\",\"code\":\"<?php require_once '\\\/wordpress\\\/wp-load.php'; if (!defined('TOWNSMITH_DEMO_MAX_AREAS')) define('TOWNSMITH_DEMO_MAX_AREAS', 6); $u = get_users(array('role'=>'administrator','number'=>1,'fields'=>'ID')); if (!empty($u)) { wp_set_current_user((int)$u[0]); } require '\\\/wordpress\\\/townsmith-demo-seed.php';\"}]}"}},"all_blocks":{"townsmith\/variation":{"$schema":"https:\/\/schemas.wp.org\/trunk\/block.json","apiVersion":3,"name":"townsmith\/variation","title":"Content Variation","category":"lpe","icon":"randomize","description":"Holds two to five alternative content sets; each generated page deterministically uses one of them.","textdomain":"townsmith-local-pages-engine","attributes":{"lpeId":{"type":"string","default":""}},"supports":{"html":false,"reusable":false},"editorScript":"file:.\/index.js","editorStyle":"file:.\/index.css"},"townsmith\/local-slot":{"$schema":"https:\/\/schemas.wp.org\/trunk\/block.json","apiVersion":3,"name":"townsmith\/local-slot","title":"Local Content Slot","category":"lpe","icon":"edit-location","description":"A named slot that starts empty on every generated page and is filled with genuinely local content, page by page.","textdomain":"townsmith-local-pages-engine","attributes":{"lpeId":{"type":"string","default":""},"name":{"type":"string","default":""},"label":{"type":"string","default":"Add something specific about {{area}} here"}},"supports":{"html":false,"reusable":false},"editorScript":"file:.\/index.js","editorStyle":"file:.\/index.css"},"townsmith\/related-areas":{"$schema":"https:\/\/schemas.wp.org\/trunk\/block.json","apiVersion":3,"name":"townsmith\/related-areas","title":"Related Areas","category":"lpe","icon":"location-alt","description":"Links to sibling area pages, baked in at generation so they keep working without the plugin.","textdomain":"townsmith-local-pages-engine","attributes":{"count":{"type":"number","default":6},"heading":{"type":"string","default":"Areas we cover"},"links":{"type":"array","default":[]},"serviceId":{"type":"number","default":0}},"supports":{"html":false,"reusable":false},"editorScript":"file:.\/index.js"},"townsmith\/variant":{"$schema":"https:\/\/schemas.wp.org\/trunk\/block.json","apiVersion":3,"name":"townsmith\/variant","title":"Variant","category":"lpe","icon":"media-default","description":"One alternative content set inside a Content Variation block.","textdomain":"townsmith-local-pages-engine","parent":["townsmith\/variation"],"supports":{"html":false,"reusable":false,"inserter":false},"editorScript":"file:.\/index.js"}},"tagged_versions":["1.0.1","1.0.2","1.0.3","1.0.4","1.1.0","1.1.1","1.1.2"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3578234,"resolution":"1","location":"assets","locale":"","width":1280,"height":832},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3578234,"resolution":"2","location":"assets","locale":"","width":1280,"height":832},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3578234,"resolution":"3","location":"assets","locale":"","width":1280,"height":832},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3578297,"resolution":"4","location":"assets","locale":"","width":1280,"height":832},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3578297,"resolution":"5","location":"assets","locale":"","width":1280,"height":832},"screenshot-6.png":{"filename":"screenshot-6.png","revision":3578297,"resolution":"6","location":"assets","locale":"","width":1280,"height":832}},"screenshots":{"1":"The dashboard: a services \u00d7 areas matrix showing every location page's status and Quality Score, with bulk generation.","2":"One blueprint in the block editor: merge tokens, copy variants that rotate per page, and a local content slot that starts empty for you to fill.","3":"The Quality screen: every generated location page scored against its closest sibling, with a plain-English list of what to fix.","4":"The setup wizard: define your business, your services and the areas you cover, then pick a blueprint.","5":"The pre-publish Quality Score panel inside the block editor: every page scored before it goes live, with the breakdown and what to fix.","6":"The blueprint sync review: change the blueprint once and see what would update on each location page, with your edited blocks kept untouched."}},"plugin_section":[],"plugin_tags":[1600,1122,257913,131713,258563],"plugin_category":[55],"plugin_contributors":[267842],"plugin_business_model":[],"class_list":["post-325362","plugin","type-plugin","status-publish","hentry","plugin_tags-landing-pages","plugin_tags-local-seo","plugin_tags-location-pages","plugin_tags-page-generator","plugin_tags-service-area-pages","plugin_category-seo-and-marketing","plugin_contributors-thestephenevans","plugin_committers-thestephenevans"],"banners":{"banner":"https:\/\/ps.w.org\/townsmith-local-pages-engine\/assets\/banner-772x250.png?rev=3578234","banner_2x":"https:\/\/ps.w.org\/townsmith-local-pages-engine\/assets\/banner-1544x500.png?rev=3578234","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/townsmith-local-pages-engine\/assets\/icon.svg?rev=3578234","icon":"https:\/\/ps.w.org\/townsmith-local-pages-engine\/assets\/icon.svg?rev=3578234","icon_2x":false,"generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/townsmith-local-pages-engine\/assets\/screenshot-1.png?rev=3578234","caption":"The dashboard: a services \u00d7 areas matrix showing every location page's status and Quality Score, with bulk generation."},{"src":"https:\/\/ps.w.org\/townsmith-local-pages-engine\/assets\/screenshot-2.png?rev=3578234","caption":"One blueprint in the block editor: merge tokens, copy variants that rotate per page, and a local content slot that starts empty for you to fill."},{"src":"https:\/\/ps.w.org\/townsmith-local-pages-engine\/assets\/screenshot-3.png?rev=3578234","caption":"The Quality screen: every generated location page scored against its closest sibling, with a plain-English list of what to fix."},{"src":"https:\/\/ps.w.org\/townsmith-local-pages-engine\/assets\/screenshot-4.png?rev=3578297","caption":"The setup wizard: define your business, your services and the areas you cover, then pick a blueprint."},{"src":"https:\/\/ps.w.org\/townsmith-local-pages-engine\/assets\/screenshot-5.png?rev=3578297","caption":"The pre-publish Quality Score panel inside the block editor: every page scored before it goes live, with the breakdown and what to fix."},{"src":"https:\/\/ps.w.org\/townsmith-local-pages-engine\/assets\/screenshot-6.png?rev=3578297","caption":"The blueprint sync review: change the blueprint once and see what would update on each location page, with your edited blocks kept untouched."}],"raw_content":"<!--section=description-->\n<p>Townsmith Local Pages Engine is a service-area and location page generator for local service businesses and the agencies that build their sites. Define your services and the areas you cover, design one page blueprint in the block editor, and generate the matrix of location landing pages as real WordPress pages, each individually editable, never auto-published, and each held to a per-page Quality Score before it goes live.<\/p>\n\n<p>It is built for the local SEO work real businesses actually do: a plumber, electrician or HVAC firm that needs a clear page for every city and service area it covers, with genuine local detail on each one. Unlike bulk page generators, Local Pages Engine is built quality-first: fewer pages, enforced uniqueness, and publish gates designed to keep the pages it produces defensible under Google's scaled-content and doorway-page policies.<\/p>\n\n<p><strong>What it does<\/strong><\/p>\n\n<ul>\n<li><strong>Real pages, never virtual.<\/strong> Generated pages are ordinary WordPress pages: editable in the block editor, owned by you, and fully intact if the plugin is ever deactivated. Merge tokens are resolved at generation time, not at render time.<\/li>\n<li><strong>One blueprint, deterministic variation.<\/strong> Design a single template with merge tokens, copy variations that rotate deterministically per page, and a local content slot that starts empty on every page, because the genuinely local part is yours to write.<\/li>\n<li><strong>A Quality Score with teeth.<\/strong> Every generated page is scored 0\u2013100: similarity against its sibling pages (5-word shingle comparison), local substance (filled slots, area descriptors, local imagery), and a thin-content floor. The pre-publish panel shows exactly what to fix and links to the spot, and warns before a below-threshold page publishes.<\/li>\n<li><strong>A safety check, before Google's.<\/strong> A portfolio Safety Check reads every page's score into one plain-English verdict: how many pages are safe to publish and which look like the near-duplicate \"doorway\" pages Google demotes. In the block editor, Twin Finder shows the exact sentences a page shares with its closest sibling, so you know precisely what to rewrite in local terms. It is guidance computed entirely on your own site, never a guarantee from Google and never a call home.<\/li>\n<li><strong>Schema that cooperates.<\/strong> One JSON-LD graph per page: LocalBusiness, Service with areaServed, breadcrumbs, FAQ. If Yoast SEO, Rank Math, All in One SEO or SEOPress is active, Local Pages Engine steps back and emits only the Service piece, wired to the host plugin's organisation, never a duplicate LocalBusiness.<\/li>\n<li><strong>Hub-and-spoke linking that survives.<\/strong> Each service gets a hub page; each area page carries an \"Areas we cover\" block and a breadcrumb back to its hub. The links are baked into the saved markup, refreshed automatically as the area set changes, and keep working even with the plugin deactivated.<\/li>\n<li><strong>Safe sync.<\/strong> When the blueprint changes, a review screen shows exactly what would change on each page. Blocks you have edited are yours: sync never overwrites them, and a deleted block is never re-added. No silent background rewrites, ever.<\/li>\n<li><strong>Resumable background generation.<\/strong> Generation runs through a batched queue built for cheap shared hosting. Interrupt it anywhere and it resumes without duplicates.<\/li>\n<\/ul>\n\n<p>Generation is unlimited: every service across every area, with no page caps.<\/p>\n\n<p>Local Pages Engine runs entirely on your own site: no external services, no API calls, no AI generation, no telemetry. Your data never leaves your install.<\/p>\n\n<p><strong>The Pro add-on<\/strong><\/p>\n\n<p>This plugin is complete as it stands: unlimited generation across every service and area, the full Quality Score, deterministic variant rotation, the advisory publish gate, schema, linking and sync. A separate Pro add-on, distributed from townsmithwp.com, builds on this plugin's public extension points to add a reusable local-proof library for the area content slot, a portfolio duplicate radar, a pre-generation saturation planner, self-hosted conversion attribution, bulk CSV import and export, scheduled drip publishing, portable blueprint kits, a multi-site command centre for agencies, a signed defensibility dossier, an enforceable publish gate, bulk re-scoring and sync, geo-ordered related areas, noindex-below-threshold, and slug updates that serve their own 301 redirects or report each move to your redirect manager. The Pro add-on is optional; the free plugin needs none of it to generate, score, and publish pages.<\/p>\n\n<p><strong>Documentation and guides<\/strong><\/p>\n\n<p>Full documentation lives at <a href=\"https:\/\/townsmithwp.com\/docs\/\">townsmithwp.com\/docs<\/a>. The guides library covers the strategy side, including <a href=\"https:\/\/townsmithwp.com\/guides\/doorway-pages\/\">doorway pages and the line Google draws<\/a> and <a href=\"https:\/\/townsmithwp.com\/guides\/how-many-location-pages\/\">how many location pages you should build<\/a>.<\/p>\n\n<h3>Development<\/h3>\n\n<p>The human-readable source of the compiled files in <code>build\/<\/code> ships in this plugin's <code>src\/<\/code> directory, alongside <code>package.json<\/code> and <code>webpack.config.js<\/code>. To rebuild: <code>npm install &amp;&amp; npm run build<\/code> (uses @wordpress\/scripts). Development happens in a private repository; the distributed zip always contains the complete, current source.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"what%20is%20a%20service-area%20or%20location%20page%3F\"><h3>What is a service-area or location page?<\/h3><\/dt>\n<dd><p>A landing page targeted at one service in one place you cover, for example \"Boiler Repair in Leeds\" or \"Emergency Electrician in Bristol\". Local Pages Engine builds the matrix of these pages from your services and areas, but leaves a local content slot empty on each so the genuinely local writing is yours.<\/p><\/dd>\n<dt id=\"is%20this%20a%20bulk%20page%20generator%3F\"><h3>Is this a bulk page generator?<\/h3><\/dt>\n<dd><p>No, and deliberately so. Local Pages Engine is built quality-first for local SEO, with sibling-similarity scoring, a thin-content floor and publish gates. It is designed to keep the pages it produces defensible under Google's scaled-content and doorway-page guidance, not to spin thousands of near-identical pages.<\/p><\/dd>\n<dt id=\"does%20this%20plugin%20send%20my%20data%20anywhere%3F\"><h3>Does this plugin send my data anywhere?<\/h3><\/dt>\n<dd><p>No. Everything runs on your own WordPress install. There are no external API calls, no AI services, and no telemetry of any kind.<\/p><\/dd>\n<dt id=\"what%20happens%20to%20generated%20pages%20if%20i%20deactivate%20or%20uninstall%20the%20plugin%3F\"><h3>What happens to generated pages if I deactivate or uninstall the plugin?<\/h3><\/dt>\n<dd><p>Nothing. Generated pages are real WordPress pages and remain fully intact, editable, and rendering, including their internal links, which are baked into the saved markup. Uninstalling removes the plugin's settings and queue table only (and even that can be kept with a setting).<\/p><\/dd>\n<dt id=\"will%20it%20conflict%20with%20my%20seo%20plugin%3F\"><h3>Will it conflict with my SEO plugin?<\/h3><\/dt>\n<dd><p>No. Yoast SEO, Rank Math, All in One SEO and SEOPress are detected at render time. When one is active, Local Pages Engine suppresses its own meta description and emits only the Service schema piece, referencing your SEO plugin's organisation data.<\/p><\/dd>\n<dt id=\"can%20it%20overwrite%20content%20i%27ve%20edited%3F\"><h3>Can it overwrite content I've edited?<\/h3><\/dt>\n<dd><p>No. The moment you edit a generated block, that block is yours. The sync flow updates only untouched blocks and shows you a per-page preview before anything is applied.<\/p><\/dd>\n<dt id=\"why%20did%20publishing%20my%20page%20get%20blocked%3F\"><h3>Why did publishing my page get blocked?<\/h3><\/dt>\n<dd><p>Its Quality Score is below your threshold and the Pro add-on's enforcement mode is switched on (this plugin only warns). The pre-publish checklist lists exactly what to fix: usually filling the local content slot, writing an area descriptor, or making the copy less similar to a sibling page.<\/p><\/dd>\n<dt id=\"will%20location%20pages%20get%20my%20site%20penalised%20by%20google%3F\"><h3>Will location pages get my site penalised by Google?<\/h3><\/dt>\n<dd><p>Not for being location pages. Google's scaled-content and doorway-page policies target near-identical pages built only to rank: many pages that read the same with a town name swapped. That is exactly what this plugin is built to prevent: every page is scored against its closest sibling, thin pages are flagged before they publish, and the Safety Check gives your whole portfolio a plain-English verdict before Google sees it.<\/p><\/dd>\n<dt id=\"does%20it%20use%20ai%20to%20write%20my%20content%3F\"><h3>Does it use AI to write my content?<\/h3><\/dt>\n<dd><p>No. This plugin contains no AI and makes no external calls: generation merges your own blueprint and copy variants deterministically, and the local content slot stays empty until a human fills it. The optional Pro add-on includes an off-by-default assistant that can reword a single sentence you select. It assists, never generates pages, and the free plugin needs none of it.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.1.2<\/h4>\n\n<ul>\n<li>Copy editing pass across the admin screens and this description. No change to generation, scoring, schema, linking or sync.<\/li>\n<\/ul>\n\n<h4>1.1.1<\/h4>\n\n<ul>\n<li>Added bundled translations for German, French, Spanish, Italian, Dutch and Brazilian Portuguese, covering the admin screens and the editor panels. Official language packs from translate.wordpress.org take over automatically as each locale is approved. No change to generation, scoring, schema, linking or sync.<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>New: a Page Safety check. On the Quality screen, a plain-English verdict shows how many of your location pages are safe to publish and which look too similar or too thin, before Google sees them. In the block editor, Twin Finder reveals the exact sentences a page shares with its closest sibling so you can rewrite them in local terms. Both run entirely on your own site: no external services, no data leaves your install. No change to generation, schema, linking or sync.<\/li>\n<\/ul>\n\n<h4>1.0.4<\/h4>\n\n<ul>\n<li>Added two optional, dismissible prompts on the plugin's own admin screens: a review invitation that appears only after you have published pages with the plugin, and a single pointer to the Pro add-on. Both are easy to dismiss for good, neither is incentivised, and nothing is sent anywhere. No change to generation, scoring, schema, linking or sync.<\/li>\n<\/ul>\n\n<h4>1.0.3<\/h4>\n\n<ul>\n<li>Renamed the plugin to describe what it does (a service-area and location page generator) and refreshed the readme. No change to generation, scoring, schema, linking or sync.<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>Added the plugin homepage and author links to the plugin header.<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Generation runs are now unlimited: every selected service and area is generated, and variation blocks rotate through all authored variants.<\/li>\n<li>Hardened capability checks: page disposal, bulk sync, bulk re-scoring and slug updates now verify edit or delete rights on each affected page rather than a broad role capability.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>First public release: blueprint blocks and merge engine, resumable batched generation, the Quality Score with pre-publish panel and publish gate, schema output with SEO-plugin cooperation, hub-and-spoke linking with deactivation-safe markup, blueprint sync with human-edit protection, and the admin app (wizard, dashboard, quality, settings).<\/li>\n<\/ul>","raw_excerpt":"Generate editable service-area and location pages, with a built-in safety check for the thin, near-duplicate content Google penalises.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/fuc.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/325362","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fuc.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/fuc.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/fuc.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=325362"}],"author":[{"embeddable":true,"href":"https:\/\/fuc.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/thestephenevans"}],"wp:attachment":[{"href":"https:\/\/fuc.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=325362"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/fuc.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=325362"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/fuc.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=325362"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/fuc.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=325362"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/fuc.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=325362"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/fuc.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=325362"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}