diff --git a/backend/go-app/files.go b/backend/go-app/files.go
index 0779a08c..419d834d 100644
--- a/backend/go-app/files.go
+++ b/backend/go-app/files.go
@@ -135,7 +135,7 @@ func handleGetFiles(resp http.ResponseWriter, request *http.Request) {
return
}
- log.Printf("Got %d files for org %s", len(files), user.ActiveOrg.Id)
+ log.Printf("[INFO] Got %d files for org %s", len(files), user.ActiveOrg.Id)
newBody, err := json.Marshal(files)
if err != nil {
log.Printf("[ERROR] Failed marshaling files: %s", err)
@@ -716,38 +716,45 @@ func handleCreateFile(resp http.ResponseWriter, request *http.Request) {
return
}
- // Try to get the org and workflow in case they don't exist
- workflow, err := getWorkflow(ctx, curfile.WorkflowId)
- if err != nil {
- log.Printf("[ERROR] Workflow %s doesn't exist.", curfile.WorkflowId)
- resp.WriteHeader(401)
- resp.Write([]byte(`{"success": false, "reason": "Error with workflow id or org id"}`))
- return
- }
-
- _, err = getOrg(ctx, curfile.OrgId)
- if err != nil {
- log.Printf("[ERROR] Org %s doesn't exist.", curfile.OrgId)
- resp.WriteHeader(401)
- resp.Write([]byte(`{"success": false, "reason": "Error with workflow id or org id"}`))
- return
- }
-
- if workflow.ExecutingOrg.Id != curfile.OrgId {
- found := false
- for _, curorg := range workflow.Org {
- if curorg.Id == curfile.OrgId {
- found = true
- break
- }
- }
-
- if !found {
- log.Printf("[ERROR] Org %s doesn't have access to %s.", curfile.OrgId, curfile.WorkflowId)
+ var workflow *Workflow
+ if curfile.WorkflowId == "global" {
+ // PS: Not a security issue.
+ // Files are global anyway, but the workflow_id is used to identify origin
+ log.Printf("[INFO] Uploading filename %s for org %s as global file.", curfile.Filename, curfile.OrgId)
+ } else {
+ // Try to get the org and workflow in case they don't exist
+ workflow, err = getWorkflow(ctx, curfile.WorkflowId)
+ if err != nil {
+ log.Printf("[ERROR] Workflow %s doesn't exist.", curfile.WorkflowId)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "Error with workflow id or org id"}`))
return
}
+
+ _, err = getOrg(ctx, curfile.OrgId)
+ if err != nil {
+ log.Printf("[ERROR] Org %s doesn't exist.", curfile.OrgId)
+ resp.WriteHeader(401)
+ resp.Write([]byte(`{"success": false, "reason": "Error with workflow id or org id"}`))
+ return
+ }
+
+ if workflow.ExecutingOrg.Id != curfile.OrgId {
+ found := false
+ for _, curorg := range workflow.Org {
+ if curorg.Id == curfile.OrgId {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ log.Printf("[ERROR] Org %s doesn't have access to %s.", curfile.OrgId, curfile.WorkflowId)
+ resp.WriteHeader(401)
+ resp.Write([]byte(`{"success": false, "reason": "Error with workflow id or org id"}`))
+ return
+ }
+ }
}
if strings.Contains(curfile.Filename, "/") || strings.Contains(curfile.Filename, `"`) || strings.Contains(curfile.Filename, "..") || strings.Contains(curfile.Filename, "~") {
@@ -776,24 +783,26 @@ func handleCreateFile(resp http.ResponseWriter, request *http.Request) {
downloadPath := fmt.Sprintf("%s/%s", folderPath, fileId)
duplicateWorkflows := []string{}
- for _, trigger := range workflow.Triggers {
- if trigger.AppName == "Shuffle Workflow" && trigger.TriggerType == "SUBFLOW" {
- for _, parameter := range trigger.Parameters {
- if parameter.Name == "workflow" && len(parameter.Value) > 0 {
+ if curfile.WorkflowId != "global" {
+ for _, trigger := range workflow.Triggers {
+ if trigger.AppName == "Shuffle Workflow" && trigger.TriggerType == "SUBFLOW" {
+ for _, parameter := range trigger.Parameters {
+ if parameter.Name == "workflow" && len(parameter.Value) > 0 {
- found := false
- for _, workflow := range duplicateWorkflows {
- if workflow == parameter.Value {
- found = true
- break
+ found := false
+ for _, workflow := range duplicateWorkflows {
+ if workflow == parameter.Value {
+ found = true
+ break
+ }
}
- }
- if !found {
- duplicateWorkflows = append(duplicateWorkflows, parameter.Value)
- }
+ if !found {
+ duplicateWorkflows = append(duplicateWorkflows, parameter.Value)
+ }
- break
+ break
+ }
}
}
}
diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js
index 45d3a1e0..1143a696 100644
--- a/frontend/src/components/Header.js
+++ b/frontend/src/components/Header.js
@@ -219,16 +219,6 @@ const Header = props => {
Settings
- {userdata === undefined || userdata.admin === undefined || userdata.admin === null || !userdata.admin ? null :
-
- }
+ {userdata === undefined || userdata.admin === undefined || userdata.admin === null || !userdata.admin ? null :
+
+
+
+ }
-
+
const loginTextMobile = !isLoggedIn ?
diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx
index 305db169..735814cf 100644
--- a/frontend/src/views/Admin.jsx
+++ b/frontend/src/views/Admin.jsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useState, useEffect } from 'react';
import { makeStyles } from '@material-ui/styles';
import {Link} from 'react-router-dom';
@@ -25,6 +25,7 @@ import IconButton from '@material-ui/core/IconButton';
import Avatar from '@material-ui/core/Avatar';
import Zoom from '@material-ui/core/Zoom';
import { useAlert } from "react-alert";
+import Dropzone from '../components/Dropzone';
import { Dialog, DialogTitle, DialogActions, DialogContent } from '@material-ui/core';
import { useTheme } from '@material-ui/core/styles';
@@ -33,6 +34,8 @@ import OrgHeader from '../components/OrgHeader'
import CircularProgress from '@material-ui/core/CircularProgress';
import EditIcon from '@material-ui/icons/Edit';
+import FileCopyIcon from '@material-ui/icons/FileCopy';
+import PublishIcon from '@material-ui/icons/Publish';
import SelectAllIcon from '@material-ui/icons/SelectAll';
import OpenInNewIcon from '@material-ui/icons/OpenInNew';
import CloudDownloadIcon from '@material-ui/icons/CloudDownload';
@@ -62,6 +65,7 @@ const Admin = (props) => {
const { globalUrl, userdata } = props;
var upload = ""
+ var to_be_copied = ""
const theme = useTheme();
const classes = useStyles();
const [firstRequest, setFirstRequest] = React.useState(true);
@@ -93,6 +97,14 @@ const Admin = (props) => {
const [selectedAuthenticationModalOpen, setSelectedAuthenticationModalOpen] = React.useState(false)
const [authenticationFields, setAuthenticationFields] = React.useState([])
const [showArchived, setShowArchived] = React.useState(false)
+ const [isDropzone, setIsDropzone] = React.useState(false);
+
+ useEffect(() => {
+ if (isDropzone) {
+ //redirectOpenApi();
+ setIsDropzone(false);
+ }
+ }, [isDropzone]);
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"
const getApps = () => {
@@ -647,6 +659,67 @@ const Admin = (props) => {
});
}
+ const handleFileUpload = (file_id, file) => {
+ //console.log("FILE: ", file_id, file)
+ fetch(`${globalUrl}/api/v1/files/${file_id}/upload`, {
+ method: 'POST',
+ credentials: "include",
+ body: file,
+ })
+ .then((response) => {
+ if (response.status !== 200) {
+ console.log("Status not 200 for apps :O!")
+ return
+ }
+
+ return response.json()
+ })
+ .then((responseJson) => {
+ //console.log("RESPONSE: ", responseJson)
+ //setFiles(responseJson)
+ })
+ .catch(error => {
+ //alert.error(error.toString())
+ });
+ }
+
+ const handleCreateFile = (filename, file) => {
+ const data = {
+ "filename": filename,
+ "org_id": selectedOrganization.id,
+ "workflow_id": "global",
+ }
+
+ fetch(globalUrl + "/api/v1/files/create", {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ },
+ credentials: "include",
+ body: JSON.stringify(data),
+ })
+ .then((response) => {
+ if (response.status !== 200) {
+ console.log("Status not 200 for apps :O!")
+ return
+ }
+
+ return response.json()
+ })
+ .then((responseJson) => {
+ //console.log("RESP: ", responseJson)
+ if (responseJson.success) {
+ handleFileUpload(responseJson.id, file)
+ } else {
+ alert.error("Failed to upload file ", filename)
+ }
+ })
+ .catch(error => {
+ alert.error(error.toString())
+ });
+ }
+
const getFiles = () => {
fetch(globalUrl + "/api/v1/files", {
method: 'GET',
@@ -1778,12 +1851,72 @@ const Admin = (props) => {
: null
+ const uploadFiles = (files) => {
+ for (var key in files) {
+ try {
+ const filename = files[key].name
+ var filedata = new FormData()
+ filedata.append('shuffle_file', files[key])
+
+ if (typeof(files[key]) === "object") {
+ handleCreateFile(filename, filedata)
+ }
+
+ /*
+ reader.addEventListener('load', (e) => {
+ var data = e.target.result;
+ setIsDropzone(false)
+ console.log(filename)
+ console.log(data)
+ console.log(files[key])
+ })
+ reader.readAsText(files[key])
+ */
+ } catch (e) {
+ console.log("Error in dropzone: ", e)
+ }
+ }
+
+ getFiles()
+ }
+
+ const uploadFile = (e) => {
+ const isDropzone = e.dataTransfer === undefined ? false : e.dataTransfer.files.length > 0;
+ const files = isDropzone ? e.dataTransfer.files : e.target.files;
+
+ //const reader = new FileReader();
+ //alert.info("Starting fileupload")
+ uploadFiles(files)
+ }
+
const filesView = curTab === 5 ?
+ 1366 ? 1366 : 1200, margin: "auto", padding: 20 }} onDrop={uploadFile}>
+
+
upload = ref} onChange={(event) => {
+ //const file = event.target.value
+ //const fileObject = URL.createObjectURL(actualFile)
+ //setFile(fileObject)
+ //const files = event.target.files[0]
+ uploadFiles(event.target.files)
+ }} />
+
@@ -1814,6 +1947,9 @@ const Admin = (props) => {
+
{files === undefined || files === null ? null : files.map((file, index) => {
var bgColor = "#27292d"
@@ -1833,13 +1969,19 @@ const Admin = (props) => {
/>
-
-
-
+ {file.workflow_id === "global" ?
+
+
-
-
+ :
+
+
+
+
+
+
+
+ }
style={{minWidth: 100, maxWidth: 100, overflow: "hidden"}}
/>
{
- {
+ {
downloadFile(file)
}}>
-
+
style={{minWidth: 75, maxWidth: 75, overflow: "hidden"}}
@@ -1878,11 +2020,31 @@ const Admin = (props) => {
*/}
+ {
+ const elementName = "copy_element_shuffle"
+ var copyText = document.getElementById(elementName);
+ if (copyText !== null && copyText !== undefined) {
+ navigator.clipboard.writeText(file.id)
+ copyText.select();
+ copyText.setSelectionRange(0, 99999); /* For mobile devices */
+
+ /* Copy the text inside the text field */
+ document.execCommand("copy");
+
+ alert.info(file.id + "copied to clipboard")
+ }
+ }}>
+
+
+ />
)
})}
+
: null
const schedulesView = curTab === 4 ?
@@ -2445,6 +2607,11 @@ const Admin = (props) => {
{editUserModal}
{editAuthenticationModal}
{data}
+
)
}