Files
tacticalrmm-web/src/components/modals/scripts/ScriptFormModal.vue

311 lines
8.6 KiB
Vue

<template>
<q-dialog ref="dialog" @hide="onHide" persistent :maximized="maximized">
<q-card class="q-dialog-plugin" :style="getMaxWidth">
<q-bar>
{{ title }}
<q-space />
<q-btn dense flat icon="minimize" @click="maximized = false" :disable="!maximized">
<q-tooltip v-if="maximized" class="bg-white text-primary">Minimize</q-tooltip>
</q-btn>
<q-btn dense flat icon="crop_square" @click="maximized = true" :disable="maximized">
<q-tooltip v-if="!maximized" class="bg-white text-primary">Maximize</q-tooltip>
</q-btn>
<q-btn dense flat icon="close" v-close-popup>
<q-tooltip class="bg-white text-primary">Close</q-tooltip>
</q-btn>
</q-bar>
<q-form @submit="submit">
<q-card-section class="row">
<div class="q-pa-sm col-1" style="width: auto">
<q-icon
class="cursor-pointer"
:name="favoriteIcon"
size="md"
color="yellow-8"
@[clickEvent]="localScript.favorite = !localScript.favorite"
/>
</div>
<div class="q-pa-sm col-2">
<q-input
filled
dense
:readonly="readonly"
v-model="localScript.name"
label="Name"
:rules="[val => !!val || '*Required']"
/>
</div>
<div class="q-pa-sm col-2">
<q-select
:readonly="readonly"
options-dense
filled
dense
v-model="localScript.shell"
:options="shellOptions"
emit-value
map-options
label="Shell Type"
/>
</div>
<div class="q-pa-sm col-2">
<q-input
type="number"
filled
dense
:readonly="readonly"
v-model.number="localScript.default_timeout"
label="Timeout (seconds)"
:rules="[val => val >= 5 || 'Minimum is 5']"
/>
</div>
<div class="q-pa-sm col-3">
<q-select
hint="Press Enter or Tab when adding a new value"
dense
options-dense
filled
v-model="localScript.category"
:options="filterOptions"
use-input
clearable
new-value-mode="add-unique"
debounce="0"
@filter="filterFn"
label="Category"
:readonly="readonly"
/>
</div>
<div class="q-pa-sm col-2">
<q-input filled dense :readonly="readonly" v-model="localScript.description" label="Description" />
</div>
</q-card-section>
<div class="q-px-sm q-pt-none q-pb-sm q-mt-none row">
<q-select
label="Script Arguments (press Enter after typing each argument)"
class="col-12"
filled
v-model="localScript.args"
use-input
use-chips
multiple
dense
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
</div>
<prism-editor
class="editor"
:readonly="readonly"
v-model="localScript.code"
:highlight="highlighter"
:style="heightVar"
line-numbers
@click="focusTextArea"
/>
<q-card-actions v-if="!readonly" align="right">
<q-btn dense flat label="Cancel" v-close-popup />
<q-btn dense flat label="Save" color="primary" type="submit" />
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<script>
import mixins from "@/mixins/mixins";
import { PrismEditor } from "vue-prism-editor";
import "vue-prism-editor/dist/prismeditor.min.css";
import { highlight, languages } from "prismjs/components/prism-core";
import "prismjs/components/prism-batch";
import "prismjs/components/prism-python";
import "prismjs/components/prism-powershell";
import "prismjs/themes/prism-tomorrow.css";
export default {
name: "ScriptFormModal",
emits: ["hide", "ok", "cancel"],
mixins: [mixins],
components: {
PrismEditor,
},
props: {
script: Object,
categories: !Array,
readonly: Boolean,
},
data() {
return {
localScript: {
name: "",
code: "",
shell: "powershell",
description: "",
args: [],
category: "",
favorite: false,
script_type: "userdefined",
default_timeout: 90,
},
maximized: false,
filterOptions: [],
shellOptions: [
{ label: "Powershell", value: "powershell" },
{ label: "Batch", value: "cmd" },
{ label: "Python", value: "python" },
],
};
},
methods: {
submit() {
this.$q.loading.show();
if (!!this.script) {
this.$axios
.put(`/scripts/${this.script.id}/script/`, this.localScript)
.then(r => {
this.$q.loading.hide();
this.onOk();
this.notifySuccess(r.data);
})
.catch(e => {
this.$q.loading.hide();
});
} else {
this.$axios
.post(`/scripts/scripts/`, this.localScript)
.then(r => {
this.$q.loading.hide();
this.onOk();
this.notifySuccess(r.data);
})
.catch(e => {
this.$q.loading.hide();
});
}
},
getCode() {
this.$q.loading.show();
this.$axios
.get(`/scripts/${this.script.id}/download/`)
.then(r => {
this.$q.loading.hide();
this.localScript.code = r.data.code;
})
.catch(e => {
this.$q.loading.hide();
});
},
highlighter(code) {
let lang = this.localScript.shell === "cmd" ? "batch" : this.localScript.shell;
return highlight(code, languages[lang]);
},
show() {
this.$refs.dialog.show();
},
hide() {
this.$refs.dialog.hide();
},
onHide() {
this.$emit("hide");
},
onOk() {
this.$emit("ok");
this.hide();
},
onCancel() {
this.hide();
},
filterFn(val, update) {
update(() => {
if (val === "") {
this.filterOptions = this.categories;
} else {
const needle = val.toLowerCase();
this.filterOptions = this.categories.filter(v => v.toLowerCase().indexOf(needle) > -1);
}
});
},
focusTextArea() {
document.getElementsByClassName("prism-editor__textarea")[0].focus();
},
},
computed: {
favoriteIcon() {
return this.localScript.favorite ? "star" : "star_outline";
},
title() {
if (!!this.script) {
return this.readonly ? `Viewing ${this.script.name}` : `Editing ${this.script.name}`;
} else {
return "Adding new script";
}
},
clickEvent() {
return !this.readonly ? "click" : null;
},
getMaxWidth() {
return this.maximized ? "" : "width: 70vw; max-width: 90vw";
},
heightVar() {
return this.maximized ? "--prism-height: 76vh" : "--prism-height: 70vh";
},
},
mounted() {
if (!!this.script) {
this.localScript.id = this.script.id;
this.localScript.name = this.script.name;
this.localScript.description = this.script.description;
this.localScript.favorite = this.script.favorite;
this.localScript.shell = this.script.shell;
this.localScript.category = this.script.category;
this.localScript.script_type = this.script.script_type;
this.localScript.default_timeout = this.script.default_timeout;
this.localScript.args = this.script.args;
this.getCode();
}
},
};
</script>
<style>
/* required class */
.editor {
/* we dont use `language-` classes anymore so thats why we need to add background and text color manually */
background: #2d2d2d;
color: #ccc;
/* you must provide font-family font-size line-height. Example: */
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
font-size: 14px;
line-height: 1.5;
padding: 5px;
height: var(--prism-height);
}
/* optional class for removing the outline */
.prism-editor__textarea:focus {
outline: none;
}
.prism-editor__textarea,
.prism-editor__container {
width: 500em !important;
-ms-overflow-style: none;
scrollbar-width: none;
}
.prism-editor__container::-webkit-scrollbar,
.prism-editor__textarea::-webkit-scrollbar {
display: none;
}
.prism-editor__editor {
white-space: pre !important;
}
.prism-editor__container {
overflow-x: auto !important;
}
</style>