Compare commits

..

33 Commits

Author SHA1 Message Date
wh1te909
0b6ae80777 Release 0.101.39 2024-02-02 00:55:23 +00:00
wh1te909
5e0fab88a3 bump version 2024-02-02 00:53:27 +00:00
wh1te909
bf8797264b feat: hide custom fields in summary tab only amidaware/tacticalrmm#1745 2024-01-28 03:22:54 +00:00
wh1te909
14bde967bd feat: add serial number to linux/mac amidaware/tacticalrmm#1683 2024-01-27 02:47:23 +00:00
wh1te909
596ce69789 feat: add from name to email amidaware/tacticalrmm#1726 2024-01-26 00:38:12 +00:00
wh1te909
c5491dcb73 feat: add time and ret code to script test closes amidaware/tacticalrmm#1713 2024-01-26 00:04:58 +00:00
wh1te909
3f6340f0a1 update reqs 2024-01-26 00:04:19 +00:00
wh1te909
351f0870a9 update reqs 2024-01-19 08:09:06 +00:00
Dan
f2638a4c5e Merge pull request #16 from silversword411/develop
Increase user preferences width
2024-01-18 23:14:41 -08:00
silversword411
2bd00d5ca0 Increase user preferences width 2024-01-19 05:31:47 +00:00
wh1te909
00a40dd450 forgot to include 80% 2024-01-17 19:19:00 +00:00
wh1te909
cfe1cb2dbf Release 0.101.38 2023-12-22 17:50:19 +00:00
wh1te909
16fb75b56c bump version 2023-12-22 17:49:31 +00:00
wh1te909
094cf45ce3 add M3 2023-12-22 17:44:55 +00:00
wh1te909
d6984b3da9 use ticket instead of email 2023-12-22 17:44:36 +00:00
sadnub
53fc6f4cde fix folder view 2023-12-12 10:50:32 -05:00
wh1te909
e1dc8050e3 Release 0.101.37 2023-12-01 18:55:37 +00:00
wh1te909
49da10cf0b bump version 2023-12-01 18:53:26 +00:00
wh1te909
a3e10910bf wording 2023-12-01 18:52:01 +00:00
wh1te909
3ff9edc424 update reqs 2023-11-29 22:41:49 +00:00
sadnub
69414d4083 add unsaved changes to new scripts and close and cancel buttons 2023-11-29 10:24:13 -05:00
sadnub
e06b7a7775 add last_seen date to summary tab as a tooltip 2023-11-24 18:34:29 -05:00
sadnub
c006e4d922 make policy tasks sort ascending 2023-11-24 18:19:39 -05:00
sadnub
df6fe0863b change sort order for reports manager 2023-11-24 18:17:29 -05:00
sadnub
d55a29911c add trmm logo to community scripts in dropdown and improve img loading with es6 import 2023-11-24 17:47:07 -05:00
sadnub
d0e49d27fd add prompt for close if using esc in script manager 2023-11-24 17:02:45 -05:00
sadnub
1299bfc93e add trmm icon to builtin scripts 2023-11-24 16:19:33 -05:00
sadnub
be999646d4 make policy status width larger 2023-11-24 15:50:44 -05:00
sadnub
e57d32f122 add onboarding task in ui 2023-11-22 23:37:18 -05:00
wh1te909
3e6365574e Release 0.101.36 2023-11-23 00:03:20 +00:00
wh1te909
08fa8da735 bump version 2023-11-23 00:03:10 +00:00
sadnub
4ab31a529e update to node 18 2023-11-11 13:51:48 -05:00
wh1te909
466725d5c2 rework uninstall perm amidaware/tacticalrmm#1673 2023-11-10 01:20:25 +00:00
21 changed files with 771 additions and 610 deletions

View File

@@ -1,9 +1,9 @@
version: '3.4' version: '3.7'
services: services:
app-dev: app-dev:
container_name: trmm-app-dev container_name: trmm-app-dev
image: node:16-alpine image: node:18-alpine
restart: always restart: always
command: /bin/sh -c "npm install --cache ~/.npm && npm run serve" command: /bin/sh -c "npm install --cache ~/.npm && npm run serve"
user: 1000:1000 user: 1000:1000

774
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "web", "name": "web",
"version": "0.101.35", "version": "0.101.39",
"private": true, "private": true,
"productName": "Tactical RMM", "productName": "Tactical RMM",
"scripts": { "scripts": {
@@ -10,34 +10,34 @@
"format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore" "format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore"
}, },
"dependencies": { "dependencies": {
"@quasar/extras": "1.16.7", "@quasar/extras": "1.16.9",
"apexcharts": "3.44.0", "apexcharts": "3.45.2",
"axios": "1.6.0", "axios": "1.6.7",
"dotenv": "16.3.1", "dotenv": "16.4.1",
"qrcode.vue": "3.4.1", "qrcode.vue": "3.4.1",
"quasar": "2.13.0", "quasar": "2.14.3",
"vue": "3.3.8", "vue": "3.4.15",
"vue3-apexcharts": "1.4.4", "vue3-apexcharts": "1.4.4",
"vuedraggable": "4.1.0", "vuedraggable": "4.1.0",
"vue-router": "4.2.5", "vue-router": "4.2.5",
"@vueuse/core": "10.5.0", "@vueuse/core": "10.7.2",
"@vueuse/shared": "10.5.0", "@vueuse/shared": "10.7.2",
"monaco-editor": "0.44.0", "monaco-editor": "0.45.0",
"vuex": "4.1.0", "vuex": "4.1.0",
"yaml": "2.3.4" "yaml": "2.3.4"
}, },
"devDependencies": { "devDependencies": {
"@quasar/cli": "2.3.0", "@quasar/cli": "2.3.0",
"@intlify/unplugin-vue-i18n": "1.4.0", "@intlify/unplugin-vue-i18n": "2.0.0",
"@quasar/app-vite": "1.6.2", "@quasar/app-vite": "1.7.3",
"@types/node": "20.8.10", "@types/node": "20.11.6",
"@typescript-eslint/eslint-plugin": "6.10.0", "@typescript-eslint/eslint-plugin": "6.19.1",
"@typescript-eslint/parser": "6.10.0", "@typescript-eslint/parser": "6.19.1",
"autoprefixer": "10.4.16", "autoprefixer": "10.4.17",
"eslint": "8.53.0", "eslint": "8.56.0",
"eslint-config-prettier": "9.0.0", "eslint-config-prettier": "9.1.0",
"eslint-plugin-vue": "8.7.1", "eslint-plugin-vue": "8.7.1",
"prettier": "3.0.3", "prettier": "3.2.4",
"typescript": "5.2.2" "typescript": "5.3.3"
} }
} }

BIN
src/assets/trmm_256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -85,10 +85,6 @@
v-model="localRole.can_uninstall_agents" v-model="localRole.can_uninstall_agents"
label="Uninstall Agents" label="Uninstall Agents"
/> />
<q-checkbox
v-model="localRole.can_ping_agents"
label="Ping Agents"
/>
<q-checkbox <q-checkbox
v-model="localRole.can_update_agents" v-model="localRole.can_update_agents"
label="Update Agents" label="Update Agents"
@@ -447,7 +443,6 @@ export default {
can_uninstall_agents: false, can_uninstall_agents: false,
can_update_agents: false, can_update_agents: false,
can_edit_agent: false, can_edit_agent: false,
can_ping_agents: false,
can_manage_procs: false, can_manage_procs: false,
can_view_eventlogs: false, can_view_eventlogs: false,
can_send_cmd: false, can_send_cmd: false,

View File

@@ -34,7 +34,7 @@
:color="dash_warning_color" :color="dash_warning_color"
class="q-mr-sm" class="q-mr-sm"
> >
<q-tooltip>Agent offline</q-tooltip> <q-tooltip>{{ store.getters.formatDate(summary.last_seen) }}</q-tooltip>
</q-icon> </q-icon>
<q-icon <q-icon
v-else v-else
@@ -43,7 +43,7 @@
:color="dash_positive_color" :color="dash_positive_color"
class="q-mr-sm" class="q-mr-sm"
> >
<q-tooltip>Agent online</q-tooltip> <q-tooltip>{{ store.getters.formatDate(summary.last_seen) }}</q-tooltip>
</q-icon> </q-icon>
<b>{{ summary.hostname }}</b> <b>{{ summary.hostname }}</b>
<span v-if="summary.maintenance_mode"> <span v-if="summary.maintenance_mode">
@@ -267,7 +267,11 @@ export default {
const loading = ref(false); const loading = ref(false);
const serial_number = computed(() => { const serial_number = computed(() => {
return summary.value.wmi_detail.bios?.[0]?.[0]?.SerialNumber; if (summary.value.plat === "windows") {
return summary.value.wmi_detail.bios?.[0]?.[0]?.SerialNumber;
} else {
return summary.value.wmi_detail.serialnumber;
}
}); });
const cpu = computed(() => { const cpu = computed(() => {
@@ -280,7 +284,7 @@ export default {
function diskBarColor(percent) { function diskBarColor(percent) {
if (percent < 80) { if (percent < 80) {
return dash_positive_color.value; return dash_positive_color.value;
} else if (percent > 80 && percent < 95) { } else if (percent >= 80 && percent < 95) {
return dash_warning_color.value; return dash_warning_color.value;
} else { } else {
return dash_negative_color.value; return dash_negative_color.value;
@@ -311,11 +315,11 @@ export default {
const ret = []; const ret = [];
for (const customField of summary.value.custom_fields) { for (const customField of summary.value.custom_fields) {
const definition = customFieldsDefinitions.value.find( const definition = customFieldsDefinitions.value.find(
(def) => def.id === customField.field (def) => def.id === customField.field,
); );
if ( if (
definition && definition &&
!definition.hide_in_ui && !definition.hide_in_summary &&
customField.value?.length > 0 customField.value?.length > 0
) { ) {
ret.push({ ret.push({
@@ -381,6 +385,7 @@ export default {
dash_negative_color, dash_negative_color,
serial_number, serial_number,
cpu, cpu,
store,
// methods // methods
getSummary, getSummary,

View File

@@ -254,7 +254,7 @@ export default {
pagination: { pagination: {
rowsPerPage: 0, rowsPerPage: 0,
sortBy: "name", sortBy: "name",
descending: true, descending: false,
}, },
}; };
}, },
@@ -321,7 +321,7 @@ export default {
runTask(task) { runTask(task) {
if (!task.enabled) { if (!task.enabled) {
this.notifyError( this.notifyError(
"Task cannot be run when it's disabled. Enable it first." "Task cannot be run when it's disabled. Enable it first.",
); );
return; return;
} }

View File

@@ -1,6 +1,6 @@
<template> <template>
<q-dialog ref="dialog" @hide="onHide"> <q-dialog ref="dialog" @hide="onHide">
<q-card class="q-dialog-plugin" style="width: 90vw"> <q-card class="q-dialog-plugin" style="min-width: 70vw">
<q-bar> <q-bar>
{{ title.slice(0, 27) }} {{ title.slice(0, 27) }}
<q-space /> <q-space />

View File

@@ -137,7 +137,7 @@
<q-radio <q-radio
v-model="goarch" v-model="goarch"
:val="GOARCH_ARM64" :val="GOARCH_ARM64"
label="Apple Silicon (M1, M2)" label="Apple Silicon (M1, M2, M3)"
v-show="agentOS === 'darwin'" v-show="agentOS === 'darwin'"
/> />
<q-radio <q-radio

View File

@@ -142,6 +142,11 @@
v-model="localField.hide_in_ui" v-model="localField.hide_in_ui"
color="green" color="green"
/> />
<q-toggle
label="Hide in Summary Tab"
v-model="localField.hide_in_summary"
color="green"
/>
</q-card-section> </q-card-section>
<q-card-actions align="right"> <q-card-actions align="right">
<q-btn flat label="Cancel" v-close-popup /> <q-btn flat label="Cancel" v-close-popup />
@@ -172,6 +177,7 @@ export default {
default_value_bool: false, default_value_bool: false,
default_values_multiple: [], default_values_multiple: [],
hide_in_ui: false, hide_in_ui: false,
hide_in_summary: false,
}, },
modelOptions: [ modelOptions: [
{ label: "Client", value: "client" }, { label: "Client", value: "client" },

View File

@@ -57,6 +57,10 @@
<q-td> <q-td>
<q-icon v-if="props.row.hide_in_ui" name="check" /> <q-icon v-if="props.row.hide_in_ui" name="check" />
</q-td> </q-td>
<!-- hide in summary tab -->
<q-td>
<q-icon v-if="props.row.hide_in_summary" name="check" />
</q-td>
<!-- default value --> <!-- default value -->
<q-td v-if="props.row.type === 'checkbox'"> <q-td v-if="props.row.type === 'checkbox'">
{{ props.row.default_value_bool }} {{ props.row.default_value_bool }}
@@ -123,6 +127,13 @@ export default {
align: "left", align: "left",
sortable: true, sortable: true,
}, },
{
name: "hide_in_summary",
label: "Hide in Summary Tab",
field: "hide_in_summary",
align: "left",
sortable: true,
},
{ {
name: "default_value", name: "default_value",
label: "Default Value", label: "Default Value",

View File

@@ -71,7 +71,7 @@
icon="info" icon="info"
@click=" @click="
openURL( openURL(
'https://quasar.dev/quasar-utils/date-utils#format-for-display' 'https://quasar.dev/quasar-utils/date-utils#format-for-display',
) )
" "
> >
@@ -216,7 +216,7 @@
<div class="text-subtitle2">SMTP Settings</div> <div class="text-subtitle2">SMTP Settings</div>
<q-separator /> <q-separator />
<q-card-section class="row"> <q-card-section class="row">
<div class="col-2">From:</div> <div class="col-2">From email:</div>
<div class="col-4"></div> <div class="col-4"></div>
<q-input <q-input
outlined outlined
@@ -226,6 +226,16 @@
:rules="[(val) => isValidEmail(val) || 'Invalid email']" :rules="[(val) => isValidEmail(val) || 'Invalid email']"
/> />
</q-card-section> </q-card-section>
<q-card-section class="row">
<div class="col-2">From name:</div>
<div class="col-4"></div>
<q-input
outlined
dense
v-model="settings.smtp_from_name"
class="col-6 q-pa-none"
/>
</q-card-section>
<q-card-section class="row"> <q-card-section class="row">
<div class="col-2">Host:</div> <div class="col-2">Host:</div>
<div class="col-4"></div> <div class="col-4"></div>
@@ -711,13 +721,13 @@ export default {
}, },
removeEmail(email) { removeEmail(email) {
const removed = this.settings.email_alert_recipients.filter( const removed = this.settings.email_alert_recipients.filter(
(k) => k !== email (k) => k !== email,
); );
this.settings.email_alert_recipients = removed; this.settings.email_alert_recipients = removed;
}, },
removeSMSNumber(num) { removeSMSNumber(num) {
const removed = this.settings.sms_alert_recipients.filter( const removed = this.settings.sms_alert_recipients.filter(
(k) => k !== num (k) => k !== num,
); );
this.settings.sms_alert_recipients = removed; this.settings.sms_alert_recipients = removed;
}, },

View File

@@ -1,6 +1,6 @@
<template> <template>
<q-dialog ref="dialog" @hide="onHide"> <q-dialog ref="dialog" @hide="onHide">
<q-card class="q-dialog-plugin" style="min-width: 85vh"> <q-card class="q-dialog-plugin" style="min-width: 60vw">
<q-splitter v-model="splitterModel"> <q-splitter v-model="splitterModel">
<template v-slot:before> <template v-slot:before>
<q-tabs dense v-model="tab" vertical class="text-primary"> <q-tabs dense v-model="tab" vertical class="text-primary">
@@ -201,7 +201,7 @@
icon="info" icon="info"
@click=" @click="
openURL( openURL(
'https://quasar.dev/quasar-utils/date-utils#format-for-display' 'https://quasar.dev/quasar-utils/date-utils#format-for-display',
) )
" "
> >
@@ -315,7 +315,7 @@ export default {
this.$axios.get("/core/urlaction/").then((r) => { this.$axios.get("/core/urlaction/").then((r) => {
if (r.data.length === 0) { if (r.data.length === 0) {
this.notifyWarning( this.notifyWarning(
"No URL Actions configured. Go to Settings > Global Settings > URL Actions" "No URL Actions configured. Go to Settings > Global Settings > URL Actions",
); );
return; return;
} }

View File

@@ -2,9 +2,11 @@
<q-dialog <q-dialog
ref="dialogRef" ref="dialogRef"
maximized maximized
no-esc-dismiss
@hide="onDialogHide" @hide="onDialogHide"
@show="loadEditor" @show="loadEditor"
@before-hide="unloadEditor" @before-hide="unloadEditor"
@keydown.esc.stop="closeEditor"
> >
<q-card class="q-dialog-plugin"> <q-card class="q-dialog-plugin">
<q-bar> <q-bar>
@@ -20,7 +22,7 @@
@click="generateScriptOpenAI" @click="generateScriptOpenAI"
/> />
<q-space /> <q-space />
<q-btn dense flat icon="close" v-close-popup> <q-btn dense flat icon="close" @click="closeEditor">
<q-tooltip class="bg-white text-primary">Close</q-tooltip> <q-tooltip class="bg-white text-primary">Close</q-tooltip>
</q-btn> </q-btn>
</q-bar> </q-bar>
@@ -190,7 +192,7 @@
</template> </template>
</tactical-dropdown> </tactical-dropdown>
<q-space /> <q-space />
<q-btn dense flat label="Cancel" v-close-popup /> <q-btn dense flat label="Cancel" @click="closeEditor" />
<q-btn <q-btn
v-if="!readonly" v-if="!readonly"
:loading="loading" :loading="loading"
@@ -363,7 +365,23 @@ function loadEditor() {
downloadScript(script.id, { with_snippets: props.readonly }).then((r) => { downloadScript(script.id, { with_snippets: props.readonly }).then((r) => {
script.script_body = r.code; script.script_body = r.code;
editor.setValue(r.code); editor.setValue(r.code);
// need to add this in the download function otherwise the above will trigger an edit
watch(
() => script.script_body,
() => {
edited.value = true;
},
);
}); });
else {
watch(
() => script.script_body,
() => {
edited.value = true;
},
);
}
// watch for changes in language // watch for changes in language
watch(lang, () => { watch(lang, () => {
@@ -394,6 +412,21 @@ function generateScriptOpenAI() {
}); });
} }
// add are you sure prompt to unsaved script
const edited = ref(false);
function closeEditor() {
if (edited.value)
$q.dialog({
title: "You have unsaved changes. Are you sure you want to close?",
cancel: true,
ok: true,
}).onOk(async () => {
unloadEditor();
});
else unloadEditor();
}
// component life cycle hooks // component life cycle hooks
onMounted(async () => { onMounted(async () => {
agentLoading.value = true; agentLoading.value = true;

View File

@@ -176,6 +176,14 @@
<q-tooltip> Shell </q-tooltip> <q-tooltip> Shell </q-tooltip>
</q-icon> </q-icon>
<!-- is community script icon -->
<img
v-if="props.node.script_type === 'builtin'"
class="vertical-middle"
:src="trmmLogo"
style="height: 20px; max-width: 20px"
/>
<span <span
class="q-pl-xs text-weight-bold" class="q-pl-xs text-weight-bold"
:style="{ color: props.node.hidden ? 'grey' : '' }" :style="{ color: props.node.hidden ? 'grey' : '' }"
@@ -488,6 +496,12 @@
:props="props" :props="props"
:style="{ color: props.row.hidden ? 'grey' : '' }" :style="{ color: props.row.hidden ? 'grey' : '' }"
> >
<!-- is community script icon -->
<img
v-if="props.row.script_type === 'builtin'"
:src="trmmLogo"
style="height: 20px; max-width: 20px"
/>
{{ truncateText(props.row.name, 50) }} {{ truncateText(props.row.name, 50) }}
<q-tooltip <q-tooltip
v-if="props.row.name.length >= 50" v-if="props.row.name.length >= 50"
@@ -550,6 +564,8 @@ import ScriptFormModal from "@/components/scripts/ScriptFormModal.vue";
import ScriptSnippets from "@/components/scripts/ScriptSnippets.vue"; import ScriptSnippets from "@/components/scripts/ScriptSnippets.vue";
import TacticalTable from "@/components/ui/TacticalTable.vue"; import TacticalTable from "@/components/ui/TacticalTable.vue";
import trmmLogo from "@/assets/trmm_256.png";
// static data // static data
const columns = [ const columns = [
{ {
@@ -620,7 +636,7 @@ export default {
// setup vuex store // setup vuex store
const store = useStore(); const store = useStore();
const showCommunityScripts = computed( const showCommunityScripts = computed(
() => store.state.showCommunityScripts () => store.state.showCommunityScripts,
); );
// setup quasar plugins // setup quasar plugins
@@ -721,7 +737,7 @@ export default {
return showCommunityScripts.value return showCommunityScripts.value
? scripts.value.filter((i) => !i.hidden) ? scripts.value.filter((i) => !i.hidden)
: scripts.value.filter( : scripts.value.filter(
(i) => i.script_type !== "builtin" && !i.hidden (i) => i.script_type !== "builtin" && !i.hidden,
); );
} }
}); });
@@ -884,6 +900,7 @@ export default {
loading, loading,
showCommunityScripts, showCommunityScripts,
showHiddenScripts, showHiddenScripts,
trmmLogo,
// computed // computed
visibleScripts, visibleScripts,

View File

@@ -8,8 +8,25 @@
<q-tooltip class="bg-white text-primary">Close</q-tooltip> <q-tooltip class="bg-white text-primary">Close</q-tooltip>
</q-btn> </q-btn>
</q-bar> </q-bar>
<q-card-section class="scroll" style="max-height: 70vh; height: 70vh"> <q-card-section style="height: 70vh" class="scroll">
<pre v-if="ret">{{ ret }}</pre> <div>
Run Time:
<code>{{ ret.execution_time }} seconds</code>
<br />Return Code:
<code>{{ ret.retcode }}</code>
<br />
</div>
<br />
<div v-if="ret.stdout">
Standard Output
<q-separator />
<pre>{{ ret.stdout }}</pre>
</div>
<div v-if="ret.stderr">
Standard Error
<q-separator />
<pre>{{ ret.stderr }}</pre>
</div>
<q-inner-loading :showing="loading" /> <q-inner-loading :showing="loading" />
</q-card-section> </q-card-section>
</q-card> </q-card>
@@ -34,7 +51,12 @@ export default {
const { dialogRef, onDialogHide } = useDialogPluginComponent(); const { dialogRef, onDialogHide } = useDialogPluginComponent();
// main run script functionality // main run script functionality
const ret = ref(null); const ret = ref({
execution_time: "",
retcode: "",
stdout: "",
stderr: "",
});
const loading = ref(false); const loading = ref(false);
async function runTestScript() { async function runTestScript() {

View File

@@ -87,181 +87,183 @@
:done="step > 2" :done="step > 2"
:error="!isValidStep2" :error="!isValidStep2"
> >
<q-form @submit.prevent="addAction"> <div class="scroll" style="max-height: 60vh">
<div class="row q-pa-sm q-gutter-x-xs items-center"> <q-form @submit.prevent="addAction">
<div class="text-subtitle2 col-12">Action Type:</div> <div class="row q-pa-sm q-gutter-x-xs items-center">
<q-option-group <div class="text-subtitle2 col-12">Action Type:</div>
class="col-12" <q-option-group
inline class="col-12"
v-model="actionType" inline
:options="[ v-model="actionType"
{ label: 'Script', value: 'script' }, :options="[
{ label: 'Command', value: 'cmd' }, { label: 'Script', value: 'script' },
]" { label: 'Command', value: 'cmd' },
/> ]"
/>
<tactical-dropdown <tactical-dropdown
v-if="actionType === 'script'" v-if="actionType === 'script'"
class="col-3" class="col-3"
label="Select script" label="Select script"
v-model="script" v-model="script"
:options="scriptOptions" :options="scriptOptions"
filled filled
mapOptions mapOptions
filterable filterable
/> />
<q-select <q-select
v-if="actionType === 'script'" v-if="actionType === 'script'"
class="col-3" class="col-3"
dense dense
label="Script Arguments (press Enter after typing each argument)" label="Script Arguments (press Enter after typing each argument)"
filled filled
v-model="defaultArgs" v-model="defaultArgs"
use-input use-input
use-chips use-chips
multiple multiple
hide-dropdown-icon hide-dropdown-icon
input-debounce="0" input-debounce="0"
new-value-mode="add" new-value-mode="add"
/> />
<q-select <q-select
v-if="actionType === 'script'" v-if="actionType === 'script'"
class="col-3" class="col-3"
dense dense
:label="envVarsLabel" :label="envVarsLabel"
filled filled
v-model="defaultEnvVars" v-model="defaultEnvVars"
use-input use-input
use-chips use-chips
multiple multiple
hide-dropdown-icon hide-dropdown-icon
input-debounce="0" input-debounce="0"
new-value-mode="add" new-value-mode="add"
/> />
<q-input <q-input
v-if="actionType === 'script'" v-if="actionType === 'script'"
class="col-2" class="col-2"
filled filled
dense dense
v-model.number="defaultTimeout" v-model.number="defaultTimeout"
type="number" type="number"
label="Timeout (seconds)" label="Timeout (seconds)"
/> />
<q-input <q-input
v-if="actionType === 'cmd'" v-if="actionType === 'cmd'"
label="Command" label="Command"
v-model="command" v-model="command"
dense
filled
class="col-7"
/>
<q-input
v-if="actionType === 'cmd'"
class="col-2"
filled
dense
v-model.number="defaultTimeout"
type="number"
label="Timeout (seconds)"
/>
<q-option-group
v-if="actionType === 'cmd'"
class="col-2 q-pl-sm"
inline
v-model="shell"
:options="[
{ label: 'Batch', value: 'cmd' },
{ label: 'Powershell', value: 'powershell' },
]"
/>
<q-btn
class="col-1"
type="submit"
style="width: 50px"
flat
dense
icon="add"
color="primary"
/>
</div>
</q-form>
<div class="text-subtitle2 q-pa-sm">
Actions:
<q-checkbox
class="float-right"
label="Continue on Errors"
v-model="state.continue_on_error"
dense dense
filled >
class="col-7" <q-tooltip>Continue task if an action fails</q-tooltip>
/> </q-checkbox>
<q-input
v-if="actionType === 'cmd'"
class="col-2"
filled
dense
v-model.number="defaultTimeout"
type="number"
label="Timeout (seconds)"
/>
<q-option-group
v-if="actionType === 'cmd'"
class="col-2 q-pl-sm"
inline
v-model="shell"
:options="[
{ label: 'Batch', value: 'cmd' },
{ label: 'Powershell', value: 'powershell' },
]"
/>
<q-btn
class="col-1"
type="submit"
style="width: 50px"
flat
dense
icon="add"
color="primary"
/>
</div> </div>
</q-form> <div class="q-pt-sm" style="height: 150px">
<div class="text-subtitle2 q-pa-sm"> <draggable
Actions: class="q-list"
<q-checkbox handle=".handle"
class="float-right" ghost-class="ghost"
label="Continue on Errors" v-model="state.actions"
v-model="state.continue_on_error" item-key="index"
dense >
> <template v-slot:item="{ index, element }">
<q-tooltip>Continue task if an action fails</q-tooltip> <q-item>
</q-checkbox> <q-item-section avatar>
</div>
<div class="scroll q-pt-sm" style="height: 40vh; max-height: 40vh">
<draggable
class="q-list"
handle=".handle"
ghost-class="ghost"
v-model="state.actions"
item-key="index"
>
<template v-slot:item="{ index, element }">
<q-item>
<q-item-section avatar>
<q-icon
class="handle"
style="cursor: move"
name="drag_handle"
/>
</q-item-section>
<q-item-section v-if="element.type === 'script'">
<q-item-label>
<q-icon size="sm" name="description" color="primary" />
&nbsp; {{ element.name }}
</q-item-label>
<q-item-label caption>
Arguments: {{ element.script_args }}
</q-item-label>
<q-item-label caption>
Env Vars: {{ element.env_vars }}
</q-item-label>
<q-item-label caption>
Timeout: {{ element.timeout }}
</q-item-label>
</q-item-section>
<q-item-section v-else>
<q-item-label>
<q-icon size="sm" name="terminal" color="primary" />
&nbsp;
<q-icon <q-icon
size="sm" class="handle"
:name=" style="cursor: move"
element.shell === 'cmd' name="drag_handle"
? 'mdi-microsoft-windows'
: 'mdi-powershell'
"
color="primary"
/> />
{{ element.command }} </q-item-section>
</q-item-label> <q-item-section v-if="element.type === 'script'">
<q-item-label caption> <q-item-label>
Timeout: {{ element.timeout }} <q-icon size="sm" name="description" color="primary" />
</q-item-label> &nbsp; {{ element.name }}
</q-item-section> </q-item-label>
<q-item-section side> <q-item-label caption>
<q-icon Arguments: {{ element.script_args }}
class="cursor-pointer" </q-item-label>
color="negative" <q-item-label caption>
name="close" Env Vars: {{ element.env_vars }}
@click="removeAction(index)" </q-item-label>
/> <q-item-label caption>
</q-item-section> Timeout: {{ element.timeout }}
</q-item> </q-item-label>
</template> </q-item-section>
</draggable> <q-item-section v-else>
<q-item-label>
<q-icon size="sm" name="terminal" color="primary" />
&nbsp;
<q-icon
size="sm"
:name="
element.shell === 'cmd'
? 'mdi-microsoft-windows'
: 'mdi-powershell'
"
color="primary"
/>
{{ element.command }}
</q-item-label>
<q-item-label caption>
Timeout: {{ element.timeout }}
</q-item-label>
</q-item-section>
<q-item-section side>
<q-icon
class="cursor-pointer"
color="negative"
name="close"
@click="removeAction(index)"
/>
</q-item-section>
</q-item>
</template>
</draggable>
</div>
</div> </div>
</q-step> </q-step>
@@ -283,7 +285,7 @@
<q-card-section <q-card-section
v-if=" v-if="
['runonce', 'daily', 'weekly', 'monthly'].includes( ['runonce', 'daily', 'weekly', 'monthly'].includes(
state.task_type state.task_type,
) )
" "
class="row" class="row"
@@ -314,6 +316,22 @@
/> />
</q-card-section> </q-card-section>
<q-card-section
v-if="
state.task_type === 'onboarding' ||
state.task_type === 'runonce'
"
class="row"
>
<span v-if="state.task_type === 'onboarding'"
>This task will run as soon as it's created on the
agent.</span
>
<span v-else-if="state.task_type === 'runonce'"
>Start Time must be in the future for run once tasks.</span
>
</q-card-section>
<!-- daily options --> <!-- daily options -->
<q-card-section v-if="state.task_type === 'daily'" class="row"> <q-card-section v-if="state.task_type === 'daily'" class="row">
<!-- daily interval --> <!-- daily interval -->
@@ -579,7 +597,8 @@
<q-card-section <q-card-section
v-if=" v-if="
state.task_type !== 'checkfailure' && state.task_type !== 'checkfailure' &&
state.task_type !== 'manual' state.task_type !== 'manual' &&
state.task_type !== 'onboarding'
" "
class="row" class="row"
> >
@@ -617,7 +636,7 @@
(val) => (val) =>
convertPeriodToSeconds(val) >= convertPeriodToSeconds(val) >=
convertPeriodToSeconds( convertPeriodToSeconds(
state.task_repetition_interval state.task_repetition_interval,
) || ) ||
'Repetition duration must be greater than repetition interval', 'Repetition duration must be greater than repetition interval',
]" ]"
@@ -712,7 +731,7 @@
@click=" @click="
validateStep( validateStep(
step === 1 ? $refs.taskGeneralForm : undefined, step === 1 ? $refs.taskGeneralForm : undefined,
$refs.stepper $refs.stepper,
) )
" "
color="primary" color="primary"
@@ -769,6 +788,7 @@ const taskTypeOptions = [
{ label: "Monthly", value: "monthly" }, { label: "Monthly", value: "monthly" },
{ label: "Run Once", value: "runonce" }, { label: "Run Once", value: "runonce" },
{ label: "On check failure", value: "checkfailure" }, { label: "On check failure", value: "checkfailure" },
{ label: "Onboarding", value: "onboarding" },
{ label: "Manual", value: "manual" }, { label: "Manual", value: "manual" },
]; ];
@@ -933,7 +953,7 @@ export default {
task.value.actions.push({ task.value.actions.push({
type: "script", type: "script",
name: scriptOptions.value.find( name: scriptOptions.value.find(
(option) => option.value === script.value (option) => option.value === script.value,
).label, ).label,
script: script.value, script: script.value,
timeout: defaultTimeout.value, timeout: defaultTimeout.value,
@@ -1019,13 +1039,13 @@ export default {
// remove milliseconds and Z to work with native date input // remove milliseconds and Z to work with native date input
task.value.run_time_date = formatDateInputField( task.value.run_time_date = formatDateInputField(
task.value.run_time_date, task.value.run_time_date,
true true,
); );
if (task.value.expire_date) if (task.value.expire_date)
task.value.expire_date = formatDateInputField( task.value.expire_date = formatDateInputField(
task.value.expire_date, task.value.expire_date,
true true,
); );
// set task type if monthlydow is being used // set task type if monthlydow is being used
@@ -1069,7 +1089,7 @@ export default {
task.value.monthly_weeks_of_month = []; task.value.monthly_weeks_of_month = [];
task.value.task_instance_policy = 0; task.value.task_instance_policy = 0;
task.value.expire_date = null; task.value.expire_date = null;
} },
); );
// check the collector box when editing task and custom field is set // check the collector box when editing task and custom field is set

View File

@@ -25,13 +25,21 @@
:key="mapOptions ? scope.opt.value : scope.opt" :key="mapOptions ? scope.opt.value : scope.opt"
> >
<q-item-section> <q-item-section>
<q-item-label <q-item-label v-html="mapOptions ? scope.opt.label : scope.opt" />
v-html="mapOptions ? scope.opt.label : scope.opt" </q-item-section>
></q-item-label> <q-item-section
v-if="
(filtered && mapOptions && scope.opt.cat) || scope.opt.img_right
"
side
>
{{ scope.opt.cat || "" }}
<img
v-if="scope.opt.img_right"
:src="scope.opt.img_right"
style="height: 20px; max-width: 20px"
/>
</q-item-section> </q-item-section>
<q-item-section v-if="filtered && mapOptions && scope.opt.cat" side>{{
scope.opt.cat
}}</q-item-section>
</q-item> </q-item>
<q-item-label <q-item-label
v-if="scope.opt.category" v-if="scope.opt.category"
@@ -80,7 +88,7 @@ export default {
if (!props.mapOptions) if (!props.mapOptions)
filteredOptions.value = props.options.filter( filteredOptions.value = props.options.filter(
(v) => v.toLowerCase().indexOf(needle) > -1 (v) => v.toLowerCase().indexOf(needle) > -1,
); );
else else
filteredOptions.value = props.options.filter((v) => { filteredOptions.value = props.options.filter((v) => {

View File

@@ -32,7 +32,7 @@ For details, see: https://license.tacticalrmm.com/ee
:rows="reportTemplates" :rows="reportTemplates"
:columns="columns" :columns="columns"
:loading="isLoading" :loading="isLoading"
:pagination="{ rowsPerPage: 0, sortBy: 'name', descending: true }" :pagination="{ rowsPerPage: 0, sortBy: 'name', descending: false }"
:filter="search" :filter="search"
row-key="id" row-key="id"
binary-state-sort binary-state-sort

View File

@@ -25,8 +25,8 @@
If you have downgraded or cancelled your sponsorship, please delete If you have downgraded or cancelled your sponsorship, please delete
your token from the Code Signing modal and refresh to get rid of this your token from the Code Signing modal and refresh to get rid of this
banner.<br /><br /> banner.<br /><br />
For any issues or to renew your sponsorship please email For any issues or to renew your sponsorship please open a ticket at
support@amidaware.com<br /><br support.amidaware.com<br /><br
/></span> /></span>
<q-btn <q-btn
color="dark" color="dark"

View File

@@ -1,5 +1,6 @@
import { date } from "quasar"; import { date } from "quasar";
import { validateTimePeriod } from "@/utils/validation"; import { validateTimePeriod } from "@/utils/validation";
import trmmLogo from "@/assets/trmm_256.png";
// dropdown options formatting // dropdown options formatting
export function removeExtraOptionCategories(array) { export function removeExtraOptionCategories(array) {
@@ -24,7 +25,7 @@ function _formatOptions(
flat = false, flat = false,
allowDuplicates = true, allowDuplicates = true,
appendToOptionObject = {}, appendToOptionObject = {},
} },
) { ) {
if (!flat) if (!flat)
// returns array of options in object format [{label: label, value: 1}] // returns array of options in object format [{label: label, value: 1}]
@@ -64,6 +65,7 @@ export function formatScriptOptions(data) {
data.forEach((script) => { data.forEach((script) => {
if (script.category === cat) { if (script.category === cat) {
tmp.push({ tmp.push({
img_right: script.script_type === "builtin" ? trmmLogo : undefined,
label: script.name, label: script.name,
value: script.id, value: script.id,
timeout: script.default_timeout, timeout: script.default_timeout,
@@ -100,7 +102,7 @@ export function formatScriptOptions(data) {
export function formatAgentOptions( export function formatAgentOptions(
data, data,
flat = false, flat = false,
value_field = "agent_id" value_field = "agent_id",
) { ) {
if (flat) { if (flat) {
// returns just agent hostnames in array // returns just agent hostnames in array
@@ -185,7 +187,7 @@ export function formatSiteOptions(data, flat = false) {
label: "name", label: "name",
flat: flat, flat: flat,
appendToOptionObject: { cat: client.name }, appendToOptionObject: { cat: client.name },
}) }),
); );
}); });
@@ -361,7 +363,7 @@ export function convertToBitArray(number) {
bitArray.push(1); bitArray.push(1);
} else { } else {
bitArray.push( bitArray.push(
parseInt(binary.slice(i), 2) - parseInt(binary.slice(i + 1), 2) parseInt(binary.slice(i), 2) - parseInt(binary.slice(i + 1), 2),
); );
} }
} }