PHP Classes

File: docs/proposals/PublicFileAreas.md

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   docs/proposals/PublicFileAreas.md   Download  
File: docs/proposals/PublicFileAreas.md
Role: Auxiliary data
Content type: text/markdown
Description: Auxiliary data
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 5 days ago
Size: 6,844 bytes
 

Contents

Class file image Download

Public File Areas

> Draft ? generated by AI, may not have been reviewed for accuracy.

Overview

This proposal adds an is_public flag to individual file areas. When enabled, the area's file listing and file downloads are accessible to unauthenticated visitors without requiring a BBS account. Comments, uploads, and all other interactive features remain login-gated. This is a registered (premium) feature.

Motivation

Sysops running public shareware libraries, FTN file echo mirrors, or community software archives may want to allow anonymous visitors to browse and download files without requiring registration. The existing gemini_public flag already exposes file areas over the Gemini protocol; this proposal extends the same concept to the web interface.

Scope

What anonymous visitors can do on a public area

  • Browse the file listing for the area (filename, description, size, date)
  • Download individual files
  • View the file preview modal (images, text/NFO, ANSI, ZIP browser, etc.)

What anonymous visitors cannot do

  • Post, view, or interact with file comments
  • Upload files
  • Access private file areas (`is_private = true`)
  • Access any area that does not have `is_public = true`

Implementation Status

Implemented in migration v1.11.0.33_public_file_areas.sql.

Admin Configuration

Per-area flag

A Public File Area checkbox is added to the file area editor in Admin ? Area Management ? File Areas, following the same pattern as the existing Gemini Public checkbox (#fileareaGeminiPublic in fileareas.twig).

  • HTML ID: `fileareaIsPublic`
  • Loaded from `area.is_public` in JS (same pattern as `gemini_public`, line ~592 of `fileareas.twig`)
  • Saved in the `PUT /api/fileareas/{id}` request body
  • Default: `false` (no change to existing behaviour)
  • Wrapped in `{% if license_valid %}...{% else %}<lock notice>{% endif %}`

Public file area index page

A new BBS setting Enable Public Files Index controls whether public areas are listed on a discoverable index page (/public-files). Options:

  • Disabled (default) ? public areas are only reachable via direct URL
  • Enabled ? a `/public-files` page lists all public areas and is linked from the guest navigation bar

This setting is stored as features.public_files_index in config/bbs.json and toggled via Admin ? BBS Settings ? BBS Features. Requires a registered license to enable.

Database Change

New boolean column on file_areas (migration v1.11.0.33_public_file_areas.sql):

ALTER TABLE file_areas ADD COLUMN IF NOT EXISTS is_public BOOLEAN NOT NULL DEFAULT FALSE;
CREATE INDEX IF NOT EXISTS idx_file_areas_is_public ON file_areas(is_public) WHERE is_public = TRUE;

Backend Changes

FileAreaManager ? access control (src/FileAreaManager.php:133)

canAccessFileArea() now allows $userId = null (guest) when the area has is_public = true and is_private = false:

// Areas flagged is_public allow unauthenticated (guest) access
if (!empty($area['is_public'])) {
    return true;
}
// Non-public areas require a logged-in user
if ($userId === null) {
    return false;
}
return true;

FileAreaManager ? save is_public (updateFileArea())

$isPublic is saved alongside gemini_public:

$isPublic = \BinktermPHP\License::isValid() ? (bool)($data['is_public'] ?? false) : false;

Stored as string 'true'/'false' for PostgreSQL compatibility.

Web route ? /files/{tag} (routes/web-routes.php)

If is_public = true and is_private = false: skips requireAuth(), gets optional user via getCurrentUser(), passes is_public_area and initial_area to files.twig.

Web route ? /public-files

New route, no auth required. Returns 404 when public_files_index feature is disabled. Queries `file_areas WHERE is_public = TRUE AND is_private = FALSE AND is_active = TRUE. Renderstemplates/public_files.twig`.

API ? GET /api/files

Guest access allowed when the requested area has is_public = true. Credits and activity tracking are skipped for guests.

API ? GET /api/files/{id}/download

Guest access allowed for public areas. All credit deduction/reward logic is skipped for unauthenticated requests.

API ? GET /api/files/{id}/preview

Extended to allow unauthenticated access when the file belongs to a public area (viaPublicArea = true), alongside the existing share-token bypass.

API ? GET /api/files/{id}/comments

No change ? still requires auth. Frontend simply does not call this endpoint for guest sessions.

API ? GET /api/fileareas

No change ? remains login-required. Public area metadata for /files/{tag} is resolved server-side in the web route handler.

Frontend Changes

files.twig

A is_public_area Twig variable (boolean) and initial_area (area record) are passed from the route. When true and no user is logged in:

  • Sidebar (file area list, search, stats) is hidden via Twig conditional
  • JS `document.ready` loads files directly for `guestPublicArea` without calling `loadFileAreas()` or `loadStats()`
  • Upload button remains hidden (JS `selectFileArea()` is not called for guests)
  • Comments section shows the existing "log in to comment" alert
  • Open Graph meta tags are added via `{% block meta_tags %}`

public_files.twig

New template listing public file areas in a card grid. Each area shows tag, description, and file count, linked to /files/AREATAG.

Navigation (templates/base.twig, templates/shells/web/base.twig)

  • The Files nav link is now only shown to authenticated users
  • A Public Files nav link appears for guests when `public_files_index` is enabled

Security Considerations

  • `is_private = true` areas are never served publicly. `canAccessFileArea()` checks `empty($area['is_private'])` before allowing guest access.
  • File downloads are served via API (no storage path exposed to client).
  • Private areas (tag format `PRIVATE_USER_{id}`) always have `is_private = true`.
  • `updateFileArea()` forces `is_public = false` when `License::isValid()` is false, preventing manipulation via direct API call.

Premium Gating

This feature requires a valid license (License::isValid()).

  • The Public checkbox in the file area editor is wrapped in `{% if license_valid %}`.
  • The Public Files Index toggle in BBS Settings is wrapped in `{% if license_valid %}`.
  • `updateFileArea()` forces `is_public = false` when no valid license is present.
  • The area access check at serve time does not re-check the license ? once set, the area stays public even if the license lapses, to avoid service disruption.

See docs/proposals/PremiumFeatures.md for the premium features table.