Fix folder share gallery view link

This commit is contained in:
Ryan
2025-04-17 02:38:32 -04:00
committed by GitHub
parent 766458f707
commit d839b3ac1c

View File

@@ -4,7 +4,8 @@
require_once __DIR__ . '/../../config/config.php'; require_once __DIR__ . '/../../config/config.php';
require_once PROJECT_ROOT . '/src/models/FolderModel.php'; require_once PROJECT_ROOT . '/src/models/FolderModel.php';
class FolderController { class FolderController
{
/** /**
* @OA\Post( * @OA\Post(
* path="/api/folder/createFolder.php", * path="/api/folder/createFolder.php",
@@ -45,22 +46,23 @@ class FolderController {
* *
* @return void Outputs a JSON response. * @return void Outputs a JSON response.
*/ */
public function createFolder(): void { public function createFolder(): void
{
header('Content-Type: application/json'); header('Content-Type: application/json');
// Ensure user is authenticated. // Ensure user is authenticated.
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) { if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
http_response_code(401); http_response_code(401);
echo json_encode(["error" => "Unauthorized"]); echo json_encode(["error" => "Unauthorized"]);
exit; exit;
} }
// Ensure the request method is POST. // Ensure the request method is POST.
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(['error' => 'Invalid request method.']); echo json_encode(['error' => 'Invalid request method.']);
exit; exit;
} }
// CSRF check. // CSRF check.
$headersArr = array_change_key_case(getallheaders(), CASE_LOWER); $headersArr = array_change_key_case(getallheaders(), CASE_LOWER);
$receivedToken = $headersArr['x-csrf-token'] ?? ''; $receivedToken = $headersArr['x-csrf-token'] ?? '';
@@ -69,7 +71,7 @@ class FolderController {
echo json_encode(['error' => 'Invalid CSRF token.']); echo json_encode(['error' => 'Invalid CSRF token.']);
exit; exit;
} }
// Check permissions. // Check permissions.
$username = $_SESSION['username'] ?? ''; $username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username); $userPermissions = loadUserPermissions($username);
@@ -77,36 +79,36 @@ class FolderController {
echo json_encode(["error" => "Read-only users are not allowed to create folders."]); echo json_encode(["error" => "Read-only users are not allowed to create folders."]);
exit; exit;
} }
// Get and decode JSON input. // Get and decode JSON input.
$input = json_decode(file_get_contents('php://input'), true); $input = json_decode(file_get_contents('php://input'), true);
if (!isset($input['folderName'])) { if (!isset($input['folderName'])) {
echo json_encode(['error' => 'Folder name not provided.']); echo json_encode(['error' => 'Folder name not provided.']);
exit; exit;
} }
$folderName = trim($input['folderName']); $folderName = trim($input['folderName']);
$parent = isset($input['parent']) ? trim($input['parent']) : ""; $parent = isset($input['parent']) ? trim($input['parent']) : "";
// Basic sanitation for folderName. // Basic sanitation for folderName.
if (!preg_match(REGEX_FOLDER_NAME, $folderName)) { if (!preg_match(REGEX_FOLDER_NAME, $folderName)) {
echo json_encode(['error' => 'Invalid folder name.']); echo json_encode(['error' => 'Invalid folder name.']);
exit; exit;
} }
// Optionally sanitize the parent. // Optionally sanitize the parent.
if ($parent && !preg_match(REGEX_FOLDER_NAME, $parent)) { if ($parent && !preg_match(REGEX_FOLDER_NAME, $parent)) {
echo json_encode(['error' => 'Invalid parent folder name.']); echo json_encode(['error' => 'Invalid parent folder name.']);
exit; exit;
} }
// Delegate to FolderModel. // Delegate to FolderModel.
$result = FolderModel::createFolder($folderName, $parent); $result = FolderModel::createFolder($folderName, $parent);
echo json_encode($result); echo json_encode($result);
exit; exit;
} }
/** /**
* @OA\Post( * @OA\Post(
* path="/api/folder/deleteFolder.php", * path="/api/folder/deleteFolder.php",
* summary="Delete an empty folder", * summary="Delete an empty folder",
@@ -145,22 +147,23 @@ class FolderController {
* *
* @return void Outputs a JSON response. * @return void Outputs a JSON response.
*/ */
public function deleteFolder(): void { public function deleteFolder(): void
{
header('Content-Type: application/json'); header('Content-Type: application/json');
// Ensure user is authenticated. // Ensure user is authenticated.
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) { if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
http_response_code(401); http_response_code(401);
echo json_encode(["error" => "Unauthorized"]); echo json_encode(["error" => "Unauthorized"]);
exit; exit;
} }
// Ensure the request is a POST. // Ensure the request is a POST.
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(["error" => "Invalid request method."]); echo json_encode(["error" => "Invalid request method."]);
exit; exit;
} }
// CSRF Protection. // CSRF Protection.
$headersArr = array_change_key_case(getallheaders(), CASE_LOWER); $headersArr = array_change_key_case(getallheaders(), CASE_LOWER);
$receivedToken = isset($headersArr['x-csrf-token']) ? trim($headersArr['x-csrf-token']) : ''; $receivedToken = isset($headersArr['x-csrf-token']) ? trim($headersArr['x-csrf-token']) : '';
@@ -169,7 +172,7 @@ class FolderController {
echo json_encode(["error" => "Invalid CSRF token."]); echo json_encode(["error" => "Invalid CSRF token."]);
exit; exit;
} }
// Check user permissions. // Check user permissions.
$username = $_SESSION['username'] ?? ''; $username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username); $userPermissions = loadUserPermissions($username);
@@ -177,28 +180,28 @@ class FolderController {
echo json_encode(["error" => "Read-only users are not allowed to delete folders."]); echo json_encode(["error" => "Read-only users are not allowed to delete folders."]);
exit; exit;
} }
// Get and decode JSON input. // Get and decode JSON input.
$input = json_decode(file_get_contents('php://input'), true); $input = json_decode(file_get_contents('php://input'), true);
if (!isset($input['folder'])) { if (!isset($input['folder'])) {
echo json_encode(["error" => "Folder name not provided."]); echo json_encode(["error" => "Folder name not provided."]);
exit; exit;
} }
$folder = trim($input['folder']); $folder = trim($input['folder']);
// Prevent deletion of the root folder. // Prevent deletion of the root folder.
if (strtolower($folder) === 'root') { if (strtolower($folder) === 'root') {
echo json_encode(["error" => "Cannot delete root folder."]); echo json_encode(["error" => "Cannot delete root folder."]);
exit; exit;
} }
// Delegate to the model. // Delegate to the model.
$result = FolderModel::deleteFolder($folder); $result = FolderModel::deleteFolder($folder);
echo json_encode($result); echo json_encode($result);
exit; exit;
} }
/** /**
* @OA\Post( * @OA\Post(
* path="/api/folder/renameFolder.php", * path="/api/folder/renameFolder.php",
* summary="Rename a folder", * summary="Rename a folder",
@@ -238,22 +241,23 @@ class FolderController {
* *
* @return void Outputs a JSON response. * @return void Outputs a JSON response.
*/ */
public function renameFolder(): void { public function renameFolder(): void
{
header('Content-Type: application/json'); header('Content-Type: application/json');
// Ensure user is authenticated. // Ensure user is authenticated.
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) { if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
http_response_code(401); http_response_code(401);
echo json_encode(["error" => "Unauthorized"]); echo json_encode(["error" => "Unauthorized"]);
exit; exit;
} }
// Ensure the request method is POST. // Ensure the request method is POST.
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(['error' => 'Invalid request method.']); echo json_encode(['error' => 'Invalid request method.']);
exit; exit;
} }
// CSRF Protection. // CSRF Protection.
$headersArr = array_change_key_case(getallheaders(), CASE_LOWER); $headersArr = array_change_key_case(getallheaders(), CASE_LOWER);
$receivedToken = isset($headersArr['x-csrf-token']) ? trim($headersArr['x-csrf-token']) : ''; $receivedToken = isset($headersArr['x-csrf-token']) ? trim($headersArr['x-csrf-token']) : '';
@@ -262,7 +266,7 @@ class FolderController {
echo json_encode(["error" => "Invalid CSRF token."]); echo json_encode(["error" => "Invalid CSRF token."]);
exit; exit;
} }
// Check that the user is not read-only. // Check that the user is not read-only.
$username = $_SESSION['username'] ?? ''; $username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username); $userPermissions = loadUserPermissions($username);
@@ -270,23 +274,23 @@ class FolderController {
echo json_encode(["error" => "Read-only users are not allowed to rename folders."]); echo json_encode(["error" => "Read-only users are not allowed to rename folders."]);
exit; exit;
} }
// Get JSON input. // Get JSON input.
$input = json_decode(file_get_contents('php://input'), true); $input = json_decode(file_get_contents('php://input'), true);
if (!isset($input['oldFolder']) || !isset($input['newFolder'])) { if (!isset($input['oldFolder']) || !isset($input['newFolder'])) {
echo json_encode(['error' => 'Required folder names not provided.']); echo json_encode(['error' => 'Required folder names not provided.']);
exit; exit;
} }
$oldFolder = trim($input['oldFolder']); $oldFolder = trim($input['oldFolder']);
$newFolder = trim($input['newFolder']); $newFolder = trim($input['newFolder']);
// Validate folder names. // Validate folder names.
if (!preg_match(REGEX_FOLDER_NAME, $oldFolder) || !preg_match(REGEX_FOLDER_NAME, $newFolder)) { if (!preg_match(REGEX_FOLDER_NAME, $oldFolder) || !preg_match(REGEX_FOLDER_NAME, $newFolder)) {
echo json_encode(['error' => 'Invalid folder name(s).']); echo json_encode(['error' => 'Invalid folder name(s).']);
exit; exit;
} }
// Delegate to the model. // Delegate to the model.
$result = FolderModel::renameFolder($oldFolder, $newFolder); $result = FolderModel::renameFolder($oldFolder, $newFolder);
echo json_encode($result); echo json_encode($result);
@@ -329,23 +333,24 @@ class FolderController {
* *
* @return void Outputs JSON response. * @return void Outputs JSON response.
*/ */
public function getFolderList(): void { public function getFolderList(): void
{
header('Content-Type: application/json'); header('Content-Type: application/json');
// Ensure user is authenticated. // Ensure user is authenticated.
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) { if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
http_response_code(401); http_response_code(401);
echo json_encode(["error" => "Unauthorized"]); echo json_encode(["error" => "Unauthorized"]);
exit; exit;
} }
// Optionally, you might add further input validation if necessary. // Optionally, you might add further input validation if necessary.
$folderList = FolderModel::getFolderList(); $folderList = FolderModel::getFolderList();
echo json_encode($folderList); echo json_encode($folderList);
exit; exit;
} }
/** /**
* @OA\Get( * @OA\Get(
* path="/api/folder/shareFolder.php", * path="/api/folder/shareFolder.php",
* summary="Display a shared folder", * summary="Display a shared folder",
@@ -396,7 +401,8 @@ class FolderController {
* *
* @return void Outputs HTML content. * @return void Outputs HTML content.
*/ */
public function shareFolder(): void { public function shareFolder(): void
{
// Retrieve GET parameters. // Retrieve GET parameters.
$token = filter_input(INPUT_GET, 'token', FILTER_SANITIZE_STRING); $token = filter_input(INPUT_GET, 'token', FILTER_SANITIZE_STRING);
$providedPass = filter_input(INPUT_GET, 'pass', FILTER_SANITIZE_STRING); $providedPass = filter_input(INPUT_GET, 'pass', FILTER_SANITIZE_STRING);
@@ -404,52 +410,83 @@ class FolderController {
if ($page === false || $page < 1) { if ($page === false || $page < 1) {
$page = 1; $page = 1;
} }
if (empty($token)) { if (empty($token)) {
http_response_code(400); http_response_code(400);
header('Content-Type: application/json'); header('Content-Type: application/json');
echo json_encode(["error" => "Missing token."]); echo json_encode(["error" => "Missing token."]);
exit; exit;
} }
// Delegate to the model. // Delegate to the model.
$data = FolderModel::getSharedFolderData($token, $providedPass, $page); $data = FolderModel::getSharedFolderData($token, $providedPass, $page);
// If a password is needed, output an HTML form. // If a password is needed, output an HTML form.
if (isset($data['needs_password']) && $data['needs_password'] === true) { if (isset($data['needs_password']) && $data['needs_password'] === true) {
header("Content-Type: text/html; charset=utf-8"); header("Content-Type: text/html; charset=utf-8");
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Enter Password</title> <title>Enter Password</title>
<style> <style>
body { font-family: Arial, sans-serif; padding: 20px; background-color: #f7f7f7; } body {
.container { max-width: 400px; margin: 80px auto; background: #fff; padding: 20px; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); } font-family: Arial, sans-serif;
input[type="password"], button { width: 100%; padding: 10px; margin: 10px 0; font-size: 1rem; } padding: 20px;
button { background-color: #007BFF; border: none; color: #fff; cursor: pointer; } background-color: #f7f7f7;
button:hover { background-color: #0056b3; } }
.container {
max-width: 400px;
margin: 80px auto;
background: #fff;
padding: 20px;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
input[type="password"],
button {
width: 100%;
padding: 10px;
margin: 10px 0;
font-size: 1rem;
}
button {
background-color: #007BFF;
border: none;
color: #fff;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
</style> </style>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<h2>Folder Protected</h2> <h2>Folder Protected</h2>
<p>This folder is protected by a password. Please enter the password to view its contents.</p> <p>This folder is protected by a password. Please enter the password to view its contents.</p>
<form method="get" action="/api/folder/shareFolder.php"> <form method="get" action="/api/folder/shareFolder.php">
<input type="hidden" name="token" value="<?php echo htmlspecialchars($token, ENT_QUOTES, 'UTF-8'); ?>"> <input type="hidden" name="token" value="<?php echo htmlspecialchars($token, ENT_QUOTES, 'UTF-8'); ?>">
<label for="pass">Password:</label> <label for="pass">Password:</label>
<input type="password" name="pass" id="pass" required> <input type="password" name="pass" id="pass" required>
<button type="submit">Submit</button> <button type="submit">Submit</button>
</form> </form>
</div> </div>
</body> </body>
</html> </html>
<?php <?php
exit; exit;
} }
// If the model returned an error, output JSON error. // If the model returned an error, output JSON error.
if (isset($data['error'])) { if (isset($data['error'])) {
http_response_code(403); http_response_code(403);
@@ -457,14 +494,15 @@ class FolderController {
echo json_encode(["error" => $data['error']]); echo json_encode(["error" => $data['error']]);
exit; exit;
} }
// Extract data for the HTML view. // Extract data for the HTML view.
$folderName = $data['folder']; $folderName = $data['folder'];
$files = $data['files']; $files = $data['files'];
$currentPage = $data['currentPage']; $currentPage = $data['currentPage'];
$totalPages = $data['totalPages']; $totalPages = $data['totalPages'];
function formatBytes($bytes) { function formatBytes($bytes)
{
if ($bytes < 1024) { if ($bytes < 1024) {
return $bytes . " B"; return $bytes . " B";
} elseif ($bytes < 1024 * 1024) { } elseif ($bytes < 1024 * 1024) {
@@ -475,187 +513,297 @@ class FolderController {
return round($bytes / (1024 * 1024 * 1024), 2) . " GB"; return round($bytes / (1024 * 1024 * 1024), 2) . " GB";
} }
} }
// Build the HTML view. // Build the HTML view.
header("Content-Type: text/html; charset=utf-8"); header("Content-Type: text/html; charset=utf-8");
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Shared Folder: <?php echo htmlspecialchars($folderName, ENT_QUOTES, 'UTF-8'); ?></title> <title>Shared Folder: <?php echo htmlspecialchars($folderName, ENT_QUOTES, 'UTF-8'); ?></title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<style> <style>
body { background: #f2f2f2; font-family: Arial, sans-serif; padding: 20px; color: #333; } body {
.header { text-align: center; margin-bottom: 30px; } background: #f2f2f2;
.container { max-width: 800px; margin: 0 auto; background: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 2px 12px rgba(0,0,0,0.1); } font-family: Arial, sans-serif;
table { width: 100%; border-collapse: collapse; margin-top: 20px; } padding: 20px;
th, td { padding: 12px; border-bottom: 1px solid #ddd; text-align: left; } color: #333;
th { background: #007BFF; color: #fff; } }
.pagination { text-align: center; margin-top: 20px; }
.pagination a, .pagination span { margin: 0 5px; padding: 8px 12px; background: #007BFF; color: #fff; border-radius: 4px; text-decoration: none; } .header {
.pagination span.current { background: #0056b3; } text-align: center;
margin-bottom: 30px;
}
.container {
max-width: 800px;
margin: 0 auto;
background: #fff;
border-radius: 4px;
padding: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th,
td {
padding: 12px;
border-bottom: 1px solid #ddd;
text-align: left;
}
th {
background: #007BFF;
color: #fff;
}
.pagination {
text-align: center;
margin-top: 20px;
}
.pagination a,
.pagination span {
margin: 0 5px;
padding: 8px 12px;
background: #007BFF;
color: #fff;
border-radius: 4px;
text-decoration: none;
}
.pagination span.current {
background: #0056b3;
}
/* Gallery view styles if needed */ /* Gallery view styles if needed */
.shared-gallery-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; padding: 10px 0; } .shared-gallery-container {
.shared-gallery-card { border: 1px solid #ccc; padding: 5px; text-align: center; } display: grid;
.shared-gallery-card img { max-width: 100%; display: block; margin: 0 auto; } grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 10px;
padding: 10px 0;
}
.shared-gallery-card {
border: 1px solid #ccc;
padding: 5px;
text-align: center;
}
.shared-gallery-card img {
max-width: 100%;
display: block;
margin: 0 auto;
}
/* Upload container */ /* Upload container */
.upload-container { margin-top: 30px; text-align: center; } .upload-container {
.upload-container h3 { font-size: 1.4rem; margin-bottom: 10px; } margin-top: 30px;
.upload-container form { display: inline-block; margin-top: 10px; } text-align: center;
.upload-container button { background-color: #28a745; border: none; color: #fff; padding: 10px 20px; font-size: 1rem; border-radius: 4px; cursor: pointer; } }
.upload-container button:hover { background-color: #218838; }
.footer { text-align: center; margin-top: 40px; font-size: 0.9rem; color: #777; } .upload-container h3 {
font-size: 1.4rem;
margin-bottom: 10px;
}
.upload-container form {
display: inline-block;
margin-top: 10px;
}
.upload-container button {
background-color: #28a745;
border: none;
color: #fff;
padding: 10px 20px;
font-size: 1rem;
border-radius: 4px;
cursor: pointer;
}
.upload-container button:hover {
background-color: #218838;
}
.footer {
text-align: center;
margin-top: 40px;
font-size: 0.9rem;
color: #777;
}
</style> </style>
</head> </head>
<body> <body>
<div class="header"> <div class="header">
<h1>Shared Folder: <?php echo htmlspecialchars($folderName, ENT_QUOTES, 'UTF-8'); ?></h1> <h1>Shared Folder: <?php echo htmlspecialchars($folderName, ENT_QUOTES, 'UTF-8'); ?></h1>
</div>
<div class="container">
<!-- Toggle Button -->
<button id="toggleBtn" class="toggle-btn" onclick="toggleViewMode()">Switch to Gallery View</button>
<!-- List View Container -->
<div id="listViewContainer">
<?php if (empty($files)): ?>
<p style="text-align:center;">This folder is empty.</p>
<?php else: ?>
<table>
<thead>
<tr>
<th>Filename</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<?php
// For each file, build a download link using your downloadSharedFile endpoint.
foreach ($files as $file):
$filePath = $data['realFolderPath'] . DIRECTORY_SEPARATOR . $file;
$fileSize = file_exists($filePath) ? formatBytes(filesize($filePath)) : "Unknown";
$downloadLink = "/api/folder/downloadSharedFile.php?token=" . urlencode($token) . "&file=" . urlencode($file);
?>
<tr>
<td>
<a href="<?php echo htmlspecialchars($downloadLink, ENT_QUOTES, 'UTF-8'); ?>">
<?php echo htmlspecialchars($file, ENT_QUOTES, 'UTF-8'); ?>
<span class="download-icon">&#x21E9;</span>
</a>
</td>
<td><?php echo $fileSize; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div> </div>
<div class="container">
<!-- Gallery View Container (hidden by default) --> <!-- Toggle Button -->
<div id="galleryViewContainer" style="display:none;"></div> <button id="toggleBtn" class="toggle-btn" onclick="toggleViewMode()">Switch to Gallery View</button>
<!-- Pagination Controls --> <!-- List View Container -->
<div class="pagination"> <div id="listViewContainer">
<?php if ($currentPage > 1): ?> <?php if (empty($files)): ?>
<a href="/api/folder/shareFolder.php?token=<?php echo urlencode($token); ?>&page=<?php echo $currentPage - 1; ?><?php echo !empty($providedPass) ? "&pass=" . urlencode($providedPass) : ""; ?>">Prev</a> <p style="text-align:center;">This folder is empty.</p>
<?php else: ?>
<span>Prev</span>
<?php endif; ?>
<?php
$startPage = max(1, $currentPage - 2);
$endPage = min($totalPages, $currentPage + 2);
for ($i = $startPage; $i <= $endPage; $i++): ?>
<?php if ($i == $currentPage): ?>
<span class="current"><?php echo $i; ?></span>
<?php else: ?> <?php else: ?>
<a href="/api/folder/shareFolder.php?token=<?php echo urlencode($token); ?>&page=<?php echo $i; ?><?php echo !empty($providedPass) ? "&pass=" . urlencode($providedPass) : ""; ?>"><?php echo $i; ?></a> <table>
<thead>
<tr>
<th>Filename</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<?php
// For each file, build a download link using your downloadSharedFile endpoint.
foreach ($files as $file):
$filePath = $data['realFolderPath'] . DIRECTORY_SEPARATOR . $file;
$fileSize = file_exists($filePath) ? formatBytes(filesize($filePath)) : "Unknown";
$downloadLink = "/api/folder/downloadSharedFile.php?token=" . urlencode($token) . "&file=" . urlencode($file);
?>
<tr>
<td>
<a href="<?php echo htmlspecialchars($downloadLink, ENT_QUOTES, 'UTF-8'); ?>">
<?php echo htmlspecialchars($file, ENT_QUOTES, 'UTF-8'); ?>
<span class="download-icon">&#x21E9;</span>
</a>
</td>
<td><?php echo $fileSize; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?> <?php endif; ?>
<?php endfor; ?> </div>
<?php if ($currentPage < $totalPages): ?> <!-- Gallery View Container (hidden by default) -->
<a href="/api/folder/shareFolder.php?token=<?php echo urlencode($token); ?>&page=<?php echo $currentPage + 1; ?><?php echo !empty($providedPass) ? "&pass=" . urlencode($providedPass) : ""; ?>">Next</a> <div id="galleryViewContainer" style="display:none;"></div>
<?php else: ?>
<span>Next</span> <!-- Pagination Controls -->
<div class="pagination">
<?php if ($currentPage > 1): ?>
<a href="/api/folder/shareFolder.php?token=<?php echo urlencode($token); ?>&page=<?php echo $currentPage - 1; ?><?php echo !empty($providedPass) ? "&pass=" . urlencode($providedPass) : ""; ?>">Prev</a>
<?php else: ?>
<span>Prev</span>
<?php endif; ?>
<?php
$startPage = max(1, $currentPage - 2);
$endPage = min($totalPages, $currentPage + 2);
for ($i = $startPage; $i <= $endPage; $i++): ?>
<?php if ($i == $currentPage): ?>
<span class="current"><?php echo $i; ?></span>
<?php else: ?>
<a href="/api/folder/shareFolder.php?token=<?php echo urlencode($token); ?>&page=<?php echo $i; ?><?php echo !empty($providedPass) ? "&pass=" . urlencode($providedPass) : ""; ?>"><?php echo $i; ?></a>
<?php endif; ?>
<?php endfor; ?>
<?php if ($currentPage < $totalPages): ?>
<a href="/api/folder/shareFolder.php?token=<?php echo urlencode($token); ?>&page=<?php echo $currentPage + 1; ?><?php echo !empty($providedPass) ? "&pass=" . urlencode($providedPass) : ""; ?>">Next</a>
<?php else: ?>
<span>Next</span>
<?php endif; ?>
</div>
<!-- Upload Container (if uploads are allowed by the share record) -->
<?php if (isset($data['record']['allowUpload']) && $data['record']['allowUpload'] == 1): ?>
<div class="upload-container">
<h3>Upload File (50mb max size)</h3>
<form action="/api/folder/uploadToSharedFolder.php" method="post" enctype="multipart/form-data">
<!-- Pass the share token so the upload endpoint can verify -->
<input type="hidden" name="token" value="<?php echo htmlspecialchars($token, ENT_QUOTES, 'UTF-8'); ?>">
<input type="file" name="fileToUpload" required>
<br><br>
<button type="submit">Upload</button>
</form>
</div>
<?php endif; ?> <?php endif; ?>
</div> </div>
<div class="footer">
<!-- Upload Container (if uploads are allowed by the share record) --> &copy; <?php echo date("Y"); ?> FileRise. All rights reserved.
<?php if (isset($data['record']['allowUpload']) && $data['record']['allowUpload'] == 1): ?> </div>
<div class="upload-container">
<h3>Upload File (50mb max size)</h3> <script>
<form action="/api/folder/uploadToSharedFolder.php" method="post" enctype="multipart/form-data"> // (Optional) JavaScript for toggling view modes (list/gallery).
<!-- Pass the share token so the upload endpoint can verify --> var viewMode = 'list';
<input type="hidden" name="token" value="<?php echo htmlspecialchars($token, ENT_QUOTES, 'UTF-8'); ?>"> window.imageCache = window.imageCache || {};
<input type="file" name="fileToUpload" required> var filesData = <?php echo json_encode($files); ?>;
<br><br>
<button type="submit">Upload</button> // Use the sharedfolder relative path (from your model), not realFolderPath
</form> // $data['folder'] should be something like "eafwef/testfolder2/test/new folder two"
</div> var rawRelPath = "<?php echo addslashes($data['folder']); ?>";
<?php endif; ?> // Split into segments, encode each segment, then re-join
</div> var folderSegments = rawRelPath
<div class="footer"> .split('/')
&copy; <?php echo date("Y"); ?> FileRise. All rights reserved. .map(encodeURIComponent)
</div> .join('/');
<script> function renderGalleryView() {
// (Optional) JavaScript for toggling view modes (list/gallery). var galleryContainer = document.getElementById("galleryViewContainer");
var viewMode = 'list'; var html = '<div class="shared-gallery-container">';
window.imageCache = window.imageCache || {}; filesData.forEach(function(file) {
// Encode the filename too
var filesData = <?php echo json_encode($files); ?>; var fileName = encodeURIComponent(file);
var fileUrl = window.location.origin +
function cacheImage(imgElem, key) { '/uploads/' +
window.imageCache[key] = imgElem.src; folderSegments +
} '/' +
fileName +
function renderGalleryView() { '?t=' +
var galleryContainer = document.getElementById("galleryViewContainer"); Date.now();
var html = '<div class="shared-gallery-container">';
filesData.forEach(function(file) { var ext = file.split('.').pop().toLowerCase();
var fileUrl = window.location.origin var thumbnail;
+ "/uploads/<?php echo rawurlencode($folderName); ?>/" if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico'].indexOf(ext) >= 0) {
+ encodeURIComponent(file); thumbnail = '<img src="' + fileUrl + '" alt="' + file + '">';
var ext = file.split('.').pop().toLowerCase(); } else {
var thumbnail = ""; thumbnail = '<span class="material-icons">insert_drive_file</span>';
if (['jpg','jpeg','png','gif','bmp','webp','svg','ico'].indexOf(ext) >= 0) { }
var cacheKey = fileUrl;
if (window.imageCache[cacheKey]) { html +=
thumbnail = '<img src="'+window.imageCache[cacheKey]+'" alt="'+file+'">'; '<div class="shared-gallery-card">' +
} else { '<div class="gallery-preview" ' +
var imageUrl = fileUrl + '?t=' + new Date().getTime(); 'onclick="window.location.href=\'' + fileUrl + '\'" ' +
thumbnail = '<img src="'+imageUrl+'" onload="cacheImage(this, \''+cacheKey+'\')" alt="'+file+'">'; 'style="cursor:pointer;">' +
} thumbnail +
} else { '</div>' +
thumbnail = '<span class="material-icons">insert_drive_file</span>'; '<div class="gallery-info">' +
'<span class="gallery-file-name">' + file + '</span>' +
'</div>' +
'</div>';
});
html += '</div>';
galleryContainer.innerHTML = html;
} }
html += '<div class="shared-gallery-card">';
html += '<div class="gallery-preview" onclick="window.location.href=\''+fileUrl+'\'" style="cursor:pointer;">'+ thumbnail +'</div>'; function toggleViewMode() {
html += '<div class="gallery-info"><span class="gallery-file-name">'+file+'</span></div>'; if (viewMode === 'list') {
html += '</div>'; viewMode = 'gallery';
}); document.getElementById("listViewContainer").style.display = "none";
html += '</div>'; renderGalleryView();
galleryContainer.innerHTML = html; document.getElementById("galleryViewContainer").style.display = "block";
} document.getElementById("toggleBtn").textContent = "Switch to List View";
} else {
function toggleViewMode() { viewMode = 'list';
if (viewMode === 'list') { document.getElementById("galleryViewContainer").style.display = "none";
viewMode = 'gallery'; document.getElementById("listViewContainer").style.display = "block";
document.getElementById("listViewContainer").style.display = "none"; document.getElementById("toggleBtn").textContent = "Switch to Gallery View";
renderGalleryView(); }
document.getElementById("galleryViewContainer").style.display = "block"; }
document.getElementById("toggleBtn").textContent = "Switch to List View"; </script>
} else {
viewMode = 'list';
document.getElementById("galleryViewContainer").style.display = "none";
document.getElementById("listViewContainer").style.display = "block";
document.getElementById("toggleBtn").textContent = "Switch to Gallery View";
}
}
</script>
</body> </body>
</html> </html>
<?php <?php
exit; exit;
} }
@@ -703,16 +851,17 @@ class FolderController {
* *
* @return void Outputs a JSON response. * @return void Outputs a JSON response.
*/ */
public function createShareFolderLink(): void { public function createShareFolderLink(): void
{
header('Content-Type: application/json'); header('Content-Type: application/json');
// Ensure user is authenticated. // Ensure user is authenticated.
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) { if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
http_response_code(401); http_response_code(401);
echo json_encode(["error" => "Unauthorized"]); echo json_encode(["error" => "Unauthorized"]);
exit; exit;
} }
// Check that the user is not read-only. // Check that the user is not read-only.
$username = $_SESSION['username'] ?? ''; $username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username); $userPermissions = loadUserPermissions($username);
@@ -721,7 +870,7 @@ class FolderController {
echo json_encode(["error" => "Read-only users are not allowed to create share folders."]); echo json_encode(["error" => "Read-only users are not allowed to create share folders."]);
exit; exit;
} }
// Retrieve and decode POST input. // Retrieve and decode POST input.
$input = json_decode(file_get_contents("php://input"), true); $input = json_decode(file_get_contents("php://input"), true);
if (!$input || !isset($input['folder'])) { if (!$input || !isset($input['folder'])) {
@@ -729,12 +878,12 @@ class FolderController {
echo json_encode(["error" => "Invalid input."]); echo json_encode(["error" => "Invalid input."]);
exit; exit;
} }
$folder = trim($input['folder']); $folder = trim($input['folder']);
$expirationMinutes = isset($input['expirationMinutes']) ? intval($input['expirationMinutes']) : 60; $expirationMinutes = isset($input['expirationMinutes']) ? intval($input['expirationMinutes']) : 60;
$password = isset($input['password']) ? $input['password'] : ""; $password = isset($input['password']) ? $input['password'] : "";
$allowUpload = isset($input['allowUpload']) ? intval($input['allowUpload']) : 0; $allowUpload = isset($input['allowUpload']) ? intval($input['allowUpload']) : 0;
// Delegate to the model. // Delegate to the model.
$result = FolderModel::createShareFolderLink($folder, $expirationMinutes, $password, $allowUpload); $result = FolderModel::createShareFolderLink($folder, $expirationMinutes, $password, $allowUpload);
echo json_encode($result); echo json_encode($result);
@@ -785,18 +934,19 @@ class FolderController {
* *
* @return void Outputs the file with proper headers. * @return void Outputs the file with proper headers.
*/ */
public function downloadSharedFile(): void { public function downloadSharedFile(): void
{
// Retrieve and sanitize GET parameters. // Retrieve and sanitize GET parameters.
$token = filter_input(INPUT_GET, 'token', FILTER_SANITIZE_STRING); $token = filter_input(INPUT_GET, 'token', FILTER_SANITIZE_STRING);
$file = filter_input(INPUT_GET, 'file', FILTER_SANITIZE_STRING); $file = filter_input(INPUT_GET, 'file', FILTER_SANITIZE_STRING);
if (empty($token) || empty($file)) { if (empty($token) || empty($file)) {
http_response_code(400); http_response_code(400);
header('Content-Type: application/json'); header('Content-Type: application/json');
echo json_encode(["error" => "Missing token or file parameter."]); echo json_encode(["error" => "Missing token or file parameter."]);
exit; exit;
} }
// Delegate to the model. // Delegate to the model.
$result = FolderModel::getSharedFileInfo($token, $file); $result = FolderModel::getSharedFileInfo($token, $file);
if (isset($result['error'])) { if (isset($result['error'])) {
@@ -805,14 +955,14 @@ class FolderController {
echo json_encode(["error" => $result['error']]); echo json_encode(["error" => $result['error']]);
exit; exit;
} }
$realFilePath = $result['realFilePath']; $realFilePath = $result['realFilePath'];
$mimeType = $result['mimeType']; $mimeType = $result['mimeType'];
// Serve the file. // Serve the file.
header("Content-Type: " . $mimeType); header("Content-Type: " . $mimeType);
$ext = strtolower(pathinfo($realFilePath, PATHINFO_EXTENSION)); $ext = strtolower(pathinfo($realFilePath, PATHINFO_EXTENSION));
if (in_array($ext, ['jpg','jpeg','png','gif','bmp','webp','svg','ico'])) { if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico'])) {
header('Content-Disposition: inline; filename="' . basename($realFilePath) . '"'); header('Content-Disposition: inline; filename="' . basename($realFilePath) . '"');
} else { } else {
header('Content-Disposition: attachment; filename="' . basename($realFilePath) . '"'); header('Content-Disposition: attachment; filename="' . basename($realFilePath) . '"');
@@ -863,7 +1013,8 @@ class FolderController {
* *
* @return void Redirects upon successful upload or outputs JSON errors. * @return void Redirects upon successful upload or outputs JSON errors.
*/ */
public function uploadToSharedFolder(): void { public function uploadToSharedFolder(): void
{
// Ensure request is POST. // Ensure request is POST.
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405); http_response_code(405);
@@ -871,7 +1022,7 @@ class FolderController {
echo json_encode(["error" => "Method not allowed."]); echo json_encode(["error" => "Method not allowed."]);
exit; exit;
} }
// Ensure the share token is provided. // Ensure the share token is provided.
if (empty($_POST['token'])) { if (empty($_POST['token'])) {
http_response_code(400); http_response_code(400);
@@ -880,7 +1031,7 @@ class FolderController {
exit; exit;
} }
$token = trim($_POST['token']); $token = trim($_POST['token']);
// Delegate the upload to the model. // Delegate the upload to the model.
if (!isset($_FILES['fileToUpload'])) { if (!isset($_FILES['fileToUpload'])) {
http_response_code(400); http_response_code(400);
@@ -889,7 +1040,7 @@ class FolderController {
exit; exit;
} }
$fileUpload = $_FILES['fileToUpload']; $fileUpload = $_FILES['fileToUpload'];
$result = FolderModel::uploadToSharedFolder($token, $fileUpload); $result = FolderModel::uploadToSharedFolder($token, $fileUpload);
if (isset($result['error'])) { if (isset($result['error'])) {
http_response_code(400); http_response_code(400);
@@ -897,13 +1048,13 @@ class FolderController {
echo json_encode($result); echo json_encode($result);
exit; exit;
} }
// Optionally, set a flash message in session. // Optionally, set a flash message in session.
$_SESSION['upload_message'] = "File uploaded successfully."; $_SESSION['upload_message'] = "File uploaded successfully.";
// Redirect back to the shared folder view. // Redirect back to the shared folder view.
$redirectUrl = "/api/folder/shareFolder.php?token=" . urlencode($token); $redirectUrl = "/api/folder/shareFolder.php?token=" . urlencode($token);
header("Location: " . $redirectUrl); header("Location: " . $redirectUrl);
exit; exit;
} }
} }