Changelog
All notable changes to Bookgram will be documented in this file.
The format is based on
Keep a Changelog,
and this project adheres to
Semantic Versioning.
[10.05.0] - 2026-01-04
Added - TaskGram Plugin (NEW)
- Complete Task Management System
- Todo list plugin embeddable in any page via [plugin:taskgram id=instance-name]
- Add, edit, and delete tasks with descriptions
- Mark tasks as completed (moves to completion pool)
- Optional due date and time for each task
- Color-coded due date badges: Red (overdue), Yellow (today), Blue (future)
- Completion pool for managing finished tasks separately
- Restore completed tasks back to active list
- Delete individual completed tasks or clear entire completion pool
- Real-time search functionality across active and completed tasks
- Export Capabilities
- Export active tasks as CSV file
- Export completed tasks as CSV file
- Export tasks with due dates as iCal calendar file (.ics format)
- CSV includes: Task, Created date, Due date, Due time, Status, Completion date
- iCal uses VTODO entries compatible with calendar applications
- Authentication & Authorization
- Read-only view for guests and viewers
- Full task management for editors and admins only
- API endpoints protected with role-based authentication
- Login prompt for guests who want to add/modify tasks
- Admin Panel
- Configure task list title and description
- View statistics: active tasks, completed tasks, tasks with due dates, overdue count
- Bulk action to clear all completed tasks
- Usage instructions and embedding help
- Instance configuration with OS/2 Warp themed interface
- User Experience
- IBM OS/2 Warp 3 aesthetic matching BookGram theme
- Collapsible completion pool section
- Responsive layout for mobile and desktop
- Client-side search with instant filtering
- Form validation and error handling
- Loading states during API operations
Added - Page Image Management System
- Multiple Images Per Page
- Upload and manage multiple images associated with each page
- Separate from feature images - dedicated image gallery per page
- Images stored in /app/storage/page_images/{page-uuid}/
- Metadata tracking in images.json per page
- Image Metadata
- Unique image ID: img{timestamp}{random}
- Original filename preservation
- Optional description field for each image
- Upload timestamp and user tracking
- Image dimensions (width × height)
- File size tracking
- Image Operations
- Automatic resizing to max 2 megapixels for optimization
- Access-controlled endpoint: /page-image/{page-uuid}/{filename}
- Images deleted automatically when page is deleted
- Support for JPG, PNG, GIF, WebP formats
- Markdown Integration
- Easy image insertion in markdown editor
- Image browser shows all uploaded images for current page
- Click to copy markdown syntax: !Description
- Preview images before insertion
Added - HTML Embed System
- Static HTML Embedding
- Embed HTML files from /embeds/ directory into pages
- Shortcode syntax: [html:filename.html]
- Sandboxed iframe rendering with security controls
- Optional parameters: width, height, class, sandbox mode
- Security Features
- Three sandbox modes: default, strict, permissive
- Path traversal protection
- Only .html and .htm files allowed
- Files served from dedicated /embeds/ directory
- allow-scripts, allow-same-origin, allow-forms permissions
- Upload Browser
- Web-based file browser for HTML files
- Upload HTML files directly through interface
- View existing HTML embeds
- Delete unused HTML files
- Copy shortcode for easy embedding
Added - PHP Embed System
- Interactive PHP Embeds
- Execute PHP code within sandboxed environment
- Shortcode syntax: [php:script.php]
- Parameter passing via URL query string
- Interactive form controls for parameters
- Parameter System
- Define parameters in PHP comments: BOOKGRAM_PARAM
- Parameter types: select, range, text, checkbox
- Auto-generated form controls
- URL parameter passthrough
- Security Blacklist
- Extensive blacklist of dangerous PHP functions
- Blocked: file operations, shell execution, network access
- Blocked: eval, include, require, database access
- Token-based static analysis before execution
- Sandboxed iframe execution environment
Added - Plugin System Architecture
- Plugin Discovery
- Automatic plugin detection via plugin_ prefix
- Plugin manifest system (plugin.json)
- Per-page instance isolation
- Multiple instances per page support
- Plugin Features
- Instance-based architecture with separate data storage
- Setup scripts for initialization
- Admin panels with authentication
- CSS and JavaScript file loading
- Embed handlers for page rendering
- Plugin Authentication
- Token-based authentication system
- 1-hour token expiration
- Session passthrough to plugins
- Editor/admin permission requirements
- Secure plugin admin access via /plugin-admin/ route
- Plugin Modal System
- OS/2-themed draggable modal windows
- Configure plugins without leaving page
- Maximizable and minimizable modals
- Auto-reload page after configuration
- plugin-admin-saved message system
Included Plugins
#### SocialGram - Discussion & Social Feed
- Threaded discussions (4 levels deep)
- Image uploads and media embeds
- YouTube and Vimeo video embedding
- Guest posting with handles
- Admin moderation (edit/delete posts)
- Search functionality
- Real-time updates
- Privacy controls per instance
#### ChatGram - Live Chat
- Real-time IRC-style chat interface
- Private 1-on-1 messaging
- Guest access with custom handles
- AJAX polling (3-second intervals)
- Rate limiting
- User presence indicators
- Message history
- Online user list
#### JoinGram - Forms & Surveys
- Drag-and-drop form builder
- Multiple field types: text, textarea, dropdown, checkbox, radio, email, number, date, HR divider
- Markdown support in titles and descriptions
- CSV export of submissions
- Submission tracking with IPs
- Form change history logging
- Unique submission IDs
- Response management dashboard
#### PlayList - Audio Management
- Audio playlist management
- YouTube integration and embedding
- Playlist organization
- Track metadata support
- Audio player controls
#### TaskGram - Todo List Manager
- Task creation and management
- Due date/time tracking with color coding
- Completion pool system
- Search across tasks
- CSV and iCal export
- Multi-instance support
- Role-based permissions
Enhanced
- Markdown Parser
- Image support: !alt text
- Inline images rendered with proper sizing
- Support for page-specific images via /page-image/ endpoint
- Image dimensions preserved in rendering
- Page Editor
- Plugin manager with visual interface
- Insert plugin shortcodes at cursor position
- Configure plugin instances from editor
- Image upload and management panel
- Preview images before insertion
Technical Details - TaskGram
#### File Structure
plugin_taskgram/
├── plugin.json # Manifest
├── setup.php # Instance initialization
├── embed.php # Main UI
├── admin/index.php # Admin panel
├── api/
│ ├── task.php # CRUD operations
│ ├── export-csv.php # CSV export
│ └── export-ical.php # iCal export
├── css/style.css # OS/2 styling
└── js/taskgram.js # Frontend JavaScript
#### Data Model
json
{
"id": "taskuniqueidtimestamp",
"text": "Task description",
"created_at": "YYYY-MM-DD HH:MM:SS",
"updated_at": "YYYY-MM-DD HH:MM:SS",
"due_date": "YYYY-MM-DD",
"due_time": "HH:MM",
"hasduedatetime": true,
"completed_at": "YYYY-MM-DD HH:MM:SS",
"order": 0
}
#### API Endpoints
POST /plugin_taskgram/api/task.php
- Actions: create
task, deletetask, complete
task, restoretask, delete
completed, clearcompletion_pool
- Authentication: Editor/Admin required
- Returns: JSON response with success status
GET /plugintaskgram/api/export-csv.php?instanceid=X&type=active|completed
- Generates CSV download
- UTF-8 BOM for Excel compatibility
GET /plugintaskgram/api/export-ical.php?instanceid=X
- Generates iCalendar file
- VTODO entries for tasks with due dates
- Compatible with Google Calendar, Outlook, Apple Calendar
Security Enhancements
TaskGram Security
- Input sanitization with
htmlspecialchars()
- Task text limited to 500 characters
- Date/time format validation with regex
- File locking (
LOCK_EX) to prevent race conditions
- Instance ID sanitization
- XSS prevention on all user input
- Role-based access control (Editor/Admin only for modifications)
File Locations Modified
/plugin_taskgram/ - Complete new plugin (NEW)
/app/storage/page_images/ - Page image storage (NEW)
/embeds/ - HTML embed directory
/php_embeds/ - PHP embed directory
/app/core/plugins.php - Plugin system core
/app/core/plugin_auth.php - Plugin authentication
/templates/edit.php - Image manager, plugin manager
/templates/page.php - Image rendering, embed parsing
/HTMLEMBEDIMPLEMENTATION.md - HTML embed documentation (NEW)
/PLUGINSYSTEMGUIDE.md - Plugin development guide (NEW)
/PLUGINMODALSYSTEM.md - Modal system documentation (NEW)
Backward Compatibility
All existing pages and content fully compatible
Plugins are optional - BookGram works without them
Existing installations can add TaskGram without data migration
Image system is additive - existing pages unaffected
HTML/PHP embeds are optional features
---
[10.02.0] - 2025-12-20
Added - ChatGram Plugin
Session-Based Authentication Fallback
- Added fallback authentication that checks
$SESSION['bookgramuser'] directly
- Ensures logged-in users are detected even if core functions don't work
- Username detection fallback to session data
- Role detection fallback to
$SESSION['bookgramrole']
Unicode Avatar Support
- Replaced missing avatar images with Unicode symbol (👤)
- Added CSS styling for default avatars matching regular avatar dimensions
- Eliminates 404 errors from missing avatar files
Enhanced Debug Logging
- Server-side logging in
heartbeat.php and
post.php
- Client-side console logging for authentication status
- Debug information in API responses showing session data
Added - JoinGram Plugin
Markdown Rendering Support
- Title and Description fields now support markdown formatting
- Converts
bold to bold text
- Converts
italic to italic text
- Automatically converts URLs to clickable hyperlinks opening in new window
- Converts newlines to
<br> tags
New Form Field Types
-
Horizontal Line (HR): Visual divider with no configuration needed
-
Radio Buttons: Single-choice selection field with custom options
Enhanced Submission Tracking
- Unique submission ID generated for each response:
sub[random][timestamp]
- IP address logging with proxy header support (X-Forwarded-For, HTTP
CLIENTIP)
- Both displayed in responses table and CSV export
Form Change Logging System
- Automatic change tracking when form is saved
- Logs: Change ID, timestamp, user, field count changes, summary
- Stores up to 100 most recent changes in
change_log.json
- New "Change Log" admin page showing detailed modification history
- Tracks who changed what and when
Customizable Form Title and Description
- New "Form Configuration" section in Dashboard
- Admin/Editors can customize form title and description
- Configuration stored per instance in
config.json
- Replaced static "Recruitment Board" with user-defined titles
Changed - ChatGram Plugin
Authentication Flow
-
heartbeat.php now checks session directly if
isloggedin() returns false
-
post.php uses same session fallback mechanism
- Defensive function existence checks before calling core functions
API Responses
- Heartbeat and post endpoints now include debug information
- Debug data shows authentication status and session values
Changed - JoinGram Plugin
Admin Interface Updates
- Responses table now shows Submission ID and IP Address as first columns
- CSV export includes new submission ID and IP fields
- Sidebar navigation includes new "Change Log" link
- Dashboard includes form configuration editor
Default Values
- Changed all default titles from "Recruitment Board" to "Application Form"
- Changed button text from "Submit Application" to "Submit Form"
- Updated in
setup.php,
embed.php,
index.php, and
dashboard.php
Fixed - ChatGram Plugin
Guest vs Logged-in User Detection
- Fixed issue where admin users were treated as guests
- Heartbeat now properly detects logged-in users via session fallback
- Messages from admins now show with correct user type and role
- Admin/Editor users now appear in online user list
Avatar Display Issues
- Fixed 404 errors from missing default avatar files
- Avatar fallback now uses Unicode symbol with proper styling
JSON Response Integrity
- Added output buffering to prevent stray output breaking JSON
- Error suppression to ensure clean API responses
- Try-catch wrappers around all API endpoints
Technical Details - ChatGram
#### Authentication Implementation
Session fallback pattern:
php
$isloggedin = functionexists('isloggedin') ? islogged_in() : false;
if (!$isloggedin && isset($SESSION['bookgramuser'])) {
$isloggedin = true;
}
Username retrieval:
php
$userid = functionexists('getcurrentusername') ? getcurrentusername() : null;
if (!$userid && isset($SESSION['bookgram_user'])) {
$userid = $SESSION['bookgram_user'];
}
#### File Locations Modified
/plugin_chatgram/api/heartbeat.php - Session fallback authentication
/plugin_chatgram/api/post.php - Session fallback authentication
/plugin_chatgram/js/chatgram.js - Unicode avatar support, debug logging
/plugin_chatgram/css/chatgram.css - Default avatar styling
Technical Details - JoinGram
#### Markdown Processing Function
php
function process_markdown($text) {
// Bold, italic, URLs, line breaks
return $processed_text;
}
#### Unique ID Generation
php
$submissionid = 'sub' . bin2hex(randombytes(8)) . '' . time();
#### Change Log Entry Structure
json
{
"changeid": "chgabc123",
"timestamp": "2025-12-20 10:30:00",
"user": "admin",
"oldfieldcount": 5,
"newfieldcount": 7,
"changes_summary": "Added 2 fields (5 → 7)"
}
#### File Locations Modified
/plugin_joingram/admin/form-builder.php - HR/Radio fields, change logging
/plugin_joingram/admin/dashboard.php - Form configuration editor
/plugin_joingram/admin/changelog.php - Change log viewer (NEW)
/plugin_joingram/admin/responses.php - Submission ID and IP columns
/plugin_joingram/admin/download.php - CSV export with new fields
/pluginjoingram/admin/sidebar.php - Change Log navigation link
/plugin_joingram/embed.php - Markdown rendering, HR/Radio rendering
/plugin_joingram/submit.php - Unique ID and IP logging
/plugin_joingram/index.php - Updated defaults
/plugin_joingram/setup.php - Updated defaults
Backward Compatibility
ChatGram fully compatible with existing installations
JoinGram existing submissions work without submissionid and ipaddress fields (display as "N/A")
Existing forms work with new field types
Form configurations created before this version will use default titles
---
[2.08.0] - 2025-11-15
Added
Optional Password Protection for Private Articles
- New optional password field for private pages as an additional security layer
- Password input field in article editor (
templates/edit.php:71-91)
- Password protection works in conjunction with share tokens
- Visitors need BOTH the share link AND correct password to access protected pages
- Authors and admins can view their own private pages without password
- Password prompt template for guest authentication (
templates/password-prompt.php)
- New
/verify-password route for password verification (
index.php:161-197)
- Session-based password authentication (stays authenticated for the session)
- Password removal option in editor interface
Navigation Enhancement
- "Back to Folder" button on article pages (
templates/page.php:81)
- Returns user to the current folder/directory level
- Positioned on left side of page actions bar
- Automatically calculates parent folder from article path
- Returns to root browser if article is at top level
Changed
Data Model Updates
- Page JSON schema now includes:
-
password_hash (string) - BCrypt/Argon2 hash of the optional password
- Updated
savepagecontent() function signature to accept
$password parameter (
app/core/functions.php:367)
- Enhanced
canviewprivate_page() to check password authentication (
app/core/functions.php:199-238)
- Added
verifypagepassword() function for secure password verification (
app/core/functions.php:246-267)
Fixed
Code Block Rendering
- Fixed
strip_tags() to allow
<pre> tags (
templates/page.php:27)
- Code blocks with
and ~~~ now properly preserve line breaks
- Previously <pre> tags were being stripped, causing code to concatenate on one line
- CSS styling from v2.07 now works correctly with allowed <pre> tags
- Changed white-space: pre to white-space: pre-wrap for responsive wrapping (public/css/style.css:244)
- Added word-wrap: break-word and max-width: 100% to prevent code blocks from breaking page layout
- Long lines now wrap within boundaries while preserving intentional line breaks
- Horizontal scrolling still available for very long unbreakable tokens
User Experience Improvements
- Password prompt shown automatically when accessing password-protected pages
- Clear error messages on incorrect password attempts
- Visual indicators show when password protection is enabled (🔒 icon with text)
- Authors see password status in both edit and view modes
- Inline JavaScript toggle for password field visibility in editor
Security
Password Hashing
- Passwords hashed using PHP's passwordhash() with PASSWORDDEFAULT (currently Argon2id or BCrypt)
- Password verification uses password_verify() to prevent timing attacks
- Passwords never stored in plaintext
- Session-based authentication prevents repeated password entry
- CSRF protection on password submission
Access Control Flow
1. Check if page is private
2. Check if user is author/admin (bypass all checks)
3. Verify share token is valid
4. If password is set, verify password via session or prompt
5. Grant access only if all conditions met
Technical Details
#### Password Protection Implementation
Password field is optional - private pages can have token-only or token+password protection
Passwords are hashed immediately upon save
Empty password field preserves existing password
"Remove password" checkbox explicitly clears password protection
Password verification stores authentication in $SESSION['authenticatedpages'] array
#### File Locations Modified
/app/core/functions.php - Password hashing and verification functions
/templates/edit.php - Password input field with toggle (lines 71-103)
/templates/page.php - Password protection indicators (lines 45-56)
/templates/password-prompt.php - Password entry form (NEW)
/index.php - Password handling in save and verify routes
/CHANGELOG.md - This file (UPDATED)
/VERSION.txt - Version number update
#### Database Schema Changes
For existing pages, the password field will be automatically initialized:
password_hash: '' (empty string = no password)
Backward Compatibility
Fully backward compatible with v2.07
Existing private pages without passwords continue to work with token-only access
Public pages unaffected
No data migration required
Usage Example
Setting up a password-protected page:
Edit or create a page
Check "Make this page private"
Enter a password in the "Optional Password Protection" field
Save the page
Copy the share link
Share both the link AND password with intended recipients
Accessing a password-protected page:
Click the share link (includes token in URL)
Enter the password when prompted
Submit to view the page
Password authentication persists for the session
Known Limitations
Password authentication is session-based only (no persistent "remember me")
No password strength requirements enforced
No password reset mechanism (author must change it manually)
Session expires when browser is closed
Multiple failed password attempts are not rate-limited
---
[2.07.0] - 2025-11-15
Added
Private Articles with Share Links
- New privacy control for articles - pages can now be marked as private
- Private pages are only accessible via a unique, auto-generated share link
- Privacy checkbox added to article editor (templates/edit.php:62-68)
- Share link display in edit mode for private pages (templates/edit.php:70-83)
- Share link display on page view for authors/admins (templates/page.php:43-62)
- Private pages are hidden from:
- Directory browser unless viewed by author/admin
- Search results unless viewed by author/admin
- RSS feeds unless viewed by author/admin
- Access control:
- Authors can always view their own private pages
- Admins can view all private pages
- Anyone with the share token URL can view the page
- 403 Forbidden error for unauthorized access attempts
Code Block Rendering Fix
- Fixed markdown code blocks to properly preserve line breaks and formatting
- Added CSS styling for <pre> elements with white-space: pre (public/css/style.css:239-254)
- Improved monospace font rendering for code blocks
- Added proper styling for inline code vs. multi-line code blocks
- Print stylesheet also updated to handle code blocks correctly
Changed
Data Model Updates
- Page JSON schema now includes:
- is_private (boolean) - privacy status flag
- share_token (string) - unique 32-character hex token for private access
- Updated savepagecontent() function signature to accept $is_private parameter (app/core/functions.php:367)
- Privacy settings preserved during page reverts (app/core/functions.php:891)
Function Enhancements
- getdirectorycontents() - Now filters private pages based on user permissions (app/core/functions.php:571-574)
- search_pages() - Excludes private pages from search unless user has permission (app/core/functions.php:929-932)
- getallpagessortedby_date() - Filters private pages from RSS/listings (app/core/functions.php:990-993)
- Added canviewprivate_page() helper function (app/core/functions.php:199-226)
- Added generatesharetoken() utility function (app/core/functions.php:190-192)
Technical Details
#### Privacy Implementation
Share tokens are generated using bin2hex(random_bytes(16)) for cryptographic security
Token validation uses hash_equals() to prevent timing attacks
Tokens are automatically generated when a page is first marked private
Tokens are cleared when a page is made public
Token parameter passed via URL query string: ?token={32-char-hex}
#### File Locations Modified
/public/css/style.css - Code block styling (lines 239-254)
/app/core/functions.php - Privacy functions and data filtering
/templates/edit.php - Privacy checkbox and share link UI
/templates/page.php - Private page notice and share link display
/index.php - Privacy parameter handling in save route (line 120)
/CHANGELOG.md - This file (NEW)
`
#### Database Schema Changes
No database migrations required (file-based storage).
For existing pages, the privacy fields will be automatically initialized to default values:
is_private: false
share_token: '' (empty string)
Security Notes
Share tokens are 32 characters (128 bits of entropy)
Private pages return HTTP 403 for unauthorized access
Share links do not expire (manual regeneration requires toggling privacy off/on)
Authors cannot see other authors' private pages without the share link
Admins have full access to all pages regardless of privacy settings
Backward Compatibility
All changes are fully backward compatible with v2.06
Existing pages will default to public (is_private = false)
No data migration required
Pages without privacy fields will be treated as public
Known Limitations
Share tokens do not expire and cannot be regenerated without toggling privacy
No analytics on who accessed a private page via share link
Private pages still increment view counts when accessed via share link
No bulk privacy operations available in admin panel
---
[2.06.0] - Previous Version
Initial release of v2.06 with:
File-based content management system
Markdown support via Parsedown
User authentication (Admin/Editor roles)
Page revision history
UUID-based permanent links
Feature image support
Directory/folder organization
Full-text search
RSS feed
Profile pages
Hit tracking (total and unique views)
Print-friendly styling
IBM OS/2 Warp 3 retro theme
Mobile responsive design
Movable launchpad navigation