Version 0.2.0 - Ajout de l'explorateur de fichier, des ViewWindow, Des DroppableMenu
All checks were successful
Neutral/pipeline/head This commit looks good

This commit is contained in:
CICD - Pipeline 2023-11-03 22:21:21 +01:00
parent 5e97ff4853
commit a3369c7035
7 changed files with 901 additions and 64 deletions

View File

@ -6,13 +6,17 @@ const clog = new LogType("Fichier")
const os = require("os");
const uuid = require('uuid');
var mime = require('mime-types');
const { set } = require("../main")
// check if shared folder exist, if not create it
if(!fs.existsSync(__glob.SHARED)) {
fs.mkdirSync(__glob.SHARED)
}
module.exports.getFiles = function(root) {
var response = new Object()
response.content = new Array()
response.root = root
response.parent = path.dirname(root)
try{
@ -20,7 +24,12 @@ module.exports.getFiles = function(root) {
if(root == "homepath") {
root = os.homedir()
response.root = root
}
if(root == "sharepath") {
root = __glob.SHARED
}
@ -48,6 +57,178 @@ module.exports.getFiles = function(root) {
response.content = "NOT_PERMITTED"
}
response.root = root
response.parent = path.dirname(root)
return response
}
module.exports.createFolder = function(root) {
// Check if folder already exist
if(!fs.existsSync(root)) {
try {
fs.mkdirSync(root)
return "OK"
} catch(err) {
return "NOT_PERMITTED"
}
} else {
return 'EXIST'
}
}
module.exports.deleteFile = function(root) {
if(fs.existsSync(root)) {
try {
fs.rmSync(root, { recursive: true, force: true })
return "OK"
} catch(err) {
console.log(err)
return "NOT_PERMITTED"
}
} else {
return "NOT_EXIST"
}
}
module.exports.renameFile = function(settings) {
if(fs.existsSync(settings.root)) {
try {
fs.renameSync(settings.root + path.sep + settings.oldName, settings.root + path.sep + settings.newName)
return "OK"
} catch(err) {
return "NOT_PERMITTED"
}
} else {
return "NOT_EXIST"
}
}
module.exports.shareFile = function(settings) {
//It's gonna coopy the file in the shared folder
// And return the link of the file
// The link is gonna be like this : http://localhost:3000/shared/name of file
// The file is gonna be in the shared folder
if(fs.existsSync(settings.root)) {
try {
fs.copyFileSync(settings.root + path.sep + settings.name, __glob.SHARED + path.sep + settings.name)
// return the link of the file shared
if(process.env.DEV) {
return "http://localhost:3001/shared/" + settings.name
} else {
return "https://neutral.raphix.fr/shared/" + settings.name
}
} catch(err) {
console.log(err)
return "NOT_PERMITTED"
}
} else {
return "NOT_EXIST"
}
}
module.exports.getFile = function(root) {
if(fs.existsSync(root)) {
try {
return fs.readFileSync(root, "utf-8")
} catch(err) {
console.log(err)
return "NOT_PERMITTED"
}
} else {
return "NOT_EXIST"
}
}
module.exports.saveFile = function(settings) {
try {
fs.writeFileSync(settings.root + path.sep + settings.name, settings.content)
return "OK"
} catch(err) {
console.log(err)
return "NOT_PERMITTED"
}
}
module.exports.downloadFile = function(root) {
if(fs.existsSync(root)) {
try {
return fs.readFileSync(root)
} catch(err) {
console.log(err)
return "NOT_PERMITTED"
}
} else {
return "NOT_EXIST"
}
}
module.exports.uploadFile = function(settings) {
try {
fs.writeFileSync(settings.root + path.sep + settings.name, settings.file)
return "OK"
} catch(err) {
console.log(err)
return "NOT_PERMITTED"
}
}
module.exports.newFile = function(settings) {
if(!fs.existsSync(settings)) {
try {
fs.writeFileSync(settings, "")
return "OK"
} catch(err) {
console.log(err)
return "NOT_PERMITTED"
}
} else {
return "EXIST"
}
}

View File

@ -8,7 +8,8 @@ const __glob = {
ICON: root + path.sep + "public" + path.sep + 'images' + path.sep + "FormatLogo_WHITE.ico",
DATA: root + path.sep + "data",
USERS: root + path.sep + "data" + path.sep + "users.json",
CONFIG: root + path.sep + "data" + path.sep + "config.json"
CONFIG: root + path.sep + "data" + path.sep + "config.json",
SHARED: root + path.sep + "data" + path.sep + "shared",
};

View File

@ -30,9 +30,9 @@ module.exports.serverIO = function(server) {
if(user) {
plog.log("Connexion au panel par '" + user.username + "' avec le socket : " + socket.id)
user.setLastLogin(new Date())
/*
GET REQUEST
*/
/**
* GET REQUEST
*/
// Get Users
GetRequest("USERINFO", () => {
@ -40,11 +40,56 @@ module.exports.serverIO = function(server) {
GetAnswer("USERINFO", {username: user.username, display_name: user.display_name ,picture: user.picture, permission: user.permission})
})
/**
* POST REQUEST
*/
PostRequest("FX_GET", (root) => {
PostAnswer("FX_GET", files.getFiles(root))
})
PostRequest("FX_NEW_FOLDER", (root) => {
PostAnswer("FX_NEW_FOLDER", files.createFolder(root))
})
PostRequest("FX_DELETE", (root) => {
PostAnswer("FX_DELETE", files.deleteFile(root))
} )
PostRequest("FX_RENAME", (settings) => {
PostAnswer("FX_RENAME", files.renameFile(settings))
})
PostRequest("FX_SHARE", (settings) => {
PostAnswer("FX_SHARE", files.shareFile(settings))
})
PostRequest("FX_GETFILE", (root) => {
PostAnswer("FX_GETFILE", files.getFile(root))
})
PostRequest("FX_SAVEFILE", (settings) => {
PostAnswer("FX_SAVEFILE", files.saveFile(settings))
})
PostRequest("FX_NEW_FILE", (settings) => {
PostAnswer("FX_NEW_FILE", files.newFile(settings))
})
PostRequest("FX_UPLOAD", (settings) => {
PostAnswer("FX_UPLOAD", files.uploadFile(settings))
})
socket.on("disconnect", () => {
plog.log("Déconnexion au panel par '" + user.username + "' avec le socket : " + socket.id)

View File

@ -42,13 +42,12 @@ function setup() {
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')))
app.use("/shared", express.static(__glob.SHARED))
getRouters()
users.fetchUsers()

View File

@ -23,6 +23,8 @@ class InfoPop {
this.element.innerHTML = " "
this.element.style.fontSize = "14px"
this.element.style.position = "sticky"
this.element.style.width = this.element.parentElement.offsetWidth + "px"
this.element.style.textAlign = "center"
}
clear() {
@ -44,6 +46,11 @@ class InfoPop {
this.element.innerHTML = "<i class='fa fa-info-circle'></i> " + text
}
setSize(size) {
this.element.style.fontSize = size
}
}
@ -65,11 +72,15 @@ document.oncontextmenu = function(e) {
}
var zIndex = 5
class ViewWindow {
ViewHTML = null
ViewProperties =null
ViewSpanInteger = null
ViewPopupSpanInteger = null
ViewPopupHTML = null
ViewPopupTitle = null
constructor(properties) {
if(!viewsAccessible.has(properties.title)) {
@ -78,6 +89,7 @@ class ViewWindow {
this.ViewHTML = `
<div draggable="true" id='${properties.title}_window' style='width: ${properties.width}; height: ${properties.height}' class='view-window'>
<div id='${properties.title}_header' class='view-window-header'>
<span style='width: 40px'></span>
<p>${properties.title}</p>
@ -85,6 +97,8 @@ class ViewWindow {
</div>
<div id='${properties.title}_content' class='view-window-content'>
</div>
<div id='${properties.title}_popupDiv'></div>
</div>
`
this.ViewSpanInteger = document.createElement("div")
@ -103,10 +117,15 @@ class ViewWindow {
let isDragging = false;
let offsetX, offsetY;
windowDiv.style.zIndex = zIndex + 1
header.addEventListener('mousedown', (e) => {
isDragging = true;
zIndex+=2
windowDiv.style.zIndex = zIndex
// Enregistrez la position de la souris par rapport à la fenêtre
offsetX = e.clientX - windowDiv.getBoundingClientRect().left;
offsetY = e.clientY - windowDiv.getBoundingClientRect().top;
@ -145,7 +164,7 @@ class ViewWindow {
destroy() {
const win = getID(`${this.ViewProperties.title}_window`)
win.outerHTML = ""
win.outerHTML = " "
viewsAccessible.delete(this.ViewProperties.title)
}
@ -159,6 +178,66 @@ class ViewWindow {
return this.ViewProperties.title
}
createPopup(properties) {
this.ViewPopupHTML = `<div id='${properties.title}_popup' class='view-popup'>
<div class='view-popup-bar'>
<p>${properties.title}</p>
<span id='${properties.title}_popupClose' class='btn-cover'><i class='fa fa-xmark'></i></span>
</div>
${properties.content}
</div>`
const contentDiv = getID(this.ViewProperties.title + "_content")
const popupDiv = getID(this.ViewProperties.title + "_popupDiv")
this.ViewPopupSpanInteger = document.createElement("div")
popupDiv.appendChild(this.ViewPopupSpanInteger);
this.ViewPopupSpanInteger.outerHTML = this.ViewPopupHTML
this.ViewPopupTitle = properties.title
const popup = getID(properties.title + "_popup")
const popupClose = getID(properties.title + "_popupClose")
popupClose.addEventListener("click", () => {
this.destroyPopup()
})
// Center the popup to the window div
popup.style.left = (contentDiv.offsetWidth / 2) - (popup.offsetWidth / 2) + "px"
popup.style.top = (contentDiv.offsetHeight / 2) - (popup.offsetHeight / 2) + "px"
// Render parent element styled blur
contentDiv.classList.add("blur")
// Disable all interractions like click and events with contentDiv children
for(var child of contentDiv.children) {
child.style.pointerEvents = "none"
}
}
destroyPopup() {
const contentDiv = getID(this.ViewProperties.title + "_content")
for(var child of contentDiv.children) {
child.style.pointerEvents = "unset"
}
contentDiv.classList.remove("blur")
const popup = getID(this.ViewPopupTitle + "_popup")
popup.outerHTML = ""
}
}
createView("files_explorer")
@ -212,6 +291,10 @@ class DroppableMenu {
const menu = getID(`dm-${this.id}`)
menu.style.zIndex = zIndex + 2
zIndex+=1
menu.style.left = (xMousePos - 40) + "px"
menu.style.top = (yMousePos - 40) + "px"
@ -229,6 +312,17 @@ class DroppableMenu {
}
get(action) {
return getID(this.id + "_" + action)
}
hide() {
const menu = getID(`dm-${this.id}`)
menu.outerHTML = ""
}
}

View File

@ -20,16 +20,24 @@ function generateFileExplorer() {
})
}
function goSharePath() {
const rFiles = post("FX_GET", "sharepath")
rFiles.then((result) => {
loadFiles(result)
})
}
View.setContent(`
<div class="fx-window">
<div class='fx-bar'>
<span id='fx-home' class='btn-cover'><i class='fa fa-home'></i></span>
<span id='${View.getViewTitle()}_home' class='btn-cover'><i class='fa fa-home'></i></span>
<span id='${View.getViewTitle()}_sharebtn' class='btn-cover'><i class="fa-solid fa-share-from-square"></i></span>
<input class='fx-root-input' type="text" id='${View.getViewTitle()}_rootInput'>
<div class='fx-bar-actions'>
<button class='btn blue'><span><i class='fa fa-folder'></i> Nouv. dossier</span></button>
<button class='btn blue'><span><i class="fa-solid fa-file-arrow-up"></i> Upload</span></button>
<button id='${View.getViewTitle()}_newFolder' class='btn blue'><span><i class='fa fa-folder'></i> Nouv. dossier</span></button>
<button id='${View.getViewTitle()}_newFile' class='btn green'><span><i class="fa-solid fa-file-arrow-up"></i> Nouveau</span></button>
</div>
</div>
<div id='${View.getViewTitle()}_explorer' class='fx-explorer'>
@ -40,6 +48,195 @@ function generateFileExplorer() {
const rootInput = getID(View.getViewTitle() + '_rootInput')
const explorer = getID(View.getViewTitle() + '_explorer')
const newFolder = getID(View.getViewTitle() + '_newFolder')
const newFile = getID(View.getViewTitle() + '_newFile')
const home = getID(View.getViewTitle() + '_home')
const sharebtn = getID(View.getViewTitle() + '_sharebtn')
home.addEventListener("click", () => {
goHomePath()
})
sharebtn.addEventListener("click", () => {
goSharePath()
})
newFolder.addEventListener("click", () => {
View.createPopup({
title: `<i class="fa fa-folder"></i> Nouveau dossier`,
content: `
<input type='text' class='field' id='${View.getViewTitle()}_foldername'>
<div id='${View.getViewTitle()}_folderInfo'></div>
<button class='btn green' id='${View.getViewTitle()}_foldercreate'><span><i class='fa fa-add'></i> Créer</span></button>
`})
const foldername = getID(View.getViewTitle() + "_foldername")
const foldercreate = getID(View.getViewTitle() + "_foldercreate")
const folderInfo = new InfoPop(View.getViewTitle() + "_folderInfo")
folderInfo.setSize("13px")
foldercreate.addEventListener("click", () => {
folderInfo.clear()
if(foldername.value.length < 1) {
folderInfo.err("Le nom du dossier est trop court.")
return
}
const regex = new RegExp(/^[a-zA-Z0-9-_]+$/)
if(!regex.test(foldername.value) | foldername.value.replace(/\s/g, '').length < 1) {
folderInfo.err("Le nom du dossier est invalide.")
return
}
const reqFiles = post("FX_NEW_FOLDER", rootInput.value + "/" + foldername.value)
reqFiles.then((result) => {
if(result == "OK") {
View.destroyPopup(`<i class="fa fa-folder"></i> Nouveau dossier`)
const reqFiles = post("FX_GET", rootInput.value)
reqFiles.then((result) => {
loadFiles(result)
})
} else if(result == "EXIST") {
folderInfo.err("Le dossier existe déjà.")
} else if(result == "NOT_PERMITTED") {
folderInfo.err("Vous n'avez pas les permissions pour créer un dossier ici.")
} else {
folderInfo.err("Une erreur est survenue.")
}
})
})
})
// Create a new file with a popup wth 2 options, upload or create, if create give a input name or if upload, upload a file
newFile.addEventListener("click", () => {
View.createPopup({
title: `<i class="fa-solid fa-file-arrow-up"></i> Nouveau fichier`,
content: `
<input type='text' class='field' id='${View.getViewTitle()}_filename'>
<div id='${View.getViewTitle()}_fileInfo'></div>
<button class='btn green' id='${View.getViewTitle()}_filecreate'><span><i class='fa fa-add'></i> Créer</span></button>
<button class='btn blue' id='${View.getViewTitle()}_fileupload'><span><i class='fa fa-upload'></i> Upload</span></button>
`})
const filename = getID(View.getViewTitle() + "_filename")
const filecreate = getID(View.getViewTitle() + "_filecreate")
const fileupload = getID(View.getViewTitle() + "_fileupload")
const fileInfo = new InfoPop(View.getViewTitle() + "_fileInfo")
fileInfo.setSize("13px")
filecreate.addEventListener("click", () => {
fileInfo.clear()
if(filename.value.length < 1) {
fileInfo.err("Le nom du fichier est trop court.")
return
}
const regex = new RegExp(/^[a-zA-Z0-9-_.]+$/)
if(!regex.test(filename.value) | filename.value.replace(/\s/g, '').length < 1) {
fileInfo.err("Le nom du fichier est invalide.")
return
}
const reqFiles = post("FX_NEW_FILE", rootInput.value + "/" + filename.value)
reqFiles.then((result) => {
if(result == "OK") {
View.destroyPopup(`<i class="fa-solid fa-file-arrow-up"></i> Nouveau fichier`)
const reqFiles = post("FX_GET", rootInput.value)
reqFiles.then((result) => {
loadFiles(result)
})
} else if(result == "EXIST") {
fileInfo.err("Le fichier existe déjà.")
} else if(result == "NOT_PERMITTED") {
fileInfo.err("Vous n'avez pas les permissions pour créer un fichier ici.")
} else {
fileInfo.err("Une erreur est survenue.")
}
})
})
fileupload.addEventListener("click", () => {
fileInfo.clear()
View.destroyPopup(`<i class="fa-solid fa-file-arrow-up"></i> Nouveau fichier`)
View.createPopup({
title: `<i class="fa-solid fa-file-arrow-up"></i> Upload`,
content: `
<input type='file' class='field' id='${View.getViewTitle()}_fileuploadInput'>
<div id='${View.getViewTitle()}_fileuploadInfo'></div>
<button class='btn green' id='${View.getViewTitle()}_fileuploadBtn'><span><i class='fa fa-upload'></i> Upload</span></button>
`})
const fileuploadInput = getID(View.getViewTitle() + "_fileuploadInput")
const fileuploadBtn = getID(View.getViewTitle() + "_fileuploadBtn")
const fileuploadInfo = new InfoPop(View.getViewTitle() + "_fileuploadInfo")
fileuploadBtn.addEventListener("click", () => {
fileuploadInfo.clear()
if(fileuploadInput.files.length < 1) {
fileuploadInfo.err("Aucun fichier sélectionné.")
return
}
const file = fileuploadInput.files[0]
const reqFiles = post("FX_UPLOAD", {name: file.name ,root: rootInput.value, file: file})
reqFiles.then((result) => {
if(result == "OK") {
View.destroyPopup(`<i class="fa-solid fa-file-arrow-up"></i> Upload`)
const reqFiles = post("FX_GET", rootInput.value)
reqFiles.then((result) => {
loadFiles(result)
})
}
if(result == "EXIST") {
fileuploadInfo.err("Le fichier existe déjà.")
}
if(result == "NOT_PERMITTED") {
fileuploadInfo.err("Vous n'avez pas les permissions pour uploader un fichier ici.")
}
if(result == "NOT_FILE") {
fileuploadInfo.err("Le fichier n'est pas valide.")
}
if(result == "NOT_EXIST") {
fileuploadInfo.err("Le fichier n'existe pas.")
}
if(result == "TOO_BIG") {
fileuploadInfo.err("Le fichier est trop volumineux.")
}
if(result == "ERROR") {
fileuploadInfo.err("Une erreur est survenue.")
}
})
})
})
})
rootInput.addEventListener("change", () => {
@ -64,20 +261,21 @@ function generateFileExplorer() {
explorer.innerHTML = "<p class='yellow' style='text-align: center;'><i class='fa fa-warning'></i> Ce dossier n'existe pas.</p>"
} else {
fileElements.unshift(`<div id='fx-parent' class='fx-element'><p><i class="fa-solid fa-rotate-left"></i> Revenir au dossier parent </p></div>`)
for(const file of files.content) {
// Convert all file.size to a human readable format with units
file.size = bytesToSize(file.size)
//Tell if the file is a directory or not
if(file.directory) {
file.size = "Dossier"
} else {
console.log('------------')
console.log(file.size)
console.log(bytesToSize(file.size))
console.log('------------')
file.size = bytesToSize(file.size)
}
fileElements.push(`<div id='${file.id}' oncontextmenu='createFileMenu("${file.id}")' class='fx-element'>
fileElements.push(`<div id='${file.id}' class='fx-element'>
<div>
${getIcon(file)}
<p>${file.name}</p>
@ -85,22 +283,31 @@ function generateFileExplorer() {
<p>Taille : ${file.size}</p>
<p>Date de modification : ${getFormattedDate(file.lastedition)}</p>
</div>`)
}
// Sort the files by directory and then by name
fileElements.sort((a, b) => {
if(a.includes("Dossier") && !b.includes("Dossier")) {
return -1
} else if(!a.includes("Dossier") && b.includes("Dossier")) {
return 1
} else {
return 0
}
})
fileElements.unshift(`<div id='fx-parent' class='fx-element'><p><i class="fa-solid fa-rotate-left"></i> Revenir au dossier parent </p></div>`)
}
explorer.innerHTML = fileElements.join("")
const parent = document.getElementById("fx-parent")
const home = document.getElementById("fx-home")
home.addEventListener("click", () => {
goHomePath()
})
parent.addEventListener("click", () => {
@ -117,9 +324,16 @@ function generateFileExplorer() {
for(const file of files.content) {
const dropMenu = new DroppableMenu({
"id": file.id
})
const element = document.getElementById(file.id)
if(file.directory) {
const element = document.getElementById(file.id)
element.addEventListener("click", () => {
const reqFiles = post("FX_GET", file.fileDirectory)
@ -127,9 +341,260 @@ function generateFileExplorer() {
loadFiles(result)
})
})
} else {
element.addEventListener("dblclick", () => {
editFile()
})
dropMenu.add("edit", "<i class='fa-solid fa-pen'></i> Editer")
dropMenu.add("share", "<i class='fa fa-share'></i> Partager")
dropMenu.add("download", "<i class='fa fa-download'></i> Télécharger")
}
}
dropMenu.add("rename", "<i class='fa-solid fa-file-signature'></i> Renommer")
dropMenu.add("delete", "<i class='fa fa-trash'></i> Supprimer")
getID(file.id).addEventListener("contextmenu", () => {
dropMenu.show()
dropMenu.get("delete").addEventListener("click", () => {
dropMenu.hide()
const reqFiles = post("FX_DELETE", file.fileDirectory)
reqFiles.then((result) => {
if(result == "OK") {
const reqFiles = post("FX_GET", files.root)
reqFiles.then((result) => {
loadFiles(result)
})
} else if(result == "NOT_PERMITTED") {
const reqFiles = post("FX_GET", files.root)
reqFiles.then((result) => {
loadFiles(result)
})
View.createPopup({
title: `<i class="fa fa-warning"></i> Erreur`,
content: `<p class='yellow'>Vous n'avez pas les permissions pour supprimer ce fichier.</p>`
})
} else {
const reqFiles = post("FX_GET", files.root)
reqFiles.then((result) => {
loadFiles(result)
})
View.createPopup({
title: `<i class="fa fa-warning"></i> Erreur`,
content: `<p class='yellow'>Une erreur est survenue.</p>`
})
}
})
})
dropMenu.get("rename").addEventListener("click", () => {
dropMenu.hide()
View.createPopup({
title: `<i class="fa fa-file-signature"></i> Renommer`,
content: `
<input type='text' class='field' id='${View.getViewTitle()}_rename'>
<div id='${View.getViewTitle()}_renameInfo'></div>
<button class='btn green' id='${View.getViewTitle()}_renameBtn'><span><i class='fa fa-pen'></i> Renommer</span></button>
`})
const rename = getID(View.getViewTitle() + "_rename")
const renameBtn = getID(View.getViewTitle() + "_renameBtn")
const renameInfo = new InfoPop(View.getViewTitle() + "_renameInfo")
renameInfo.setSize("13px")
rename.value = file.name
rename.focus()
rename.select()
renameBtn.addEventListener("click", () => {
renameInfo.clear()
if(rename.value.length < 1) {
renameInfo.err("Le nom du fichier / dossier est trop court.")
return
}
const regex = new RegExp(/^[a-zA-Z0-9-_]+$/)
if(!regex.test(rename.value) | rename.value.replace(/\s/g, '').length < 1) {
renameInfo.err("Le nom du fichier / dossier est invalide.")
return
}
const reqFiles = post("FX_RENAME", {root: files.root, oldName: file.name, newName: rename.value})
reqFiles.then((result) => {
if(result == "OK") {
View.destroyPopup(`<i class="fa fa-file-signature"></i> Renommer`)
const reqFiles = post("FX_GET", files.root)
reqFiles.then((result) => {
loadFiles(result)
})
} else if(result == "EXIST") {
renameInfo.err("Le fichier / dossier existe déjà.")
} else if(result == "NOT_PERMITTED") {
renameInfo.err("Vous n'avez pas les permissions pour renommer ce fichier / dossier.")
} else {
renameInfo.err("Une erreur est survenue.")
}
})
})
})
if(!file.directory) {
dropMenu.get("share").addEventListener("click", () => {
dropMenu.hide()
const reqFiles = post("FX_SHARE", {root: files.root, name: file.name})
reqFiles.then((result) => {
if(result == "NOT_PERMITTED") {
View.createPopup({
title: `<i class="fa fa-warning"></i> Erreur`,
content: `<p class='yellow'>Vous n'avez pas les permissions pour partager ce fichier.</p>`
})
} else {
View.createPopup({
title: `<i class="fa fa-share"></i> Partager`,
content: `
<input style='width: 300px' type='text' class='field' id='${View.getViewTitle()}_sharelink'>
<div id='${View.getViewTitle()}_shareInfo'></div>
<button class='btn green' id='${View.getViewTitle()}_shareBtn'><span><i class='fa fa-share'></i> Copier</span></button>
`})
const sharelink = getID(View.getViewTitle() + "_sharelink")
const shareBtn = getID(View.getViewTitle() + "_shareBtn")
const shareInfo = new InfoPop(View.getViewTitle() + "_shareInfo")
shareInfo.setSize("13px")
sharelink.value = result
sharelink.focus()
sharelink.select()
shareBtn.addEventListener("click", () => {
sharelink.focus()
sharelink.select()
window.navigator.clipboard.writeText(sharelink.value)
shareInfo.ok("Copié !")
})
}
})
})
// Edit file with an ViewWindow with 2 options close & save and the name of the window is like File - Editor
dropMenu.get("edit").addEventListener("click", () => {
dropMenu.hide()
editFile()
})
dropMenu.get("download").addEventListener("click", () => {
dropMenu.hide()
const reqFiles = post("FX_GETFILE", file.fileDirectory)
reqFiles.then((result) => {
if(result == "NOT_PERMITTED") {
View.createPopup({
title: `<i class="fa fa-warning"></i> Erreur`,
content: `<p class='yellow'>Vous n'avez pas les permissions pour télécharger ce fichier.</p>`
})
} else {
// Make Download using result
const element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result));
element.setAttribute('download', file.name);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
})
})
}
})
function editFile() {
const reqFiles = post("FX_GETFILE", file.fileDirectory)
reqFiles.then((result) => {
if(result == "NOT_PERMITTED") {
View.createPopup({
title: `<i class="fa fa-warning"></i> Erreur`,
content: `<p class='yellow'>Vous n'avez pas les permissions pour éditer ce fichier.</p>`
})
} else {
const editor = new ViewWindow({
title: `<i class="fa-solid fa-file-pen"></i> Editeur - ${file.fileDirectory}`,
width: "1000px",
height: "600px"
})
editor.setContent(`
<div class='fx-bar'>
<span id='${editor.getViewTitle()}_save' class='btn-cover'><i class='fa fa-save'></i></span>
</div>
<textarea id='${editor.getViewTitle()}_editorContent' class='fx-editor-content'>${result}</textarea>
`)
const editorSave = getID(editor.getViewTitle() + "_save")
const editorContent = getID(editor.getViewTitle() + "_editorContent")
// Sauvegarder le fichier en l'envoyant
editorSave.addEventListener("click", () => {
const reqFiles = post("FX_SAVEFILE", {root: files.root, name: file.name, content: editorContent.value})
reqFiles.then((result) => {
if(result == "OK") {
editor.destroy()
const reqFiles = post("FX_GET", files.root)
reqFiles.then((result) => {
loadFiles(result)
})
} else if(result == "NOT_PERMITTED") {
View.createPopup({
title: `<i class="fa fa-warning"></i> Erreur`,
content: `<p class='yellow'>Vous n'avez pas les permissions pour éditer ce fichier.</p>`
})
} else {
View.createPopup({
title: `<i class="fa fa-warning"></i> Erreur`,
content: `<p class='yellow'>Une erreur est survenue.</p>`
})
}
})
})
//forbid textarea resize
editorContent.style.resize = "none"
editorClose.addEventListener("click", () => {
editor.destroy()
})
}
})
}
}
}
@ -140,19 +605,6 @@ function generateFileExplorer() {
}
function createFileMenu(idCreated) {
const dropMenu = new DroppableMenu({
"id": idCreated
})
dropMenu.add("rename", "<i class='fa-solid fa-file-signature'></i> Renommer")
dropMenu.add("share", "<i class='fa fa-share'></i> Partager")
dropMenu.add("delete", "<i class='fa fa-trash'></i> Supprimer")
dropMenu.show()
}
function getIcon(file) {
if(file.type == "application/json") {
@ -188,7 +640,7 @@ function getIcon(file) {
}
if(file.type == "application/javascript") {
return '<i style="color:rgb(179, 141, 4);" class="fa-brands fa-js">'
return '<i style="color:rgb(179, 141, 4);" class="fa-brands fa-js"></i>'
}
if(file.type == "image/png" | file.type == "image/jpeg") {

View File

@ -1,4 +1,5 @@
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300&display=swap');
@import url('https://fonts.cdnfonts.com/css/gunship');
body {
padding: 2%;
@ -20,10 +21,10 @@ html {
background-color: transparent;
outline: none;
border-radius: 0.5vw;
border-radius: 10px;
border: 1px solid currentColor;
color: currentColor;
padding: 0.5vw;
padding: 10px;
transition: 0.2s;
cursor: pointer;
}
@ -125,7 +126,7 @@ code {
background-color: #3D3B3C;
padding: 0.3vw;
border-radius: 0.5vw;
border-radius: 10px;
}
p {
@ -207,6 +208,11 @@ a {
/* Logo */
.logo {
user-select: none;
}
@media (max-width: 640px) {
.logo {
@ -262,36 +268,39 @@ a {
width: 500px;
height: 400px;
border-radius: 0.5vw;
border-radius: 10px;
display: flex;
padding: 2%;
flex-direction: column;
justify-content: space-between;
background-color: #605e5863;
backdrop-filter: blur(10px);
}
.panel-box {
width: 900px;
height: 500px;
border-radius: 0.5vw;
border-radius: 10px;
display: flex;
padding: 2%;
flex-direction: column;
justify-content: space-between;
background-color: #605e5863;
gap: 50px;
backdrop-filter: blur(10px);
}
.subpanel-box {
width: 900px;
height: 50px;
border-radius: 0.5vw;
border-radius: 10px;
padding: 2%;
display: flex;
justify-content: space-between;
background-color: #605e5863;
backdrop-filter: blur(10px);
}
.subpanel-image {
@ -421,17 +430,47 @@ a {
font-size: 72px;
}
.view-popup {
position: absolute;
background-color: #3D3B3C;
backdrop-filter: blur(10px);
display: flex;
flex-direction: column;
gap: 20px;
align-items: center;
justify-content: center;
padding: 2%;
border-radius: 10px;
}
.view-popup-bar {
display: flex;
justify-content: space-between;
flex-direction: row;
align-items: center;
gap: 20px;
width: 100%;
}
.blur {
filter: blur(10px);
}
/*ViewWindow*/
.view-window {
position: absolute;
display: flex;
flex-direction: column;
z-index: 1;
background-color: #605e58c1;
backdrop-filter: blur(10px);
border-radius: 0.5vw;
border-radius: 10px;
-webkit-user-drag: auto;
}
.view-window-header {
@ -441,11 +480,20 @@ a {
align-items: center;
padding: 10px;
background-color: rgba(33, 32, 33, 0.753);
border-top-right-radius: 0.5vw;
border-top-left-radius: 0.5vw;
border-top-right-radius: 10px;
border-top-left-radius: 10px;
user-select: none;
}
.view-window-content {
height: 100%;
display: flex;
flex-direction: column;
width: 100%;
}
/* Files Explorer */
@ -459,7 +507,7 @@ a {
.fx-root-input {
background-color: #323031;
border: solid 1px #323031;
border-radius: 0.5vw;
border-radius: 10px;
padding: 1%;
color: white;
outline:0px;
@ -472,7 +520,7 @@ a {
flex-direction: column;
overflow-y: auto ;
height: 450px;
gap: 20px
}
.fx-element {
@ -517,13 +565,30 @@ a {
.fx-bar-actions {
width: 30%;
width: 33%;
display: flex;
flex-direction: row;
justify-content: end;
gap: 10px;
}
.fx-editor-content {
background-color: transparent;
color: white;
border: none;
padding: 15px;
height: 100%;
}
.fx-editor-content:focus {
outline: none;
}
/* ===== Scrollbar CSS ===== */
/* Firefox */
* {
@ -553,7 +618,7 @@ a {
display: flex;
flex-direction: column;
width: 150px;
border-radius: 0.5vw;
border-radius: 10px;
z-index: 3;
height: auto;
}