{"id":303166,"date":"2026-05-04T09:12:37","date_gmt":"2026-05-04T09:12:37","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/sprigly\/"},"modified":"2026-05-06T14:35:46","modified_gmt":"2026-05-06T14:35:46","slug":"sprigly","status":"publish","type":"plugin","link":"https:\/\/en-ca.wordpress.org\/plugins\/sprigly\/","author":23482255,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_crdt_document":"","version":"1.5.1","stable_tag":"1.5.1","tested":"6.9.4","requires":"6.0","requires_php":"7.4","requires_plugins":null,"header_name":"Sprigly","header_author":"Sprigly","header_description":"Client journey and progress portal for coaches, trainers, therapists and practitioners. Give your clients a journey they can see.","assets_banners_color":"cee0d4","last_updated":"2026-05-06 14:35:46","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/sprigly.co\/","header_author_uri":"https:\/\/sprigly.co\/about-sprigly\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":75,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.5.0":{"tag":"1.5.0","author":"sprigly","date":"2026-05-04 10:25:30"},"1.5.1":{"tag":"1.5.1","author":"sprigly","date":"2026-05-06 14:35:46"}},"upgrade_notice":{"1.5.0":"<p>First public release on WordPress.org.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3522119,"resolution":"128x128","location":"assets","locale":""},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3522119,"resolution":"256x256","location":"assets","locale":""}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3522119,"resolution":"1544x500","location":"assets","locale":""},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3522119,"resolution":"772x250","location":"assets","locale":""}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.5.0","1.5.1"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3522207,"resolution":"1","location":"assets","locale":""},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3522119,"resolution":"2","location":"assets","locale":""},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3522212,"resolution":"3","location":"assets","locale":""},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3522162,"resolution":"4","location":"assets","locale":""},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3522162,"resolution":"5","location":"assets","locale":""},"screenshot-6.png":{"filename":"screenshot-6.png","revision":3522207,"resolution":"6","location":"assets","locale":""},"screenshot-7.png":{"filename":"screenshot-7.png","revision":3522207,"resolution":"7","location":"assets","locale":""},"screenshot-8.png":{"filename":"screenshot-8.png","revision":3522207,"resolution":"8","location":"assets","locale":""}},"screenshots":{"1":"Practitioner dashboard, with recent activity feed and per-client progress overview.","2":"Journeys list, where you manage and organise your client programmes.","3":"Milestone editor, with rich text descriptions and video URL embedding.","4":"Client list, with progress overview and one-click resend welcome email.","5":"Backup\/Restore, with nightly auto-snapshots and reversible restore for safe data recovery.","6":"Client portal, a clean dashboard showing assigned journeys with progress.","7":"Client milestone page, with guidance and an inline reflection form.","8":"Branding settings, where you set your portal name, logos, primary colour, and footer credit."},"jetpack_post_was_ever_published":false},"plugin_section":[],"plugin_tags":[17989,9110,156384,261818,10305],"plugin_category":[],"plugin_contributors":[261820],"plugin_business_model":[],"class_list":["post-303166","plugin","type-plugin","status-publish","hentry","plugin_tags-client-portal","plugin_tags-coaching","plugin_tags-mentoring","plugin_tags-personal-trainer","plugin_tags-wellness","plugin_contributors-sprigly","plugin_committers-sprigly"],"banners":{"banner":"https:\/\/ps.w.org\/sprigly\/assets\/banner-772x250.png?rev=3522119","banner_2x":"https:\/\/ps.w.org\/sprigly\/assets\/banner-1544x500.png?rev=3522119","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/sprigly\/assets\/icon-128x128.png?rev=3522119","icon_2x":"https:\/\/ps.w.org\/sprigly\/assets\/icon-256x256.png?rev=3522119","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/sprigly\/assets\/screenshot-1.png?rev=3522207","caption":"Practitioner dashboard, with recent activity feed and per-client progress overview."},{"src":"https:\/\/ps.w.org\/sprigly\/assets\/screenshot-2.png?rev=3522119","caption":"Journeys list, where you manage and organise your client programmes."},{"src":"https:\/\/ps.w.org\/sprigly\/assets\/screenshot-3.png?rev=3522212","caption":"Milestone editor, with rich text descriptions and video URL embedding."},{"src":"https:\/\/ps.w.org\/sprigly\/assets\/screenshot-4.png?rev=3522162","caption":"Client list, with progress overview and one-click resend welcome email."},{"src":"https:\/\/ps.w.org\/sprigly\/assets\/screenshot-5.png?rev=3522162","caption":"Backup\/Restore, with nightly auto-snapshots and reversible restore for safe data recovery."},{"src":"https:\/\/ps.w.org\/sprigly\/assets\/screenshot-6.png?rev=3522207","caption":"Client portal, a clean dashboard showing assigned journeys with progress."},{"src":"https:\/\/ps.w.org\/sprigly\/assets\/screenshot-7.png?rev=3522207","caption":"Client milestone page, with guidance and an inline reflection form."},{"src":"https:\/\/ps.w.org\/sprigly\/assets\/screenshot-8.png?rev=3522207","caption":"Branding settings, where you set your portal name, logos, primary colour, and footer credit."}],"raw_content":"<!--section=description-->\n<p>Sprigly gives practitioners a structured way to guide their clients through a journey, and gives clients a beautiful portal where they can see exactly where they are, complete milestones, and write reflections.<\/p>\n\n<p>Whether you're a life coach, personal trainer, mentor, tutor, instructor, teacher, nutritionist, or any kind of practitioner, Sprigly replaces scattered emails and spreadsheets with a clear, trackable progress experience your clients will love.<\/p>\n\n<p><strong>How it works:<\/strong><\/p>\n\n<ol>\n<li>You create a Journey, a structured programme with named milestones (e.g. \"Week 1 - Setting Your Foundation\")<\/li>\n<li>You assign clients to that journey<\/li>\n<li>Clients log in at <code>\/portal\/<\/code> on your WordPress site and see their journey, milestone by milestone<\/li>\n<li>Clients complete milestones and write rich-text reflections<\/li>\n<li>You get notified when clients complete milestones (via the welcome email or the Pro upgrade for richer events)<\/li>\n<li>Everyone can see exactly how far along the journey they are<\/li>\n<\/ol>\n\n<p><strong>What's included in Sprigly (free, uncapped):<\/strong><\/p>\n\n<ul>\n<li>Unlimited journeys, clients, milestones<\/li>\n<li>Branded client portal at <code>\/portal\/<\/code><\/li>\n<li>Custom portal name, navigation logo, and brand colour<\/li>\n<li>Welcome email when a new client account is created (always on)<\/li>\n<li>Rich-text editor for milestone descriptions<\/li>\n<li>Video URL embeds on milestone descriptions (YouTube, Vimeo)<\/li>\n<li>Plain-text and clickable-URL reflections (Pro adds the rich-media embed)<\/li>\n<li>Image lightbox in the portal<\/li>\n<li>Standard milestone type with immediate unlock<\/li>\n<li>Reflection privacy: file proxy + <code>.htaccess<\/code> hardening + media library filter<\/li>\n<li>Self-hosted Poppins typography (no Google Fonts call-out)<\/li>\n<li>Built-in SMTP plugin detection, test-email tool, bug-report form<\/li>\n<li>Designated Sprigly Owner so multiple admins on one site can't accidentally manage each other's data<\/li>\n<li>Mobile-responsive admin and portal<\/li>\n<\/ul>\n\n<p><strong>Built for:<\/strong><\/p>\n\n<ul>\n<li>Life coaches running structured programmes<\/li>\n<li>Personal trainers with weekly check-ins<\/li>\n<li>Mentors guiding clients through structured growth<\/li>\n<li>Tutors and music teachers running multi-week courses<\/li>\n<li>Nutritionists tracking client progress<\/li>\n<li>Instructors, business coaches, and anyone running multi-step client work<\/li>\n<\/ul>\n\n<p><strong>Available as a paid upgrade (Sprigly Pro, sold separately):<\/strong><\/p>\n\n<p>Sprigly Pro is a separate plugin available from sprigly.co that adds the practitioner toolkit on top of Sprigly Lite:<\/p>\n\n<ul>\n<li><strong>Authoring and workflow:<\/strong> checkpoint sign-offs, scheduled milestone unlocks, per-milestone custom check-in fields (7 input types) with required-field validation, Require Reflection toggle, sequential-only journeys, duplicate journey\/milestone, bulk-add milestones, Preview Journey (signed 1-hour link)<\/li>\n<li><strong>Client experience:<\/strong> Resources library (PDFs, links, videos attached to journeys or specific milestones), practitioner notes per client and per milestone, video URL embeds in reflection bodies, file uploads on reflections (image, PDF, doc), re-enrol, pause, resend welcome<\/li>\n<li><strong>Reports and data:<\/strong> branded PDF Progress Reports, JSON export and import, Pro insights dashboard widget (pending sign-offs, average completion, reflections this week vs last)<\/li>\n<li><strong>Communications:<\/strong> 7 additional email events with per-event toggles (milestone completed, journey assigned, journey completed, reflection submitted, sign-off requested, practitioner note, checkpoint signed off), and a branded login page that matches your portal<\/li>\n<li><strong>Multi-practitioner Team plan:<\/strong> up to 5 practitioner seats with strict per-practitioner data isolation, branded invite emails, ownership transfer with a signed time-limited confirmation link<\/li>\n<li><strong>Branding:<\/strong> hide the \"Powered by Sprigly\" footer, portal welcome message<\/li>\n<\/ul>\n\n<p>Pro requires Sprigly to be installed and active. Learn more at https:\/\/sprigly.co\/pricing\/.<\/p>\n\n<h3>External Services<\/h3>\n\n<p>Sprigly is designed to keep your client data on your own WordPress installation. Plugin emails are sent through your site's own <code>wp_mail()<\/code> (which you can route through any SMTP plugin you choose), and the plugin's typography is loaded from font files that ship inside the plugin (no Google Fonts request, no preconnect).<\/p>\n\n<p>There are two narrow exceptions where the plugin reaches out to a third party. Both are documented below.<\/p>\n\n<h4>1. YouTube and Vimeo oEmbed (only when you paste a YouTube or Vimeo URL into a milestone)<\/h4>\n\n<p>When a practitioner pastes a YouTube or Vimeo video URL into a milestone's \"Video URL\" field, Sprigly uses WordPress's built-in oEmbed system (<code>wp_oembed_get()<\/code>) to fetch the embed metadata so the milestone page can show the video. This sends one HTTP request to the relevant provider's oEmbed endpoint to retrieve the iframe HTML; the provider sees the milestone's video URL plus a referrer header from your site. The response is cached by WordPress for 24 hours, so subsequent page loads do not re-fetch.<\/p>\n\n<ul>\n<li>What is sent: the video URL the practitioner pasted (and the standard request headers WordPress emits, including a referrer pointing at your site).<\/li>\n<li>When it is sent: when a portal page or admin page renders a milestone that contains a YouTube or Vimeo URL, and only on the first render within the 24-hour cache window.<\/li>\n<li>Service providers and policies:\n\n<ul>\n<li>YouTube (Google LLC), https:\/\/policies.google.com\/terms, https:\/\/policies.google.com\/privacy<\/li>\n<li>Vimeo, https:\/\/vimeo.com\/terms, https:\/\/vimeo.com\/privacy<\/li>\n<\/ul><\/li>\n<\/ul>\n\n<p>The feature is opt-in by behaviour: pasting a video URL into a milestone is the only way to trigger it. Leave the field blank and Sprigly never contacts either service.<\/p>\n\n<h4>2. Bug report email (only when you click \"Send Bug Report\")<\/h4>\n\n<p>The optional Report a Bug form in Sprigly, Settings, Tools sends a single email through your site's own <code>wp_mail()<\/code> to the Sprigly support address. The form is never submitted automatically. When you click the button, the email contains:<\/p>\n\n<ul>\n<li>The description you typed in the form<\/li>\n<li>The screenshot you attached, if any<\/li>\n<li><p>The System Information block: Sprigly version, WordPress version, PHP version, site URL, active theme name, and the name of any SMTP plugin you have installed<\/p><\/li>\n<li><p>What is sent: only what you typed plus the System Information block above.<\/p><\/li>\n<li>When it is sent: only when you click \"Send Bug Report\".<\/li>\n<li>Service provider: Sprigly support (https:\/\/sprigly.co\/), terms https:\/\/sprigly.co\/terms-and-conditions\/, privacy https:\/\/sprigly.co\/privacy-policy\/.<\/li>\n<\/ul>\n\n<p>Sprigly's Privacy Policy: https:\/\/sprigly.co\/privacy-policy\/\nSprigly's Terms and Conditions: https:\/\/sprigly.co\/terms\/<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin zip via <strong>Plugins \u2192 Add New \u2192 Upload Plugin<\/strong>, or install directly from the WordPress Plugin Directory<\/li>\n<li>Click <strong>Activate<\/strong><\/li>\n<li>Go to <strong>Sprigly \u2192 Settings \u2192 Branding<\/strong> and set your portal name, logo, and brand colour<\/li>\n<li>Go to <strong>Sprigly \u2192 Settings \u2192 Tools<\/strong> and send a test email to confirm delivery<\/li>\n<li>Create your first journey at <strong>Sprigly \u2192 Journeys \u2192 Add New<\/strong><\/li>\n<li>Add a client at <strong>Sprigly \u2192 Clients \u2192 Add \/ Edit Client<\/strong> and assign them to a journey<\/li>\n<li>The client receives a welcome email with their portal login link<\/li>\n<\/ol>\n\n<p><strong>Important:<\/strong> Set your WordPress permalink structure to <strong>Post name<\/strong> (Settings \u2192 Permalinks \u2192 Post name) before activating Sprigly. This is required for the <code>\/portal\/<\/code> URL to work correctly.<\/p>\n\n<p><strong>Recommended:<\/strong> Install and configure an SMTP plugin (WP Mail SMTP, Post SMTP, FluentSMTP, or Easy WP SMTP) to ensure reliable email delivery. By default, WordPress sends mail through your hosting server, which can be filtered or junked by Gmail\/Outlook\/Yahoo. Sprigly detects an active SMTP plugin and shows its status in <strong>Sprigly \u2192 Settings \u2192 Tools<\/strong>.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20sprigly%20work%20with%20any%20wordpress%20theme%3F\"><h3>Does Sprigly work with any WordPress theme?<\/h3><\/dt>\n<dd><p>Yes. The client portal at <code>\/portal\/<\/code> is fully self-contained and does not inherit your theme's styles. It works correctly with any theme \u2014 including page builders like Divi, Elementor, and Beaver Builder.<\/p><\/dd>\n<dt id=\"do%20clients%20need%20a%20wordpress%20account%3F\"><h3>Do clients need a WordPress account?<\/h3><\/dt>\n<dd><p>Yes, each client has a WordPress user account with the <code>sprigly_client<\/code> role. This role gives them access only to the client portal \u2014 they cannot access wp-admin. Sprigly creates the account automatically when you add a client.<\/p><\/dd>\n<dt id=\"where%20is%20the%20client%20portal%3F\"><h3>Where is the client portal?<\/h3><\/dt>\n<dd><p>At <code>\/portal\/<\/code> on your WordPress site (e.g. <code>yourdomain.com\/portal\/<\/code>). You can find the full URL in <strong>Sprigly \u2192 Settings \u2192 Portal<\/strong>. Share this link with your clients.<\/p><\/dd>\n<dt id=\"what%20happens%20to%20my%20data%20if%20i%20deactivate%20the%20plugin%3F\"><h3>What happens to my data if I deactivate the plugin?<\/h3><\/dt>\n<dd><p>Nothing, by default. The \"Protect data on uninstall\" setting is ON by default, which means all your journeys, clients, milestones, and reflections are kept in the database even if the plugin is deactivated or deleted. You can change this behaviour in <strong>Sprigly \u2192 Settings \u2192 Tools<\/strong>.<\/p><\/dd>\n<dt id=\"does%20sprigly%20work%20with%20my%20smtp%20plugin%3F\"><h3>Does Sprigly work with my SMTP plugin?<\/h3><\/dt>\n<dd><p>Yes. Sprigly detects WP Mail SMTP, Post SMTP, FluentSMTP, and Easy WP SMTP automatically and routes all email through whichever is active. We strongly recommend installing an SMTP plugin for reliable delivery.<\/p><\/dd>\n<dt id=\"can%20i%20use%20my%20own%20branding%3F\"><h3>Can I use my own branding?<\/h3><\/dt>\n<dd><p>Yes. You can set a custom portal name, upload separate logos for the login page and the portal nav bar, set logo heights, and pick a primary brand colour (used for buttons, progress bars, and accents in the portal and emails).<\/p><\/dd>\n<dt id=\"can%20i%20change%20how%20i%27m%20referred%20to%20in%20client%20emails%3F\"><h3>Can I change how I'm referred to in client emails?<\/h3><\/dt>\n<dd><p>Yes. When creating or editing a journey, set the \"Client-facing role\" field to whatever suits your profession, coach, trainer, mentor, teacher, tutor, instructor, guide, etc. This replaces the default \"practitioner\" in all client-facing emails for that journey. A suggestion is auto-filled based on the Industry\/Category you select, but you can change it to anything you like.<\/p><\/dd>\n<dt id=\"what%20does%20the%20paid%20sprigly%20pro%20plugin%20add%3F\"><h3>What does the paid Sprigly Pro plugin add?<\/h3><\/dt>\n<dd><p>Sprigly Pro (sold separately at sprigly.co) layers on the practitioner toolkit: checkpoint sign-offs, scheduled milestone unlocks, custom check-in fields, a Resources library, practitioner notes, file uploads on reflections, branded PDF progress reports, JSON export and import, multi-practitioner Team support, and a branded login page. See https:\/\/sprigly.co\/pricing\/ for the full feature list.<\/p><\/dd>\n<dt id=\"does%20sprigly%20contact%20any%20external%20services%3F\"><h3>Does Sprigly contact any external services?<\/h3><\/dt>\n<dd><p>Sprigly keeps your client data on your own WordPress installation, sends emails through your own site's <code>wp_mail()<\/code>, and ships its display font (Poppins) inside the plugin so the portal does not contact Google Fonts.<\/p>\n\n<p>There are two narrow situations where the plugin does reach out to a third party, both opt-in by behaviour:<\/p>\n\n<ul>\n<li>When a practitioner pastes a YouTube or Vimeo URL into a milestone's Video URL field, Sprigly uses WordPress's standard <code>wp_oembed_get()<\/code> to fetch the embed metadata from YouTube or Vimeo so the video can render. The response is cached for 24 hours.<\/li>\n<li>When you click the optional \"Send Bug Report\" button under Settings, Tools, Sprigly emails Sprigly support with the description you typed plus a System Information block.<\/li>\n<\/ul>\n\n<p>Both are documented in full in the External Services section of this readme, including Terms and Privacy links for each provider.<\/p><\/dd>\n<dt id=\"is%20sprigly%20gdpr-compliant%3F\"><h3>Is Sprigly GDPR-compliant?<\/h3><\/dt>\n<dd><p>Sprigly is built to support GDPR compliance. Client data is stored only on your own WordPress installation. You remain the data controller for all client data on your site.<\/p><\/dd>\n<dt id=\"how%20do%20i%20back%20up%20my%20sprigly%20data%3F\"><h3>How do I back up my Sprigly data?<\/h3><\/dt>\n<dd><p>Sprigly ships a built-in Safety &amp; Backups page (Sprigly, Safety &amp; Backups, admin-only). It takes snapshots of all nine Sprigly DB tables (journeys, milestones, clients, progress, reflections, notes, resources, custom fields, field responses) directly inside your own database. Nothing is sent off-site. By default the plugin takes a snapshot every night and after every Sprigly plugin update, plus you can take a manual snapshot any time. Restores are reversible (a pre-restore safety snapshot is taken automatically). Both automatic behaviours can be disabled in the Settings tab. This is a safety net for routine \"before I touch this\" moments, your hosting provider's full-site backups remain the authoritative recovery path for catastrophic loss.<\/p><\/dd>\n<dt id=\"what%20are%20the%20hosting%20requirements%3F\"><h3>What are the hosting requirements?<\/h3><\/dt>\n<dd><ul>\n<li>PHP 7.4 or higher (PHP 8.0+ recommended)<\/li>\n<li>WordPress 6.0 or higher<\/li>\n<li>MySQL 5.7+ or MariaDB 10.4+<\/li>\n<li>HTTPS (SSL certificate) \u2014 required for the client portal login<\/li>\n<\/ul><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.5.1<\/h4>\n\n<ul>\n<li>Updated: Industry\/Category dropdown refreshed. Removed \"Therapy \/ Counselling\", renamed \"Business Mentoring\" to \"Mentoring\", added \"Creative \/ Performance\". Existing journey records keep their stored category.<\/li>\n<li>Updated: Plugin description and audience copy refreshed for non-clinical practitioners (coaches, mentors, trainers, tutors, instructors, teachers).<\/li>\n<li>Updated: WordPress.org tags refreshed (added \"wellness\" and \"mentoring\", removed \"therapist\" and \"progress tracker\").<\/li>\n<li>Updated: Client-facing role auto-fill includes \"teacher\" suggestion for the new Creative \/ Performance category.<\/li>\n<\/ul>\n\n<h4>1.5.0<\/h4>\n\n<ul>\n<li>First public release on WordPress.org. No functional changes from 1.4.3.<\/li>\n<\/ul>\n\n<h4>1.4.3<\/h4>\n\n<ul>\n<li>Full-site backup heads-up on the Backup\/Restore page. A new yellow callout under the existing description paragraph reminds operators that Sprigly snapshots cover only Sprigly's own database tables and aren't a substitute for a full-site backup. Suggests running regular hosting-provider or backup-plugin backups (UpdraftPlus, BlogVault, Solid Backups) so a corrupted database has a fallback that doesn't live in the same database. Display only, no behaviour change.<\/li>\n<\/ul>\n\n<h4>1.4.2<\/h4>\n\n<p>WordPress.org pre-approval response cycle 3: addresses the two issues flagged in the third reviewer email (escape-on-output and unique prefixes).<\/p>\n\n<ul>\n<li>Escape on output. The chronological timeline inside each milestone card on the admin client-profile page now wraps the rendered HTML in <code>wp_kses_post()<\/code> before echoing. The two affected echoes (<code>$milestone_card_extras<\/code> from the <code>sprigly_admin_enrolment_milestone_card_extras<\/code> extension point, and <code>$sp_item['html']<\/code> from the <code>sprigly_admin_milestone_timeline_items<\/code> filter) previously trusted contributing renderers to pre-escape; they now late-escape regardless, so a future filter callback returning unfiltered HTML cannot inject into the admin screen.<\/li>\n<li>Unique prefixes. Two remaining classes still using the short <code>SP_<\/code> prefix are renamed to the standard <code>Sprigly_<\/code> prefix: <code>SP_Backup<\/code> becomes <code>Sprigly_Backup<\/code> and <code>SP_Admin_Safety<\/code> becomes <code>Sprigly_Admin_Safety<\/code>. Both ship a <code>class_alias()<\/code> for backwards compatibility so any external code that still references the old names keeps working. The <code>wp_localize_script<\/code> JS object handed to <code>portal-reflection.js<\/code> is renamed from <code>spReflectionStrings<\/code> to <code>spriglyReflectionStrings<\/code> (the JS file is updated in lockstep).<\/li>\n<li>Option-key migration. Three residual <code>sp_*<\/code> keys missed by the v1.2.0 prefix migration are now handled. A new <code>upgrade_to_1_4_2_rename_residual_sp_options()<\/code> routine copies values for <code>sp_portal_page_id<\/code> to <code>sprigly_portal_page_id<\/code> and <code>sp_suspended_practitioners<\/code> to <code>sprigly_suspended_practitioners<\/code>, then renames the <code>_sp_client_id<\/code> post meta key on reflection attachments to <code>_sprigly_client_id<\/code> in place. Idempotent. The legacy <code>admin_post_sp_send_test_email<\/code> and <code>admin_post_sp_send_bug_report<\/code> aliases (kept for the v1.2.0 to v1.3.0 transition) are removed; the canonical <code>sprigly_*<\/code> action hooks remain. Stale <code>_sp_*<\/code> references in doc comments are corrected to match the live <code>_sprigly_*<\/code> keys.<\/li>\n<li>Paused enrolment fallback. When a Pro user lets their license lapse with paused enrolments still on the books, those clients used to be stranded. Lite recognised the <code>paused<\/code> status but had no UI to transition out of it (Pro's Pause\/Reactivate buttons hide when <code>is_pro()<\/code> returns false). Lite now renders a Reactivate button for paused enrolments whenever no add-on is hooked to <code>sprigly_client_status_actions<\/code>, so a Pro to Lite downgrade can never lock the practitioner out of their own data. The button posts <code>new_status=active<\/code> through the existing <code>update_client_status<\/code> handler (which already accepts <code>active<\/code> in its default whitelist, no DB layer change needed).<\/li>\n<li>Cleanup-deleted-user no-ops on missing tables. <code>Sprigly-&gt;cleanup_deleted_user()<\/code> now early-returns when the <code>sp_journeys<\/code> table does not exist on the site. Previously, when WordPress fired <code>deleted_user<\/code> on an install where Sprigly was loaded but its tables had never been created (or had been dropped by a cleanup utility), the cascade fired several queries against missing tables and flooded <code>debug.log<\/code> on every WP user deletion. The guard is one <code>SHOW TABLES LIKE<\/code> per call.<\/li>\n<li>Em-dash cleanup across user-facing strings. ~28 em-dashes in admin info boxes (Settings \u2192 Branding, Tools, License, SMTP setup), portal page titles, system-page labels (<code>Sprigly: Do not delete<\/code>), client-profile and journey-list dropdown placeholders (<code>Select journey...<\/code>, <code>Select user...<\/code>, <code>Select...<\/code>), bug-report email subject, and admin notices are replaced with commas, periods, parentheses, or colons depending on context. Single em-dash placeholders for empty values (Phone, Industry, First Assigned columns) become empty strings. Code comments and <code>error_log()<\/code> debug output are unchanged. Internal punctuation only, no behaviour change.<\/li>\n<\/ul>\n\n<h4>1.4.1<\/h4>\n\n<p>Follow-up release after v1.4.0 testing. Bundles 16 fixes flagged during the cycle-2 reviewer-response testing pass plus a security tightening on the Backup\/Restore admin.<\/p>\n\n<ul>\n<li>Preview Journey is no longer in Sprigly itself. The 1-hour signed-token preview link is removed from Lite entirely (no preview code, no preview button, no preview-mode body class). Preview returns to being an add-on-only feature, with neutral extension points in <code>class-sp-portal.php<\/code> (<code>sprigly_portal_skip_auth_check<\/code>, <code>sprigly_portal_skip_form_actions<\/code>, <code>sprigly_portal_skip_role_autorestore<\/code>, <code>sprigly_portal_resolve_user_id<\/code>, <code>sprigly_portal_skip_render_dispatch<\/code> + <code>sprigly_portal_render<\/code>, <code>sprigly_portal_url<\/code>, <code>sprigly_portal_body_class<\/code>, <code>sprigly_portal_after_shell_open<\/code>, <code>sprigly_portal_nav_html<\/code>, <code>sprigly_portal_head<\/code>) so add-ons can re-implement preview without Sprigly carrying any preview-specific code itself. The Settings \u2192 Upgrade tab lists Preview Journey again under Authoring &amp; workflow.<\/li>\n<li>Backup\/Restore is owner-only. The Settings \u2192 Backup\/Restore tab, its render, and its post handler are gated on the designated Sprigly owner instead of the generic <code>manage_options<\/code> capability, so a hosting account, web designer, accountant, or any other non-owner administrator on the site cannot create, restore, or delete snapshots.<\/li>\n<li><code>ensure_admin_practitioner_role()<\/code> no longer leaks the <code>sprigly_practitioner<\/code> role to every WordPress administrator on <code>admin_init<\/code>. Pre-v1.4.1 the helper auto-added the role to ANY user with <code>manage_options<\/code>, which meant a brand-new WP admin (hosting account, web designer, etc.) was silently treated as a Sprigly practitioner the first time they loaded wp-admin: they showed up in Sprigly \u2192 Team and saw the full Sprigly menu. The auto-grant is now scoped to the designated Sprigly owner. Non-owners only become practitioners through an explicit Team \u2192 Invite Practitioner \/ \"Add me as a practitioner\" banner \/ practice-setup form path.<\/li>\n<li><code>is_practitioner()<\/code> no longer treats every WordPress administrator as a Sprigly practitioner. Only the designated owner OR users explicitly carrying the <code>sprigly_practitioner<\/code> role pass the helper now.<\/li>\n<li>Reflection save flow keeps the client moving. Marking a milestone complete now redirects back to the same milestone view (with the Continue to next milestone CTA already on screen) instead of bouncing to the journey overview. The locked \"Up next\" line is rendered as a styled disabled button matching the unlocked Continue button instead of as plain text.<\/li>\n<li>Snapshot operation messages reference timestamps instead of internal <code>#ID<\/code>s, matching the table's \"When\" column. Post-update snapshots now also fire on single-plugin updates (the previous handler only listened for the <code>plugins<\/code> plural array key, missing the <code>plugin<\/code> singular set by core's standard upgrade flow).<\/li>\n<li>Per-enrolment \"Remove Client\" button renamed to \"Unenrol from this journey\", with confirm copy that makes it explicit the unenrol is per-journey and the client account stays put. A separate \"Delete client account\" destructive action lives under a danger-zone block on the Client Directory profile page, with cascade-delete of all the practitioner's enrolments + reflections + attachments and a tenant-aware skip when the client is also enrolled with another practitioner.<\/li>\n<li>Plugin Check warning at <code>class-sp-admin-settings.php:191<\/code> (<code>WordPress.Security.ValidatedSanitizedInput.InputNotSanitized<\/code>) is suppressed with a phpcs:ignore comment naming the per-type whitelist (<code>sanitizer_for_type<\/code>) \u2014 sanitization is happening, just dynamically dispatched.<\/li>\n<\/ul>\n\n<h4>1.4.0<\/h4>\n\n<p>WordPress.org pre-approval response cycle 2: this release lands the changes flagged in the second reviewer email so the plugin can clear the trialware, attribution, sanitization, hardcoded-URL, and text-domain checks.<\/p>\n\n<ul>\n<li>Trialware cleanup. Two features the reviewer flagged as \"implemented but locked\" are resolved at the source. (1) Preview Journey is now a free, fully functional Sprigly feature: the \"Preview Journey\" button renders directly on the journey edit screen for every user, with the same 1-hour signed-token preview link that previously existed only when the Sprigly Pro add-on was active. (2) The \"Paused\" enrolment status is no longer exposed in Sprigly itself; the status filter dropdown now lists only the statuses Sprigly can actually transition into (Active \/ Completed \/ Archived), with <code>sprigly_admin_client_filter_status_options<\/code> available for add-on plugins that introduce additional workflow states.<\/li>\n<li>\"Powered by Sprigly\" footer is now opt-in. The credit link in the client portal footer and in client-facing emails is OFF by default. A new \"Powered by Sprigly footer\" checkbox in Settings \u2192 Branding is the explicit opt-in control; the credit only renders when the site administrator ticks it. Existing installs that previously relied on the on-by-default behaviour will see the credit disappear until the admin opts in via the new setting.<\/li>\n<li>Per-option sanitization on <code>register_setting()<\/code>. The single generic <code>sanitize_text_field<\/code> callback is replaced by per-option sanitizers driven by an explicit type schema: <code>esc_url_raw<\/code> for logo URLs, bounded <code>absint<\/code> (with min\/max) for the login-logo and nav-logo heights, <code>sanitize_hex_color<\/code> for the brand colour, and a <code>yes<\/code>\/<code>no<\/code> whitelist for toggles. The custom save handler in Settings \u2192 Branding now routes every posted value through the same sanitizer table, so writes via the Settings UI and writes via the Options API stay consistent.<\/li>\n<li><code>home_url('\/wp-login.php?...')<\/code> replaced with <code>wp_login_url()<\/code> + <code>add_query_arg()<\/code> in the SiteGround Security login-redirect branch (<code>class-sp-portal.php<\/code>), so subdirectory installs and renamed-login plugins resolve the login URL correctly.<\/li>\n<li>Two <code>__( '...', 'default' )<\/code> calls in the Plugins-page row-meta de-duplication helper are removed. The helper now compares against the canonical English labels emitted by WordPress core; a non-English locale may show a benign extra row-meta link, which is the smaller of the two trade-offs.<\/li>\n<li>Code-comment scrub. Every \"Pro feature\", \"Pro extends\", \"Pro hooks here\", \"Pro plugin\", \"Pro tier\", \"Pro overrides\", \"Lite\/Pro extension point\", and similar phrase across <code>class-sp-portal.php<\/code>, <code>class-sp-email.php<\/code>, <code>class-sp-roles.php<\/code>, <code>class-sp-database.php<\/code>, <code>class-sp-install.php<\/code>, <code>class-sp-login.php<\/code>, <code>class-sp-admin-clients.php<\/code>, <code>class-sp-admin-journeys.php<\/code>, <code>class-sp-admin-settings.php<\/code>, and <code>class-sp-admin-menus.php<\/code> is rewritten in neutral \"extension point\" \/ \"add-on plugins can hook here\" language. The Settings \u2192 Upgrade tab no longer lists Preview Journey or Hide Powered by Sprigly as Pro features (Preview is free now; the Powered by toggle ships in Sprigly itself).<\/li>\n<li><code>count_hidden_pro_features()<\/code> and <code>count_hidden_pro_features_for_milestone()<\/code> helpers (which existed in Lite solely to support the add-on's downgrade-detection email + dashboard banner) are removed from <code>class-sp-database.php<\/code>. The add-on plugin owns this functionality at its own callsite now.<\/li>\n<li>CRUD scrub. <code>SP_Database::insert_journey()<\/code> \/ <code>update_journey()<\/code> no longer write <code>sequential_mode<\/code>; <code>SP_Database::insert_milestone()<\/code> \/ <code>update_milestone()<\/code> no longer write <code>milestone_type<\/code>, <code>unlock_type<\/code>, <code>unlock_date<\/code>, <code>unlock_days<\/code>, or <code>require_reflection<\/code>. Add-on plugins that rely on those columns hook the existing <code>sprigly_save_journey<\/code> and <code>sprigly_save_milestone<\/code> actions to persist them via direct <code>$wpdb-&gt;update()<\/code> (the patterns Sprigly Pro already uses). Database schema is unchanged so existing rows keep their values.<\/li>\n<li>Inline <code>&lt;style&gt;<\/code> block on the file-proxy notice page (the page rendered when a portal user requests a private file via <code>\/?sp_file=ID<\/code> and the lookup fails) is moved to a real stylesheet at <code>assets\/css\/portal-notice.css<\/code>. The notice page exits without firing <code>wp_head<\/code>\/<code>wp_footer<\/code>, so it manually calls <code>wp_register_style()<\/code> + <code>wp_enqueue_style()<\/code> + <code>wp_print_styles()<\/code> to emit a standard <code>&lt;link rel=\"stylesheet\"&gt;<\/code> tag. No visual change, just compliance.<\/li>\n<\/ul>\n\n<h4>1.3.4<\/h4>\n\n<ul>\n<li>Backups moved into Settings. The \"Safety &amp; Backups\" admin submenu link has been retired; its contents now live under Sprigly, Settings, Backup\/Restore (sits between Tools and License). The two inner sub-tabs (Snapshots, Settings) are unchanged. The page is still admin-only (<code>manage_options<\/code>), so non-owner admins with <code>manage_sprigly<\/code> cannot see it even though they can reach the rest of Settings. All existing snapshot \/ restore \/ delete \/ prune \/ save handlers and URLs were re-pointed at the new tab; no data migration needed.<\/li>\n<\/ul>\n\n<h4>1.3.3<\/h4>\n\n<ul>\n<li>WPCS \/ Plugin Check cleanup on the new Safety &amp; Backups stack. <code>class-sp-admin-safety.php<\/code>: display-only <code>$_GET<\/code> reads (active tab, post-redirect notice text) wrapped in a scoped <code>phpcs:disable WordPress.Security.NonceVerification.Recommended<\/code> block (no form data is processed there); five <code>sprintf( __() )<\/code> strings carrying numeric placeholders gained <code>translators:<\/code> comments; the settings save handler extracts <code>retention_days<\/code> and <code>keep_minimum<\/code> via <code>absint( wp_unslash( $_POST[\u2026] ) )<\/code> so WPCS sees them as sanitized (the previous inline <code>(int)<\/code> cast was equivalent at runtime but not recognised by the input-sanitization rule). <code>class-sp-backup.php<\/code>: every <code>$wpdb<\/code> call against the snapshot tables annotated with targeted <code>phpcs:ignore<\/code> for <code>WordPress.DB.PreparedSQL.InterpolatedNotPrepared<\/code>, <code>WordPress.DB.DirectDatabaseQuery.{DirectQuery,NoCaching,SchemaChange}<\/code>, and <code>PluginCheck.Security.DirectDB.UnescapedDBParameter<\/code> (table identifiers are not parameterisable; names come from <code>SP_Database::table()<\/code> \/ <code>self::get_manifest_table()<\/code> plus a server-generated <code>gmdate + wp_generate_password<\/code> suffix, so no user input touches the SQL string). No behaviour change.<\/li>\n<\/ul>\n\n<h4>1.3.2<\/h4>\n\n<ul>\n<li><code>SP_Portal::write_protection_files()<\/code> now accepts an optional <code>$practitioner_id<\/code> argument. When supplied, the helper creates <code>\/uploads\/sprigly\/{practitioner_id}\/<\/code> and drops an empty <code>index.html<\/code> inside it (so directory listing is blocked even on hosts with <code>Options +Indexes<\/code> enabled). Pairs with Sprigly Pro v2.2.53, which uses this to partition reflection + resource uploads into per-practitioner subfolders. Lite-only installs are unaffected (Lite has no file upload features); the helper just exposes the new arg for Pro to consume.<\/li>\n<\/ul>\n\n<h4>1.3.1<\/h4>\n\n<ul>\n<li>Snapshot retention default lowered from 60 days to 30 days. Halves baseline disk usage on new installs (~30 nightly snapshots instead of ~60). Existing customers keep saved settings. The 'keep at least N most recent' floor (default 5) is unchanged. Configurable on the Safety tab.<\/li>\n<\/ul>\n\n<h4>1.3.0<\/h4>\n\n<ul>\n<li><strong>New, Safety &amp; Backups page (Sprigly, Safety &amp; Backups, admin only).<\/strong> Snapshot\/restore for the nine Sprigly DB tables (journeys, milestones, clients, progress, reflections, notes, resources, custom fields, field responses). Snapshots live on the customer's own database, nothing is sent off-site. Restores are reversible: a pre-restore safety snapshot is taken automatically before live data is overwritten. The Restore confirmation explicitly warns that ALL practitioners' data on the site is replaced. Three operator-controlled toggles in Settings: nightly auto-snapshot (default ON), post-plugin-update snapshot (default ON), and the manual snapshot button (always available regardless). Retention defaults to 60 days with a minimum of 5 most-recent always kept. Page is gated to manage_options (the practice owner \/ WP admin), hidden from team practitioners. No activity log, no per-practitioner view: this is a backend safety net, not a monitoring tool. New tables: <code>wp_sp_backup_manifest<\/code> (snapshot ledger) plus per-snapshot <code>wp_sp_*_bak_*<\/code> copy tables.<\/li>\n<\/ul>\n\n<h4>1.2.26<\/h4>\n\n<ul>\n<li>UX, mobile clients table: removed the 120px hard cap on Actions-cell buttons so the v1.2.24 form full-width fix actually takes effect. v1.2.24 lifted the cap on the FORM child (so Pro's Resend Welcome form could span full row when wrapped) but a sibling rule on <code>.button, button<\/code> direct children still capped each button at 120px, which kept the button INSIDE the form at 120px even when its form parent had grown to 100%. Lifted the button cap to 100% to match. With <code>flex: 1 1 auto<\/code> on every Actions-cell child, View \/ Edit \/ Remove still distribute the row width evenly when they fit on one line, and the wrapped Resend Welcome (Pro) now actually fills its row.<\/li>\n<\/ul>\n\n<h4>1.2.25<\/h4>\n\n<ul>\n<li>Bug fix, Settings, Branding tab: the Login Page Logo and Portal Nav Logo previews are now scheme-normalised so an <code>http:\/\/<\/code> upload URL doesn't trigger a Mixed Content warning when the admin page is served over <code>https:\/\/<\/code>. Same <code>set_url_scheme()<\/code> fix as v1.2.19's portal nav and v2.2.38's branded login page; the Branding admin preview was the third render path that was missed. The hidden form inputs intentionally keep the original (un-normalised) value so saving the form doesn't silently rewrite the option, scheme normalisation stays a display-only concern.<\/li>\n<\/ul>\n\n<h4>1.2.24<\/h4>\n\n<ul>\n<li>UX, mobile clients table: Actions cell child forms (e.g. Pro's Resend Welcome, Pro's Pause\/Reactivate) can now grow to the full cell width when wrapped to their own row. The mobile rule on <code>.sp-table-clients td[data-label=\"Actions\"] form<\/code> had <code>max-width: 120px !important<\/code> from when those forms only ever fit alongside View \/ Edit \/ Remove. Bumped to <code>max-width: 100% !important<\/code> so a wrapped form gets a comfortable full-width tap target instead of staying capped at 120px. Sibling buttons in the same cell still cap at 120px (unchanged) so the View \/ Edit \/ Remove row stays balanced.<\/li>\n<\/ul>\n\n<h4>1.2.23<\/h4>\n\n<ul>\n<li>Plugin Check pass: trimmed the readme.txt Changelog section to the most recent four releases so it fits under the 5000-character limit Plugin Check enforces. Older entries (v1.0.0 \u2192 v1.2.19) are preserved verbatim in <code>CHANGELOG.md<\/code> inside the plugin folder, alongside the pre-split monolithic v1.30.x history. No code changes.<\/li>\n<\/ul>\n\n<h4>1.2.21<\/h4>\n\n<ul>\n<li><strong>UX, \"Protect data on uninstall\" toggle on Settings \u2192 Tools is now visible only to the Sprigly Owner.<\/strong> Pre-1.2.21 the form rendered for every admin who could reach the settings page, even though <code>handle_save()<\/code> already rejected non-owner saves with a <code>wp_die()<\/code> (so non-owners could TRY to change the value but the change wouldn't persist). The visible-but-inert form was confusing \u2014 admins saw the checkbox in their preferred state, clicked Save, were dumped on a <code>wp_die<\/code> page, and had no obvious \"this is owner-only\" signal. Now non-owners see a read-only status block instead: \"Status: Data is protected \u2014 deleting the plugin will keep your journeys, clients, milestones, reflections, and settings. Only the Sprigly Owner can change this setting. Contact the owner if it needs adjusting.\" The owner sees the form unchanged. Server-side gate at <code>handle_save()<\/code> line 95 stays as defence-in-depth. The amber \"Data protection is OFF\" warning still renders for all admins regardless of who can edit, since they all need to know if the site is currently in the unprotected state.<\/li>\n<\/ul>\n\n<h4>Earlier releases<\/h4>\n\n<ul>\n<li>For the full Lite changelog (1.0.0 -&gt; 1.2.19) plus the pre-split monolithic v1.30.x history, see <a href=\"CHANGELOG.md\">CHANGELOG.md<\/a> inside the plugin folder. Older entries are kept in a separate file so the readme.txt Changelog section fits under WordPress.org's 5000-character limit.<\/li>\n<\/ul>","raw_excerpt":"Client journey portal for coaches, mentors, trainers and tutors. Guide clients through milestones and reflections.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/303166","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=303166"}],"author":[{"embeddable":true,"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/sprigly"}],"wp:attachment":[{"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=303166"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=303166"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=303166"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=303166"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=303166"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=303166"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}