diff --git a/CHANGELOG.md b/CHANGELOG.md index 099c502..5533370 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,43 @@ # Changelog +## Folder Sharing Feature - Changelog 4/9/2025 + +### New Endpoints + +- **createFolderShareLink.php:** + - Generates secure, expiring share tokens for folders (with an optional password and allow-upload flag). + - Stores folder share records separately from file shares in `share_folder_links.json`. + - Builds share links that point to **shareFolder.php**, using a proper BASE_URL or the server’s IP when a default placeholder is detected. + +- **shareFolder.php:** + - Serves shared folders via GET requests by reading tokens from `share_folder_links.json`. + - Validates token expiration and password (if set). + - Displays folder contents with pagination (10 items per page) and shows file sizes in megabytes. + - Provides navigation links (Prev, Next, and numbered pages) for folder listings. + - Includes an upload form (if allowed) that redirects back to the same share page after upload. + +- **downloadSharedFile.php:** + - A dedicated, secure download endpoint for shared files. + - Validates the share token and ensures the requested file is inside the shared folder. + - Serves files using proper MIME types and Content-Disposition headers (inline for images, attachment for others). + +- **uploadToSharedFolder.php:** + - Handles file uploads for public folder shares. + - Enforces file size limits and file type whitelists. + - Generates unique filenames (with a unique prefix) to prevent collisions. + - Updates metadata for the uploaded file (upload date and sets uploader as "Outside Share"). + - Redirects back to **shareFolder.php** after a successful upload so the file listing refreshes. + +### New Front-End Module + +- **folderShareModal.js:** + - Provides a modal interface for users to generate folder share links. + - Includes expiration selection, optional password entry, and an allow-upload checkbox. + - Uses the **createFolderShareLink.php** endpoint to generate share links. + - Displays the generated share link with a “copy to clipboard” button. + +--- + ## Changes 4/8/2025 **May have missed some stuff or could have bugs. Please report any issue you may encounter.** @@ -28,6 +66,9 @@ - Modal Popup for Single File: Replaced the direct download link for single files with a modal-driven flow. When the download button is clicked, the openDownloadModal(fileName, folder) function is called. This stores the file details and shows a modal where the user can confirm (or edit) the file name. - Confirm Download Function: When the user clicks the Download button in the modal, the confirmSingleDownload() function is called. This function constructs a URL for download.php (using GET parameters for folder and file), fetches the file as a blob, and triggers a download using a temporary anchor element. A progress modal is also used here to give feedback during the download process. +- **Zip Extraction** + - Reused Zip Download modal to use same progress Modal Popup with Extracting files.... text. + --- ## Changes 4/7/2025 v1.0.9 diff --git a/README.md b/README.md index 3129d59..ed6c67c 100644 --- a/README.md +++ b/README.md @@ -12,17 +12,19 @@ Upload, organize, and share files through a sleek web interface. **FileRise** is --- -## Features at a Glance +## Features at a Glance or [Full Feature Wiki](https://github.com/error311/FileRise/wiki/Features) - 🚀 **Easy File Uploads:** Upload multiple files and folders via drag & drop or file picker. Supports large files with pause/resumable chunked uploads and shows real-time progress for each file. No more failed transfers – FileRise will pick up where it left off if your connection drops. - 🗂️ **File Management:** Full set of file/folder operations – move or copy files (via intuitive drag-drop or dialogs), rename items, and delete in batches. You can even download selected files as a ZIP archive or extract uploaded ZIP files server-side. Organize content with an interactive folder tree and breadcrumb navigation for quick jumps. +- 🗃️ **Folder Sharing & File Sharing:** Easily share entire folders via secure, expiring public links. Folder shares can be password-protected, and shared folders support file uploads from outside users with a separate, secure upload mechanism. Folder listings are paginated (10 items per page) with navigation controls, and file sizes are displayed in MB for clarity. Share files with others using one-time or expiring public links (with password protection if desired) – convenient for sending files without exposing the whole app. + - 📝 **Built-in Editor & Preview:** View images, videos, audio, and PDFs inline with a preview modal – no need to download just to see them. Edit text/code files right in your browser with a CodeMirror-based editor featuring syntax highlighting and line numbers. Great for config files or notes – tweak and save changes without leaving FileRise. - 🏷️ **Tags & Search:** Categorize your files with color-coded tags (labels) and later find them easily. The global search bar filters by filename or tag, making it simple to locate that “important” document in seconds. Tag management is built-in – create, reuse, or remove tags as needed. -- 🔒 **User Authentication, User Permissions & Sharing:** Secure your portal with username/password login. Supports multiple users – create user accounts (admin UI provided) for family or team members. User permissions such as User “Folder Only” feature assigns each user a dedicated folder within the root directory, named after their username, restricting them from viewing or modifying other directories. User Read Only and Disable Upload are additional permissions. FileRise also integrates with Single Sign-On (OIDC) providers (e.g., OAuth2/OIDC for Google/Authentik/Keycloak) and offers optional TOTP two-factor auth for extra security. Share files with others using one-time or expiring public links (with password protection if desired) – convenient for sending files without exposing the whole app. +- 🔒 **User Authentication & User Permissions:** Secure your portal with username/password login. Supports multiple users – create user accounts (admin UI provided) for family or team members. User permissions such as User “Folder Only” feature assigns each user a dedicated folder within the root directory, named after their username, restricting them from viewing or modifying other directories. User Read Only and Disable Upload are additional permissions. FileRise also integrates with Single Sign-On (OIDC) providers (e.g., OAuth2/OIDC for Google/Authentik/Keycloak) and offers optional TOTP two-factor auth for extra security. - 🎨 **Responsive UI (Dark/Light Mode):** FileRise is mobile-friendly out of the box – manage files from your phone or tablet with a responsive layout. Choose between Dark mode or Light theme, or let it follow your system preference. The interface remembers your preferences (layout, items per page, last visited folder, etc.) for a personalized experience each time. diff --git a/createFolderShareLink.php b/createFolderShareLink.php new file mode 100644 index 0000000..ef3d9c1 --- /dev/null +++ b/createFolderShareLink.php @@ -0,0 +1,84 @@ + "Invalid input."]); + exit; +} + +$folder = isset($input['folder']) ? trim($input['folder']) : ""; +$expirationMinutes = isset($input['expirationMinutes']) ? intval($input['expirationMinutes']) : 60; +$password = isset($input['password']) ? $input['password'] : ""; +$allowUpload = isset($input['allowUpload']) ? intval($input['allowUpload']) : 0; + +// Validate folder name using regex. +// Allow letters, numbers, underscores, hyphens, spaces and slashes. +if ($folder !== 'root' && !preg_match('/^[A-Za-z0-9_\- \/]+$/', $folder)) { + echo json_encode(["error" => "Invalid folder name."]); + exit; +} + +// Generate a secure token. +try { + $token = bin2hex(random_bytes(16)); // 32 hex characters. +} catch (Exception $e) { + echo json_encode(["error" => "Could not generate token."]); + exit; +} + +// Calculate expiration time (Unix timestamp). +$expires = time() + ($expirationMinutes * 60); + +// Hash password if provided. +$hashedPassword = !empty($password) ? password_hash($password, PASSWORD_DEFAULT) : ""; + +// Define the file to store share folder links. +$shareFile = META_DIR . "share_folder_links.json"; +$shareLinks = []; +if (file_exists($shareFile)) { + $data = file_get_contents($shareFile); + $shareLinks = json_decode($data, true); + if (!is_array($shareLinks)) { + $shareLinks = []; + } +} + +// Clean up expired share links. +$currentTime = time(); +foreach ($shareLinks as $key => $link) { + if (isset($link["expires"]) && $link["expires"] < $currentTime) { + unset($shareLinks[$key]); + } +} + +// Add the new share record. +$shareLinks[$token] = [ + "folder" => $folder, + "expires" => $expires, + "password" => $hashedPassword, + "allowUpload" => $allowUpload +]; + +// Save the share links. +if (file_put_contents($shareFile, json_encode($shareLinks, JSON_PRETTY_PRINT))) { +// Determine base URL. +if (defined('BASE_URL') && !empty(BASE_URL) && strpos(BASE_URL, 'yourwebsite') === false) { + $baseUrl = rtrim(BASE_URL, '/'); +} else { + // Prefer HTTP_HOST over SERVER_ADDR. + $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http"; + // Use HTTP_HOST if set; fallback to gethostbyname if needed. + $host = !empty($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : gethostbyname($_SERVER['SERVER_ADDR'] ?? 'localhost'); + $baseUrl = $protocol . "://" . $host; +} + // The share URL points to shareFolder.php. + $link = $baseUrl . "/shareFolder.php?token=" . urlencode($token); + echo json_encode(["token" => $token, "expires" => $expires, "link" => $link]); +} else { + echo json_encode(["error" => "Could not save share link."]); +} +?> \ No newline at end of file diff --git a/css/styles.css b/css/styles.css index eb611b3..870baef 100644 --- a/css/styles.css +++ b/css/styles.css @@ -849,7 +849,8 @@ body:not(.dark-mode) .material-icons.pauseResumeBtn:hover { color: white; } -.rename-btn .material-icons { +.rename-btn .material-icons, +#renameFolderBtn .material-icons { color: black !important; } diff --git a/downloadSharedFile.php b/downloadSharedFile.php new file mode 100644 index 0000000..75b8f18 --- /dev/null +++ b/downloadSharedFile.php @@ -0,0 +1,89 @@ + $record['expires']) { + http_response_code(403); + echo "This share link has expired."; + exit; +} + +// Get the shared folder from the record. +$folder = trim($record['folder'], "/\\ "); +$folderPath = rtrim(UPLOAD_DIR, '/\\') . DIRECTORY_SEPARATOR . $folder; +$realFolderPath = realpath($folderPath); +$uploadDirReal = realpath(UPLOAD_DIR); + +if ($realFolderPath === false || strpos($realFolderPath, $uploadDirReal) !== 0 || !is_dir($realFolderPath)) { + http_response_code(404); + echo "Shared folder not found."; + exit; +} + +// Sanitize the filename to prevent directory traversal. +if (strpos($file, "/") !== false || strpos($file, "\\") !== false) { + http_response_code(400); + echo "Invalid file name."; + exit; +} +$file = basename($file); + +// Build the full file path and verify it is inside the shared folder. +$filePath = $realFolderPath . DIRECTORY_SEPARATOR . $file; +$realFilePath = realpath($filePath); +if ($realFilePath === false || strpos($realFilePath, $realFolderPath) !== 0 || !is_file($realFilePath)) { + http_response_code(404); + echo "File not found."; + exit; +} + +// Determine MIME type. +$mimeType = mime_content_type($realFilePath); +header("Content-Type: " . $mimeType); + +// Set Content-Disposition header. +// Inline if the file is an image; attachment for others. +$ext = strtolower(pathinfo($realFilePath, PATHINFO_EXTENSION)); +if (in_array($ext, ['jpg','jpeg','png','gif','bmp','webp','svg','ico'])) { + header('Content-Disposition: inline; filename="' . basename($realFilePath) . '"'); +} else { + header('Content-Disposition: attachment; filename="' . basename($realFilePath) . '"'); +} + +// Disable caching. +header("Cache-Control: no-store, no-cache, must-revalidate"); +header("Pragma: no-cache"); + +// Read and output the file. +readfile($realFilePath); +exit; +?> \ No newline at end of file diff --git a/index.html b/index.html index 96462e8..b486374 100644 --- a/index.html +++ b/index.html @@ -23,12 +23,24 @@ - - - - - - + + + + + + @@ -106,44 +118,46 @@
-
- - - @@ -181,7 +195,8 @@
- Use Basic HTTP Login + Use Basic HTTP + Login
@@ -200,14 +215,16 @@
- Drop files/folders here or click 'Choose Files' + Drop files/folders here or click 'Choose + Files'
- +
@@ -228,41 +245,56 @@
- + - + + @@ -272,8 +304,10 @@
@@ -287,22 +321,26 @@