Docs  /  Developer Guide  /  File structure

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 addingGoes inLook at this for the pattern
A REST endpointsrc/API/Endpoints/AddCustomerNoteEndpoint.php — clean can_access + handle split
An admin emailsrc/Admin/Notifications/Emails/AssigneeOrderUpdateEmail.php
A customer emailsrc/Frontend/Notifications/Emails/CustomerOrderUpdateEmail.php
A settings tabsrc/Admin/Settings/Controllers/Any existing controller — they all share the same shape
A stateless utilitysrc/Helpers/DateHelper.php or UpdateState.php
A view partial (theme-overridable)Inside a Templates/ sub-folder under the featureAdmin/Orders/Templates/card/header.php
Hooking an existing surface — new file isn’t neededYour own addonSee 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 namespaceOrderUpdatesForWoo\<Module>\<Sub>
Hook namesorder_updates_for_woo_*
ConstantsOrderUpdatesForWoo\Shared\Config\Constants::*
Admin CSS classesawts_*
Admin-bar CSS classesawts-ab-*
Customer-portal CSS classesawts_cou_*
JS data attributesdata-awts-*
Database tableswp_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.