From 56a0b76c6def0f44f1d9eabedaef606244a122b3 Mon Sep 17 00:00:00 2001 From: Pasq G Date: Tue, 18 Feb 2025 19:00:27 +0100 Subject: [PATCH] =?UTF-8?q?Abans=20de=20la=20col=C2=B7lecci=C3=B3=20Avatar?= =?UTF-8?q?sRegistre?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .meteor/packages | 1 + .meteor/release | 2 +- .meteor/versions | 8 +- imports/api/files.js | 275 ++++++++++++++++++ imports/api/lib/grid/createBucket.js | 6 + imports/api/lib/grid/createObjectId.js | 3 + imports/api/lib/images.js | 110 +++++++ imports/api/lib/persons.js | 62 ++++ imports/ui/BarraNav/UserStat.jsx | 5 +- imports/ui/Login.tsx | 80 ++++- imports/ui/files/AvatarFileUpload.jsx | 226 ++++++++++++++ imports/ui/files/AvatarIndividualFile.jsx | 82 ++++++ imports/ui/files/FileUpload.jsx | 204 +++++++++++++ imports/ui/files/FileUploadAdmin.jsx | 208 +++++++++++++ imports/ui/files/FileUploadEnt.jsx | 203 +++++++++++++ imports/ui/files/FileUploadPOI.jsx | 235 +++++++++++++++ imports/ui/files/FileUploadStart.jsx | 196 +++++++++++++ imports/ui/files/FileUpload_abans.jsx | 188 ++++++++++++ imports/ui/files/FileUpload_ori.jsx | 186 ++++++++++++ imports/ui/files/Files.jsx | 34 +++ .../FileUploadCartellEvent.jsx | 217 ++++++++++++++ .../IndividualFileCartellEvent.jsx | 84 ++++++ imports/ui/files/FolderTreeFileUpload.jsx | 210 +++++++++++++ imports/ui/files/FolderTreeIndividualFile.jsx | 78 +++++ imports/ui/files/IndividualFile.jsx | 79 +++++ imports/ui/files/IndividualFileEnt.jsx | 85 ++++++ imports/ui/files/IndividualFilePOI.jsx | 81 ++++++ imports/ui/files/IndividualFileStart.jsx | 86 ++++++ imports/ui/files/ListPersons.jsx | 26 ++ imports/ui/files/LlistaArxiusEnt.jsx | 87 ++++++ imports/ui/files/LlistaArxiusFolderTM.jsx | 187 ++++++++++++ server/main.js | 191 +++++++++++- 32 files changed, 3709 insertions(+), 16 deletions(-) create mode 100644 imports/api/files.js create mode 100644 imports/api/lib/grid/createBucket.js create mode 100644 imports/api/lib/grid/createObjectId.js create mode 100644 imports/api/lib/images.js create mode 100644 imports/api/lib/persons.js create mode 100644 imports/ui/files/AvatarFileUpload.jsx create mode 100644 imports/ui/files/AvatarIndividualFile.jsx create mode 100644 imports/ui/files/FileUpload.jsx create mode 100644 imports/ui/files/FileUploadAdmin.jsx create mode 100644 imports/ui/files/FileUploadEnt.jsx create mode 100644 imports/ui/files/FileUploadPOI.jsx create mode 100644 imports/ui/files/FileUploadStart.jsx create mode 100644 imports/ui/files/FileUpload_abans.jsx create mode 100644 imports/ui/files/FileUpload_ori.jsx create mode 100644 imports/ui/files/Files.jsx create mode 100644 imports/ui/files/FilesCartellEvent/FileUploadCartellEvent.jsx create mode 100644 imports/ui/files/FilesCartellEvent/IndividualFileCartellEvent.jsx create mode 100644 imports/ui/files/FolderTreeFileUpload.jsx create mode 100644 imports/ui/files/FolderTreeIndividualFile.jsx create mode 100644 imports/ui/files/IndividualFile.jsx create mode 100644 imports/ui/files/IndividualFileEnt.jsx create mode 100644 imports/ui/files/IndividualFilePOI.jsx create mode 100644 imports/ui/files/IndividualFileStart.jsx create mode 100644 imports/ui/files/ListPersons.jsx create mode 100644 imports/ui/files/LlistaArxiusEnt.jsx create mode 100644 imports/ui/files/LlistaArxiusFolderTM.jsx diff --git a/.meteor/packages b/.meteor/packages index fe1d901..fe1b6aa 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -22,3 +22,4 @@ react-meteor-data # React higher-order component for reactively tracking M roles@1.0.1 accounts-password@3.0.3 react-meteor-accounts +ostrio:files diff --git a/.meteor/release b/.meteor/release index eaae1a4..5f22892 100644 --- a/.meteor/release +++ b/.meteor/release @@ -1 +1 @@ -METEOR@3.1.1 +METEOR@3.1.2 diff --git a/.meteor/versions b/.meteor/versions index 6ea1169..3df2ca5 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -23,7 +23,7 @@ ecmascript-runtime@0.8.3 ecmascript-runtime-client@0.12.2 ecmascript-runtime-server@0.11.1 ejson@1.1.4 -email@3.1.1 +email@3.1.2 es5-shim@4.8.1 facts-base@1.0.2 fetch@0.1.5 @@ -42,7 +42,7 @@ minifier-js@3.0.1 minimongo@2.0.2 mobile-experience@1.1.2 mobile-status-bar@1.1.1 -modern-browsers@0.1.11 +modern-browsers@0.2.0 modules@0.20.3 modules-runtime@0.13.2 modules-runtime-hot@0.14.3 @@ -52,6 +52,8 @@ mongo-dev-server@1.1.1 mongo-id@1.0.9 npm-mongo@6.10.2 ordered-dict@1.2.0 +ostrio:cookies@2.8.1 +ostrio:files@3.0.0-beta.6 promise@1.0.0 random@1.2.2 rate-limit@1.1.2 @@ -73,5 +75,5 @@ static-html-tools@1.0.0 tracker@1.3.4 typescript@5.6.3 url@1.3.5 -webapp@2.0.4 +webapp@2.0.5 webapp-hashing@1.1.2 diff --git a/imports/api/files.js b/imports/api/files.js new file mode 100644 index 0000000..2a563cd --- /dev/null +++ b/imports/api/files.js @@ -0,0 +1,275 @@ +import { Meteor } from 'meteor/meteor'; +import { FilesCollection } from 'meteor/ostrio:files'; +import { createBucket } from '/imports/api/lib/grid/createBucket.js'; +import { createObjectId } from '/imports/api/lib/grid/createObjectId.js'; +import fs from 'fs'; + +// import { Mongo } from 'meteor/mongo'; + +let filesBucket; + +if (Meteor.isServer) { + filesBucket = createBucket('allFiles'); +} + +const FilesCol = new FilesCollection({ + collectionName: 'Files', + allowClientCode: true, + debug: Meteor.isServer && process.env.NODE_ENV === 'development', + onBeforeUpload (file) { + // if (file.size <= 10485760 && /png|jpg|jpeg/i.test(file.extension)) { + // return true; + // } + // return 'Please upload image, with size equal or less than 10MB'; + return true; + }, + + onAfterUpload(file) { + const self = this; + + // here you could manipulate your file + // and create a new version, for example a scaled 'thumbnail' + // ... + + console.log("file_versions: ", file.versions); + + // then we read all versions we have got so far + Object.keys(file.versions).forEach(versionName => { + const metadata = { ...file.meta, versionName, fileId: file._id }; + fs.createReadStream(file.versions[ versionName ].path) + + // this is where we upload the binary to the bucket + .pipe(filesBucket.openUploadStream( + file.name, + { + contentType: file.type || 'binary/octet-stream', + metadata + } + )) + + // and we unlink the file from the fs on any error + // that occurred during the upload to prevent zombie files + .on('error', async err => { + console.error(err); + self.unlink(await this.collection.findOneAsync(file._id), versionName); // Unlink files from FS + }) + + // once we are finished, we attach the gridFS Object id on the + // FilesCollection document's meta section and finally unlink the + // upload file from the filesystem + .on('finish', Meteor.bindEnvironment(async ver => { + const property = `versions.${versionName}.meta.gridFsFileId`; + await self.collection.updateAsync(file._id, { + $set: { + [ property ]: ver._id.toHexString() + } + }); + self.unlink(await this.collection.findOneAsync(file._id), versionName); // Unlink files from FS + })) + }) + }, + + interceptDownload (http, file, versionName) { + const { gridFsFileId } = file.versions[ versionName ].meta || {}; + if (gridFsFileId) { + const gfsId = createObjectId({ gridFsFileId }); + const readStream = filesBucket.openDownloadStream(gfsId); + readStream.on('data', (data) => { + http.response.write(data); + }) + + readStream.on('end', () => { + http.response.end('end'); + }) + + readStream.on('error', () => { + // not found probably + // eslint-disable-next-line no-param-reassign + http.response.statusCode = 404; + http.response.end('not found'); + }) + + http.response.setHeader('Cache-Control', this.cacheControl); + http.response.setHeader('Content-Disposition', `inline; filename="${file.name}"`); + } + return Boolean(gridFsFileId) // Serve file from either GridFS or FS if it wasn't uploaded yet + }, + + onAfterRemove (files) { + files.forEach(file => { + Object.keys(file.versions).forEach(versionName => { + const gridFsFileId = (file.versions[ versionName ].meta || {}).gridFsFileId; + if (gridFsFileId) { + const gfsId = createObjectId({ gridFsFileId }); + filesBucket.deleteAsync(gfsId, err => { if (err) console.error(err); }); + } + }); + }); + } + +}); + +// if (Meteor.isClient) { +// Meteor.subscribe('files.all'); +// } + +if (Meteor.isServer) { + + Meteor.publish('files.all', () => { + return FilesCol.collection.find({userId: Meteor.userId()}); + }); + + Meteor.publish('avatars.all', () => { + return Avatars.collection.find({}); +}); + + // Meteor.publish('files.avatar', () => { + // return FilesCol.collection.find({ + // userId: Meteor.userId(), + // 'meta.type': 'avatar' + // }); + // }); + + // Meteor.publish('files.avatarRegister', (avatarId) => { + // if (avatarId) { + // if (!Meteor.userId()) { + // return FilesCol.collection.find({_id: avatarId}); + // } + // } else { + // return []; + // } + // }); + + // Meteor.publish('files.folderTree', () => { + // //if (upId) { + // if (Meteor.userId()) { + // return FilesCol.collection.find({ + // userId: Meteor.userId(), + // "meta.type": "folderTree" + // }); + // } else { + // return []; + // } + // }); + + // // files.cartellEventUpload + // Meteor.publish('files.cartellEventUpload', (cartellId) => { + // if (cartellId) { + // //if (!Meteor.userId()) { + // return FilesCol.collection.find({_id: cartellId}); + // } else { + // return []; + // } + // }); +} + +Meteor.methods({ + + 'RenameFile'(data){ + // if (!Meteor.userId()){ + // throw new Meteor.Error('not-authorized'); + // } + FilesCol.insertAsync({ + ...data, + createdAt: new Date(), + user: Meteor.userId() + }); + }, + + 'ReassignaUserIdFile'(userIdProvisional, uid){ + // if (!Meteor.userId()){ + // throw new Meteor.Error('not-authorized'); + // } + + FilesCol.collection.updateAsync({_id: userIdProvisional}, { + $set: { + meta: { + userId: uid + } + } + }); + }, + + 'RemoveFile'(fileToRemoveId) { + FilesCol.collection.removeAsync(fileToRemoveId); + } + + + // getFolderTreeFiles() { + // return FilesCol.find({ + // // userId: Meteor.userId(), + // // 'meta.type': 'folderTree' + // }).fetch(); + // } + // 'dates.update'(data){ + // // if (Meteor.userId() !== allcod.user){ + // // throw new Meteor.Error('not-authorized'); + // // } + // DatesCollection.update(data._id, { + // $set: { + // ...data + // } + // }); + // }, + + // 'dates.delete'(data){ + // // if (Meteor.userId() !== allcod.user){ + // // throw new Meteor.Error('not-authorized'); + // // } + // DatesCollection.remove(data._id); + // }, + + // 'dates.remove'(id, context) { + // DatesCollection.remove(id); + // } + +}); + + +// Create a new instance of the FilesCollection +const Avatars = new FilesCollection({ + collectionName: 'Avatars', + storagePath: 'assets/avatarStorage', + downloadRoute: '/avatar', + permissions: 0o755, + cacheControl: 'public, max-age=31536000', + allowClientCode: false, // Disallow remove files from Client +}); + +Meteor.methods({ + + 'registraUsuariAmbAvatar'(username, email, password, avatar) { + // Check if the username and email are valid + if (!username || !email || !password) { + throw new Meteor.Error('invalid-input', 'Please fill in all fields'); + } + + // Check if the avatar is a valid file + if (!avatar || !avatar.file) { + throw new Meteor.Error('invalid-avatar', 'Please select a valid avatar image'); + } + + // Upload the avatar to GridFS + const avatarId = Avatars.insert(avatar.file, (err, fileObj) => { + if (err) { + throw new Meteor.Error('avatar-upload-failed', 'Failed to upload avatar'); + } + }); + + // Create the new user + const userId = Accounts.createUser({ + username, + email, + password, + profile: { + avatar: avatarId, + }, + }); + + // Return the new user's ID + return userId; + } + +}); + +export { FilesCol, Avatars }; \ No newline at end of file diff --git a/imports/api/lib/grid/createBucket.js b/imports/api/lib/grid/createBucket.js new file mode 100644 index 0000000..5de873f --- /dev/null +++ b/imports/api/lib/grid/createBucket.js @@ -0,0 +1,6 @@ +import { MongoInternals } from 'meteor/mongo'; + +export const createBucket = bucketName => { + const options = bucketName ? {bucketName} : (void 0); + return new MongoInternals.NpmModule.GridFSBucket(MongoInternals.defaultRemoteCollectionDriver().mongo.db, options); +} diff --git a/imports/api/lib/grid/createObjectId.js b/imports/api/lib/grid/createObjectId.js new file mode 100644 index 0000000..079b845 --- /dev/null +++ b/imports/api/lib/grid/createObjectId.js @@ -0,0 +1,3 @@ +import { MongoInternals } from 'meteor/mongo' + +export const createObjectId = ({gridFsFileId}) => new MongoInternals.NpmModule.ObjectID(gridFsFileId); diff --git a/imports/api/lib/images.js b/imports/api/lib/images.js new file mode 100644 index 0000000..4f9fbec --- /dev/null +++ b/imports/api/lib/images.js @@ -0,0 +1,110 @@ +import { Meteor } from 'meteor/meteor'; +import { FilesCollection } from 'meteor/ostrio:files'; +import { createBucket } from '/imports/api/lib/grid/createBucket.js'; +import { createObjectId } from '/imports/api/lib/grid/createObjectId.js'; +import fs from 'fs'; + +let imagesBucket; +if (Meteor.isServer) { + imagesBucket = createBucket('allImages'); +} + +const Images = new FilesCollection({ + collectionName: 'Images', + allowClientCode: true, + debug: Meteor.isServer && process.env.NODE_ENV === 'development', + onBeforeUpload (file) { + if (file.size <= 10485760 && /png|jpg|jpeg/i.test(file.extension)) { + return true; + } + return 'Please upload image, with size equal or less than 10MB'; + }, + onAfterUpload (file) { + const self = this; + + // here you could manipulate your file + // and create a new version, for example a scaled 'thumbnail' + // ... + + // then we read all versions we have got so far + Object.keys(file.versions).forEach(versionName => { + const metadata = { ...file.meta, versionName, fileId: file._id }; + fs.createReadStream(file.versions[ versionName ].path) + + // this is where we upload the binary to the bucket + .pipe(imagesBucket.openUploadStream( + file.name, + { + contentType: file.type || 'binary/octet-stream', + metadata + } + )) + + // and we unlink the file from the fs on any error + // that occurred during the upload to prevent zombie files + .on('error', err => { + console.error(err); + self.unlink(this.collection.findOne(file._id), versionName); // Unlink files from FS + }) + + // once we are finished, we attach the gridFS Object id on the + // FilesCollection document's meta section and finally unlink the + // upload file from the filesystem + .on('finish', Meteor.bindEnvironment(ver => { + const property = `versions.${versionName}.meta.gridFsFileId`; + self.collection.update(file._id, { + $set: { + [ property ]: ver._id.toHexString() + } + }); + self.unlink(this.collection.findOne(file._id), versionName); // Unlink files from FS + })) + }) + }, + interceptDownload (http, file, versionName) { + const { gridFsFileId } = file.versions[ versionName ].meta || {}; + if (gridFsFileId) { + const gfsId = createObjectId({ gridFsFileId }); + const readStream = imagesBucket.openDownloadStream(gfsId); + readStream.on('data', (data) => { + http.response.write(data); + }) + + readStream.on('end', () => { + http.response.end('end'); + }) + + readStream.on('error', () => { + // not found probably + // eslint-disable-next-line no-param-reassign + http.response.statusCode = 404; + http.response.end('not found'); + }) + + http.response.setHeader('Cache-Control', this.cacheControl); + http.response.setHeader('Content-Disposition', `inline; filename="${file.name}"`); + } + return Boolean(gridFsFileId) // Serve file from either GridFS or FS if it wasn't uploaded yet + }, + onAfterRemove (files) { + files.forEach(file => { + Object.keys(file.versions).forEach(versionName => { + const gridFsFileId = (file.versions[ versionName ].meta || {}).gridFsFileId; + if (gridFsFileId) { + const gfsId = createObjectId({ gridFsFileId }); + imagesBucket.delete(gfsId, err => { if (err) console.error(err); }); + } + }); + }); + } +}); + +if (Meteor.isClient) { + Meteor.subscribe('files.images.all'); +} + +if (Meteor.isServer) { + Meteor.publish('files.images.all', () => Images.collection.find({})); +} + +export { Images }; diff --git a/imports/api/lib/persons.js b/imports/api/lib/persons.js new file mode 100644 index 0000000..4e1e37d --- /dev/null +++ b/imports/api/lib/persons.js @@ -0,0 +1,62 @@ +import { Mongo } from 'meteor/mongo'; +import { Images } from './images.js'; +// import SimpleSchema from 'simpl-schema'; + +// SimpleSchema.extendOptions(['autoform']); +// SimpleSchema.setDefaultMessages({ +// initialLanguage: 'en', +// messages: { +// en: { +// uploadError: '{{{value}}}' +// } +// } +// }); + +const Persons = new Mongo.Collection('persons'); + +Persons.helpers({ + profilePic() { + return Images.find({_id: this.profilePicId}); + }, + backgroundPic() { + return Images.find({_id: this.backgroundPicId}); + } +}); + +// Persons.attachSchema({ +// name: { +// type: String, +// label: 'Name' +// }, +// profilePicId: { +// type: String, +// label: 'Profile Pic Id', +// autoform: { +// afFieldInput: { +// type: 'fileUpload', +// collection: 'Images', +// insertConfig: { +// transport: 'http' +// }, +// uploadTemplate: 'uploadField', // <- Optional +// previewTemplate: 'uploadPreview', // <- Optional, +// } +// } +// }, +// backgroundPicId: { +// type: String, +// label: 'Background Pic Id', +// autoform: { +// afFieldInput: { +// type: 'fileUpload', +// collection: 'Images', +// accept: '.png,.jpg,.jpeg', // use built-in accept config +// insertConfig: { +// transport: 'http' +// } +// } +// } +// } +// }); + +export { Persons }; diff --git a/imports/ui/BarraNav/UserStat.jsx b/imports/ui/BarraNav/UserStat.jsx index eace1b1..ac2526b 100644 --- a/imports/ui/BarraNav/UserStat.jsx +++ b/imports/ui/BarraNav/UserStat.jsx @@ -47,7 +47,7 @@ const useLongPress = (mostraMenu, setMostraMenu) => { if (isPressed) { setIsPressed(false); // Clear any pending timeout - clearTimeout(timeoutId?.current); + // clearTimeout(timeoutId?.current); } }; @@ -69,6 +69,7 @@ const useLongPress = (mostraMenu, setMostraMenu) => { } }, 500); + // return () => clearTimeout(timeoutId); } return () => clearTimeout(timeoutId); @@ -248,7 +249,7 @@ const UserStat = ({esAdministrador, setEsAdministrador}) => { left: 0, top: `5em`, padding: `.4em .5em`, - border: `1px #aaa`, + border: `1px solid #aaa`, cursor: `pointer`, zIndex: `200` }} diff --git a/imports/ui/Login.tsx b/imports/ui/Login.tsx index 16d7f9b..35c44e0 100644 --- a/imports/ui/Login.tsx +++ b/imports/ui/Login.tsx @@ -1,14 +1,46 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { Accounts } from 'meteor/accounts-base'; import { useNavigate } from 'react-router-dom'; import { Meteor } from 'meteor/meteor'; +import { useTracker, useSubscribe, useFind } from 'meteor/react-meteor-data/suspense'; import { Roles } from 'meteor/roles'; import { ROLS_GLOBALS } from '../roles'; +import { Avatars } from '/imports/api/files.js'; +import AvatarFileUpload from '/imports/ui/files/AvatarFileUpload'; + export const Login = () => { const [isLogin, setIsLogin] = useState( { initialState: true } ); const navigate = useNavigate(); + // const avatarinput = useRef(); + + // const [avatarId, setAvatarId] = useState(); + // const [avatarLink, setAvatarLink] = useState(); + + // const [novaImg, setNovaImg] = useState(false); + + // const [avatar, setAvatar] = useState(null); + + // let files; + + // useEffect(() => { + + // useSubscribe('avatars.all'); + + // files = useFind(Avatars, [ + // {}, + // { sort: { createdAt: -1 } }, + // ]); + + // }, []); + + // const files = useTracker("avatars", () => { + // return Avatars.find({}).fetchAsync(); + // }); + + + const handleLogin = (e) => { e.preventDefault(); // console.dir(e); @@ -74,6 +106,8 @@ export const Login = () => { } + + return (
{ @@ -109,7 +143,11 @@ export const Login = () => { password2 }; - Meteor.callAsync('creaUsuariAmbCodi', uObj, codi); + // Meteor.callAsync('creaUsuariAmbCodi', uObj, codi); + + + + Meteor.callAsync('registraUsuariAmbAvatarICodi', uObj, codi, avatarinput.current); // userId = await Accounts.createUser({ // username, @@ -163,7 +201,8 @@ export const Login = () => { backgroundColor: `#eeed` }} > - Avatar: { }} /> + + */} + + + {/* */} +
+ +
+
diff --git a/imports/ui/files/AvatarFileUpload.jsx b/imports/ui/files/AvatarFileUpload.jsx new file mode 100644 index 0000000..49d0657 --- /dev/null +++ b/imports/ui/files/AvatarFileUpload.jsx @@ -0,0 +1,226 @@ +import { useTracker, useSubscribe, useFind } from 'meteor/react-meteor-data/suspense'; +import { Meteor } from 'meteor/meteor'; +import React, { useState, useRef, Suspense } from 'react'; +// import PropTypes from 'prop-types'; +import { Avatars } from '/imports/api/files.js'; + +import IndividualFileStart from '/imports/ui/files/IndividualFile.jsx'; // <------------------------- + +import _ from 'lodash'; + +//const debug = require('debug')('demo:file'); + +const AvatarFileUpload = (props) => { + + const [uploading, setUploading] = useState([]); + const [progress, setProgress] = useState(0); + const [inProgress, setInProgress] = useState(false); + + const [preview, setPreview] = useState(""); + + // const reseter = useState(0); + + // const refForm = useRef(); + const fileinput = useRef(); + + useSubscribe('avatars.all'); + + const files = useFind(Avatars, [ + {}, + { sort: { createdAt: -1 } }, + ]); + + // const files = useTracker("avatars", async () => { + // // const docsReadyYet = filesHandle.ready(); + // const files = await Avatars?.find({meta:{userId: props.uidProvisional || Meteor.userId(), entId: props.entId}}, {sort: {name: 1}})//.fetchAsync(); // Meteor.userId() ?? "nop" + + // return files; + // }, []); + + function uploadIt(e) { + e.preventDefault(); + + //let self = this; + + if (e.currentTarget.files && e.currentTarget.files[0]) { + // We upload only one file, in case + // there was multiple files selected + var file = e.currentTarget.files[0]; + + const reader = new FileReader(); + + reader.onload = (event) => { + console.log("eventTarget: ", event.target); + setPreview(event.target.result); + }; + + reader.readAsDataURL(file); + + // setPreview(file); + + if (file) { + let uploadInstance = Avatars.insert({ + file, + meta: { + locator: props.fileLocator, + userId: props.uidProvisional || Meteor.userId(), // Optional, used to check on server for file tampering + entId: props.entId + }, + chunkSize: 'dynamic', + allowWebWorkers: true // If you see issues with uploads, change this to false + }, false) + + setUploading(uploadInstance); // Keep track of this instance to use below + setInProgress(true); // Show the progress bar now + + // These are the event functions, don't need most of them, it shows where we are in the process + uploadInstance.on('start', function () { + console.log('Starting'); + }) + + uploadInstance.on('end', function (error, fileObj) { + console.log('On end File Object: ', fileObj); + }) + + uploadInstance.on('uploaded', function (error, fileObj) { + console.log('uploaded: ', fileObj); + + // props.setUpId(fileObj._id); + + // Remove the filename from the upload box + fileinput.current.value = ''; + + // Reset our state for the next file + setUploading([]); + setProgress(0); + setInProgress(false); + }) + + uploadInstance.on('error', function (error, fileObj) { + console.log('Error during upload: ' + error) + }); + + uploadInstance.on('progress', function (progress, fileObj) { + console.log('Upload Percentage: ' + progress) + // Update our progress bar + setProgress(progress); + }); + + uploadInstance.start(); // Must manually start the upload + } + } + } + + // This is our progress bar, bootstrap styled + // Remove this function if not needed + function UploadingFile({preview}) { + + // console.log('**********************************', uploading); + + if (!_.isEmpty(uploading)) { + // console.log("uploading: ", uploading); + // console.log("preview: ", preview); + return
+ + +
+ + {/* {uploading.file.name} */} + +
+
+ {progress}% Complete (success) + {/* {progress}% */} +
+
+
+ } + } + + const Display = async () => await files?.map(async (aFile, key) => { + + let link = await Avatars.findOneAsync({_id: aFile._id}); //The "view/download" link + let linkOriginalURL = `${window.location.origin}${link._fileRef._downloadRoute}/${link._fileRef._collectionName}/${link._fileRef._id}/original/${link._fileRef._id}.${link._fileRef.extension}`; + + // Send out components that show details of each file + + return +
+ +
+
+}).reverse(); + + // Run through each file that the user has stored + // (make sure the subscription only sends files owned by this user) + // let display = + + return + + + + + + + + + + // else return
Carregant llista d'arxius...
; +}; + +// +// This is the HOC - included in this file just for convenience, but usually kept +// in a separate file to provide separation of concerns. +// +// export default withTracker( ( props ) => { + +// const filesHandle = Meteor.subscribe('files.all'); +// const docsReadyYet = filesHandle.ready(); +// const files = Avatars.find({meta:{userId: props.uidProvisional || Meteor.userId()}}, {sort: {name: 1}}).fetch(); // Meteor.userId() ?? "nop" + +// return { +// docsReadyYet, +// files, +// }; + +// })(FileUploadComponent); + +export default AvatarFileUpload; \ No newline at end of file diff --git a/imports/ui/files/AvatarIndividualFile.jsx b/imports/ui/files/AvatarIndividualFile.jsx new file mode 100644 index 0000000..5861840 --- /dev/null +++ b/imports/ui/files/AvatarIndividualFile.jsx @@ -0,0 +1,82 @@ +import React from 'react'; +// import PropTypes from 'prop-types'; + +const AvatarIndividualFile = props => { + +// propTypes: { +// fileName: PropTypes.string.isRequired, +// fileSize: PropTypes.number.isRequired, +// fileUrl: PropTypes.string, +// fileId: PropTypes.string.isRequired +// } + + function removeFile(){ + let conf = confirm('Are you sure you want to delete the file?') || false; + if (conf == true) { + Meteor.call('RemoveFile', props.fileId, function (err, res) { + if (err) + console.log(err); + }) + } + } + + function renameFile(){ + + let validName = /[^a-zA-Z0-9 \.:\+()\-_%!&]/gi; + let prompt = window.prompt('New file name?', props.fileName); + + // Replace any non valid characters, also do this on the server + if (prompt) { + prompt = prompt.replace(validName, '-'); + prompt.trim(); + } + + if (!_.isEmpty(prompt)) { + Meteor.call('RenameFile', props.fileId, prompt, function (err, res) { + if (err) + console.log(err); + }) + } + } + + return
+ {props.fileName} + +
+
+ {props.fileName} +
+
+
+
+ + {/*
+
+ +
+ + +
+ View +
+ +
+ +
+ +
+ Size: {props.fileSize} +
+
*/} +
+ } +export default AvatarIndividualFile; \ No newline at end of file diff --git a/imports/ui/files/FileUpload.jsx b/imports/ui/files/FileUpload.jsx new file mode 100644 index 0000000..c9156a3 --- /dev/null +++ b/imports/ui/files/FileUpload.jsx @@ -0,0 +1,204 @@ +import { useSubscribe, useFind, useTracker } from 'meteor/react-meteor-data'; +import { Meteor } from 'meteor/meteor'; +import React, { useState, useRef } from 'react'; +// import PropTypes from 'prop-types'; +import { FilesCol } from '/imports/api/files.js'; + +import IndividualFile from '/imports/ui/files/IndividualFile.jsx'; + +import _ from 'lodash'; + +// const debug = require('debug')('demo:file'); + +const FileUploadComponent = (props) => { + + const [uploading, setUploading] = useState([]); + const [progress, setProgress] = useState(0); + const [inProgress, setInProgress] = useState(false); + + const fileinput = useRef(); + + const isLoading = useSubscribe('files.all'); + + const files = useTracker(() => { + // const filesHandle = Meteor.subscribe('files.all'); + // const docsReadyYet = filesHandle.ready(); + //const files = FilesCol?.find({meta:{userId: props.uidProvisional || Meteor.userId()}}, {sort: {name: 1}}); // Meteor.userId() ?? "nop" + + console.dir("files from FU: ", files.cursor); + + return files; + }, [isLoading()]); + + + + console.dir("fufiles: ", files); + + + function uploadIt(e) { + e.preventDefault(); + + //let self = this; + + if (e.currentTarget.files && e.currentTarget.files[0]) { + // We upload only one file, in case + // there was multiple files selected + var file = e.currentTarget.files[0]; + + if (file) { + let uploadInstance = FilesCol.insertAsync({ + file, + meta: { + locator: props.fileLocator, + userId: props.uidProvisional || Meteor.userId() // Optional, used to check on server for file tampering + }, + chunkSize: 'dynamic', + allowWebWorkers: true // If you see issues with uploads, change this to false + }, false) + + setUploading(uploadInstance); // Keep track of this instance to use below + setInProgress(true);// Show the progress bar now + + // These are the event functions, don't need most of them, it shows where we are in the process + uploadInstance.on('start', function () { + console.log('Starting'); + }) + + uploadInstance.on('end', function (error, fileObj) { + console.log('On end File Object: ', fileObj); + }) + + uploadInstance.on('uploaded', function (error, fileObj) { + // console.dir("uploaded: this ", this.config.fileId); + // console.log('uploaded: ', fileObj); + + // console.log("upError: ", error); + props?.setUpId(fileObj?.config.fileId); + + // Remove the filename from the upload box + fileinput.current.value = ''; + + // Reset our state for the next file + setUploading([]); + setProgress(0); + setInProgress(false); + }) + + uploadInstance.on('error', function (error, fileObj) { + console.log('Error during upload: ' + error) + }); + + uploadInstance.on('progress', function (progress, fileObj) { + console.log('Upload Percentage: ' + progress) + // Update our progress bar + setProgress(progress); + }); + + uploadInstance.start(); // Must manually start the upload + } + } + } + + // This is our progress bar, bootstrap styled + // Remove this function if not needed + function showUploads() { + console.log('**********************************', uploading); + + if (!_.isEmpty(uploading)) { + return
+ {uploading.file.name} + +
+
+ {progress}% Complete (success) + {progress}% +
+
+
+ } + } + + + { + // debug("Rendering FileUpload",docsReadyYet); + // if(!isLoading() && files) + //if (files /* && docsReadyYet*/) { + { + console.dir("FUfiles: ", files); + //let fileCursors = files; + + // console.dir("fileCursors: ", fileCursors); + + // Run through each file that the user has stored + // (make sure the subscription only sends files owned by this user) + let display = files?.map(async (aFile, key) => { + console.log('A file: ', aFile.link(), aFile.get('name')) + let link = await FilesCol.findOneAsync({_id: aFile._id}).link(); //The "view/download" link + + console.log("link: ", link); + + // Send out components that show details of each file + return
+ +
+ }) + + return
+ +
+
+

Upload New File:

+ +
+
+ +
+
+ + {showUploads()} + +
+
+
+
+ + {display} + +
+ } + // else return
Loading file list
; + } +}; + +// +// This is the HOC - included in this file just for convenience, but usually kept +// in a separate file to provide separation of concerns. +// +// export default withTracker( ( props ) => { + +// const filesHandle = Meteor.subscribe('files.all'); +// const docsReadyYet = filesHandle.ready(); +// const files = FilesCol.find({meta:{userId: props.uidProvisional || Meteor.userId()}}, {sort: {name: 1}}).fetch(); // Meteor.userId() ?? "nop" + +// return { +// docsReadyYet, +// files, +// }; + +// })(FileUploadComponent); + +export default FileUploadComponent; \ No newline at end of file diff --git a/imports/ui/files/FileUploadAdmin.jsx b/imports/ui/files/FileUploadAdmin.jsx new file mode 100644 index 0000000..0ec6f78 --- /dev/null +++ b/imports/ui/files/FileUploadAdmin.jsx @@ -0,0 +1,208 @@ +import { useTracker } from 'meteor/react-meteor-data'; +import { Meteor } from 'meteor/meteor'; +import React, { useState, useRef } from 'react'; +// import PropTypes from 'prop-types'; +import { FilesCol } from '/imports/api/files.js'; +import IndividualFile from '/imports/ui/files/IndividualFile.jsx'; +import _ from 'lodash'; +// import { useUser } from 'meteor/react-meteor-accounts'; +// import { Roles } from 'meteor/alanning:roles'; +// import { GLOBAL_ROLES } from '../roles'; +// import { useTracker } from 'meteor/react-meteor-data'; + +//const debug = require('debug')('demo:file'); + +const FileUploadAdmin = (props) => { + + // const user = useUser(); + // const isAdmin = useTracker(() => Roles.userIsInRole(user?._id, GLOBAL_ROLES.ADMIN)); + + const [uploading, setUploading] = useState([]); + const [progress, setProgress] = useState(0); + const [inProgress, setInProgress] = useState(false); + + const reseter = useState(0); + + const refForm = useRef(); + const fileinput = useRef(); + + const files = useTracker(() => { + const filesHandle = Meteor.subscribe('files.all'); + // const docsReadyYet = filesHandle.ready(); + const files = FilesCol?.find({meta:{userId: props.uidProvisional || Meteor.userId(), entId: props.entId}}, {sort: {name: 1}}).fetch(); // Meteor.userId() ?? "nop" + + return files; + }); + + + + function uploadIt(e) { + e.preventDefault(); + + //let self = this; + + if (e.currentTarget.files && e.currentTarget.files[0]) { + // We upload only one file, in case + // there was multiple files selected + var file = e.currentTarget.files[0]; + + if (file) { + let uploadInstance = FilesCol.insert({ + file, + meta: { + locator: props.fileLocator, + userId: props.uidProvisional || Meteor.userId(), // Optional, used to check on server for file tampering + entId: props.entId + }, + chunkSize: 'dynamic', + allowWebWorkers: true // If you see issues with uploads, change this to false + }, false) + + setUploading(uploadInstance); // Keep track of this instance to use below + setInProgress(true); // Show the progress bar now + + // These are the event functions, don't need most of them, it shows where we are in the process + uploadInstance.on('start', function () { + console.log('Starting'); + }) + + uploadInstance.on('end', function (error, fileObj) { + console.log('On end File Object: ', fileObj); + }) + + uploadInstance.on('uploaded', function (error, fileObj) { + console.log('uploaded: ', fileObj); + + // props.setUpId(fileObj._id); + + // Remove the filename from the upload box + fileinput.current.value = ''; + + // Reset our state for the next file + setUploading([]); + setProgress(0); + setInProgress(false); + }) + + uploadInstance.on('error', function (error, fileObj) { + console.log('Error during upload: ' + error) + }); + + uploadInstance.on('progress', function (progress, fileObj) { + console.log('Upload Percentage: ' + progress) + // Update our progress bar + setProgress(progress); + }); + + uploadInstance.start(); // Must manually start the upload + } + } + } + + // This is our progress bar, bootstrap styled + // Remove this function if not needed + function showUploads() { + console.log('**********************************', uploading); + + if (!_.isEmpty(uploading)) { + return
+ {uploading.file.name} + +
+
+ {progress}% Complete (success) + {progress}% +
+
+
+ } + } + + + { + // debug("Rendering FileUpload",docsReadyYet); + if (files /* && docsReadyYet*/) { + + let fileCursors = files; + + // Run through each file that the user has stored + // (make sure the subscription only sends files owned by this user) + let display = fileCursors.map((aFile, key) => { + // console.log('A file: ', aFile.link(), aFile.get('name')) + let link = FilesCol.findOne({_id: aFile._id}).link(); //The "view/download" link + + // Send out components that show details of each file + return
+ +
+ }) + + return <> + + + + + + + + {/*
+
+ + {showUploads()} + +
+
*/} + + {/* {display} */} + + + } + else return
Carregant llista d'arxius...
; + } +}; + +// +// This is the HOC - included in this file just for convenience, but usually kept +// in a separate file to provide separation of concerns. +// +// export default withTracker( ( props ) => { + +// const filesHandle = Meteor.subscribe('files.all'); +// const docsReadyYet = filesHandle.ready(); +// const files = FilesCol.find({meta:{userId: props.uidProvisional || Meteor.userId()}}, {sort: {name: 1}}).fetch(); // Meteor.userId() ?? "nop" + +// return { +// docsReadyYet, +// files, +// }; + +// })(FileUploadComponent); + +export default FileUploadAdmin; \ No newline at end of file diff --git a/imports/ui/files/FileUploadEnt.jsx b/imports/ui/files/FileUploadEnt.jsx new file mode 100644 index 0000000..1de8a77 --- /dev/null +++ b/imports/ui/files/FileUploadEnt.jsx @@ -0,0 +1,203 @@ +import { useTracker } from 'meteor/react-meteor-data'; +import { Meteor } from 'meteor/meteor'; +import React, { useState, useRef } from 'react'; +// import PropTypes from 'prop-types'; +import { FilesCol } from '/imports/api/files.js'; + +import IndividualFile from '/imports/ui/files/IndividualFile.jsx'; + +import _ from 'lodash'; + +//const debug = require('debug')('demo:file'); + +const FileUploadEnt = (props) => { + + const [uploading, setUploading] = useState([]); + const [progress, setProgress] = useState(0); + const [inProgress, setInProgress] = useState(false); + + const reseter = useState(0); + + const refForm = useRef(); + const fileinput = useRef(); + + const files = useTracker(() => { + const filesHandle = Meteor.subscribe('files.all'); + // const docsReadyYet = filesHandle.ready(); + const files = FilesCol?.find({meta:{userId: props.uidProvisional || Meteor.userId(), entId: props.entId}}, {sort: {name: 1}}).fetch(); // Meteor.userId() ?? "nop" + + return files; + }); + + + + function uploadIt(e) { + e.preventDefault(); + + //let self = this; + + if (e.currentTarget.files && e.currentTarget.files[0]) { + // We upload only one file, in case + // there was multiple files selected + var file = e.currentTarget.files[0]; + + if (file) { + let uploadInstance = FilesCol.insert({ + file, + meta: { + locator: props.fileLocator, + userId: props.uidProvisional || Meteor.userId(), // Optional, used to check on server for file tampering + entId: props.entId + }, + chunkSize: 'dynamic', + allowWebWorkers: true // If you see issues with uploads, change this to false + }, false) + + setUploading(uploadInstance); // Keep track of this instance to use below + setInProgress(true); // Show the progress bar now + + // These are the event functions, don't need most of them, it shows where we are in the process + uploadInstance.on('start', function () { + console.log('Starting'); + }) + + uploadInstance.on('end', function (error, fileObj) { + console.log('On end File Object: ', fileObj); + }) + + uploadInstance.on('uploaded', function (error, fileObj) { + console.log('uploaded: ', fileObj); + + // props.setUpId(fileObj._id); + + // Remove the filename from the upload box + fileinput.current.value = ''; + + // Reset our state for the next file + setUploading([]); + setProgress(0); + setInProgress(false); + }) + + uploadInstance.on('error', function (error, fileObj) { + console.log('Error during upload: ' + error) + }); + + uploadInstance.on('progress', function (progress, fileObj) { + console.log('Upload Percentage: ' + progress) + // Update our progress bar + setProgress(progress); + }); + + uploadInstance.start(); // Must manually start the upload + } + } + } + + // This is our progress bar, bootstrap styled + // Remove this function if not needed + function showUploads() { + console.log('**********************************', uploading); + + if (!_.isEmpty(uploading)) { + return
+ {uploading.file.name} + +
+
+ {progress}% Complete (success) + {progress}% +
+
+
+ } + } + + + { + // debug("Rendering FileUpload",docsReadyYet); + if (files /* && docsReadyYet*/) { + + let fileCursors = files; + + // Run through each file that the user has stored + // (make sure the subscription only sends files owned by this user) + let display = fileCursors.map((aFile, key) => { + // console.log('A file: ', aFile.link(), aFile.get('name')) + let link = FilesCol.findOne({_id: aFile._id}).link(); //The "view/download" link + + // Send out components that show details of each file + return
+ +
+ }) + + return <> + + + + + + + + {/*
+
+ + {showUploads()} + +
+
*/} + + {/* {display} */} + + + } + else return
Carregant llista d'arxius...
; + } +}; + +// +// This is the HOC - included in this file just for convenience, but usually kept +// in a separate file to provide separation of concerns. +// +// export default withTracker( ( props ) => { + +// const filesHandle = Meteor.subscribe('files.all'); +// const docsReadyYet = filesHandle.ready(); +// const files = FilesCol.find({meta:{userId: props.uidProvisional || Meteor.userId()}}, {sort: {name: 1}}).fetch(); // Meteor.userId() ?? "nop" + +// return { +// docsReadyYet, +// files, +// }; + +// })(FileUploadComponent); + +export default FileUploadEnt; \ No newline at end of file diff --git a/imports/ui/files/FileUploadPOI.jsx b/imports/ui/files/FileUploadPOI.jsx new file mode 100644 index 0000000..bb94eb6 --- /dev/null +++ b/imports/ui/files/FileUploadPOI.jsx @@ -0,0 +1,235 @@ +import { useTracker, useSubscribe, useFind } from 'meteor/react-meteor-data/suspense'; +import { Meteor } from 'meteor/meteor'; +import React, { useState, useRef, Suspense } from 'react'; +// import PropTypes from 'prop-types'; +import { FilesCol } from '/imports/api/files.js'; + +import IndividualFilePOI from '/imports/ui/files/IndividualFilePOI.jsx'; // <------------------------- + +import _ from 'lodash'; + +//const debug = require('debug')('demo:file'); + +const useFiles = () => { + useSubscribe('files.all'); + + const files = useTracker("files", () => FilesCol.find({}).fetchAsync(), []); + + return files; +}; + + + +const ShowUploads = ({uploading, progress, filePreviewURI, inProgress}) => { + // console.log('**********************************', uploading); + + if (!_.isEmpty(uploading)) { + // console.log("uploading: ", uploading); + return inProgress &&
+ {uploading.file.name} + + +
+
+ {progress}% Complete (success) + {progress}% +
+
+
; + } +}; + + +const DisplayUploadFile = ({files}) => files?.map(async (aFile, key) => { + + let link = await FilesCol.findOneAsync({_id: aFile._id}); //The "view/download" link + let linkOriginalURL = `${window.location.origin}${link._fileRef._downloadRoute}/${link._fileRef._collectionName}/${link._fileRef._id}/original/${link._fileRef._id}.${link._fileRef.extension}`; + + // Send out components that show details of each file + + return
+ +
; +}).reverse(); + + +const FileUploadPOI = (props) => { + + const [uploading, setUploading] = useState([]); + const [progress, setProgress] = useState(0); + const [inProgress, setInProgress] = useState(false); + + const [filePreviewURI, setFilePreviewURI] = useState(null); + + const fileinput = useRef(); + + const files = useFiles(); + + console.log("files: ", files); + // console.log("fileinput: ", fileinput); + + function uploadIt(e) { + e.preventDefault(); + + console.log("UPLOADING IT"); + + if (e.currentTarget.files && e.currentTarget.files[0]) { + // We upload only one file, in case + // there was multiple files selected + var file = e.currentTarget.files[0]; + + console.log("E: ", e); + console.log("FILE: ", file); + + if (file) { + + let reader = new FileReader(); + + console.log("READER: ", reader); + + // reader.onload = (evt) => { + // console.log("ONLOAD: ", evt.target.result); + // }; + reader.readAsDataURL(e.currentTarget.files[0]); + + reader.onload = function(ev){ + + + console.log("ev.target.result: ", ev.target.result); + + setFilePreviewURI(ev.target.result); + + console.log("filePreviewURI: ", filePreviewURI); + + + let uploadInstance = FilesCol.insert({ + file, + meta: { + locator: props.fileLocator, + userId: props.uidProvisional || Meteor.userId(), // Optional, used to check on server for file tampering + entId: props.entId + }, + chunkSize: 'dynamic', + allowWebWorkers: true // If you see issues with uploads, change this to false + }, false) + + setUploading(uploadInstance); // Keep track of this instance to use below + console.log("setInProgress: true"); + setInProgress(true); // Show the progress bar now + + // These are the event functions, don't need most of them, it shows where we are in the process + uploadInstance.on('start', function () { + console.log('Starting'); + console.log("file: ", file); + }) + + uploadInstance.on('End', function (error, fileObj) { + console.log('On end File Object: ', fileObj); + setProgress(0); + console.log("setInProgress: false"); + setInProgress(false); + }) + + uploadInstance.on('uploaded', function (error, fileObj) { + console.log('uploaded: ', fileObj); + + // props.setUpId(fileObj._id); + + // Remove the filename from the upload box + fileinput.current.value = ''; + + // Reset our state for the next file + setUploading([]); + setProgress(0); + console.log("setInProgress: false"); + setInProgress(false); + }) + + uploadInstance.on('error', function (error, fileObj) { + console.log('Error during upload: ' + error) + console.log("setInProgress: false"); + setInProgress(false); + }); + + uploadInstance.on('progress', function (progress, fileObj) { + console.log('Upload Percentage: ' + progress) + // Update our progress bar + setProgress(progress); + if (progress === 100) { + console.log("setInProgress: false"); + setInProgress(false); + } + }); + + uploadInstance.start(); // Must manually start the upload + } + } + } + } + + + return <> + + + + { + setInProgress(true); + }} + style={{display: `none`}} + name="fileinput" + onClick={(ev) => { + // ev.preventDefault(); + ev.stopPropagation(); + }} + /> + +

+ +
+
+ + + +
+
+ + + + ; + // else return
Carregant llista d'arxius...
; +}; + +export default FileUploadPOI; \ No newline at end of file diff --git a/imports/ui/files/FileUploadStart.jsx b/imports/ui/files/FileUploadStart.jsx new file mode 100644 index 0000000..049222d --- /dev/null +++ b/imports/ui/files/FileUploadStart.jsx @@ -0,0 +1,196 @@ +import { useTracker, useSubscribe, useFind } from 'meteor/react-meteor-data/suspense'; +import { Meteor } from 'meteor/meteor'; +import React, { useState, useRef, Suspense } from 'react'; +// import PropTypes from 'prop-types'; +import { FilesCol } from '/imports/api/files.js'; + +import IndividualFileStart from '/imports/ui/files/IndividualFile.jsx'; // <------------------------- + +import _ from 'lodash'; + +//const debug = require('debug')('demo:file'); + +const FileUploadStart = (props) => { + + const [uploading, setUploading] = useState([]); + const [progress, setProgress] = useState(0); + const [inProgress, setInProgress] = useState(false); + + const reseter = useState(0); + + const refForm = useRef(); + const fileinput = useRef(); + + useSubscribe('files.all'); + + const files = useTracker("files", async () => { + // const docsReadyYet = filesHandle.ready(); + const files = await FilesCol?.find({meta:{userId: props.uidProvisional || Meteor.userId(), entId: props.entId}}, {sort: {name: 1}})//.fetchAsync(); // Meteor.userId() ?? "nop" + + return files; + }, []); + + function uploadIt(e) { + e.preventDefault(); + + //let self = this; + + if (e.currentTarget.files && e.currentTarget.files[0]) { + // We upload only one file, in case + // there was multiple files selected + var file = e.currentTarget.files[0]; + + if (file) { + let uploadInstance = FilesCol.insert({ + file, + meta: { + locator: props.fileLocator, + userId: props.uidProvisional || Meteor.userId(), // Optional, used to check on server for file tampering + entId: props.entId + }, + chunkSize: 'dynamic', + allowWebWorkers: true // If you see issues with uploads, change this to false + }, false) + + setUploading(uploadInstance); // Keep track of this instance to use below + setInProgress(true); // Show the progress bar now + + // These are the event functions, don't need most of them, it shows where we are in the process + uploadInstance.on('start', function () { + console.log('Starting'); + }) + + uploadInstance.on('end', function (error, fileObj) { + console.log('On end File Object: ', fileObj); + }) + + uploadInstance.on('uploaded', function (error, fileObj) { + console.log('uploaded: ', fileObj); + + // props.setUpId(fileObj._id); + + // Remove the filename from the upload box + fileinput.current.value = ''; + + // Reset our state for the next file + setUploading([]); + setProgress(0); + setInProgress(false); + }) + + uploadInstance.on('error', function (error, fileObj) { + console.log('Error during upload: ' + error) + }); + + uploadInstance.on('progress', function (progress, fileObj) { + console.log('Upload Percentage: ' + progress) + // Update our progress bar + setProgress(progress); + }); + + uploadInstance.start(); // Must manually start the upload + } + } + } + + // This is our progress bar, bootstrap styled + // Remove this function if not needed + function showUploads() { + // console.log('**********************************', uploading); + + if (!_.isEmpty(uploading)) { + return
+ {uploading.file.name} + +
+
+ {progress}% Complete (success) + {progress}% +
+
+
+ } + } + + const Display = async () => await files?.map(async (aFile, key) => { + + let link = await FilesCol.findOneAsync({_id: aFile._id}); //The "view/download" link + let linkOriginalURL = `${window.location.origin}${link._fileRef._downloadRoute}/${link._fileRef._collectionName}/${link._fileRef._id}/original/${link._fileRef._id}.${link._fileRef.extension}`; + + // Send out components that show details of each file + + return +
+ +
+
+ }).reverse(); + + // Run through each file that the user has stored + // (make sure the subscription only sends files owned by this user) + // let display = + + return + + + + +
+
+ + {showUploads()} + +
+
+ + +
+ // else return
Carregant llista d'arxius...
; +}; + +// +// This is the HOC - included in this file just for convenience, but usually kept +// in a separate file to provide separation of concerns. +// +// export default withTracker( ( props ) => { + +// const filesHandle = Meteor.subscribe('files.all'); +// const docsReadyYet = filesHandle.ready(); +// const files = FilesCol.find({meta:{userId: props.uidProvisional || Meteor.userId()}}, {sort: {name: 1}}).fetch(); // Meteor.userId() ?? "nop" + +// return { +// docsReadyYet, +// files, +// }; + +// })(FileUploadComponent); + +export default FileUploadStart; \ No newline at end of file diff --git a/imports/ui/files/FileUpload_abans.jsx b/imports/ui/files/FileUpload_abans.jsx new file mode 100644 index 0000000..b0b1fc0 --- /dev/null +++ b/imports/ui/files/FileUpload_abans.jsx @@ -0,0 +1,188 @@ +import { useTracker } from 'meteor/react-meteor-data'; +import { Meteor } from 'meteor/meteor'; +import React, { useState, useRef } from 'react'; +// import PropTypes from 'prop-types'; +import { FilesCol } from '/imports/api/files.js'; + +import IndividualFile from '/imports/ui/files/IndividualFile.jsx'; + +import _ from 'lodash'; + +const debug = require('debug')('demo:file'); + +const FileUploadComponent = (props) => { + + const [uploading, setUploading] = useState([]); + const [progress, setProgress] = useState(0); + const [inProgress, setInProgress] = useState(false); + + const fileinput = useRef(); + + const files = useTracker(() => { + const filesHandle = Meteor.subscribe('files.all'); + // const docsReadyYet = filesHandle.ready(); + const files = FilesCol?.find({meta:{userId: props?.uidProvisional || Meteor.userId()}}, {sort: {name: 1}}).fetch(); // Meteor.userId() ?? "nop" + + return files; + }); + + + + function uploadIt(e) { + e.preventDefault(); + + //let self = this; + + if (e.currentTarget.files && e.currentTarget.files[0]) { + // We upload only one file, in case + // there was multiple files selected + var file = e.currentTarget.files[0]; + + if (file) { + let uploadInstance = FilesCol.insert({ + file, + meta: { + locator: props.fileLocator, + userId: props.uidProvisional || Meteor.userId() // Optional, used to check on server for file tampering + }, + chunkSize: 'dynamic', + allowWebWorkers: true // If you see issues with uploads, change this to false + }, false) + + setUploading(uploadInstance); // Keep track of this instance to use below + setInProgress(true);// Show the progress bar now + + // These are the event functions, don't need most of them, it shows where we are in the process + uploadInstance.on('start', function () { + console.log('Starting'); + }) + + uploadInstance.on('end', function (error, fileObj) { + console.log('On end File Object: ', fileObj); + }) + + uploadInstance.on('uploaded', function (error, fileObj) { + console.log('uploaded: ', fileObj); + + props.setUpId(fileObj?._id); + + // Remove the filename from the upload box + fileinput.current.value = ''; + + // Reset our state for the next file + setUploading([]); + setProgress(0); + setInProgress(false); + }) + + uploadInstance.on('error', function (error, fileObj) { + console.log('Error during upload: ' + error) + }); + + uploadInstance.on('progress', function (progress, fileObj) { + console.log('Upload Percentage: ' + progress) + // Update our progress bar + setProgress(progress); + }); + + uploadInstance.start(); // Must manually start the upload + } + } + } + + // This is our progress bar, bootstrap styled + // Remove this function if not needed + function showUploads() { + console.log('**********************************', uploading); + + if (!_.isEmpty(uploading)) { + return
+ {uploading.file.name} + +
+
+ {progress}% Complete (success) + {progress}% +
+
+
+ } + } + + + { + // debug("Rendering FileUpload",docsReadyYet); + if (files /* && docsReadyYet*/) { + + let fileCursors = files; + + // Run through each file that the user has stored + // (make sure the subscription only sends files owned by this user) + let display = fileCursors.map((aFile, key) => { + // console.log('A file: ', aFile.link(), aFile.get('name')) + let link = FilesCol.findOne({_id: aFile._id}).link(); //The "view/download" link + + // Send out components that show details of each file + return
+ +
+ }) + + return
+ +
+
+

Upload New File:

+ +
+
+ +
+
+ + {showUploads()} + +
+
+
+
+ + {display} + +
+ } + else return
Loading file list
; + } +}; + +// +// This is the HOC - included in this file just for convenience, but usually kept +// in a separate file to provide separation of concerns. +// +// export default withTracker( ( props ) => { + +// const filesHandle = Meteor.subscribe('files.all'); +// const docsReadyYet = filesHandle.ready(); +// const files = FilesCol.find({meta:{userId: props.uidProvisional || Meteor.userId()}}, {sort: {name: 1}}).fetch(); // Meteor.userId() ?? "nop" + +// return { +// docsReadyYet, +// files, +// }; + +// })(FileUploadComponent); + +export default FileUploadComponent; \ No newline at end of file diff --git a/imports/ui/files/FileUpload_ori.jsx b/imports/ui/files/FileUpload_ori.jsx new file mode 100644 index 0000000..4c311ae --- /dev/null +++ b/imports/ui/files/FileUpload_ori.jsx @@ -0,0 +1,186 @@ +import { withTracker } from 'meteor/react-meteor-data'; +import { Meteor } from 'meteor/meteor'; +import React, { Component } from 'react'; +// import PropTypes from 'prop-types'; +import { FilesCol } from '/imports/api/files.js'; + +import IndividualFile from '/imports/ui/files/IndividualFile.jsx'; + +import _ from 'lodash'; + +const debug = require('debug')('demo:file'); + +class FileUploadComponent extends Component { + constructor(props) { + super(props); + + this.state = { + uploading: [], + progress: 0, + inProgress: false + }; + + this.uploadIt = this.uploadIt.bind(this); + } + + uploadIt(e) { + e.preventDefault(); + + let self = this; + + if (e.currentTarget.files && e.currentTarget.files[0]) { + // We upload only one file, in case + // there was multiple files selected + var file = e.currentTarget.files[0]; + + if (file) { + let uploadInstance = FilesCol.insert({ + file: file, + meta: { + locator: self.props.fileLocator, + userId: this.props.uidProvisional || Meteor.userId() // Optional, used to check on server for file tampering + }, + chunkSize: 'dynamic', + allowWebWorkers: true // If you see issues with uploads, change this to false + }, false) + + self.setState({ + uploading: uploadInstance, // Keep track of this instance to use below + inProgress: true // Show the progress bar now + }); + + // These are the event functions, don't need most of them, it shows where we are in the process + uploadInstance.on('start', function () { + console.log('Starting'); + }) + + uploadInstance.on('end', function (error, fileObj) { + console.log('On end File Object: ', fileObj); + //this.props?.setAvatarId(fileObj._id); + }) + + uploadInstance.on('uploaded', function (error, fileObj) { + console.log('uploaded: ', fileObj); + //console.log(`setAvatarId: ${this.props?.setAvatarId}`); + + // this.props.setUpId(fileObj._id); + // Remove the filename from the upload box + self.refs['fileinput'].value = ''; + + // Reset our state for the next file + self.setState({ + uploading: [], + progress: 0, + inProgress: false + }); + }) + + uploadInstance.on('error', function (error, fileObj) { + console.log('Error during upload: ' + error) + }); + + uploadInstance.on('progress', function (progress, fileObj) { + console.log('Upload Percentage: ' + progress) + // Update our progress bar + self.setState({ + progress: progress + }); + }); + + uploadInstance.start(); // Must manually start the upload + } + } + } + + // This is our progress bar, bootstrap styled + // Remove this function if not needed + showUploads() { + console.log('**********************************', this.state.uploading); + + if (!_.isEmpty(this.state.uploading)) { + return
+ {this.state.uploading.file.name} + +
+
+ {this.state.progress}% Complete (success) + {this.state.progress}% +
+
+
+ } + } + + render() { + debug("Rendering FileUpload",this.props.docsReadyYet); + if (this.props.files && this.props.docsReadyYet) { + + let fileCursors = this.props.files; + + // Run through each file that the user has stored + // (make sure the subscription only sends files owned by this user) + let display = fileCursors.map((aFile, key) => { + // console.log('A file: ', aFile.link(), aFile.get('name')) + let link = FilesCol.findOne({_id: aFile._id}).link(); //The "view/download" link + + // Send out components that show details of each file + return
+ +
+ }) + + return
+
+
+

Upload New File:

+ +
+
+ +
+
+ + {this.showUploads()} + +
+
+
+
+ + {display} + +
+ } + else return
Loading file list
; + } +} + +// +// This is the HOC - included in this file just for convenience, but usually kept +// in a separate file to provide separation of concerns. +// +export default withTracker( ( props ) => { + const filesHandle = Meteor.subscribe('files.all'); + const docsReadyYet = filesHandle.ready(); + const files = FilesCol.find({meta:{userId: props.uidProvisional || Meteor.userId()}}, {sort: {name: 1}}).fetch(); // Meteor.userId() ?? "nop" +// const setAvatarId = props.setAvatarId; + + return { + docsReadyYet, + files, + // setAvatarId + }; +})(FileUploadComponent); \ No newline at end of file diff --git a/imports/ui/files/Files.jsx b/imports/ui/files/Files.jsx new file mode 100644 index 0000000..2ae742b --- /dev/null +++ b/imports/ui/files/Files.jsx @@ -0,0 +1,34 @@ +// import { Meteor } from 'meteor/meteor'; +import React from 'react'; +import { useSubscribe, useFind, useTracker } from 'meteor/react-meteor-data/suspense'; + +// import { FilesCollection } from '/imports/api/files.js'; +import { FilesCol } from '/imports/api/files.js'; +import FileUploadStart from '/imports/ui/files/FileUploadStart.jsx'; + + +const Files = () => { + + useSubscribe('files.all'); + + const files = useTracker("files", () => { + const files = FilesCol.find({}).fetchAsync(); + return files; + }); + + return <> +

Files

+ { + + + + } + ; +}; + +export { Files }; \ No newline at end of file diff --git a/imports/ui/files/FilesCartellEvent/FileUploadCartellEvent.jsx b/imports/ui/files/FilesCartellEvent/FileUploadCartellEvent.jsx new file mode 100644 index 0000000..f06b153 --- /dev/null +++ b/imports/ui/files/FilesCartellEvent/FileUploadCartellEvent.jsx @@ -0,0 +1,217 @@ +import { useTracker } from 'meteor/react-meteor-data'; +import { Meteor } from 'meteor/meteor'; +import React, { useState, useRef } from 'react'; +// import PropTypes from 'prop-types'; +import { FilesCol } from '/imports/api/files.js'; + +import { IndividualFileCartellEvent } from '/imports/ui/files/FilesCartellEvent/IndividualFileCartellEvent.jsx'; + +import _ from 'lodash'; + +//const debug = require('debug')('demo:file'); + +const FileUploadCartellEvent = (props) => { + + //////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////// + const cartellId = new Mongo.ObjectID(); + ///////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////// + const [upId, setUpId] = useState(null); + + const [uploading, setUploading] = useState([]); + const [progress, setProgress] = useState(0); + const [inProgress, setInProgress] = useState(false); + + const fileinput = useRef(); + + const files = useTracker(() => { + const filesHandle = Meteor.subscribe('files.cartellEventUpload', upId); + // const docsReadyYet = filesHandle.ready(); + const files = FilesCol?.find(upId).fetch(); // Meteor.userId() ?? "nop" //"meta.avatarId": avatarId + + return files; + }); + + + function uploadIt(e) { + e.preventDefault(); + + //let self = this; + + if (e.currentTarget.files && e.currentTarget.files[0]) { + // We upload only one file, in case + // there was multiple files selected + var file = e.currentTarget.files[0]; + + if (file) { + let uploadInstance = FilesCol.insert({ + file, + meta: { + // locator: props.fileLocator, + // userId: props.uidProvisional || Meteor.userId(), // Optional, used to check on server for file tampering + // eventId: props.eventId + }, + chunkSize: 'dynamic', + allowWebWorkers: true // If you see issues with uploads, change this to false + }, false) + + setUploading(uploadInstance); // Keep track of this instance to use below + setInProgress(true); // Show the progress bar now + + // These are the event functions, don't need most of them, it shows where we are in the process + uploadInstance.on('start', function () { + console.log('Starting'); + }) + + uploadInstance.on('end', function (error, fileObj) { + console.log('On end File Object: ', fileObj); + + props.setCartellId(fileObj._id); + }) + + uploadInstance.on('uploaded', function (error, fileObj) { + console.log('uploaded: ', fileObj); + + setUpId(fileObj._id); + + // Remove the filename from the upload box + fileinput.current.value = ''; + + // Reset our state for the next file + setUploading([]); + setProgress(0); + setInProgress(false); + + props.setNovaImg(true); + }) + + uploadInstance.on('error', function (error, fileObj) { + console.log('Error during upload: ' + error) + }); + + uploadInstance.on('progress', function (progress, fileObj) { + console.log('Upload Percentage: ' + progress) + // Update our progress bar + setProgress(progress); + }); + + uploadInstance.start(); // Must manually start the upload + } + } + } + + // This is our progress bar, bootstrap styled + // Remove this function if not needed + function showUploads() { + console.log('**********************************', uploading); + + if (!_.isEmpty(uploading)) { + return
+ {uploading.file.name} + +
+
+ {progress}% Complete (success) + {progress}% +
+
+
+ } + } + + + //{ + // debug("Rendering FileUpload",docsReadyYet); + // if (files /* && docsReadyYet*/) { + + // let fileCursors = files; + + // Run through each file that the user has stored + // (make sure the subscription only sends files owned by this user) + let display = files?.map((aFile, key) => { + // console.log('A file: ', aFile.link(), aFile.get('name')) + let link = FilesCol.findOne({_id: upId})?.link(); //The "view/download" link + + props.setCartellLink(link); + + // Send out components that show details of each file + return
+ + {/* */} +
+ }) + + return
+ +
+
+

Upload New File:

+ +
+ +
+
+ +
+
+ + {showUploads()} + +
+
+
+
+ + {display} + +
+ // } + // else return
Loading file list
; + // } +}; + +// +// This is the HOC - included in this file just for convenience, but usually kept +// in a separate file to provide separation of concerns. +// +// export default withTracker( ( props ) => { + +// const filesHandle = Meteor.subscribe('files.all'); +// const docsReadyYet = filesHandle.ready(); +// const files = FilesCol.find({meta:{userId: props.uidProvisional || Meteor.userId()}}, {sort: {name: 1}}).fetch(); // Meteor.userId() ?? "nop" + +// return { +// docsReadyYet, +// files, +// }; + +// })(FileUploadComponent); + +export { FileUploadCartellEvent }; \ No newline at end of file diff --git a/imports/ui/files/FilesCartellEvent/IndividualFileCartellEvent.jsx b/imports/ui/files/FilesCartellEvent/IndividualFileCartellEvent.jsx new file mode 100644 index 0000000..a567763 --- /dev/null +++ b/imports/ui/files/FilesCartellEvent/IndividualFileCartellEvent.jsx @@ -0,0 +1,84 @@ +import React from 'react'; +// import PropTypes from 'prop-types'; + +const IndividualFileCartellEvent = props => { + +// propTypes: { +// fileName: PropTypes.string.isRequired, +// fileSize: PropTypes.number.isRequired, +// fileUrl: PropTypes.string, +// fileId: PropTypes.string.isRequired +// } + + function removeFile(){ + let conf = confirm('Are you sure you want to delete the file?') || false; + if (conf == true) { + Meteor.call('RemoveFile', props.fileId, function (err, res) { + if (err) + console.log(err); + }) + } + } + + function renameFile(){ + + let validName = /[^a-zA-Z0-9 \.:\+()\-_%!&]/gi; + let prompt = window.prompt('New file name?', props.fileName); + + // Replace any non valid characters, also do this on the server + if (prompt) { + prompt = prompt.replace(validName, '-'); + prompt.trim(); + } + + if (!_.isEmpty(prompt)) { + Meteor.call('RenameFile', props.fileId, prompt, function (err, res) { + if (err) + console.log(err); + }) + } + } + + return
+ + {props.fileName} + + + {/*
+
+ {props.fileName} +
+
+
+
+ +
+
+ +
+ + +
+ View +
+ +
+ +
+ +
+ Size: {props.fileSize} +
+
*/} +
+ } +export { IndividualFileCartellEvent }; \ No newline at end of file diff --git a/imports/ui/files/FolderTreeFileUpload.jsx b/imports/ui/files/FolderTreeFileUpload.jsx new file mode 100644 index 0000000..6d75d8a --- /dev/null +++ b/imports/ui/files/FolderTreeFileUpload.jsx @@ -0,0 +1,210 @@ +import { useTracker } from 'meteor/react-meteor-data'; +import { Meteor } from 'meteor/meteor'; +import { Mongo } from 'meteor/mongo'; +import React, { useState, useRef } from 'react'; +// import PropTypes from 'prop-types'; +import { FilesCol } from '/imports/api/files.js'; + +import FolderTreeIndividualFile from '/imports/ui/files/FolderTreeIndividualFile.jsx'; + +import _, { upperCase } from 'lodash'; + +const debug = require('debug')('demo:file'); + + +const FolderTreeFileUpload = (props) => { +//////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + const treeFileId = new Mongo.ObjectID(); +///////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + const [upId, setUpId] = useState(null); + + const [uploading, setUploading] = useState([]); + const [progress, setProgress] = useState(0); + const [inProgress, setInProgress] = useState(false); + + const fileinput = useRef(); + + const files = useTracker(() => { + const filesHandle = Meteor.subscribe('files.folderTree', upId); + // const docsReadyYet = filesHandle.ready(); + const files = FilesCol?.find(upId).fetch(); // Meteor.userId() ?? "nop" //"meta.avatarId": avatarId + + return files; + }); + + function uploadIt(e) { + e.preventDefault(); + + //let self = this; + + if (e.currentTarget.files && e.currentTarget.files[0]) { + // We upload only one file, in case + // there was multiple files selected + var file = e.currentTarget.files[0]; + + if (file) { + console.log("IfFile: ", file); + let uploadInstance = FilesCol.insert({ + file, + meta: { + //locator: file.link(), + userId: Meteor.userId(), // Optional, used to check on server for file tampering + type: "folderTree" + //avatarId + }, + chunkSize: 'dynamic', + allowWebWorkers: true // If you see issues with uploads, change this to false + }, false); + + setUploading(uploadInstance); // Keep track of this instance to use below + setInProgress(true); // Show the progress bar now + + // These are the event functions, don't need most of them, it shows where we are in the process + uploadInstance.on('start', function () { + console.log('Starting'); + }); + + uploadInstance.on('end', function (error, fileObj) { + console.log('On end File Object: ', fileObj); + + props.setFolderTreeId(fileObj._id); + + + }); + + uploadInstance.on('uploaded', function (error, fileObj) { + console.log('uploaded: ', fileObj); + + setUpId(fileObj._id); + + // Remove the filename from the upload box + fileinput.current.value = ''; + + // Reset our state for the next file + setUploading([]); + setProgress(0); + setInProgress(false); + + props.setNouFolderTree(true); + }); + + uploadInstance.on('error', function (error, fileObj) { + console.log('Error during upload: ' + error) + }); + + uploadInstance.on('progress', function (progress, fileObj) { + console.log('Upload Percentage: ' + progress) + // Update our progress bar + setProgress(progress); + }); + + uploadInstance.start(); // Must manually start the upload + } + } + } + + // This is our progress bar, bootstrap styled + // Remove this function if not needed + function showUploads() { + console.log('**********************************', uploading); + + if (!_.isEmpty(uploading)) { + return
+ {uploading.file.name} + +
+
+ {progress}% Complete (success) + {progress}% +
+
+
+ } + } + + + { + // debug("Rendering FileUpload",docsReadyYet); + //if (files.length /* && docsReadyYet*/) { + + //let fileCursors = files; + + // Run through each file that the user has stored + // (make sure the subscription only sends files owned by this user) + let display = files?.map((aFile, key) => { + // console.log('A file: ', aFile.link(), aFile.get('name')) + let link = FilesCol?.findOne({_id: upId})?.link(); //The "view/download" link + + alert(`Enllaç a l'arxiu: ${link}`); + + props.setFolderTreeLink(link); + + // Send out components that show details of each file + return
+ {props.nouFolderTree && } +
+ }) + + return
+
+
+

Adjunta un arxiu FolderTree*:

+ +
+
+ +
+
+ + {showUploads()} + +
+ +
+ + {display} + +

* Un FolderTree és un arxiu en format JSON generat per un commandament 'treedump'** o 'treedumpl'***

+

{`** treedump () { + tree -J -s -f \${@[$#]} +}`}

+ +
+ // } + // return null; + } +}; + +// +// This is the HOC - included in this file just for convenience, but usually kept +// in a separate file to provide separation of concerns. +// +// export default withTracker( ( props ) => { + +// const filesHandle = Meteor.subscribe('files.all'); +// const docsReadyYet = filesHandle.ready(); +// const files = FilesCol.find({meta:{userId: props.uidProvisional || Meteor.userId()}}, {sort: {name: 1}}).fetch(); // Meteor.userId() ?? "nop" + +// return { +// docsReadyYet, +// files, +// }; + +// })(FileUploadComponent); + +export default FolderTreeFileUpload; \ No newline at end of file diff --git a/imports/ui/files/FolderTreeIndividualFile.jsx b/imports/ui/files/FolderTreeIndividualFile.jsx new file mode 100644 index 0000000..4daa678 --- /dev/null +++ b/imports/ui/files/FolderTreeIndividualFile.jsx @@ -0,0 +1,78 @@ +import React from 'react'; +// import PropTypes from 'prop-types'; + +const FolderTreeIndividualFile = props => { + +// propTypes: { +// fileName: PropTypes.string.isRequired, +// fileSize: PropTypes.number.isRequired, +// fileUrl: PropTypes.string, +// fileId: PropTypes.string.isRequired +// } + + function removeFile(){ + let conf = confirm('Are you sure you want to delete the file?') || false; + if (conf == true) { + Meteor.call('RemoveFile', props.fileId, function (err, res) { + if (err) + console.log(err); + }); + } + } + + function renameFile(){ + + let validName = /[^a-zA-Z0-9 \.:\+()\-_%!&]/gi; + let prompt = window.prompt('New file name?', props.fileName); + + // Replace any non valid characters, also do this on the server + if (prompt) { + prompt = prompt.replace(validName, '-'); + prompt.trim(); + } + + if (!_.isEmpty(prompt)) { + Meteor.call('RenameFile', props.fileId, prompt, function (err, res) { + if (err) + console.log(err); + }); + } + } + + return
+ {props.fileName} + +
+
+ {props.fileName} +
+
+
+
+ + {/*
+
+ +
+ + +
+ View +
+ +
+ +
+ +
+ Size: {props.fileSize} +
+
*/} +
+ } +export default FolderTreeIndividualFile; \ No newline at end of file diff --git a/imports/ui/files/IndividualFile.jsx b/imports/ui/files/IndividualFile.jsx new file mode 100644 index 0000000..ce753d6 --- /dev/null +++ b/imports/ui/files/IndividualFile.jsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { Meteor } from 'meteor/meteor'; +// import PropTypes from 'prop-types'; + +const IndividualFile = props => { + +// propTypes: { +// fileName: PropTypes.string.isRequired, +// fileSize: PropTypes.number.isRequired, +// fileUrl: PropTypes.string, +// fileId: PropTypes.string.isRequired +// } + + function removeFile(){ + let conf = confirm('Are you sure you want to delete the file?') || false; + if (conf == true) { + Meteor.call('RemoveFile', props.fileId, function (err, res) { + if (err) + console.log(err); + }) + } + } + + function renameFile(){ + + let validName = /[^a-zA-Z0-9 \.:\+()\-_%!&]/gi; + let prompt = window.prompt('New file name?', props.fileName); + + // Replace any non valid characters, also do this on the server + if (prompt) { + prompt = prompt.replace(validName, '-'); + prompt.trim(); + } + + if (!_.isEmpty(prompt)) { + Meteor.call('RenameFile', props.fileId, prompt, function (err, res) { + if (err) + console.log(err); + }) + } + } + + return
+ {props.fileName} + +
+
+ {props.fileName} +
+
+
+
+ +
+
+ +
+ + +
+ View +
+ +
+ +
+ +
+ Size: {props.fileSize} +
+
+
+ } +export default IndividualFile; \ No newline at end of file diff --git a/imports/ui/files/IndividualFileEnt.jsx b/imports/ui/files/IndividualFileEnt.jsx new file mode 100644 index 0000000..bb87174 --- /dev/null +++ b/imports/ui/files/IndividualFileEnt.jsx @@ -0,0 +1,85 @@ +import React from 'react'; +// import PropTypes from 'prop-types'; + +const IndividualFileEnt = props => { + +// propTypes: { +// fileName: PropTypes.string.isRequired, +// fileSize: PropTypes.number.isRequired, +// fileUrl: PropTypes.string, +// fileId: PropTypes.string.isRequired +// } + + function removeFile(){ + let conf = confirm('Are you sure you want to delete the file?') || false; + if (conf == true) { + Meteor.call('RemoveFile', props.fileId, function (err, res) { + if (err) + console.log(err); + }) + } + } + + function renameFile(){ + + let validName = /[^a-zA-Z0-9 \.:\+()\-_%!&]/gi; + let prompt = window.prompt('New file name?', props.fileName); + + // Replace any non valid characters, also do this on the server + if (prompt) { + prompt = prompt.replace(validName, '-'); + prompt.trim(); + } + + if (!_.isEmpty(prompt)) { + Meteor.call('RenameFile', props.fileId, prompt, function (err, res) { + if (err) + console.log(err); + }) + } + } + + return
+ + {props.fileName} + + + {/*
+
+ {props.fileName} +
+
+
+
+ +
+
+ +
+ + +
+ View +
+ +
+ +
+ +
+ Size: {props.fileSize} +
+
*/} +
+ } +export default IndividualFileEnt; \ No newline at end of file diff --git a/imports/ui/files/IndividualFilePOI.jsx b/imports/ui/files/IndividualFilePOI.jsx new file mode 100644 index 0000000..7b5ced7 --- /dev/null +++ b/imports/ui/files/IndividualFilePOI.jsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { Meteor } from 'meteor/meteor'; +// import PropTypes from 'prop-types'; + +const IndividualFile = props => { + +// propTypes: { +// fileName: PropTypes.string.isRequired, +// fileSize: PropTypes.number.isRequired, +// fileUrl: PropTypes.string, +// fileId: PropTypes.string.isRequired +// } + + function removeFile(ev) { + // ev.preventDefault(); + ev.stopPropagation(); + + let conf = confirm('Are you sure you want to delete the file?') || false; + if (conf == true) { + Meteor.callAsync('RemoveFile', props.fileId) + .catch (err => { + console.log(err); + }) + } + } + + async function renameFile() { + let validName = /[^a-zA-Z0-9 \.:\+()\-_%!&]/gi; + let prompt = window.prompt('New file name?', props.fileName); + + // Replace any non valid characters, also do this on the server + if (prompt) { + prompt = prompt.replace(validName, '-'); + prompt.trim(); + } + + if (!_.isEmpty(prompt)) { + await Meteor.callAsync('RenameFile', props.fileId, prompt) + .catch(err => { + console.log(err); + }) + } + } + + return
+ {props.fileName} + +
+
+ {props.fileName} +
+
+
+
+ +
+
+ +
+ + +
+ View +
+ +
+ +
+ +
+ Size: {props.fileSize} +
+
+
+ } +export default IndividualFile; \ No newline at end of file diff --git a/imports/ui/files/IndividualFileStart.jsx b/imports/ui/files/IndividualFileStart.jsx new file mode 100644 index 0000000..5ef54fb --- /dev/null +++ b/imports/ui/files/IndividualFileStart.jsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { Meteor } from 'meteor/meteor'; +// import PropTypes from 'prop-types'; + +const IndividualFileStart = props => { + +// propTypes: { +// fileName: PropTypes.string.isRequired, +// fileSize: PropTypes.number.isRequired, +// fileUrl: PropTypes.string, +// fileId: PropTypes.string.isRequired +// } + + function removeFile() { + let conf = confirm('Are you sure you want to delete the file?') || false; + if (conf == true) { + Meteor.callAsync('RemoveFile', props.fileId, function (err, res) { + if (err) + console.log(err); + }) + } + } + + function renameFile() { + + let validName = /[^a-zA-Z0-9 \.:\+()\-_%!&]/gi; + let prompt = window.prompt('New file name?', props.fileName); + + // Replace any non valid characters, also do this on the server + if (prompt) { + prompt = prompt.replace(validName, '-'); + prompt.trim(); + } + + if (!_.isEmpty(prompt)) { + Meteor.callAsync('RenameFile', props.fileId, prompt, function (err, res) { + if (err) + console.log(err); + }) + } + } + + return
+ + {props.fileName} + + + {/*
+
+ {props.fileName} +
+
+
+
+ +
+
+ +
+ + +
+ View +
+ */} + +
+ +
+ +
+ Size: {props.fileSize} +
+ {/*
*/} +
+ } +export default IndividualFileStart; \ No newline at end of file diff --git a/imports/ui/files/ListPersons.jsx b/imports/ui/files/ListPersons.jsx new file mode 100644 index 0000000..0671615 --- /dev/null +++ b/imports/ui/files/ListPersons.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { useTracker } from 'meteor/react-meteor-data'; + +const ListPersons = () => { + + return
    + { + persons.map((person, i) => { + return
  • + {{#if update _id}} + {{> quickForm type="update" collection="Persons" doc=this id=getFormId}} + + {{else}} +
    +

    {{name}}

    + {{#each profilePic.each}} + + {{/each}} +
    + + {{/if}} +
  • + }) + } +
+} diff --git a/imports/ui/files/LlistaArxiusEnt.jsx b/imports/ui/files/LlistaArxiusEnt.jsx new file mode 100644 index 0000000..40ddddb --- /dev/null +++ b/imports/ui/files/LlistaArxiusEnt.jsx @@ -0,0 +1,87 @@ +import { useTracker } from 'meteor/react-meteor-data'; +import { Meteor } from 'meteor/meteor'; +import React, { useState, useRef } from 'react'; +// import PropTypes from 'prop-types'; +import { FilesCol } from '/imports/api/files.js'; + +import IndividualFileEnt from '/imports/ui/files/IndividualFileEnt.jsx'; + +import _ from 'lodash'; + + + +const LlistaArxiusEnt = (props) => { + + const files = useTracker(() => { + const filesHandle = Meteor.subscribe('files.all'); + // const docsReadyYet = filesHandle.ready(); + const files = FilesCol?.find({meta:{userId: props.uidProvisional || Meteor.userId(), entId: props.entId}}, {sort: {name: 1}}).fetch(); // Meteor.userId() ?? "nop" + + return files; + }); + + // This is our progress bar, bootstrap styled + // Remove this function if not needed + // function showUploads() { + // //console.log('**********************************', uploading); + + // if (!_.isEmpty(uploading)) { + // return
+ // {uploading.file.name} + + //
+ //
+ // {progress}% Complete (success) + // {progress}% + //
+ //
+ //
+ // } + // } + + { + // debug("Rendering FileUpload",docsReadyYet); + if (files /* && docsReadyYet*/) { + + let fileCursors = files; + + // Run through each file that the user has stored + // (make sure the subscription only sends files owned by this user) + let display = fileCursors.map((aFile, key) => { + // console.log('A file: ', aFile.link(), aFile.get('name')) + let link = FilesCol.findOne({_id: aFile._id}).link(); //The "view/download" link + + // Send out components that show details of each file + return
+ +
+ }) + + return
+ + {display} + +
+ } + else return
Loading file list
; + } +}; + +export default LlistaArxiusEnt; \ No newline at end of file diff --git a/imports/ui/files/LlistaArxiusFolderTM.jsx b/imports/ui/files/LlistaArxiusFolderTM.jsx new file mode 100644 index 0000000..9b86479 --- /dev/null +++ b/imports/ui/files/LlistaArxiusFolderTM.jsx @@ -0,0 +1,187 @@ +import { useTracker } from 'meteor/react-meteor-data'; +import { Meteor } from 'meteor/meteor'; +import React, { useState, useRef } from 'react'; +// import PropTypes from 'prop-types'; +import { FilesCol } from '/imports/api/files.js'; + +import FolderTreeIndividualFile from '/imports/ui/files/FolderTreeIndividualFile.jsx'; + +import _ from 'lodash'; + +import * as d3 from 'd3'; + + +const TreeMap = ({data, link}) => { + + console.table(data); + console.log("Link: ", link); + + fetch(link) + .then(x => x.text()) + .then(x => { + if(x.lastIndexOf("\n")>0) { + return x.substring(0, x.lastIndexOf("\n")); + } else { + return x; + } + //console.log(x); + }) + // .then(x => x.json()) + .then(x => JSON.parse(x)) + + .then(x => { + const dataset = x[0]; + + // Necessitem canviar el format de l'arbre i substituir "contents" per "children". De moment. + function tmFormat(arbre) { + + if (arbre.hasOwnProperty("contents")) { + + arbre.contents.forEach(node => { + + let nodeMod; + + if (node.hasOwnProperty("children")) { + nodeMod = { + ...node, + children: node.contents, + value: node.size + }; + } else { + nodeMod = { + ...node, + value: node.size + } + ; + } + + return tmFormat(nodeMod); + }); + + } else { + + const nouArbre = { + ...arbre, + value: arbre.size + }; + + return nouArbre; + + } + } + + const tmData = tmFormat(dataset); + + const hierarchyTree = d3.hierarchy(tmData) + .sum(d => d.value) //sum every child's values + .sort((a, b) => b.value - a.value) + ; + + //const hierarchyTree = d3.hierarchy(tmData); + + console.log("Dataset: ", dataset); + console.log("TMData: ", tmData); + console.log("Hierarchy: ", hierarchyTree); + }) + // .then(x => x.json()) + // .then(x => console.log(x)) + //.then(x => console.table(x)) + + ; + + + return null; +} + + +const LlistaArxiusFolderTM = (props) => { + + const files = useTracker(() => { + const filesHandle = Meteor.subscribe('files.folderTree'); + // const docsReadyYet = filesHandle.ready(); + const files = FilesCol?.find({'meta.type': 'folderTree'}, {sort: {name: 1}}).fetch(); // Meteor.userId() ?? "nop" + + return files; + }); + + + // const methodCall = (methodName, ...args) => + // new Promise((resolve, reject) => { + // Meteor.call(methodName, ...args, (error, result) => { + // if (error) reject(error); + // else resolve(result); + // }); + // }); + // ; + + // const files = await methodCall('getFolderTreeFiles'); + + // This is our progress bar, bootstrap styled + // Remove this function if not needed + // function showUploads() { + // //console.log('**********************************', uploading); + + // if (!_.isEmpty(uploading)) { + // return
+ // {uploading.file.name} + + //
+ //
+ // {progress}% Complete (success) + // {progress}% + //
+ //
+ //
+ // } + // } + + { + // debug("Rendering FileUpload",docsReadyYet); + if (files /* && docsReadyYet*/) { + + let fileCursors = files; + + // Run through each file that the user has stored + // (make sure the subscription only sends files owned by this user) + let display = fileCursors.map((aFile, key) => { + // console.log('A file: ', aFile.link(), aFile.get('name')) + let link = FilesCol.findOne({_id: aFile._id}).link(); //The "view/download" link + + // Send out components that show details of each file + return <> +
+ +
+ + + + }) + + return
+ + {display} + +
+ } + else return
Loading file list
; + } +}; + +export default LlistaArxiusFolderTM; \ No newline at end of file diff --git a/server/main.js b/server/main.js index 6303975..7638dd6 100644 --- a/server/main.js +++ b/server/main.js @@ -1,12 +1,13 @@ import { Meteor } from 'meteor/meteor'; import { PoblesCollection } from '/imports/api/pobles.js'; -import { check } from "meteor/check"; +// import { check } from "meteor/check"; import { Roles } from 'meteor/roles'; import { ROLS_GLOBALS, ROLS_DE_POBLE } from '../imports/roles'; import { NecessitatsCollection } from '../imports/api/necessitats'; import { TipusCollection } from '../imports/api/tipus'; import { CodisCollection } from '../imports/api/codis'; +import { Avatars } from '../imports/api/files'; // import { Codis } from '../imports/ui/Codis'; async function insertPoble({ nomPoble, cp, comarca }) { @@ -439,11 +440,11 @@ Meteor.methods({ const ara = new Date(); let dataIni, dataFi; - console.log("codiObj.periode_validesa_ini: ", await codiObj.periode_validesa_ini); + console.log("codiObj.condIni: ", await codiObj.condIni); - if (codiObj.act_cond === "on") { - dataIni = new Date(await codiObj.periode_validesa_ini); - dataFi = new Date(await codiObj.periode_validesa_fi); + if (codiObj.absCond === "cond") { + dataIni = new Date(await codiObj.condIni); + dataFi = new Date(await codiObj.condFi); } // const dataIni = codiObj.act_cond === "on" ? new Date(codiObj.periode_validesa_ini) : null; // const dataFi = codiObj.act_cond === "on" ? new Date(codiObj.periode_validesa_fi) : null; @@ -452,12 +453,12 @@ Meteor.methods({ console.log("ara: ", ara); console.log("dataIni: ", dataIni); console.log("dataFi: ", dataFi); - console.log(`codiObj.act_cond === "on"`, codiObj.act_cond === "on"); + console.log(`codiObj.absCond`, codiObj.absCond); try { console.log(`APLICANT CODI "${codi}" sobre "${userId}".`); // Comprovant si ${Meteor.userId()} és Admin: `, esAdmin); - if ((codiObj.act_cond==="on" && (ara >= dataIni && ara <= dataFi)) || codiObj.act_abs) { + if ((codiObj.absCond==="cond" && (ara >= dataIni && ara <= dataFi)) || codiObj.actAbs ) { Roles.addUsersToRolesAsync(userId, codiObj.rol, codiObj.ambit); } @@ -467,6 +468,182 @@ Meteor.methods({ }, + async 'registraUsuariAmbAvatarICodi'(uObj, codi, avatar) { + + if (codi) { + + console.log("+codi"); + + if (avatar) { + + console.log("+avatar"); + + // Upload the avatar to GridFS + const avatarId = Avatars.insert(avatar.file, (err, fileObj) => { + if (err) { + throw new Meteor.Error('avatar-upload-failed', 'Failed to upload avatar'); + } + }); + + const codiObj = await CodisCollection.findOneAsync({codi}); + + // Create the new user + const userId = Accounts.createUserAsync({ + ...uObj, + profile: { + avatar: avatarId, + }, + }); + + // Return the new user's ID + // return userId; + + // const userId = await Accounts.createUserAsync(uObj); + + const ara = new Date(); + let dataIni, dataFi; + + console.log("codiObj.condIni: ", await codiObj.condIni); + + if (codiObj.absCond === "cond") { + dataIni = new Date(await codiObj.condIni); + dataFi = new Date(await codiObj.condFi); + } + // const dataIni = codiObj.act_cond === "on" ? new Date(codiObj.periode_validesa_ini) : null; + // const dataFi = codiObj.act_cond === "on" ? new Date(codiObj.periode_validesa_fi) : null; + + console.log("codiObj: ", codiObj); + console.log("ara: ", ara); + console.log("dataIni: ", dataIni); + console.log("dataFi: ", dataFi); + console.log(`codiObj.absCond`, codiObj.absCond); + + try { + console.log(`APLICANT CODI "${codi}" sobre "${userId}".`); // Comprovant si ${Meteor.userId()} és Admin: `, esAdmin); + + if ((codiObj.absCond === "cond" && (ara >= dataIni && ara <= dataFi)) || codiObj.absActiu) { + Roles.addUsersToRolesAsync(userId, codiObj.rol, codiObj.ambit); + } + + } catch (e) { + console.error(e); + } + + } else { + + console.log("-avatar"); + + const codiObj = await CodisCollection.findOneAsync({codi}); + + const userId = await Accounts.createUserAsync({ + ...uObj, + profile: { + avatar: null + } + }); + + const ara = new Date(); + let dataIni, dataFi; + + console.log("codiObj.condIni: ", await codiObj.condIni); + + if (codiObj.absCond === "cond") { + dataIni = new Date(await codiObj.condIni); + dataFi = new Date(await codiObj.condFi); + } + // const dataIni = codiObj.act_cond === "on" ? new Date(codiObj.periode_validesa_ini) : null; + // const dataFi = codiObj.act_cond === "on" ? new Date(codiObj.periode_validesa_fi) : null; + + console.log("codiObj: ", codiObj); + console.log("ara: ", ara); + console.log("dataIni: ", dataIni); + console.log("dataFi: ", dataFi); + console.log(`codiObj.absCond`, codiObj.absCond); + + try { + console.log(`APLICANT CODI "${codi}" sobre "${userId}".`); // Comprovant si ${Meteor.userId()} és Admin: `, esAdmin); + + if ((codiObj.absCond === "cond" && (ara >= dataIni && ara <= dataFi)) || codiObj.absActiu) { + Roles.addUsersToRolesAsync(userId, codiObj.rol, codiObj.ambit); + } + + } catch (e) { + console.error(e); + } + + } + + } else { + // const codiObj = await CodisCollection.findOneAsync({codi}); + + console.log("-codi"); + + const userId = await Accounts.createUserAsync(uObj); + + // const ara = new Date(); + // let dataIni, dataFi; + + // console.log("codiObj.periode_validesa_ini: ", await codiObj.periode_validesa_ini); + + // if (codiObj.act_cond === "on") { + // dataIni = new Date(await codiObj.periode_validesa_ini); + // dataFi = new Date(await codiObj.periode_validesa_fi); + // } + // const dataIni = codiObj.act_cond === "on" ? new Date(codiObj.periode_validesa_ini) : null; + // const dataFi = codiObj.act_cond === "on" ? new Date(codiObj.periode_validesa_fi) : null; + + // console.log("codiObj: ", codiObj); + // console.log("ara: ", ara); + // console.log("dataIni: ", dataIni); + // console.log("dataFi: ", dataFi); + // console.log(`codiObj.act_cond === "on"`, codiObj.act_cond === "on"); + + try { + // console.log(`APLICANT CODI "${codi}" sobre "${userId}".`); // Comprovant si ${Meteor.userId()} és Admin: `, esAdmin); + + // if ((codiObj.act_cond==="on" && (ara >= dataIni && ara <= dataFi)) || codiObj.act_abs) { + Roles.addUsersToRolesAsync(userId, 'usuari', 'GENERAL'); + // } + + } catch (e) { + console.error(e); + } + } + + + + + // Check if the username and email are valid + // if (!username || !email || !password) { + // throw new Meteor.Error('invalid-input', 'Please fill in all fields'); + // } + + // // Check if the avatar is a valid file + // if (!avatar || !avatar.file) { + // throw new Meteor.Error('invalid-avatar', 'Please select a valid avatar image'); + // } + + // // Upload the avatar to GridFS + // const avatarId = Avatars.insert(avatar.file, (err, fileObj) => { + // if (err) { + // throw new Meteor.Error('avatar-upload-failed', 'Failed to upload avatar'); + // } + // }); + + console.log("-avatar-codi"); + + // Create the new user + const userId = Accounts.createUserAsync({ + ...uObj, + profile: { + avatar: null + } + }); + + // Return the new user's ID + return userId; + }, + 'usaCodiAmbUsuari': async function (userId, codi) { // const esAdmin = await Roles.userIsInRoleAsync(Meteor.userId(), "admin"); const codiObj = await CodisCollection.find({codi}).fetchAsync();