mirror of
https://github.com/jpros/tacticalrmm-web.git
synced 2026-03-01 07:41:00 +00:00
added script cloning functionality
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
<q-item-label>New Script</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-close-popup @click="showScriptUploadModal = true">
|
||||
<q-item clickable v-close-popup @click="uploadScript">
|
||||
<q-item-section side>
|
||||
<q-icon size="xs" name="cloud_upload" />
|
||||
</q-item-section>
|
||||
@@ -30,30 +30,6 @@
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
<q-btn
|
||||
label="View Code"
|
||||
:disable="!isRowSelected"
|
||||
dense
|
||||
flat
|
||||
push
|
||||
unelevated
|
||||
no-caps
|
||||
icon="remove_red_eye"
|
||||
@click="viewCode(selectedScript)"
|
||||
/>
|
||||
<q-btn
|
||||
label="Download Script"
|
||||
:disable="!isRowSelected"
|
||||
dense
|
||||
flat
|
||||
push
|
||||
unelevated
|
||||
no-caps
|
||||
icon="cloud_download"
|
||||
@click="downloadScript(selectedScript)"
|
||||
/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<q-btn
|
||||
dense
|
||||
flat
|
||||
@@ -95,13 +71,11 @@
|
||||
no-connectors
|
||||
node-key="id"
|
||||
v-model:expanded="expanded"
|
||||
@update:selected="nodeSelected"
|
||||
v-model:selected="selected"
|
||||
no-results-label="No Scripts Found"
|
||||
no-nodes-label="No Scripts Found"
|
||||
>
|
||||
<template v-slot:header-script="props">
|
||||
<div :class="props.node.id === props.tree.selected ? 'text-primary' : ''">
|
||||
<div>
|
||||
<q-icon v-if="props.node.favorite" color="yellow-8" name="star" size="sm" class="q-px-sm" />
|
||||
<q-icon v-else color="yellow-8" name="star_outline" size="sm" class="q-px-sm" />
|
||||
|
||||
@@ -119,9 +93,16 @@
|
||||
<span class="q-pl-xs">{{ props.node.description }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Context Menu -->
|
||||
<!-- context menu -->
|
||||
<q-menu context-menu>
|
||||
<q-list dense style="min-width: 200px">
|
||||
<q-item clickable v-close-popup @click="cloneScript(props.node)">
|
||||
<q-item-section side>
|
||||
<q-icon name="content_copy" />
|
||||
</q-item-section>
|
||||
<q-item-section>Clone</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item
|
||||
clickable
|
||||
v-close-popup
|
||||
@@ -207,14 +188,17 @@
|
||||
<template v-slot:no-data> No Scripts Found </template>
|
||||
<template v-slot:body="props">
|
||||
<!-- Table View -->
|
||||
<q-tr
|
||||
:class="`${rowSelectedClass(props.row.id)} cursor-pointer`"
|
||||
@click="selectedScript = props.row"
|
||||
@contextmenu="selectedScript = props.row"
|
||||
>
|
||||
<q-tr>
|
||||
<!-- Context Menu -->
|
||||
<q-menu context-menu>
|
||||
<q-list dense style="min-width: 200px">
|
||||
<q-item clickable v-close-popup @click="cloneScript(props.node)">
|
||||
<q-item-section side>
|
||||
<q-icon name="content_copy" />
|
||||
</q-item-section>
|
||||
<q-item-section>Clone</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item
|
||||
clickable
|
||||
v-close-popup
|
||||
@@ -314,14 +298,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</q-card>
|
||||
<q-dialog v-model="showScriptUploadModal">
|
||||
<ScriptUploadModal
|
||||
:script="selectedScript"
|
||||
:categories="categories"
|
||||
@close="showScriptUploadModal = false"
|
||||
@add="getScripts"
|
||||
/>
|
||||
</q-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -333,17 +309,13 @@ import ScriptFormModal from "@/components/modals/scripts/ScriptFormModal";
|
||||
|
||||
export default {
|
||||
name: "ScriptManager",
|
||||
components: { ScriptUploadModal },
|
||||
mixins: [mixins],
|
||||
data() {
|
||||
return {
|
||||
scripts: [],
|
||||
selectedScript: {},
|
||||
showScriptUploadModal: false,
|
||||
search: "",
|
||||
tableView: true,
|
||||
expanded: [],
|
||||
selected: null,
|
||||
pagination: {
|
||||
rowsPerPage: 0,
|
||||
sortBy: "favorite",
|
||||
@@ -405,7 +377,6 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
getScripts() {
|
||||
this.clearRow();
|
||||
this.$axios
|
||||
.get("/scripts/scripts/")
|
||||
.then(r => {
|
||||
@@ -416,9 +387,6 @@ export default {
|
||||
setShowCommunityScripts(show) {
|
||||
this.$store.dispatch("setShowCommunityScripts", show);
|
||||
},
|
||||
clearRow() {
|
||||
this.selectedScript = {};
|
||||
},
|
||||
viewCode(script) {
|
||||
this.$q.dialog({
|
||||
component: ScriptFormModal,
|
||||
@@ -478,9 +446,6 @@ export default {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
rowSelectedClass(id) {
|
||||
if (this.selectedScript.id === id) return this.$q.dark.isActive ? "highlight-dark" : "highlight";
|
||||
},
|
||||
favoriteText(isFavorite) {
|
||||
return isFavorite ? "Remove as Favorite" : "Add as Favorite";
|
||||
},
|
||||
@@ -513,16 +478,34 @@ export default {
|
||||
},
|
||||
setTableView(view) {
|
||||
this.tableView = view;
|
||||
this.selectedScript = {};
|
||||
this.selected = null;
|
||||
this.expanded = [];
|
||||
},
|
||||
nodeSelected(nodeid) {
|
||||
if (nodeid) {
|
||||
this.selectedScript = this.$refs.folderTree.getNodeByKey(nodeid);
|
||||
} else {
|
||||
this.selectedScript = {};
|
||||
}
|
||||
cloneScript(script) {
|
||||
this.$q
|
||||
.dialog({
|
||||
component: ScriptFormModal,
|
||||
componentProps: {
|
||||
script: script,
|
||||
categories: this.categories,
|
||||
readonly: false,
|
||||
clone: true,
|
||||
},
|
||||
})
|
||||
.onOk(() => {
|
||||
this.getScripts();
|
||||
});
|
||||
},
|
||||
uploadScript() {
|
||||
this.$q
|
||||
.dialog({
|
||||
component: ScriptUploadModal,
|
||||
componentProps: {
|
||||
categories: this.categories,
|
||||
},
|
||||
})
|
||||
.onOk(() => {
|
||||
this.getScripts();
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
@@ -539,9 +522,6 @@ export default {
|
||||
});
|
||||
return list;
|
||||
},
|
||||
isRowSelected() {
|
||||
return this.selectedScript.id !== null && this.selectedScript.id !== undefined;
|
||||
},
|
||||
tree() {
|
||||
if (this.tableView || this.visibleScripts.length === 0) {
|
||||
return [];
|
||||
|
||||
@@ -136,7 +136,14 @@ export default {
|
||||
props: {
|
||||
script: Object,
|
||||
categories: !Array,
|
||||
readonly: Boolean,
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
clone: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -164,7 +171,7 @@ export default {
|
||||
submit() {
|
||||
this.$q.loading.show();
|
||||
|
||||
if (!!this.script) {
|
||||
if (this.script && !this.clone) {
|
||||
this.$axios
|
||||
.put(`/scripts/${this.script.id}/script/`, this.localScript)
|
||||
.then(r => {
|
||||
@@ -247,8 +254,12 @@ export default {
|
||||
return this.localScript.favorite ? "star" : "star_outline";
|
||||
},
|
||||
title() {
|
||||
if (!!this.script) {
|
||||
return this.readonly ? `Viewing ${this.script.name}` : `Editing ${this.script.name}`;
|
||||
if (this.script) {
|
||||
return this.readonly
|
||||
? `Viewing ${this.script.name}`
|
||||
: this.clone
|
||||
? `Copying ${this.script.name}`
|
||||
: `Editing ${this.script.name}`;
|
||||
} else {
|
||||
return "Adding new script";
|
||||
}
|
||||
@@ -264,14 +275,14 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (!!this.script) {
|
||||
this.localScript.id = this.script.id;
|
||||
this.localScript.name = this.script.name;
|
||||
if (this.script) {
|
||||
this.localScript.id = this.clone ? null : this.script.id;
|
||||
this.localScript.name = this.clone ? "Copy of " + this.script.name : this.script.name;
|
||||
this.localScript.description = this.script.description;
|
||||
this.localScript.favorite = this.script.favorite;
|
||||
this.localScript.favorite = this.clone ? false : 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.script_type = this.clone ? "userdefined" : this.script.script_type;
|
||||
this.localScript.default_timeout = this.script.default_timeout;
|
||||
this.localScript.args = this.script.args;
|
||||
this.getCode();
|
||||
|
||||
@@ -1,107 +1,109 @@
|
||||
<template>
|
||||
<q-card style="width: 40vw">
|
||||
<q-bar>
|
||||
Add Script
|
||||
<q-space />
|
||||
<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.prevent="submit">
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">Name:</div>
|
||||
<div class="col-10">
|
||||
<q-input outlined dense v-model="script.name" :rules="[val => !!val || '*Required']" />
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">Description:</div>
|
||||
<div class="col-10">
|
||||
<q-input outlined dense v-model="script.description" type="textarea" />
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">Category:</div>
|
||||
<q-select
|
||||
hint="Press Enter or Tab when adding a new value"
|
||||
dense
|
||||
options-dense
|
||||
class="col-10"
|
||||
outlined
|
||||
v-model="script.category"
|
||||
:options="filterOptions"
|
||||
use-input
|
||||
clearable
|
||||
new-value-mode="add-unique"
|
||||
debounce="0"
|
||||
@filter="filterFn"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">File Upload:</div>
|
||||
<div class="col-10">
|
||||
<q-file
|
||||
v-model="script.filename"
|
||||
label="Supported file types: .ps1, .bat, .py"
|
||||
stack-label
|
||||
filled
|
||||
counter
|
||||
class="full-width"
|
||||
accept=".ps1, .bat, .py"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="attach_file" />
|
||||
</template>
|
||||
</q-file>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">Type:</div>
|
||||
<q-select
|
||||
dense
|
||||
options-dense
|
||||
class="col-10"
|
||||
outlined
|
||||
v-model="script.shell"
|
||||
:options="shellOptions"
|
||||
emit-value
|
||||
map-options
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">Script Arguments:</div>
|
||||
<q-select
|
||||
label="(press Enter after typing each argument)"
|
||||
class="col-10"
|
||||
filled
|
||||
v-model="script.args"
|
||||
use-input
|
||||
use-chips
|
||||
multiple
|
||||
dense
|
||||
hide-dropdown-icon
|
||||
input-debounce="0"
|
||||
new-value-mode="add"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-4">Default Timeout (seconds)</div>
|
||||
<q-input
|
||||
type="number"
|
||||
outlined
|
||||
dense
|
||||
class="col-8"
|
||||
v-model.number="script.default_timeout"
|
||||
:rules="[val => val >= 5 || 'Minimum is 5']"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-actions>
|
||||
<q-dialog ref="dialog" @hide="onHide">
|
||||
<q-card class="q-dialog-plugin" style="width: 40vw">
|
||||
<q-bar>
|
||||
Add Script
|
||||
<q-space />
|
||||
<q-btn dense flat label="Cancel" v-close-popup />
|
||||
<q-btn dense flat label="Add" color="primary" type="submit" />
|
||||
</q-card-actions>
|
||||
</q-form>
|
||||
</q-card>
|
||||
<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.prevent="submit">
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">Name:</div>
|
||||
<div class="col-10">
|
||||
<q-input outlined dense v-model="script.name" :rules="[val => !!val || '*Required']" />
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">Description:</div>
|
||||
<div class="col-10">
|
||||
<q-input outlined dense v-model="script.description" type="textarea" />
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">Category:</div>
|
||||
<q-select
|
||||
hint="Press Enter or Tab when adding a new value"
|
||||
dense
|
||||
options-dense
|
||||
class="col-10"
|
||||
outlined
|
||||
v-model="script.category"
|
||||
:options="filterOptions"
|
||||
use-input
|
||||
clearable
|
||||
new-value-mode="add-unique"
|
||||
debounce="0"
|
||||
@filter="filterFn"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">File Upload:</div>
|
||||
<div class="col-10">
|
||||
<q-file
|
||||
v-model="script.filename"
|
||||
label="Supported file types: .ps1, .bat, .py"
|
||||
stack-label
|
||||
filled
|
||||
counter
|
||||
class="full-width"
|
||||
accept=".ps1, .bat, .py"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="attach_file" />
|
||||
</template>
|
||||
</q-file>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">Type:</div>
|
||||
<q-select
|
||||
dense
|
||||
options-dense
|
||||
class="col-10"
|
||||
outlined
|
||||
v-model="script.shell"
|
||||
:options="shellOptions"
|
||||
emit-value
|
||||
map-options
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">Script Arguments:</div>
|
||||
<q-select
|
||||
label="(press Enter after typing each argument)"
|
||||
class="col-10"
|
||||
filled
|
||||
v-model="script.args"
|
||||
use-input
|
||||
use-chips
|
||||
multiple
|
||||
dense
|
||||
hide-dropdown-icon
|
||||
input-debounce="0"
|
||||
new-value-mode="add"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-4">Default Timeout (seconds)</div>
|
||||
<q-input
|
||||
type="number"
|
||||
outlined
|
||||
dense
|
||||
class="col-8"
|
||||
v-model.number="script.default_timeout"
|
||||
:rules="[val => val >= 5 || 'Minimum is 5']"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-actions>
|
||||
<q-space />
|
||||
<q-btn dense flat label="Cancel" v-close-popup />
|
||||
<q-btn dense flat label="Add" color="primary" type="submit" />
|
||||
</q-card-actions>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -109,7 +111,7 @@ import mixins from "@/mixins/mixins";
|
||||
|
||||
export default {
|
||||
name: "ScriptModal",
|
||||
emits: ["add", "close"],
|
||||
emits: ["ok", "hide", "cancel"],
|
||||
mixins: [mixins],
|
||||
props: {
|
||||
categories: !Array,
|
||||
@@ -159,8 +161,7 @@ export default {
|
||||
.post("/scripts/scripts/", formData)
|
||||
.then(r => {
|
||||
this.$q.loading.hide();
|
||||
this.$emit("close");
|
||||
this.$emit("add");
|
||||
this.onOk();
|
||||
this.notifySuccess(r.data);
|
||||
})
|
||||
.catch(e => {
|
||||
@@ -177,6 +178,19 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
show() {
|
||||
this.$refs.dialog.show();
|
||||
},
|
||||
hide() {
|
||||
this.$refs.dialog.hide();
|
||||
},
|
||||
onHide() {
|
||||
this.$emit("hide");
|
||||
},
|
||||
onOk() {
|
||||
this.$emit("ok");
|
||||
this.hide();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user