Files
tinker_tickets/README.md
Jared Vititoe 7695c6134c Accessibility pass: ARIA roles, label associations, CSS class migrations
- Add role=dialog/aria-modal/aria-labelledby to all 12 modal overlays (JS + PHP)
- Add aria-label="Close" to all 14 modal close buttons
- Add full ARIA combobox pattern to @mention autocomplete (listbox, option, aria-selected, aria-expanded)
- Add for= attributes to admin filter form labels (AuditLog, UserActivity, ApiKeys)
- Remove dead closeOnAdvancedSearchBackdropClick() from advanced-search.js

CSS/JS style cleanup:
- Move .ascii-banner static styles from JS inline to CSS class; add .ascii-banner--glow
- Add .ascii-banner-cursor, .loading-overlay--hiding, .has-overlay, tr[data-clickable]
- Add .animate-fadein/.animate-fadeout/.comment--deleting to ticket.css
- Add .lt-toast--hiding to base.css; remove opacity/transition inline JS
- Remove redundant cursor:pointer JS (already in th{} CSS rule)
- Remove trailing space in lt-select class attributes

Bug fixes:
- base.js: boot overlay opacity inline style was overriding .fade-out class opacity via
  specificity (1000 vs 20), preventing the fade-out animation — removed
- ascii-banner.js: cursor used blink-caret (border-color only) instead of blink-cursor
  (opacity-based), so the █ cursor never actually blinked — fixed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:29:58 -04:00

442 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tinker Tickets
A feature-rich PHP-based ticketing system designed for tracking and managing data center infrastructure issues with enterprise-grade workflow management and a retro terminal aesthetic.
**Documentation**: [Wiki](https://wiki.lotusguild.org/en/Services/service-tinker-tickets)
**Design System**: [web_template](https://code.lotusguild.org/LotusGuild/web_template) — shared CSS, JS, and layout patterns for all LotusGuild apps
## Styling & Layout
Tinker Tickets uses the **LotusGuild Terminal Design System**. For all styling, component, and layout documentation see:
- [`web_template/README.md`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/README.md) — full component reference, CSS variables, JS API
- [`web_template/base.css`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/base.css) — unified CSS (`.lt-*` classes)
- [`web_template/base.js`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/base.js) — `window.lt` utilities (toast, modal, CSRF, fetch helpers)
- [`web_template/php/layout.php`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/php/layout.php) — PHP base layout template
**Key conventions:**
- All `.lt-*` CSS classes come from `base.css` — do not duplicate them in `assets/css/`
- All `lt.*` JS utilities come from `base.js` — use `lt.toast`, `lt.modal`, `lt.api`, etc.
- CSP nonces: every `<script>` tag needs `nonce="<?php echo $nonce; ?>"`
- CSRF: inject `window.CSRF_TOKEN` via the nonce-protected inline script block; `lt.api.*` adds the header automatically
## Design Decisions
The following features are intentionally **not planned** for this system:
- **Email Integration**: Discord webhooks are the chosen notification method
- **SLA Management**: Not required for internal infrastructure use
- **Time Tracking**: Out of scope for current requirements
- **OAuth2/External Identity Providers**: Authelia is the only approved SSO method
## Core Features
### Dashboard & Ticket Management
- **View Modes**: Toggle between Table view and Kanban card view
- **Collapsible Sidebar**: Click the arrow to collapse/expand the filter sidebar
- **Inline Ticket Preview**: Hover over ticket IDs for a quick preview popup (300ms delay)
- **Stats Widgets**: Clickable cards for quick filtering (Open, Critical, Unassigned, Today's tickets)
- **Full-Text Search**: Search across tickets, descriptions, and metadata
- **Advanced Search**: Date ranges, priority ranges, user filters with saved filter support
- **Ticket Assignment**: Assign tickets to specific users with quick-assign from dashboard
- **Priority Tracking**: P1 (Critical) to P5 (Minimal Impact) with color-coded indicators
- **Custom Categories**: Hardware, Software, Network, Security, General
- **Ticket Types**: Maintenance, Install, Task, Upgrade, Issue, Problem
- **Export**: Export selected tickets to CSV or JSON format
- **Ticket Linking**: Reference other tickets in comments using `#123456789` format
### Ticket Visibility Levels
- **Public**: All authenticated users can view the ticket
- **Internal**: Only users in specified groups can view the ticket (at least one group required)
- **Confidential**: Only the creator, assignee, and admins can view the ticket
### Workflow Management
- **Status Transitions**: Enforced workflow rules (Open → Pending → In Progress → Closed)
- **Workflow Designer**: Visual admin UI at `/admin/workflow` to configure transitions
- **Workflow Validation**: Server-side validation prevents invalid status changes
- **Admin Controls**: Certain transitions can require admin privileges
- **Comment Requirements**: Optional comment requirements for specific transitions
### Collaboration Features
- **Markdown Comments**: Full Markdown support with live preview, toolbar, and table rendering
- **@Mentions**: Tag users in comments with autocomplete
- **Comment Edit/Delete**: Comment owners and admins can edit or delete comments
- **Auto-linking**: URLs in comments are automatically converted to clickable links
- **File Attachments**: Upload files to tickets with drag-and-drop support
- **Ticket Dependencies**: Link tickets as blocks/blocked-by/relates-to/duplicates
- **Activity Timeline**: Complete audit trail of all ticket changes
### Ticket Templates
- **Template Management**: Admin UI at `/admin/templates` to create/edit templates
- **Quick Creation**: Pre-configured templates for common issues
- **Auto-fill**: Templates populate title, description, category, type, and priority
### Recurring Tickets
- **Scheduled Tickets**: Automatically create tickets on a schedule
- **Admin UI**: Manage at `/admin/recurring-tickets`
- **Flexible Scheduling**: Daily, weekly, or monthly recurrence
- **Cron Integration**: Run `cron/create_recurring_tickets.php` to process
### Custom Fields
- **Per-Category Fields**: Define custom fields for specific ticket categories
- **Admin UI**: Manage at `/admin/custom-fields`
- **Field Types**: Text, textarea, select, checkbox, date, number
- **Required Fields**: Mark fields as required for validation
### API Key Management
- **Admin UI**: Generate and manage API keys at `/admin/api-keys`
- **Bearer Token Auth**: Use API keys with `Authorization: Bearer YOUR_KEY` header
- **Expiration**: Optional expiration dates for keys
- **Revocation**: Revoke compromised keys instantly
### User Management & Authentication
- **SSO Integration**: Authelia authentication with LLDAP backend
- **Role-Based Access**: Admin and standard user roles
- **User Groups**: Groups displayed in settings modal, used for visibility
- **User Activity**: View per-user stats at `/admin/user-activity`
- **Session Management**: Secure PHP session handling with timeout
### Bulk Actions (Admin Only)
- **Bulk Close**: Close multiple tickets at once
- **Bulk Assign**: Assign multiple tickets to a user
- **Bulk Priority**: Change priority for multiple tickets
- **Bulk Status**: Change status for multiple tickets
- **Checkbox Click Area**: Click anywhere in the checkbox cell to toggle
### Admin Pages
Access all admin pages via the **Admin dropdown** in the dashboard header.
| Route | Description |
|-------|-------------|
| `/admin/templates` | Create and edit ticket templates |
| `/admin/workflow` | Visual workflow transition designer |
| `/admin/recurring-tickets` | Manage recurring ticket schedules |
| `/admin/custom-fields` | Define custom fields per category |
| `/admin/user-activity` | View per-user activity statistics |
| `/admin/audit-log` | Browse all audit log entries |
| `/admin/api-keys` | Generate and manage API keys |
### Notifications
- **Discord Integration**: Webhook notifications for ticket creation and updates
- **Rich Embeds**: Color-coded priority indicators and ticket links
- **Dynamic URLs**: Ticket links adapt to the server hostname (set `APP_DOMAIN` in `.env`)
### Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| `Ctrl/Cmd + E` | Toggle edit mode (ticket page) |
| `Ctrl/Cmd + S` | Save changes (ticket page) |
| `Ctrl/Cmd + K` | Focus search box (dashboard) |
| `N` | New ticket (dashboard) |
| `J` / `K` | Next / previous row (dashboard table) |
| `Enter` | Open selected ticket (dashboard) |
| `G` then `D` | Go to dashboard |
| `1``4` | Quick status change (ticket page) |
| `ESC` | Cancel edit / close modal |
| `?` | Show keyboard shortcuts help |
### Security Features
- **CSRF Protection**: Token-based protection with constant-time comparison
- **Rate Limiting**: Session-based AND IP-based rate limiting to prevent abuse
- **Security Headers**: CSP with nonces (no unsafe-inline), X-Frame-Options, X-Content-Type-Options
- **SQL Injection Prevention**: All queries use prepared statements with parameter binding
- **XSS Protection**: HTML escaped in markdown parser, CSP headers block inline scripts
- **Audit Logging**: Complete audit trail of all actions
- **Visibility Enforcement**: Access checks on ticket views, downloads, and bulk operations
- **Collision-Safe IDs**: Ticket IDs verified unique before creation
## Technical Architecture
### Backend
- **Language**: PHP 7.4+
- **Database**: MariaDB/MySQL
- **Architecture**: MVC pattern with models, views, controllers
- **Authentication**: Authelia SSO with LLDAP backend
### Frontend
- **HTML5/CSS3**: Semantic markup with retro terminal styling
- **JavaScript**: Vanilla JS with Fetch API for AJAX
- **Markdown**: Custom markdown parser with toolbar
- **Terminal UI**: Box-drawing characters, monospace fonts, CRT effects
- **Mobile Responsive**: Touch-friendly controls, responsive layouts
### Database Tables
| Table | Purpose |
|-------|---------|
| `tickets` | Core ticket data with visibility |
| `ticket_comments` | Markdown-supported comments |
| `ticket_attachments` | File attachment metadata |
| `ticket_dependencies` | Ticket relationships |
| `users` | User accounts with groups |
| `user_preferences` | User settings |
| `audit_log` | Complete audit trail |
| `status_transitions` | Workflow configuration |
| `ticket_templates` | Reusable templates |
| `recurring_tickets` | Scheduled tickets |
| `custom_field_definitions` | Custom field schemas |
| `custom_field_values` | Custom field data |
| `saved_filters` | Saved filter combinations |
| `bulk_operations` | Bulk operation tracking |
| `api_keys` | API key storage with hashed keys |
#### `tickets` Table Key Columns
| Column | Type | Description |
|--------|------|-------------|
| `ticket_id` | varchar(9) | Unique 9-digit identifier |
| `visibility` | enum | `public`, `internal`, `confidential` |
| `visibility_groups` | varchar(500) | Comma-separated group names (for internal) |
| `created_by` | int | Foreign key to users |
| `assigned_to` | int | Foreign key to users (nullable) |
| `updated_by` | int | Foreign key to users |
| `priority` | int | 15 (1=Critical, 5=Minimal) |
| `status` | varchar(20) | Open, Pending, In Progress, Closed |
#### Indexed Columns (performance)
- `tickets`: `ticket_id` (unique), `status`, `priority`, `created_at`, `created_by`, `assigned_to`, `visibility`
- `audit_log`: `user_id`, `action_type`, `entity_type`, `created_at`
### API Endpoints
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/update_ticket.php` | POST | Update ticket with workflow validation |
| `/api/assign_ticket.php` | POST | Assign ticket to user |
| `/api/add_comment.php` | POST | Add comment to ticket |
| `/api/get_template.php` | GET | Fetch ticket template |
| `/api/get_users.php` | GET | Get user list for assignments |
| `/api/bulk_operation.php` | POST | Perform bulk operations |
| `/api/ticket_dependencies.php` | GET/POST/DELETE | Manage dependencies |
| `/api/upload_attachment.php` | GET/POST | List or upload attachments |
| `/api/export_tickets.php` | GET | Export tickets to CSV/JSON |
| `/api/generate_api_key.php` | POST | Generate API key (admin) |
| `/api/revoke_api_key.php` | POST | Revoke API key (admin) |
| `/api/delete_comment.php` | POST | Delete comment (owner/admin) |
| `/api/update_comment.php` | POST | Update comment (owner/admin) |
| `/api/delete_attachment.php` | POST/DELETE | Delete attachment |
| `/api/download_attachment.php` | GET | Download attachment (visibility checked) |
| `/api/check_duplicates.php` | GET | Check for duplicate tickets |
| `/api/manage_recurring.php` | CRUD | Recurring tickets (admin) |
| `/api/manage_templates.php` | CRUD | Templates (admin) |
| `/api/manage_workflows.php` | CRUD | Workflow rules (admin) |
## Project Structure
```
tinker_tickets/
├── api/
│ ├── add_comment.php # POST: Add comment
│ ├── assign_ticket.php # POST: Assign ticket to user
│ ├── bulk_operation.php # POST: Bulk operations (admin only)
│ ├── check_duplicates.php # GET: Check for duplicate tickets
│ ├── delete_attachment.php # POST/DELETE: Delete attachment
│ ├── delete_comment.php # POST: Delete comment (owner/admin)
│ ├── download_attachment.php # GET: Download with visibility check
│ ├── export_tickets.php # GET: Export tickets to CSV/JSON
│ ├── generate_api_key.php # POST: Generate API key (admin)
│ ├── get_template.php # GET: Fetch ticket template
│ ├── get_users.php # GET: Get user list
│ ├── manage_recurring.php # CRUD: Recurring tickets (admin)
│ ├── manage_templates.php # CRUD: Templates (admin)
│ ├── manage_workflows.php # CRUD: Workflow rules (admin)
│ ├── revoke_api_key.php # POST: Revoke API key (admin)
│ ├── ticket_dependencies.php # GET/POST/DELETE: Ticket dependencies
│ ├── update_comment.php # POST: Update comment (owner/admin)
│ ├── update_ticket.php # POST: Update ticket (workflow validation)
│ └── upload_attachment.php # GET/POST: List or upload attachments
├── assets/
│ ├── css/
│ │ ├── base.css # LotusGuild Terminal Design System (symlinked from web_template)
│ │ ├── dashboard.css # Dashboard + terminal styling
│ │ └── ticket.css # Ticket view styling
│ ├── js/
│ │ ├── advanced-search.js # Advanced search modal
│ │ ├── ascii-banner.js # ASCII art banner (rendered in boot overlay on first visit)
│ │ ├── base.js # LotusGuild JS utilities — window.lt (symlinked from web_template)
│ │ ├── dashboard.js # Dashboard + bulk actions + kanban + sidebar
│ │ ├── keyboard-shortcuts.js # Keyboard shortcuts (uses lt.keys)
│ │ ├── markdown.js # Markdown rendering + ticket linking (XSS-safe)
│ │ ├── settings.js # User preferences
│ │ ├── ticket.js # Ticket + comments + visibility
│ │ ├── toast.js # Deprecated shim (no longer loaded — all callers use lt.toast directly)
│ │ └── utils.js # escapeHtml (→ lt.escHtml), getTicketIdFromUrl, showConfirmModal
│ └── images/
│ └── favicon.png
├── config/
│ └── config.php # Config + .env loading
├── controllers/
│ ├── DashboardController.php # Dashboard with stats + filters
│ └── TicketController.php # Ticket CRUD + timeline + visibility
├── cron/
│ └── create_recurring_tickets.php # Process recurring ticket schedules
├── helpers/
│ └── ResponseHelper.php # Standardized JSON responses
├── middleware/
│ ├── AuthMiddleware.php # Authelia SSO integration
│ ├── CsrfMiddleware.php # CSRF protection
│ ├── RateLimitMiddleware.php # Session + IP-based rate limiting
│ └── SecurityHeadersMiddleware.php # CSP with nonces, security headers
├── models/
│ ├── ApiKeyModel.php # API key generation/validation
│ ├── AuditLogModel.php # Audit logging + timeline
│ ├── BulkOperationsModel.php # Bulk operations tracking
│ ├── CommentModel.php # Comment data access
│ ├── CustomFieldModel.php # Custom field definitions/values
│ ├── DependencyModel.php # Ticket dependencies
│ ├── RecurringTicketModel.php # Recurring ticket schedules
│ ├── StatsModel.php # Dashboard statistics
│ ├── TemplateModel.php # Ticket templates
│ ├── TicketModel.php # Ticket CRUD + visibility + collision-safe IDs
│ ├── UserModel.php # User management + groups
│ ├── UserPreferencesModel.php # User preferences
│ └── WorkflowModel.php # Status transition workflows
├── scripts/
│ ├── cleanup_orphan_uploads.php # Clean orphaned uploads
│ └── create_dependencies_table.php # Create ticket_dependencies table
├── uploads/ # File attachment storage
├── views/
│ ├── admin/
│ │ ├── ApiKeysView.php # API key management
│ │ ├── AuditLogView.php # Audit log browser
│ │ ├── CustomFieldsView.php # Custom field management
│ │ ├── RecurringTicketsView.php # Recurring ticket management
│ │ ├── TemplatesView.php # Template management
│ │ ├── UserActivityView.php # User activity report
│ │ └── WorkflowDesignerView.php # Workflow transition designer
│ ├── CreateTicketView.php # Ticket creation with visibility
│ ├── DashboardView.php # Dashboard with kanban + sidebar
│ └── TicketView.php # Ticket view with visibility editing
├── .env # Environment variables (GITIGNORED)
├── README.md # This file
└── index.php # Main router
```
## Workflow States
### Default Workflow
```
Open → Pending → In Progress → Closed
↑ ↑
└───────────┘
```
All states can transition to Closed (with comment).
Closed tickets can be reopened to Open or In Progress.
## Setup & Configuration
### 1. Environment Configuration
Copy the example file and edit with your values:
```bash
cp .env.example .env
nano .env
```
Required environment variables:
```env
DB_HOST=your_db_host
DB_USER=your_db_user
DB_PASS=your_password
DB_NAME=ticketing_system
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
APP_DOMAIN=your.domain.example
TIMEZONE=America/New_York
```
**Note**: `APP_DOMAIN` is required for Discord webhook ticket links to work correctly. Without it, links will default to localhost.
### 2. Cron Jobs
Add to crontab for recurring tickets:
```bash
# Run every hour to create scheduled recurring tickets
0 * * * * php /path/to/tinkertickets/cron/create_recurring_tickets.php
```
### 3. File Uploads
Ensure the `uploads/` directory exists and is writable:
```bash
mkdir -p /path/to/tinkertickets/uploads
chown www-data:www-data /path/to/tinkertickets/uploads
chmod 755 /path/to/tinkertickets/uploads
```
### 4. Authelia Integration
Tinker Tickets uses Authelia for SSO. User information is passed via headers:
- `Remote-User`: Username
- `Remote-Name`: Display name
- `Remote-Email`: Email address
- `Remote-Groups`: User groups (comma-separated)
Admin users must be in the `admin` group in LLDAP.
## Developer Notes
Key conventions and gotchas for working with this codebase:
1. **Session format**: `$_SESSION['user']['user_id']` (not `$_SESSION['user_id']`)
2. **API auth check**: Verify `$_SESSION['user']['user_id']` exists
3. **Admin check**: `$_SESSION['user']['is_admin'] ?? false`
4. **Config path**: `config/config.php` (not `config/db.php`)
5. **Comments table**: `ticket_comments` (not `comments`)
6. **CSRF**: Required for all POST/DELETE requests via `X-CSRF-Token` header
7. **Cache busting**: Use `?v=YYYYMMDD` query params on JS/CSS files
8. **Ticket linking**: Use `#123456789` in markdown-enabled comments
9. **User groups**: Stored in `users.groups` as comma-separated values
10. **API routing**: All API endpoints must be registered in `index.php` router
11. **Session in APIs**: `RateLimitMiddleware` starts the session — do not call `session_start()` again
12. **Database collation**: Use `utf8mb4_general_ci` (not `unicode_ci`) for new tables
13. **Discord URLs**: Set `APP_DOMAIN` in `.env` for correct ticket URLs in Discord notifications
14. **Ticket ID extraction**: Use `getTicketIdFromUrl()` helper in JS files
15. **CSP nonces**: All inline `<script>` tags require `nonce="<?php echo $nonce; ?>"`
16. **Visibility validation**: Internal visibility requires at least one group — validated server-side
17. **Rate limiting**: Both session-based AND IP-based limits are enforced
18. **Relative timestamps**: Dashboard dates use `lt.time.ago()` and refresh every 60s; full date is always in the `title` attribute for hover
19. **Boot sequence**: Shows ASCII banner then boot messages on first page visit per session (`sessionStorage.getItem('booted')`); removed on subsequent loads
20. **No raw `fetch()`**: All AJAX calls use `lt.api.get/post/put/delete()` — never use raw `fetch()`. The wrapper auto-adds `Content-Type: application/json` and `X-CSRF-Token`, auto-parses JSON, and throws on non-2xx.
21. **Confirm dialogs**: Never use browser `confirm()`. Use `showConfirmModal(title, message, type, onConfirm)` (defined in `utils.js`, available on all pages). Types: `'warning'` | `'error'` | `'info'`.
22. **`utils.js` on all pages**: `utils.js` is loaded by all views (including admin). It provides `escapeHtml()`, `getTicketIdFromUrl()`, and `showConfirmModal()`.
23. **No `toast.js`**: `toast.js` is deprecated and no longer loaded by any view. Use `lt.toast.success/error/warning/info()` directly from `base.js`.
24. **CSS utility classes** (dashboard.css): Use `.text-green`, `.text-amber`, `.text-cyan`, `.text-danger`, `.text-open`, `.text-closed`, `.text-muted`, `.text-muted-green`, `.text-sm`, `.text-center`, `.nowrap`, `.mono`, `.fw-bold`, `.mb-1` for typography. Use `.admin-container` (1200px) or `.admin-container-wide` (1400px) for admin page max-widths. Use `.admin-header-row` for title+button header rows. Use `.admin-form-row`, `.admin-form-field`, `.admin-label`, `.admin-input` for filter forms. Use `.admin-form-actions` for filter form button groups. Use `.table-wrapper` + `td.empty-state` for tables. Use `.setting-grid-2` and `.setting-grid-3` for modal form grids. Use `.lt-modal-sm` or `.lt-modal-lg` for modal sizing.
25. **CSS utility classes** (ticket.css): Use `.form-hint` (green helper text), `.form-hint-warning` (amber helper text), `.visibility-groups-list` (checkbox row), `.group-checkbox-label` (checkbox label) for ticket forms.
## File Reference
| File | Purpose |
|------|---------|
| `index.php` | Main router for all routes |
| `config/config.php` | Config loader + .env parsing |
| `api/update_ticket.php` | Ticket updates with workflow + visibility validation |
| `api/download_attachment.php` | File downloads with visibility check |
| `api/bulk_operation.php` | Bulk operations with visibility filtering |
| `models/TicketModel.php` | Ticket CRUD, visibility filtering, collision-safe ID generation |
| `models/ApiKeyModel.php` | API key generation and validation |
| `middleware/AuthMiddleware.php` | Authelia header parsing + session setup |
| `middleware/CsrfMiddleware.php` | CSRF token generation and validation |
| `middleware/SecurityHeadersMiddleware.php` | CSP headers with per-request nonce generation |
| `middleware/RateLimitMiddleware.php` | Session + IP-based rate limiting |
| `assets/js/dashboard.js` | Dashboard UI, kanban, sidebar, bulk actions |
| `assets/js/ticket.js` | Ticket UI, visibility editing |
| `assets/js/markdown.js` | Markdown parsing + ticket linking (XSS-safe) |
| `assets/css/dashboard.css` | Terminal styling, kanban, sidebar |
## Security Implementations
| Feature | Implementation |
|---------|---------------|
| SQL Injection | All queries use prepared statements with parameter binding |
| XSS Prevention | HTML escaped in markdown parser; CSP with per-request nonces |
| CSRF Protection | Token-based with constant-time comparison (`hash_equals`) |
| Session Security | Fixation prevention, secure cookies, session timeout |
| Rate Limiting | Session-based + IP-based (file storage) |
| File Security | Path traversal prevention, MIME type validation |
| Visibility | Enforced on ticket views, downloads, and bulk operations |
## License
Internal use only - LotusGuild Infrastructure