File structure
Where everything lives, and where new code goes. The table at the bottom is the cheat-sheet.
The top-level tree
Open the plugin folder and you’ll see this:
order-updates-for-woo/
├── order-updates-for-woo.php # entry file; HPOS declaration; boots Plugin
├── readme.txt # what WordPress.org shows on the listing page
├── languages/ # .pot file lives here; translations land here
├── assets/
│ ├── Admin/{css,js,images}/
│ └── Frontend/{css,js}/
├── src/ # all PHP, PSR-4, namespaced
├── tests/ # PHPUnit suite
├── scripts/ # dev utilities (sample-data seeder, email previewer)
└── docs/ # this documentation site (excluded from plugin zip)
Only src/, assets/, languages/, the entry file, and readme.txt ship in the plugin zip. Tests, scripts, and these docs are dev-only — excluded via .distignore.
Inside src/
Six modules, one purpose each:
src/
├── API/
│ ├── Concerns/ VerifiesAccess, RendersCardHtml (traits)
│ ├── Contracts/ Registrable (interface)
│ └── Endpoints/ 27 REST endpoint classes + Analytics/ sub-folder
├── Admin/
│ ├── AdminBar/ Heartbeat-driven mention/assignment badge
│ ├── Analytics/ Analytics page controller + views
│ ├── Notifications/Emails/ AdminOrderUpdateEmail, AssigneeOrderUpdateEmail, etc.
│ ├── Orders/ Meta box, deleted-update audit
│ └── Settings/ 9 settings tab controllers
├── Frontend/
│ ├── Notifications/Emails/ CustomerOrderUpdateEmail, CustomerRatingFollowupEmail, etc.
│ └── OrderUpdates/ Customer portal view, shortcode, controllers
├── Helpers/ 26 stateless utility classes
├── Shared/
│ ├── Attachments/ AttachmentService, AttachmentsDb, AttachmentSigner, AttachmentStorage
│ ├── Audit/ Deleted-update log
│ ├── Config/ Constants, Variables
│ ├── Language/ JS string registry
│ ├── Notifications/ OrderUpdateEmailBase + dispatch pipeline
│ ├── Updates/ OrderUpdatesDb, UpdatesTable, NoteActionPolicy
│ └── Validation/ Validator
└── Welcome/ First-activation welcome page
Where to put new code
The fastest path to writing something new: find the nearest sibling and mirror it. Use this table to figure out which folder.
| What you’re adding | Goes in | Look at this for the pattern |
|---|---|---|
| A REST endpoint | src/API/Endpoints/ | AddCustomerNoteEndpoint.php — clean can_access + handle split |
| An admin email | src/Admin/Notifications/Emails/ | AssigneeOrderUpdateEmail.php |
| A customer email | src/Frontend/Notifications/Emails/ | CustomerOrderUpdateEmail.php |
| A settings tab | src/Admin/Settings/Controllers/ | Any existing controller — they all share the same shape |
| A stateless utility | src/Helpers/ | DateHelper.php or UpdateState.php |
| A view partial (theme-overridable) | Inside a Templates/ sub-folder under the feature | Admin/Orders/Templates/card/header.php |
| Hooking an existing surface — new file isn’t needed | Your own addon | See Hooks for the action/filter to use |
Why Templates/ folders matter
Anywhere you see a Templates/ sub-folder — src/Admin/Orders/Templates/card/, src/Frontend/Notifications/Templates/, etc. — those files are theme-overridable. Copy them into your-theme/order-updates-for-woo/<path-after-Templates/> and your version wins automatically.
If you’re adding a view that should be customisable without forking the plugin, drop it inside a Templates/ sub-folder and call it through View::render(). Never use raw include for views — you lose the override path. See Theme overrides for the full mechanism.
Naming conventions
| PHP namespace | OrderUpdatesForWoo\<Module>\<Sub> |
| Hook names | order_updates_for_woo_* |
| Constants | OrderUpdatesForWoo\Shared\Config\Constants::* |
| Admin CSS classes | awts_* |
| Admin-bar CSS classes | awts-ab-* |
| Customer-portal CSS classes | awts_cou_* |
| JS data attributes | data-awts-* |
| Database tables | wp_awts_* (prefix respects $wpdb->prefix) |
If your addon adds anything that ends up in the DOM or the database, prefix it with something obviously yours — not awts_*. That namespace is taken.