New modern Znote AAC version compatible 8.1 - 8.5 PHP and TFS 1.1-1.6 // Canary
This PR improves the admin panel navigation and upgrades the content editing experience across ZnoteX.

  • Adds a new admin panel search system with a top-bar search input.
  • Indexes admin pages, settings fields, payment fields and layout options so admins can quickly jump to the right page or field.
  • Adds search result highlighting and smooth scrolling when opening a matched setting/field.
  • Integrates SCEditor as the shared BBCode/WYSIWYG editor.
  • Updates News, Changelog and Forum editing forms to use the new editor.
  • Moves BBCode rendering into shared helpers instead of duplicating TransformToBBCode() in multiple pages/layouts.
  • Improves forum posting UI, including better character selection and image limit validation.
  • Adds editor assets/styles for both ACP and public/forum usage.
  • Includes small ACP UI polish such as sidebar scrollbar styling and button link color fixes.

Notes​

The main goal is to make admin workflows faster and content editing more consistent, while centralizing BBCode handling so News, Changelog and Forum render formatted content through the same system.
- Fixed schema import failing on Windows — CRLF line endings broke statement splitting, so the whole SQL file was sent as one query
- Fixed installer writing the character name to `page_admin_access` instead of the account name, which locked everyone out of the admin panel
- Fixed the "write admin name into config.php" checkbox doing nothing — the syntax check called `PHP_BINARY -l`, which is not the PHP CLI under a web server
- Failed schema statements now report which statement failed
- Fresh installs redirect to `/install/` instead of showing a raw MySQL connection error
- Documented APCu in the README — added it to optional extensions, and a new Memory cache section covering the "APCu is not enabled" error, the `$config['cache']['memory'] = false` fallback, and how to pick the right Windows DLL build
- Corrected the README and installer wording: `page_admin_access` holds account names, not character names

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 answerThe new answer
Edit config.php by hand and hopeOpen /install/, six steps, done
Fork the whole AAC to restyle itDrop a theme folder in layouts/
Patch core files to add a feature — and lose it on the next updateDrop 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​

HookKindWhen
plugins.loadednotifyevery plugin is loaded
page.head / page.footercollectmarkup injected into the theme's own output
shop.pricefilterbefore a purchase is priced
shop.purchasednotifyafter the points are taken
account.registerednotifyafter an account is created
character.creatednotifyafter 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/:

MigrationWhat it adds
2.0.0_znote_config.sqlthe settings / options / plugin-state table
2.0.0_menus.sqlthe 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.
Top