Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d6a1be777 | ||
|
|
56f34ba362 | ||
|
|
4d329e046f | ||
|
|
f3977153fb |
50
CHANGELOG.md
50
CHANGELOG.md
@@ -1,5 +1,55 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Changes 5/3/2025 v1.3.0
|
||||||
|
|
||||||
|
**Admin Panel Refactor & Enhancements**
|
||||||
|
|
||||||
|
### Moved from `authModals.js` to `adminPanel.js`
|
||||||
|
|
||||||
|
- Extracted all admin-related UI and logic out of `authModals.js`
|
||||||
|
- Created a standalone `adminPanel.js` module
|
||||||
|
- Initialized `openAdminPanel()` and `closeAdminPanel()` exports
|
||||||
|
|
||||||
|
### Responsive, Collapsible Sections
|
||||||
|
|
||||||
|
- Injected new CSS via JS (`adminPanelStyles`)
|
||||||
|
- Default modal width: 50%
|
||||||
|
- Small-screen override (`@media (max-width: 600px)`) to 90% width
|
||||||
|
- Introduced `.section-header` / `.section-content` pattern
|
||||||
|
- Click header to expand/collapse its content
|
||||||
|
- Animated arrow via Material Icons
|
||||||
|
- Indented and padded expanded content
|
||||||
|
|
||||||
|
### “Manage Shared Links” Feature
|
||||||
|
|
||||||
|
- Added new **Manage Shared Links** section to Admin Panel
|
||||||
|
- Endpoint **GET** `/api/admin/readMetadata.php?file=…`
|
||||||
|
- Reads `share_folder_links.json` & `share_links.json` under `META_DIR`
|
||||||
|
- Endpoint **POST**
|
||||||
|
- `/api/folder/deleteShareFolderLink.php`
|
||||||
|
- `/api/file/deleteShareLink.php`
|
||||||
|
- `loadShareLinksSection()` AJAX loader
|
||||||
|
- Displays folder & file shares, expiry dates, upload-allowed, and 🔒 if password-protected
|
||||||
|
- “🗑️” delete buttons refresh the list on success
|
||||||
|
|
||||||
|
### Dark-Mode & Theming Fixes
|
||||||
|
|
||||||
|
- Dark-mode CSS overrides for:
|
||||||
|
- Modal border
|
||||||
|
- `.btn-primary`, `.btn-secondary`
|
||||||
|
- `.form-control` backgrounds & placeholders
|
||||||
|
- Section headers & icons
|
||||||
|
- Close button restyled to use shared **.editor-close-btn** look
|
||||||
|
|
||||||
|
### API and Controller changes
|
||||||
|
|
||||||
|
- Updated all endpoints to use correct controller casing
|
||||||
|
- Renamed controller files to PascalCase (e.g. `adminController.php` to `AdminController.php`, `fileController.php` to `FileController.php`, `folderController.php` to `FolderController.php`)
|
||||||
|
- Adjusted endpoint paths to match controller filenames
|
||||||
|
- Fix FolderController readOnly create folder permission
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Changes 4/30/2025 v1.2.8
|
## Changes 4/30/2025 v1.2.8
|
||||||
|
|
||||||
- **Added** PDF preview in `filePreview.js` (the `extension === "pdf"` block): replaced in-modal `<embed>` with `window.open(urlWithTs, "_blank")` and closed the modal to avoid CSP `frame-ancestors 'none'` restrictions.
|
- **Added** PDF preview in `filePreview.js` (the `extension === "pdf"` block): replaced in-modal `<embed>` with `window.open(urlWithTs, "_blank")` and closed the modal to avoid CSP `frame-ancestors 'none'` restrictions.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/addUser.php
|
// public/api/addUser.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/userController.php';
|
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||||
|
|
||||||
$userController = new UserController();
|
$userController = new UserController();
|
||||||
$userController->addUser();
|
$userController->addUser();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/admin/getConfig.php
|
// public/api/admin/getConfig.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/adminController.php';
|
require_once PROJECT_ROOT . '/src/controllers/AdminController.php';
|
||||||
|
|
||||||
$adminController = new AdminController();
|
$adminController = new AdminController();
|
||||||
$adminController->getConfig();
|
$adminController->getConfig();
|
||||||
44
public/api/admin/readMetadata.php
Normal file
44
public/api/admin/readMetadata.php
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
// public/api/admin/readMetadata.php
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
|
|
||||||
|
// Simple auth‐check: only admins may read these
|
||||||
|
if (empty($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['error'=>'Forbidden']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expect a ?file=share_links.json or share_folder_links.json
|
||||||
|
if (empty($_GET['file'])) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error'=>'Missing `file` parameter']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = basename($_GET['file']);
|
||||||
|
$allowed = ['share_links.json','share_folder_links.json'];
|
||||||
|
if (!in_array($file, $allowed, true)) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['error'=>'Invalid file requested']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = META_DIR . $file;
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
http_response_code(404);
|
||||||
|
echo json_encode((object)[]); // return empty object
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = file_get_contents($path);
|
||||||
|
$json = json_decode($data, true);
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error'=>'Corrupted JSON']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode($json);
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/admin/updateConfig.php
|
// public/api/admin/updateConfig.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/adminController.php';
|
require_once PROJECT_ROOT . '/src/controllers/AdminController.php';
|
||||||
|
|
||||||
$adminController = new AdminController();
|
$adminController = new AdminController();
|
||||||
$adminController->updateConfig();
|
$adminController->updateConfig();
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/vendor/autoload.php';
|
require_once PROJECT_ROOT . '/vendor/autoload.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/authController.php';
|
require_once PROJECT_ROOT . '/src/controllers/AuthController.php';
|
||||||
|
|
||||||
$authController = new AuthController();
|
$authController = new AuthController();
|
||||||
$authController->auth();
|
$authController->auth();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/auth/checkAuth.php
|
// public/api/auth/checkAuth.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/authController.php';
|
require_once PROJECT_ROOT . '/src/controllers/AuthController.php';
|
||||||
|
|
||||||
$authController = new AuthController();
|
$authController = new AuthController();
|
||||||
$authController->checkAuth();
|
$authController->checkAuth();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/auth/login_basic.php
|
// public/api/auth/login_basic.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/authController.php';
|
require_once PROJECT_ROOT . '/src/controllers/AuthController.php';
|
||||||
|
|
||||||
$authController = new AuthController();
|
$authController = new AuthController();
|
||||||
$authController->loginBasic();
|
$authController->loginBasic();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/auth/logout.php
|
// public/api/auth/logout.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/authController.php';
|
require_once PROJECT_ROOT . '/src/controllers/AuthController.php';
|
||||||
|
|
||||||
$authController = new AuthController();
|
$authController = new AuthController();
|
||||||
$authController->logout();
|
$authController->logout();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/auth/token.php
|
// public/api/auth/token.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/authController.php';
|
require_once PROJECT_ROOT . '/src/controllers/AuthController.php';
|
||||||
|
|
||||||
$authController = new AuthController();
|
$authController = new AuthController();
|
||||||
$authController->getToken();
|
$authController->getToken();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/changePassword.php
|
// public/api/changePassword.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/userController.php';
|
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||||
|
|
||||||
$userController = new UserController();
|
$userController = new UserController();
|
||||||
$userController->changePassword();
|
$userController->changePassword();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/copyFiles.php
|
// public/api/file/copyFiles.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->copyFiles();
|
$fileController->copyFiles();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/createShareLink.php
|
// public/api/file/createShareLink.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->createShareLink();
|
$fileController->createShareLink();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/deleteFiles.php
|
// public/api/file/deleteFiles.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->deleteFiles();
|
$fileController->deleteFiles();
|
||||||
6
public/api/file/deleteShareLink.php
Normal file
6
public/api/file/deleteShareLink.php
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
|
$fileController = new FileController();
|
||||||
|
$fileController->deleteShareLink();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/deleteTrashFiles.php
|
// public/api/file/deleteTrashFiles.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->deleteTrashFiles();
|
$fileController->deleteTrashFiles();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/download.php
|
// public/api/file/download.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->downloadFile();
|
$fileController->downloadFile();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/downloadZip.php
|
// public/api/file/downloadZip.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->downloadZip();
|
$fileController->downloadZip();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/extractZip.php
|
// public/api/file/extractZip.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->extractZip();
|
$fileController->extractZip();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/getFileList.php
|
// public/api/file/getFileList.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->getFileList();
|
$fileController->getFileList();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/getFileTag.php
|
// public/api/file/getFileTag.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->getFileTags();
|
$fileController->getFileTags();
|
||||||
6
public/api/file/getShareLinks.php
Normal file
6
public/api/file/getShareLinks.php
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
|
$fileController = new FileController();
|
||||||
|
$fileController->getShareLinks();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/getTrashItems.php
|
// public/api/file/getTrashItems.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->getTrashItems();
|
$fileController->getTrashItems();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/moveFiles.php
|
// public/api/file/moveFiles.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->moveFiles();
|
$fileController->moveFiles();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/renameFile.php
|
// public/api/file/renameFile.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->renameFile();
|
$fileController->renameFile();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/restoreFiles.php
|
// public/api/file/restoreFiles.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->restoreFiles();
|
$fileController->restoreFiles();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/saveFile.php
|
// public/api/file/saveFile.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->saveFile();
|
$fileController->saveFile();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/saveFileTag.php
|
// public/api/file/saveFileTag.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->saveFileTag();
|
$fileController->saveFileTag();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/file/share.php
|
// public/api/file/share.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/fileController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||||
|
|
||||||
$fileController = new FileController();
|
$fileController = new FileController();
|
||||||
$fileController->shareFile();
|
$fileController->shareFile();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/folder/createFolder.php
|
// public/api/folder/createFolder.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/folderController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||||
|
|
||||||
$folderController = new FolderController();
|
$folderController = new FolderController();
|
||||||
$folderController->createFolder();
|
$folderController->createFolder();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/folder/createShareFolderLink.php
|
// public/api/folder/createShareFolderLink.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/folderController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||||
|
|
||||||
$folderController = new FolderController();
|
$folderController = new FolderController();
|
||||||
$folderController->createShareFolderLink();
|
$folderController->createShareFolderLink();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/folder/deleteFolder.php
|
// public/api/folder/deleteFolder.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/folderController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||||
|
|
||||||
$folderController = new FolderController();
|
$folderController = new FolderController();
|
||||||
$folderController->deleteFolder();
|
$folderController->deleteFolder();
|
||||||
6
public/api/folder/deleteShareFolderLink.php
Normal file
6
public/api/folder/deleteShareFolderLink.php
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
|
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||||
|
|
||||||
|
$folderController = new FolderController();
|
||||||
|
$folderController->deleteShareFolderLink();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/folder/downloadSharedFile.php
|
// public/api/folder/downloadSharedFile.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/folderController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||||
|
|
||||||
$folderController = new FolderController();
|
$folderController = new FolderController();
|
||||||
$folderController->downloadSharedFile();
|
$folderController->downloadSharedFile();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/folder/getFolderList.php
|
// public/api/folder/getFolderList.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/folderController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||||
|
|
||||||
$folderController = new FolderController();
|
$folderController = new FolderController();
|
||||||
$folderController->getFolderList();
|
$folderController->getFolderList();
|
||||||
6
public/api/folder/getShareFolderLinks.php
Normal file
6
public/api/folder/getShareFolderLinks.php
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
|
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||||
|
|
||||||
|
$folderController = new FolderController();
|
||||||
|
$folderController->getShareFolderLinks();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/folder/renameFolder.php
|
// public/api/folder/renameFolder.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/folderController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||||
|
|
||||||
$folderController = new FolderController();
|
$folderController = new FolderController();
|
||||||
$folderController->renameFolder();
|
$folderController->renameFolder();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/folder/shareFolder.php
|
// public/api/folder/shareFolder.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/folderController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||||
|
|
||||||
$folderController = new FolderController();
|
$folderController = new FolderController();
|
||||||
$folderController->shareFolder();
|
$folderController->shareFolder();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/folder/uploadToSharedFolder.php
|
// public/api/folder/uploadToSharedFolder.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/folderController.php';
|
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||||
|
|
||||||
$folderController = new FolderController();
|
$folderController = new FolderController();
|
||||||
$folderController->uploadToSharedFolder();
|
$folderController->uploadToSharedFolder();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/getUserPermissions.php
|
// public/api/getUserPermissions.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/userController.php';
|
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||||
|
|
||||||
$userController = new UserController();
|
$userController = new UserController();
|
||||||
$userController->getUserPermissions();
|
$userController->getUserPermissions();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/getUsers.php
|
// public/api/getUsers.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/userController.php';
|
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||||
|
|
||||||
$userController = new UserController();
|
$userController = new UserController();
|
||||||
$userController->getUsers(); // This will output the JSON response
|
$userController->getUsers(); // This will output the JSON response
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/removeUser.php
|
// public/api/removeUser.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/userController.php';
|
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||||
|
|
||||||
$userController = new UserController();
|
$userController = new UserController();
|
||||||
$userController->removeUser();
|
$userController->removeUser();
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/vendor/autoload.php';
|
require_once PROJECT_ROOT . '/vendor/autoload.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/userController.php';
|
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||||
|
|
||||||
$userController = new UserController();
|
$userController = new UserController();
|
||||||
$userController->disableTOTP();
|
$userController->disableTOTP();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/totp_recover.php
|
// public/api/totp_recover.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/userController.php';
|
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||||
|
|
||||||
$userController = new UserController();
|
$userController = new UserController();
|
||||||
$userController->recoverTOTP();
|
$userController->recoverTOTP();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/totp_saveCode.php
|
// public/api/totp_saveCode.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/userController.php';
|
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||||
|
|
||||||
$userController = new UserController();
|
$userController = new UserController();
|
||||||
$userController->saveTOTPRecoveryCode();
|
$userController->saveTOTPRecoveryCode();
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/vendor/autoload.php';
|
require_once PROJECT_ROOT . '/vendor/autoload.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/userController.php';
|
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||||
|
|
||||||
$userController = new UserController();
|
$userController = new UserController();
|
||||||
$userController->setupTOTP();
|
$userController->setupTOTP();
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/vendor/autoload.php';
|
require_once PROJECT_ROOT . '/vendor/autoload.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/userController.php';
|
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||||
|
|
||||||
$userController = new UserController();
|
$userController = new UserController();
|
||||||
$userController->verifyTOTP();
|
$userController->verifyTOTP();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/updateUserPanel.php
|
// public/api/updateUserPanel.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/userController.php';
|
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||||
|
|
||||||
$userController = new UserController();
|
$userController = new UserController();
|
||||||
$userController->updateUserPanel();
|
$userController->updateUserPanel();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/updateUserPermissions.php
|
// public/api/updateUserPermissions.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/userController.php';
|
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||||
|
|
||||||
$userController = new UserController();
|
$userController = new UserController();
|
||||||
$userController->updateUserPermissions();
|
$userController->updateUserPermissions();
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// public/api/upload/removeChunks.php
|
// public/api/upload/removeChunks.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/uploadController.php';
|
require_once PROJECT_ROOT . '/src/controllers/UploadController.php';
|
||||||
|
|
||||||
$uploadController = new UploadController();
|
$uploadController = new UploadController();
|
||||||
$uploadController->removeChunks();
|
$uploadController->removeChunks();
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
// public/api/upload/upload.php
|
// public/api/upload/upload.php
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
require_once __DIR__ . '/../../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/controllers/uploadController.php';
|
require_once PROJECT_ROOT . '/src/controllers/UploadController.php';
|
||||||
|
|
||||||
$uploadController = new UploadController();
|
$uploadController = new UploadController();
|
||||||
$uploadController->handleUpload();
|
$uploadController->handleUpload();
|
||||||
658
public/js/adminPanel.js
Normal file
658
public/js/adminPanel.js
Normal file
@@ -0,0 +1,658 @@
|
|||||||
|
import { t } from './i18n.js';
|
||||||
|
import { loadAdminConfigFunc } from './auth.js';
|
||||||
|
import { showToast, toggleVisibility, attachEnterKeyListener } from './domUtils.js';
|
||||||
|
import { sendRequest } from './networkUtils.js';
|
||||||
|
|
||||||
|
const version = "v1.3.0";
|
||||||
|
const adminTitle = `${t("admin_panel")} <small style="font-size:12px;color:gray;">${version}</small>`;
|
||||||
|
|
||||||
|
// ————— Inject updated styles —————
|
||||||
|
(function () {
|
||||||
|
if (document.getElementById('adminPanelStyles')) return;
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.id = 'adminPanelStyles';
|
||||||
|
style.textContent = `
|
||||||
|
/* Modal sizing */
|
||||||
|
#adminPanelModal .modal-content {
|
||||||
|
max-width: 1100px;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Small phones: 90% width */
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
#adminPanelModal .modal-content {
|
||||||
|
width: 90% !important;
|
||||||
|
max-width: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark-mode fixes */
|
||||||
|
body.dark-mode #adminPanelModal .modal-content {
|
||||||
|
border-color: #555 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enforce light‐mode styling */
|
||||||
|
#adminPanelModal .modal-content {
|
||||||
|
max-width: 1100px;
|
||||||
|
width: 50%;
|
||||||
|
background: #fff !important;
|
||||||
|
color: #000 !important;
|
||||||
|
border: 1px solid #ccc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enforce dark‐mode styling */
|
||||||
|
body.dark-mode #adminPanelModal .modal-content {
|
||||||
|
background: #2c2c2c !important;
|
||||||
|
color: #e0e0e0 !important;
|
||||||
|
border-color: #555 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* form controls in dark */
|
||||||
|
body.dark-mode .form-control {
|
||||||
|
background-color: #333;
|
||||||
|
border-color: #555;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
body.dark-mode .form-control::placeholder { color: #888; }
|
||||||
|
|
||||||
|
/* Section headers */
|
||||||
|
.section-header {
|
||||||
|
background: #f5f5f5;
|
||||||
|
padding: 10px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-weight: bold;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
.section-header:first-of-type { margin-top: 0; }
|
||||||
|
.section-header.collapsed .material-icons { transform: rotate(-90deg); }
|
||||||
|
.section-header .material-icons { transition: transform .3s; color: #444; }
|
||||||
|
|
||||||
|
body.dark-mode .section-header {
|
||||||
|
background: #3a3a3a;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
body.dark-mode .section-header .material-icons { color: #ccc; }
|
||||||
|
|
||||||
|
/* Hidden by default */
|
||||||
|
.section-content {
|
||||||
|
display: none;
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close button */
|
||||||
|
#adminPanelModal .editor-close-btn {
|
||||||
|
position: absolute; top:10px; right:10px;
|
||||||
|
display:flex; align-items:center; justify-content:center;
|
||||||
|
font-size:20px; font-weight:bold; cursor:pointer;
|
||||||
|
z-index:1000; width:32px; height:32px; border-radius:50%;
|
||||||
|
text-align:center; line-height:30px;
|
||||||
|
color:#ff4d4d; background:rgba(255,255,255,0.9);
|
||||||
|
border:2px solid transparent; transition:all .3s;
|
||||||
|
}
|
||||||
|
#adminPanelModal .editor-close-btn:hover {
|
||||||
|
color:white; background:#ff4d4d;
|
||||||
|
box-shadow:0 0 6px rgba(255,77,77,.8);
|
||||||
|
transform:scale(1.05);
|
||||||
|
}
|
||||||
|
body.dark-mode #adminPanelModal .editor-close-btn {
|
||||||
|
background:rgba(0,0,0,0.6);
|
||||||
|
color:#ff4d4d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Action-row */
|
||||||
|
.action-row {
|
||||||
|
display:flex;
|
||||||
|
justify-content:space-between;
|
||||||
|
margin-top:15px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
})();
|
||||||
|
// ————————————————————————————————————
|
||||||
|
|
||||||
|
let originalAdminConfig = {};
|
||||||
|
function captureInitialAdminConfig() {
|
||||||
|
originalAdminConfig = {
|
||||||
|
headerTitle: document.getElementById("headerTitle").value.trim(),
|
||||||
|
oidcProviderUrl: document.getElementById("oidcProviderUrl").value.trim(),
|
||||||
|
oidcClientId: document.getElementById("oidcClientId").value.trim(),
|
||||||
|
oidcClientSecret: document.getElementById("oidcClientSecret").value.trim(),
|
||||||
|
oidcRedirectUri: document.getElementById("oidcRedirectUri").value.trim(),
|
||||||
|
disableFormLogin: document.getElementById("disableFormLogin").checked,
|
||||||
|
disableBasicAuth: document.getElementById("disableBasicAuth").checked,
|
||||||
|
disableOIDCLogin: document.getElementById("disableOIDCLogin").checked,
|
||||||
|
enableWebDAV: document.getElementById("enableWebDAV").checked,
|
||||||
|
sharedMaxUploadSize: document.getElementById("sharedMaxUploadSize").value.trim(),
|
||||||
|
globalOtpauthUrl: document.getElementById("globalOtpauthUrl").value.trim()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function hasUnsavedChanges() {
|
||||||
|
const o = originalAdminConfig;
|
||||||
|
return (
|
||||||
|
document.getElementById("headerTitle").value.trim() !== o.headerTitle ||
|
||||||
|
document.getElementById("oidcProviderUrl").value.trim() !== o.oidcProviderUrl ||
|
||||||
|
document.getElementById("oidcClientId").value.trim() !== o.oidcClientId ||
|
||||||
|
document.getElementById("oidcClientSecret").value.trim() !== o.oidcClientSecret ||
|
||||||
|
document.getElementById("oidcRedirectUri").value.trim() !== o.oidcRedirectUri ||
|
||||||
|
document.getElementById("disableFormLogin").checked !== o.disableFormLogin ||
|
||||||
|
document.getElementById("disableBasicAuth").checked !== o.disableBasicAuth ||
|
||||||
|
document.getElementById("disableOIDCLogin").checked !== o.disableOIDCLogin ||
|
||||||
|
document.getElementById("enableWebDAV").checked !== o.enableWebDAV ||
|
||||||
|
document.getElementById("sharedMaxUploadSize").value.trim() !== o.sharedMaxUploadSize ||
|
||||||
|
document.getElementById("globalOtpauthUrl").value.trim() !== o.globalOtpauthUrl
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showCustomConfirmModal(message) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const modal = document.getElementById("customConfirmModal");
|
||||||
|
const msg = document.getElementById("confirmMessage");
|
||||||
|
const yes = document.getElementById("confirmYesBtn");
|
||||||
|
const no = document.getElementById("confirmNoBtn");
|
||||||
|
msg.textContent = message;
|
||||||
|
modal.style.display = "block";
|
||||||
|
function clean() {
|
||||||
|
modal.style.display = "none";
|
||||||
|
yes.removeEventListener("click", onYes);
|
||||||
|
no.removeEventListener("click", onNo);
|
||||||
|
}
|
||||||
|
function onYes() { clean(); resolve(true); }
|
||||||
|
function onNo() { clean(); resolve(false); }
|
||||||
|
yes.addEventListener("click", onYes);
|
||||||
|
no.addEventListener("click", onNo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleSection(id) {
|
||||||
|
const hdr = document.getElementById(id + "Header");
|
||||||
|
const cnt = document.getElementById(id + "Content");
|
||||||
|
const isCollapsedNow = hdr.classList.toggle("collapsed");
|
||||||
|
// collapsed class present => hide; absent => show
|
||||||
|
cnt.style.display = isCollapsedNow ? "none" : "block";
|
||||||
|
if (!isCollapsedNow && id === "shareLinks") {
|
||||||
|
loadShareLinksSection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadShareLinksSection() {
|
||||||
|
const container = document.getElementById("shareLinksContent");
|
||||||
|
container.textContent = t("loading") + "...";
|
||||||
|
|
||||||
|
// Helper to fetch a metadata file or return {} on any error
|
||||||
|
const fetchMeta = file =>
|
||||||
|
fetch(`/api/admin/readMetadata.php?file=${file}`, { credentials: "include" })
|
||||||
|
.then(r => r.ok ? r.json() : {}) // non-2xx → treat as empty
|
||||||
|
.catch(() => ({}));
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
fetchMeta("share_folder_links.json"),
|
||||||
|
fetchMeta("share_links.json")
|
||||||
|
])
|
||||||
|
.then(([folders, files]) => {
|
||||||
|
// If nothing at all, show a friendly message
|
||||||
|
if (Object.keys(folders).length === 0 && Object.keys(files).length === 0) {
|
||||||
|
container.textContent = t("no_shared_links_available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let html = `<h5>${t("folder_shares")}</h5><ul>`;
|
||||||
|
Object.entries(folders).forEach(([token, o]) => {
|
||||||
|
const lock = o.password ? `🔒 ` : "";
|
||||||
|
html += `
|
||||||
|
<li>
|
||||||
|
${lock}<strong>${o.folder}</strong>
|
||||||
|
<small>(${new Date(o.expires * 1000).toLocaleString()})</small>
|
||||||
|
<button type="button"
|
||||||
|
data-key="${token}"
|
||||||
|
data-type="folder"
|
||||||
|
class="btn btn-sm btn-link delete-share">🗑️</button>
|
||||||
|
</li>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
html += `</ul><h5 style="margin-top:1em;">${t("file_shares")}</h5><ul>`;
|
||||||
|
Object.entries(files).forEach(([token, o]) => {
|
||||||
|
const lock = o.password ? `🔒 ` : "";
|
||||||
|
html += `
|
||||||
|
<li>
|
||||||
|
${lock}<strong>${o.folder}/${o.file}</strong>
|
||||||
|
<small>(${new Date(o.expires * 1000).toLocaleString()})</small>
|
||||||
|
<button type="button"
|
||||||
|
data-key="${token}"
|
||||||
|
data-type="file"
|
||||||
|
class="btn btn-sm btn-link delete-share">🗑️</button>
|
||||||
|
</li>`;
|
||||||
|
});
|
||||||
|
html += `</ul>`;
|
||||||
|
|
||||||
|
container.innerHTML = html;
|
||||||
|
|
||||||
|
// wire up delete buttons
|
||||||
|
container.querySelectorAll(".delete-share").forEach(btn => {
|
||||||
|
btn.addEventListener("click", evt => {
|
||||||
|
evt.preventDefault();
|
||||||
|
const token = btn.dataset.key;
|
||||||
|
const isFolder = btn.dataset.type === "folder";
|
||||||
|
const endpoint = isFolder
|
||||||
|
? "/api/folder/deleteShareFolderLink.php"
|
||||||
|
: "/api/file/deleteShareLink.php";
|
||||||
|
|
||||||
|
fetch(endpoint, {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
|
body: new URLSearchParams({ token })
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then(json => {
|
||||||
|
if (json.success) {
|
||||||
|
showToast(t("share_deleted_successfully"));
|
||||||
|
loadShareLinksSection();
|
||||||
|
} else {
|
||||||
|
showToast(t("error_deleting_share") + ": " + (json.error || ""), "error");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error("Delete error:", err);
|
||||||
|
showToast(t("error_deleting_share"), "error");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error("loadShareLinksSection error:", err);
|
||||||
|
container.textContent = t("error_loading_share_links");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function openAdminPanel() {
|
||||||
|
fetch("/api/admin/getConfig.php", { credentials: "include" })
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(config => {
|
||||||
|
// apply header title + globals
|
||||||
|
if (config.header_title) {
|
||||||
|
document.querySelector(".header-title h1").textContent = config.header_title;
|
||||||
|
window.headerTitle = config.header_title;
|
||||||
|
}
|
||||||
|
if (config.oidc) Object.assign(window.currentOIDCConfig, config.oidc);
|
||||||
|
if (config.globalOtpauthUrl) window.currentOIDCConfig.globalOtpauthUrl = config.globalOtpauthUrl;
|
||||||
|
|
||||||
|
const dark = document.body.classList.contains("dark-mode");
|
||||||
|
const bg = dark ? "rgba(0,0,0,0.7)" : "rgba(0,0,0,0.3)";
|
||||||
|
const inner = `
|
||||||
|
background:${dark ? "#2c2c2c" : "#fff"};
|
||||||
|
color:${dark ? "#e0e0e0" : "#000"};
|
||||||
|
padding:20px; max-width:1100px; width:50%;
|
||||||
|
border-radius:8px; position:relative;
|
||||||
|
max-height:90vh; overflow:auto;
|
||||||
|
border:1px solid ${dark ? "#555" : "#ccc"};
|
||||||
|
`;
|
||||||
|
|
||||||
|
let mdl = document.getElementById("adminPanelModal");
|
||||||
|
if (!mdl) {
|
||||||
|
mdl = document.createElement("div");
|
||||||
|
mdl.id = "adminPanelModal";
|
||||||
|
mdl.style.cssText = `
|
||||||
|
position:fixed; top:0; left:0;
|
||||||
|
width:100vw; height:100vh;
|
||||||
|
background:${bg};
|
||||||
|
display:flex; justify-content:center; align-items:center;
|
||||||
|
z-index:3000;
|
||||||
|
`;
|
||||||
|
mdl.innerHTML = `
|
||||||
|
<div class="modal-content" style="${inner}">
|
||||||
|
<div class="editor-close-btn" id="closeAdminPanel">×</div>
|
||||||
|
<h3>${adminTitle}</h3>
|
||||||
|
<form id="adminPanelForm">
|
||||||
|
|
||||||
|
<!-- each section: header + content -->
|
||||||
|
${[
|
||||||
|
{ id: "userManagement", label: t("user_management") },
|
||||||
|
{ id: "headerSettings", label: t("header_settings") },
|
||||||
|
{ id: "loginOptions", label: t("login_options") },
|
||||||
|
{ id: "webdav", label: "WebDAV Access" },
|
||||||
|
{ id: "upload", label: t("shared_max_upload_size_bytes_title") },
|
||||||
|
{ id: "oidc", label: t("oidc_configuration") + " & TOTP" },
|
||||||
|
{ id: "shareLinks", label: t("manage_shared_links") }
|
||||||
|
].map(sec => `
|
||||||
|
<div id="${sec.id}Header" class="section-header collapsed">
|
||||||
|
${sec.label} <i class="material-icons">expand_more</i>
|
||||||
|
</div>
|
||||||
|
<div id="${sec.id}Content" class="section-content"></div>
|
||||||
|
`).join("")}
|
||||||
|
|
||||||
|
<div class="action-row">
|
||||||
|
<button type="button" id="cancelAdminSettings" class="btn btn-secondary">${t("cancel")}</button>
|
||||||
|
<button type="button" id="saveAdminSettings" class="btn btn-primary">${t("save_settings")}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(mdl);
|
||||||
|
|
||||||
|
// Bind close & cancel
|
||||||
|
document.getElementById("closeAdminPanel")
|
||||||
|
.addEventListener("click", closeAdminPanel);
|
||||||
|
document.getElementById("cancelAdminSettings")
|
||||||
|
.addEventListener("click", closeAdminPanel);
|
||||||
|
|
||||||
|
// Section toggles
|
||||||
|
["userManagement", "headerSettings", "loginOptions", "webdav", "upload", "oidc", "shareLinks"]
|
||||||
|
.forEach(id => {
|
||||||
|
document.getElementById(id + "Header")
|
||||||
|
.addEventListener("click", () => toggleSection(id));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Populate each section’s CONTENT:
|
||||||
|
// — User Mgmt —
|
||||||
|
document.getElementById("userManagementContent").innerHTML = `
|
||||||
|
<button type="button" id="adminOpenAddUser" class="btn btn-success me-2">${t("add_user")}</button>
|
||||||
|
<button type="button" id="adminOpenRemoveUser" class="btn btn-danger me-2">${t("remove_user")}</button>
|
||||||
|
<button type="button" id="adminOpenUserPermissions" class="btn btn-secondary">${t("user_permissions")}</button>
|
||||||
|
`;
|
||||||
|
document.getElementById("adminOpenAddUser")
|
||||||
|
.addEventListener("click", () => {
|
||||||
|
toggleVisibility("addUserModal", true);
|
||||||
|
document.getElementById("newUsername").focus();
|
||||||
|
});
|
||||||
|
document.getElementById("adminOpenRemoveUser")
|
||||||
|
.addEventListener("click", () => {
|
||||||
|
if (typeof window.loadUserList === "function") window.loadUserList();
|
||||||
|
toggleVisibility("removeUserModal", true);
|
||||||
|
});
|
||||||
|
document.getElementById("adminOpenUserPermissions")
|
||||||
|
.addEventListener("click", openUserPermissionsModal);
|
||||||
|
|
||||||
|
// — Header Settings —
|
||||||
|
document.getElementById("headerSettingsContent").innerHTML = `
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="headerTitle">${t("header_title")}:</label>
|
||||||
|
<input type="text" id="headerTitle" class="form-control" value="${window.headerTitle}" />
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// — Login Options —
|
||||||
|
document.getElementById("loginOptionsContent").innerHTML = `
|
||||||
|
<div class="form-group"><input type="checkbox" id="disableFormLogin" /> <label for="disableFormLogin">${t("disable_login_form")}</label></div>
|
||||||
|
<div class="form-group"><input type="checkbox" id="disableBasicAuth" /> <label for="disableBasicAuth">${t("disable_basic_http_auth")}</label></div>
|
||||||
|
<div class="form-group"><input type="checkbox" id="disableOIDCLogin" /> <label for="disableOIDCLogin">${t("disable_oidc_login")}</label></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// — WebDAV —
|
||||||
|
document.getElementById("webdavContent").innerHTML = `
|
||||||
|
<div class="form-group"><input type="checkbox" id="enableWebDAV" /> <label for="enableWebDAV">Enable WebDAV</label></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// — Upload —
|
||||||
|
document.getElementById("uploadContent").innerHTML = `
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="sharedMaxUploadSize">${t("shared_max_upload_size_bytes")}:</label>
|
||||||
|
<input type="number" id="sharedMaxUploadSize" class="form-control" placeholder="e.g. 52428800" />
|
||||||
|
<small>${t("max_bytes_shared_uploads_note")}</small>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// — OIDC & TOTP —
|
||||||
|
document.getElementById("oidcContent").innerHTML = `
|
||||||
|
<div class="form-group"><label for="oidcProviderUrl">${t("oidc_provider_url")}:</label><input type="text" id="oidcProviderUrl" class="form-control" value="${window.currentOIDCConfig.providerUrl}" /></div>
|
||||||
|
<div class="form-group"><label for="oidcClientId">${t("oidc_client_id")}:</label><input type="text" id="oidcClientId" class="form-control" value="${window.currentOIDCConfig.clientId}" /></div>
|
||||||
|
<div class="form-group"><label for="oidcClientSecret">${t("oidc_client_secret")}:</label><input type="text" id="oidcClientSecret" class="form-control" value="${window.currentOIDCConfig.clientSecret}" /></div>
|
||||||
|
<div class="form-group"><label for="oidcRedirectUri">${t("oidc_redirect_uri")}:</label><input type="text" id="oidcRedirectUri" class="form-control" value="${window.currentOIDCConfig.redirectUri}" /></div>
|
||||||
|
<div class="form-group"><label for="globalOtpauthUrl">${t("global_otpauth_url")}:</label><input type="text" id="globalOtpauthUrl" class="form-control" value="${window.currentOIDCConfig.globalOtpauthUrl || 'otpauth://totp/{label}?secret={secret}&issuer=FileRise'}" /></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// — Share Links —
|
||||||
|
document.getElementById("shareLinksContent").textContent = t("loading") + "…";
|
||||||
|
|
||||||
|
// — Save handler & constraints —
|
||||||
|
document.getElementById("saveAdminSettings")
|
||||||
|
.addEventListener("click", handleSave);
|
||||||
|
["disableFormLogin", "disableBasicAuth", "disableOIDCLogin"].forEach(id => {
|
||||||
|
document.getElementById(id)
|
||||||
|
.addEventListener("change", e => {
|
||||||
|
const chk = ["disableFormLogin", "disableBasicAuth", "disableOIDCLogin"]
|
||||||
|
.filter(i => document.getElementById(i).checked).length;
|
||||||
|
if (chk === 3) {
|
||||||
|
showToast(t("at_least_one_login_method"));
|
||||||
|
e.target.checked = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize inputs from config + capture
|
||||||
|
document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true;
|
||||||
|
document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true;
|
||||||
|
document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true;
|
||||||
|
document.getElementById("enableWebDAV").checked = config.enableWebDAV === true;
|
||||||
|
document.getElementById("sharedMaxUploadSize").value = config.sharedMaxUploadSize || "";
|
||||||
|
captureInitialAdminConfig();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// modal already exists → just refresh values & re-show
|
||||||
|
mdl.style.display = "flex";
|
||||||
|
// update dark/light as above...
|
||||||
|
document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true;
|
||||||
|
document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true;
|
||||||
|
document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true;
|
||||||
|
document.getElementById("enableWebDAV").checked = config.enableWebDAV === true;
|
||||||
|
document.getElementById("sharedMaxUploadSize").value = config.sharedMaxUploadSize || "";
|
||||||
|
document.getElementById("oidcProviderUrl").value = window.currentOIDCConfig.providerUrl;
|
||||||
|
document.getElementById("oidcClientId").value = window.currentOIDCConfig.clientId;
|
||||||
|
document.getElementById("oidcClientSecret").value = window.currentOIDCConfig.clientSecret;
|
||||||
|
document.getElementById("oidcRedirectUri").value = window.currentOIDCConfig.redirectUri;
|
||||||
|
document.getElementById("globalOtpauthUrl").value = window.currentOIDCConfig.globalOtpauthUrl || '';
|
||||||
|
captureInitialAdminConfig();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {/* if even fetching fails, open empty panel */ });
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSave() {
|
||||||
|
const dFL = document.getElementById("disableFormLogin").checked;
|
||||||
|
const dBA = document.getElementById("disableBasicAuth").checked;
|
||||||
|
const dOIDC = document.getElementById("disableOIDCLogin").checked;
|
||||||
|
const eWD = document.getElementById("enableWebDAV").checked;
|
||||||
|
const sMax = parseInt(document.getElementById("sharedMaxUploadSize").value, 10) || 0;
|
||||||
|
const nHT = document.getElementById("headerTitle").value.trim();
|
||||||
|
const nOIDC = {
|
||||||
|
providerUrl: document.getElementById("oidcProviderUrl").value.trim(),
|
||||||
|
clientId: document.getElementById("oidcClientId").value.trim(),
|
||||||
|
clientSecret: document.getElementById("oidcClientSecret").value.trim(),
|
||||||
|
redirectUri: document.getElementById("oidcRedirectUri").value.trim()
|
||||||
|
};
|
||||||
|
const gURL = document.getElementById("globalOtpauthUrl").value.trim();
|
||||||
|
|
||||||
|
if ([dFL, dBA, dOIDC].filter(x => x).length === 3) {
|
||||||
|
showToast(t("at_least_one_login_method"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendRequest("/api/admin/updateConfig.php", "POST", {
|
||||||
|
header_title: nHT, oidc: nOIDC,
|
||||||
|
disableFormLogin: dFL, disableBasicAuth: dBA, disableOIDCLogin: dOIDC,
|
||||||
|
enableWebDAV: eWD, sharedMaxUploadSize: sMax, globalOtpauthUrl: gURL
|
||||||
|
}, {
|
||||||
|
"X-CSRF-Token": window.csrfToken
|
||||||
|
}).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
showToast(t("settings_updated_successfully"), "success");
|
||||||
|
captureInitialAdminConfig();
|
||||||
|
closeAdminPanel();
|
||||||
|
loadAdminConfigFunc();
|
||||||
|
} else {
|
||||||
|
showToast(t("error_updating_settings") + ": " + (res.error || t("unknown_error")), "error");
|
||||||
|
}
|
||||||
|
}).catch(() => {/*noop*/ });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function closeAdminPanel() {
|
||||||
|
if (hasUnsavedChanges()) {
|
||||||
|
const ok = await showCustomConfirmModal(t("unsaved_changes_confirm"));
|
||||||
|
if (!ok) return;
|
||||||
|
}
|
||||||
|
document.getElementById("adminPanelModal").style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- New: User Permissions Modal ---
|
||||||
|
export function openUserPermissionsModal() {
|
||||||
|
let userPermissionsModal = document.getElementById("userPermissionsModal");
|
||||||
|
const isDarkMode = document.body.classList.contains("dark-mode");
|
||||||
|
const overlayBackground = isDarkMode ? "rgba(0,0,0,0.7)" : "rgba(0,0,0,0.3)";
|
||||||
|
const modalContentStyles = `
|
||||||
|
background: ${isDarkMode ? "#2c2c2c" : "#fff"};
|
||||||
|
color: ${isDarkMode ? "#e0e0e0" : "#000"};
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 500px;
|
||||||
|
width: 90%;
|
||||||
|
border-radius: 8px;
|
||||||
|
position: relative;
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (!userPermissionsModal) {
|
||||||
|
userPermissionsModal = document.createElement("div");
|
||||||
|
userPermissionsModal.id = "userPermissionsModal";
|
||||||
|
userPermissionsModal.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: ${overlayBackground};
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 3500;
|
||||||
|
`;
|
||||||
|
userPermissionsModal.innerHTML = `
|
||||||
|
<div class="modal-content" style="${modalContentStyles}">
|
||||||
|
<span id="closeUserPermissionsModal" style="position: absolute; top: 10px; right: 10px; cursor: pointer; font-size: 24px;">×</span>
|
||||||
|
<h3>${t("user_permissions")}</h3>
|
||||||
|
<div id="userPermissionsList" style="max-height: 300px; overflow-y: auto; margin-bottom: 15px;">
|
||||||
|
<!-- User rows will be loaded here -->
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; justify-content: flex-end; gap: 10px;">
|
||||||
|
<button type="button" id="cancelUserPermissionsBtn" class="btn btn-secondary">${t("cancel")}</button>
|
||||||
|
<button type="button" id="saveUserPermissionsBtn" class="btn btn-primary">${t("save_permissions")}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(userPermissionsModal);
|
||||||
|
document.getElementById("closeUserPermissionsModal").addEventListener("click", () => {
|
||||||
|
userPermissionsModal.style.display = "none";
|
||||||
|
});
|
||||||
|
document.getElementById("cancelUserPermissionsBtn").addEventListener("click", () => {
|
||||||
|
userPermissionsModal.style.display = "none";
|
||||||
|
});
|
||||||
|
document.getElementById("saveUserPermissionsBtn").addEventListener("click", () => {
|
||||||
|
// Collect permissions data from each user row.
|
||||||
|
const rows = userPermissionsModal.querySelectorAll(".user-permission-row");
|
||||||
|
const permissionsData = [];
|
||||||
|
rows.forEach(row => {
|
||||||
|
const username = row.getAttribute("data-username");
|
||||||
|
const folderOnlyCheckbox = row.querySelector("input[data-permission='folderOnly']");
|
||||||
|
const readOnlyCheckbox = row.querySelector("input[data-permission='readOnly']");
|
||||||
|
const disableUploadCheckbox = row.querySelector("input[data-permission='disableUpload']");
|
||||||
|
permissionsData.push({
|
||||||
|
username,
|
||||||
|
folderOnly: folderOnlyCheckbox.checked,
|
||||||
|
readOnly: readOnlyCheckbox.checked,
|
||||||
|
disableUpload: disableUploadCheckbox.checked
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// Send the permissionsData to the server.
|
||||||
|
sendRequest("/api/updateUserPermissions.php", "POST", { permissions: permissionsData }, { "X-CSRF-Token": window.csrfToken })
|
||||||
|
.then(response => {
|
||||||
|
if (response.success) {
|
||||||
|
showToast(t("user_permissions_updated_successfully"));
|
||||||
|
userPermissionsModal.style.display = "none";
|
||||||
|
} else {
|
||||||
|
showToast(t("error_updating_permissions") + ": " + (response.error || t("unknown_error")));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
showToast(t("error_updating_permissions"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
userPermissionsModal.style.display = "flex";
|
||||||
|
}
|
||||||
|
// Load the list of users into the modal.
|
||||||
|
loadUserPermissionsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadUserPermissionsList() {
|
||||||
|
const listContainer = document.getElementById("userPermissionsList");
|
||||||
|
if (!listContainer) return;
|
||||||
|
listContainer.innerHTML = "";
|
||||||
|
|
||||||
|
// First, fetch the current permissions from the server.
|
||||||
|
fetch("/api/getUserPermissions.php", { credentials: "include" })
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(permissionsData => {
|
||||||
|
// Then, fetch the list of users.
|
||||||
|
return fetch("/api/getUsers.php", { credentials: "include" })
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(usersData => {
|
||||||
|
const users = Array.isArray(usersData) ? usersData : (usersData.users || []);
|
||||||
|
if (users.length === 0) {
|
||||||
|
listContainer.innerHTML = "<p>" + t("no_users_found") + "</p>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
users.forEach(user => {
|
||||||
|
// Skip admin users.
|
||||||
|
if ((user.role && user.role === "1") || user.username.toLowerCase() === "admin") return;
|
||||||
|
|
||||||
|
// Use stored permissions if available; otherwise fall back to defaults.
|
||||||
|
const defaultPerm = {
|
||||||
|
folderOnly: false,
|
||||||
|
readOnly: false,
|
||||||
|
disableUpload: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normalize the username key to match server storage (e.g., lowercase)
|
||||||
|
const usernameKey = user.username.toLowerCase();
|
||||||
|
|
||||||
|
const userPerm = (permissionsData && typeof permissionsData === "object" && (usernameKey in permissionsData))
|
||||||
|
? permissionsData[usernameKey]
|
||||||
|
: defaultPerm;
|
||||||
|
|
||||||
|
// Create a row for the user.
|
||||||
|
const row = document.createElement("div");
|
||||||
|
row.classList.add("user-permission-row");
|
||||||
|
row.setAttribute("data-username", user.username);
|
||||||
|
row.style.padding = "10px 0";
|
||||||
|
row.innerHTML = `
|
||||||
|
<div style="font-weight: bold; margin-bottom: 5px;">${user.username}</div>
|
||||||
|
<div style="display: flex; flex-direction: column; gap: 5px;">
|
||||||
|
<label style="display: flex; align-items: center; gap: 5px;">
|
||||||
|
<input type="checkbox" data-permission="folderOnly" ${userPerm.folderOnly ? "checked" : ""} />
|
||||||
|
${t("user_folder_only")}
|
||||||
|
</label>
|
||||||
|
<label style="display: flex; align-items: center; gap: 5px;">
|
||||||
|
<input type="checkbox" data-permission="readOnly" ${userPerm.readOnly ? "checked" : ""} />
|
||||||
|
${t("read_only")}
|
||||||
|
</label>
|
||||||
|
<label style="display: flex; align-items: center; gap: 5px;">
|
||||||
|
<input type="checkbox" data-permission="disableUpload" ${userPerm.disableUpload ? "checked" : ""} />
|
||||||
|
${t("disable_upload")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<hr style="margin-top: 10px; border: 0; border-bottom: 1px solid #ccc;">
|
||||||
|
`;
|
||||||
|
listContainer.appendChild(row);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
listContainer.innerHTML = "<p>" + t("error_loading_users") + "</p>";
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -15,10 +15,9 @@ import {
|
|||||||
openUserPanel,
|
openUserPanel,
|
||||||
openTOTPModal,
|
openTOTPModal,
|
||||||
closeTOTPModal,
|
closeTOTPModal,
|
||||||
openAdminPanel,
|
|
||||||
closeAdminPanel,
|
|
||||||
setLastLoginData
|
setLastLoginData
|
||||||
} from './authModals.js';
|
} from './authModals.js';
|
||||||
|
import { openAdminPanel } from './adminPanel.js';
|
||||||
|
|
||||||
// Production OIDC configuration (override via API as needed)
|
// Production OIDC configuration (override via API as needed)
|
||||||
const currentOIDCConfig = {
|
const currentOIDCConfig = {
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import { sendRequest } from './networkUtils.js';
|
|||||||
import { t, applyTranslations, setLocale } from './i18n.js';
|
import { t, applyTranslations, setLocale } from './i18n.js';
|
||||||
import { loadAdminConfigFunc } from './auth.js';
|
import { loadAdminConfigFunc } from './auth.js';
|
||||||
|
|
||||||
const version = "v1.2.8"; // Update this version string as needed
|
|
||||||
const adminTitle = `${t("admin_panel")} <small style="font-size: 12px; color: gray;">${version}</small>`;
|
|
||||||
|
|
||||||
let lastLoginData = null;
|
let lastLoginData = null;
|
||||||
export function setLastLoginData(data) {
|
export function setLastLoginData(data) {
|
||||||
@@ -544,539 +542,4 @@ export function closeTOTPModal(disable = true) {
|
|||||||
})
|
})
|
||||||
.catch(() => { showToast(t("error_disabling_totp_setting")); });
|
.catch(() => { showToast(t("error_disabling_totp_setting")); });
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Global variable to hold the initial state of the admin form.
|
|
||||||
let originalAdminConfig = {};
|
|
||||||
|
|
||||||
// Capture the initial state of the admin form fields.
|
|
||||||
function captureInitialAdminConfig() {
|
|
||||||
originalAdminConfig = {
|
|
||||||
headerTitle: document.getElementById("headerTitle").value.trim(),
|
|
||||||
oidcProviderUrl: document.getElementById("oidcProviderUrl").value.trim(),
|
|
||||||
oidcClientId: document.getElementById("oidcClientId").value.trim(),
|
|
||||||
oidcClientSecret: document.getElementById("oidcClientSecret").value.trim(),
|
|
||||||
oidcRedirectUri: document.getElementById("oidcRedirectUri").value.trim(),
|
|
||||||
disableFormLogin: document.getElementById("disableFormLogin").checked,
|
|
||||||
disableBasicAuth: document.getElementById("disableBasicAuth").checked,
|
|
||||||
disableOIDCLogin: document.getElementById("disableOIDCLogin").checked,
|
|
||||||
globalOtpauthUrl: document.getElementById("globalOtpauthUrl").value.trim()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare current values to the captured initial state.
|
|
||||||
function hasUnsavedChanges() {
|
|
||||||
return (
|
|
||||||
document.getElementById("headerTitle").value.trim() !== originalAdminConfig.headerTitle ||
|
|
||||||
document.getElementById("oidcProviderUrl").value.trim() !== originalAdminConfig.oidcProviderUrl ||
|
|
||||||
document.getElementById("oidcClientId").value.trim() !== originalAdminConfig.oidcClientId ||
|
|
||||||
document.getElementById("oidcClientSecret").value.trim() !== originalAdminConfig.oidcClientSecret ||
|
|
||||||
document.getElementById("oidcRedirectUri").value.trim() !== originalAdminConfig.oidcRedirectUri ||
|
|
||||||
document.getElementById("disableFormLogin").checked !== originalAdminConfig.disableFormLogin ||
|
|
||||||
document.getElementById("disableBasicAuth").checked !== originalAdminConfig.disableBasicAuth ||
|
|
||||||
document.getElementById("disableOIDCLogin").checked !== originalAdminConfig.disableOIDCLogin ||
|
|
||||||
document.getElementById("globalOtpauthUrl").value.trim() !== originalAdminConfig.globalOtpauthUrl
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use your custom confirmation modal.
|
|
||||||
function showCustomConfirmModal(message) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
// Get modal elements from DOM.
|
|
||||||
const modal = document.getElementById("customConfirmModal");
|
|
||||||
const messageElem = document.getElementById("confirmMessage");
|
|
||||||
const yesBtn = document.getElementById("confirmYesBtn");
|
|
||||||
const noBtn = document.getElementById("confirmNoBtn");
|
|
||||||
|
|
||||||
// Set the message in the modal.
|
|
||||||
messageElem.textContent = message;
|
|
||||||
modal.style.display = "block";
|
|
||||||
|
|
||||||
// Define event handlers.
|
|
||||||
function onYes() {
|
|
||||||
cleanup();
|
|
||||||
resolve(true);
|
|
||||||
}
|
|
||||||
function onNo() {
|
|
||||||
cleanup();
|
|
||||||
resolve(false);
|
|
||||||
}
|
|
||||||
// Remove event listeners and hide modal after choice.
|
|
||||||
function cleanup() {
|
|
||||||
yesBtn.removeEventListener("click", onYes);
|
|
||||||
noBtn.removeEventListener("click", onNo);
|
|
||||||
modal.style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
yesBtn.addEventListener("click", onYes);
|
|
||||||
noBtn.addEventListener("click", onNo);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function openAdminPanel() {
|
|
||||||
fetch("/api/admin/getConfig.php", { credentials: "include" })
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(config => {
|
|
||||||
if (config.header_title) {
|
|
||||||
document.querySelector(".header-title h1").textContent = config.header_title;
|
|
||||||
window.headerTitle = config.header_title || "FileRise";
|
|
||||||
}
|
|
||||||
if (config.oidc) Object.assign(window.currentOIDCConfig, config.oidc);
|
|
||||||
if (config.globalOtpauthUrl) window.currentOIDCConfig.globalOtpauthUrl = config.globalOtpauthUrl;
|
|
||||||
|
|
||||||
const isDarkMode = document.body.classList.contains("dark-mode");
|
|
||||||
const overlayBackground = isDarkMode ? "rgba(0,0,0,0.7)" : "rgba(0,0,0,0.3)";
|
|
||||||
const modalContentStyles = `
|
|
||||||
background: ${isDarkMode ? "#2c2c2c" : "#fff"};
|
|
||||||
color: ${isDarkMode ? "#e0e0e0" : "#000"};
|
|
||||||
padding: 20px;
|
|
||||||
max-width: 600px;
|
|
||||||
width: 90%;
|
|
||||||
border-radius: 8px;
|
|
||||||
position: relative;
|
|
||||||
overflow-y: auto;
|
|
||||||
max-height: 90vh;
|
|
||||||
border: ${isDarkMode ? "1px solid #444" : "1px solid #ccc"};
|
|
||||||
`;
|
|
||||||
|
|
||||||
let adminModal = document.getElementById("adminPanelModal");
|
|
||||||
|
|
||||||
if (!adminModal) {
|
|
||||||
adminModal = document.createElement("div");
|
|
||||||
adminModal.id = "adminPanelModal";
|
|
||||||
adminModal.style.cssText = `
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
background-color: ${overlayBackground};
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 3000;
|
|
||||||
`;
|
|
||||||
adminModal.innerHTML = `
|
|
||||||
<div class="modal-content" style="${modalContentStyles}">
|
|
||||||
<span id="closeAdminPanel" style="position: absolute; top: 10px; right: 10px; cursor: pointer; font-size: 24px;">×</span>
|
|
||||||
<h3>${adminTitle}</h3>
|
|
||||||
<form id="adminPanelForm">
|
|
||||||
<fieldset style="margin-bottom: 15px;">
|
|
||||||
<legend>${t("user_management")}</legend>
|
|
||||||
<div style="display: flex; gap: 10px;">
|
|
||||||
<button type="button" id="adminOpenAddUser" class="btn btn-success">${t("add_user")}</button>
|
|
||||||
<button type="button" id="adminOpenRemoveUser" class="btn btn-danger">${t("remove_user")}</button>
|
|
||||||
<button type="button" id="adminOpenUserPermissions" class="btn btn-secondary">${t("user_permissions")}</button>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset style="margin-bottom: 15px;">
|
|
||||||
<legend>Header Settings</legend>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="headerTitle">Header Title:</label>
|
|
||||||
<input type="text" id="headerTitle" class="form-control" value="${window.headerTitle}" />
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset style="margin-bottom: 15px;">
|
|
||||||
<legend>${t("login_options")}</legend>
|
|
||||||
<div class="form-group">
|
|
||||||
<input type="checkbox" id="disableFormLogin" />
|
|
||||||
<label for="disableFormLogin">${t("disable_login_form")}</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<input type="checkbox" id="disableBasicAuth" />
|
|
||||||
<label for="disableBasicAuth">${t("disable_basic_http_auth")}</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<input type="checkbox" id="disableOIDCLogin" />
|
|
||||||
<label for="disableOIDCLogin">${t("disable_oidc_login")}</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<!-- New WebDAV setting -->
|
|
||||||
<fieldset style="margin-bottom: 15px;">
|
|
||||||
<legend>WebDAV Access</legend>
|
|
||||||
<div class="form-group">
|
|
||||||
<input type="checkbox" id="enableWebDAV" />
|
|
||||||
<label for="enableWebDAV">Enable WebDAV</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<!-- End WebDAV setting -->
|
|
||||||
|
|
||||||
<!-- New Shared Max Upload Size setting -->
|
|
||||||
<fieldset style="margin-bottom: 15px;">
|
|
||||||
<legend>Shared Max Upload Size (bytes)</legend>
|
|
||||||
<div class="form-group">
|
|
||||||
<input type="number" id="sharedMaxUploadSize" class="form-control"
|
|
||||||
placeholder="e.g. 52428800" />
|
|
||||||
<small>Enter maximum bytes allowed for shared-folder uploads</small>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<!-- End Shared Max Upload Size setting -->
|
|
||||||
|
|
||||||
<fieldset style="margin-bottom: 15px;">
|
|
||||||
<legend>${t("oidc_configuration")}</legend>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="oidcProviderUrl">${t("oidc_provider_url")}:</label>
|
|
||||||
<input type="text" id="oidcProviderUrl" class="form-control" value="${window.currentOIDCConfig.providerUrl}" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="oidcClientId">${t("oidc_client_id")}:</label>
|
|
||||||
<input type="text" id="oidcClientId" class="form-control" value="${window.currentOIDCConfig.clientId}" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="oidcClientSecret">${t("oidc_client_secret")}:</label>
|
|
||||||
<input type="text" id="oidcClientSecret" class="form-control" value="${window.currentOIDCConfig.clientSecret}" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="oidcRedirectUri">${t("oidc_redirect_uri")}:</label>
|
|
||||||
<input type="text" id="oidcRedirectUri" class="form-control" value="${window.currentOIDCConfig.redirectUri}" />
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset style="margin-bottom: 15px;">
|
|
||||||
<legend>${t("global_totp_settings")}</legend>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="globalOtpauthUrl">${t("global_otpauth_url")}:</label>
|
|
||||||
<input type="text" id="globalOtpauthUrl" class="form-control" value="${window.currentOIDCConfig.globalOtpauthUrl || 'otpauth://totp/{label}?secret={secret}&issuer=FileRise'}" />
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<div style="display: flex; justify-content: space-between;">
|
|
||||||
<button type="button" id="cancelAdminSettings" class="btn btn-secondary">${t("cancel")}</button>
|
|
||||||
<button type="button" id="saveAdminSettings" class="btn btn-primary">${t("save_settings")}</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
document.body.appendChild(adminModal);
|
|
||||||
|
|
||||||
// Bind closing
|
|
||||||
document.getElementById("closeAdminPanel").addEventListener("click", closeAdminPanel);
|
|
||||||
adminModal.addEventListener("click", e => { if (e.target === adminModal) closeAdminPanel(); });
|
|
||||||
document.getElementById("cancelAdminSettings").addEventListener("click", closeAdminPanel);
|
|
||||||
|
|
||||||
// Bind other buttons
|
|
||||||
document.getElementById("adminOpenAddUser").addEventListener("click", () => {
|
|
||||||
toggleVisibility("addUserModal", true);
|
|
||||||
document.getElementById("newUsername").focus();
|
|
||||||
});
|
|
||||||
document.getElementById("adminOpenRemoveUser").addEventListener("click", () => {
|
|
||||||
if (typeof window.loadUserList === "function") window.loadUserList();
|
|
||||||
toggleVisibility("removeUserModal", true);
|
|
||||||
});
|
|
||||||
document.getElementById("adminOpenUserPermissions").addEventListener("click", () => {
|
|
||||||
openUserPermissionsModal();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Save handler
|
|
||||||
document.getElementById("saveAdminSettings").addEventListener("click", () => {
|
|
||||||
const disableFormLoginCheckbox = document.getElementById("disableFormLogin");
|
|
||||||
const disableBasicAuthCheckbox = document.getElementById("disableBasicAuth");
|
|
||||||
const disableOIDCLoginCheckbox = document.getElementById("disableOIDCLogin");
|
|
||||||
const enableWebDAVCheckbox = document.getElementById("enableWebDAV");
|
|
||||||
const sharedMaxUploadSizeInput = document.getElementById("sharedMaxUploadSize");
|
|
||||||
|
|
||||||
const totalDisabled = [disableFormLoginCheckbox, disableBasicAuthCheckbox, disableOIDCLoginCheckbox]
|
|
||||||
.filter(cb => cb.checked).length;
|
|
||||||
if (totalDisabled === 3) {
|
|
||||||
showToast(t("at_least_one_login_method"));
|
|
||||||
disableOIDCLoginCheckbox.checked = false;
|
|
||||||
localStorage.setItem("disableOIDCLogin", "false");
|
|
||||||
if (typeof window.updateLoginOptionsUI === "function") {
|
|
||||||
window.updateLoginOptionsUI({
|
|
||||||
disableFormLogin: disableFormLoginCheckbox.checked,
|
|
||||||
disableBasicAuth: disableBasicAuthCheckbox.checked,
|
|
||||||
disableOIDCLogin: disableOIDCLoginCheckbox.checked
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newHeaderTitle = document.getElementById("headerTitle").value.trim();
|
|
||||||
const newOIDCConfig = {
|
|
||||||
providerUrl: document.getElementById("oidcProviderUrl").value.trim(),
|
|
||||||
clientId: document.getElementById("oidcClientId").value.trim(),
|
|
||||||
clientSecret: document.getElementById("oidcClientSecret").value.trim(),
|
|
||||||
redirectUri: document.getElementById("oidcRedirectUri").value.trim()
|
|
||||||
};
|
|
||||||
const disableFormLogin = disableFormLoginCheckbox.checked;
|
|
||||||
const disableBasicAuth = disableBasicAuthCheckbox.checked;
|
|
||||||
const disableOIDCLogin = disableOIDCLoginCheckbox.checked;
|
|
||||||
const enableWebDAV = enableWebDAVCheckbox.checked;
|
|
||||||
const sharedMaxUploadSize = parseInt(sharedMaxUploadSizeInput.value, 10) || 0;
|
|
||||||
const globalOtpauthUrl = document.getElementById("globalOtpauthUrl").value.trim();
|
|
||||||
|
|
||||||
sendRequest("/api/admin/updateConfig.php", "POST", {
|
|
||||||
header_title: newHeaderTitle,
|
|
||||||
oidc: newOIDCConfig,
|
|
||||||
disableFormLogin,
|
|
||||||
disableBasicAuth,
|
|
||||||
disableOIDCLogin,
|
|
||||||
enableWebDAV,
|
|
||||||
sharedMaxUploadSize,
|
|
||||||
globalOtpauthUrl
|
|
||||||
}, { "X-CSRF-Token": window.csrfToken })
|
|
||||||
.then(response => {
|
|
||||||
if (response.success) {
|
|
||||||
showToast(t("settings_updated_successfully"));
|
|
||||||
localStorage.setItem("disableFormLogin", disableFormLogin);
|
|
||||||
localStorage.setItem("disableBasicAuth", disableBasicAuth);
|
|
||||||
localStorage.setItem("disableOIDCLogin", disableOIDCLogin);
|
|
||||||
localStorage.setItem("enableWebDAV", enableWebDAV);
|
|
||||||
localStorage.setItem("sharedMaxUploadSize", sharedMaxUploadSize);
|
|
||||||
if (typeof window.updateLoginOptionsUI === "function") {
|
|
||||||
window.updateLoginOptionsUI({
|
|
||||||
disableFormLogin,
|
|
||||||
disableBasicAuth,
|
|
||||||
disableOIDCLogin
|
|
||||||
});
|
|
||||||
}
|
|
||||||
captureInitialAdminConfig();
|
|
||||||
closeAdminPanel();
|
|
||||||
loadAdminConfigFunc();
|
|
||||||
} else {
|
|
||||||
showToast(t("error_updating_settings") + ": " + (response.error || t("unknown_error")));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => { });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Enforce login option constraints.
|
|
||||||
const disableFormLoginCheckbox = document.getElementById("disableFormLogin");
|
|
||||||
const disableBasicAuthCheckbox = document.getElementById("disableBasicAuth");
|
|
||||||
const disableOIDCLoginCheckbox = document.getElementById("disableOIDCLogin");
|
|
||||||
function enforceLoginOptionConstraint(changedCheckbox) {
|
|
||||||
const totalDisabled = [disableFormLoginCheckbox, disableBasicAuthCheckbox, disableOIDCLoginCheckbox]
|
|
||||||
.filter(cb => cb.checked).length;
|
|
||||||
if (changedCheckbox.checked && totalDisabled === 3) {
|
|
||||||
showToast(t("at_least_one_login_method"));
|
|
||||||
changedCheckbox.checked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
disableFormLoginCheckbox.addEventListener("change", function () { enforceLoginOptionConstraint(this); });
|
|
||||||
disableBasicAuthCheckbox.addEventListener("change", function () { enforceLoginOptionConstraint(this); });
|
|
||||||
disableOIDCLoginCheckbox.addEventListener("change", function () { enforceLoginOptionConstraint(this); });
|
|
||||||
|
|
||||||
// Initial checkbox and input states
|
|
||||||
document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true;
|
|
||||||
document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true;
|
|
||||||
document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true;
|
|
||||||
document.getElementById("enableWebDAV").checked = config.enableWebDAV === true;
|
|
||||||
document.getElementById("sharedMaxUploadSize").value = config.sharedMaxUploadSize || "";
|
|
||||||
|
|
||||||
captureInitialAdminConfig();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Update existing modal and show
|
|
||||||
adminModal.style.backgroundColor = overlayBackground;
|
|
||||||
const modalContent = adminModal.querySelector(".modal-content");
|
|
||||||
if (modalContent) {
|
|
||||||
modalContent.style.background = isDarkMode ? "#2c2c2c" : "#fff";
|
|
||||||
modalContent.style.color = isDarkMode ? "#e0e0e0" : "#000";
|
|
||||||
modalContent.style.border = isDarkMode ? "1px solid #444" : "1px solid #ccc";
|
|
||||||
}
|
|
||||||
document.getElementById("oidcProviderUrl").value = window.currentOIDCConfig.providerUrl;
|
|
||||||
document.getElementById("oidcClientId").value = window.currentOIDCConfig.clientId;
|
|
||||||
document.getElementById("oidcClientSecret").value = window.currentOIDCConfig.clientSecret;
|
|
||||||
document.getElementById("oidcRedirectUri").value = window.currentOIDCConfig.redirectUri;
|
|
||||||
document.getElementById("globalOtpauthUrl").value = window.currentOIDCConfig.globalOtpauthUrl || 'otpauth://totp/{label}?secret={secret}&issuer=FileRise';
|
|
||||||
document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true;
|
|
||||||
document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true;
|
|
||||||
document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true;
|
|
||||||
document.getElementById("enableWebDAV").checked = config.enableWebDAV === true;
|
|
||||||
document.getElementById("sharedMaxUploadSize").value = config.sharedMaxUploadSize || "";
|
|
||||||
adminModal.style.display = "flex";
|
|
||||||
captureInitialAdminConfig();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
let adminModal = document.getElementById("adminPanelModal");
|
|
||||||
if (adminModal) {
|
|
||||||
adminModal.style.backgroundColor = "rgba(0,0,0,0.5)";
|
|
||||||
const modalContent = adminModal.querySelector(".modal-content");
|
|
||||||
if (modalContent) {
|
|
||||||
modalContent.style.background = "#fff";
|
|
||||||
modalContent.style.color = "#000";
|
|
||||||
modalContent.style.border = "1px solid #ccc";
|
|
||||||
}
|
|
||||||
document.getElementById("oidcProviderUrl").value = window.currentOIDCConfig.providerUrl;
|
|
||||||
document.getElementById("oidcClientId").value = window.currentOIDCConfig.clientId;
|
|
||||||
document.getElementById("oidcClientSecret").value = window.currentOIDCConfig.clientSecret;
|
|
||||||
document.getElementById("oidcRedirectUri").value = window.currentOIDCConfig.redirectUri;
|
|
||||||
document.getElementById("globalOtpauthUrl").value = window.currentOIDCConfig.globalOtpauthUrl || 'otpauth://totp/{label}?secret={secret}&issuer=FileRise';
|
|
||||||
document.getElementById("disableFormLogin").checked = localStorage.getItem("disableFormLogin") === "true";
|
|
||||||
document.getElementById("disableBasicAuth").checked = localStorage.getItem("disableBasicAuth") === "true";
|
|
||||||
document.getElementById("disableOIDCLogin").checked = localStorage.getItem("disableOIDCLogin") === "true";
|
|
||||||
document.getElementById("enableWebDAV").checked = localStorage.getItem("enableWebDAV") === "true";
|
|
||||||
document.getElementById("sharedMaxUploadSize").value = localStorage.getItem("sharedMaxUploadSize") || "";
|
|
||||||
adminModal.style.display = "flex";
|
|
||||||
captureInitialAdminConfig();
|
|
||||||
} else {
|
|
||||||
openAdminPanel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function closeAdminPanel() {
|
|
||||||
if (hasUnsavedChanges()) {
|
|
||||||
const userConfirmed = await showCustomConfirmModal(t("unsaved_changes_confirm"));
|
|
||||||
if (!userConfirmed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const adminModal = document.getElementById("adminPanelModal");
|
|
||||||
if (adminModal) adminModal.style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- New: User Permissions Modal ---
|
|
||||||
export function openUserPermissionsModal() {
|
|
||||||
let userPermissionsModal = document.getElementById("userPermissionsModal");
|
|
||||||
const isDarkMode = document.body.classList.contains("dark-mode");
|
|
||||||
const overlayBackground = isDarkMode ? "rgba(0,0,0,0.7)" : "rgba(0,0,0,0.3)";
|
|
||||||
const modalContentStyles = `
|
|
||||||
background: ${isDarkMode ? "#2c2c2c" : "#fff"};
|
|
||||||
color: ${isDarkMode ? "#e0e0e0" : "#000"};
|
|
||||||
padding: 20px;
|
|
||||||
max-width: 500px;
|
|
||||||
width: 90%;
|
|
||||||
border-radius: 8px;
|
|
||||||
position: relative;
|
|
||||||
`;
|
|
||||||
|
|
||||||
if (!userPermissionsModal) {
|
|
||||||
userPermissionsModal = document.createElement("div");
|
|
||||||
userPermissionsModal.id = "userPermissionsModal";
|
|
||||||
userPermissionsModal.style.cssText = `
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
background-color: ${overlayBackground};
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 3500;
|
|
||||||
`;
|
|
||||||
userPermissionsModal.innerHTML = `
|
|
||||||
<div class="modal-content" style="${modalContentStyles}">
|
|
||||||
<span id="closeUserPermissionsModal" style="position: absolute; top: 10px; right: 10px; cursor: pointer; font-size: 24px;">×</span>
|
|
||||||
<h3>${t("user_permissions")}</h3>
|
|
||||||
<div id="userPermissionsList" style="max-height: 300px; overflow-y: auto; margin-bottom: 15px;">
|
|
||||||
<!-- User rows will be loaded here -->
|
|
||||||
</div>
|
|
||||||
<div style="display: flex; justify-content: flex-end; gap: 10px;">
|
|
||||||
<button type="button" id="cancelUserPermissionsBtn" class="btn btn-secondary">${t("cancel")}</button>
|
|
||||||
<button type="button" id="saveUserPermissionsBtn" class="btn btn-primary">${t("save_permissions")}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
document.body.appendChild(userPermissionsModal);
|
|
||||||
document.getElementById("closeUserPermissionsModal").addEventListener("click", () => {
|
|
||||||
userPermissionsModal.style.display = "none";
|
|
||||||
});
|
|
||||||
document.getElementById("cancelUserPermissionsBtn").addEventListener("click", () => {
|
|
||||||
userPermissionsModal.style.display = "none";
|
|
||||||
});
|
|
||||||
document.getElementById("saveUserPermissionsBtn").addEventListener("click", () => {
|
|
||||||
// Collect permissions data from each user row.
|
|
||||||
const rows = userPermissionsModal.querySelectorAll(".user-permission-row");
|
|
||||||
const permissionsData = [];
|
|
||||||
rows.forEach(row => {
|
|
||||||
const username = row.getAttribute("data-username");
|
|
||||||
const folderOnlyCheckbox = row.querySelector("input[data-permission='folderOnly']");
|
|
||||||
const readOnlyCheckbox = row.querySelector("input[data-permission='readOnly']");
|
|
||||||
const disableUploadCheckbox = row.querySelector("input[data-permission='disableUpload']");
|
|
||||||
permissionsData.push({
|
|
||||||
username,
|
|
||||||
folderOnly: folderOnlyCheckbox.checked,
|
|
||||||
readOnly: readOnlyCheckbox.checked,
|
|
||||||
disableUpload: disableUploadCheckbox.checked
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// Send the permissionsData to the server.
|
|
||||||
sendRequest("/api/updateUserPermissions.php", "POST", { permissions: permissionsData }, { "X-CSRF-Token": window.csrfToken })
|
|
||||||
.then(response => {
|
|
||||||
if (response.success) {
|
|
||||||
showToast(t("user_permissions_updated_successfully"));
|
|
||||||
userPermissionsModal.style.display = "none";
|
|
||||||
} else {
|
|
||||||
showToast(t("error_updating_permissions") + ": " + (response.error || t("unknown_error")));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
showToast(t("error_updating_permissions"));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
userPermissionsModal.style.display = "flex";
|
|
||||||
}
|
|
||||||
// Load the list of users into the modal.
|
|
||||||
loadUserPermissionsList();
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadUserPermissionsList() {
|
|
||||||
const listContainer = document.getElementById("userPermissionsList");
|
|
||||||
if (!listContainer) return;
|
|
||||||
listContainer.innerHTML = "";
|
|
||||||
|
|
||||||
// First, fetch the current permissions from the server.
|
|
||||||
fetch("/api/getUserPermissions.php", { credentials: "include" })
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(permissionsData => {
|
|
||||||
// Then, fetch the list of users.
|
|
||||||
return fetch("/api/getUsers.php", { credentials: "include" })
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(usersData => {
|
|
||||||
const users = Array.isArray(usersData) ? usersData : (usersData.users || []);
|
|
||||||
if (users.length === 0) {
|
|
||||||
listContainer.innerHTML = "<p>" + t("no_users_found") + "</p>";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
users.forEach(user => {
|
|
||||||
// Skip admin users.
|
|
||||||
if ((user.role && user.role === "1") || user.username.toLowerCase() === "admin") return;
|
|
||||||
|
|
||||||
// Use stored permissions if available; otherwise fall back to defaults.
|
|
||||||
const defaultPerm = {
|
|
||||||
folderOnly: false,
|
|
||||||
readOnly: false,
|
|
||||||
disableUpload: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Normalize the username key to match server storage (e.g., lowercase)
|
|
||||||
const usernameKey = user.username.toLowerCase();
|
|
||||||
|
|
||||||
const userPerm = (permissionsData && typeof permissionsData === "object" && (usernameKey in permissionsData))
|
|
||||||
? permissionsData[usernameKey]
|
|
||||||
: defaultPerm;
|
|
||||||
|
|
||||||
// Create a row for the user.
|
|
||||||
const row = document.createElement("div");
|
|
||||||
row.classList.add("user-permission-row");
|
|
||||||
row.setAttribute("data-username", user.username);
|
|
||||||
row.style.padding = "10px 0";
|
|
||||||
row.innerHTML = `
|
|
||||||
<div style="font-weight: bold; margin-bottom: 5px;">${user.username}</div>
|
|
||||||
<div style="display: flex; flex-direction: column; gap: 5px;">
|
|
||||||
<label style="display: flex; align-items: center; gap: 5px;">
|
|
||||||
<input type="checkbox" data-permission="folderOnly" ${userPerm.folderOnly ? "checked" : ""} />
|
|
||||||
${t("user_folder_only")}
|
|
||||||
</label>
|
|
||||||
<label style="display: flex; align-items: center; gap: 5px;">
|
|
||||||
<input type="checkbox" data-permission="readOnly" ${userPerm.readOnly ? "checked" : ""} />
|
|
||||||
${t("read_only")}
|
|
||||||
</label>
|
|
||||||
<label style="display: flex; align-items: center; gap: 5px;">
|
|
||||||
<input type="checkbox" data-permission="disableUpload" ${userPerm.disableUpload ? "checked" : ""} />
|
|
||||||
${t("disable_upload")}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<hr style="margin-top: 10px; border: 0; border-bottom: 1px solid #ccc;">
|
|
||||||
`;
|
|
||||||
listContainer.appendChild(row);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
listContainer.innerHTML = "<p>" + t("error_loading_users") + "</p>";
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
@@ -182,6 +182,22 @@ const translations = {
|
|||||||
"switch_to_light_mode": "Switch to light mode",
|
"switch_to_light_mode": "Switch to light mode",
|
||||||
"switch_to_dark_mode": "Switch to dark mode",
|
"switch_to_dark_mode": "Switch to dark mode",
|
||||||
|
|
||||||
|
// Admin Panel
|
||||||
|
"header_settings": "Header Settings",
|
||||||
|
"shared_max_upload_size_bytes_title": "Shared Max Upload Size",
|
||||||
|
"shared_max_upload_size_bytes": "Shared Max Upload Size (bytes)",
|
||||||
|
"max_bytes_shared_uploads_note": "Enter maximum bytes allowed for shared-folder uploads",
|
||||||
|
"manage_shared_links": "Manage Shared Links",
|
||||||
|
"folder_shares": "Folder Shares",
|
||||||
|
"file_shares": "File Shares",
|
||||||
|
"loading": "Loading…",
|
||||||
|
"error_loading_share_links": "Error loading share links",
|
||||||
|
"share_deleted_successfully": "Share deleted successfully",
|
||||||
|
"error_deleting_share": "Error deleting share",
|
||||||
|
"password_protected": "Password protected",
|
||||||
|
"no_shared_links_available": "No shared links available",
|
||||||
|
|
||||||
|
|
||||||
// NEW KEYS ADDED FOR ADMIN, USER PANELS, AND TOTP MODALS:
|
// NEW KEYS ADDED FOR ADMIN, USER PANELS, AND TOTP MODALS:
|
||||||
"admin_panel": "Admin Panel",
|
"admin_panel": "Admin Panel",
|
||||||
"user_panel": "User Panel",
|
"user_panel": "User Panel",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// src/controllers/adminController.php
|
// src/controllers/AdminController.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/models/AdminModel.php';
|
require_once PROJECT_ROOT . '/src/models/AdminModel.php';
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// src/controllers/authController.php
|
// src/controllers/AuthController.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/models/AuthModel.php';
|
require_once PROJECT_ROOT . '/src/models/AuthModel.php';
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// src/controllers/fileController.php
|
// src/controllers/FileController.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/models/FileModel.php';
|
require_once PROJECT_ROOT . '/src/models/FileModel.php';
|
||||||
@@ -1571,4 +1571,34 @@ class FileController
|
|||||||
echo json_encode($result);
|
echo json_encode($result);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /api/file/getShareLinks.php
|
||||||
|
*/
|
||||||
|
public function getShareLinks()
|
||||||
|
{
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
$shareFile = FileModel::getAllShareLinks();
|
||||||
|
echo json_encode($shareFile, JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /api/file/deleteShareLink.php
|
||||||
|
*/
|
||||||
|
public function deleteShareLink()
|
||||||
|
{
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
$token = $_POST['token'] ?? '';
|
||||||
|
if (!$token) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'No token provided']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$deleted = FileModel::deleteShareLink($token);
|
||||||
|
if ($deleted) {
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Not found']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// src/controllers/folderController.php
|
// src/controllers/FolderController.php
|
||||||
|
|
||||||
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';
|
||||||
@@ -76,7 +76,11 @@ class FolderController
|
|||||||
$username = $_SESSION['username'] ?? '';
|
$username = $_SESSION['username'] ?? '';
|
||||||
$userPermissions = loadUserPermissions($username);
|
$userPermissions = loadUserPermissions($username);
|
||||||
if ($username && isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) {
|
if ($username && isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) {
|
||||||
echo json_encode(["error" => "Read-only users are not allowed to create folders."]);
|
http_response_code(403);
|
||||||
|
echo json_encode([
|
||||||
|
"success" => false,
|
||||||
|
"error" => "Read-only users are not allowed to create folders."
|
||||||
|
]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1074,4 +1078,34 @@ class FolderController
|
|||||||
header("Location: " . $redirectUrl);
|
header("Location: " . $redirectUrl);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /api/folder/getShareFolderLinks.php
|
||||||
|
*/
|
||||||
|
public function getShareFolderLinks()
|
||||||
|
{
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
$links = FolderModel::getAllShareFolderLinks();
|
||||||
|
echo json_encode($links, JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /api/folder/deleteShareFolderLink.php
|
||||||
|
*/
|
||||||
|
public function deleteShareFolderLink()
|
||||||
|
{
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
$token = $_POST['token'] ?? '';
|
||||||
|
if (!$token) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'No token provided']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$deleted = FolderModel::deleteShareFolderLink($token);
|
||||||
|
if ($deleted) {
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Not found']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// src/controllers/uploadController.php
|
// src/controllers/UploadController.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/models/UploadModel.php';
|
require_once PROJECT_ROOT . '/src/models/UploadModel.php';
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// userController.php located in src/controllers/
|
// UserController.php located in src/controllers/
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once PROJECT_ROOT . '/src/models/UserModel.php';
|
require_once PROJECT_ROOT . '/src/models/UserModel.php';
|
||||||
@@ -1253,4 +1253,29 @@ public static function saveFile(string $folder, string $fileName, $content, ?str
|
|||||||
|
|
||||||
return ["files" => $fileList, "globalTags" => $globalTags];
|
return ["files" => $fileList, "globalTags" => $globalTags];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getAllShareLinks(): array
|
||||||
|
{
|
||||||
|
$shareFile = META_DIR . "share_links.json";
|
||||||
|
if (!file_exists($shareFile)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$links = json_decode(file_get_contents($shareFile), true);
|
||||||
|
return is_array($links) ? $links : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function deleteShareLink(string $token): bool
|
||||||
|
{
|
||||||
|
$shareFile = META_DIR . "share_links.json";
|
||||||
|
if (!file_exists($shareFile)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$links = json_decode(file_get_contents($shareFile), true);
|
||||||
|
if (!is_array($links) || !isset($links[$token])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
unset($links[$token]);
|
||||||
|
file_put_contents($shareFile, json_encode($links, JSON_PRETTY_PRINT));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -570,4 +570,29 @@ class FolderModel
|
|||||||
|
|
||||||
return ["success" => "File uploaded successfully.", "newFilename" => $newFilename];
|
return ["success" => "File uploaded successfully.", "newFilename" => $newFilename];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getAllShareFolderLinks(): array
|
||||||
|
{
|
||||||
|
$shareFile = META_DIR . "share_folder_links.json";
|
||||||
|
if (!file_exists($shareFile)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$links = json_decode(file_get_contents($shareFile), true);
|
||||||
|
return is_array($links) ? $links : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function deleteShareFolderLink(string $token): bool
|
||||||
|
{
|
||||||
|
$shareFile = META_DIR . "share_folder_links.json";
|
||||||
|
if (!file_exists($shareFile)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$links = json_decode(file_get_contents($shareFile), true);
|
||||||
|
if (!is_array($links) || !isset($links[$token])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
unset($links[$token]);
|
||||||
|
file_put_contents($shareFile, json_encode($links, JSON_PRETTY_PRINT));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user