diff --git a/client/package-lock.json b/client/package-lock.json
index 0a3109f9..8e0bdf51 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -12,6 +12,7 @@
"date-and-time": "^2.4.2",
"file-saver": "^2.0.5",
"filepond": "^4.30.4",
+ "filepond-plugin-file-validate-type": "^1.2.9",
"js-cookie": "^3.0.1",
"jszip": "^3.10.1",
"react": "^18.2.0",
@@ -966,6 +967,15 @@
"integrity": "sha512-FCwsMvG9iiEs6uobdDrTaKsCgsqys0NuLgPPD8n37AYVYBiiDkrPkk9MSIU5rT2FahYcL1bScYI9huIPtlzqyA==",
"license": "MIT"
},
+ "node_modules/filepond-plugin-file-validate-type": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/filepond-plugin-file-validate-type/-/filepond-plugin-file-validate-type-1.2.9.tgz",
+ "integrity": "sha512-Tzv07aNdZvjUXDRA3XL16QMEvh6llDrXlcZ6W0eTHQ+taHaVg/JKJTFs/AViO+6ZcpPCcQStbhYEL2HoS+vldw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "filepond": ">=1.x <5.x"
+ }
+ },
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -2506,6 +2516,12 @@
"resolved": "https://registry.npmjs.org/filepond/-/filepond-4.30.4.tgz",
"integrity": "sha512-FCwsMvG9iiEs6uobdDrTaKsCgsqys0NuLgPPD8n37AYVYBiiDkrPkk9MSIU5rT2FahYcL1bScYI9huIPtlzqyA=="
},
+ "filepond-plugin-file-validate-type": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/filepond-plugin-file-validate-type/-/filepond-plugin-file-validate-type-1.2.9.tgz",
+ "integrity": "sha512-Tzv07aNdZvjUXDRA3XL16QMEvh6llDrXlcZ6W0eTHQ+taHaVg/JKJTFs/AViO+6ZcpPCcQStbhYEL2HoS+vldw==",
+ "requires": {}
+ },
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
diff --git a/client/package.json b/client/package.json
index 6c4f5a20..2f0abce2 100644
--- a/client/package.json
+++ b/client/package.json
@@ -13,6 +13,7 @@
"date-and-time": "^2.4.2",
"file-saver": "^2.0.5",
"filepond": "^4.30.4",
+ "filepond-plugin-file-validate-type": "^1.2.9",
"js-cookie": "^3.0.1",
"jszip": "^3.10.1",
"react": "^18.2.0",
diff --git a/client/src/screens/browse/components/year-info/year-options.jsx b/client/src/screens/browse/components/year-info/year-options.jsx
index f40996cd..ad7fff86 100644
--- a/client/src/screens/browse/components/year-info/year-options.jsx
+++ b/client/src/screens/browse/components/year-info/year-options.jsx
@@ -1,6 +1,6 @@
const currentDate = new Date();
const currentMonth = currentDate.getMonth() + 1;
-const acadYear = (currentMonth >= 1 && currentMonth <= 5) ? currentDate.getFullYear() - 1 : currentDate.getFullYear();
+const acadYear = currentDate.getFullYear();
const Yroptions = ({
course,
diff --git a/client/src/screens/contributions/index.jsx b/client/src/screens/contributions/index.jsx
index c38969aa..4a51cc7f 100644
--- a/client/src/screens/contributions/index.jsx
+++ b/client/src/screens/contributions/index.jsx
@@ -2,6 +2,7 @@ import Wrapper from "./components/wrapper";
import SectionC from "./components/sectionC";
import axios from "axios";
import { FilePond, registerPlugin } from "react-filepond";
+import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type';
import "filepond/dist/filepond.min.css";
import { useEffect, useRef, useState } from "react";
import "./styles.scss";
@@ -18,6 +19,9 @@ import {
RefreshCurrentFolder,
ChangeCurrentYearData,
} from "../../actions/filebrowser_actions";
+
+registerPlugin(FilePondPluginFileValidateType);
+
const Contributions = () => {
const uploadedBy = useSelector((state) => state.user.user._id);
const userName = useSelector((state) => state.user.user.name);
@@ -27,6 +31,15 @@ const Contributions = () => {
const code = currentFolder?.course;
const [contributionId, setContributionId] = useState("");
const dispatch = useDispatch();
+ const allowed_file_types = [
+ 'application/pdf',
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ 'video/mp4',
+ 'video/x-matroska', // mkv
+ 'image/png',
+ 'image/jpeg'
+ ]
+
useEffect(() => {
setContributionId(uuidv4());
}, []);
@@ -35,26 +48,25 @@ const Contributions = () => {
const [isUploading, setIsUploading] = useState(false);
// const [contributionId, setContributionId] = useState("");
-
+ const [files, setFiles] = useState([]);
let pond = useRef();
const handleUpdateFiles = (fileItems) => {
+ setFiles(fileItems);
if (fileItems.length > 0) setSubmitEnabled(true);
else setSubmitEnabled(false);
};
async function handleSubmit() {
- if (isUploading) return;
+ if (isUploading || files.length === 0) return;
const collection = document.getElementsByClassName("contri");
const contributionSection = collection[0];
- // console.log(toggle);
- // console.log(isAnoynmous);
try {
setIsUploading(true);
setSubmitEnabled(false);
- // console.log(resp);
+
let resp = await CreateNewContribution({
parentFolder: currentFolder._id,
courseCode: currentFolder.course,
@@ -63,22 +75,37 @@ const Contributions = () => {
contributionId,
uploadedBy,
});
- // console.log(resp);
- await pond.current.processFiles();
+
+ const formData = new FormData();
+ files.forEach((fileItem, index) => {
+ formData.append(`files`, fileItem.file);
+ });
+
+ pond.current.processFiles();
+
+ await fetch(`${server}/api/contribution/upload`, {
+ method: 'POST',
+ headers: {
+ "contribution-id": contributionId,
+ username: userName,
+ },
+ body: formData
+ });
+
pond.current.removeFiles();
contributionSection.classList.remove("show");
toast.success("Files uploaded successfully!");
setContributionId(uuidv4());
setSubmitEnabled(true);
+
} catch (error) {
setSubmitEnabled(true);
contributionSection.classList.remove("show");
- toast.error("Upload failed. Please try again!");
- // console.log(error);
} finally {
setIsUploading(false);
}
+
//refresh the course in session storage to include the new file.
try {
let loadingCourseToastId = toast.loading("Loading course data...");
@@ -114,21 +141,20 @@ const Contributions = () => {
allowMultiple={true}
onupdatefiles={handleUpdateFiles}
maxFiles={40}
- server={{
- url: `${server}/api/contribution/upload`,
- process: {
- headers: {
- "contribution-id": contributionId,
- username: userName,
- },
- },
- }}
instantUpload={false}
- allowProcess={false}
+ acceptedFileTypes={allowed_file_types}
+ fileValidateTypeLabelExpectedTypes="Expects PDF and PowerPoint files"
+ allowProcess={true}
allowRevert={false}
ref={(ref) => {
pond.current = ref;
}}
+ server={{
+ process: (fieldName, file, metadata, load) => {
+ load();
+ },
+ revert: null,
+ }}
/>
diff --git a/client/yarn.lock b/client/yarn.lock
index a4ff28eb..1907d043 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -540,7 +540,12 @@ file-saver@^2.0.5:
resolved "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz"
integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
-filepond@^4.30.4, "filepond@>=3.7.x < 5.x":
+filepond-plugin-file-validate-type@^1.2.9:
+ version "1.2.9"
+ resolved "https://registry.npmjs.org/filepond-plugin-file-validate-type/-/filepond-plugin-file-validate-type-1.2.9.tgz"
+ integrity sha512-Tzv07aNdZvjUXDRA3XL16QMEvh6llDrXlcZ6W0eTHQ+taHaVg/JKJTFs/AViO+6ZcpPCcQStbhYEL2HoS+vldw==
+
+filepond@^4.30.4, "filepond@>=1.x <5.x", "filepond@>=3.7.x < 5.x":
version "4.30.4"
resolved "https://registry.npmjs.org/filepond/-/filepond-4.30.4.tgz"
integrity sha512-FCwsMvG9iiEs6uobdDrTaKsCgsqys0NuLgPPD8n37AYVYBiiDkrPkk9MSIU5rT2FahYcL1bScYI9huIPtlzqyA==
diff --git a/server/external/mobile/055228a2aadf749161d2abc0d4860165 b/server/external/mobile/055228a2aadf749161d2abc0d4860165
deleted file mode 100644
index 940137c0..00000000
Binary files a/server/external/mobile/055228a2aadf749161d2abc0d4860165 and /dev/null differ
diff --git a/server/external/mobile/2a1d8ff81af7ed2cf0d4f5239f146b11 b/server/external/mobile/2a1d8ff81af7ed2cf0d4f5239f146b11
deleted file mode 100644
index 940137c0..00000000
Binary files a/server/external/mobile/2a1d8ff81af7ed2cf0d4f5239f146b11 and /dev/null differ
diff --git a/server/external/mobile/5b35bd641d976b5cb1e4facd9798e068 b/server/external/mobile/5b35bd641d976b5cb1e4facd9798e068
deleted file mode 100644
index 940137c0..00000000
Binary files a/server/external/mobile/5b35bd641d976b5cb1e4facd9798e068 and /dev/null differ
diff --git a/server/external/mobile/aaf8c0bd472c9c22ae4a42d0e60d6704 b/server/external/mobile/aaf8c0bd472c9c22ae4a42d0e60d6704
deleted file mode 100644
index 940137c0..00000000
Binary files a/server/external/mobile/aaf8c0bd472c9c22ae4a42d0e60d6704 and /dev/null differ
diff --git a/server/external/mobile/d81f2d3942919762e6eee446b00dd2ce b/server/external/mobile/d81f2d3942919762e6eee446b00dd2ce
deleted file mode 100644
index 940137c0..00000000
Binary files a/server/external/mobile/d81f2d3942919762e6eee446b00dd2ce and /dev/null differ
diff --git a/server/modules/contribution/contribution.controller.js b/server/modules/contribution/contribution.controller.js
index 3151b8bd..a5034689 100644
--- a/server/modules/contribution/contribution.controller.js
+++ b/server/modules/contribution/contribution.controller.js
@@ -51,44 +51,62 @@ async function GetAllContributions(req, res, next) {
async function HandleFileUpload(req, res, next) {
console.log("Handling File Upload");
const contributionId = req.headers["contribution-id"];
- const files = req.files; // Changed from req.file to req.files for array handling
+ const username = req.headers.username;
+ const files = req.files;
- // Check if files were uploaded
if (!files || files.length === 0) {
return res.status(400).json({ error: "No files were uploaded" });
}
- // Handle multiple files
const uploadedFiles = [];
- for (const file of files) {
- // Files names
- let initialPath = file.path;
- let newFilename = file.filename;
- let originalFilename = file.originalname;
-
- let wordArr = originalFilename.split(".");
- let fileExtension = wordArr[wordArr.length - 1];
- let finalFileName = "";
-
- for (let i = 0; i < wordArr.length - 1; i++) {
- finalFileName += wordArr[i];
+ try {
+ for (const file of files) {
+ // Files names
+ let initialPath = file.path;
+ let newFilename = file.filename;
+ let originalFilename = file.originalname;
+
+ let wordArr = originalFilename.split(".");
+ let fileExtension = wordArr[wordArr.length - 1];
+ let finalFileName = "";
+
+ for (let i = 0; i < wordArr.length - 1; i++) {
+ finalFileName += wordArr[i];
+ }
+ finalFileName += "~" + username;
+ finalFileName += "." + fileExtension;
+
+ const finalPath = initialPath.slice(0, initialPath.indexOf(newFilename));
+
+ await fs.promises.rename(finalPath + newFilename, finalPath + finalFileName);
+ const fileId = await UploadFile(contributionId, finalPath, finalFileName);
+
+ if (fileId) {
+ await HandleFileToDB(contributionId, fileId);
+ uploadedFiles.push({
+ fileId,
+ originalName: originalFilename,
+ finalName: finalFileName
+ });
+ }
+
+ await fs.promises.unlink(finalPath + finalFileName);
}
- finalFileName += "~" + req.headers.username;
- finalFileName += "." + fileExtension;
- const finalPath = initialPath.slice(0, initialPath.indexOf(newFilename));
+ console.log(`Successfully uploaded ${uploadedFiles.length} files`);
+ return res.json({
+ files: uploadedFiles,
+ count: uploadedFiles.length
+ });
- await fs.promises.rename(finalPath + newFilename, finalPath + finalFileName);
- const fileId = await UploadFile(contributionId, finalPath, finalFileName);
- if (fileId) {
- await HandleFileToDB(contributionId, fileId);
- uploadedFiles.push({ fileId, originalName: originalFilename });
- }
- await fs.promises.unlink(finalPath + finalFileName);
+ } catch (error) {
+ console.error("File upload error:", error);
+ return res.status(500).json({
+ error: "File upload failed",
+ details: error.message
+ });
}
-
- return res.json({ files: uploadedFiles, count: uploadedFiles.length });
}
async function CreateNewContribution(req, res, next) {
diff --git a/server/modules/contribution/contribution.routes.js b/server/modules/contribution/contribution.routes.js
index 36725237..4fe97b20 100644
--- a/server/modules/contribution/contribution.routes.js
+++ b/server/modules/contribution/contribution.routes.js
@@ -10,7 +10,7 @@ router.get("/", isAuthenticated, ContributionController.GetMyContributions);
router.get("/all", ContributionController.GetAllContributions);
router.delete("/:contributionId", ContributionController.DeleteContribution);
router.post("/", catchAsync(ContributionController.CreateNewContribution));
-router.post("/upload", upload.array("file"), catchAsync(ContributionController.HandleFileUpload));
+router.post("/upload", upload.array("files"), catchAsync(ContributionController.HandleFileUpload));
router.post(
"/upload/mobile",
isAuthenticated,
diff --git a/server/onedrive-device-code.token b/server/onedrive-device-code.token
index 2b0c7266..54e65146 100644
--- a/server/onedrive-device-code.token
+++ b/server/onedrive-device-code.token
@@ -1 +1 @@
-SAQABIQEAAABVrSpeuWamRam2jAF1XRQEWGdKsPq8yryqwLbeStuwomr7_ecQpZI0SkTNkLul23HPrGog08llLH4HjavMgc7vHrYEowvnWD3KOJaGcH6aVC2LAH1STKBlfczrzJEPaELD27PsyOboCOUey0JLjYAkrVc5qSuJRvIFOBXHTO1HA9IT4AZeca7utAiXhPO56Pk2rXs45wLr6dAhpt0OUgHpIAA
\ No newline at end of file
+SAQABIQEAAABVrSpeuWamRam2jAF1XRQEWGdKsPq8yryqwLbeStuwomr7_ecQpZI0SkTNkLul23HPrGog08llLH4HjavMgc7vHrYEowvnWD3KOJaGcH6aVC2LAH1STKBlfczrzJEPaELD27PsyOboCOUey0JLjYAkrVc5qSuJRvIFOBXHTO1HA9IT4AZeca7utAiXhPO56Pk2rXs45wLr6dAhpt0OUgHpIAA
diff --git a/server/yarn.lock b/server/yarn.lock
index 586fa22f..4f24dbbb 100644
--- a/server/yarn.lock
+++ b/server/yarn.lock
@@ -2040,6 +2040,11 @@ fs.realpath@^1.0.0:
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+fsevents@~2.3.2:
+ version "2.3.2"
+ resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz"
+ integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
+
function-bind@^1.1.1, function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"