-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathdashboard_2_file_browser.json
1 lines (1 loc) · 65 KB
/
dashboard_2_file_browser.json
1
[{"id":"066aec543be078be","type":"comment","z":"0e3249ddee2000e3","name":"DB2 File Browser","info":"\n\n## v1.3 Editor\nNew Features\n- File editor (editing text files online)\n\n## v1.2 More filters, UI changes\nNew Features\n- Filter by Created and Change date\n- current folder in the toolbar\n- Files in orange color\n- File Upload\n\nBug Fixed\n- Screen layout\n- Unused buttons removed\n\n## v1.1 Download and Delete functions \nNew Features\n- Hide folders button\n- Show hidden files button\n- Download individual or multiple files\n- Delete individual or multiple files\n\nBug Fixes\n- Selection did not reset after refresh\n- select search at refresh","x":120,"y":4540,"wires":[]},{"id":"ba2f4d9e595b71ac","type":"ui-template","z":"0e3249ddee2000e3","group":"74da605bad30eb60","page":"","ui":"","name":"Toolbar","order":1,"width":"0","height":"0","head":"","format":"<template>\n <v-toolbar>\n <template v-slot:prepend>\n <v-tooltip text=\"Refresh\" location=\"top\">\n <template v-slot:activator=\"{ props }\">\n <v-btn v-bind=\"props\" icon=\"mdi-refresh\" @click=\"send({topic: 'refresh', payload: null})\"></v-btn>\n </template>\n </v-tooltip>\n <v-tooltip text=\"Parent folder\" location=\"top\">\n <template v-slot:activator=\"{ props }\">\n <v-btn v-bind=\"props\" icon=\"mdi-arrow-up-bold-outline\" @click=\"send({topic: 'up', payload: null})\"></v-btn>\n </template>\n </v-tooltip>\n <v-tooltip text=\"Home folder\" location=\"top\">\n <template v-slot:activator=\"{ props }\">\n <v-btn v-bind=\"props\" icon=\"mdi-home-account\" \n @click=\"send({topic: 'home', payload: null})\"></v-btn>\n </template>\n </v-tooltip>\n <v-divider class=\"mx-3 align-self-center\" length=\"24\" thickness=\"2\" vertical></v-divider>\n <div class=\"text-h6\" >{{buttonstate.folder}}</div>\n </template>\n\n <v-tooltip text=\"Delete file\" location=\"top\">\n <template v-slot:activator=\"{ props }\">\n <v-btn v-bind=\"props\" icon=\"mdi-delete-outline\" @click=\"send({topic: 'delete', payload: null})\" :disabled=\"buttonstate.delete\"></v-btn>\n </template>\n </v-tooltip>\n <v-tooltip text=\"Download file\" location=\"top\">\n <template v-slot:activator=\"{ props }\">\n <v-btn v-bind=\"props\" icon=\"mdi-download\" @click=\"send({topic: 'download', payload: null})\"\n :disabled=\"buttonstate.download\"></v-btn>\n </template>\n </v-tooltip>\n <v-tooltip text=\"Download all files\" location=\"top\">\n <template v-slot:activator=\"{ props }\">\n <v-btn v-bind=\"props\" icon=\"mdi-download-multiple\" @click=\"send({topic: 'downloadall', payload: null})\"></v-btn>\n </template>\n </v-tooltip>\n <v-tooltip text=\"Upload file\" location=\"top\">\n <template v-slot:activator=\"{ props }\">\n <v-btn v-bind=\"props\" icon=\"mdi-upload\" @click=\"send({topic: 'upload', payload: null})\"></v-btn>\n </template>\n </v-tooltip>\n <v-tooltip text=\"Edit text file\" location=\"top\">\n <template v-slot:activator=\"{ props }\">\n <v-btn v-bind=\"props\" icon=\"mdi-file-edit-outline\" @click=\"send({topic: 'editfile', payload: null})\"\n :disabled=\"buttonstate.editfile\"></v-btn>\n </template>\n </v-tooltip>\n\n <v-divider class=\"mx-3 align-self-center\" length=\"24\" thickness=\"2\" vertical></v-divider>\n\n <v-tooltip text=\"Hide folders\" location=\"top\">\n <template v-slot:activator=\"{ props }\">\n <v-btn v-bind=\"props\" icon=\"mdi-folder-remove-outline\" :active=\"buttonstate.hidefolders\" @click=\"send({topic: 'showhidefolders', payload: null})\"></v-btn>\n </template>\n </v-tooltip>\n <v-tooltip text=\"Show hidden files\" location=\"top\">\n <template v-slot:activator=\"{ props }\">\n <v-btn v-bind=\"props\" icon=\"mdi-file-hidden\" :active=\"buttonstate.hiddenfiles\"\n @click=\"send({topic: 'hiddenfiles', payload: null})\"></v-btn>\n </template>\n </v-tooltip>\n <v-tooltip text=\"Filter on Created date\" location=\"top\">\n <template v-slot:activator=\"{ props }\">\n <v-btn v-bind=\"props\" icon=\"mdi-calendar-month\" :active=\"buttonstate.createdfilter\"\n @click=\"send({topic: 'createdfilter', payload: null})\"></v-btn>\n </template>\n </v-tooltip>\n <v-tooltip text=\"Filter on Changed date\" location=\"top\">\n <template v-slot:activator=\"{ props }\">\n <v-btn v-bind=\"props\" icon=\"mdi-calendar-edit\" :active=\"buttonstate.changedfilter\"\n @click=\"send({topic: 'changedfilter', payload: null})\"></v-btn>\n </template>\n </v-tooltip>\n\n </v-toolbar>\n</template>\n\n<script>\n export default {\n data() {\n // define variables available component-wide\n // (in <template> and component functions)\n return {\n buttonstate: {}\n }\n },\n watch: {\n // watch for any changes of \"count\"\n msg: function() {\n if(this.msg.payload != undefined){\n this.buttonstate = this.msg.payload;\n }\n }\n },\n computed: {\n // automatically compute this variable\n // whenever VueJS deems appropriate\n },\n methods: {\n // expose a method to our <template> and Vue Application\n },\n mounted() {\n // code here when the component is first loaded\n },\n unmounted() {\n // code here when the component is removed from the Dashboard\n // i.e. when the user navigates away from the page\n }\n }\n</script>\n<style>\n .v-toolbar{\n color: black\n }\n</style>","storeOutMessages":true,"passthru":false,"resendOnRefresh":true,"templateScope":"local","className":"","x":1200,"y":4580,"wires":[["974c6d787e653f5d","78b15bd9528e5e9a"]]},{"id":"860334734a6a6752","type":"ui-template","z":"0e3249ddee2000e3","group":"78b1a8fd63ef7bbc","page":"","ui":"","name":"File Explorer","order":2,"width":"18","height":"1","head":"","format":"<template>\n <!-- Provide an input text box to search the content -->\n <v-text-field v-model=\"search\" label=\"Search\" prepend-inner-icon=\"mdi-magnify\" single-line variant=\"outlined\"\n hide-details></v-text-field>\n <v-data-table \n v-model:search=\"search\" \n :items=\"msg?.payload\" \n :headers=\"msg?.headers\"\n v-model=\"selected\"\n item-selectable=\"selectable\"\n select-strategy=\"all\"\n show-select \n return-object\n >\n\n <template v-slot:item.icon=\"{ item }\">\n <v-icon :icon=\"item.icon\"></v-icon>\n </template>\n <template v-slot:item.fname=\"{ item }\">\n <v-chip variant=\"tonal\" :color=\"item.chipcolor\" @click=\"send({topic: 'clicked', payload: item})\">{{ item.fname }}</v-chip>\n </template>\n <template v-slot:item.stat.size_text=\"{ item }\">\n <div class=\"text-right\">{{ item.stat.size_text }}</div>\n </template>\n\n </v-data-table>\n</template>\n\n<script>\n export default {\n data () {\n return {\n selected: [],\n search: ''\n }\n },\n watch: {\n selected: function () {\n this.send({topic:'selected', payload: this.selected});\n },\n msg: function () {\n this.selected = [];\n this.search = '';\n }\n },\n methods: {\n // add a function to determine the color of the progress bar given the row's item\n\n }\n }\n</script>","storeOutMessages":true,"passthru":false,"resendOnRefresh":true,"templateScope":"local","className":"","x":970,"y":4680,"wires":[["a17bf6e7624e9cf3","22bda8abb863b3eb"]]},{"id":"6c96462a11721ad2","type":"change","z":"0e3249ddee2000e3","name":"Add Headers","rules":[{"t":"set","p":"headers","pt":"msg","to":"[{\"title\":\"Icon\",\"value\":\"icon\"},{\"title\":\"Name\",\"value\":\"fname\",\"sortable\":true},{\"title\":\"Size\",\"value\":\"stat.size_text\",\"sortable\":true},{\"title\":\"Created\",\"value\":\"stat.created\",\"sortable\":true},{\"title\":\"Changed\",\"value\":\"stat.changed\",\"sortable\":true}]","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":4680,"wires":[["860334734a6a6752"]]},{"id":"974c6d787e653f5d","type":"debug","z":"0e3249ddee2000e3","name":"Toolbar action","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1440,"y":4520,"wires":[]},{"id":"a17bf6e7624e9cf3","type":"debug","z":"0e3249ddee2000e3","name":"Filelist action","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1130,"y":4780,"wires":[]},{"id":"4eb9f90cdd5cff28","type":"inject","z":"0e3249ddee2000e3","name":"Init","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"change","payload":"filebrowser_default_folder","payloadType":"global","x":90,"y":4640,"wires":[["3bf7430f2d43a7b2"]]},{"id":"3bf7430f2d43a7b2","type":"function","z":"0e3249ddee2000e3","name":"Folder handling","func":"let folder = context.get(\"folder\");\nlet browsersettings = flow.get(\"browsersettings\") ?? {};\nif (folder===undefined) {\n folder=\"/\";\n context.set(\"folder\", folder);\n}\n\nif (msg.topic===\"up\") {\n var the_arr = folder.split('/');\n the_arr.pop();\n folder=the_arr.join('/'); \n context.set(\"folder\", folder);\n}\nif (msg.topic===\"change\") {\n folder=msg.payload; \n context.set(\"folder\", folder);\n}\n\nmsg.payload = {\"start\":folder, \"hidden\": browsersettings.hiddenfiles ?? false};\nflow.set(\"folder\", folder);\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":4680,"wires":[["d824a4042497107b"]]},{"id":"d824a4042497107b","type":"fs-file-lister","z":"0e3249ddee2000e3","name":"Files","start":"/home/nygma","pattern":"*.*","folders":"*","hidden":false,"lstype":"both","path":true,"single":true,"depth":0,"stat":true,"showWarnings":false,"x":470,"y":4680,"wires":[["db2e450d66584744"]]},{"id":"db2e450d66584744","type":"function","z":"0e3249ddee2000e3","name":"Prep Data","func":"function convertDate(local) {\n let d = new Date(local);\n d.setTime(d.getTime()+3600*1000);\n return d.toISOString().slice(0, 19).replace('T', ' ');\n}\n\nlet createdFromDate;\nlet createdToDate;\nlet changedFromDate;\nlet changedToDate;\n\nlet browsersettings = flow.get(\"browsersettings\") ?? {};\nif (browsersettings.createdfilter) {\n createdFromDate = new Date(browsersettings.createdfilter_data.from);\n createdToDate = new Date(browsersettings.createdfilter_data.to);\n}\nif (browsersettings.changedfilter) {\n changedFromDate = new Date(browsersettings.changedfilter_data.from);\n changedToDate = new Date(browsersettings.changedfilter_data.to);\n}\n\nfor (var i = msg.payload.length-1; i >= 0; i--) {\n if (browsersettings.hidefolders) {\n if (msg.payload[i].stat.isDirectory) {\n msg.payload.splice(i,1);\n continue;\n }\n }\n if (browsersettings.createdfilter) {\n if ((new Date(msg.payload[i].stat.created) < createdFromDate) || (new Date(msg.payload[i].stat.created) > createdToDate)) {\n msg.payload.splice(i, 1);\n continue;\n }\n }\n if (browsersettings.changedfilter) {\n if ((new Date(msg.payload[i].stat.changed) < changedFromDate) || (new Date(msg.payload[i].stat.changed) > changedToDate)) {\n msg.payload.splice(i, 1);\n continue;\n }\n }\n}\n\nfor (var i=0; i<msg.payload.length; i++) {\n\n msg.payload[i].stat.created = convertDate(msg.payload[i].stat.created);\n msg.payload[i].stat.changed = convertDate(msg.payload[i].stat.changed);\n msg.payload[i].stat.accessed = convertDate(msg.payload[i].stat.accessed);\n msg.payload[i].stat.statusChanged = convertDate(msg.payload[i].stat.statusChanged);\n msg.payload[i].fname = msg.payload[i].name.replace(/^.*(\\\\|\\/|\\:)/, '');\n if (msg.payload[i].stat.isDirectory) {\n msg.payload[i].ext = \"folder\";\n msg.payload[i].icon = \"mdi-folder-outline\";\n msg.payload[i].chipcolor = \"blue\";\n } else {\n msg.payload[i].ext = msg.payload[i].fname.split('.').pop().toLowerCase();\n msg.payload[i].stat.size_text = msg.payload[i].stat.size.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");\n msg.payload[i].downloadlink = \"/download?filename=\"+msg.payload[i].name;\n msg.payload[i].chipcolor = \"orange-accent-3\";\n switch (msg.payload[i].ext) {\n case \"csv\":\n case \"txt\":\n msg.payload[i].icon = \"mdi-file-document-outline\";\n break;\n case \"jpg\":\n case \"jpeg\":\n case \"png\":\n case \"gif\":\n msg.payload[i].icon = \"mdi-file-image\";\n break;\n case \"json\":\n case \"py\":\n case \"sh\":\n msg.payload[i].icon = \"mdi-file-code-outline\";\n break;\n default:\n msg.payload[i].icon = \"mdi-file-document-edit-outline\";\n }\n }\n}\nflow.set(\"currentfolder\",msg.payload);\nreturn msg;\n","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":620,"y":4680,"wires":[["6c96462a11721ad2","81bdebf226289166"]]},{"id":"b4ab571c4ae055ae","type":"inject","z":"0e3249ddee2000e3","name":"Default folder","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"/home/nygma","payloadType":"str","x":120,"y":4600,"wires":[["3cd8606b06a2ef63"]]},{"id":"3cd8606b06a2ef63","type":"change","z":"0e3249ddee2000e3","name":"Store settings","rules":[{"t":"set","p":"filebrowser_default_folder","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":330,"y":4600,"wires":[[]]},{"id":"81bdebf226289166","type":"debug","z":"0e3249ddee2000e3","name":"debug 354","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":780,"y":4740,"wires":[]},{"id":"78b15bd9528e5e9a","type":"switch","z":"0e3249ddee2000e3","name":"Button pressed","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"refresh","vt":"str"},{"t":"eq","v":"up","vt":"str"},{"t":"eq","v":"home","vt":"str"},{"t":"eq","v":"download","vt":"str"},{"t":"eq","v":"showhidefolders","vt":"str"},{"t":"eq","v":"hiddenfiles","vt":"str"},{"t":"eq","v":"downloadall","vt":"str"},{"t":"eq","v":"delete","vt":"str"},{"t":"eq","v":"createdfilter","vt":"str"},{"t":"eq","v":"changedfilter","vt":"str"},{"t":"eq","v":"upload","vt":"str"},{"t":"eq","v":"editfile","vt":"str"}],"checkall":"true","repair":false,"outputs":12,"x":1420,"y":4640,"wires":[["0009cf79df680a7d"],["0009cf79df680a7d"],["2957b62d1a068bd1"],["7e7f6138b76b6c67"],["256a0219aedd128f"],["ca0f41b6af236825"],["279423ccec7630d4"],["d98761837a385dcd"],["d7b2c505f42ab81e"],["d00e84fa303ea509"],["2c1147aeeaf3a886"],["df11ec0e75f78ce1"]]},{"id":"2957b62d1a068bd1","type":"change","z":"0e3249ddee2000e3","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"filebrowser_default_folder","tot":"global"},{"t":"set","p":"topic","pt":"msg","to":"change","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1700,"y":4600,"wires":[["0009cf79df680a7d"]]},{"id":"f62329c112539834","type":"link in","z":"0e3249ddee2000e3","name":"New File Browser Refresh","links":["0009cf79df680a7d","30804f1a8a4633c5","05e9769c41be095e"],"x":135,"y":4700,"wires":[["3bf7430f2d43a7b2"]]},{"id":"0009cf79df680a7d","type":"link out","z":"0e3249ddee2000e3","name":"link out 61","mode":"link","links":["f62329c112539834"],"x":1995,"y":4560,"wires":[]},{"id":"22bda8abb863b3eb","type":"function","z":"0e3249ddee2000e3","name":"File List Actions","func":"let buttonstates = {};\nlet folder = flow.get(\"folder\") ?? \"\";\nlet browsersettings = flow.get(\"browsersettings\") ?? {};\nbuttonstates = {\n \"folder\": folder,\n // inverted logic, true for disabled\n \"delete\" : true,\n \"download\" : true,\n \"hidefolders\": browsersettings.hidefolders ?? false,\n \"hiddenfiles\": browsersettings.hiddenfiles ?? false,\n \"createdfilter\": browsersettings.createdfilter ?? false,\n \"changedfilter\": browsersettings.changedfilter ?? false,\n \"editfile\": true\n};\n\nfunction DetermineToolBarState(count) {\n // inverted logic, true for disabled\n switch (count) {\n case 0:\n buttonstates.delete = true;\n buttonstates.download = true;\n buttonstates.editfile = true;\n break;\n case 1:\n buttonstates.delete = false;\n buttonstates.download = false;\n buttonstates.editfile = true;\n break;\n default:\n buttonstates.delete = false;\n buttonstates.download = false;\n buttonstates.editfile = true;\n }\n}\n\nif (msg.topic === \"toolbar\") {\n // Toolbar icons need to be updated\n let lastselected = flow.get(\"lastselected\") ?? [];\n DetermineToolBarState(lastselected.length);\n node.send([{ \"topic\": \"buttons\", \"payload\": buttonstates }, null, null, null]);\n}\n\nif (msg.topic === \"clicked\") {\n flow.set(\"lastclicked\", msg.payload);\n //flow.set(\"lastselected\", msg.payload);\n DetermineToolBarState(msg.payload.length);\n if ([\"csv\", \"txt\", \"sh\", \"py\", \"json\"].includes(msg.payload.ext)) {\n buttonstates.editfile = false;\n }\n // Directory clicked\n if (msg.payload.stat.isDirectory) {\n return [null,{\"topic\": \"change\", \"payload\": msg.payload.name},null,null];\n } else {\n buttonstates.download = false;\n return [{ \"topic\": \"buttons\", \"payload\": buttonstates }, null, msg, null];\n }\n}\nif (msg.topic === \"selected\") {\n if (Array.isArray(msg.payload)) {\n flow.set(\"lastselected\", msg.payload);\n DetermineToolBarState(msg.payload.length);\n if (msg.payload.length===1) {\n if ([\"csv\", \"txt\", \"sh\", \"py\", \"json\"].includes(msg.payload[0].ext)) {\n buttonstates.editfile = false;\n }\n }\n node.send([{\"topic\": \"buttons\", \"payload\": buttonstates},null,null,msg]);\n }\n}\n\n","outputs":4,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1190,"y":4680,"wires":[["ba2f4d9e595b71ac"],["0009cf79df680a7d"],["35815c5686a075af"],["bbc53f7072538dd1"]]},{"id":"7e7f6138b76b6c67","type":"function","z":"0e3249ddee2000e3","name":"Handle Download Actions","func":"let lastselected = flow.get(\"lastselected\") ?? [];\nif (lastselected.length === 0) {\n // download the last clicked file if nothing is selected\n let lastclicked = flow.get(\"lastclicked\"); \n if (lastclicked !== undefined) {\n msg.payload = lastclicked.downloadlink;\n return [msg,null];\n }\n}\nif (lastselected.length === 1) {\n // one file is selected, download that single file\n// msg.template = \"<script>window.open('\" + encodeURIComponent(lastselected[0].downloadlink) + \"', '_blank');</script>\";\n msg.payload = lastselected[0].downloadlink;\n return [msg,null];\n}\nif (lastselected.length > 1) {\n // multiple files are selected, need to zip and download the zip\n let filecontent = \"\";\n let folders = 0;\n let files = 0;\n let size = 0;\n for (let i = 0; i < lastselected.length; i++) {\n if (lastselected[i].stat.isDirectory) {\n folders++;\n } else {\n files++;\n size += lastselected[i].stat.size;\n filecontent += lastselected[i].fname + \"<br/>\";\n }\n }\n\n msg.payload = { \n \"title\": \"Download files\", \n \"content\": \"You have selected \"+files+\" files, which will be downloaded as a zip file.<br/>Compressing the files will take time.<br/>Download will start automatically once zip file is ready<br/>Are you sure?\", \n \"okText\": \"OK\", \n \"cancelText\": \"Cancel\" \n }\n \n return [null,msg];\n}\n","outputs":2,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1730,"y":4640,"wires":[["0dd538ad5abf0d66"],["203655569fc6e71c"]]},{"id":"0dd538ad5abf0d66","type":"ui-template","z":"0e3249ddee2000e3","group":"78b1a8fd63ef7bbc","page":"","ui":"","name":"File Download Dummy HTML","order":0,"width":0,"height":0,"head":"","format":"<script>\n export default {\n data() {\n return {\n }\n },\n watch: {\n msg: function() {\n if(this.msg.payload != undefined){\n window.open(this.msg.payload, '_blank');\n this.send({payload:undefined});\n }\n }\n }\n }\n</script>\n","storeOutMessages":true,"passthru":false,"resendOnRefresh":true,"templateScope":"local","className":"","x":2040,"y":4640,"wires":[[]]},{"id":"8db0b4781c475060","type":"ui-template","z":"0e3249ddee2000e3","group":"771fdaf427baf0f4","page":"","ui":"","name":"File Card","order":0,"width":"6","height":"1","head":"","format":"<template>\n <v-card class=\"mx-auto\" color=\"white\" width=\"600\">\n <v-img class=\"align-end text-white\" height=\"52\" src=\"https://cdn.vuetifyjs.com/images/backgrounds/vbanner.jpg\" cover>\n <v-card-title>{{ fname }}</v-card-title>\n </v-img>\n\n <v-card-subtitle>\n {{ size_text }} byte(s)\n </v-card-subtitle>\n\n <v-card-text>\n <div v-html=\"content\"></div>\n </v-card-text>\n\n </v-card>\n</template>\n\n<script>\n export default {\n data() {\n // define variables available component-wide\n // (in <template> and component functions)\n return {\n content: \"\",\n fname: \"\",\n size_text: \"\"\n }\n },\n watch: {\n // watch for any changes of \"count\"\n msg: function() {\n if(this.msg.payload != undefined){\n if (this.msg.payload.filecontent != undefined) {\n this.content = this.msg.payload.filecontent;\n } else {\n this.content = \"\";\n }\n this.fname = this.msg.payload.fname;\n this.size_text = this.msg.payload.stat.size_text;\n }\n }\n },\n computed: {\n },\n methods: {\n },\n mounted() {\n },\n unmounted() {\n }\n }\n</script>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":2300,"y":5200,"wires":[[]]},{"id":"35815c5686a075af","type":"switch","z":"0e3249ddee2000e3","name":"File type","property":"payload.ext","propertyType":"msg","rules":[{"t":"eq","v":"csv","vt":"str"},{"t":"eq","v":"txt","vt":"str"},{"t":"eq","v":"json","vt":"str"},{"t":"eq","v":"sh","vt":"str"},{"t":"eq","v":"py","vt":"str"},{"t":"eq","v":"jpg","vt":"str"},{"t":"eq","v":"jpeg","vt":"str"},{"t":"eq","v":"png","vt":"str"},{"t":"eq","v":"gif","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":10,"x":1420,"y":5320,"wires":[["6b0c85ac76f33610"],["6b0c85ac76f33610"],["6b0c85ac76f33610"],["6b0c85ac76f33610"],["6b0c85ac76f33610"],["e6d8948f0c4f91f1"],["e6d8948f0c4f91f1"],["e6d8948f0c4f91f1"],["e6d8948f0c4f91f1"],["0723d28ecb2de203"]]},{"id":"6b0c85ac76f33610","type":"function","z":"0e3249ddee2000e3","name":"Get the file name","func":"if (msg.payload.stat.size<200000) {\n msg.filename = msg.payload.name;\n msg.save = msg.payload;\n return [msg,null];\n} else {\n return [null,msg]; \n}\n","outputs":2,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1630,"y":5280,"wires":[["1766c4558495ec1e"],["996af3577e9293f2"]],"outputLabels":["Folder selected",""]},{"id":"1766c4558495ec1e","type":"file in","z":"0e3249ddee2000e3","name":"","filename":"","format":"","chunk":false,"sendError":false,"encoding":"none","x":1820,"y":5280,"wires":[["7d7e78be85e6c295"]]},{"id":"7d7e78be85e6c295","type":"function","z":"0e3249ddee2000e3","name":"Div text","func":"function HTMLEncode(str) {\n var i = str.length,\n aRet = [];\n\n while (i--) {\n var iC = str[i].charCodeAt();\n if (iC < 65 || iC > 127 || (iC > 90 && iC < 97)) {\n aRet[i] = '&#' + iC + ';';\n } else {\n aRet[i] = str[i];\n }\n }\n return aRet.join('');\n}\n\n\nlet filecontent = \"\";\nfilecontent += msg.payload;\n\n// filecontent = HTMLEncode(filecontent);\n\n// Convert line feed to <br>\nfilecontent = filecontent.replace(/(?:\\r\\n|\\r|\\n)/g, '<br/>');\n\n\n\nmsg.payload = msg.save;\nmsg.payload.filecontent = filecontent;\nmsg.payload.isText = true;\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1980,"y":5280,"wires":[["8db0b4781c475060"]]},{"id":"e6d8948f0c4f91f1","type":"function","z":"0e3249ddee2000e3","name":"Get the file name","func":"if (msg.payload.stat.size < 2000000) {\n msg.filename = msg.payload.name;\n msg.save = msg.payload;\n return [msg, null];\n} else {\n return [null, msg];\n}\n","outputs":2,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1630,"y":5360,"wires":[["0725cd9674de0d0a"],["996af3577e9293f2"]],"outputLabels":["Folder selected",""]},{"id":"0725cd9674de0d0a","type":"file in","z":"0e3249ddee2000e3","name":"","filename":"filename","filenameType":"msg","format":"","chunk":false,"sendError":false,"encoding":"none","x":1820,"y":5360,"wires":[["d557be83132d6587"]]},{"id":"401fcaba2ade0d05","type":"function","z":"0e3249ddee2000e3","name":"Div image","func":"let text = \"<img src=\\\"data:image/png;base64,\";\ntext += msg.payload;\ntext += \"\\\" width=\\\"100%\\\"/>\";\nmsg.payload = msg.save;\nmsg.payload.filecontent = text;\nmsg.payload.isText = true;\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":2120,"y":5360,"wires":[["8db0b4781c475060"]]},{"id":"d557be83132d6587","type":"base64","z":"0e3249ddee2000e3","name":"","action":"","property":"payload","x":1980,"y":5360,"wires":[["401fcaba2ade0d05"]]},{"id":"0723d28ecb2de203","type":"function","z":"0e3249ddee2000e3","name":"Div text","func":"msg.payload.filecontent = \"No preview available\";\nmsg.payload.isText = true;\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":2120,"y":5420,"wires":[["8db0b4781c475060"]]},{"id":"996af3577e9293f2","type":"function","z":"0e3249ddee2000e3","name":"File too big","func":"msg.payload.filecontent = \"File too large for preview\";\nmsg.payload.isText = true;\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1990,"y":5320,"wires":[["8db0b4781c475060"]]},{"id":"bbc53f7072538dd1","type":"function","z":"0e3249ddee2000e3","name":"Multiple files","func":"\nlet filecontent = \"\";\nlet folders = 0;\nlet files = 0;\nlet size = 0;\nfor (let i=0; i<msg.payload.length; i++) {\n if (msg.payload[i].stat.isDirectory) {\n folders++;\n } else {\n files++;\n size += msg.payload[i].stat.size;\n filecontent += msg.payload[i].fname+\"<br/>\";\n }\n}\n\nlet title = \"\";\nif (folders>0) {\n title += folders + \" folder\";\n if (folders>1) {\n title += \"s\";\n }\n if (files>0) {\n title += \" and \";\n }\n}\nif (files>0) {\n title += files + \" file\";\n if (files>1) {\n title += \"s\";\n }\n}\nif ((files===0)&&(folders===0)) {\n title += \"Nothing\";\n}\ntitle += \" selected\";\n\nmsg.payload = {\n \"fname\": title,\n \"stat\": { \"size_text\": size.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\") },\n \"filecontent\": filecontent,\n \"isText\": true\n}\n\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1630,"y":5220,"wires":[["8db0b4781c475060"]]},{"id":"aa8c482.93734b8","type":"http in","z":"0e3249ddee2000e3","name":"","url":"/download","method":"get","upload":false,"swaggerDoc":"","x":180,"y":4840,"wires":[["d969ba04.e24028"]]},{"id":"d19cc7d8.646328","type":"http response","z":"0e3249ddee2000e3","name":"","statusCode":"","headers":{},"x":970,"y":4840,"wires":[]},{"id":"d969ba04.e24028","type":"function","z":"0e3249ddee2000e3","name":"Get the file name","func":"msg.filename = msg.req.query.filename;\nmsg.contentdisposition = \"attachment; filename=\\\"\" + msg.req.query.filename.replace(/^.*(\\\\|\\/|\\:)/, '') + \"\\\"\";\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":4840,"wires":[["e92381c3.c4cd2"]],"outputLabels":["Folder selected"]},{"id":"e92381c3.c4cd2","type":"file in","z":"0e3249ddee2000e3","name":"","filename":"filename","filenameType":"msg","format":"","chunk":false,"sendError":false,"encoding":"none","x":620,"y":4840,"wires":[["99ff4953.d0d5c8"]]},{"id":"99ff4953.d0d5c8","type":"change","z":"0e3249ddee2000e3","name":"Set Headers","rules":[{"t":"set","p":"headers","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"headers.content-type","pt":"msg","to":"text/csv","tot":"str"},{"t":"set","p":"headers.Content-Disposition","pt":"msg","to":"contentdisposition","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":4840,"wires":[["d19cc7d8.646328"]]},{"id":"256a0219aedd128f","type":"function","z":"0e3249ddee2000e3","name":"Show/Hide folders","func":"let browsersettings = flow.get(\"browsersettings\") ?? {};\n\nlet value = browsersettings.hidefolders ?? false;\nbrowsersettings.hidefolders = !value;\nflow.set(\"browsersettings\", browsersettings);\nmsg.topic=\"toolbar\";\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1710,"y":4680,"wires":[["0009cf79df680a7d","26d6b3b2304e5ec0"]]},{"id":"ca0f41b6af236825","type":"function","z":"0e3249ddee2000e3","name":"Hidden files","func":"let browsersettings = flow.get(\"browsersettings\") ?? {};\n\nlet value = browsersettings.hiddenfiles ?? false;\nbrowsersettings.hiddenfiles = !value;\nflow.set(\"browsersettings\", browsersettings);\nmsg.topic=\"toolbar\";\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1690,"y":4720,"wires":[["0009cf79df680a7d","26d6b3b2304e5ec0"]]},{"id":"4d63463472fff4a7","type":"link in","z":"0e3249ddee2000e3","name":"File List Action loopback","links":["26d6b3b2304e5ec0","be80804ac03d6092"],"x":1025,"y":4640,"wires":[["22bda8abb863b3eb"]]},{"id":"26d6b3b2304e5ec0","type":"link out","z":"0e3249ddee2000e3","name":"link out 62","mode":"link","links":["4d63463472fff4a7"],"x":1925,"y":4700,"wires":[]},{"id":"074fa4cbb05a1f77","type":"inject","z":"0e3249ddee2000e3","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"selected","payload":"[]","payloadType":"json","x":1440,"y":5220,"wires":[["bbc53f7072538dd1"]]},{"id":"203655569fc6e71c","type":"ui-template","z":"0e3249ddee2000e3","group":"","page":"","ui":"cb79bc4520925e32","name":"OK/Cancel dialog","order":0,"width":0,"height":0,"head":"","format":"<!-- \n This pops up an OK/Cancel dialog when sent a payload of the form\n {\n title: \"dialog title\",\n content: \"The text to display in the window\",\n okText: \"OK\", // optional, if not present defaults to OK\n cancelText: \"Cancel\" // optional, defaults to Cancel\n }\n All properties may contain embedded html tags such as <br/>\n If msg._client is present and contains a socketId (as will be the case if the template is triggered\n from another widget such as a button) then the popup will only appear on the matching session.\n For example, if a button widget is fed into the template then the popup will appear only on the browser where the button\n was clicked.\n If msg._client is not present then the popup will appear on all connected browsers and will have to be acknowleged on each one.\n\n When one of the buttons is clicked a message is sent with msg.payload containing the ok or cancel text\n-->\n<template>\n <v-dialog width=\"auto\" v-model=\"showDialog\">\n <v-card color=\"white\" v-click-outside=\"{handler: onClickOutside}\">\n <v-toolbar color=\"primary\">\n <v-card-title>\n <span>{{title}}</span>\n </v-card-title>\n </v-toolbar>\n <v-card-text>\n <div v-html=\"content\"></div>\n </v-card-text>\n <v-card-actions class=\"justify-end\">\n <v-btn density=\"compact\" variant=\"outlined\" size=\"large\" @click=\"cancelDialog\" v-html=\"cancelText\"></v-btn>\n <v-btn density=\"compact\" variant=\"outlined\" size=\"large\" @click=\"okDialog\" v-html=\"okText\"></v-btn>\n </v-card-actions>\n </v-card>\n </v-dialog>\n</template>\n\n<script>\nexport default {\n data() {\n return {\n dialogData:null\n }\n },\n watch: {\n msg: function(){\n // only show the dialog if msg.payload is an object and the socket id in the message\n // matches our socket id (which means the popup was initiated from this session) or\n // there is no _client property present which indicates it should be shown on all sessions.\n if (typeof this.msg.payload === \"object\" && (!this.msg._client || this.msg._client.socketId === this.$socket.id)) { \n this.dialogData = this.msg.payload;\n if (!this.dialogData.okText) {\n this.dialogData.okText = \"OK\"\n }\n if (!this.dialogData.cancelText) {\n this.dialogData.cancelText = \"Cancel\"\n }\n this.dialogData.show = true\n }\n // prevent redraw on deploy\n this.msg.payload = null\n }\n },\n methods:{\n okDialog:function(){\n this.dialogData.show = false;\n this.msg.payload = this.dialogData.okText\n this.send(this.msg);\n },\n cancelDialog:function(){\n this.dialogData.show = false;\n this.msg.payload = this.dialogData.cancelText\n this.send(this.msg);\n },\n onClickOutside () {\n this.dialogData.show = false;\n this.msg.payload = this.dialogData.cancelText\n this.send(this.msg);\n },\n },\n computed : {\n title:function(){\n return this.dialogData?.title ?? \"\"\n },\n content:function(){\n return this.dialogData?.content ?? \"\"\n },\n okText:function(){\n return this.dialogData?.okText ?? \"OK\"\n },\n cancelText:function(){\n return this.dialogData?.cancelText ?? \"Cancel\"\n },\n showDialog: function (){\n return this.dialogData?.show === true\n }\n },\n unmounted () {\n this.dialogData = null\n }\n}\n</script>","storeOutMessages":true,"passthru":false,"resendOnRefresh":true,"templateScope":"widget:ui","className":"","x":2370,"y":4700,"wires":[["4ac725fe65a5fa3f"]]},{"id":"4ac725fe65a5fa3f","type":"switch","z":"0e3249ddee2000e3","name":"OK","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"OK","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":2540,"y":4700,"wires":[["24532852895ff10e"]]},{"id":"57120c96c38c042c","type":"function","z":"0e3249ddee2000e3","name":"Multi Zip command","func":"let folder = flow.get(\"folder\");\n\n// Get the current time and convert it to text\nvar now = new Date();\nvar yyyy = now.getFullYear();\nvar mm = now.getMonth() < 9 ? \"0\" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based\nvar dd = now.getDate() < 10 ? \"0\" + now.getDate() : now.getDate();\nvar hh = now.getHours() < 10 ? \"0\" + now.getHours() : now.getHours();\nvar mmm = now.getMinutes() < 10 ? \"0\" + now.getMinutes() : now.getMinutes();\nvar ss = now.getSeconds() < 10 ? \"0\" + now.getSeconds() : now.getSeconds();\n\n// Generate out file name pattern\nlet filename = folder.match(/([^\\/]*)\\/*$/)[1]+\"_\" + yyyy + mm + dd + hh + mmm+ss+\".zip\";\nlet filepath = folder + \"/\" + filename;\n\nlet filelist = \"\";\nlet lastselected = flow.get(\"lastselected\");\nfor (let i = 0; i < lastselected.length; i++) {\n filelist += folder + \"/\" +lastselected[i].fname+\" \";\n}\n\nmsg.save = {\"name\": filepath, \"fname\": filename };\n\nmsg.payload = \"zip \" +filepath+ \" \"+filelist;\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":2910,"y":4700,"wires":[["9de1802d8ac2805b"]]},{"id":"9de1802d8ac2805b","type":"exec","z":"0e3249ddee2000e3","command":"","addpay":"payload","append":"","useSpawn":"false","timer":"","winHide":false,"oldrc":false,"name":"Execute zip","x":3110,"y":4700,"wires":[["51aae8c4596167cb"],[],[]]},{"id":"51aae8c4596167cb","type":"switch","z":"0e3249ddee2000e3","name":"Success?","property":"rc.code","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"num"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":3280,"y":4680,"wires":[["8d15368b9a90aa1b","0d44f306349e66d8"],["46c659c23c3324ef"]]},{"id":"8d15368b9a90aa1b","type":"change","z":"0e3249ddee2000e3","name":"Notification","rules":[{"t":"set","p":"payload","pt":"msg","to":"Zip file containing all files in this folder has been generated. Click on Download above.","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":3490,"y":4700,"wires":[[]]},{"id":"46c659c23c3324ef","type":"change","z":"0e3249ddee2000e3","name":"Failed notification","rules":[{"t":"set","p":"payload","pt":"msg","to":"Download all failed!","tot":"str"},{"t":"set","p":"topic","pt":"msg","to":"Download All","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":3510,"y":4740,"wires":[["423620baf465adcb"]]},{"id":"4edb79d0893cf1eb","type":"function","z":"0e3249ddee2000e3","name":"All Zip command","func":"let folder = flow.get(\"folder\");\n\n// Get the current time and convert it to text\nvar now = new Date();\nvar yyyy = now.getFullYear();\nvar mm = now.getMonth() < 9 ? \"0\" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based\nvar dd = now.getDate() < 10 ? \"0\" + now.getDate() : now.getDate();\nvar hh = now.getHours() < 10 ? \"0\" + now.getHours() : now.getHours();\nvar mmm = now.getMinutes() < 10 ? \"0\" + now.getMinutes() : now.getMinutes();\nvar ss = now.getSeconds() < 10 ? \"0\" + now.getSeconds() : now.getSeconds();\n\n// Generate out file name pattern\nlet filename = folder.match(/([^\\/]*)\\/*$/)[1]+\"_\" + yyyy + mm + dd + hh + mmm+ss+\".zip\";\nlet filepath = folder + \"/\" + filename;\n\nmsg.save = {\"name\": filepath, \"fname\": filename };\n\nmsg.payload = \"zip \" + filepath+\" \"+folder+\"/*.*\";\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":2910,"y":4740,"wires":[["9de1802d8ac2805b"]]},{"id":"0d44f306349e66d8","type":"function","z":"0e3249ddee2000e3","name":"Get download link","func":"msg.payload = \"/download?filename=\" + msg.save.name;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":3510,"y":4660,"wires":[["0dd538ad5abf0d66"]]},{"id":"423620baf465adcb","type":"ui-notification","z":"0e3249ddee2000e3","ui":"cb79bc4520925e32","position":"top right","colorDefault":true,"color":"#000000","displayTime":"5","showCountdown":true,"outputs":0,"allowDismiss":true,"dismissText":"Close","raw":false,"className":"","name":"","x":3770,"y":4740,"wires":[]},{"id":"279423ccec7630d4","type":"function","z":"0e3249ddee2000e3","name":"Handle Download All Actions","func":"msg.payload = { \n \"title\": \"Download All files\", \n \"content\": \"This will download all files in this folder as a zip file.<br/>Compressing the files will take time.<br/>Download will start automatically once zip file is ready<br/>Are you sure?\", \n \"okText\": \"OK\", \n \"cancelText\": \"Cancel\" \n}\n\nreturn msg;\n","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1740,"y":4760,"wires":[["203655569fc6e71c"]]},{"id":"24532852895ff10e","type":"switch","z":"0e3249ddee2000e3","name":"Action","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"download","vt":"str"},{"t":"eq","v":"downloadall","vt":"str"},{"t":"eq","v":"delete","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":2670,"y":4700,"wires":[["57120c96c38c042c"],["4edb79d0893cf1eb"],["6e9470f0139e1dfc"]]},{"id":"d98761837a385dcd","type":"function","z":"0e3249ddee2000e3","name":"Handle Delete Actions","func":"let lastselected = flow.get(\"lastselected\") ?? [];\nif (lastselected.length === 0) {\n // single file delete\n let lastclicked = flow.get(\"lastclicked\"); \n if (lastclicked !== undefined) {\n msg.payload = {\n \"title\": \"Delete file\",\n \"content\": \"Are you sure you want to delete: \" + lastclicked.fname,\n \"okText\": \"OK\", \n \"cancelText\": \"Cancel\"\n }\n return msg;\n }\n}\nif (lastselected.length === 1) {\n // one file is selected, delete that single file\n msg.payload = {\n \"title\": \"Delete file\",\n \"content\": \"Are you sure you want to delete: \" + lastselected[0].fname,\n \"okText\": \"OK\",\n \"cancelText\": \"Cancel\"\n }\n return msg;\n}\nif (lastselected.length > 1) {\n // multiple files are selected, need to zip and download the zip\n let filecontent = \"\";\n let folders = 0;\n let files = 0;\n let size = 0;\n for (let i = 0; i < lastselected.length; i++) {\n if (lastselected[i].stat.isDirectory) {\n folders++;\n } else {\n files++;\n size += lastselected[i].stat.size;\n filecontent += lastselected[i].fname + \"<br/>\";\n }\n }\n\n msg.payload = { \n \"title\": \"Delete files\", \n \"content\": \"You have selected \"+files+\" files<br/>Are you sure you want to delete them all?\", \n \"okText\": \"OK\", \n \"cancelText\": \"Cancel\" \n }\n \n return msg;\n}\n","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1720,"y":4800,"wires":[["203655569fc6e71c"]]},{"id":"6e9470f0139e1dfc","type":"function","z":"0e3249ddee2000e3","name":"Handle Download Actions","func":"let lastselected = flow.get(\"lastselected\") ?? [];\nif (lastselected.length === 0) {\n // single file delete\n let lastclicked = flow.get(\"lastclicked\"); \n if (lastclicked !== undefined) {\n msg.filename = lastclicked.name;\n return msg;\n }\n}\nif (lastselected.length === 1) {\n // one file is selected, delete that single file\n msg.filename = lastselected[0].name;\n return msg;\n}\nif (lastselected.length > 1) {\n // multiple files are selected, need to zip and download the zip\n let filelist = [];\n for (let i = 0; i < lastselected.length; i++) {\n if (lastselected[i].stat.isDirectory) {\n } else {\n filelist.push({ \"filename\": lastselected[i].name, \"payload\": null, \"topic\": \"refresh\" });\n }\n }\n\n return [filelist];\n}\n","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":2930,"y":4780,"wires":[["3e7bcb8361d4bbcc"]]},{"id":"3e7bcb8361d4bbcc","type":"file","z":"0e3249ddee2000e3","name":"","filename":"filename","filenameType":"msg","appendNewline":true,"createDir":false,"overwriteFile":"delete","encoding":"none","x":3170,"y":4780,"wires":[["30804f1a8a4633c5"]]},{"id":"30804f1a8a4633c5","type":"link out","z":"0e3249ddee2000e3","name":"link out 63","mode":"link","links":["f62329c112539834"],"x":3315,"y":4780,"wires":[]},{"id":"d7b2c505f42ab81e","type":"function","z":"0e3249ddee2000e3","name":"Created date filter","func":"let browsersettings = flow.get(\"browsersettings\") ?? {};\n\nmsg.payload = {\n \"title\": \"Filter files by Created date\",\n \"content\": \"Specify the From and To dates and press Set filter button.<br/>To disable the filtering, click on Remove filter button.\",\n \"okText\": \"Set filter\",\n \"cancelText\": \"Remove filter\",\n \"fromdatelabel\": \"From date\",\n \"fromdatevalue\": browsersettings.createdfilter_data?.from ?? \"\",\n \"todatelabel\": \"To date\",\n \"todatevalue\": browsersettings.createdfilter_data?.to ?? \"\"\n};\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1710,"y":4840,"wires":[["7ffba3cfaacf8948"]]},{"id":"7ffba3cfaacf8948","type":"ui-template","z":"0e3249ddee2000e3","group":"","page":"","ui":"cb79bc4520925e32","name":"From-To Dates dialog","order":0,"width":0,"height":0,"head":"","format":"<!-- \n This pops up an OK/Cancel dialog when sent a payload of the form\n {\n title: \"Kitchen thermostat\",\n content: \"Enter the target temperature\",\n okText: \"OK\", // optional, if not present defaults to OK\n cancelText: \"Cancel\", // optional, defaults to Cancel\n fromdatelabel: \"From date\",\n fromdatevalue: \"2024-03-22\",\n todatelabel: \"To date\",\n todatevalue: \"2024-04-22\"\n }\n All properties may contain embedded html tags such as <br/>\n If msg._client is present and contains a socketId (as will be the case if the template is triggered\n from another widget such as a button) then the popup will only appear on the matching session.\n For example, if a button widget is fed into the template then the popup will appear only on the browser where the button\n was clicked.\n If msg._client is not present then the popup will appear on all connected browsers and will have to be acknowleged on each one.\n\n When one of the buttons is clicked a message is sent with msg.payload containing the ok or cancel text\n-->\n<template>\n <v-dialog width=\"auto\" v-model=\"showDialog\">\n <v-card color=\"white\">\n <v-toolbar color=\"primary\">\n <v-card-title>\n <span>{{title}}</span>\n </v-card-title>\n </v-toolbar>\n <v-card-text>\n <div v-html=\"content\"></div>\n <div class=\"d-flex py-2 justify-space-between\"></div>\n <v-row>\n <v-col>\n <v-text-field :label=\"fromdatelabel\" v-model=\"fromdatevalue\" type=\"date\"></v-text-field>\n </v-col>\n <v-col>\n <v-text-field :label=\"todatelabel\" v-model=\"todatevalue\" type=\"date\"></v-text-field>\n </v-col>\n </v-row>\n <div class=\"d-flex py-2 justify-space-between\"></div>\n <v-row>\n <v-col>\n <v-combobox label=\"Presets\"\n :items=\"['Today', 'Yesterday', 'Last 7 Days', 'This Week', 'Last Week', 'This Month', 'Last Month']\"\n v-model=\"preset\"></v-combobox>\n </v-col>\n </v-row>\n </v-card-text>\n\n <v-card-actions class=\"justify-end\">\n <v-btn density=\"compact\" variant=\"outlined\" size=\"large\" @click=\"cancelDialog\" v-html=\"cancelText\"></v-btn>\n <v-btn density=\"compact\" variant=\"outlined\" size=\"large\" @click=\"okDialog\" v-html=\"okText\"></v-btn>\n </v-card-actions>\n </v-card>\n </v-dialog>\n</template>\n\n<script>\n export default {\n data() {\n return {\n dialogData:null,\n fromdatevalue: Date.now(),\n todatevalue: Date.now(),\n preset: ''\n }\n },\n watch: {\n msg: function(){\n // only show the dialog if msg.payload is an object and the socket id in the message\n // matches our socket id (which means the popup was initiated from this session) or\n // there is no _client property present which indicates it should be shown on all sessions.\n if (typeof this.msg.payload === \"object\" && (!this.msg._client || this.msg._client.socketId === this.$socket.id)) { \n this.dialogData = this.msg.payload;\n if (!this.dialogData.okText) {\n this.dialogData.okText = \"OK\";\n }\n if (!this.dialogData.cancelText) {\n this.dialogData.cancelText = \"Cancel\";\n }\n this.dialogData.show = true;\n this.fromdatevalue = this.msg.payload?.fromdatevalue ?? \"\";\n this.todatevalue = this.msg.payload?.todatevalue ?? \"\";\n }\n // prevent redraw on deploy\n this.msg.payload = null\n },\n preset: function() {\n var today = new Date();\n if (this.preset === 'Today') {\n this.fromdatevalue = today.toISOString().split('T')[0];\n this.todatevalue = new Date(today.getTime() + 24*60*60*1000).toISOString().split('T')[0];\n }\n if (this.preset === 'Yesterday') {\n this.todatevalue = today.toISOString().split('T')[0];\n this.fromdatevalue = new Date(today.getTime() - 24*60*60*1000).toISOString().split('T')[0];\n }\n if (this.preset === 'Last 7 Days') {\n this.todatevalue = today.toISOString().split('T')[0];\n this.fromdatevalue = new Date(today.getTime() - 7*24*60*60*1000).toISOString().split('T')[0];\n }\n if (this.preset === 'This Week') {\n this.fromdatevalue = new Date(today.setDate(today.getDate() - today.getDay()+1)).toISOString().split('T')[0];\n this.todatevalue = new Date(today.setDate(today.getDate() - today.getDay()+8)).toISOString().split('T')[0];\n }\n if (this.preset === 'Last Week') {\n this.fromdatevalue = new Date(today.setDate(today.getDate() - today.getDay()+1-7)).toISOString().split('T')[0];\n this.todatevalue = new Date(today.setDate(today.getDate() - today.getDay()+8-7)).toISOString().split('T')[0];\n }\n if (this.preset === 'This Month') {\n this.fromdatevalue = new Date(today.getFullYear(), today.getMonth(), 2).toISOString().split('T')[0];\n this.todatevalue = new Date(today.getFullYear(), today.getMonth()+1, 2).toISOString().split('T')[0];\n }\n if (this.preset === 'Last Month') {\n this.fromdatevalue = new Date(today.getFullYear(), today.getMonth()-1, 2).toISOString().split('T')[0];\n this.todatevalue = new Date(today.getFullYear(), today.getMonth(), 2).toISOString().split('T')[0];\n }\n }\n },\n methods:{\n okDialog:function(){\n this.dialogData.show = false;\n this.msg.payload = { action: this.dialogData.okText, fromdatevalue: this.fromdatevalue, todatevalue: this.todatevalue };\n this.send(this.msg);\n },\n cancelDialog:function(){\n this.dialogData.show = false;\n this.msg.payload = { action: this.dialogData.cancelText };\n this.send(this.msg);\n },\n onClickOutside () {\n this.dialogData.show = false;\n this.msg.payload = { action: this.dialogData.cancelText };\n this.send(this.msg);\n }\n },\n computed : {\n title:function(){\n return this.dialogData?.title ?? \"\"\n },\n content:function(){\n return this.dialogData?.content ?? \"\"\n },\n okText:function(){\n return this.dialogData?.okText ?? \"OK\"\n },\n cancelText:function(){\n return this.dialogData?.cancelText ?? \"Cancel\"\n },\n showDialog: function (){\n return this.dialogData?.show === true\n },\n fromdatelabel: function (){\n return this.dialogData?.fromdatelabel ?? \"\"\n },\n todatelabel: function (){\n return this.dialogData?.todatelabel ?? \"\"\n }\n },\n unmounted () {\n this.dialogData = null\n }\n}\n</script>","storeOutMessages":true,"passthru":false,"resendOnRefresh":true,"templateScope":"widget:ui","className":"","x":2040,"y":4840,"wires":[["5c48cef7618e71b4"]]},{"id":"5c48cef7618e71b4","type":"switch","z":"0e3249ddee2000e3","name":"Action","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"createdfilter","vt":"str"},{"t":"eq","v":"changedfilter","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":2250,"y":4840,"wires":[["b8396996a8b01293"],["b8396996a8b01293"]]},{"id":"b8396996a8b01293","type":"function","z":"0e3249ddee2000e3","name":"Store data","func":"let browsersettings = flow.get(\"browsersettings\") ?? {};\n\nif (msg.payload.action === \"Set filter\") {\n browsersettings[msg.topic] = true ;\n browsersettings[msg.topic + \"_data\"] = { \"from\": msg.payload.fromdatevalue, \"to\": msg.payload.todatevalue };\n} else {\n browsersettings[msg.topic] = false;\n delete browsersettings[msg.topic + \"_data\"];\n}\n\nflow.set(\"browsersettings\", browsersettings);\nmsg.topic = \"toolbar\";\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":2470,"y":4840,"wires":[["be80804ac03d6092","05e9769c41be095e"]]},{"id":"be80804ac03d6092","type":"link out","z":"0e3249ddee2000e3","name":"link out 64","mode":"link","links":["4d63463472fff4a7"],"x":2605,"y":4840,"wires":[]},{"id":"d00e84fa303ea509","type":"function","z":"0e3249ddee2000e3","name":"Changed date filter","func":"let browsersettings = flow.get(\"browsersettings\") ?? {};\n\nmsg.payload = {\n \"title\": \"Filter files by Changed date\",\n \"content\": \"Specify the From and To dates and press Set filter button.<br/>To disable the filtering, click on Remove filter button.\",\n \"okText\": \"Set filter\",\n \"cancelText\": \"Remove filter\",\n \"fromdatelabel\": \"From date\",\n \"fromdatevalue\": browsersettings.changedfilter_data?.from ?? \"\",\n \"todatelabel\": \"To date\",\n \"todatevalue\": browsersettings.changedfilter_data?.to ?? \"\"\n};\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1710,"y":4880,"wires":[["7ffba3cfaacf8948"]]},{"id":"05e9769c41be095e","type":"link out","z":"0e3249ddee2000e3","name":"link out 65","mode":"link","links":["f62329c112539834"],"x":2605,"y":4880,"wires":[]},{"id":"2c1147aeeaf3a886","type":"ui-template","z":"0e3249ddee2000e3","group":"","page":"","ui":"cb79bc4520925e32","name":"Binary File Upload","order":0,"width":0,"height":0,"head":"","format":"<template>\n <v-dialog width=\"auto\" v-model=\"showDialog\">\n <v-card width=\"600\" color=\"white\">\n <v-toolbar color=\"primary\">\n <v-card-title>Upload file(s) to Node-Red</v-card-title>\n </v-toolbar>\n <br>\n <v-card-text>\n <div>File size is limited to around 1MB.</div>\n <div class=\"d-flex py-2 justify-space-between\"></div>\n <v-file-input label=\"Click here to select a file\" show-size v-model=\"uploadFile\">\n </v-file-input>\n <div class=\"text-right\">\n <v-btn density=\"compact\" variant=\"outlined\" right @click=\"startUpload\">Upload File</v-btn>\n </div>\n <div class=\"d-flex py-2 justify-space-between\"></div>\n <v-progress-linear v-model=\"progress\" height=\"25\" :color=\"progresscolor\">\n <strong>{{ progresstext }}</strong>\n </v-progress-linear>\n </v-card-text>\n <v-card-actions>\n <v-spacer></v-spacer>\n <v-btn density=\"compact\" variant=\"outlined\" right @click=\"onCloseDialog\">Close</v-btn>\n </v-card-actions>\n </v-card>\n </v-dialog>\n</template>\n\n<script>\n export default {\n data() {\n // define variables available component-wide\n // (in <template> and component functions)\n return {\n dialogData:null,\n uploadFile: null,\n progress: 0,\n progresstext: \"Select a file and click Upload File\",\n progresscolor: \"light-blue\"\n }\n },\n watch: {\n // watch for any changes of \"count\"\n msg: function(){\n // only show the dialog if msg.payload is an object and the socket id in the message\n // matches our socket id (which means the popup was initiated from this session) or\n // there is no _client property present which indicates it should be shown on all sessions.\n if (!this.msg._client || this.msg._client.socketId === this.$socket.id) {\n this.dialogData = {};\n this.dialogData.show = true;\n this.progress = 0;\n this.progresstext = \"Select a file and click Upload File\";\n this.progresscolor = \"light-blue\";\n this.uploadFile = null;\n }\n // prevent redraw on deploy\n this.msg.payload = null\n }\n },\n computed: {\n // automatically compute this variable\n // whenever VueJS deems appropriate\n showDialog: function (){\n return this.dialogData?.show === true\n }\n },\n methods: {\n // expose a method to our <template> and Vue Application\n startUpload() {\n // debugger;\n if (!this.uploadFile) {\n return;\n } else {\n if (!this.uploadFile || this.uploadFile.length !== 1) {\n console.warn('expected uploadFile to contain 1 item')\n return\n }\n const reader = new FileReader();\n \n // Use the javascript reader object to load the contents\n // of the file in the v-model prop\n reader.readAsArrayBuffer(this.uploadFile[0]);\n reader.onloadstart = () => {\n this.progresscolor = \"light-blue\";\n this.progress = 0;\n this.progresstext = \"0%\";\n };\n reader.onerror = (data) => {\n this.progresscolor = \"red\";\n this.progress = 0;\n this.progresstext = \"Failed: \" + data.target.error.name;\n };\n reader.onabort = (data) => {\n this.progresscolor = \"red\";\n this.progress = 0;\n this.progresstext = \"Aborted\";\n };\n reader.onload = () => {\n // this.data = reader.result;\n this.send({topic:\"upload\", payload: this.uploadFile[0], file:{name: this.uploadFile[0].name, size: this.uploadFile[0].size, type: this.uploadFile[0].type } });\n this.progresstext = \"Uploaded successfully\";\n };\n reader.onprogress = (data) => {\n this.progress = parseInt( ((data.loaded / data.total) * 100), 10 );\n this.progresstext = this.progress + \"%\";\n };\n }\n\n },\n onCloseDialog () {\n this.dialogData.show = false;\n }\n },\n mounted() {\n // code here when the component is first loaded\n },\n unmounted() {\n // code here when the component is removed from the Dashboard\n // i.e. when the user navigates away from the page\n }\n }\n</script>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"widget:ui","className":"","x":1710,"y":4920,"wires":[["ed131cefe755b6e2"]]},{"id":"e40a2f0c68bf45b8","type":"file","z":"0e3249ddee2000e3","name":"","filename":"filename","filenameType":"msg","appendNewline":true,"createDir":false,"overwriteFile":"true","encoding":"none","x":2140,"y":4920,"wires":[["05e9769c41be095e"]]},{"id":"ed131cefe755b6e2","type":"function","z":"0e3249ddee2000e3","name":"Get Filename","func":"if (msg.file !== undefined) {\n let folder = flow.get(\"folder\") ?? \"\";\n msg.filename = folder + \"/\" + msg.file.name;\n msg.topic = \"refresh\";\n return msg;\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1940,"y":4920,"wires":[["e40a2f0c68bf45b8"]]},{"id":"1c24793c904d41a6","type":"ui-template","z":"0e3249ddee2000e3","group":"","page":"","ui":"cb79bc4520925e32","name":"File Edit dialog","order":0,"width":0,"height":0,"head":"","format":"<template>\n <div class=\"text-center pa-4\">\n <v-dialog v-model=\"dialog\" transition=\"dialog-bottom-transition\" fullscreen>\n <v-card color=\"white\">\n <v-toolbar>\n <v-btn icon=\"mdi-close\" @click=\"dialog = false\"></v-btn>\n\n <v-toolbar-title>Edit File: {{ filename }}</v-toolbar-title>\n\n <v-spacer></v-spacer>\n\n <v-toolbar-items>\n <v-btn text=\"Save\" variant=\"text\" @click=\"saveFile\"></v-btn>\n </v-toolbar-items>\n </v-toolbar>\n <div width=\"100%\" height=\"100%\">\n <v-textarea v-model=\"filecontent\" auto-grow></v-textarea>\n </div>\n\n </v-card>\n </v-dialog>\n </div>\n</template>\n\n\n\n\n<script>\nexport default {\n data() {\n return {\n dialog: false,\n filecontent: \"\",\n filename: \"\"\n }\n },\n watch: {\n msg: function(){\n // only show the dialog if msg.payload is an object and the socket id in the message\n // matches our socket id (which means the popup was initiated from this session) or\n // there is no _client property present which indicates it should be shown on all sessions.\n if (!this.msg._client || this.msg._client.socketId === this.$socket.id) { \n this.filecontent = this.msg.payload;\n this.filename = this.msg.fname ?? \"\";\n this.dialog = true;\n }\n // prevent redraw on deploy\n this.msg.payload = null\n }\n },\n methods:{\n saveFile:function(){\n this.dialog = false;\n this.msg.payload = this.filecontent;\n this.send(this.msg);\n }\n },\n computed : {\n },\n unmounted () {\n this.dialog = false;\n }\n}\n</script>","storeOutMessages":true,"passthru":false,"resendOnRefresh":true,"templateScope":"widget:ui","className":"","x":2060,"y":4960,"wires":[["aa29081e46a58947","d6688fa71eb11ec8"]]},{"id":"b8e9174b18451592","type":"inject","z":"0e3249ddee2000e3","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"},{"p":"filename","v":"test.json","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"this is my file content","payloadType":"str","x":1850,"y":5000,"wires":[["1c24793c904d41a6"]]},{"id":"aa29081e46a58947","type":"debug","z":"0e3249ddee2000e3","name":"debug 368","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":2250,"y":5000,"wires":[]},{"id":"df11ec0e75f78ce1","type":"function","z":"0e3249ddee2000e3","name":"Edit file action","func":"let lastselected = flow.get(\"lastselected\") ?? [];\nif (lastselected.length === 0) {\n // single file delete\n let lastclicked = flow.get(\"lastclicked\");\n if (lastclicked !== undefined) {\n msg.filename = lastclicked.name;\n msg.fname = lastclicked.fname;\n msg.topic = \"refresh\";\n return msg;\n }\n}\nif (lastselected.length === 1) {\n // one file is selected, delete that single file\n msg.filename = lastselected[0].name;\n msg.fname = lastselected[0].fname;\n msg.topic = \"refresh\";\n return msg;\n}\n\n\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1700,"y":4960,"wires":[["d00dd4fb6767700f"]]},{"id":"d00dd4fb6767700f","type":"file in","z":"0e3249ddee2000e3","name":"","filename":"filename","filenameType":"msg","format":"utf8","chunk":false,"sendError":false,"encoding":"none","allProps":false,"x":1880,"y":4960,"wires":[["1c24793c904d41a6"]]},{"id":"d6688fa71eb11ec8","type":"file","z":"0e3249ddee2000e3","name":"","filename":"filename","filenameType":"msg","appendNewline":true,"createDir":true,"overwriteFile":"true","encoding":"none","x":2250,"y":4960,"wires":[["05e9769c41be095e"]]},{"id":"74da605bad30eb60","type":"ui-group","name":"Toolbar","page":"e8e20bd9167fab37","width":"24","height":"1","order":1,"showTitle":false,"className":"","visible":"true","disabled":"false"},{"id":"78b1a8fd63ef7bbc","type":"ui-group","name":"File List","page":"e8e20bd9167fab37","width":"18","height":"1","order":2,"showTitle":false,"className":"","visible":"true","disabled":"false"},{"id":"771fdaf427baf0f4","type":"ui-group","name":"File View","page":"e8e20bd9167fab37","width":"6","height":"1","order":3,"showTitle":false,"className":"","visible":"true","disabled":"false"},{"id":"cb79bc4520925e32","type":"ui-base","name":"My UI","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false},{"id":"e8e20bd9167fab37","type":"ui-page","name":"File Browser","ui":"cb79bc4520925e32","path":"/filebrowser","icon":"file-document-multiple","layout":"grid","theme":"70c3f16306584459","order":-1,"className":"","visible":"true","disabled":"false"},{"id":"70c3f16306584459","type":"ui-theme","name":"File Browser","colors":{"surface":"#5b56fc","primary":"#0094ce","bgPage":"#ffffff","groupBg":"#ffffff","groupOutline":"#ffffff"},"sizes":{"pagePadding":"0px","groupGap":"0px","groupBorderRadius":"0px","widgetGap":"6px"}}]