ZnoteX 2.0.0 — installer, themes, plugins, and a single admin panel
PHP 8.1 – 8.5 · TFS 1.1 – 1.6 · Canary compatible
This is the release that turns ZnoteX from
an AAC you edit into
an AAC you configure.
Three systems carry most of it — a web installer, a theme system and a plugin system — and
together they mean a normal install now involves editing
no PHP at all, and an update no
longer overwrites your work.
Why
ZnoteAAC went unmaintained for about five years. Picking it back up meant answering the three
questions every server owner hit:
| The old answer | The new answer |
|---|
| Edit config.php by hand and hope | Open /install/, six steps, done |
| Fork the whole AAC to restyle it | Drop a theme folder in layouts/ |
| Patch core files to add a feature — and lose it on the next update | Drop a plugin in plugins/ |
Everything below follows from that.
1. Web installer — install/
Open /install/ and answer six steps: requirements, database, server engine, schema import,
administrator, finish.
It refuses to install over a database without your OT server's schema. ZnoteX reads
accounts and players; it has never created them and will not pretend to. Importing
TFS/Canary's own schema.sql
after ZnoteX would overwrite what the installer just wrote, so
step 2 stops there and says so.
Step 5 creates a
real working administrator — an account, a character on it, and the password
hashed the way login.php expects on the selected engine — so /admin/ is reachable the moment
the installer finishes.
Step 6 writes
config.local.php, not config.php. That split is the point: config.php
keeps every default and every comment and stays replaceable by a future release, while
config.local.php holds only what is specific to this install and is included last. A checkbox
will write the admin name into config.php instead — it keeps a .bak, replaces only the
page_admin_access array, and validates the result with php -l before saving.
The installer then locks itself.
2. Theme system — layouts/
Every theme is a folder under layouts/, and
a theme is plain HTML and CSS. The PHP stayed
in ZnoteX: the 46 root pages were split so each one keeps its logic and hands a view the data it
already computed. Editing a theme is editing markup.
Managed from
Admin Panel → Layouts:
- Switch between installed themes, listed with screenshots, 12 per page
- Options — a theme declares its own settings in theme.json (social links, a tagline, a
colour); they appear on its card and are stored in the database, so no theme file is edited
- Browse themes — lists themes hosted in a repository and installs one with a button
- Child themes — a theme names another as its parent and overrides only the files it wants;
the rest falls through, so a recolour is one stylesheet instead of a fork
Downloads are refused unless the URL is
https and its host is on allowed_hosts. Archives
are extracted with zip-slip prevention, and ZipArchive falls back to PharData so the feature
works on the many Windows stacks that ship without the zip extension.
Ships with default (the Snavy layout, 31 views) plus _example and _childexample as
documented skeletons. Further themes, tibiacom_v1 among them, are published to the repository
and installed from the panel rather than bundled - a full theme is mostly images, and there is no
reason for every clone of the code to carry them. layouts/README.md is the contract.
3. Plugin system — plugins/
A plugin is a folder that adds public pages, admin pages, database tables and behaviour
without
editing a single ZnoteX file.
plugins/my_plugin/
plugin.json name, version, author, description [required]
plugin.php registers hooks
pages/<page>.php live at page.php?plugin=my_plugin&p=<page>
admin/<mod>.php appears in the admin sidebar
install.sql its tables
assets/ css, js, images
Nothing to register anywhere: dropping the file in is what publishes it. Admin module keys are
namespaced <plugin>__<module>, so a plugin cannot shadow a core module.
Install / update from the panel. ZnoteX records the version at install time and compares it
to the folder's plugin.json. Replace the folder with a newer version and an
Update button
appears; it re-runs install.sql, which is required to be idempotent, so new tables are created
and existing data is left alone. Neither
Disable nor
Uninstall ever drops a table.
ZnoteX never downloads a plugin by itself. A theme is markup; a plugin is PHP running on
every page with full site privileges, and nothing sandboxes it. Installing one stays a deliberate
act: you put the folder there, then you press Install.
Hooks
| Hook | Kind | When |
|---|
| plugins.loaded | notify | every plugin is loaded |
| page.head / page.footer | collect | markup injected into the theme's own output |
| shop.price | filter | before a purchase is priced |
| shop.purchased | notify | after the points are taken |
| account.registered | notify | after an account is created |
| character.created | notify | after a character is created |
shop.price is applied to the affordability check, the points deducted
and the shop log, so
the three cannot disagree. A hook that throws is caught and logged — one broken plugin does not
take the site down.
Example plugin — plugins/shop_coupons/
Redeemable codes that either credit shop points or take a percentage off the next purchase. It
exercises every extension type on purpose — a public page, an admin module, its own tables, and
all three hook shapes — and is commented as a tutorial. Ships
not installed.
4. Admin panel — admin/
The nine scattered admin_*.php pages are gone, replaced by one panel at /admin/ with a
responsive sidebar and a day/night switch.
18 modules, and adding one is a file with a
docblock header — no registry to edit:
/**
* Title: My Page
* Icon: fa-cube
* Group: Content
* Order: 40
*/
Dashboard · Players · Accounts · Visitors · Skills · News · Changelog · Gallery · Reports ·
Helpdesk · Feedback · Shop · Shop Orders · Auction · Layouts · Menus · Settings · Plugins
New beyond the old pages:
- Settings — most of config.php, edited from the browser and stored in znote_config
- Menus — the site navigation built in the panel instead of a template
- Accounts / Visitors — account browsing and site traffic
- Changelog and News as first-class modules
POST is CSRF-checked centrally, so a module cannot forget.
5. Everything else
- PHP 8.1 – 8.5. mysqli exception mode handled explicitly (since 8.1, @ no longer
suppresses it), deprecated dynamic properties removed, implicit-nullable parameters fixed.
- Engines. TFS 0.2.13+, 0.3.6+/0.4/OTX, 1.1 - 1.4.2, 1.6, OTHire and Canary, with the
differences behind helpers rather than scattered ifs.
- New public pages — bans.php (engine-aware; IP bans are counted but never listed) and
creatures.php (parsed from the server's own monster files, cached for 24 h).
- Maintenance mode that keeps administrators, /admin/, login.php and logout.php
reachable.
- Cache gained a per-instance expiry, so a page that parses hundreds of monster files no
longer inherits a global 5-second lifespan.
- Hardening. plugins/.htaccess and layouts/.htaccess block direct requests to anything
but assets/ — before this, install.sql and theme.json were readable over HTTP and a theme
view would execute standalone outside init.php. config.local.php and config.php.bak are
now git-ignored.
- Anonymous install tracker removed entirely, along with its schema columns and docs.
- Documentation — root README.md rewritten around the three systems, plus
layouts/README.md and plugins/README.md.
Upgrading from ZnoteAAC
Replace everything
except config.local.php, layouts/, plugins/ and engine/cache/,
then apply the new files in SQL/migrations/:
| Migration | What it adds |
|---|
| 2.0.0_znote_config.sql | the settings / options / plugin-state table |
| 2.0.0_menus.sql | the menu table and its 22 seeded entries |
config.php is safe to replace wholesale once your own values live in config.local.php.
Breaking: shop offers are no longer read from $config['shop_offers']. They live in
znote_shop_offers and are managed from Admin Panel → Shop Manager.
Verification
Everything claimed above was exercised on Uniform Server (PHP 8.3 + MariaDB) against a live
database, not reasoned about:
- Installer — full six-step walkthrough, including the refusal on a database with no OT
schema, then cleanup and restore
- Views — 28/28 pages render-equivalent before and after the split
- Themes — 103-theme pagination benchmark (266 ms → 87 ms after replacing glob with
readdir); 11/11 URL-guard cases; 8/8 archive-security cases including zip-slip
- Plugins — 26/26 registry, hook, page-resolution and redemption checks; then a real purchase
end to end: 10-point offer with a 25 % coupon charged 8, balance 100 → 92, znote_shop_logs
recording 8 rather than the list price, discount consumed, second purchase back at full price
- Install / update — the four states (not installed → install → update available → updated)
with an existing coupon surviving the update intact
- Direct access — install.sql, plugin.json and theme views returned 200 before the
.htaccess files and 403 after, with theme CSS still served
Credits
Original ZnoteAAC by Znote. Default layout by Blackwolf (Snavy). ZnoteX maintained by Alex —
Open Games Community.