Toolkit
Every fix we kept writing twice, written once.
The plugin that ships with every site Pigment Dev builds. Seventeen modules, hundreds of switches, and a settings panel you can search: WooCommerce, LearnDash, Elementor, Woodmart, SMS, Telegram, secure video, performance, roles, and the long tail of small fixes that every serious WordPress site eventually needs.
Installed on the sites Pigment Dev builds and looks after, and nowhere else. Not for sale; some modules are licensed per site. I co-founded the studio and lead its development.
General
Dates, editor, mediaDates and locale
Jalali dates across the siteOr on the front end only
Geo detection modeUsed by the currency and gateway rules
Editor and content
Disable the block editorOr keep the classic editor with syntax highlighting
Read moreCollapse long text with your own labels and height
Media and admin
Media size column in the library
Debug mode for the toolkit itself
WooCommerce
Currency & GeoCurrency and geo
Add second currencyShow prices in a second currency alongside the store's own
Use currency switcherLet visitors switch, remembered per visitor
Currency-aware page cacheSeparate cache per currency so switching never shows stale prices
Checkout fields
Different fields for courses and eventsSkip address fields for anything that is not shipped
Use mobile number as emailFor markets where customers do not have one
Hide the customer note
Custom Fees
RulesHow the rules run
Only active rules are evaluatedEach matching rule adds its own named line to the cart
Taxable feeSet per rule
When a rule applies
Subtotal rangeA minimum, a maximum, or both
Payment gateway chosen at checkout
User roleIncluding guests who are not logged in
Currency being viewedAny, or only the primary or the second currency
Email Templates
Builder and assignmentBuilder
Customise every WooCommerce emailNew order through to new account
CSS editor scoped to emailsStyling an email cannot leak into the site
Assignment
Template by order typePhysical, virtual, course or event
Template by site languagePer language when WPML is running
Store emails
Force store emails to English
A Persian typeface in store emails
Waitlist
Back in stockThe form
Email channel
SMS channel
Consent checkboxOptional, with editable text
The messages
Welcome email on joining
Back-in-stock SMS
Send through the queueSo a restock never fires thousands of messages at once
Woodmart
Price, navigation, fixesPrice and product
New price interfaceEverywhere, product pages only, or listings only
Sticky add-to-cart bar
Fixes
Accordion wired to ACF FAQsA product's questions come from fields, not pasted text
Re-initialise lazy loadingSo images below a filter or a tab still appear
Repair the heading outlineProduct titles in listings demoted to paragraphs
Roles
CapabilitiesEditors can manage
Products
Orders
Coupons
Admin
Search users by mobile number
Elementor
Controls, conditions, fixesControls and behaviour
Wrapper linkMake any container a link
Smooth scroll for anchor links
Floating action buttonA template or a URL, with its own colours
Display conditions and fixes
Show or hide by an ACF field's value
Fix the edit panel's endless spinner
Fix duplicate SVG masksOn repeated widgets
Forms & Telegram
Bot and form notificationsTelegram bot
Hide link previews
Alternate API baseFor networks where the default is unreachable
Form notifications
Send to Telegram form actionAttachments arrive as a document or a photo, by type
Send by SMS form action
Throttle per hourSo a spammed form cannot flood a channel
Daily summary spreadsheetOne sheet per form, yesterday only
LearnDash
Access and enrolmentAccess
Buy a course onceA purchased course cannot be added to the cart again
Expire access on a dateRather than after a duration
Extend access on repurchaseOnly if the buyer already had active, time-limited access
Continue learning linkOne link back to the last lesson
Bulk actions
Enrol from a spreadsheetWith a sample file generated for you
Performance
Speed and bloatSpeed and bloat
Disable emoji scripts
Disable XML-RPC
Throttle the heartbeat
Limit revisionsTo a number you choose
Transient guard
Block layered-navigation count transientsThey grow without limit on large catalogues
Silence the password-change email
SMS / Mail
One provider for everythingSMS
Pattern messagesFor providers that require pre-approved templates
Intercept another plugin's SMSIts messages ride the toolkit's provider
Convert Persian digits before sendingSo phone numbers dial
Styled mail templateFor everything the toolkit sends
What the panel does
- Search that edits: type a few letters and land on the setting, inside its tab
- Every setting carries a tooltip a client can read before flipping it
- The address follows the tab, so a link lands a colleague on the same one
- Saves in place: no reload, no jump back to the top
- Lays itself out right to left or left to right, from the admin language
Pick a module, or search every setting.
Try
// why
Why we built it instead of writing it again
It did not start as a product. It started as one client's plugin, and a folder full of others exactly like it.
Before
For years, every client site we built came with a small plugin of its own, called something like Edits or Custom Codes or Snippets. It held everything WordPress, WooCommerce and the theme could not do out of the box. Hide the customer note at checkout. Show dates in the Persian calendar. Send an SMS when an order ships. Let editors manage products without making them administrators. Show a zero price as Free. Add a second currency.
The requests were never exotic. That was the problem. The same dozen asks arrived from client after client, each one slightly different, and each time we wrote it again: copy the function from the last site, rename it, adjust it, forget which version was newest.
Then WooCommerce would change something, and one of those plugins would break. Not all of them at once, which would at least have been efficient. One at a time, on whichever site happened to update first, on a Friday. And because each plugin was a file of code with no interface, the client could not turn anything off, and we could not remember without opening the file which site had which fix switched on.
We were maintaining the same feature in a dozen places and calling it custom work.
In March 2023 we took the largest of those client plugins and made it the seed of something else. The first commit says init snippets plugin, because that is exactly what it was: one client's snippets, lifted out of their site and made general. It had already lived on two client sites, each time copied and renamed. This time it kept its own name, and within a week it had a settings page.
The rule from the start was simple. Every fix becomes a module. Every module has a switch. Only what is switched on ever loads. And every switch has a label and a tooltip a client can read, so which site has which fix is a page in the admin rather than a memory in our heads.
It has grown every year since, and faster this year than in the two before combined, because once the toolkit existed the question changed. Not how do we build this for this client, but which module is missing.
Before
A plugin per client
Copied from the last site and renamed. A WooCommerce change broke them one at a time, and nobody could see which fix was on where.
Now
One plugin on every site
Clients get the fixes on day one instead of on request. We maintain one plugin instead of a folder of them, and when WooCommerce changes something, we fix it once.
// every-module
Every module, every switch
Seventeen modules. Each is a tab in the panel, each can be switched off entirely, and none loads a single line unless it is on. Open one to see what it holds, taken from the settings schema.
WooCommerceThe largest by far. The commerce layer stores in our market need, and the fixes WooCommerce itself never shipped.
Currency and geo
- A second currency with its own mapping, name, symbol and symbol position
- A switcher for visitors, changing prices without a reload
- A currency-aware page cache, so a cached page never shows the wrong currency
- WPML-aware symbols; a geo detection mode, including forcing a country for testing
Payment gateways
- A notice shown at checkout
- The gateway list reshaped by rule, and a gateway per currency
- The final status for cash on delivery, chosen by the store
- A transaction log for the regional provider
Order status and checkout
- Final statuses for course and event orders and for virtual-only orders
- The pay button restricted to chosen statuses
- Different checkout fields for courses and events
- Hide the country field or the customer note; the mobile number as the email
Cart and shipping
- Redirect after add to cart, separately for products and for courses or events
- Hide the added-to-cart notice; remove recurring totals from the cart
- A shipping class for courses and events
- Shipping only when something is actually shipped
Pricing and stock
- A zero price shown as Free, with the label translatable
- A variable price range collapsed to From, with the pattern editable
- A unit price shortcode for any product or variation
- A stock log on its own admin page; an out-of-stock SMS
Emails, notices, catalogue
- Custom email templates; store emails forced to English; a Persian typeface in them
- A thank-you per product or variation: one for the page, one for the email, one for SMS
- The right-to-left price slider, fixed; sort by price only
- Sell on WhatsApp: a button that opens a conversation instead of a cart
Custom feesA rule engine for cart fees. Every active rule whose conditions match adds its own fee line, visible and explained.
How a fee is calculated
- A fixed amount
- A percentage of one product's line, of the cart subtotal before discounts, of the total after coupons, of the total plus shipping, or of the whole order
- Taxable or not, per rule
When a rule applies
- Cart type, subtotal range, products or variations in the cart, coupon codes
- Billing country, shipping country and state, shipping method
- Payment gateway, user role including guests
- Currency: any, or only while the primary or the second one is being viewed
In the panel
- A label, a priority so lower rules run first, and an active switch per rule
- Duplicate a rule to start the next one from it
- Export and import as JSON, so a set of rules moves between sites in one file
Email templatesA template builder for WooCommerce's emails, where the right template depends on what was bought and the language the buyer reads.
Builder
- A library of templates you author and keep
- Every WooCommerce email: new order, processing, completed, refunded, cancelled, failed, on hold, customer invoice, note, reset password, new account
- Rich HTML editing, with a CSS editor scoped to emails alone
Assignment
- By order type: a physical order, a virtual one, a course or an event can each use a different template for the same email
- By site language, and per language under WPML, so a bilingual store sends the email in the customer's language with a layout designed for it
WaitlistCustomers ask to be told when a product is back, then they are told, by email, SMS or both.
The form
- Channels: email, SMS, or both
- Position on the product page, title, placeholders, button label
- Success and already-joined messages; an optional consent checkbox
The messages
- A welcome email on joining
- A back-in-stock email and SMS, each editable
- Keep or remove the entry after notifying; translatable strings
The page
- Every entry with its status: waiting, notified, left
- Export to a spreadsheet, sized for large lists
- Delivered through the queue, so a restock never fires thousands of messages at once
LearnDashAccess that expires, access that extends, and enrolment in bulk.
Access
- Buy a course once: a purchased course cannot be added to the cart again
- Expire access on a specific date rather than after a duration
- Extend access on repurchase, only if the buyer already had active, time-limited access
- Continue learning: one link back to the last lesson
Notifications
- Scheduled notifications for enrolled students
- Delivered through the queue, with retries
Bulk actions
- Enrol from a spreadsheet, with a sample file generated for you
- Remove or extend access in bulk
- Find users by email or mobile, whichever the spreadsheet has
LearnDash did not offer expiry on a date when we added it. As of September 2026 it does, officially. We ship the features we know a platform is missing.
ElementorWidgets we kept rebuilding, display conditions Elementor never shipped, a control it should have had, and fixes for the editor's own bugs.
Widgets
- Mega menu and search bar
- FAQ accordion, video player
- Comment form and comment list, as separate widgets
- ACF field, and an ACF repeater rendered as a swipeable carousel
Display conditions
- By an ACF field's value
- By product attribute, taxonomy, meta, or a property such as stock or sale state
- By invoice state, for stores running our invoicing plugin
- A custom dynamic tag so any of these can feed any control
Controls and fixes
- Wrapper link: make any container a link
- Smooth scroll for anchors; a popup with a Gravity Form inside; a floating action button
- The edit panel's endless loading spinner, fixed
- Duplicate SVG masks on repeated widgets, fixed
Forms and TelegramForm submissions that reach a person, not an inbox. One Telegram transport shared by every feature that needs it.
Telegram bot
- Bot token, default recipients, and topics inside a group
- An alternate API base for networks where the default is unreachable
- Hidden link previews, silent delivery, a timeout
Form notifications
- Two new Elementor form actions: send to Telegram, send by SMS
- An uploaded file arrives as a document and an image as a photo
- Buttons under the message, a throttle per hour, a delivery log with retry
- Persian digits converted before sending, so numbers dial
Daily summary
- One spreadsheet a day: an overview sheet, then one sheet per form
- Send time, recipients, file name pattern, caption, rows per sheet
- Yesterday only, so the summary is a summary
WoodmartFor stores on the Woodmart theme: the price interface it should have, and the mobile navigation it almost has.
Price and product
- A new price interface, everywhere or in chosen places
- A single variation's swatch as the product image
- A sticky add-to-cart bar; add-to-cart removed from listings
Navigation
- The site icon before the mobile navigation, extra content after it
- An alternative responsive menu by shortcode
- Profile login on checkout, for stores using our profile plugin
Fixes
- An accordion wired to ACF FAQs
- Lazy loading re-initialised after dynamic content loads
- The heading outline repaired: product titles in listings demoted to paragraphs
- Duplicate and stray markup removed where it hurt the page's structure
RolesThe request we heard most often from shop owners: let my staff run the shop without handing them the keys.
Capabilities
- Editors manage products, orders and coupons, each separately
- Search users by mobile number in the admin
PerformanceThe list every performance consultant sends, as switches, plus fixes for problems WooCommerce and Woodmart cause on their own.
Speed and bloat
- Emoji scripts, oEmbed, XML-RPC and self pingbacks off
- Query strings removed from asset URLs; the head cleaned
- The heartbeat throttled; revisions limited
Fixes
- WordPress's own lazy loading removed when the theme already does it
- The WooCommerce admin repaired when it breaks its own layout
- Woodmart's listing headings demoted, for the page's outline
Transient guard
- Layered-navigation count transients blocked, since they grow without limit on large catalogues
- Transient and database cleanup from the panel
- The password-change and new-user emails silenced
SMS and mailOne switch chooses the SMS provider. Everything else in the toolkit that sends a text goes through it.
Gateways
- Four gateways: two regional providers by API, plus two existing SMS plugins used as transports, which opens every gateway those plugins support
- A new gateway is a class
- Pattern messages for the provider that requires pre-approved templates
- A bridge that routes another plugin's texts through the toolkit's provider; a test SMS from the panel
- Sender name and address
- A styled template for everything the toolkit sends
- A test email from the panel
Order notifierAn SMS on every order status change, written per status, to the customer, the shop, or both.
Per status
- A message per status: processing, completed, cancelled and the rest
- Customer, admin, or both for each
- Global admin numbers, or numbers per status
- Sent immediately, or through the queue
Post-purchase notificationsMessages that go out days after a purchase: a reminder to review, a link to the course, a follow-up offer.
Per product
- Up to eight notifications per product or variation
- Email, SMS or both, each with its own delay
- A cron-driven queue with an interval, a batch size, retries and its own manager page
Secure videoCourse video that cannot simply be downloaded, served to each student from the server that will be fastest for them.
Delivery
- Up to three delivery servers, each with its own base URL and signing secret
- The server chosen by the student's country, or a default
- Signed links through nginx secure link, bound to the viewer's IP address and expiring after a set time
- Servers switched at runtime through an endpoint
Player
- A notice showing which server is active, with its position configurable
- Button labels for video and audio, and their colours
Live stream Licensed moduleFor course sites that stream rather than host, so nobody walks away with the original video files.
Providers
- Bunny Stream, integrated end to end
- Mux, in development
- HLS live streams as well as stored lessons
Protection
- Token authentication, so a stream link only works for the viewer it was issued to
- DRM through MediaCage
- A watermark rendered per viewer, so a leaked recording carries the name of whoever leaked it
CustomizationThe can-you-change-this-word requests, answered from a page instead of a code editor.
Text
- Translation replacement: rewrite any translated string the site prints
- String replacement: rewrite anything else, translated or not
- A standalone Translations page for both
Popups and profile
- A welcome popup asking a first-time visitor to choose a currency
- A course intro on the profile, for stores running our profile plugin
SEOFor the cases where a plugin's generated structured data is not what the page needs.
Custom structured data
- Hand-written JSON-LD in a proper code editor
- Per post on the post types you choose, per term on the taxonomies you choose
- The injection location in the page, your call
GeneralNot about a plugin or a theme, but about running a WordPress site in this part of the world.
Dates and locale
- Jalali dates across the site, or on the front end only
- The geo detection mode used by currency and gateway rules
Editor and content
- The block editor off, or the classic editor with syntax highlighting
- Custom comments
- Read more: long text collapsed with your own labels and height
Media and admin
- Favicon URL, front-end icon font URL
- A media size column in the library
- Debug mode for the toolkit itself
// the-panel
The panel
Hundreds of settings would be a liability without a good interface. So the interface is where a lot of the work went.
Search that edits
Type a few letters and the panel jumps to the matching setting inside its tab, highlights it, and lets you change it right there. Nobody remembers which tab holds the free-price label.
Every setting explains itself
Each item carries a short tooltip, so clients read what a switch does before they flip it, and send fewer messages asking.
Tabs split into sections
WooCommerce alone is a dozen: currency, gateways, order status, checkout fields, cart, shipping, pricing, stock, emails, thank-you notices, catalogue fixes, products.
Deep links
The address bar follows the tab you are on. Bookmark it, reload it, send it to a colleague; they land on the same tab.
Saves without leaving
A floating save button, and saving happens in place. No reload, no scroll to the top, a small confirmation where your eyes already are.
Reads in both directions
The panel detects the admin language and lays itself out right to left or left to right, fields included.
Pages where a table beats a form
The stock log, the waitlist and its export, translations and rewrites, the cron queue, the email template builder and the fee rules each have their own page.
Its own changelog and guide
A changelog viewer inside the plugin, entry by entry, and a documentation page with the hooks. Nobody leaves the admin to find out what changed.
A plugin manager
An installer page for companion plugins, without the round trip through the WordPress plugin screens.
// under-the-hood
Under the hood
The decisions that let one plugin carry this much without becoming the thing it replaced.
- Nothing loads unless it is on
- Each module checks its switch before registering a single hook. A site with everything off is, in effect, running nothing.
- One schema, one source of truth
- Tabs, sections, fields, types and tooltips are defined once as data. The panel renders from it, saving reads from it, search indexes it.
- One transport per channel
- Exactly one class talks to Telegram and one path sends SMS, so throttling, logging and retries happen in one place.
- A queue for anything that can wait
- Post-purchase messages, waitlist alerts, course reminders and bulk sends go through a cron queue with a batch size and a retry ceiling.
- Spreadsheets in both directions
- An Excel reader and writer ship with the plugin: bulk enrolment reads, the waitlist export and the daily summary write. No external service.
- Two calendars, natively
- A Jalali date library is part of the toolkit, so every module that shows a date can show it in either calendar.
- One release, every site, at once
- Each tagged release builds an install-ready zip, and the plugin's own updater delivers it to every client site the moment it is published.
- One plugin, licensed parts
- Some modules need a signed, site-bound authorization with plan tiers, checked on load. Locked modules stay visible behind an overlay.
- Shortcodes and hooks for the rest
- Unit price, ACF field and responsive menu shortcodes, and hooks documented on the plugin's own guide page.
// developers
For developers
The parts that matter when you inherit a site running this, or when you have to move one.
One option row, not autoloaded
The entire configuration, every tab and every switch, lives in a single row in the options table, excluded from what WordPress loads on every request. Switches and short values, no serialised content blobs.
Portable
Export and import the whole configuration as a file, so a new site starts from a known-good setup in one step. Fee rules, templates, translations and rewrites move the same way.
Real editors
CodeMirror and Ace wherever the panel takes code, a snippets area for site-specific CSS and JavaScript, and a CSS editor scoped to WooCommerce emails only.
Extension points
Documented hooks, shortcodes for what the switches do not cover. A new SMS gateway is a class; a new Elementor condition is a class.
// built-where-it-is-hard
Built where it is hard
Much of this toolkit exists because the market it serves has requirements the global plugin ecosystem never bothered with.
- Two calendars
- Jalali dates across the admin and the front end, or the front end alone.
- Two currencies
- A second currency with its own gateway, symbol and switcher, cache-safe.
- Regional gateways
- A dedicated slug and a transaction log for the local payment provider.
- Regional SMS
- Two local providers by API, pattern messages, and bridges into two SMS plugins.
- Right to left
- The price slider fix, a Persian typeface in store emails, a panel that follows the admin's language.
- Persian digits
- Converted before they reach an SMS gateway, so phone numbers dial.
- Mobile as identity
- A mobile number where the customer has no email, and users searchable by it.
- Restricted networks
- An alternate Telegram API base for servers that cannot reach the default.
Toolkit is installed on the sites Pigment Dev builds and looks after, and nowhere else. Some of its modules are licensed per site. It is here because it is the clearest example of how we work: the same fix, written once, maintained in one place, and switched on wherever a client needs it, from day one instead of on request.
// products/
Other things I keep running
Plugins, apps and internal systems. Each one is shipped, documented, and still looked after.
- Ultimate InvoiceInvoices, slips and labels for WooCommerce
- Ultimate Integration for TelegramEvery site event, in the chat you already read
- Easy ReceiptBank transfer receipts, against the right order
- Advanced Profile SolutionLogin, registration, profiles and the account area
- Q&A CommentsA moderated Q&A your experts actually answer
- ToolkitEvery fix we kept writing twice, written once
- html2elementorA finished design, rebuilt as a real Elementor site
- UptimeA WordPress fleet, watched from one channel
- Client HubA client's whole account, inside Telegram
- TunnelGuardDomain-level VPN split tunneling on macOS