]> **Docker Folders** — Organize your Docker containers into drag-and-drop folders with a modern Vue 3 interface, live resource stats, and real-time updates. ###2026.08.17-3 - feat(settings): add a "Buy me a coffee" Ko-fi link ###2026.08.17 - feat(settings): add a "Buy me a coffee" Ko-fi link - feat(ui): collapse the create buttons into one menu, widen the search - feat: rename to "Docker Folders" and shorten the page URLs - feat(ui): one update button, and a re-check inside the confirm modal - feat(ui): collapse the three create buttons to "+" plus an icon ###2026.08.16-dev2 - fix(ui): render the update-check controls before settings land ###2026.08.16-dev1 - docs: summarize each release in CHANGELOG, stop regenerating it - chore: drop the beta label from Image Updates settings - fix(ui): stop compose buttons blinking in while availability is checked - test: fix session tests broken by PHPUnit output, silence constant warnings - fix(security): validate every user-influenced filesystem path - chore: prepare repo for Community Applications submission ###2026.08.10-dev1 - feat: show release notes in the update confirm modal ###2026.07.26-dev5 - feat: list containers before updating, and update several at once ###2026.07.26-dev4 - feat: show a container's schedules in its expanded card - fix: read logs from TTY containers, brighten secondary text, link container icons ###2026.07.26-dev3 - feat: replace container status dot/bar with a halo around the icon ###2026.07.26-dev2 - feat: restructure expanded container card and widen its click target ###2026.07.26-dev1 - fix: modals and add create container button ###2026.07.25-dev2 - fix: schedules modal ###2026.07.18-dev8 - Faint tint on collapsed folders; fix expand animation snapping open ###2026.07.18-dev7 - Replace folder left-border accent with expanded-state gradient tint ###2026.07.18-dev6 - Add design guardrails (DESIGN.md) and migrate off palette colors ###2026.07.18-dev5 - Remove live connection status indicator from main header ###2026.07.18-dev4 - Hide icon-only Docker Containers title bar variant ###2026.07.18-dev3 - Fix legacy button hiding on the native Docker page ###2026.07.18-dev2 - Add settings to hide legacy Docker containers and buttons ###2026.07.18-dev1 - Add targeted update checks for containers and compose stacks - Sync package-lock.json with package.json - Add GitHub Actions release workflows for main and dev ###2026.06.06-dev1 - Fix cron registration: write to plugin config dir, drop user field ###2026.05.30-dev2 - Pin yarn in .prototools to match packageManager - Show conflicting ports in expanded container details ###2026.05.30-dev1 - Add port conflict badge for stopped containers ###2026.04.22-dev1 - Fix update-check cron registration and link Update badge to release notes ###2026.04.12-dev1 - Add container/stack scheduling and backup system ###2026.04.11-dev9 - Add container expand/collapse animation and brighten info text - Optimize container stats with cgroup reads and slow-data caching ###2026.04.11-dev8 - Add compose file version history with rollback support ###2026.04.11-dev7 - Fix modal close button alignment, populate faded preview from compose file ###2026.04.11-dev6 - Consolidate modals, simplify compose folder UI, fix validation env ###2026.04.11-dev5 - Compose editor: logs tab, recompose flow, save lock, YAML highlighting ###2026.04.11-dev4 - Disable text wrapping in compose/env monospace textareas ###2026.04.11-dev3 - Add Logs tab to edit-stack modal with auto-refresh, fix modal layout bugs ###2026.04.11-dev2 - Force textarea resize/fill with !important to beat Unraid global CSS ###2026.04.11-dev1 - Stream compose stack start, fix create-stack textarea and banner flash ###2026.04.07-dev2 - Fix compose plugin import banner for users without compose.manager ###2026.04.07-dev1 - Render all modals in parent window to escape iframe clipping ###2026.03.21-dev14 - Fix folder counts, cleanup deleted containers, unify modals, delete image option ###2026.03.21-dev13 - Fix autostart: read/write Unraid's flat file, not just XML templates ###2026.03.21-dev12 - Fix: hide .title div before inject comment, not after ###2026.03.21-dev11 - Hide empty .title div generated by Unraid for inject page ###2026.03.21-dev10 - Fix autostart detection, add delay support, modal UIs, accordion width ###2026.03.21-dev9 - Extract icons into reusable components; uniform sizes; always-visible globe/autostart ###2026.03.21-dev8 - Add Create Stack button and container autostart toggle ###2026.03.21-dev7 - Fix compose UX: conditional play/stop, container sync, PortBindings ###2026.03.21-dev6 - add test files - Fix compose SHA256 hash, empty folder UX, and import banner persistence - Fix Docker Compose v2.32.4 SHA256 hash after upstream binary update ###2026.03.21-dev5 - Render compose modal in parent page DOM to avoid iframe clipping ###2026.03.21-dev4 - Lock parent scroll when modal open via wheel/touch/key event prevention ###2026.03.21-dev3 - Elevate iframe z-index when modal open; fix input/textarea styling ###2026.03.21-dev2 - Fix modal buttons and tabs overridden by Unraid button reset ###2026.03.21-dev1 - Fix modal overlay coverage, sizing, tab styling; add compose export feature ###2026.03.15-dev7 - Fix modal clipping: cap overlay to available iframe space instead of expanding iframe ###2026.03.15-dev6 - Fix scroll feedback loop: lock parent scroll while modal is open ###2026.03.15-dev5 - Fix modal clipping in iframe: direct height floor + correct max-height ###2026.03.15-dev4 - Fix compose modal visibility in iframe and title color ###2026.03.15-dev3 - Fix compose import file migration, modal z-index, and editor styling ###2026.03.15-dev2 - Fix compose UI: controls visibility, import banner, mock data alignment ###2026.03.15-dev1 - Add Docker Compose management feature ###2026.03.06-2 - Fix CHANGELOG.md entries for 2026.03.06 release - Fix SHA digest stored as container image, stop/restart timeouts, cron restore on boot, and modal viewport centering in iframe ###2026.02.28-dev11 - Show container actions in kebab menu only for mobile list view ###2026.02.28-dev10 - Mobile responsive overhaul, inline logs polish, and kebab menu improvements ###2026.02.28-dev2 - Add inline logs panel in list view expanded details ###2026.02.28-dev1 - Fix 502 error on container Console and Logs actions - Add "Clear" button to update check log viewer ###2026.02.27-dev6 - Fix stale update badges after checks find no updates ###2026.02.27-dev5 - Resolve SHA image refs to tags when recreating containers ###2026.02.27-dev4 - Fix false positive update detection after pulling images ###2026.02.27-dev3 - Fix ExposedPorts/Volumes empty object encoding in container recreate ###2026.02.27-dev2 - Surface Docker API errors in recreate flow, add plugin log entries ###2026.02.27-dev1 - Make post-pull operations resilient to prevent stream from dying ###2026.02.25-dev10 - Fix post-pull red X and add auto-recreate container support ###2026.02.25-dev9 - Fix Database::prepare() not existing, use query() instead ###2026.02.25-dev8 - Add PHP tests for checkAllImageUpdates update check logic - Fix iframe not shrinking on folder collapse, harden update loop ###2026.02.25-dev7 - Fix update check timeout and uneven card button sizing ###2026.02.25-dev6 - Fix update checks only completing for first container ###2026.02.25-dev5 - Fix build script bugs and improve robustness ###2026.02.25-dev3 - Fix folder stats bars, add StatsBar tests, update project docs - Extract reusable components, refactor backend config and stats - Fix concurrent container action loading states, add dev/stable release channels ###2026.02.25-dev2 - Extract reusable components, refactor backend config and stats - Fix concurrent container action loading states, add dev/stable release channels ###2026.02.25 - Add logging to manual update checks, gitignore phpunit cache - chore: update gitignore ###2026.02.24-10 - Add update check logging with viewer in settings ###2026.02.24-9 - Fix session check: csrf_token is runtime-only, not in session file ###2026.02.24-8 - Fix session validation for Unraid's custom session cookie ###2026.02.24-7 - Add Docker-based PHPUnit tests for auth layer - Fix PUT/DELETE CSRF validation and folder collapse persistence ###2026.02.24-6 - Fix form controls using nonexistent --input-background variable ###2026.02.24-5 - Fix dropdown text color on dark themes in settings page ###2026.02.24-4 - Revamp settings page: dark theme, grouped sections, container exclude modal ###2026.02.24-3 - Fix auth: read Unraid's Flask session cookie instead of PHP session ###2026.02.24-2 - Fix iframe auth: add credentials to all fetch calls, aggregate changelog by date - chore: update build script ###2026.02.15 **Added** - Mobile-responsive layout with sm breakpoints across all components - Collapsible "Unfoldered Containers" section (persisted to localStorage) - Auto-group Docker Compose stacks into folders - Replace Docker Containers section setting (fully takes over the Docker tab) - Styled confirmation modals for stop/restart/remove/delete-folder actions - Container edit, WebUI, Console, Logs, Project, Support in kebab menu - WebUI globe icon on card/row action bar - Average CPU/memory stats on collapsed folders with loading skeleton - Show ports on collapsed folders (configurable, default on) - Restart count badge on collapsed containers - Running/total count in folder badge - Settings button in nav bar - Volume paths link to Unraid file browser - Changelog displayed on settings page - Container details in expanded accordion: command, health status, and docker labels - Health/command/labels displayed side-by-side with I/O stats in two-column layout **Changed** - Grid minmax reduced from 350px to 280px for single-column on small screens - Reduced padding and gaps on mobile (cards, folders, modals, header) - List view expanded accordion uses single-column grid on mobile - Flat nav button style (removed gradients) - Smaller text sizes throughout - Stats bar track uses subtle color-mix for better dark theme contrast - Dark theme hover states use color-mix for better contrast - Settings button moved to far left of nav bar **Fixed** - List view expanded details used excessive left margin, now uses more available width - Header no longer breaks into column layout on desktop inside iframe - Console and Logs URLs corrected - Stats bars hidden in iframe due to responsive breakpoints - Stats load immediately on page load instead of waiting for poll interval - Stats persist through folder collapse/expand cycles (no skeleton flash) - Card view accordion text overflow with long volume paths - Confirm modal font size when rendered inside Unraid iframe - Accordion chevron direction (now points right when collapsed, down when expanded) ###2026.02.14 - Folder organization with drag-and-drop - Create, edit, and delete folders with custom icons and colors - Drag containers between folders and reorder within - Import/export folder configuration - Grid and list view modes with drag lock toggle - Health status distinction (green vs blue indicators) - Persistent folder state (collapsed/expanded) - Automatic database backups on uninstall ###2026.02.13 - Initial release - Docker container listing with start/stop/restart controls - SQLite database with automatic migrations - Modern responsive interface built with Vue 3 and Tailwind /dev/null) if [ -n "$INSTALLED_PKGS" ]; then echo "Found existing installation(s), removing..." for pkg in $INSTALLED_PKGS; do PKG_NAME=$(basename "$pkg") echo " Removing: $PKG_NAME" removepkg "$PKG_NAME" 2>&1 | grep -v "Skipping" || true done echo "Old versions removed." fi # Clean up old downloaded packages if [ -d "$CONFIG_DIR" ]; then echo "Cleaning up old package files..." find "$CONFIG_DIR" -name "${PLUGIN_NAME}-*.txz" -mtime +7 -delete 2>/dev/null || true fi echo "Pre-install cleanup complete." ]]> https://github.com/&github;/releases/download/v&version;/&name;-&version;.txz &md5; &version; "${LOG_FILE}" # Verify plugin directory exists if [ ! -d "${PLUGIN_DIR}" ]; then echo "ERROR: Plugin directory not found: ${PLUGIN_DIR}" echo "ERROR: Plugin directory not found: ${PLUGIN_DIR}" >> "${LOG_FILE}" echo "Installation may have failed. Check package integrity." exit 1 fi # Create necessary directories echo "Creating configuration directories..." mkdir -p "${CONFIG_DIR}/backups" mkdir -p "${CONFIG_DIR}/data" # Handle existing database DB_EXISTS=0 if [ -f "${CONFIG_DIR}/data.db" ]; then DB_EXISTS=1 echo "Existing database found." echo "Existing database found at: ${CONFIG_DIR}/data.db" >> "${LOG_FILE}" # Create backup of existing database before migration BACKUP_FILE="${CONFIG_DIR}/backups/pre-upgrade-$(date +%Y%m%d-%H%M%S).db" echo "Creating backup: ${BACKUP_FILE}" cp "${CONFIG_DIR}/data.db" "${BACKUP_FILE}" if [ $? -eq 0 ]; then echo "Backup created successfully." >> "${LOG_FILE}" else echo "WARNING: Failed to create backup. Continuing anyway..." echo "WARNING: Failed to create backup" >> "${LOG_FILE}" fi fi # Run database migrations (works for both new and existing databases) echo "Running database migrations..." echo "Running migrations..." >> "${LOG_FILE}" if [ ! -f "${PLUGIN_DIR}/scripts/migrate.php" ]; then echo "ERROR: Migration script not found: ${PLUGIN_DIR}/scripts/migrate.php" echo "ERROR: Migration script not found" >> "${LOG_FILE}" exit 1 fi # Run migration and capture output MIGRATION_OUTPUT=$(php "${PLUGIN_DIR}/scripts/migrate.php" 2>&1) MIGRATION_STATUS=$? echo "$MIGRATION_OUTPUT" >> "${LOG_FILE}" if [ $MIGRATION_STATUS -ne 0 ]; then echo "ERROR: Database migration failed!" echo "ERROR: Migration failed with status $MIGRATION_STATUS" >> "${LOG_FILE}" echo "" echo "Migration output:" echo "$MIGRATION_OUTPUT" echo "" # Restore backup if this was an upgrade if [ $DB_EXISTS -eq 1 ] && [ -f "${BACKUP_FILE}" ]; then echo "Restoring database from backup..." cp "${BACKUP_FILE}" "${CONFIG_DIR}/data.db" echo "Database restored from backup." >> "${LOG_FILE}" fi echo "See log file for details: ${LOG_FILE}" exit 1 fi echo "Migration completed successfully." echo "Migration completed successfully" >> "${LOG_FILE}" # Set proper permissions echo "Setting permissions..." find "${PLUGIN_DIR}" -type d -exec chmod 755 {} \; find "${PLUGIN_DIR}" -type f -name "*.page" -exec chmod 644 {} \; find "${PLUGIN_DIR}" -type f -name "*.php" -exec chmod 644 {} \; find "${PLUGIN_DIR}" -type f -name "*.sql" -exec chmod 644 {} \; find "${PLUGIN_DIR}" -type f -name "*.sh" -exec chmod 755 {} \; # Make cron check script executable if [ -f "${PLUGIN_DIR}/scripts/check-updates.php" ]; then chmod 755 "${PLUGIN_DIR}/scripts/check-updates.php" echo " check-updates.php made executable" fi # Clean up stale cron file from older versions that wrote to /etc/cron.d/ # (update_cron never read it; the schedule now lives in CONFIG_DIR). if [ -f "/etc/cron.d/${PLUGIN_NAME}" ]; then rm -f "/etc/cron.d/${PLUGIN_NAME}" echo " Removed stale /etc/cron.d/${PLUGIN_NAME}" fi # Make restore-cron script executable and run it to restore schedule from DB if [ -f "${PLUGIN_DIR}/scripts/restore-cron.php" ]; then chmod 755 "${PLUGIN_DIR}/scripts/restore-cron.php" CRON_OUTPUT=$(/usr/bin/php "${PLUGIN_DIR}/scripts/restore-cron.php" 2>&1) if [ -n "$CRON_OUTPUT" ]; then echo " $CRON_OUTPUT" echo "$CRON_OUTPUT" >> "${LOG_FILE}" fi fi # Make compose scripts executable if [ -f "${PLUGIN_DIR}/scripts/compose-autostart.php" ]; then chmod 755 "${PLUGIN_DIR}/scripts/compose-autostart.php" echo " compose-autostart.php made executable" fi if [ -f "${PLUGIN_DIR}/scripts/compose-shutdown.php" ]; then chmod 755 "${PLUGIN_DIR}/scripts/compose-shutdown.php" echo " compose-shutdown.php made executable" fi # Make event hook scripts executable if [ -d "${PLUGIN_DIR}/event" ]; then chmod 755 "${PLUGIN_DIR}/event/"* echo " Event hook scripts made executable" fi find "${PLUGIN_DIR}" -type f -name "*.html" -exec chmod 644 {} \; find "${PLUGIN_DIR}" -type f -name "*.js" -exec chmod 644 {} \; find "${PLUGIN_DIR}" -type f -name "*.css" -exec chmod 644 {} \; if [ -f "${CONFIG_DIR}/data.db" ]; then chmod 644 "${CONFIG_DIR}/data.db" echo "Database permissions set." >> "${LOG_FILE}" fi # Verify installation echo "Verifying installation..." VERIFY_FAIL=0 if [ ! -f "${PLUGIN_DIR}/DockerFolders.page" ]; then echo " ✗ DockerFolders page not found." echo "WARNING: DockerFolders page not found" >> "${LOG_FILE}" VERIFY_FAIL=1 else echo " ✓ DockerFolders page found" fi if [ ! -f "${PLUGIN_DIR}/Folders.page" ]; then echo " ✗ Folders page not found." echo "WARNING: Folders page not found" >> "${LOG_FILE}" VERIFY_FAIL=1 else echo " ✓ Folders page found" fi if [ ! -f "${CONFIG_DIR}/data.db" ]; then echo " ✗ Database file not created." echo "WARNING: Database file not created" >> "${LOG_FILE}" VERIFY_FAIL=1 else echo " ✓ Database file created" fi if [ ! -f "${PLUGIN_DIR}/assets/index.html" ]; then echo " ✗ Frontend assets not found." echo "WARNING: Frontend assets not found" >> "${LOG_FILE}" VERIFY_FAIL=1 else echo " ✓ Frontend assets found" fi if [ ! -f "${PLUGIN_DIR}/api/folders.php" ]; then echo " ✗ API endpoints not found." echo "WARNING: API endpoints not found" >> "${LOG_FILE}" VERIFY_FAIL=1 else echo " ✓ API endpoints found" fi # List installed files for debugging echo "" >> "${LOG_FILE}" echo "Installed files:" >> "${LOG_FILE}" ls -la "${PLUGIN_DIR}/" >> "${LOG_FILE}" 2>&1 echo "" >> "${LOG_FILE}" echo "Page files:" >> "${LOG_FILE}" ls -la "${PLUGIN_DIR}"/*.page >> "${LOG_FILE}" 2>&1 echo "Installation finished at $(date)" >> "${LOG_FILE}" if [ $VERIFY_FAIL -eq 0 ]; then echo "=========================================" echo "${PLUGIN_NAME} v${VERSION} installed successfully!" echo "=========================================" echo "" echo "Access the plugin:" echo " • Docker > Folders" echo " • Settings > Utilities > Docker Folders" echo "" echo "Installation log: ${LOG_FILE}" else echo "=========================================" echo "Installation completed with warnings!" echo "=========================================" echo "" echo "Some files may be missing. Check the log file:" echo "${LOG_FILE}" echo "" echo "Try reinstalling the plugin or contact support." fi ]]> /dev/null 2>&1 fi # Backup data before removal if [ -f "${CONFIG_DIR}/data.db" ]; then echo "Backing up database..." mkdir -p "${CONFIG_DIR}/backups" BACKUP_FILE="${CONFIG_DIR}/backups/data-backup-$(date +%Y%m%d-%H%M%S).db" cp "${CONFIG_DIR}/data.db" "${BACKUP_FILE}" echo "Database backed up to: ${BACKUP_FILE}" fi # Export configuration as JSON backup if [ -f "${CONFIG_DIR}/data.db" ] && [ -f "${PLUGIN_DIR}/api/folders.php" ]; then echo "Exporting configuration..." EXPORT_FILE="${CONFIG_DIR}/backups/export-$(date +%Y%m%d-%H%M%S).json" curl -s "http://localhost/plugins/${PLUGIN_NAME}/api/folders.php?action=export" > "${EXPORT_FILE}" 2>/dev/null || true if [ -s "${EXPORT_FILE}" ]; then echo "Configuration exported to: ${EXPORT_FILE}" else rm -f "${EXPORT_FILE}" 2>/dev/null fi fi # Find and remove all installed versions echo "Removing installed packages..." REMOVED=0 for pkg in /var/log/packages/${PLUGIN_NAME}-*; do if [ -f "$pkg" ]; then PKG_NAME=$(basename "$pkg") echo " Removing: $PKG_NAME" removepkg "$PKG_NAME" 2>&1 | grep -v "Skipping" || true REMOVED=1 fi done if [ $REMOVED -eq 0 ]; then echo " No packages found to remove." fi # Clean up plugin directory if it still exists if [ -d "${PLUGIN_DIR}" ]; then echo "Cleaning up plugin directory..." rm -rf "${PLUGIN_DIR}" fi # Clean up downloaded packages (keep backups) if [ -d "${CONFIG_DIR}" ]; then echo "Cleaning up old package files..." rm -f ${CONFIG_DIR}/${PLUGIN_NAME}-*.txz 2>/dev/null || true fi echo "=========================================" echo "${PLUGIN_NAME} removed successfully." echo "=========================================" echo "" echo "Your data has been preserved in: ${CONFIG_DIR}" echo "Database backups are in: ${CONFIG_DIR}/backups/" echo "" echo "To completely remove all plugin data, run:" echo " rm -rf ${CONFIG_DIR}" echo "" ]]>