{"id":356901,"date":"2026-08-22T13:11:47","date_gmt":"2026-08-22T13:11:47","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/trade-dispatch\/"},"modified":"2026-08-22T13:42:50","modified_gmt":"2026-08-22T13:42:50","slug":"trade-dispatch","status":"publish","type":"plugin","link":"https:\/\/gl.wordpress.org\/plugins\/trade-dispatch\/","author":12029770,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"0.3.53","stable_tag":"0.3.53","tested":"7.0.4","requires":"6.2","requires_php":"7.4","requires_plugins":null,"header_name":"Trade Dispatch","header_author":"Land Tech Web Designs, Corp","header_description":"Field service management for small trades: customers, jobs, scheduling, and a client portal on your own WordPress site.","assets_banners_color":"a0a0a0","last_updated":"2026-08-22 13:42:50","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/tradedispatch.app","header_author_uri":"https:\/\/landtechwebdesigns.com","rating":0,"author_block_rating":0,"active_installs":0,"downloads":110,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"0.3.53":{"tag":"0.3.53","author":"brelandr","date":"2026-08-22 13:42:50"}},"upgrade_notice":{"0.3.47":"<p>Custom-table queries use the WordPress 6.2 identifier placeholder.<\/p>","0.3.46":"<p>Nonce-first inbound handlers and tighter portal capability checks.<\/p>","0.3.45":"<p>Office setup pages, customer-job cleanup, and paginated privacy tools.<\/p>","0.1.0":"<p>Initial release.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3660589,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3660589,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3660589,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3660589,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{"blueprint.json":{"filename":"blueprint.json","revision":3660657,"resolution":false,"location":"assets","locale":"","contents":"{\"$schema\":\"https:\\\/\\\/playground.wordpress.net\\\/blueprint-schema.json\",\"landingPage\":\"\\\/\",\"preferredVersions\":{\"php\":\"8.2\",\"wp\":\"latest\"},\"features\":{\"networking\":true},\"steps\":[{\"step\":\"login\",\"username\":\"admin\",\"password\":\"password\"},{\"step\":\"installPlugin\",\"options\":{\"activate\":true},\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"trade-dispatch\"}},{\"step\":\"writeFile\",\"path\":\"\\\/wordpress\\\/wp-content\\\/mu-plugins\\\/trdsp-playground-pack.css\",\"data\":\"\\\/* Playground pack chrome \\u2014 fullscreen-cover, edge-to-edge. Not enqueued on real shops. *\\\/\\n\\n:root {\\n\\t--trdsp-pack-bg: #1d2327;\\n\\t--trdsp-pack-text: #f0f0f1;\\n\\t--trdsp-pack-accent: #2e8b57;\\n\\t--trdsp-pack-muted: #8c8f94;\\n}\\n\\nhtml,\\nbody.trdsp-playground-pack {\\n\\tmargin: 0;\\n\\tbackground: var(--trdsp-pack-bg);\\n\\tcolor: var(--trdsp-pack-text);\\n}\\n\\nbody.trdsp-playground-pack.admin-bar {\\n\\tmargin-top: 0 !important;\\n}\\n\\nbody.trdsp-playground-pack.home #wpadminbar {\\n\\tdisplay: none;\\n}\\n\\nbody.trdsp-playground-pack {\\n\\t--trdsp-bg: var(--trdsp-pack-bg);\\n\\t--trdsp-text: var(--trdsp-pack-text);\\n\\t--trdsp-accent: var(--trdsp-pack-accent);\\n\\t--trdsp-muted: var(--trdsp-pack-muted);\\n\\t--trdsp-radius: 0;\\n\\t--trdsp-font: \\\"Arial Narrow\\\", \\\"Helvetica Neue\\\", Helvetica, Arial, sans-serif;\\n\\t--trdsp-display: var(--trdsp-font);\\n\\tfont-family: var(--trdsp-font);\\n\\tletter-spacing: 0.01em;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-shell {\\n\\tmin-height: 100vh;\\n\\tmin-height: 100dvh;\\n\\tbackground: var(--trdsp-bg);\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-header {\\n\\tposition: absolute;\\n\\tleft: 0;\\n\\tright: 0;\\n\\ttop: 0;\\n\\tz-index: 20;\\n\\tpadding: 1.1rem 8vw;\\n\\tbackground: linear-gradient(180deg, rgba(0, 0, 0, 0.5), transparent 85%);\\n\\tcolor: #fff;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-header .inner,\\nbody.trdsp-playground-pack .trdsp-theme-footer .inner {\\n\\tdisplay: flex;\\n\\tjustify-content: space-between;\\n\\talign-items: center;\\n\\twidth: 100%;\\n\\tmax-width: none;\\n\\tmargin: 0;\\n\\tgap: 1rem;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-brand {\\n\\tfont-weight: 700;\\n\\tletter-spacing: 0.04em;\\n\\tfont-size: 1.05rem;\\n\\tcolor: #fff;\\n\\ttext-decoration: none;\\n\\ttext-transform: uppercase;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-header nav {\\n\\tdisplay: flex;\\n\\tflex-wrap: wrap;\\n\\tgap: 1.15rem;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-header a,\\nbody.trdsp-playground-pack .trdsp-theme-footer a {\\n\\tcolor: #fff;\\n\\ttext-decoration: none;\\n\\tfont-weight: 650;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-header a:hover,\\nbody.trdsp-playground-pack .trdsp-theme-footer a:hover {\\n\\tcolor: #fff;\\n\\ttext-decoration: underline;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-main {\\n\\tpadding: 0;\\n\\tmax-width: none;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-hero {\\n\\tposition: relative;\\n\\twidth: 100vw;\\n\\tmax-width: 100vw;\\n\\tmargin: 0;\\n\\tmin-height: 100vh;\\n\\tmin-height: 100dvh;\\n\\tbox-sizing: border-box;\\n\\tdisplay: flex;\\n\\tflex-direction: column;\\n\\tjustify-content: flex-end;\\n\\tpadding: 7rem 8vw 4.5rem;\\n\\tcolor: #fff;\\n\\tbackground:\\n\\t\\tlinear-gradient(180deg, rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.72)),\\n\\t\\tlinear-gradient(130deg, var(--trdsp-accent), #0f1416);\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-hero::before {\\n\\tcontent: \\\"\\\";\\n\\tposition: absolute;\\n\\tinset: 0;\\n\\tpointer-events: none;\\n\\tbackground: linear-gradient(135deg, color-mix(in srgb, var(--trdsp-accent) 28%, transparent), transparent 60%);\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-hero-copy {\\n\\tposition: relative;\\n\\tz-index: 1;\\n\\tmax-width: 46rem;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-hero h1 {\\n\\tfont-family: var(--trdsp-display);\\n\\tfont-size: clamp(2.8rem, 8vw, 5.4rem);\\n\\tline-height: 0.92;\\n\\tmax-width: 14ch;\\n\\tmargin: 0 0 0.75rem;\\n\\tletter-spacing: -0.02em;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-hero p {\\n\\tfont-size: clamp(1.05rem, 2vw, 1.35rem);\\n\\tmax-width: 34rem;\\n\\tmargin: 0 0 1.5rem;\\n\\tcolor: rgba(255, 255, 255, 0.88);\\n}\\n\\nbody.trdsp-playground-pack .wp-block-buttons {\\n\\tdisplay: flex;\\n\\tflex-wrap: wrap;\\n\\tgap: 0.75rem;\\n}\\n\\nbody.trdsp-playground-pack .wp-block-button__link {\\n\\tdisplay: inline-block;\\n\\tpadding: 0.8rem 1.25rem;\\n\\tbackground: var(--trdsp-accent);\\n\\tcolor: #fff;\\n\\ttext-decoration: none;\\n\\tfont-weight: 700;\\n\\tborder: 2px solid var(--trdsp-accent);\\n\\tborder-radius: 0;\\n}\\n\\nbody.trdsp-playground-pack .wp-block-button.is-style-outline .wp-block-button__link {\\n\\tbackground: transparent;\\n\\tcolor: #fff;\\n\\tborder-color: #fff;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-band {\\n\\twidth: 100%;\\n\\tpadding: 4rem 8vw;\\n\\tbox-sizing: border-box;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-cards {\\n\\tdisplay: grid;\\n\\tgrid-template-columns: repeat(3, 1fr);\\n\\tgap: 1.25rem;\\n\\tmargin: 0;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-cards .wp-block-column {\\n\\tbackground: color-mix(in srgb, #fff 6%, var(--trdsp-bg));\\n\\tborder: 1px solid color-mix(in srgb, #fff 12%, var(--trdsp-bg));\\n\\tpadding: 1.5rem 1.25rem;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-cards h3 {\\n\\tmargin: 0 0 0.4rem;\\n\\tfont-size: 1.35rem;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-cards p,\\nbody.trdsp-playground-pack .trdsp-theme-trust {\\n\\tmargin: 0;\\n\\tcolor: var(--trdsp-muted);\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-trust {\\n\\ttext-align: center;\\n\\tletter-spacing: 0.08em;\\n\\ttext-transform: uppercase;\\n\\tfont-size: 0.78rem;\\n\\tpadding: 0 8vw 1rem;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-reviews {\\n\\tmargin: 0 8vw 0;\\n\\tpadding: 1.75rem 8vw;\\n\\tbackground: color-mix(in srgb, var(--trdsp-accent) 16%, var(--trdsp-bg));\\n\\tfont-size: clamp(1.15rem, 2.2vw, 1.55rem);\\n\\tfont-family: var(--trdsp-display);\\n\\twidth: auto;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-service-list {\\n\\tpadding: 3.5rem 8vw;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-service-list h2 {\\n\\tmargin: 0 0 1rem;\\n\\tfont-size: clamp(1.6rem, 3vw, 2.2rem);\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-services {\\n\\tmargin: 0;\\n\\tpadding: 0;\\n\\tlist-style: none;\\n\\tdisplay: grid;\\n\\tgrid-template-columns: repeat(3, 1fr);\\n\\tgap: 0.75rem;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-services li {\\n\\tpadding: 0.85rem 1rem;\\n\\tborder-left: 3px solid var(--trdsp-accent);\\n\\tbackground: color-mix(in srgb, #fff 5%, var(--trdsp-bg));\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-book {\\n\\twidth: 100vw;\\n\\tmax-width: 100vw;\\n\\tmargin: 0;\\n\\tpadding: 4.5rem 8vw 5rem;\\n\\tbox-sizing: border-box;\\n\\tbackground: #f0f0f1;\\n\\tcolor: #1d2327;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-book h2 {\\n\\tmargin: 0 0 0.4rem;\\n\\tfont-size: clamp(2rem, 4vw, 3rem);\\n\\tline-height: 1;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-book > p {\\n\\tmargin: 0 0 1.75rem;\\n\\tcolor: #50575e;\\n\\tmax-width: 40rem;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-book .trdsp-booking {\\n\\tmax-width: none;\\n\\tmargin: 0;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-book .trdsp-form {\\n\\tdisplay: grid;\\n\\tgrid-template-columns: 1fr 1fr;\\n\\tgap: 0 1.5rem;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-book .trdsp-form p {\\n\\tmargin: 0 0 1rem;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-book .trdsp-form p:has(textarea),\\nbody.trdsp-playground-pack .trdsp-theme-book .trdsp-form p:has(.trdsp-submit),\\nbody.trdsp-playground-pack .trdsp-theme-book .trdsp-hint,\\nbody.trdsp-playground-pack .trdsp-theme-book .trdsp-slots,\\nbody.trdsp-playground-pack .trdsp-theme-book .trdsp-busy {\\n\\tgrid-column: 1 \\\/ -1;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-book .trdsp-form label {\\n\\tfont-weight: 700;\\n\\tletter-spacing: 0.02em;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-book .trdsp-form input[type=\\\"text\\\"],\\nbody.trdsp-playground-pack .trdsp-theme-book .trdsp-form input[type=\\\"email\\\"],\\nbody.trdsp-playground-pack .trdsp-theme-book .trdsp-form input[type=\\\"datetime-local\\\"],\\nbody.trdsp-playground-pack .trdsp-theme-book .trdsp-form select,\\nbody.trdsp-playground-pack .trdsp-theme-book .trdsp-form textarea {\\n\\twidth: 100%;\\n\\tpadding: 0.7rem 0.75rem;\\n\\tborder: 1px solid #c3c4c7;\\n\\tborder-radius: 0;\\n\\tbackground: #fff;\\n\\tbox-sizing: border-box;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-book .trdsp-submit {\\n\\tbackground: var(--trdsp-accent);\\n\\tcolor: #fff;\\n\\tpadding: 0.85rem 1.4rem;\\n\\tfont-weight: 700;\\n\\tletter-spacing: 0.03em;\\n\\ttext-transform: uppercase;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-preview-logins {\\n\\twidth: 100%;\\n\\tpadding: 3.5rem 8vw 3rem;\\n\\tbox-sizing: border-box;\\n\\tbackground: color-mix(in srgb, #fff 6%, var(--trdsp-bg));\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-preview-logins h2 {\\n\\tmargin: 0 0 0.4rem;\\n\\tfont-size: clamp(1.6rem, 3vw, 2.2rem);\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-preview-logins > p {\\n\\tmargin: 0 0 1.5rem;\\n\\tcolor: var(--trdsp-muted);\\n\\tmax-width: 40rem;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-preview-cards {\\n\\tdisplay: grid;\\n\\tgrid-template-columns: 1fr 1fr;\\n\\tgap: 1.25rem;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-preview-cards > div {\\n\\tpadding: 1.5rem 1.25rem;\\n\\tborder: 1px solid color-mix(in srgb, #fff 12%, var(--trdsp-bg));\\n\\tbackground: color-mix(in srgb, #fff 5%, var(--trdsp-bg));\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-preview-cards h3 {\\n\\tmargin: 0 0 0.5rem;\\n\\tfont-size: 1.25rem;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-preview-cards p {\\n\\tmargin: 0 0 0.65rem;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-preview-cards code {\\n\\tpadding: 0.1rem 0.35rem;\\n\\tbackground: color-mix(in srgb, #000 35%, var(--trdsp-bg));\\n\\tcolor: #fff;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-preview-cards a {\\n\\tcolor: #fff;\\n\\tfont-weight: 700;\\n}\\n\\nbody.trdsp-playground-pack .trdsp-theme-footer {\\n\\tpadding: 1.5rem 8vw;\\n\\tmargin: 0;\\n\\tbackground: #111416;\\n\\tcolor: #f0f0f1;\\n}\\n\\n@media (max-width: 800px) {\\n\\tbody.trdsp-playground-pack .trdsp-theme-cards,\\n\\tbody.trdsp-playground-pack .trdsp-theme-services,\\n\\tbody.trdsp-playground-pack .trdsp-theme-preview-cards,\\n\\tbody.trdsp-playground-pack .trdsp-theme-book .trdsp-form {\\n\\t\\tgrid-template-columns: 1fr;\\n\\t}\\n\\n\\tbody.trdsp-playground-pack .trdsp-theme-header .inner {\\n\\t\\tflex-direction: column;\\n\\t\\talign-items: flex-start;\\n\\t}\\n}\\n\"},{\"step\":\"writeFile\",\"path\":\"\\\/wordpress\\\/wp-content\\\/mu-plugins\\\/trdsp-playground-pack.php\",\"data\":\"<?php\\n\\\/**\\n * Playground-only pack chrome for Trade Dispatch Live Preview.\\n *\\n * Homepage HTML lives in this file (hooks only at load time). Do not add\\n * another .php file under mu-plugins\\\/ \\u2014 WordPress auto-runs those.\\n *\\\/\\nadd_filter(\\n\\t'body_class',\\n\\tstatic function ( $classes ) {\\n\\t\\t$extra = array(\\n\\t\\t\\t'trdsp-playground-pack',\\n\\t\\t\\t'trdsp-has-pack',\\n\\t\\t\\t'trdsp-pack-fullscreen-cover',\\n\\t\\t\\t'trdsp-frame-fullscreen',\\n\\t\\t\\t'trdsp-surface-dark',\\n\\t\\t\\t'trdsp-font-condensed',\\n\\t\\t\\t'trdsp-header-transparent',\\n\\t\\t\\t'trdsp-radius-sharp',\\n\\t\\t\\t'trdsp-pattern-wash',\\n\\t\\t);\\n\\t\\treturn array_merge( (array) $classes, $extra );\\n\\t}\\n);\\n\\nadd_action(\\n\\t'wp_enqueue_scripts',\\n\\tstatic function () {\\n\\t\\twp_enqueue_style(\\n\\t\\t\\t'trdsp-playground-pack',\\n\\t\\t\\tcontent_url( 'mu-plugins\\\/trdsp-playground-pack.css' ),\\n\\t\\t\\tarray(),\\n\\t\\t\\t'0.3.53'\\n\\t\\t);\\n\\t}\\n);\\n\\nadd_action(\\n\\t'template_redirect',\\n\\tstatic function () {\\n\\t\\tif ( ! is_front_page() ) {\\n\\t\\t\\treturn;\\n\\t\\t}\\n\\t\\ttrdsp_playground_render_home();\\n\\t\\texit;\\n\\t}\\n);\\n\\n\\\/**\\n * Full-viewport pack homepage.\\n *\\\/\\nfunction trdsp_playground_render_home() {\\n\\t?><!DOCTYPE html>\\n<html <?php language_attributes(); ?>>\\n<head>\\n\\t<meta charset=\\\"<?php bloginfo( 'charset' ); ?>\\\" \\\/>\\n\\t<meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\" \\\/>\\n\\t<?php wp_head(); ?>\\n<\\\/head>\\n<body <?php body_class(); ?>>\\n<div class=\\\"trdsp-theme-shell\\\">\\n\\t<header class=\\\"trdsp-theme-header\\\"><div class=\\\"inner\\\">\\n\\t\\t<a class=\\\"trdsp-theme-brand\\\" href=\\\"<?php echo esc_url( home_url( '\\\/' ) ); ?>\\\">Greenline Lawn Co.<\\\/a>\\n\\t\\t<nav>\\n\\t\\t\\t<a href=\\\"<?php echo esc_url( home_url( '\\\/' ) ); ?>\\\">Home<\\\/a>\\n\\t\\t\\t<a href=\\\"#services\\\">Services<\\\/a>\\n\\t\\t\\t<a href=\\\"#book\\\">Book<\\\/a>\\n\\t\\t\\t<a href=\\\"<?php echo esc_url( home_url( '\\\/customer-portal\\\/' ) ); ?>\\\">Portal<\\\/a>\\n\\t\\t\\t<a href=\\\"<?php echo esc_url( admin_url( 'admin.php?page=trade-dispatch' ) ); ?>\\\">Office<\\\/a>\\n\\t\\t\\t<a href=\\\"#preview-logins\\\">Logins<\\\/a>\\n\\t\\t<\\\/nav>\\n\\t<\\\/div><\\\/header>\\n\\t<main class=\\\"trdsp-theme-main\\\">\\n\\t\\t<div class=\\\"trdsp-theme-hero\\\">\\n\\t\\t\\t<div class=\\\"trdsp-theme-hero-copy\\\">\\n\\t\\t\\t\\t<h1>Greenline Lawn Co.<\\\/h1>\\n\\t\\t\\t\\t<p>Scheduled lawn care for Austin yards. Book a visit or open the customer portal.<\\\/p>\\n\\t\\t\\t\\t<div class=\\\"wp-block-buttons\\\">\\n\\t\\t\\t\\t\\t<div class=\\\"wp-block-button\\\"><a class=\\\"wp-block-button__link\\\" href=\\\"#book\\\">Book a visit<\\\/a><\\\/div>\\n\\t\\t\\t\\t\\t<div class=\\\"wp-block-button is-style-outline\\\"><a class=\\\"wp-block-button__link\\\" href=\\\"#preview-logins\\\">Office &amp; portal logins<\\\/a><\\\/div>\\n\\t\\t\\t\\t<\\\/div>\\n\\t\\t\\t<\\\/div>\\n\\t\\t<\\\/div>\\n\\t\\t<section class=\\\"trdsp-theme-preview-logins\\\" id=\\\"preview-logins\\\">\\n\\t\\t\\t<h2>Preview logins<\\\/h2>\\n\\t\\t\\t<p>This Playground is a demo shop with sample customers and jobs. Use one account at a time \\u2014 log out to switch.<\\\/p>\\n\\t\\t\\t<div class=\\\"trdsp-theme-preview-cards\\\">\\n\\t\\t\\t\\t<div>\\n\\t\\t\\t\\t\\t<h3>Office (dispatch)<\\\/h3>\\n\\t\\t\\t\\t\\t<p><code>admin<\\\/code> \\\/ <code>password<\\\/code><\\\/p>\\n\\t\\t\\t\\t\\t<p>Jobs, customers, estimates, and the booking request from Alicia Brooks.<\\\/p>\\n\\t\\t\\t\\t\\t<p><a href=\\\"<?php echo esc_url( admin_url( 'admin.php?page=trade-dispatch' ) ); ?>\\\">Open the office<\\\/a><\\\/p>\\n\\t\\t\\t\\t<\\\/div>\\n\\t\\t\\t\\t<div>\\n\\t\\t\\t\\t\\t<h3>Customer portal<\\\/h3>\\n\\t\\t\\t\\t\\t<p><code>maya<\\\/code> \\\/ <code>password<\\\/code><\\\/p>\\n\\t\\t\\t\\t\\t<p>Maya Rivera \\u2014 upcoming mow, last visit, and a sent estimate.<\\\/p>\\n\\t\\t\\t\\t\\t<p><a href=\\\"<?php echo esc_url( wp_logout_url( wp_login_url( home_url( '\\\/customer-portal\\\/' ) ) ) ); ?>\\\">Log out, then sign in as Maya<\\\/a><\\\/p>\\n\\t\\t\\t\\t<\\\/div>\\n\\t\\t\\t<\\\/div>\\n\\t\\t<\\\/section>\\n\\t\\t<div class=\\\"trdsp-theme-band\\\" id=\\\"services\\\">\\n\\t\\t\\t<div class=\\\"wp-block-columns trdsp-theme-cards\\\">\\n\\t\\t\\t\\t<div class=\\\"wp-block-column\\\"><h3>Weekly mow<\\\/h3><p>Edge, blow, and a consistent route.<\\\/p><\\\/div>\\n\\t\\t\\t\\t<div class=\\\"wp-block-column\\\"><h3>Aeration<\\\/h3><p>Spring cores so water and seed reach the soil.<\\\/p><\\\/div>\\n\\t\\t\\t\\t<div class=\\\"wp-block-column\\\"><h3>Irrigation<\\\/h3><p>Heads, zones, and a leak check before summer.<\\\/p><\\\/div>\\n\\t\\t\\t<\\\/div>\\n\\t\\t<\\\/div>\\n\\t\\t<p class=\\\"trdsp-theme-trust\\\">Licensed \\u00b7 Your WordPress site \\u00b7 Your data<\\\/p>\\n\\t\\t<div class=\\\"trdsp-theme-reviews\\\"><p>\\u201cThey showed up when they said they would, left the gate as they found it, and the next visit was already on the calendar.\\u201d \\u2014 Jordan Hale, East Austin<\\\/p><\\\/div>\\n\\t\\t<div class=\\\"trdsp-theme-service-list\\\">\\n\\t\\t\\t<h2>On the truck this week<\\\/h2>\\n\\t\\t\\t<ul class=\\\"trdsp-theme-services\\\"><li>Weekly mow<\\\/li><li>Aeration<\\\/li><li>Irrigation<\\\/li><\\\/ul>\\n\\t\\t<\\\/div>\\n\\t\\t<section class=\\\"trdsp-theme-book\\\" id=\\\"book\\\">\\n\\t\\t\\t<h2>Ready for the next visit?<\\\/h2>\\n\\t\\t\\t<p>Request a time. The office confirms the appointment before a customer account is created.<\\\/p>\\n\\t\\t\\t<?php echo do_shortcode( '[trdsp_booking]' ); ?>\\n\\t\\t<\\\/section>\\n\\t<\\\/main>\\n\\t<footer class=\\\"trdsp-theme-footer\\\"><div class=\\\"inner\\\">\\n\\t\\t<span>Trade Dispatch preview<\\\/span>\\n\\t\\t<nav>\\n\\t\\t\\t<a href=\\\"<?php echo esc_url( admin_url( 'admin.php?page=trade-dispatch' ) ); ?>\\\">Jobs<\\\/a>\\n\\t\\t\\t<a href=\\\"<?php echo esc_url( home_url( '\\\/customer-portal\\\/' ) ); ?>\\\">Portal<\\\/a>\\n\\t\\t<\\\/nav>\\n\\t<\\\/div><\\\/footer>\\n<\\\/div>\\n<?php wp_footer(); ?>\\n<\\\/body>\\n<\\\/html>\\n\\t<?php\\n}\\n\"},{\"step\":\"runPHP\",\"code\":\"<?php\\n\\\/\\\/ trdsp playground seed v4\\nforeach ( array( '\\\/wordpress\\\/wp-content\\\/mu-plugins\\\/trdsp-playground-home.php' ) as $trdsp_stale ) {\\n\\tif ( is_file( $trdsp_stale ) ) {\\n\\t\\tunlink( $trdsp_stale );\\n\\t}\\n}\\nif ( ! defined( 'WP_USE_THEMES' ) ) {\\n\\tdefine( 'WP_USE_THEMES', false );\\n}\\nrequire_once '\\\/wordpress\\\/wp-load.php';\\n\\nif ( ! class_exists( 'TRDSP_Customers' ) || ! class_exists( 'TRDSP_Jobs' ) ) {\\n\\treturn;\\n}\\n\\n$admin    = get_user_by( 'login', 'admin' );\\n$admin_id = $admin ? (int) $admin->ID : 1;\\n\\nupdate_option( 'blogname', 'Greenline Lawn Co.' );\\nupdate_option( 'blogdescription', 'Austin, TX' );\\n\\n$settings = get_option( 'trdsp_settings', array() );\\nif ( ! is_array( $settings ) ) {\\n\\t$settings = array();\\n}\\n$settings['business_name']      = 'Greenline Lawn Co.';\\n$settings['notify_email']       = 'admin@example.com';\\n$settings['booking_hours_hint'] = 'Weekdays 8:00 AM \\u2013 5:00 PM. Rain days move to the next open slot.';\\n$settings['booking_open']       = '08:00';\\n$settings['booking_close']      = '17:00';\\n$settings['booking_days']       = array( 1, 2, 3, 4, 5 );\\nupdate_option( 'trdsp_settings', $settings );\\n\\n$now         = gmdate( 'Y-m-d H:i:s' );\\n$service_ids = array();\\nif ( class_exists( 'TRDSP_Services' ) ) {\\n\\tglobal $wpdb;\\n\\t$table   = TRDSP_Services::table();\\n\\t$catalog = array(\\n\\t\\tarray( 'Weekly mow', 'Edge, blow, and a consistent route.', 60, '55.00' ),\\n\\t\\tarray( 'Aeration', 'Spring cores so water and seed reach the soil.', 90, '175.00' ),\\n\\t\\tarray( 'Irrigation', 'Heads, zones, and a leak check before summer.', 75, '125.00' ),\\n\\t);\\n\\tforeach ( $catalog as $item ) {\\n\\t\\t$wpdb->insert(\\n\\t\\t\\t$table,\\n\\t\\t\\tarray(\\n\\t\\t\\t\\t'name'            => $item[0],\\n\\t\\t\\t\\t'description'     => $item[1],\\n\\t\\t\\t\\t'default_minutes' => $item[2],\\n\\t\\t\\t\\t'default_amount'  => $item[3],\\n\\t\\t\\t\\t'created_at'      => $now,\\n\\t\\t\\t\\t'updated_at'      => $now,\\n\\t\\t\\t),\\n\\t\\t\\tarray( '%s', '%s', '%d', '%s', '%s', '%s' )\\n\\t\\t);\\n\\t\\t$service_ids[] = (int) $wpdb->insert_id;\\n\\t}\\n}\\n$mow   = isset( $service_ids[0] ) ? $service_ids[0] : 0;\\n$aerate = isset( $service_ids[1] ) ? $service_ids[1] : 0;\\n$irrig = isset( $service_ids[2] ) ? $service_ids[2] : 0;\\n\\n$rivera = TRDSP_Customers::save(\\n\\tarray(\\n\\t\\t'name'      => 'Maya Rivera',\\n\\t\\t'email'     => 'maya.rivera@example.com',\\n\\t\\t'phone'     => '(555) 201-4400',\\n\\t\\t'address_1' => '1842 Oak Ridge Dr',\\n\\t\\t'city'      => 'Austin',\\n\\t\\t'state'     => 'TX',\\n\\t\\t'postcode'  => '78704',\\n\\t\\t'notes'     => 'Prefers morning windows. Dog in backyard \\u2014 use side gate.',\\n\\t)\\n);\\n$chen = TRDSP_Customers::save(\\n\\tarray(\\n\\t\\t'name'      => 'James Chen',\\n\\t\\t'email'     => 'james.chen@example.com',\\n\\t\\t'phone'     => '(555) 201-4411',\\n\\t\\t'address_1' => '90 Commerce Blvd',\\n\\t\\t'city'      => 'Austin',\\n\\t\\t'state'     => 'TX',\\n\\t\\t'postcode'  => '78745',\\n\\t\\t'notes'     => 'Retail suite. Ask for the store manager.',\\n\\t)\\n);\\n$brooks = TRDSP_Customers::save(\\n\\tarray(\\n\\t\\t'name'      => 'Alicia Brooks',\\n\\t\\t'email'     => 'alicia.brooks@example.com',\\n\\t\\t'phone'     => '(555) 201-4422',\\n\\t\\t'address_1' => '512 Cedar Lane',\\n\\t\\t'city'      => 'Austin',\\n\\t\\t'state'     => 'TX',\\n\\t\\t'postcode'  => '78702',\\n\\t\\t'notes'     => 'New customer from the public booking form.',\\n\\t)\\n);\\n\\nif ( is_wp_error( $rivera ) || is_wp_error( $chen ) || is_wp_error( $brooks ) ) {\\n\\treturn;\\n}\\n\\nif ( class_exists( 'TRDSP_Roles' ) ) {\\n\\tTRDSP_Roles::maybe_register();\\n}\\nif ( ! username_exists( 'maya' ) && ! email_exists( 'maya.rivera@example.com' ) ) {\\n\\twp_insert_user(\\n\\t\\tarray(\\n\\t\\t\\t'user_login'   => 'maya',\\n\\t\\t\\t'user_email'   => 'maya.rivera@example.com',\\n\\t\\t\\t'user_pass'    => 'password',\\n\\t\\t\\t'display_name' => 'Maya Rivera',\\n\\t\\t\\t'role'         => 'trdsp_customer',\\n\\t\\t)\\n\\t);\\n}\\n\\n$tomorrow  = gmdate( 'Y-m-d H:i:s', time() + DAY_IN_SECONDS + 9 * HOUR_IN_SECONDS );\\n$today     = gmdate( 'Y-m-d H:i:s', time() + 3 * HOUR_IN_SECONDS );\\n$nextweek  = gmdate( 'Y-m-d H:i:s', time() + 7 * DAY_IN_SECONDS + 10 * HOUR_IN_SECONDS );\\n$yesterday = gmdate( 'Y-m-d H:i:s', time() - DAY_IN_SECONDS );\\n\\n$job_mow = TRDSP_Jobs::save(\\n\\tarray(\\n\\t\\t'customer_id'      => $rivera,\\n\\t\\t'assigned_user_id' => $admin_id,\\n\\t\\t'service_id'       => $mow,\\n\\t\\t'title'            => 'Weekly mow \\u2014 Oak Ridge',\\n\\t\\t'status'           => 'scheduled',\\n\\t\\t'scheduled_at'     => $tomorrow,\\n\\t\\t'gate_notes'       => 'Side gate latch sticks. Code 4412 on the lockbox.',\\n\\t\\t'hazard_notes'     => 'Dog in backyard until 9 AM.',\\n\\t\\t'office_brief'     => 'Edge the walk. Blow the drive toward the street.',\\n\\t\\t'recurrence'       => 'weekly',\\n\\t)\\n);\\nTRDSP_Jobs::save(\\n\\tarray(\\n\\t\\t'customer_id'      => $chen,\\n\\t\\t'assigned_user_id' => $admin_id,\\n\\t\\t'service_id'       => $irrig,\\n\\t\\t'title'            => 'Irrigation leak check',\\n\\t\\t'status'           => 'in_progress',\\n\\t\\t'scheduled_at'     => $today,\\n\\t\\t'office_brief'     => 'Manager on site. Start at zone 3.',\\n\\t)\\n);\\nTRDSP_Jobs::save(\\n\\tarray(\\n\\t\\t'customer_id'  => $brooks,\\n\\t\\t'service_id'   => $aerate,\\n\\t\\t'title'        => 'Aeration request',\\n\\t\\t'status'       => 'requested',\\n\\t\\t'scheduled_at' => '',\\n\\t\\t'office_brief' => 'Came in from the public booking form. Confirm a weekday morning.',\\n\\t)\\n);\\n$done = TRDSP_Jobs::save(\\n\\tarray(\\n\\t\\t'customer_id'      => $rivera,\\n\\t\\t'assigned_user_id' => $admin_id,\\n\\t\\t'service_id'       => $mow,\\n\\t\\t'title'            => 'Weekly mow \\u2014 last visit',\\n\\t\\t'status'           => 'completed',\\n\\t\\t'scheduled_at'     => $yesterday,\\n\\t)\\n);\\nTRDSP_Jobs::save(\\n\\tarray(\\n\\t\\t'customer_id'      => $chen,\\n\\t\\t'assigned_user_id' => $admin_id,\\n\\t\\t'service_id'       => $mow,\\n\\t\\t'title'            => 'Weekly mow \\u2014 Commerce',\\n\\t\\t'status'           => 'scheduled',\\n\\t\\t'scheduled_at'     => $nextweek,\\n\\t\\t'recurrence'       => 'weekly',\\n\\t)\\n);\\n\\nif ( class_exists( 'TRDSP_Notes' ) && ! is_wp_error( $job_mow ) ) {\\n\\tTRDSP_Notes::add( (int) $job_mow, 'Customer asked for a text 30 minutes before arrival.', $admin_id );\\n}\\n\\nif ( class_exists( 'TRDSP_Estimates' ) && ! is_wp_error( $done ) ) {\\n\\tTRDSP_Estimates::save(\\n\\t\\tarray(\\n\\t\\t\\t'customer_id' => $rivera,\\n\\t\\t\\t'job_id'      => $done,\\n\\t\\t\\t'title'       => 'Aeration + extra bags',\\n\\t\\t\\t'amount'      => 210.00,\\n\\t\\t\\t'status'      => 'sent',\\n\\t\\t)\\n\\t);\\n}\\n\\n$home_post = wp_insert_post(\\n\\tarray(\\n\\t\\t'post_title'   => 'Home',\\n\\t\\t'post_name'    => 'home',\\n\\t\\t'post_content' => '<!-- wp:shortcode -->[trdsp_booking]<!-- \\\/wp:shortcode -->',\\n\\t\\t'post_status'  => 'publish',\\n\\t\\t'post_type'    => 'page',\\n\\t),\\n\\ttrue\\n);\\n$portal_post = wp_insert_post(\\n\\tarray(\\n\\t\\t'post_title'   => 'Customer portal',\\n\\t\\t'post_name'    => 'customer-portal',\\n\\t\\t'post_content' => '<!-- wp:shortcode -->[trdsp_portal]<!-- \\\/wp:shortcode -->',\\n\\t\\t'post_status'  => 'publish',\\n\\t\\t'post_type'    => 'page',\\n\\t),\\n\\ttrue\\n);\\n$home_id   = is_wp_error( $home_post ) ? 0 : (int) $home_post;\\n$portal_id = is_wp_error( $portal_post ) ? 0 : (int) $portal_post;\\n\\n$settings = get_option( 'trdsp_settings', array() );\\nif ( ! is_array( $settings ) ) {\\n\\t$settings = array();\\n}\\nif ( $portal_id > 0 ) {\\n\\t$settings['portal_page_id'] = $portal_id;\\n\\tupdate_option( 'trdsp_settings', $settings );\\n}\\nif ( $home_id > 0 ) {\\n\\tupdate_option( 'show_on_front', 'page' );\\n\\tupdate_option( 'page_on_front', $home_id );\\n}\\n\"}]}"}},"all_blocks":{"trade-dispatch\/booking":{"name":"trade-dispatch\/booking","title":"Trade Dispatch Booking"},"trade-dispatch\/portal":{"name":"trade-dispatch\/portal","title":"Trade Dispatch Portal"}},"tagged_versions":["0.3.53"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3660589,"resolution":"1","location":"assets","locale":"","width":1280,"height":800},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3660589,"resolution":"2","location":"assets","locale":"","width":1280,"height":800},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3660589,"resolution":"3","location":"assets","locale":"","width":1280,"height":800},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3660589,"resolution":"4","location":"assets","locale":"","width":1280,"height":800},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3660589,"resolution":"5","location":"assets","locale":"","width":1280,"height":800}},"screenshots":{"1":"Jobs list \u2014 scheduled, in-progress, and requested work with customer and time.","2":"Job edit \u2014 address, gate notes, hazards, assignee, and office brief.","3":"Customers \u2014 names, phones, and property addresses on your WordPress site.","4":"Public booking form (<code>[trdsp_booking]<\/code>) \u2014 visitors request a visit without creating an account.","5":"Settings \u2014 business name, booking hours, and the customer portal page."}},"plugin_section":[262246],"plugin_tags":[269,179524,204259,9023,268],"plugin_category":[39,40],"plugin_contributors":[251791],"plugin_business_model":[],"class_list":["post-356901","plugin","type-plugin","status-publish","hentry","plugin_section-dashboard-widgets","plugin_tags-booking","plugin_tags-dispatch","plugin_tags-field-service","plugin_tags-jobs","plugin_tags-scheduling","plugin_category-business","plugin_category-calendar-and-events","plugin_contributors-brelandr","plugin_committers-brelandr"],"banners":{"banner":"https:\/\/ps.w.org\/trade-dispatch\/assets\/banner-772x250.png?rev=3660589","banner_2x":"https:\/\/ps.w.org\/trade-dispatch\/assets\/banner-1544x500.png?rev=3660589","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/trade-dispatch\/assets\/icon-128x128.png?rev=3660589","icon_2x":"https:\/\/ps.w.org\/trade-dispatch\/assets\/icon-256x256.png?rev=3660589","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/trade-dispatch\/assets\/screenshot-1.png?rev=3660589","caption":"Jobs list \u2014 scheduled, in-progress, and requested work with customer and time."},{"src":"https:\/\/ps.w.org\/trade-dispatch\/assets\/screenshot-2.png?rev=3660589","caption":"Job edit \u2014 address, gate notes, hazards, assignee, and office brief."},{"src":"https:\/\/ps.w.org\/trade-dispatch\/assets\/screenshot-3.png?rev=3660589","caption":"Customers \u2014 names, phones, and property addresses on your WordPress site."},{"src":"https:\/\/ps.w.org\/trade-dispatch\/assets\/screenshot-4.png?rev=3660589","caption":"Public booking form (<code>[trdsp_booking]<\/code>) \u2014 visitors request a visit without creating an account."},{"src":"https:\/\/ps.w.org\/trade-dispatch\/assets\/screenshot-5.png?rev=3660589","caption":"Settings \u2014 business name, booking hours, and the customer portal page."}],"raw_content":"<!--section=description-->\n<p>Trade Dispatch turns a WordPress site into an office dispatch hub. Create customers, schedule jobs, and assign work to crew members (WordPress users) without sending business data to a third-party SaaS.<\/p>\n\n<p>The free plugin is a complete web\/office tool. There are no crew caps and no license checks.<\/p>\n\n<p>Roles (in addition to Administrator):<\/p>\n\n<ul>\n<li><strong>Customer<\/strong> \u2014 created when the office confirms a booking (if that email is new), so the visitor can log in to the portal. An existing WordPress user with the same email is reused (including a Subscriber). The public form does not create an account.<\/li>\n<li><strong>Employee<\/strong> \u2014 sees assigned jobs only.<\/li>\n<li><strong>Dispatcher<\/strong> \u2014 runs the office (jobs, customers, estimates) without editing pages, themes, or plugins.<\/li>\n<\/ul>\n\n<p>Shortcodes and Gutenberg blocks:<\/p>\n\n<ul>\n<li><code>[trdsp_booking]<\/code> or the <strong>Trade Dispatch Booking<\/strong> block \u2014 public booking request form<\/li>\n<li><code>[trdsp_portal]<\/code> or the <strong>Trade Dispatch Portal<\/strong> block \u2014 customer portal for logged-in visitors (matched by email)<\/li>\n<\/ul>\n\n<p>A public booking stores a customer record and a requested job. A Customer-role WordPress account is created only after the office confirms the visit (if that email is new), so the public form cannot create users or send new-account emails. Estimates are office records only and do not process payments.<\/p>\n\n<p>Assigned crew members may receive a next-day job list by email (title, time, and address) at their WordPress user email. Trade Dispatch \u2192 Emails lets you edit every plugin message (plain text, placeholders). Leave a field blank to use the default.<\/p>\n\n<p>Plugin home: https:\/\/tradedispatch.app\nUnminified source: https:\/\/github.com\/brelandr\/trade-dispatch<\/p>\n\n<h3>Try It Live - Preview This Plugin Instantly<\/h3>\n\n<p>Open WordPress Playground with Trade Dispatch installed from WordPress.org. The homepage is a full-width shop pack (hero, services, and a full-bleed <code>[trdsp_booking]<\/code> form). Sample customers, jobs, and an estimate are already loaded.<\/p>\n\n<p>Use one account at a time (log out to switch):<\/p>\n\n<ul>\n<li>Office \/ dispatch: <strong>admin<\/strong> \/ <strong>password<\/strong> \u2014 Jobs, Customers, Estimates, and Alicia Brooks\u2019s booking request. Header <strong>Office<\/strong> or Trade Dispatch in wp-admin.<\/li>\n<li>Customer portal: <strong>maya<\/strong> \/ <strong>password<\/strong> \u2014 Maya Rivera\u2019s upcoming mow, last visit, and a sent estimate. Header <strong>Portal<\/strong> after you sign in as Maya.<\/li>\n<\/ul>\n\n<p><a href=\"https:\/\/playground.wordpress.net\/?blueprint-url=https:\/\/raw.githubusercontent.com\/brelandr\/trade-dispatch\/main\/blueprint.json\">Preview on WordPress Playground<\/a><\/p>\n\n<p>The same blueprint ships in the plugin package as <code>blueprint.json<\/code> (repository root) and <code>assets\/blueprints\/blueprint.json<\/code>. After this listing is live, WordPress.org can also run the copy under the plugin\u2019s directory assets for Live Preview.<\/p>\n\n<h3>Privacy Policy<\/h3>\n\n<p>Trade Dispatch stores customer names, contact details, job addresses, service notes, estimates, and a WordPress account (Customer role when the office confirms a booking, or an existing Subscriber) on this site so the business can schedule work and the customer can open the portal. The public booking form does not create a WordPress user. Optional Employee and Dispatcher roles let crew work assigned jobs or run the office without editing pages or plugins. Booking form submissions are emailed to the site owner with WordPress mail. Assigned crew members may receive a tomorrow job list (title, time, and address) at their WordPress user email. Site owners can edit those messages (and other Trade Dispatch emails) under Trade Dispatch \u2192 Emails; templates stay in this site\u2019s options. Data stays on this WordPress installation unless the site owner connects a separately installed premium add-on.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>trade-dispatch<\/code> folder to <code>\/wp-content\/plugins\/<\/code>.<\/li>\n<li>Activate Trade Dispatch through the Plugins menu.<\/li>\n<li>Open <strong>Trade Dispatch<\/strong> in the admin menu to manage jobs and customers.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20this%20plugin%20limit%20how%20many%20crew%20members%20i%20can%20assign%3F\"><h3>Does this plugin limit how many crew members I can assign?<\/h3><\/dt>\n<dd><p>No. Assign jobs to any WordPress user.<\/p><\/dd>\n<dt id=\"does%20the%20public%20booking%20form%20create%20a%20wordpress%20user%3F\"><h3>Does the public booking form create a WordPress user?<\/h3><\/dt>\n<dd><p>No. A public request stores a customer record and a requested job only. After the office confirms the visit, Trade Dispatch creates a Customer-role account (if that email is new) so the customer can open the portal. Existing WordPress users with the same email are reused.<\/p><\/dd>\n<dt id=\"does%20the%20free%20plugin%20phone%20home%3F\"><h3>Does the free plugin phone home?<\/h3><\/dt>\n<dd><p>No.<\/p><\/dd>\n<dt id=\"the%20booking%20form%20or%20portal%20says%20my%20request%20could%20not%20be%20sent.%20what%20is%20wrong%3F\"><h3>The booking form or portal says my request could not be sent. What is wrong?<\/h3><\/dt>\n<dd><p>The public forms use a WordPress security token that can expire when the page is served from a full-page cache. If visitors see a \"please try again\" message, exclude the page that holds <code>[trdsp_booking]<\/code> and the page that holds <code>[trdsp_portal]<\/code> from your caching plugin (or your host's page cache). The request is never lost \u2014 the visitor just re-submits.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>0.3.53<\/h4>\n\n<ul>\n<li>Fix: Job and estimate list queries splat prepare() replacements. Latest-note lookup uses a two-placeholder join so Plugin Check can count replacements.<\/li>\n<\/ul>\n\n<h4>0.3.52<\/h4>\n\n<ul>\n<li>Fix: Settings portal page dropdown is escaped at output. List queries pass prepare() without an intermediate SQL variable so Plugin Check stops flagging them.<\/li>\n<\/ul>\n\n<h4>0.3.51<\/h4>\n\n<ul>\n<li>Docs: Collapse 0.3.42 through 0.1.0 into an Earlier releases note so the WordPress.org changelog stays under the size guideline.<\/li>\n<\/ul>\n\n<h4>0.3.50<\/h4>\n\n<ul>\n<li>Docs: Portal REST class and permission callback document that these routes are web-only (not field-tech).<\/li>\n<\/ul>\n\n<h4>0.3.49<\/h4>\n\n<ul>\n<li>Fix: Test-email sample name, address, and crew digest line are translatable.<\/li>\n<li>Fix: Portal loads the same localized booking script as the public form.<\/li>\n<li>Fix: Requests menu badge caches the inbox count for one minute and clears it when jobs, preferred times, or estimate requests change.<\/li>\n<\/ul>\n\n<h4>0.3.48<\/h4>\n\n<ul>\n<li>Fix: Delete, decline, and restore-template confirms use an enqueued admin script and data attributes instead of inline onclick.<\/li>\n<\/ul>\n\n<h4>0.3.47<\/h4>\n\n<ul>\n<li>Fix: Custom-table SELECT, COUNT, and uninstall DROP queries pass the table name through $wpdb-&gt;prepare() %i (WordPress 6.2+). dbDelta schema statements are unchanged.<\/li>\n<\/ul>\n\n<h4>0.3.46<\/h4>\n\n<ul>\n<li>Fix: Portal and admin-post handlers verify the nonce before reading record IDs and require current_user_can on portal writes.<\/li>\n<li>Fix: Service default amount is sanitized before cast. Customer count and service list queries use $wpdb-&gt;prepare().<\/li>\n<\/ul>\n\n<h4>0.3.45<\/h4>\n\n<ul>\n<li>Add: Create booking and portal pages from Settings (separate from the options form).<\/li>\n<li>Add: Jobs list customer filter and an Add estimate link on job edit.<\/li>\n<li>Fix: Deleting a customer deletes that customer\u2019s jobs (and notes) first.<\/li>\n<li>Fix: GDPR export and erase paginate jobs so large accounts finish.<\/li>\n<li>Fix: Portal REST requires a logged-in user with portal or office access.<\/li>\n<\/ul>\n\n<h4>0.3.44<\/h4>\n\n<ul>\n<li>Add: TRDSP_Requests::decide() so the companion office app can approve or decline booking, time, and estimate requests with the same customer emails as wp-admin.<\/li>\n<\/ul>\n\n<h4>0.3.43<\/h4>\n\n<ul>\n<li>Fix: Public booking no longer creates a WordPress user or sends a new-account email. The Customer-role portal account is created when the office confirms the visit. Booking submissions are rate-limited by IP and email.<\/li>\n<li>Add: .distignore and create-plugin-zip.sh so Cursor files, deploy-to-hub.sh, and README.md stay out of the WordPress.org package.<\/li>\n<li>Fix: Services Default minutes uses a number input. Uninstall checkbox text matches the opt-in (tables, templates, settings, and roles).<\/li>\n<\/ul>\n\n<h4>Earlier releases<\/h4>\n\n<p>0.3.42 through 0.1.0 added customers, jobs, estimates, notes, booking and portal shortcodes\/blocks, Customer\/Employee\/Dispatcher roles, the Requests inbox, the Emails screen, services, suggested visit times, recurring jobs, printed work orders, crew tomorrow emails, GDPR export\/erase, and extension hooks (including email placeholders) so add-ons can show invoices, photos, time, and checklists without changing free records.<\/p>","raw_excerpt":"Field service management for small trades: customers, jobs, and scheduling on your own WordPress site.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/gl.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/356901","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gl.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/gl.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/gl.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=356901"}],"author":[{"embeddable":true,"href":"https:\/\/gl.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/brelandr"}],"wp:attachment":[{"href":"https:\/\/gl.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=356901"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/gl.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=356901"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/gl.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=356901"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/gl.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=356901"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/gl.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=356901"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/gl.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=356901"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}