import { userdata, api, entityCtrl, entitiesCtrl } from "./CommonControllers";
import { TokenUser, QueueMessage, SigninMessage, Ace, NoderedUser, stripe_base, Base, NoderedUtil, WebSocketClient, Role, NoderedConfig, stripe_invoice, Message, Customer, KubeResources, KubeResourceValues, Resource, ResourceVariant, ResourceUsage } from "@openiap/openflow-api";
import { RPAWorkflow, Provider, Form, WorkflowInstance, Workflow, unattendedclient } from "./Entities";
import { WebSocketClientService } from "./WebSocketClientService";
import * as jsondiffpatch from "jsondiffpatch";
import * as ofurl from "./formsio_of_provider";
import { AddWorkitemMessage, AddWorkitemQueueMessage, DeleteWorkitemMessage, DeleteWorkitemQueueMessage, UpdateWorkitemMessage, UpdateWorkitemQueueMessage, Workitem, WorkitemQueue } from "@openiap/openflow-api";
declare let $: any;
function treatAsUTC(date): number {
const result = new Date(date);
result.setMinutes(result.getMinutes() - result.getTimezoneOffset());
return result as any;
}
function daysBetween(startDate, endDate): number {
const millisecondsPerDay = 24 * 60 * 60 * 1000;
return (treatAsUTC(endDate) - treatAsUTC(startDate)) / millisecondsPerDay;
}
declare const Formio: any;
declare const FileSaver: any;
export class jsutil {
public static async ensureJQuery() {
try {
const ele = $('body');
} catch (error) {
await this.loadScript("jquery.min.js");
}
}
public static async loadScript(url: string): Promise {
return new Promise(async (resolve) => {
var script = document.createElement("script")
script.type = "text/javascript";
script.onload = function () {
resolve();
};
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
});
}
public static async getScript(url: string): Promise {
return new Promise(async (resolve, reject) => {
$.getScript(url, () => {
resolve();
}).fail((e1, e2, e3) => {
if (e1.readyState == 0) {
reject('script failed to load');
//script failed to load
} else if (e3 != null) {
reject(e3.toString());
} else {
reject('unknonw error loading ' + url);
}
});
});
}
}
export class MenuCtrl {
public user: TokenUser;
public signedin: boolean = false;
public path: string = "";
public searchstring: string = "";
public halfmoon: any;
public static $inject = [
"$rootScope",
"$scope",
"$location",
"$routeParams",
"WebSocketClientService",
"api",
"userdata"
];
public customer: Base;
public customers: Base[];
public allowclick: boolean = true;
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
document.addEventListener(
"click",
(event) => {
try {
if (!this.allowclick) {
// event.cancelBubble = true;
event.stopImmediatePropagation();
return event.preventDefault();
}
} catch (error) {
console.error(error);
}
});
this.halfmoon = require("halfmoon");
console.debug("MenuCtrl::constructor");
$scope.$root.$on('$routeChangeStart', (...args) => { this.routeChangeStart.apply(this, args); });
this.path = this.$location.path();
this.halfmoon.onDOMContentLoaded();
const cleanup = this.$scope.$on('signin', async (event, data) => {
if (event && data) { }
this.user = data;
this.signedin = true;
this.customer = this.WebSocketClientService.customer;
this.customers = await NoderedUtil.Query({ collectionname: "users", query: { _type: "customer" }, orderby: { "name": 1 }, top: 20 });
if (!NoderedUtil.IsNullEmpty(this.user.selectedcustomerid)) {
if (this.customers.filter(x => x._id == this.user.selectedcustomerid).length == 0) {
this.customers = (await NoderedUtil.Query({ collectionname: "users", query: { _type: "customer", _id: this.user.selectedcustomerid } })).concat(this.customers);
}
}
if (!NoderedUtil.IsNullEmpty(this.user.customerid)) {
if (this.customers.filter(x => x._id == this.user.customerid).length == 0) {
this.customers = (await NoderedUtil.Query({ collectionname: "users", query: { _type: "customer", _id: this.user.customerid } })).concat(this.customers);
}
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
this.StartNewFeaturesTour(null);
});
const cleanup2 = this.$scope.$on('refreshtoken', async (event, data) => {
if (event && data) { }
this.user = data;
this.signedin = true;
if (this.user.selectedcustomerid == null) {
this.customer = null;
} else {
this.customer = this.WebSocketClientService.customer;
this.customers = await NoderedUtil.Query({ collectionname: "users", query: { _type: "customer" }, orderby: { "name": 1 }, top: 20 });
if (!NoderedUtil.IsNullEmpty(this.user.selectedcustomerid)) {
if (this.customers.filter(x => x._id == this.user.selectedcustomerid).length == 0) {
this.customers = (await NoderedUtil.Query({ collectionname: "users", query: { _type: "customer", _id: this.user.selectedcustomerid } })).concat(this.customers);
}
}
if (!NoderedUtil.IsNullEmpty(this.user.customerid)) {
if (this.customers.filter(x => x._id == this.user.customerid).length == 0) {
this.customers = (await NoderedUtil.Query({ collectionname: "users", query: { _type: "customer", _id: this.user.customerid } })).concat(this.customers);
}
}
if (this.customers && this.customers.length > 0) {
for (let cust of this.customers) {
if (cust._id == this.user.selectedcustomerid) {
this.customer = cust;
this.WebSocketClientService.customer = cust as any;
}
}
if (this.customers.length == 1) {
this.customer = this.customers[0];
this.WebSocketClientService.customer = this.customers[0] as any;
}
}
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
this.StartNewFeaturesTour(null)
// cleanup();
});
this.$scope.$on('setsearch', (event, data) => {
if (event && data) { }
this.searchstring = data;
});
this.$scope.$on('menurefresh', async (event, data) => {
if (event && data) { }
this.customer = this.WebSocketClientService.customer;
this.customers = await NoderedUtil.Query({ collectionname: "users", query: { _type: "customer" }, orderby: { "name": 1 }, top: 20 });
if (!NoderedUtil.IsNullEmpty(this.user.selectedcustomerid)) {
if (this.customers.filter(x => x._id == this.user.selectedcustomerid).length == 0) {
this.customers = (await NoderedUtil.Query({ collectionname: "users", query: { _type: "customer", _id: this.user.selectedcustomerid } })).concat(this.customers);
}
}
if (!NoderedUtil.IsNullEmpty(this.user.customerid)) {
if (this.customers.filter(x => x._id == this.user.customerid).length == 0) {
this.customers = (await NoderedUtil.Query({ collectionname: "users", query: { _type: "customer", _id: this.user.customerid } })).concat(this.customers);
}
}
if (this.customers.length > 0) {
for (let cust of this.customers)
if (cust._id == this.user.selectedcustomerid) this.customer = cust;
if (this.customers.length == 1) {
this.customer = this.customers[0];
this.WebSocketClientService.customer = this.customers[0] as any;
}
}
if (this.customer != null) this.WebSocketClientService.customer = this.customer as any;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
});
}
routeChangeStart(event: any, next: any, current: any) {
this.path = this.$location.path();
}
hasrole(role: string) {
if (NoderedUtil.IsNullUndefinded(WebSocketClient.instance)) return false;
if (NoderedUtil.IsNullUndefinded(WebSocketClient.instance.user)) return false;
if (role == "customer admins" && !NoderedUtil.IsNullUndefinded(WebSocketClient.instance.user.customerid)) {
return true;
}
const hits = WebSocketClient.instance.user.roles.filter(member => member.name == role);
return (hits.length == 1)
}
hascordova() {
return this.WebSocketClientService.usingCordova;
}
stopimpersonation() {
// this.WebSocketClientService.loadToken();
this.WebSocketClientService.impersonate("-1");
}
PathIs(path: string) {
if (path == null && path == undefined) return false;
if (this.path == null && this.path == undefined) return false;
if (Array.isArray(path)) {
for (var i = 0; i < path.length; i++) {
if (path[i].endsWith("/") && this.path.toLowerCase().startsWith(path[i].toLowerCase()))
return true;
else if (this.path.toLowerCase() == path[i].toLowerCase()) {
return true;
}
}
return false;
} else {
if (path.endsWith("/") && this.path.toLowerCase().startsWith(path.toLowerCase()))
return true;
else if (this.path.toLowerCase() == path.toLowerCase()) {
return true;
}
return false;
}
}
toggleDarkMode() {
this.halfmoon.toggleDarkMode();
}
toggleSidebar() {
this.halfmoon.toggleSidebar();
}
Search() {
this.$rootScope.$broadcast("search", this.searchstring);
}
async EditCustomer(customer) {
try {
if (customer == null) return;
WebSocketClient.instance.user.selectedcustomerid = customer._id;
this.WebSocketClientService.customer = customer as any;
await NoderedUtil.SelectCustomer({ customerid: WebSocketClient.instance.user.selectedcustomerid });
this.$location.path("/Customer/" + customer._id);
if (!this.$scope.$$phase) { this.$scope.$apply(); }
} catch (error) {
console.error(error);
}
}
async SelectCustomer(customer) {
// if (customer != null) {
// console.debug("SelectCustomer " + customer.name, customer)
// } else {
// console.debug("SelectCustomer null", customer)
// }
try {
this.customer = customer;
if (customer != null) {
WebSocketClient.instance.user.selectedcustomerid = customer._id;
await NoderedUtil.SelectCustomer({ customerid: WebSocketClient.instance.user.selectedcustomerid });
this.WebSocketClientService.customer = customer as any;
if (this.PathIs("/Customer")) {
this.$location.path("/Customer/" + customer._id);
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
} else {
WebSocketClient.instance.user.selectedcustomerid = null;
await NoderedUtil.SelectCustomer({ customerid: WebSocketClient.instance.user.selectedcustomerid });
this.WebSocketClientService.customer = null;
}
// this.$rootScope.$broadcast("menurefresh");
this.$rootScope.$broadcast("search", this.searchstring);
} catch (error) {
console.error(error);
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
setCookie(cname, cvalue, exdays) {
const d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
const expires = "expires=" + d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
getCookie(cname) {
const name = cname + "=";
const decodedCookie = decodeURIComponent(document.cookie);
const ca = decodedCookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
public NewFeaturesTour: any;
public Shepherd = require("shepherd.js");
StartNewFeaturesTour(startfrom) {
try {
if (this.NewFeaturesTour != null) return;
if (!this.WebSocketClientService.enable_web_tours) return;
var me = this;
this.NewFeaturesTour = new this.Shepherd.Tour({
useModalOverlay: true,
tourName: 'featuretour',
exitOnEsc: true,
defaultStepOptions: {
cancelIcon: {
enabled: true
},
scrollTo: { behavior: 'smooth', block: 'center' }
},
});
let step: number = this.getCookie("newfeatures") as any;
if (NoderedUtil.IsNullEmpty(step)) step = 0;
if (!NoderedUtil.IsNullEmpty(startfrom)) {
step = startfrom;
}
step = parseInt(step as any);
this.NewFeaturesTour.on("show", (e) => {
const currentstep = parseInt(e.step.id);
if (currentstep < 0) {
step = step + 1;
this.setCookie("newfeatures", step, 365);
} else {
step = currentstep;
this.setCookie("newfeatures", currentstep, 365);
}
});
this.NewFeaturesTour.on("complete", (e) => {
this.NewFeaturesTour = null;
});
this.NewFeaturesTour.on("cancel", (e) => {
this.NewFeaturesTour = null;
});
const backbutton = {
action() {
return this.back();
},
classes: 'shepherd-button-secondary',
text: 'Back'
};
const nextbutton = {
action() {
return this.next();
},
text: 'Next'
};
const completebutton = {
action() {
return this.complete();
},
text: 'Complete'
};
this.NewFeaturesTour.addStep({
title: 'New User Interface in OpenFlow',
text: `The new UI in Openflow, allows for using darkmode, you can toogle darkmode on this button or you can use the keyboard shortcut Shift+D.`,
attachTo: {
element: '#menudarkmode'
},
buttons: [nextbutton],
id: '0'
});
if (this.WebSocketClientService.multi_tenant && this.customer == null && this.customers.length == 0) this.NewFeaturesTour.addStep({
title: 'Enable multi tenancy',
text: `Per default OpenFlow is running in a single user mode, where users cannot share information. Click here to create a new Customer, and enable access to multiple user, roles, control access to data and workflows and to buy additional services`,
attachTo: {
element: '#menumultitenant'
},
buttons: [backbutton, nextbutton],
id: '1'
});
if (this.hasrole("customer admins") || this.hasrole("resellers") || this.hasrole("admins")) {
if (this.WebSocketClientService.multi_tenant && this.customer != null && this.customers.length == 1) this.NewFeaturesTour.addStep({
title: 'Manage your company',
text: `Click here to manage you company details, this is also where you can check your next Invoice and how many services you have added`,
attachTo: {
element: '#menumanagecustomer'
},
buttons: [backbutton, nextbutton],
id: '50'
});
if (this.WebSocketClientService.multi_tenant && this.customer != null && this.customers.length > 0) this.NewFeaturesTour.addStep({
title: 'Manage your users ',
text: `Click here to manage your users. You can create, edit and delete new users, and you can purchase and assign new services to users here`,
attachTo: {
element: '#menuadminusers'
},
when: {
show() {
me.OpenAdminsMenu();
},
hide() {
me.CloseAllMenus();
}
},
buttons: [backbutton, nextbutton],
id: '51'
});
if (this.WebSocketClientService.multi_tenant && this.customer != null && this.customers.length > 1) this.NewFeaturesTour.addStep({
title: 'Select a company',
text: `Click here to select a company to work with. This will filter the users and roles list, and control what customer to add new items too`,
attachTo: {
element: '#menuresellermenu'
},
buttons: [backbutton, nextbutton],
id: '52'
});
}
if (this.NewFeaturesTour.steps.length > 0) {
const laststepid = parseInt(this.NewFeaturesTour.steps[this.NewFeaturesTour.steps.length - 1].id);
if (step <= laststepid) {
this.NewFeaturesTour.addStep({
title: 'Thank you for using OpenIAP',
text: `We hope you will enjoy the power of the leading open Source Integrated Automation Platform, click here to see different help tours.`,
attachTo: {
element: '#menutour'
},
buttons: [backbutton, completebutton],
id: '-1'
});
for (let i = 0; i < this.NewFeaturesTour.steps.length; i++) {
const _stepid = parseInt(this.NewFeaturesTour.steps[i].id);
if (_stepid < step) continue;
this.NewFeaturesTour.show(_stepid.toString())
return;
}
}
}
this.NewFeaturesTour = null;
} catch (error) {
console.error(error);
}
}
ListTours() {
var me = this;
try {
const tour = new this.Shepherd.Tour({
useModalOverlay: true,
tourName: 'listoftour',
exitOnEsc: true,
defaultStepOptions: {
cancelIcon: {
enabled: true
},
scrollTo: { behavior: 'smooth', block: 'center' }
},
});
let bottons: any[] = [];
bottons.push({
action() {
me.StartNewFeaturesTour(0);
return this.complete();
},
text: 'New Features'
});
if (this.WebSocketClientService.multi_tenant && this.customers.length > 0 && (this.hasrole("admins") ||
this.hasrole("resellers") || this.hasrole("customer admins"))) {
bottons.push({
action() {
me.StartManageCompanyTour();
return this.complete();
},
text: 'Manage Company'
});
}
bottons.push({
action() {
me.StartManageDataTour();
return this.complete();
},
text: 'Manage Data'
});
bottons.push({
action() {
me.StartManageRobotsAndNoderedTour();
return this.complete();
},
text: 'Manage Robots and Nodered'
});
if (this.WebSocketClientService.stripe_api_key == "pk_live_0XOJdv1fPLPnOnRn40CSdBsh009Ge1B2yI") {
tour.addStep({
title: 'What do you want to explorer ?',
text: `Select from one of the below guided tours to learn more. Use your keyboard arror keys to move back and forward and Esc to exit the tour.
For billing questions and sales support feel free to reach out on support@openiap.io, for all other questions use the forum or rocket chat`,
buttons: bottons,
id: 'tourlist'
});
} else {
tour.addStep({
title: 'What do you want to explorer ?',
text: `Select from one of the below guided tours to learn more. Use your keyboard arror keys to move back and forward and Esc to exit the tour.`,
buttons: bottons,
id: 'tourlist'
});
}
tour.start();
} catch (error) {
console.error(error);
}
}
OpenAdminsMenu() {
var me = this;
this.allowclick = false;
var target = document.getElementById("navbar-dropdown-toggle-btn-1");
this.halfmoon.deactivateAllDropdownToggles();
target.classList.add("active");
target.closest(".dropdown").classList.add("show");
setTimeout(() => {
me.allowclick = true;
}, 250);
}
CloseAllMenus() {
this.halfmoon.deactivateAllDropdownToggles();
}
StartManageCompanyTour() {
try {
var me = this;
const tour = new this.Shepherd.Tour({
useModalOverlay: false,
tourName: 'managecompanytour',
exitOnEsc: true,
defaultStepOptions: {
cancelIcon: {
enabled: true
},
scrollTo: { behavior: 'smooth', block: 'center' }
},
});
let step: number = 0;
tour.on("show", (e) => {
const currentstep = parseInt(e.step.id);
if (currentstep == 0 || currentstep == 2 || currentstep == 4) {
me.OpenAdminsMenu();
}
if (currentstep < 0) {
step = step + 1;
} else {
step = currentstep;
}
});
const backbutton = {
action() {
return this.back();
},
classes: 'shepherd-button-secondary',
text: 'Back'
};
const nextbutton = {
action() {
return this.next();
},
text: 'Next'
};
const completebutton = {
action() {
return this.complete();
},
text: 'Complete'
};
tour.addStep({
title: 'User management',
text: `You manage users by clicking Users in the admin menu`,
beforeShowPromise: function () {
return new Promise((resolve) => setTimeout(resolve, 250));
},
when: {
show() {
me.$location.path("/Users");
if (!me.$scope.$$phase) { me.$scope.$apply(); }
}
},
attachTo: {
element: '#menuadminusers',
on: 'bottom'
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [0, 15] } }]
},
buttons: [nextbutton],
id: '0'
});
tour.addStep({
title: 'User management',
text: `You assign new services to your users by clicking the icon. This require a valid vat number to have been added on the company page`,
beforeShowPromise: function () {
return new Promise((resolve) => setTimeout(resolve, 250));
},
when: {
show() {
}
},
buttons: [backbutton, nextbutton],
id: '1'
});
tour.addStep({
title: 'Roles management',
text: `You manage roles by clicking Roles in the admin menu. It is more efficent to use roles as a way to control access to resources and data. Many features will auto generate roles you can use to control access to these, like NodeRED workflows`,
beforeShowPromise: function () {
return new Promise((resolve) => setTimeout(resolve, 250));
},
when: {
show() {
}
},
attachTo: {
element: '#menuadminroles',
on: 'bottom'
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [0, 15] } }]
},
buttons: [backbutton, nextbutton],
id: '2'
});
tour.addStep({
title: 'Roles management',
text: `Roles is also how we load balance workload across multiple robots. Simply check RPA on the edit role page to allow assigning workflows to that role. Any robot that is only and not busy, will then pick up that workitem `,
attachTo: {
},
buttons: [backbutton, nextbutton],
id: '3'
});
tour.addStep({
title: 'Audit logs',
text: `This is the log of security events related to you and users you manage, this combined with the built in version control and on-the-fly encryption, makes it easy to comply with various regulatory demands like GDRP, FedRAMP, HIPAA etc. By default only your own entries are shown`,
beforeShowPromise: function () {
return new Promise((resolve) => setTimeout(resolve, 250));
},
when: {
show() {
me.$location.path("/Auditlogs");
if (!me.$scope.$$phase) { me.$scope.$apply(); }
}
},
attachTo: {
element: '#menuadminauditlogs',
on: 'bottom'
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [0, 15] } }]
},
buttons: [backbutton, completebutton],
id: '4'
});
// tour.addStep({
// title: 'Manage credentials',
// text: `For a more secure envoriment, it is a good practice to use encrypted credentials added here and not save those as plaintext in a robot workflow. Remember to give all robots access to the credentials.`,
// attachTo: {
// element: '#menuadmincredentials'
// },
// buttons: defaultbuttons,
// id: '3'
// });
// tour.addStep({
// title: 'Workflow forms',
// text: `Nodered Workflows allows you to design forms with an endless combination of different form elements to interact with users as part of a process`,
// attachTo: {
// element: '#menuadminforms'
// },
// buttons: defaultbuttons,
// id: '5'
// });
// tour.addStep({
// title: 'Files',
// text: `Files associated with robot workflows, forms and files you use as part of a Nodered workflow gets stored here. You can upload, download, delete and manage permissions on all files here. Remember to clean up, as a free user you only get 25 megabyte of storage`,
// attachTo: {
// element: '#menuadminfiles'
// },
// buttons: defaultbuttons,
// id: '6'
// });
// if (this.WebSocketClientService.multi_tenant && this.customer != null && this.customers.length > 1) tour.addStep({
// title: 'Enable multi tenancy',
// text: `Per default OpenFlow is running in a single user mode, where users cannot share information. Click here to create a new Customer, and enable access to multiple user, roles, control access to data and workflows and to buy additional services`,
// attachTo: {
// element: '#menumultitenant'
// },
// buttons: defaultbuttons,
// id: '7'
// });
// if (this.WebSocketClientService.multi_tenant && this.customer != null && this.customers.length < 2) tour.addStep({
// title: 'Manage you users ',
// text: `Click here to manage your users. You can create, edit and delete new users, and you can purchase and assign new services to users here`,
// attachTo: {
// element: '#menuadminusers'
// },
// buttons: defaultbuttons,
// id: '8'
// });
// if (this.WebSocketClientService.multi_tenant && this.customer != null && this.customers.length < 2) tour.addStep({
// title: 'Manage you roles',
// text: `Click here to manage your roles. It is much more efficent to use a role when assigning permissons`,
// attachTo: {
// element: '#menuadminroles'
// },
// buttons: defaultbuttons,
// id: '9'
// });
// if (this.WebSocketClientService.multi_tenant && this.customer != null && this.customers.length < 2) tour.addStep({
// title: 'Manage you company',
// text: `Click here to manage you company details, this is also where you can check your next Invoice and how many services you have added`,
// attachTo: {
// element: '#menumanagecustomer'
// },
// buttons: defaultbuttons,
// id: '10'
// });
// tour.addStep({
// title: 'Rerun tour',
// text: `We hope you will enjoy the power on the leading opensource automation platform, click here to restart all tour steps.`,
// attachTo: {
// element: '#menutour'
// },
// buttons: [
// {
// action() {
// return this.back();
// },
// classes: 'shepherd-button-secondary',
// text: 'Back'
// },
// {
// action() {
// return this.cancel();
// },
// text: 'Exit'
// }
// ],
// id: '-1'
// });
for (let i = 0; i < tour.steps.length; i++) {
const _stepid = parseInt(tour.steps[i].id);
if (_stepid < step) continue;
tour.show(_stepid.toString())
break;
}
} catch (error) {
console.error(error);
}
}
StartManageDataTour() {
try {
var me = this;
const tour = new this.Shepherd.Tour({
useModalOverlay: false,
tourName: 'managedatatour',
exitOnEsc: true,
defaultStepOptions: {
cancelIcon: {
enabled: true
},
scrollTo: { behavior: 'smooth', block: 'center' }
},
});
let step: number = 0;
tour.on("show", (e) => {
const currentstep = parseInt(e.step.id);
// if (currentstep == 0 || currentstep == 2 || currentstep == 4) {
// me.OpenAdminsMenu();
// }
if (currentstep < 0) {
step = step + 1;
} else {
step = currentstep;
}
});
const backbutton = {
action() {
return this.back();
},
classes: 'shepherd-button-secondary',
text: 'Back'
};
const nextbutton = {
action() {
return this.next();
},
text: 'Next'
};
tour.addStep({
title: 'Managing Data',
text: `OpenFlow is primarily a database with an security layer, and an api to orchestrate multiple NodeRED and OpenRPA robots. Data is there for a central element of understanding and getting the ful benefit of the platform`,
buttons: [nextbutton],
id: '0'
});
tour.addStep({
title: 'Managing Data',
text: `Most pages is a "view" on the data, but you can access ALL data inside the database, by clicking entities in the menu`,
attachTo: {
element: '#menuentities',
on: 'bottom'
},
when: {
hide() {
delete me.userdata.data.EntitiesCtrl;
me.$location.path("/Entities/entities");
if (!me.$scope.$$phase) { me.$scope.$apply(); }
}
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [50, 10] } }]
},
buttons: [backbutton, nextbutton],
id: '1'
});
tour.addStep({
title: 'Managing Data',
text: `The database contains a list of collections, similar to tables in an traditional relational database. We can store different kinds of data in the same collection, and there for group, and search our data in a more meaningful way`,
beforeShowPromise: function () {
return new Promise((resolve) => setTimeout(resolve, 250));
},
attachTo: {
element: '#menucollections',
on: 'bottom'
},
when: {
hide() {
delete me.userdata.data.EntitiesCtrl;
me.$location.path("/Entities/users");
if (!me.$scope.$$phase) { me.$scope.$apply(); }
}
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [50, 20] } }]
},
buttons: [backbutton, nextbutton],
id: '2'
});
tour.addStep({
title: 'Managing Data',
text: `I selected the "users" collection, and as you can see it contains both user and role objects.
Clicking will open the history for that object, allowing you to see different versions of the object
to edit and set permissions, to delete the entity`,
buttons: [backbutton, nextbutton],
id: '3'
});
tour.addStep({
title: 'Managing Data',
text: `Up here we have access to Undelete to restore deleted object, clone tool, that allows us to group all data by different keys and to add a new entity to this collection`,
attachTo: {
element: '#entitiestools',
on: 'bottom'
},
when: {
hide() {
me.$location.path("/Entity/entities");
delete me.userdata.data.EntitiesCtrl;
if (!me.$scope.$$phase) { me.$scope.$apply(); }
}
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [0, 20] } }]
},
buttons: [backbutton, nextbutton],
id: '4'
});
tour.addStep({
title: 'Managing Data',
text: `When adding data, either from the webpage, a robot, NodeRED, PowerShell or the API, you need to comply with the entity restrictions setup for this OpenFlow instance, you will get an Access Denied if you do not have the right create permissions.`,
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [50, 20] } }]
},
buttons: [backbutton, nextbutton],
id: '5'
});
tour.addStep({
title: 'Managing Data',
text: `Every entity in the database has an Access Control List that defines who can read, edit, delete or invoke this entity. Invoke will have different meanings for different types of entities`,
attachTo: {
element: '#entitypermissions',
on: 'bottom'
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [50, 40] } }]
},
buttons: [backbutton, nextbutton],
id: '6'
});
tour.addStep({
title: 'Managing Data',
text: `Hear you can search for, and then add any user or role. You define what right you want to assign them. As a rule of thumb use roles, and not users unless absolutely necessary. Even with a low number of users it is often much more effecient to use roles to control permissions, than having to go back and update the permissions on all objects later to add/remove a user.`,
attachTo: {
element: '#addusergroup',
on: 'bottom'
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [50, 20] } }]
},
buttons: [backbutton, nextbutton],
id: '7'
});
tour.addStep({
title: 'Managing Data',
text: `By default you get an structured view that allows adding or removing properties, but you are free to click the "show json" button to edit the object directly`,
attachTo: {
element: '#enableshowjson',
on: 'bottom'
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [50, 20] } }]
},
buttons: [backbutton, nextbutton],
id: '8'
});
for (let i = 0; i < tour.steps.length; i++) {
const _stepid = parseInt(tour.steps[i].id);
if (_stepid < step) continue;
tour.show(_stepid.toString())
break;
}
} catch (error) {
console.error(error);
}
}
StartManageRobotsAndNoderedTour() {
try {
var me = this;
const tour = new this.Shepherd.Tour({
useModalOverlay: false,
tourName: 'managerobotnoderedtour',
exitOnEsc: true,
defaultStepOptions: {
cancelIcon: {
enabled: true
},
scrollTo: { behavior: 'smooth', block: 'center' }
},
});
let step: number = 0;
tour.on("show", (e) => {
const currentstep = parseInt(e.step.id);
if (currentstep == 0 || currentstep == 1 || currentstep == 3 || currentstep == 6) {
me.OpenAdminsMenu();
}
if (currentstep < 0) {
step = step + 1;
} else {
step = currentstep;
}
});
const backbutton = {
action() {
return this.back();
},
classes: 'shepherd-button-secondary',
text: 'Back'
};
const nextbutton = {
action() {
return this.next();
},
text: 'Next'
};
const completebutton = {
action() {
return this.complete();
},
text: 'Complete'
};
tour.addStep({
title: 'Managing Robots',
text: `Robots run as a User. Normaly you will run a robot as your own user, but once you start to scale it makes sense to create dedicated bot accounts.
Keep in mind you cannot run multiply robots with the same user account, so create meaning full names when adding new users`,
beforeShowPromise: function () {
return new Promise((resolve) => setTimeout(resolve, 250));
},
classes: 'shepherd shepherd-open shepherd-theme-arrows shepherd-transparent-text',
when: {
show() {
me.$location.path("/Users");
if (!me.$scope.$$phase) { me.$scope.$apply(); }
}
},
attachTo: {
element: '#menuadminusers',
on: 'bottom'
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [0, 15] } }]
},
buttons: [nextbutton],
id: '0'
});
tour.addStep({
title: 'Managing Robots',
text: `When scaling to many robots, you will need to spread out the workload to many robots. You can create a role, and add all the robot user accounts to that role.`,
beforeShowPromise: function () {
return new Promise((resolve) => setTimeout(resolve, 250));
},
when: {
show() {
me.$location.path("/Roles");
if (!me.$scope.$$phase) { me.$scope.$apply(); }
},
hide() {
me.CloseAllMenus();
me.$location.path("/Role");
if (!me.$scope.$$phase) { me.$scope.$apply(); }
}
},
attachTo: {
element: '#menuadminroles',
on: 'bottom'
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [0, 15] } }]
},
buttons: [backbutton, nextbutton],
id: '1'
});
tour.addStep({
title: 'Managing Robots',
text: `On the new role make sure to check the RPA role. This tell the robots that is member of this role, to wait for work sent to this role. When you send work to a role, any robot that is online and is not busy with other workflows will take the job. If no robots pick up the message it will be queue up and retry automatically`,
beforeShowPromise: function () {
return new Promise((resolve) => setTimeout(resolve, 250));
},
when: {
show() {
me.$location.path("/Role");
if (!me.$scope.$$phase) { me.$scope.$apply(); }
}
},
attachTo: {
element: '#rparole',
on: 'right'
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [0, 120] } }]
},
buttons: [backbutton, nextbutton],
id: '2'
});
tour.addStep({
title: 'Credentials',
text: `For a more secure envoriment, it is a good practice to use encrypted credentials added here and not save those as plaintext in a robot workflow. Remember to give all robots access to the credentials.`,
beforeShowPromise: function () {
return new Promise((resolve) => setTimeout(resolve, 250));
},
when: {
show() {
me.$location.path("/Credentials");
if (!me.$scope.$$phase) { me.$scope.$apply(); }
},
hide() {
me.CloseAllMenus();
}
},
attachTo: {
element: '#menuadmincredentials',
on: 'bottom'
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [0, 15] } }]
},
buttons: [backbutton, nextbutton],
id: '3'
});
tour.addStep({
title: 'Clients',
text: `On the clients page you can see all online users, and filter on the type of client used.`,
beforeShowPromise: function () {
return new Promise((resolve) => setTimeout(resolve, 250));
},
when: {
show() {
me.$location.path("/Clients");
if (!me.$scope.$$phase) { me.$scope.$apply(); }
},
hide() {
me.CloseAllMenus();
}
},
attachTo: {
element: '#menuclients',
on: 'bottom'
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [0, 15] } }]
},
buttons: [backbutton, nextbutton],
id: '3'
});
tour.addStep({
title: 'RPA Workflows',
text: `On the rpa workflows page, you can see a list of all the RPA workflows you have access too, if you click invoke , you can even start them from this webpage, given the robot is online`,
beforeShowPromise: function () {
return new Promise((resolve) => setTimeout(resolve, 250));
},
when: {
show() {
me.$location.path("/RPAWorkflows");
if (!me.$scope.$$phase) { me.$scope.$apply(); }
},
hide() {
me.CloseAllMenus();
}
},
attachTo: {
element: '#menurpaworkflows',
on: 'bottom'
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [0, 15] } }]
},
buttons: [backbutton, nextbutton],
id: '4'
});
tour.addStep({
title: 'Nodered',
text: `On the NodeRED page, you can start your personal NodeRED instance. The free version will stop after 24 hours and have limited amount of ram. This is where you can schedule robots, and install modules that allows easy integration to more than 3500 IT systems. This is also where you create workflow, that can involve humans using different channels like email, chat, voice or the forms you design in OpenFlow`,
beforeShowPromise: function () {
return new Promise((resolve) => setTimeout(resolve, 250));
},
when: {
show() {
me.$location.path("/Nodered");
if (!me.$scope.$$phase) { me.$scope.$apply(); }
},
hide() {
me.CloseAllMenus();
}
},
attachTo: {
element: '#menunodered',
on: 'bottom'
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [0, 15] } }]
},
buttons: [backbutton, nextbutton],
id: '5'
});
tour.addStep({
title: 'Forms',
text: `This is where you can create forms, used by workflows in NodeRED. You can combine this with other channels as well, and then automated based on the input you get and/or present the results`,
beforeShowPromise: function () {
return new Promise((resolve) => setTimeout(resolve, 250));
},
when: {
show() {
me.$location.path("/Forms");
if (!me.$scope.$$phase) { me.$scope.$apply(); }
},
hide() {
me.CloseAllMenus();
}
},
attachTo: {
element: '#menuadminforms',
on: 'bottom'
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [0, 15] } }]
},
buttons: [backbutton, nextbutton],
id: '6'
});
tour.addStep({
title: 'Workflows',
text: `Once you created a Workflow in NodeRED, this is where you and your users can start the workflow. Each workflow will have a corrosponding role created, that you need to add the users too, in order to see and invoke the workflow. You can "chain" many workflows, so triggering one workflow will create one or more sub workflows and wait for the results. This is handy when working with complex swim lanes or process that span multiple departments.`,
beforeShowPromise: function () {
return new Promise((resolve) => setTimeout(resolve, 250));
},
when: {
show() {
me.$location.path("/Workflows");
if (!me.$scope.$$phase) { me.$scope.$apply(); }
},
hide() {
me.CloseAllMenus();
}
},
attachTo: {
element: '#menuworkflows',
on: 'bottom'
},
popperOptions: {
modifiers: [{ name: 'offset', options: { offset: [0, 15] } }]
},
buttons: [backbutton, nextbutton],
id: '7'
});
// tour.addStep({
// title: 'Files',
// text: `Files associated with robot workflows, forms and files you use as part of a Nodered workflow gets stored here. You can upload, download, delete and manage permissions on all files here. Remember to clean up, as a free user you only get 25 megabyte of storage`,
// attachTo: {
// element: '#menuadminfiles'
// },
// buttons: defaultbuttons,
// id: '6'
// });
for (let i = 0; i < tour.steps.length; i++) {
const _stepid = parseInt(tour.steps[i].id);
if (_stepid < step) continue;
tour.show(_stepid.toString())
break;
}
} catch (error) {
console.error(error);
}
}
}
export class RPAWorkflowCtrl extends entityCtrl {
public arguments: any;
public users: TokenUser[];
public user: TokenUser;
public messages: string;
public queuename: string = "";
public timeout: string = (60 * 1000).toString(); // 1 min;
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
console.debug("RPAWorkflowCtrl");
this.collection = "openrpa";
this.messages = "";
WebSocketClientService.onSignedin(async (_user: TokenUser) => {
await this.RegisterQueue();
this.$scope.$on('signin', (event, data) => {
this.RegisterQueue();
});
if (this.id !== null && this.id !== undefined) {
await this.loadData();
await this.loadUsers();
} else {
console.error("Missing id");
}
});
}
async RegisterQueue() {
try {
this.queuename = await NoderedUtil.RegisterQueue({
callback: (data: QueueMessage, ack: any) => {
ack();
console.debug(data);
if (data.data.command == undefined && data.data.data != null) data.data = data.data.data;
this.messages += data.data.command + "\n";
if (data.data.command == "invokecompleted") {
this.arguments = data.data.data;
}
if (data.data.command == "invokefailed") {
if (data.data && data.data.data && data.data.data.Message) {
this.errormessage = data.data.data.Message;
} else {
this.errormessage = JSON.stringify(data.data);
}
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}, closedcallback: (msg) => {
this.queuename = "";
console.debug("rabbitmq disconnected, start reconnect")
setTimeout(this.RegisterQueue.bind(this), (Math.floor(Math.random() * 6) + 1) * 500);
}
});
console.debug("queuename: " + this.queuename);
} catch (error) {
this.queuename = "";
console.debug("register queue failed, start reconnect. " + error.message ? error.message : error)
setTimeout(this.RegisterQueue.bind(this), (Math.floor(Math.random() * 6) + 1) * 500);
}
}
async loadUsers(): Promise {
this.users = await NoderedUtil.Query({ collectionname: "users", query: { $or: [{ _type: "user" }, { _type: "role", rparole: true }] } });
this.users.forEach(user => {
if (user._id == this.model._createdbyid || user._id == this.model._modifiedbyid) {
this.user = user;
}
});
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
async submit(): Promise {
try {
this.errormessage = "";
const rpacommand = {
command: "invoke",
workflowid: this.model._id,
data: this.arguments
}
if (this.arguments === null || this.arguments === undefined) { this.arguments = {}; }
const result: any = await NoderedUtil.Queue({ queuename: this.user._id, replyto: this.queuename, data: rpacommand, expiration: parseInt(this.timeout), striptoken: true });
try {
// result = JSON.parse(result);
} catch (error) {
}
} catch (error) {
this.errormessage = error.message ? error.message : error;
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
}
export class RPAWorkflowsCtrl extends entitiesCtrl {
public message: string = "";
public charts: chartset[] = [];
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
console.debug("RPAWorkflowsCtrl");
this.collection = "openrpa";
this.basequery = { _type: "workflow" };
this.baseprojection = { _type: 1, type: 1, name: 1, _created: 1, _createdby: 1, _modified: 1, projectandname: 1 };
this.postloadData = this.processdata;
if (this.userdata.data != null && this.userdata.data.basequeryas != null) {
this.basequeryas = this.userdata.data.basequeryas;
} else if (this.userdata.data.RPAWorkflowsCtrl) {
if (this.userdata.data.RPAWorkflowsCtrl.basequeryas) this.basequeryas = this.userdata.data.RPAWorkflowsCtrl.basequeryas;
if (this.userdata.data.RPAWorkflowsCtrl.basequery) {
this.basequery = this.userdata.data.RPAWorkflowsCtrl.basequery;
this.collection = this.userdata.data.RPAWorkflowsCtrl.collection;
this.baseprojection = this.userdata.data.RPAWorkflowsCtrl.baseprojection;
this.orderby = this.userdata.data.RPAWorkflowsCtrl.orderby;
this.searchstring = this.userdata.data.RPAWorkflowsCtrl.searchstring;
this.basequeryas = this.userdata.data.RPAWorkflowsCtrl.basequeryas;
}
}
WebSocketClientService.onSignedin((user: TokenUser) => {
this.loadData();
});
}
processdata() {
this.loading = true;
this.loading = false;
if (!this.userdata.data.RPAWorkflowsCtrl) this.userdata.data.RPAWorkflowsCtrl = {};
this.userdata.data.RPAWorkflowsCtrl.basequery = this.basequery;
this.userdata.data.RPAWorkflowsCtrl.collection = this.collection;
this.userdata.data.RPAWorkflowsCtrl.baseprojection = this.baseprojection;
this.userdata.data.RPAWorkflowsCtrl.orderby = this.orderby;
this.userdata.data.RPAWorkflowsCtrl.searchstring = this.searchstring;
this.userdata.data.RPAWorkflowsCtrl.basequeryas = this.basequeryas;
const chart: chartset = null;
this.loading = false;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
this.dographs();
}
async dographs() {
const datatimeframe = new Date(new Date().toISOString());
datatimeframe.setDate(datatimeframe.getDate() - 5);
const aggregates: any = [
{ $match: { _created: { "$gte": datatimeframe } } },
{
$group:
{
_id:
{
WorkflowId: "$WorkflowId",
name: "$name",
day: { $dayOfMonth: "$_created" }
},
count: { $sum: 1 }
}
},
{ $sort: { "_id.day": 1 } }
// ,{ "$limit": 20 }
];
const workflowruns = await NoderedUtil.Aggregate({ collectionname: "openrpa_instances", aggregates });
for (let i = 0; i < this.models.length; i++) {
const workflow = this.models[i] as any;
const chart: chartset = new chartset();
chart.data = [];
for (let x = 0; x < workflowruns.length; x++) {
if (workflowruns[x]._id.WorkflowId == workflow._id) {
chart.data.push(workflowruns[x].count);
chart.labels.push(workflowruns[x]._id.day);
}
}
if (chart.data.length > 0) {
workflow.chart = chart;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
}
}
download(data, filename, type) {
const file = new Blob([data], { type: type });
if ((window.navigator as any).msSaveOrOpenBlob) // IE10+
(window.navigator as any).msSaveOrOpenBlob(file, filename);
else { // Others
const a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function () {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
}
async Download(model: any) {
const workflows = await NoderedUtil.Query({ collectionname: "openrpa", query: { _type: "workflow", _id: model._id }, top: 1 });
if (workflows.length > 0) {
model = workflows[0];
if (NoderedUtil.IsNullEmpty(model.Xaml)) model.Xaml = "";
this.download(model.Xaml, model.name + ".xaml", "application/xaml+xml");
}
}
}
export class WorkflowsCtrl extends entitiesCtrl {
public message: string = "";
public charts: chartset[] = [];
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
this.collection = "workflow";
this.basequery = { _type: "workflow", web: true };
console.debug("WorkflowsCtrl");
this.postloadData = this.processData;
if (this.userdata.data.WorkflowsCtrl) {
this.basequery = this.userdata.data.WorkflowsCtrl.basequery;
this.collection = this.userdata.data.WorkflowsCtrl.collection;
this.baseprojection = this.userdata.data.WorkflowsCtrl.baseprojection;
this.orderby = this.userdata.data.WorkflowsCtrl.orderby;
this.searchstring = this.userdata.data.WorkflowsCtrl.searchstring;
this.basequeryas = this.userdata.data.WorkflowsCtrl.basequeryas;
}
WebSocketClientService.onSignedin((user: TokenUser) => {
this.loadData();
});
}
async processData(): Promise {
if (!this.userdata.data.WorkflowsCtrl) this.userdata.data.WorkflowsCtrl = {};
this.userdata.data.WorkflowsCtrl.basequery = this.basequery;
this.userdata.data.WorkflowsCtrl.collection = this.collection;
this.userdata.data.WorkflowsCtrl.baseprojection = this.baseprojection;
this.userdata.data.WorkflowsCtrl.orderby = this.orderby;
this.userdata.data.WorkflowsCtrl.searchstring = this.searchstring;
this.userdata.data.WorkflowsCtrl.basequeryas = this.basequeryas;
this.loading = false;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
}
export class chartset {
options: any = {
legend: { display: true }
};
// baseColors: string[] = ['#F7464A', '#97BBCD', '#FDB45C', '#46BFBD', '#949FB1', '#4D5360'];
// baseColors: string[] = ['#803690', '#00ADF9', '#DCDCDC', '#46BFBD', '#FDB45C', '#949FB1', '#4D5360'];
baseColors: [
'#97BBCD', // blue
'#DCDCDC', // light grey
'#F7464A', // red
'#46BFBD', // green
'#FDB45C', // yellow
'#949FB1', // grey
'#4D5360' // dark grey
];
colors: string[] = [
'#97BBCD', // blue
'#DCDCDC', // light grey
'#F7464A', // red
'#46BFBD', // green
'#FDB45C', // yellow
'#949FB1', // grey
'#4D5360' // dark grey
];
type: string = 'bar';
heading: string = "";
labels: string[] = [];
series: string[] = [];
data: any[] = [];
ids: any[] = [];
charttype: string = "bar";
click: any = null;
}
export declare function emit(k, v);
export class ReportsCtrl extends entitiesCtrl {
public message: string = "";
public charts: chartset[] = [];
public datatimeframe: Date;
public onlinetimeframe: Date;
public timeframedesc: string = "";
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
console.debug("ReportsCtrl");
WebSocketClientService.onSignedin((user: TokenUser) => {
if (this.userdata.data.ReportsCtrl) {
this.datatimeframe = this.userdata.data.ReportsCtrl.datatimeframe;
this.onlinetimeframe = this.userdata.data.ReportsCtrl.onlinetimeframe;
this.processData();
} else {
this.settimeframe(30, 0, "30 days");
}
});
}
settimeframe(days, hours, desc) {
this.datatimeframe = new Date(new Date().toISOString());
if (days > 0) this.datatimeframe.setDate(this.datatimeframe.getDate() - days);
if (hours > 0) this.datatimeframe.setHours(this.datatimeframe.getHours() - hours);
this.timeframedesc = desc;
this.onlinetimeframe = new Date(new Date().toISOString());
this.onlinetimeframe.setMinutes(this.onlinetimeframe.getMinutes() - 1);
// this.datatimeframe = new Date(new Date().toISOString());
// this.datatimeframe.setMonth(this.datatimeframe.getMonth() - 1);
// dt = new Date(new Date().toISOString());
// dt.setMonth(dt.getMonth() - 1);
// //dt.setDate(dt.getDate() - 1);
// dt = new Date(new Date().toISOString());
// dt.setMonth(dt.getMonth() - 1);
// const dt2 = new Date(new Date().toISOString());
// dt2.setMinutes(dt.getMinutes() - 1);
if (!this.userdata.data.ReportsCtrl) this.userdata.data.ReportsCtrl = { run: this.processData.bind(this) };
this.userdata.data.ReportsCtrl.datatimeframe = this.datatimeframe;
this.userdata.data.ReportsCtrl.onlinetimeframe = this.onlinetimeframe;
this.userdata.data.ReportsCtrl.run(this.userdata.data.ReportsCtrl.points);
}
async processData(): Promise {
console.debug('processData');
this.userdata.data.ReportsCtrl.run = this.processData.bind(this);
this.userdata.data.ReportsCtrl.points = null;
this.loading = true;
this.charts = [];
let aggregates: any = [
{ $match: { _rpaheartbeat: { "$gte": this.datatimeframe } } },
{ "$count": "_rpaheartbeat" }
];
let data: any[] = await NoderedUtil.Aggregate({ collectionname: "users", aggregates });
let totalrobots = 0;
if (data.length > 0) totalrobots = data[0]._rpaheartbeat;
aggregates = [
{ $match: { _rpaheartbeat: { "$gte": this.onlinetimeframe } } },
{ "$count": "_rpaheartbeat" }
];
data = await NoderedUtil.Aggregate({ collectionname: "users", aggregates });
let onlinerobots = 0;
if (data.length > 0) onlinerobots = data[0]._rpaheartbeat;
const chart: chartset = new chartset();
chart.heading = onlinerobots + " Online and " + (totalrobots - onlinerobots) + " offline robots, seen the last " + this.timeframedesc;
chart.labels = ['online', 'offline'];
chart.data = [onlinerobots, (totalrobots - onlinerobots)];
chart.charttype = "pie";
chart.colors = [
// '#98FB98', // very light green
// '#F08080', // very light red
// '#228B22', // green
// '#B22222', // red
'#006400', // green
'#8B0000', // red
];
// chart.click = this.robotsclick.bind(this);
chart.click = this.robotsclick.bind(this);
this.charts.push(chart);
if (!this.$scope.$$phase) { this.$scope.$apply(); }
// const agg = [{ "$group": { "_id": "$_type", "count": { "$sum": 1 } } }];
aggregates = [
{ $match: { _created: { "$gte": this.datatimeframe }, _type: "workflowinstance" } },
{ "$group": { "_id": { "WorkflowId": "$WorkflowId", "name": "$name" }, "count": { "$sum": 1 } } },
{ $sort: { "count": -1 } },
{ "$limit": 20 }
];
const workflowruns = await NoderedUtil.Aggregate({ collectionname: "openrpa_instances", aggregates });
const chart2: chartset = new chartset();
chart2.heading = "Workflow runs (top 20)";
// chart2.series = ['name', 'count'];
// chart2.labels = ['name', 'count'];
chart2.data = [];
chart2.ids = [];
for (let x = 0; x < workflowruns.length; x++) {
// chart2.data[0].push(workflowruns[x]._id.name);
// chart2.data[1].push(workflowruns[x].count);
chart2.data.push(workflowruns[x].count);
chart2.ids.push(workflowruns[x]._id.WorkflowId);
chart2.labels.push(workflowruns[x]._id.name);
// if (workflow == undefined) { chart2.labels.push("unknown"); } else { chachart2rt.labels.push(workflow.name); }
// }
}
chart2.click = this.workflowclick.bind(this);
this.charts.push(chart2);
this.loading = false;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
async robotsclick(points, evt): Promise {
console.debug('robotsclick');
this.userdata.data.ReportsCtrl.run = this.robotsclick.bind(this);
this.userdata.data.ReportsCtrl.points = points;
if (points.length > 0) {
} else { return; }
let chart: chartset = null;
let aggregates: any = {};
let rpaheartbeat: any = [];
if (points[0]._index == 0) // Online robots
{
// rpaheartbeat = { $match: { "user._rpaheartbeat": { "$gte": this.onlinetimeframe } } };
rpaheartbeat = { $match: { "_rpaheartbeat": { "$gte": this.onlinetimeframe } } };
} else {
// rpaheartbeat = { $match: { "user._rpaheartbeat": { "$lt": this.onlinetimeframe } } };
rpaheartbeat = { $match: { "_rpaheartbeat": { "$lt": this.onlinetimeframe } } };
}
this.charts = [];
aggregates = [
{ $match: { _type: 'user' } }
, { $sort: { "_rpaheartbeat": -1 } }
, { "$limit": 20 }
, rpaheartbeat
, {
$lookup: {
from: "audit",
localField: "_id",
foreignField: "userid",
as: "audit"
}
}
, {
$project: {
"_id": 1,
"name": 1,
"count": { "$size": "$audit" }
}
}
, { $sort: { "count": -1 } }
// , { $sort: { "_rpaheartbeat": -1 } }
// , { "$limit": 20 }
];
let data = await NoderedUtil.Aggregate({ collectionname: "users", aggregates });
chart = new chartset();
if (points[0]._index == 0) // Online robots
{
chart.heading = "Logins per online robot the last " + this.timeframedesc + " (top 20)";
} else {
chart.heading = "Logins per offline robot the last " + this.timeframedesc + " (top 20)";
}
chart.data = [];
chart.ids = [];
for (let x = 0; x < data.length; x++) {
chart.data.push(data[x].count);
chart.ids.push(data[x]._id);
chart.labels.push(data[x].name);
}
chart.click = this.robotclick.bind(this);
this.charts.push(chart);
if (!this.$scope.$$phase) { this.$scope.$apply(); }
if (points[0]._index == 0) // Online robots
{
rpaheartbeat = { $match: { "user._rpaheartbeat": { "$gte": this.onlinetimeframe } } };
} else {
rpaheartbeat = { $match: { "user._rpaheartbeat": { "$lt": this.onlinetimeframe } } };
}
aggregates = [
{ $match: { _created: { "$gte": this.datatimeframe }, _type: "workflowinstance" } },
{
$lookup: {
from: "users",
localField: "ownerid",
foreignField: "_id",
as: "userarr"
}
},
{
"$project": {
"WorkflowId": 1,
"name": 1,
"user": { "$arrayElemAt": ["$userarr", 0] }
}
},
{
"$project": {
"WorkflowId": 1,
"newname": { $concat: ["$name", " (", "$user.name", ")"] },
"name": 1,
"user": 1
}
},
rpaheartbeat,
// { $project: { "newname": } },
{ "$group": { "_id": { "WorkflowId": "$WorkflowId", "name": "$newname" }, "count": { "$sum": 1 } } },
{ $sort: { "count": -1 } },
{ "$limit": 20 }
];
const workflowruns = await NoderedUtil.Aggregate({ collectionname: "openrpa_instances", aggregates });
chart = new chartset();
if (points[0]._index == 0) // Online robots
{
chart.heading = "Workflow runs for online robots (top 20)";
} else {
chart.heading = "Workflow runs for offline robots (top 20)";
}
chart.data = [];
chart.ids = [];
for (let x = 0; x < workflowruns.length; x++) {
chart.data.push(workflowruns[x].count);
chart.ids.push(workflowruns[x]._id.WorkflowId);
chart.labels.push(workflowruns[x]._id.name);
}
chart.click = this.workflowclick.bind(this);
this.charts.push(chart);
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
async robotclick(points, evt): Promise {
console.debug('robotclick');
if (points.length > 0) {
} else { return; }
const userid = this.charts[0].ids[points[0]._index];
let chart: chartset = null;
let aggregates: any = {};
aggregates = [
{ $match: { _created: { "$gte": this.datatimeframe }, _type: "workflowinstance", ownerid: userid } },
{ "$group": { "_id": { "WorkflowId": "$WorkflowId", "name": "$name", "owner": "$owner" }, "count": { "$sum": 1 } } },
{ $sort: { "count": -1 } },
{ "$limit": 20 }
];
const workflowruns = await NoderedUtil.Aggregate({ collectionname: "openrpa_instances", aggregates });
chart = new chartset();
if (workflowruns.length > 0) // Online robots
{
chart.heading = "Workflow runs for " + workflowruns[0].owner + " (top 20)";
} else {
chart.heading = "No data (or permissions) for robot";
}
chart.data = [];
chart.ids = [];
for (let x = 0; x < workflowruns.length; x++) {
chart.data.push(workflowruns[x].count);
chart.ids.push(workflowruns[x]._id.WorkflowId);
chart.labels.push(workflowruns[x]._id.name);
}
chart.click = this.workflowclick.bind(this);
this.charts.splice(1, 1);
this.charts.push(chart);
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
async workflowclick(points, evt): Promise {
console.debug('workflowclick');
if (points.length > 0) {
} else { return; }
const WorkflowId = this.charts[1].ids[points[0]._index];
let chart: chartset = null;
let aggregates: any = {};
aggregates = [
{ $match: { _created: { "$gte": this.datatimeframe }, WorkflowId: WorkflowId } },
{
$group:
{
_id:
{
name: "$name",
day: { $dayOfMonth: "$_created" },
month: { $month: "$_created" },
year: { $year: "$_created" }
},
total: { $sum: "$data" },
count: { $sum: 1 }
}
},
{ $sort: { "_id.day": 1 } },
{ "$limit": 20 }
];
const workflowruns = await NoderedUtil.Aggregate({ collectionname: "openrpa_instances", aggregates });
chart = new chartset();
if (workflowruns.length > 0) {
chart.heading = "Number of runs per day for " + workflowruns[0]._id.name;
} else {
chart.heading = "No data ";
}
chart.data = [];
for (let x = 0; x < workflowruns.length; x++) {
chart.data.push(workflowruns[x].count);
chart.labels.push(workflowruns[x]._id.day);
}
chart.click = this.processData.bind(this);
this.charts.splice(1, 1);
this.charts.push(chart);
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
async InsertNew(): Promise {
// this.loading = true;
const item = { name: "Find me " + NoderedUtil.GetUniqueIdentifier(), "temp": "hi mom" };
const result = await NoderedUtil.InsertOne({ collectionname: this.collection, item });
this.models.push(result);
this.loading = false;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
async UpdateOne(item: any): Promise {
const index = this.models.indexOf(item);
this.loading = true;
item.name = "Find me " + NoderedUtil.GetUniqueIdentifier();
const newmodel = await NoderedUtil.UpdateOne({ collectionname: this.collection, item });
this.models = this.models.filter(function (m: any): boolean { return m._id !== item._id; });
this.models.splice(index, 0, newmodel);
this.loading = false;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
}
export class MainCtrl extends entitiesCtrl {
public showcompleted: boolean = false;
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
console.debug("MainCtrl");
this.collection = "workflow_instances"
// this.basequery = { state: { $ne: "completed" }, $and: [{ form: { $exists: true } }, { form: { "$ne": "none" } }] };
// this.basequery = { state: { $ne: "completed" }, form: { $exists: true } };
this.preloadData = () => {
const user = WebSocketClient.instance.user;
const ors: any[] = [];
ors.push({ targetid: user._id });
WebSocketClient.instance.user.roles.forEach(role => {
ors.push({ targetid: role._id });
});
this.basequery = {};
this.basequery = { $or: ors };
if (!this.showcompleted) {
// this.basequery.state = { $ne: "completed" };
this.basequery["$and"] = [{ state: { $ne: "completed" } }, { state: { $ne: "failed" } }];
this.basequery.form = { $exists: true };
// this.basequery.$or = ors;
} else {
}
};
WebSocketClientService.onSignedin((_user: TokenUser) => {
this.loadData();
});
}
}
declare const QRScanner: any;
export class LoginCtrl {
public localenabled: boolean = false;
public scanning: boolean = false;
public qrcodescan: boolean = false;
public providers: any = false;
public username: string = "";
public password: string = "";
public message: string = "";
public domain: string = "";
public allow_user_registration: boolean = false;
public forgot_pass_emails: boolean = false;
public static $inject = [
"$scope",
"$location",
"$routeParams",
"WebSocketClientService",
"api"
];
constructor(
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public WebSocketClientService: WebSocketClientService,
public api: api
) {
console.debug("LoginCtrl::constructor");
this.domain = window.location.hostname;
WebSocketClientService.getJSON("/loginproviders", async (error: any, data: any) => {
this.forgot_pass_emails = WebSocketClientService.forgot_pass_emails;
this.providers = data;
this.allow_user_registration = WebSocketClientService.allow_user_registration;
for (let i: number = this.providers.length - 1; i >= 0; i--) {
if (this.providers[i].provider == "local") {
this.providers.splice(i, 1);
this.localenabled = true;
}
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
setTimeout(this.scanForQRScanner.bind(this), 200);
});
}
readfile(filename: string) {
return new Promise(async (resolve, reject) => {
const win: any = window;
//const type = win.TEMPORARY;
const type = win.PERSISTENT;
const size = 5 * 1024 * 1024;
win.requestFileSystem(type, size, successCallback, errorCallback)
function successCallback(fs) {
fs.root.getFile(filename, {}, function (fileEntry) {
fileEntry.file(function (file) {
const reader = new FileReader();
reader.onloadend = function (e) {
resolve(this.result as string);
};
reader.readAsText(file);
}, errorCallback);
}, errorCallback);
}
function errorCallback(error) {
console.debug(error);
resolve(null);
}
});
}
writefile(filename: string, content: string) {
return new Promise(async (resolve, reject) => {
const win: any = window;
//const type = win.TEMPORARY;
const type = win.PERSISTENT;
const size = 5 * 1024 * 1024;
win.requestFileSystem(type, size, successCallback, errorCallback)
function successCallback(fs) {
fs.root.getFile(filename, { create: true }, function (fileEntry) {
fileEntry.createWriter(function (fileWriter) {
fileWriter.onwriteend = function (e) {
console.debug('Write completed.');
resolve();
};
fileWriter.onerror = function (e) {
console.error('Write failed: ' + e.toString());
resolve();
};
const blob = new Blob([content], { type: 'text/plain' });
fileWriter.write(blob);
}, errorCallback);
}, errorCallback);
}
function errorCallback(error) {
console.error(error);
resolve();
}
});
}
scanForQRScanner() {
try {
if (QRScanner !== undefined) {
console.debug("Found QRScanner!!!!");
this.qrcodescan = true;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
} else {
console.debug("QRScanner not definded");
setTimeout(this.scanForQRScanner, 200);
}
} catch (error) {
console.debug("Failed locating QRScanner");
setTimeout(this.scanForQRScanner, 200);
}
}
Scan() {
try {
console.debug("Scan");
if (this.scanning) {
this.scanning = false;
QRScanner.destroy();
if (!this.$scope.$$phase) { this.$scope.$apply(); }
return;
}
this.scanning = true;
QRScanner.scan(this.QRScannerHit.bind(this));
QRScanner.show();
if (!this.$scope.$$phase) { this.$scope.$apply(); }
} catch (error) {
console.error("Error Scan");
console.error(error);
}
}
async QRScannerHit(err, value) {
try {
console.debug("QRScannerHit");
if (err) {
// console.error(err._message);
console.error(err);
return;
}
console.debug(value);
QRScanner.hide();
QRScanner.destroy();
this.scanning = false;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
if (value === null || value === undefined || value === "") {
console.debug("QRCode had null value"); return;
}
console.debug("QRCode value: " + value);
const config = JSON.parse(value);
if (config.url !== null || config.url !== undefined || config.url !== "" || config.loginurl !== null || config.loginurl !== undefined || config.loginurl !== "") {
console.debug("set mobiledomain to " + value);
await this.writefile("mobiledomain.txt", value);
window.location.replace(config.url);
}
} catch (error) {
console.error("Error QRScannerHit");
console.error(error);
}
}
BeginForgotPassword() {
document.location.href = "/login?forgot=true";
}
async submit(): Promise {
this.message = "";
try {
console.debug("signing in with username/password");
const result: SigninMessage = await NoderedUtil.SigninWithUsername({ username: this.username, password: this.password });
if (result.user == null) { return; }
this.setCookie("jwt", result.jwt, 365);
this.$location.path("/");
} catch (error) {
this.message = error.message ? error.message : error;
console.error(error);
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
Signup() {
this.$location.path("/Signup");
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
setCookie(cname, cvalue, exdays) {
const d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
const expires = "expires=" + d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
getCookie(cname) {
const name = cname + "=";
const decodedCookie = decodeURIComponent(document.cookie);
const ca = decodedCookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
usernameblur() {
if (!NoderedUtil.IsNullEmpty(this.username) && this.username.indexOf("@") > -1) {
var domain = this.username.substr(this.username.indexOf("@") + 1);
if (this.WebSocketClientService.forceddomains && Array.isArray(this.WebSocketClientService.forceddomains)) {
for (let d = 0; d < this.WebSocketClientService.forceddomains.length; d++) {
let forceddomain = new RegExp(this.WebSocketClientService.forceddomains[d], "i");
if (forceddomain.test(domain)) {
console.log("domain found in forceddomains");
document.getElementById("password").style.display = "none";
document.getElementById("localbuttons").style.display = "none";
this.message = "Please use provider button to login with this domain";
if (!this.$scope.$$phase) { this.$scope.$apply(); }
return;
}
}
}
}
this.message = "";
if (!this.$scope.$$phase) { this.$scope.$apply(); }
document.getElementById("password").style.display = "block";
document.getElementById("localbuttons").style.display = "block";
}
}
export class ProvidersCtrl extends entitiesCtrl {
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
console.debug("ProvidersCtrl");
this.basequery = { _type: "provider" };
this.collection = "config";
this.skipcustomerfilter = true;
WebSocketClientService.onSignedin((user: TokenUser) => {
this.loadData();
});
}
}
export class ProviderCtrl extends entityCtrl {
public newforceddomain: string = "";
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
console.debug("ProviderCtrl");
this.collection = "config";
WebSocketClientService.onSignedin((user: TokenUser) => {
if (this.id !== null && this.id !== undefined) {
this.loadData();
} else {
try {
this.model = new Provider("", "", "", "uri:" + this.WebSocketClientService.domain, "")
} catch (error) {
this.model = {} as any;
this.model.name = "";
this.model._type = "provider";
this.model.issuer = "uri:" + this.WebSocketClientService.domain;
}
}
});
}
async submit(): Promise {
try {
if (this.model._id) {
await NoderedUtil.UpdateOne({ collectionname: this.collection, item: this.model });
} else {
await NoderedUtil.InsertOne({ collectionname: this.collection, item: this.model });
}
this.$location.path("/Providers");
} catch (error) {
this.errormessage = error.message ? error.message : error;
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
deleteforceddomains(id) {
if ((this.model as any).forceddomains === null || (this.model as any).forceddomains === undefined) {
(this.model as any).forceddomains = [];
}
(this.model as any).forceddomains = (this.model as any).forceddomains.filter(function (m: any): boolean { return m !== id; });
}
addforceddomains() {
if ((this.model as any).forceddomains === null || (this.model as any).forceddomains === undefined) {
(this.model as any).forceddomains = [];
}
var v = this.newforceddomain;
try {
v = JSON.parse(v);
} catch (error) {
}
(this.model as any).forceddomains.push(v);
}
}
export class UsersCtrl extends entitiesCtrl {
public stripe: any = null;
public proration: boolean = false;
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
this.autorefresh = true;
console.debug("UsersCtrl");
this.basequery = { _type: "user" };
this.collection = "users";
this.searchfields = ["name", "username"];
this.postloadData = this.processData;
if (this.userdata.data.UsersCtrl) {
this.basequery = this.userdata.data.UsersCtrl.basequery;
this.collection = this.userdata.data.UsersCtrl.collection;
this.baseprojection = this.userdata.data.UsersCtrl.baseprojection;
this.orderby = this.userdata.data.UsersCtrl.orderby;
this.searchstring = this.userdata.data.UsersCtrl.searchstring;
this.basequeryas = this.userdata.data.UsersCtrl.basequeryas;
this.skipcustomerfilter = this.userdata.data.UsersCtrl.skipcustomerfilter;
}
WebSocketClientService.onSignedin(async (user: TokenUser) => {
let haderror: boolean = false;
if (!NoderedUtil.IsNullEmpty(this.WebSocketClientService.stripe_api_key)) {
try {
this.stripe = Stripe(this.WebSocketClientService.stripe_api_key);
} catch (error) {
haderror = true;
}
if (haderror) {
console.debug("loading stripe script")
await jsutil.loadScript('//js.stripe.com/v3/');
this.stripe = Stripe(this.WebSocketClientService.stripe_api_key);
}
}
this.loadData();
});
}
async processData(): Promise {
if (!this.userdata.data.UsersCtrl) this.userdata.data.UsersCtrl = {};
this.userdata.data.UsersCtrl.basequery = this.basequery;
this.userdata.data.UsersCtrl.collection = this.collection;
this.userdata.data.UsersCtrl.baseprojection = this.baseprojection;
this.userdata.data.UsersCtrl.orderby = this.orderby;
this.userdata.data.UsersCtrl.searchstring = this.searchstring;
this.userdata.data.UsersCtrl.basequeryas = this.basequeryas;
this.userdata.data.UsersCtrl.skipcustomerfilter = this.skipcustomerfilter;
this.loading = false;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
async Impersonate(model: TokenUser): Promise {
try {
this.loading = true;
await this.WebSocketClientService.impersonate(model._id);
this.loading = false;
this.loadData();
} catch (error) {
this.errormessage = JSON.stringify(error);
}
this.loading = false;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
public Resources: Resource[];
public Assigned: ResourceUsage[];
public user: TokenUser;
async ShowPlans(user: TokenUser) {
try {
this.errormessage = "";
this.user = user;
this.proration = false;
var title = document.getElementById("title");
title.scrollIntoView();
this.ToggleModal()
this.Resources = await NoderedUtil.Query({
collectionname: "config", query: { "_type": "resource", "target": "user", "allowdirectassign": true },
orderby: { _created: -1, "order": 1 }
});
this.Assigned = await NoderedUtil.Query({ collectionname: "config", query: { "_type": "resourceusage", "userid": user._id }, orderby: { _created: -1 } });
for (var i = this.Resources.length - 1; i >= 0; i--) {
var res = this.Resources[i];
for (var prod of res.products) {
(prod as any).count = this.AssignCount(prod);
if ((prod as any).count > 0) {
(res as any).newproduct = prod;
}
}
res.products = res.products.filter(x => x.allowdirectassign == true || (x as any).count > 0);
}
} catch (error) {
this.errormessage = error;
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
AssignCount(Product: ResourceVariant) {
const assigned = this.Assigned.filter(x => x.product.stripeprice == Product.stripeprice && x.quantity > 0 && x.siid != null);
return assigned.length;
}
ToggleModal() {
var modal = document.getElementById("resourceModal");
modal.classList.toggle("show");
}
CloseModal() {
var modal = document.getElementById("resourceModal");
modal.classList.remove("show");
}
ToggleNextInvoiceModal() {
var modal = document.getElementById("NextInvoiceModal");
modal.classList.toggle("show");
}
CloseNextInvoiceModal() {
var modal = document.getElementById("NextInvoiceModal");
modal.classList.remove("show");
}
async RemovePlan(resource: Resource, product: ResourceVariant) {
try {
this.CloseNextInvoiceModal();
this.loading = true;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
const assigned = this.Assigned.filter(x => x.product.stripeprice == product.stripeprice);
if (assigned.length > 0) {
await NoderedUtil.StripeCancelPlan({ resourceusageid: assigned[0]._id });
}
this.loading = false;
this.CloseModal();
this.loadData();
this.loading = false;
} catch (error) {
this.loading = false;
this.errormessage = error;
try {
this.CloseNextInvoiceModal();
this.CloseModal();
} catch (error) {
}
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
async AddPlan2() {
this.loading = true;
this.CloseNextInvoiceModal();
this.CloseModal();
if (!this.$scope.$$phase) { this.$scope.$apply(); }
var result = await NoderedUtil.StripeAddPlan({
userid: this.user._id, customerid: this.user.customerid,
resourceid: this.resource._id, stripeprice: this.product.stripeprice
});
var checkout = result.checkout;
if (checkout) {
const stripe = Stripe(this.WebSocketClientService.stripe_api_key);
stripe
.redirectToCheckout({
sessionId: checkout.id,
})
.then(function (event) {
if (event.complete) {
// enable payment button
} else if (event.error) {
console.error(event.error);
if (event.error && event.error.message) {
this.cardmessage = event.error.message;
} else {
this.cardmessage = event.error;
}
console.error(event.error);
// show validation to customer
} else {
}
}).catch((error) => {
console.error(error);
this.errormessage = error;
});
} else {
this.loading = false;
this.loadData();
}
}
private resource: Resource;
private product: ResourceVariant;
public nextinvoice: stripe_invoice;
public period_start: string;
public period_end: string;
async AddPlan(resource: Resource, product: ResourceVariant) {
try {
this.resource = resource;
this.product = product;
this.loading = true;
this.errormessage = "";
let items = [];
items.push({ quantity: 1, price: product.stripeprice });
this.nextinvoice = await NoderedUtil.GetNextInvoice({ customerid: this.WebSocketClientService.customer._id, subscription_items: items })
if (this.nextinvoice != null) {
const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
const period_start = new Date(this.nextinvoice.period_start * 1000);
const period_end = new Date(this.nextinvoice.period_end * 1000);
this.period_start = period_start.getDate() + " " + monthNames[period_start.getMonth()] + " " + period_start.getFullYear();
this.period_end = period_end.getDate() + " " + monthNames[period_end.getMonth()] + " " + period_end.getFullYear();
this.proration = true;
this.ToggleNextInvoiceModal();
this.loading = false;
} else {
this.AddPlan2();
}
} catch (error) {
this.loading = false;
this.errormessage = error;
try {
var modal = document.getElementById("resourceModal");
modal.classList.toggle("show");
} catch (error) {
}
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
}
export class UserCtrl extends entityCtrl {
public newid: string;
public memberof: Role[];
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
console.debug("UserCtrl");
this.collection = "users";
this.postloadData = this.processdata;
this.memberof = [];
WebSocketClientService.onSignedin((user: TokenUser) => {
if (this.id !== null && this.id !== undefined) {
this.loadData();
} else {
this.model = new TokenUser();
this.model._type = "user";
this.model.name = "";
this.model.username = "";
(this.model as any).newpassword = "";
(this.model as any).sid = "";
(this.model as any).federationids = [];
this.processdata();
}
});
}
async processdata() {
if (this.model != null && (this.model._id != null && this.model._id != "")) {
if (this.model._id == WebSocketClient.instance.user._id) {
this.memberof = WebSocketClient.instance.user.roles as any;
} else {
this.memberof = await NoderedUtil.Query({
collectionname: "users",
query: {
$and: [
{ _type: "role" },
{ members: { $elemMatch: { _id: this.model._id } } }
]
}, orderby: { _type: -1, name: 1 }
});
}
} else {
this.memberof = [];
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
deleteid(id) {
if ((this.model as any).federationids === null || (this.model as any).federationids === undefined) {
(this.model as any).federationids = [];
}
(this.model as any).federationids = (this.model as any).federationids.filter(function (m: any): boolean { return m !== id; });
}
addid() {
if ((this.model as any).federationids === null || (this.model as any).federationids === undefined) {
(this.model as any).federationids = [];
}
var v = this.newid;
try {
v = JSON.parse(v);
} catch (error) {
}
(this.model as any).federationids.push(v);
}
removedmembers: Role[] = [];
RemoveMember(model: Role) {
this.removedmembers.push(model);
this.memberof = this.memberof.filter(x => x._id != model._id);
}
async submit(): Promise {
try {
this.loading = true;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
if (this.model._id) {
await NoderedUtil.UpdateOne({ collectionname: this.collection, item: this.model });
} else {
await NoderedUtil.InsertOne({ collectionname: this.collection, item: this.model });
}
if (this.removedmembers.length > 0) {
for (let i = 0; i < this.removedmembers.length; i++) {
const roles = await NoderedUtil.Query({ collectionname: "users", query: { _type: "role", _id: this.removedmembers[i]._id }, orderby: { _type: -1, name: 1 }, top: 5 });
if (roles.length > 0) {
const memberof = roles[i];
const exists = memberof.members.filter(x => x._id == this.model._id);
if (exists.length > 0) {
memberof.members = memberof.members.filter(x => x._id != this.model._id);
try {
await NoderedUtil.UpdateOne({ collectionname: "users", item: memberof });
} catch (error) {
console.error("Error updating " + memberof.name, error);
}
}
}
}
}
this.loading = false;
this.$location.path("/Users");
} catch (error) {
this.loading = false;
this.errormessage = error.message ? error.message : error;
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
}
export class RolesCtrl extends entitiesCtrl {
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
this.autorefresh = true;
console.debug("RolesCtrl");
this.basequery = { _type: "role" };
this.collection = "users";
this.postloadData = this.processdata;
if (this.userdata.data.RolesCtrl) {
this.basequery = this.userdata.data.RolesCtrl.basequery;
this.collection = this.userdata.data.RolesCtrl.collection;
this.baseprojection = this.userdata.data.RolesCtrl.baseprojection;
this.orderby = this.userdata.data.RolesCtrl.orderby;
this.searchstring = this.userdata.data.RolesCtrl.searchstring;
this.basequeryas = this.userdata.data.RolesCtrl.basequeryas;
this.skipcustomerfilter = this.userdata.data.RolesCtrl.skipcustomerfilter;
}
WebSocketClientService.onSignedin((user: TokenUser) => {
this.loadData();
});
}
processdata() {
if (!this.userdata.data.RolesCtrl) this.userdata.data.RolesCtrl = {};
this.userdata.data.RolesCtrl.basequery = this.basequery;
this.userdata.data.RolesCtrl.collection = this.collection;
this.userdata.data.RolesCtrl.baseprojection = this.baseprojection;
this.userdata.data.RolesCtrl.orderby = this.orderby;
this.userdata.data.RolesCtrl.searchstring = this.searchstring;
this.userdata.data.RolesCtrl.basequeryas = this.basequeryas;
this.userdata.data.RolesCtrl.skipcustomerfilter = this.skipcustomerfilter;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
}
export class RoleCtrl extends entityCtrl {
searchFilteredList: Role[] = [];
searchSelectedItem: Role = null;
searchtext: string = "";
e: any = null;
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
console.debug("RoleCtrl");
this.collection = "users";
WebSocketClientService.onSignedin(async (user: TokenUser) => {
if (this.id !== null && this.id !== undefined) {
await this.loadData();
} else {
this.model = new Role();
}
});
}
async submit(): Promise {
try {
if (this.model._id) {
await NoderedUtil.UpdateOne({ collectionname: this.collection, item: this.model });
} else {
this.model = await NoderedUtil.InsertOne({ collectionname: this.collection, item: this.model });
}
this.$location.path("/Roles");
} catch (error) {
console.error(error);
this.errormessage = error.message ? error.message : error;
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
RemoveMember(model: any) {
if (this.model.members === undefined) { this.model.members = []; }
for (let i: number = 0; i < this.model.members.length; i++) {
if (this.model.members[i]._id === model._id) {
this.model.members.splice(i, 1);
}
}
}
AddMember(model: any) {
if (this.model.members === undefined) { this.model.members = []; }
const user: any = this.searchSelectedItem;;
this.model.members.push({ name: user.name, _id: user._id });
this.searchSelectedItem = null;
this.searchtext = "";
}
restrictInput(e) {
if (e.keyCode == 13) {
e.preventDefault();
return false;
}
}
setkey(e) {
this.e = e;
this.handlekeys();
}
handlekeys() {
if (this.searchFilteredList.length > 0) {
let idx: number = -1;
for (let i = 0; i < this.searchFilteredList.length; i++) {
if (this.searchSelectedItem != null) {
if (this.searchFilteredList[i]._id == this.searchSelectedItem._id) {
idx = i;
}
}
}
if (this.e.keyCode == 38) { // up
if (idx <= 0) {
idx = 0;
} else { idx--; }
// this.searchtext = this.searchFilteredList[idx].name;
this.searchSelectedItem = this.searchFilteredList[idx];
return;
}
else if (this.e.keyCode == 40) { // down
if (idx >= this.searchFilteredList.length) {
idx = this.searchFilteredList.length - 1;
} else { idx++; }
// this.searchtext = this.searchFilteredList[idx].name;
this.searchSelectedItem = this.searchFilteredList[idx];
return;
}
else if (this.e.keyCode == 13) { // enter
if (idx >= 0) {
this.searchtext = this.searchFilteredList[idx].name;
this.searchSelectedItem = this.searchFilteredList[idx];
this.searchFilteredList = [];
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
return;
}
} else {
if (this.e.keyCode == 13 && this.searchSelectedItem != null) {
this.AddMember(this.searchSelectedItem);
}
}
}
async handlefilter(e) {
this.e = e;
const ids: string[] = this.model.members.map(item => item._id);
this.searchFilteredList = await NoderedUtil.Query({
collectionname: "users",
query: {
$and: [
{ $or: [{ _type: "user" }, { _type: "role" }] },
{ name: this.searchtext }
]
}
, orderby: { _type: -1, name: 1 }, top: 2
});
this.searchFilteredList = this.searchFilteredList.concat(await NoderedUtil.Query({
collectionname: "users",
query: {
$and: [
{ $or: [{ _type: "user" }, { _type: "role" }] },
{ name: new RegExp([this.searchtext].join(""), "i") },
{ _id: { $nin: ids } }
]
}
, orderby: { _type: -1, name: 1 }, top: 5
}));
// this.searchFilteredList = await NoderedUtil.Query("users",
// {
// $and: [
// { $or: [{ _type: "user" }, { _type: "role" }] },
// { name: new RegExp([this.searchtext].join(""), "i") },
// { _id: { $nin: ids } }
// ]
// }
// , null, { _type: -1, name: 1 }, 8, 0, null);
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
fillTextbox(searchtext) {
this.searchFilteredList.forEach((item: any) => {
if (item.name.toLowerCase() == searchtext.toLowerCase()) {
this.searchtext = item.name;
this.searchSelectedItem = item;
this.searchFilteredList = [];
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
});
}
}
export class FilesCtrl extends entitiesCtrl {
public file: string;
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
console.debug("EntitiesCtrl");
this.autorefresh = true;
this.basequery = {};
this.searchfields = ["metadata.name"];
this.orderby = { "metadata._created": -1 }
this.collection = "files";
this.baseprojection = { _type: 1, type: 1, name: 1, _created: 1, _createdby: 1, _modified: 1 };
const elem = document.getElementById("myBar");
elem.style.width = '0%';
WebSocketClientService.onSignedin((user: TokenUser) => {
this.loadData();
});
}
async Download(id: string) {
const lastp: number = 0;
const fileinfo = await NoderedUtil.GetFile({ id });
const elem = document.getElementById("myBar");
elem.style.width = '0%';
elem.innerText = '';
const blob = this.b64toBlob(fileinfo.file, fileinfo.mimeType);
// const blobUrl = URL.createObjectURL(blob);
// (window.location as any) = blobUrl;
const anchor = document.createElement('a');
anchor.download = fileinfo.metadata.name;
anchor.href = ((window as any).webkitURL || window.URL).createObjectURL(blob);
anchor.dataset.downloadurl = [fileinfo.mimeType, anchor.download, anchor.href].join(':');
anchor.click();
}
b64toBlob(b64Data: string, contentType: string, sliceSize: number = 512) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
const byteCharacters = atob(b64Data);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
const blob = new Blob(byteArrays, { type: contentType });
return blob;
}
async Upload() {
// const e: any = document.querySelector('input[type="file"]');
const e: any = document.getElementById('fileupload')
const fd = new FormData();
for (let i = 0; i < e.files.length; i++) {
const file = e.files[i];
fd.append(e.name, file, file.name);
};
const xhr = new XMLHttpRequest();
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
console.debug("upload complete");
// we done!
if (!this.$scope.$$phase) { this.$scope.$apply(); }
this.loadData();
}
};
console.debug("open");
xhr.open('POST', '/upload', true);
console.debug("send");
xhr.send(fd);
}
async Upload_usingapi() {
try {
const filename = (this.$scope as any).filename;
const mimeType = (this.$scope as any).type;
console.debug("filename: " + filename + " mimeType: " + mimeType);
this.loading = true;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
const lastp: number = 0;
await NoderedUtil.SaveFile({ filename, mimeType, file: this.file, compressed: false });
const elem = document.getElementById("myBar");
elem.style.width = '0%';
elem.innerText = '';
this.loading = false;
} catch (error) {
console.error(error);
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
this.loadData();
}
}
export class EntitiesCtrl extends entitiesCtrl {
public collections: any;
public showrunning: boolean = false;
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
console.debug("EntitiesCtrl");
this.autorefresh = true;
this.basequery = {};
this.collection = $routeParams.collection;
this.baseprojection = { _type: 1, type: 1, name: 1, _created: 1, _createdby: 1, _modified: 1 };
this.postloadData = this.processdata;
if (this.userdata.data.EntitiesCtrl) {
this.basequery = this.userdata.data.EntitiesCtrl.basequery;
this.collection = this.userdata.data.EntitiesCtrl.collection;
this.baseprojection = this.userdata.data.EntitiesCtrl.baseprojection;
this.orderby = this.userdata.data.EntitiesCtrl.orderby;
this.searchstring = this.userdata.data.EntitiesCtrl.searchstring;
this.basequeryas = this.userdata.data.EntitiesCtrl.basequeryas;
this.showrunning = this.userdata.data.EntitiesCtrl.showrunning;
} else {
if (NoderedUtil.IsNullEmpty(this.collection)) {
this.$location.path("/Entities/entities");
if (!this.$scope.$$phase) { this.$scope.$apply(); }
return;
}
}
console.debug("path: " + this.$location.path());
if (NoderedUtil.IsNullEmpty(this.collection)) {
this.$location.path("/Entities/entities");
if (!this.$scope.$$phase) { this.$scope.$apply(); }
return;
} else if (this.$location.path() != "/Entities/" + this.collection) {
this.$location.path("/Entities/" + this.collection);
if (!this.$scope.$$phase) { this.$scope.$apply(); }
return;
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
this.preloadData = () => {
if (this.showrunning) {
this.basequery = { "state": { "$in": ["idle", "running"] } };
} else {
this.basequery = {};
}
};
WebSocketClientService.onSignedin(async (user: TokenUser) => {
try {
if (this.collection == "audit") {
if (this.orderby && this.orderby["_id"]) {
this.orderby["_created"] = this.orderby["_id"];
delete this.orderby["_id"];
}
}
this.loadData();
this.collections = await NoderedUtil.ListCollections({});
} catch (error) {
this.errormessage = error;
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
});
}
processdata() {
if (!this.userdata.data.EntitiesCtrl) this.userdata.data.EntitiesCtrl = {};
this.userdata.data.EntitiesCtrl.basequery = this.basequery;
this.userdata.data.EntitiesCtrl.collection = this.collection;
this.userdata.data.EntitiesCtrl.baseprojection = this.baseprojection;
this.userdata.data.EntitiesCtrl.orderby = this.orderby;
this.userdata.data.EntitiesCtrl.searchstring = this.searchstring;
this.userdata.data.EntitiesCtrl.basequeryas = this.basequeryas;
this.userdata.data.EntitiesCtrl.showrunning = this.showrunning;
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
SelectCollection() {
this.userdata.data.EntitiesCtrl.collection = this.collection;
this.$location.path("/Entities/" + this.collection);
//this.$location.hash("#/Entities/" + this.collection);
if (!this.$scope.$$phase) { this.$scope.$apply(); }
// this.loadData();
}
async DropCollection() {
await NoderedUtil.DropCollection({ collectionname: this.collection });
this.collections = await NoderedUtil.ListCollections({});
this.collection = "entities";
this.loadData();
}
}
export class FormsCtrl extends entitiesCtrl {
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
console.debug("FormsCtrl");
this.autorefresh = true;
this.collection = "forms";
this.basequery = { "_type": "form" }
this.baseprojection = { _type: 1, type: 1, name: 1, _created: 1, _createdby: 1, _modified: 1 };
WebSocketClientService.onSignedin((user: TokenUser) => {
this.loadData();
});
}
}
export class FormResourcesCtrl extends entitiesCtrl {
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
console.debug("FormsCtrl");
this.autorefresh = true;
this.collection = "forms";
this.basequery = { "_type": "resource" }
this.baseprojection = { _type: 1, type: 1, name: 1, _created: 1, _createdby: 1, _modified: 1 };
WebSocketClientService.onSignedin((user: TokenUser) => {
this.loadData();
});
}
}
export class FormResourceCtrl extends entityCtrl {
public newforceddomain: string = "";
public collections: any[];
constructor(
public $rootScope: ng.IRootScopeService,
public $scope: ng.IScope,
public $location: ng.ILocationService,
public $routeParams: ng.route.IRouteParamsService,
public $interval: ng.IIntervalService,
public WebSocketClientService: WebSocketClientService,
public api: api,
public userdata: userdata
) {
super($rootScope, $scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
console.debug("FormResourceCtrl");
this.collection = "forms";
this.postloadData = this.postload;
WebSocketClientService.onSignedin(async (user: TokenUser) => {
this.collections = await NoderedUtil.ListCollections({});
if (this.id !== null && this.id !== undefined) {
this.loadData();
} else {
try {
this.model = new Base()
this.model._type = "resource";
// @ts-ignore
this.model.collection = "entities"
this.model.name = "entities"
// @ts-ignore
this.model.aggregates = [{ "$match": {} }, { "$project": { "name": 1, "_type": 1 } }];
} catch (error) {
this.model = {} as any;
this.model.name = "ente";
this.model._type = "resource";
// @ts-ignore
this.model.collection = "entities"
this.model.name = "entities"
// @ts-ignore
this.model.aggregates = [{ "$match": {} }, { "$project": { "name": 1, "_type": 1 } }];
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
this.fixtextarea()
}
});
}
collapsobject(o) {
const keys = Object.keys(o);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
if (key.startsWith("$")) {
let newkey = "___" + key.substr(1);
o[newkey] = o[key];
delete o[key];
key = newkey;
}
if (typeof (o[key]) === "object") {
this.collapsobject(o[key]);
}
}
}
expandobject(o) {
const keys = Object.keys(o);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
if (key.startsWith("___")) {
let newkey = "$" + key.substr(3);
o[newkey] = o[key];
delete o[key];
key = newkey;
}
if (typeof (o[key]) === "object") {
this.expandobject(o[key]);
}
}
}
postload() {
if (this.model) {
this.expandobject(this.model);
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
this.fixtextarea()
}
async submit(): Promise {
try {
this.collapsobject(this.model);
if (this.model._id) {
await NoderedUtil.UpdateOne({ collectionname: this.collection, item: this.model });
} else {
await NoderedUtil.InsertOne({ collectionname: this.collection, item: this.model });
}
this.$location.path("/FormResources");
} catch (error) {
this.errormessage = error.message ? error.message : error;
}
if (!this.$scope.$$phase) { this.$scope.$apply(); }
}
fixtextarea() {
setTimeout(() => {
const tx = document.getElementsByTagName('textarea');
for (let i = 0; i < tx.length; i++) {
tx[i].setAttribute('style', 'height:' + (tx[i].scrollHeight) + 'px;overflow-y:hidden;');
}
}, 500);
}
}
export class EditFormCtrl extends entityCtrl