Hobbes OS/2 Archive - Changelog
================================


v1.03 (2026-03-24)
------------------

NEW: Meta Merge tool (Admin / Editor, /admin/meta-merge)
  - Upload a .txt file to bulk-import metadata into existing archive
    entries without re-uploading the files themselves.
  - Parses two formats automatically:
      hobbes.txt: dashed-separator blocks with DIR / FILE / DESC fields.
      pmmail.txt: aligned key: value pairs (Archive Filename, Short
                  Description, Long Description, Proposed directory,
                  Your name, Program URL, Operating System/Version,
                  Additional requirements).
  - Matches entries to archive files by normalising the DIR path to
    slugified category segments and comparing against each file's
    stored category_upload_path + original_name.
  - Three-phase workflow: upload -> review report -> apply.
  - Review page shows:
      Matched entries  — before/after diff table per field; individual
                         checkboxes to include or exclude each entry.
      Unmatched entries — filename-only suggestions drawn from the
                          archive, manual file-ID input, or Skip.
      Skipped summary  — entries marked skip listed for reference.
  - Merge mode options: fill blank/"Unknown" fields only (default), or
    overwrite all existing values.
  - Approved files are re-indexed in the search index after merge.
  - Temporary sessions stored in data/merges/ and auto-expired at 2 h.

NEW: Bulk Delete tool (Admin only, /admin/bulk-delete)
  - Select any category to list its files (approved and pending).
  - Per-page selector: 25 / 50 / 100 / All.
    Warning banner shown when "All" is chosen with > 250 files.
  - "Select all on this page" checkbox.
  - "Select ALL N files in this category (all pages)" checkbox
    triggers server-side deletion of every file in the category
    regardless of pagination, so nothing is missed.
  - Listing shows filename, title, size, download count, date,
    and approved/pending status.
  - JavaScript confirmation dialog shows the deletion count before
    committing; blocks submission when nothing is selected.
  - Removes physical file from disk, metadata JSON from data/files/,
    and all entries from the search index.
  - Empty category upload directories are pruned after deletion.

NEW: Single-file delete from edit page (Admin only)
  - Danger Zone section added at the bottom of /file/edit/{id}.
  - Visible to admin role only; hidden from editors.
  - Confirmation dialog includes the filename.
  - Handled by POST /admin/file-delete/{id}; redirects to the file's
    category browse page after deletion.

NEW: Download counter displayed in all views
  - browse.php: "DLs" column added to the file listing table.
  - file.php:   "Downloads" row added to the file detail info table.
  - search.php: "DLs" column added to search results table.
  - Counter has been tracked since v1.01; this release makes it
    visible to all users including unauthenticated OS/2 guests.

NEW: Approve All Web Uploads button (/pool)
  - A green summary bar appears at the top of the pool when one or more
    web-uploaded files are pending approval.
  - "Approve All Web Uploads" button approves every pending web upload
    in a single action (POST /pool/approve_all, CSRF-protected).
  - FTP single files and FTP folder imports are intentionally excluded
    as they require metadata to be filled in or imported first.
  - Each approved file is immediately indexed in the search index.
  - JavaScript confirmation dialog shows the count before committing.

CHANGED: Admin dashboard links updated
  - "Meta Merge" button added to the Site Management panel.
  - "Bulk Delete" button (styled red) added to the Site Management panel.


v1.02
-----

NEW: FTP folder batch import (Editor+, /pool)
  - Drop an entire directory tree into data/pool/ via FTP.
  - The pool listing now detects folders and presents a Batch Import
    Folder button alongside individual file imports.
  - Sub-folders are mapped to nested categories using
    category_find_or_create(), which creates missing categories on
    the fly (slugified, de-duplicated) without overwriting existing ones.
  - Files receive titles derived from their filenames; required fields
    default to "Unknown" and can be corrected post-import.
  - Approve-immediately checkbox available for trusted bulk imports.
  - The pool folder is recursively removed after a successful import.
  - Reject & Delete Folder button purges the folder without importing.

NEW: "Hobbes OG" CSS preset (Admin > CSS)
  - Fourth built-in color preset added.
  - Deep navy blue palette inspired by the original hobbes.nmsu.edu
    color scheme: dark blue panels, cream text, teal accents.

ADDED: category_find_or_create() (includes/functions.php)
  - Looks up a category by name + parent in the live category list;
    creates it with a unique slug if absent. Used by folder import.

ADDED: category_upload_path_from_cats() (includes/functions.php)
  - Variant of category_upload_path() that accepts an explicit array
    instead of the static-cached categories_load() result. Required
    during batch import when the category list has been mutated but not
    yet written to disk.

ADDED: rmdir_recursive() (includes/functions.php)
  - Recursively removes a directory tree. Used to clean up pool folders
    after import and rejected folder deletion.

ADDED: pool_folder_file_count(), pool_folder_files() (includes/storage.php)
  - Helper functions for counting and listing files inside pool folders
    during the batch import workflow.

FIXED: Pool route regex (index.php)
  - Changed [a-z]+ to [a-z_]+ so that import_folder and reject_folder
    action names are matched correctly.


v1.01 (initial release)
------------------------

Core archive system:
  - Flat-file JSON storage; no database required.
  - Atomic file writes via temp file + rename() (no flock()).
  - Custom PHP session handler using the same atomic pattern.

Browsing and downloads:
  - Category tree browser (/browse, /browse/{slug}).
  - File detail page with full metadata display.
  - Download streaming in 64 KB chunks with per-file download counter.
  - Canonical URL scheme: /files/{cat/path}/{filename} and
    /download/{cat/path}/{filename}.
  - Legacy /file/{id} redirects (301) to canonical URL.

Search:
  - Inverted keyword index (data/index/search.json).
  - AND search with OR fallback; stop-word filter; minimum 3 characters.
  - Index updated on approval and on metadata edit.
  - Admin: Rebuild Search Index action.

User system:
  - Four roles: guest, contributor, editor, admin.
  - OS/2 User-Agent auto-detection for guest browse access.
  - Invite-code registration (invite generates code, recipient redeems).
  - Open registration toggle in site settings.
  - Password hashing via bcrypt (PASSWORD_BCRYPT).

Uploads and approval:
  - Web upload form for contributors (/upload).
  - FTP pool import for single files (/pool).
  - Optional .meta.json companion file to pre-fill pool import forms.
  - Editor approval workflow: approve, reject, or edit before publishing.
  - File stored using category slug path (uploads/{cat}/{subcat}/file).

File editing and organisation:
  - Edit metadata for any approved file (/file/edit/{id}).
  - Category change moves physical file on disk and updates stored_name.
  - Legacy per-file id/ directory layout supported and cleaned up on move.

Admin tools:
  - Site settings: name, tagline, open registration.
  - CSS theme editor: 26 individually configurable colors.
  - Three CSS presets: OS/2 Classic, Dark Mode, Green Terminal.
  - Category manager: add, rename, nest, delete.
  - User manager: list, change role, deactivate.
  - Landing page and splash screen Markdown editors.
