unclean SPARC
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
<style>
|
||||
.data-explorer .results {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 480px;
|
||||
background: var(--eicui-base-color-grey);
|
||||
resize:vertical;
|
||||
}
|
||||
.data-explorer .results .detail {
|
||||
position: absolute;
|
||||
left: var(--eicui-base-spacing-2xs);
|
||||
top: var(--eicui-base-spacing-2xs);
|
||||
}
|
||||
.data-explorer .results .detail article {
|
||||
resize: horizontal;
|
||||
min-width: 240px;
|
||||
}
|
||||
.data-explorer .results .detail article footer:empty:before {
|
||||
content: "(no entity relation)";
|
||||
text-align: center;
|
||||
font-size: smaller;
|
||||
color: var(--eicui-base-color-grey);
|
||||
}
|
||||
.data-explorer .results .detail article > section {
|
||||
padding: 0;
|
||||
max-height: 320px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.data-explorer .results .detail article > footer { grid-auto-flow: row; }
|
||||
.data-explorer .results .detail article > section [eicdatagrid] { font-size: small; }
|
||||
.data-explorer .results svg .entity {
|
||||
cursor:pointer;
|
||||
transition: all 0.7s;
|
||||
}
|
||||
.data-explorer .results svg .entity .icon {
|
||||
font-family: 'glyphs';
|
||||
font-size: 24px;
|
||||
}
|
||||
.data-explorer .results svg .entity .bg {
|
||||
fill: var(--app-color-white);
|
||||
transition: all 0.7s;
|
||||
}
|
||||
.data-explorer .results svg .entity text {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
text-align: center;
|
||||
fill: var(--app-color-black);
|
||||
stroke-width: 0.1;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
transition: all 0.7s;
|
||||
}
|
||||
.data-explorer .results svg .entity .title { font-size: 14px; font-weight: bold; }
|
||||
.data-explorer .results svg .entity .type { font-size: 11px; }
|
||||
.data-explorer .results svg .relation {
|
||||
fill: none;
|
||||
stroke: var(--app-color-white);
|
||||
stroke-width: 3px;
|
||||
transition: all 0.7s;
|
||||
}
|
||||
.data-explorer .results svg .entity.selected .bg { fill: var(--app-color-primary); }
|
||||
.data-explorer .results svg .entity.selected text {
|
||||
fill: var(--app-color-white);
|
||||
}
|
||||
.data-explorer .results svg .related { stroke: var(--app-color-accent); }
|
||||
.data-explorer .results svg .entity.related .bg { fill: var(--app-color-accent); }
|
||||
.data-explorer .results svg .entity.related text { stroke: var(--app-color-black); fill: var(--app-color-black); }
|
||||
</style>
|
||||
<article eiccard class="data-explorer">
|
||||
<header>
|
||||
<h1>MarkLogic™ Data Storage Explorer</h1>
|
||||
<h2></h2>
|
||||
</header>
|
||||
<section>
|
||||
<div class="filters">
|
||||
<input eicinput type="search" name="q" placeholder="full text search" />
|
||||
<select eicselect name="attributes" editable placeholder="add some filters..."></select>
|
||||
|
||||
</div>
|
||||
<div class="results">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"></svg>
|
||||
<div class="detail" placeholder="select an entity"></div>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
@@ -0,0 +1,406 @@
|
||||
/**
|
||||
* n00014p5
|
||||
*/
|
||||
class DataExplorerView extends EICDomContent {
|
||||
|
||||
options = {
|
||||
entity: {
|
||||
width: 140,
|
||||
height: 70,
|
||||
gap: 20,
|
||||
dragging: false,
|
||||
current: null
|
||||
},
|
||||
map: {
|
||||
position: { x: 0, y: 0 },
|
||||
size: { width: 0, height: 0 },
|
||||
dragging: false,
|
||||
},
|
||||
scale: {
|
||||
current: 1.0,
|
||||
factor: 0.085,
|
||||
min: 0.5,
|
||||
max: 4
|
||||
},
|
||||
guides: true
|
||||
}
|
||||
|
||||
filtersList = [];
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.components = ui.eicfy(this.el);
|
||||
|
||||
this.q = this.components.find(c => c.el.name == 'q');
|
||||
this.q.onQuery = this.onQuery.bind(this);
|
||||
|
||||
this.attributes = this.components.find(c => c.el.name == 'attributes');
|
||||
this.attributes.activeEditor = DataExplorerFilter;
|
||||
this.attributes.onEditorOpened = this.fillFilters.bind(this);
|
||||
this.attributes.el.addEventListener( 'change' , this.onQuery.bind(this));
|
||||
|
||||
this.results = this.find('.results');
|
||||
this.viewboxPosition = {x: 0, y: 0};
|
||||
|
||||
this.map = this.find('.results svg');
|
||||
this.map.addEventListener('wheel', this.onMapScroll.bind(this));
|
||||
this.map.addEventListener('mousedown', this.onMapDragStart.bind(this));
|
||||
this.map.addEventListener('mousemove', this.onMapDragMove.bind(this));
|
||||
this.mouseUpHandler = this.onMapDragStop.bind(this);
|
||||
this.entityUpHandler = this.onEntityDragStop.bind(this);
|
||||
// observe map viewport resizing
|
||||
new ResizeObserver(this.onMapResize.bind(this)).observe(this.find('.results '))
|
||||
|
||||
this.ml.filters().then(payload => { this.filtersList = payload; })
|
||||
}
|
||||
|
||||
clearMap() {
|
||||
this.map.innerHTML = '';
|
||||
this.entities = [];
|
||||
}
|
||||
|
||||
clearDetail() { this.find('.detail').innerHTML = ''; }
|
||||
|
||||
fillFilters(editor) {
|
||||
let select = editor.el.querySelector('select');
|
||||
|
||||
for(let field of this.filtersList) {
|
||||
let option = ui.create(`<option value="${field.name}">${field.name}</option>`)
|
||||
select.append(option)
|
||||
}
|
||||
}
|
||||
|
||||
fillMap(data) {
|
||||
this.clearMap();
|
||||
this.clearDetail();
|
||||
|
||||
this.viewboxSize = {
|
||||
x: this.find('.results').clientWidth,
|
||||
y: this.find('.results').clientHeight
|
||||
};
|
||||
|
||||
this.viewboxStartPosition = {x: 0, y: 0};
|
||||
this.options.map.dragging = false;
|
||||
|
||||
for(let entity of data.entities) this.addEntity(entity);
|
||||
for(let relation of data.relations) this.addRelation(relation);
|
||||
|
||||
this.reorderMap();
|
||||
}
|
||||
|
||||
addEntity(entity) {
|
||||
const width = this.options.entity.width;
|
||||
const height = this.options.entity.height;
|
||||
const gap = this.options.entity.gap;
|
||||
|
||||
let template = this.ml.getType(entity._entity_type);
|
||||
let index = this.entities.length;
|
||||
let content = ui.create(
|
||||
`<svg class="entity" x="${gap + (width + gap) * index}" y="30" width="${width}" text-anchor="middle" data-id="${entity.id}">
|
||||
<rect class="bg" width="${width}" height="${height}" x="2" y="5" rx="10"></rect>
|
||||
<text class="icon" x="${width / 2}" y="32">${template.icon}</text>
|
||||
<text class="title" x="${width / 2}" y="50">${entity._entity_label}</text>
|
||||
<text class="type" x="${width / 2}" y="65">${template.label}</text>
|
||||
</svg>`
|
||||
);
|
||||
content.addEventListener('click', this.onEntitySelect.bind(this));
|
||||
content.addEventListener('mousedown', this.onEntityDragStart.bind(this));
|
||||
content.addEventListener('mousemove', this.onEntityMove.bind(this));
|
||||
this.entities.push({ el: content, data: entity, lines: [] });
|
||||
this.map.append(content);
|
||||
}
|
||||
|
||||
addRelation(relation) {
|
||||
let o1 = this.entities.find(item => item.data.id == relation.source);
|
||||
let o2 = this.entities.find(item => item.data.id == relation.target);
|
||||
|
||||
if(!o1 || !o2) return;
|
||||
|
||||
let line = ui.create(
|
||||
`<svg><line class="relation"
|
||||
x1="${o1.el.getAttribute('x')}" y1="${o1.el.getAttribute('y')}"
|
||||
x2="${o2.el.getAttribute('x')}" y2="${o2.el.getAttribute('y')}"
|
||||
data-source="${o1.data.id}" data-target="${o2.data.id}" /></svg>`
|
||||
).querySelector('line');
|
||||
this.map.prepend(line);
|
||||
|
||||
o1.lines.push(line)
|
||||
o2.lines.push(line)
|
||||
}
|
||||
|
||||
reorderMap() {
|
||||
let a = (Math.PI * 2) / this.entities.length;
|
||||
let r = (this.options.entity.width) * Math.log(this.entities.length);
|
||||
let i = 0;
|
||||
let center = { x: this.viewboxSize.x / 2, y: this.viewboxSize.y / 2 }
|
||||
|
||||
for(let entity of this.entities) {
|
||||
let pos = {
|
||||
x: center.x + (Math.cos(a * i - Math.PI * .5) * r) - (this.options.entity.width / 2),
|
||||
y: center.y + (Math.sin(a * i - Math.PI * .5) * r) - (this.options.entity.height / 2)
|
||||
}
|
||||
entity.el.setAttribute('x', pos.x)
|
||||
entity.el.setAttribute('y', pos.y)
|
||||
|
||||
for(let line of entity.lines) {
|
||||
if(line.getAttribute("data-source") == entity.data.id) {
|
||||
line.setAttribute("x1", pos.x + (this.options.entity.width / 2));
|
||||
line.setAttribute("y1", pos.y + (this.options.entity.height / 2));
|
||||
} else {
|
||||
line.setAttribute("x2", pos.x + (this.options.entity.width / 2));
|
||||
line.setAttribute("y2", pos.y + (this.options.entity.height / 2));
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if(this.entities.length > 0 && this.options.guides) {
|
||||
let guide = ui.create(`<svg><circle stroke="#888" fill="none" stroke-width="1" stroke-dasharray="5,5" cx="${center.x}" cy="${center.y}" r="${r}" /></svg>`)
|
||||
this.map.prepend(guide.querySelector('circle'));
|
||||
}
|
||||
}
|
||||
|
||||
setEntityDetail(entity) {
|
||||
let template = this.ml.getType(entity._entity_type);
|
||||
|
||||
let el = ui.create(`<article eiccard primary>
|
||||
<header><h1>${entity._entity_label}</h1><h2 small>${template.label}</h2></header>
|
||||
<section>
|
||||
<div eicdatagrid footer="hidden"></div>
|
||||
</section>
|
||||
<footer></footer>
|
||||
</article>`);
|
||||
|
||||
let footer = el.querySelector('footer');
|
||||
|
||||
let props = new DataGrid(el.querySelector('[eicdatagrid]'), {
|
||||
headers: [
|
||||
{label: 'property', filter:'text'},
|
||||
{label: 'value', filter:'text'},
|
||||
],
|
||||
height: '270px'
|
||||
});
|
||||
|
||||
this.setEntityProp(props, entity, 'id', null, entity._entity_type)
|
||||
|
||||
|
||||
for(let prop in entity.data) {
|
||||
//if(['_entity_type', '_entity_label'].indexOf(prop) == -1) {
|
||||
this.setEntityProp(props, entity.data, prop, null, entity._entity_type)
|
||||
//}
|
||||
}
|
||||
|
||||
let lines = this.entities.find(item => item.data.id == entity.id).lines;
|
||||
|
||||
if(lines.length > 0) {
|
||||
footer.append(ui.create(`<div xsmall>Relates locally to:</small>`))
|
||||
let ul = ui.create(`<ul bulleted small></ul>`)
|
||||
footer.append(ul);
|
||||
|
||||
for(let line of lines) {
|
||||
let other = this.entities.find(item => (item.data.id == line.getAttribute('data-target') && line.getAttribute('data-source') == entity.id) || (item.data.id == line.getAttribute('data-source') && line.getAttribute('data-target') == entity.id))
|
||||
let template = this.ml.getType(other.data._entity_type);
|
||||
let li = ui.create(`<li data-id="${other.data.id}"><a href="#">${other.data._entity_label} (${template.label})</a></li>`);
|
||||
li.addEventListener('click', this.onEntitySelect.bind(this))
|
||||
ul.append(li)
|
||||
}
|
||||
}
|
||||
|
||||
this.clearDetail();
|
||||
this.find('.detail').append(el);
|
||||
}
|
||||
|
||||
setEntityProp(table, parent, prop, prefix, type) {
|
||||
if(typeof parent[prop] !== 'object') {
|
||||
let allowSearch = ['id', 'email', 'euLogin', 'pic'].indexOf(prop) != -1;
|
||||
let value = allowSearch ?
|
||||
`<a data-query="${type}:${prop}:${parent[prop]}" href="#">${parent[prop]}</a>` :
|
||||
parent[prop];
|
||||
|
||||
let row = table.addRow('', [ (prefix ? prefix + '.': '') + prop, value ]);
|
||||
|
||||
if(allowSearch) {
|
||||
row.querySelector('.cell:nth-child(3) a').addEventListener('click', this.onQueryEntity.bind(this))
|
||||
}
|
||||
} else {
|
||||
for(let subprop in parent[prop])
|
||||
this.setEntityProp(table,parent[prop], subprop, (prefix ? prefix + '.': '') + prop, type)
|
||||
}
|
||||
}
|
||||
|
||||
onQuery(event) {
|
||||
|
||||
if(this.q.value.trim() == '' && this.attributes.value.length == 0) return;
|
||||
|
||||
let query = { freeText: this.q.value }
|
||||
|
||||
this.attributes.value.map(val => {
|
||||
let parts = val.split('=');
|
||||
if(parts.length > 1) query[parts[0]] = parts[1];
|
||||
})
|
||||
|
||||
this.ml.search(query).then( this.fillMap.bind(this));
|
||||
}
|
||||
|
||||
onQueryEntity(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.q.value = event.currentTarget.dataset.query;
|
||||
this.onQuery(event);
|
||||
}
|
||||
|
||||
onEntityDragStart(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.options.entity.current = event.currentTarget
|
||||
|
||||
this.mouseStartPosition = { x: event.pageX, y: event.pageY }
|
||||
this.entityStartPosition = { x: Number.parseFloat(this.options.entity.current.getAttribute("x")), y: Number.parseFloat(this.options.entity.current.getAttribute("y")) }
|
||||
|
||||
window.addEventListener("mouseup", this.entityUpHandler);
|
||||
|
||||
this.options.entity.dragging = true;
|
||||
}
|
||||
|
||||
onEntityMove(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.mousePosition = { x: event.offsetX, y: event.offsetY };
|
||||
|
||||
if(this.options.entity.dragging) {
|
||||
this.options.entity.current.setAttribute("x", this.entityStartPosition.x - (this.mouseStartPosition.x - event.pageX) * this.options.scale.current)
|
||||
this.options.entity.current.setAttribute("y", this.entityStartPosition.y - (this.mouseStartPosition.y - event.pageY) * this.options.scale.current)
|
||||
|
||||
let entity = this.entities.find(item => item.data.id == (this.options.entity.current.getAttribute("data-id")))
|
||||
let pos = { x: Number.parseFloat(this.options.entity.current.getAttribute("x")), y: Number.parseFloat(this.options.entity.current.getAttribute("y")) }
|
||||
|
||||
for(let line of entity.lines) {
|
||||
if(line.getAttribute("data-source") == entity.data.id) {
|
||||
line.setAttribute("x1", pos.x + (this.options.entity.width / 2));
|
||||
line.setAttribute("y1", pos.y + (this.options.entity.height / 2));
|
||||
} else {
|
||||
line.setAttribute("x2", pos.x + (this.options.entity.width / 2));
|
||||
line.setAttribute("y2", pos.y + (this.options.entity.height / 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onEntityDragStop(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
window.removeEventListener("mouseup", this.entityUpHandler);
|
||||
this.options.entity.dragging = false;
|
||||
}
|
||||
|
||||
onEntitySelect(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
let selected = this.entities.find(item => item.el.dataset.id == event.currentTarget.dataset.id);
|
||||
this.entities.forEach(item => {
|
||||
item.el.classList.remove('selected');
|
||||
item.el.classList.remove('related');
|
||||
item.lines.forEach(line => line.classList.remove('related'))
|
||||
});
|
||||
selected.el.classList.add('selected');
|
||||
selected.lines.forEach(line => {
|
||||
line.classList.add('related');
|
||||
let other = this.entities.find(item => (item.data.id == line.getAttribute('data-target') && line.getAttribute('data-source') == selected.data.id) || (item.data.id == line.getAttribute('data-source') && line.getAttribute('data-target') == selected.data.id));
|
||||
other.el.classList.add('related');
|
||||
});
|
||||
|
||||
this.setEntityDetail(selected.data);
|
||||
}
|
||||
|
||||
onMapScroll(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
let scale = 1. + (event.deltaY < 0 ? - this.options.scale.factor : this.options.scale.factor);
|
||||
|
||||
if((this.options.scale.current * scale < this.options.scale.max) && (this.options.scale.current * scale > this.options.scale.min))
|
||||
{
|
||||
let pos = {
|
||||
x: (this.mousePosition.x * this.options.scale.current) + this.viewboxPosition.x,
|
||||
y: (this.mousePosition.y * this.options.scale.current) + this.viewboxPosition.y
|
||||
}
|
||||
|
||||
this.viewboxPosition.x = (this.viewboxPosition.x - pos.x) * scale + pos.x;
|
||||
this.viewboxPosition.y = (this.viewboxPosition.y - pos.y) * scale + pos.y;
|
||||
this.options.scale.current *= scale;
|
||||
|
||||
this.setViewbox();
|
||||
}
|
||||
}
|
||||
|
||||
onMapDragStart(event) {
|
||||
this.mouseStartPosition = { x: event.pageX, y: event.pageY }
|
||||
this.viewboxStartPosition = { x: this.viewboxPosition.x, y: this.viewboxPosition.y }
|
||||
this.options.map.dragging = true;
|
||||
|
||||
window.addEventListener("mouseup", this.mouseUpHandler);
|
||||
}
|
||||
|
||||
onMapDragMove(event) {
|
||||
this.mousePosition = { x: event.offsetX, y: event.offsetY };
|
||||
|
||||
if(this.options.map.dragging) {
|
||||
this.viewboxPosition.x = this.viewboxStartPosition.x + (this.mouseStartPosition.x - event.pageX) * this.options.scale.current;
|
||||
this.viewboxPosition.y = this.viewboxStartPosition.y + (this.mouseStartPosition.y - event.pageY) * this.options.scale.current;
|
||||
this.setViewbox();
|
||||
}
|
||||
}
|
||||
|
||||
onMapDragStop(event) {
|
||||
window.removeEventListener("mouseup", this.mouseUpHandler);
|
||||
this.options.map.dragging = false;
|
||||
}
|
||||
|
||||
onMapResize(event) {
|
||||
this.viewboxSize = {
|
||||
x: this.find('.results').clientWidth,
|
||||
y: this.find('.results').clientHeight
|
||||
};
|
||||
|
||||
this.setViewbox();
|
||||
}
|
||||
|
||||
setViewbox() { this.map.setAttribute("viewBox", this.viewboxPosition.x + " " + this.viewboxPosition.y + " " + (this.viewboxSize.x * this.options.scale.current) + " " + (this.viewboxSize.y * this.options.scale.current)); }
|
||||
}
|
||||
|
||||
class DataExplorerFilter extends SelectEditor {
|
||||
|
||||
_template = `<div class="eicui-select-editor cols-2">
|
||||
<select eicselect placeholder="select a field"></select><input eicinput data-type="ignore" placeholder="set a value" />
|
||||
</div>`;
|
||||
|
||||
open() {
|
||||
super.open();
|
||||
this.input = this.components.find(c => c.el.nodeName == 'INPUT')
|
||||
this.select = this.components.find(c => c.el.nodeName == 'SELECT')
|
||||
this.select.clear();
|
||||
}
|
||||
|
||||
initEvents() {
|
||||
this.el.querySelector('input').addEventListener('keypress', this.onKeyPressed.bind(this));
|
||||
this.el.querySelector('select').addEventListener('change', this.onFieldChange.bind(this) );
|
||||
}
|
||||
|
||||
onFieldChange(event) {
|
||||
this.input.disabled = this.select.value.length == 0;
|
||||
}
|
||||
|
||||
get valid() { return super.valid && this.el.querySelector('select').value != ''; }
|
||||
|
||||
get value() { return {value: this.el.querySelector('select').value + "=" + this.el.querySelector('input').value, label: this.el.querySelector('select').value + ' is "' + this.el.querySelector('input').value + '"' } }
|
||||
|
||||
}
|
||||
|
||||
app.registerClass('DataExplorerView', DataExplorerView);
|
||||
@@ -0,0 +1,82 @@
|
||||
<style>
|
||||
.admin-access-control > header {
|
||||
background: url(/app/assets/images/cards/eic_purple_banner.png);
|
||||
background-position: top;
|
||||
background-size: contain !important;
|
||||
background-repeat: no-repeat;
|
||||
background-color: #5336a3;
|
||||
border-bottom: 4px solid white !important;
|
||||
}
|
||||
.admin-access-control .platform-status { min-width:25vw; }
|
||||
.admin-access-control .platform-monitor {
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
grid-gap: var(--eicui-base-spacing-m);
|
||||
}
|
||||
.admin-access-control .platform-monitor .grants { text-align: center; line-height:150% }
|
||||
.admin-access-control .active-users-grid .row { grid-template-columns: 1fr 2fr 3fr 2fr 110px; }
|
||||
.admin-access-control .active-users-grid .actions > div {
|
||||
display: inline-grid;
|
||||
grid-gap: var(--eicui-base-spacing-2xs);
|
||||
grid-auto-flow: column;
|
||||
}
|
||||
.admin-access-control .active-users-grid .dataset .cell:nth-child(2) { text-align: center; }
|
||||
.admin-access-control .active-users-grid .dataset .cell:nth-child(5) { text-align: center; }
|
||||
.admin-access-control .active-users-grid .dataset .cell:nth-child(6) { text-align: right; }
|
||||
</style>
|
||||
<article eiccard media class="admin-access-control">
|
||||
<header>
|
||||
<h1>Users Access Management</h1>
|
||||
<h2>EIC Platform Administration</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div class="cols-2 left">
|
||||
<div>
|
||||
<article warning eiccard class="platform-status">
|
||||
<section class="platform-monitor">Unknown state</section>
|
||||
<footer>
|
||||
<div class="cols-2">
|
||||
<button small primary eicbutton class="start" data-trigger="onStartPlatform"><span>Free access</span></button>
|
||||
<button small danger eicbutton class="stop" data-trigger="onStopPlatform"><span>Restrict access</span></button>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
<article eiccard class="banned-users">
|
||||
<header>
|
||||
<h1>Banned users</h1>
|
||||
</header>
|
||||
<section>
|
||||
<div eicdatagrid class="banned-users-grid" xsmall></div>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="cols-1 right"><button eicbutton primary small disabled><span>Add</span></button></div>
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<article eiccard class="ws-users">
|
||||
<header>
|
||||
<div class="cols-2 right">
|
||||
<h1>Active user sessions</h1>
|
||||
<!--<button primary small eicbutton data-trigger="refreshUsers"><span>Refresh</span></button>-->
|
||||
</div>
|
||||
</header>
|
||||
<section>
|
||||
<div eicdatagrid class="active-users-grid" xsmall></div>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="actions cols-2">
|
||||
<div></div>
|
||||
<div class="cols-2">
|
||||
<button eicbutton class="kick-all" danger small data-trigger="onUsersKick"><span>Kick all</span></button>
|
||||
<button eicbutton class="message-all" primary small data-trigger="onUsersMessage"><span>Message all</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
@@ -0,0 +1,391 @@
|
||||
class AdminAccessControlView extends EICDomContent {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
Object.assign(this, app.helpers.basicDialogs)
|
||||
}
|
||||
|
||||
refreshRate = 1000;
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.components = ui.eicfy(this.el);
|
||||
|
||||
this.buttonStart = this.components.find(component => component.el.classList.contains('start'));
|
||||
this.buttonStop = this.components.find(component => component.el.classList.contains('stop'));
|
||||
|
||||
this.gridUsers = new DataGrid(this.find('.active-users-grid'), {
|
||||
headers: [
|
||||
{ label: 'Online', type:'markup', filter: 'list', sortable: true},
|
||||
{ label: 'EU Login', type:'markup', filter: 'text', sortable: true},
|
||||
{ label: 'Full name', type:'markup', filter: 'text', sortable: true},
|
||||
{ label: 'Last req.', type:'markup', filter: false, sortable: true},
|
||||
],
|
||||
height: '60vh'
|
||||
});
|
||||
|
||||
this.gridBannedUsers = new DataGrid(this.find('.banned-users-grid'), {
|
||||
headers: [ { label: 'EU Login', type:'markup', filter: 'text'} ],
|
||||
height: '40vh'
|
||||
});
|
||||
|
||||
this.initEvents();
|
||||
this.startAutoRefreshUser()
|
||||
this.startAutoRefreshPlatform()
|
||||
}
|
||||
|
||||
initEvents() {
|
||||
let triggers = this.components.filter(c => ![ '', undefined ].includes( c.el.dataset.trigger) );
|
||||
for(let element of triggers) {
|
||||
if(typeof this[element.el.dataset.trigger] === 'function') {
|
||||
element.click = this[element.el.dataset.trigger].bind(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOMContentRemoved(){
|
||||
this.stopAutoRefreshUser()
|
||||
this.stopAutoRefreshPlatform()
|
||||
}
|
||||
|
||||
DOMContentFocused(){
|
||||
this.startAutoRefreshUser()
|
||||
this.startAutoRefreshPlatform()
|
||||
}
|
||||
|
||||
DOMContentBlured(){
|
||||
this.stopAutoRefreshUser()
|
||||
this.stopAutoRefreshPlatform()
|
||||
}
|
||||
|
||||
startAutoRefreshPlatform(){
|
||||
if(this.refreshPlatformTo) return
|
||||
this.refreshPlatformTo = null
|
||||
this.refreshPlatformActive = true
|
||||
this.refreshPlatform()
|
||||
}
|
||||
|
||||
stopAutoRefreshPlatform(){
|
||||
if(this.refreshPlatformTo){
|
||||
clearTimeout(this.refreshPlatformTo)
|
||||
this.refreshPlatformTo = null
|
||||
}
|
||||
this.refreshPlatformActive = false
|
||||
}
|
||||
|
||||
refreshPlatform(){
|
||||
if(this.refreshPlatformTo){
|
||||
clearTimeout(this.refreshPlatformTo)
|
||||
this.refreshPlatformTo = null
|
||||
}
|
||||
this.platform.getPlatformStatus().then(this.fillPlatformState.bind(this))
|
||||
if(this.refreshPlatformActive) this.refreshPlatformTo = setTimeout(this.refreshPlatform.bind(this),this.refreshRate)
|
||||
}
|
||||
|
||||
startAutoRefreshUser(){
|
||||
if(this.refreshUserTo) return
|
||||
this.refreshUserTo = null
|
||||
this.refreshUserActive = true
|
||||
this.refreshUsers()
|
||||
}
|
||||
|
||||
stopAutoRefreshUser(){
|
||||
if(this.refreshUserTo){
|
||||
clearTimeout(this.refreshUserTo)
|
||||
this.refreshUserTo = null
|
||||
}
|
||||
this.refreshUserActive = false
|
||||
}
|
||||
|
||||
refreshUsers(event){
|
||||
|
||||
if(this.refreshUserTo){
|
||||
clearTimeout(this.refreshUserTo)
|
||||
this.refreshUserTo = null
|
||||
}
|
||||
this.platform.getUsers().then(this.fillUsers.bind(this))
|
||||
if(this.refreshUserActive) this.refreshUserTo = setTimeout(this.refreshUsers.bind(this), this.refreshRate)
|
||||
}
|
||||
|
||||
fillUsers(list) {
|
||||
if(!list) return
|
||||
// seriously ?
|
||||
const noKickRoles = ['EIC_Admin', 'EIC_Dev'] // just for display, anti-foot-shoot exists BE side
|
||||
|
||||
this.gridUsers.loading = false;
|
||||
this.gridUsers.clear()
|
||||
for(let item of list) {
|
||||
let lastAjax = 3600-item.sessionExpire
|
||||
lastAjax = lastAjax<60 ? `${lastAjax} sec` : `${Math.floor(lastAjax/60)} min ${lastAjax%60} sec`
|
||||
let row = this.gridUsers.addRow(item.uid, [
|
||||
`<span style="font-size:0">${item.busConnected ? 'Yes': 'No'}</span><span ${item.busConnected ? 'success': 'danger'} small class="icon-big-bullet"></span>`,
|
||||
item.uid,
|
||||
`<span title="${item.given_name + ' ' + item.family_name}">${item.given_name + ' ' + item.family_name}</span>`,
|
||||
`<span style="font-size:0">${item.sessionExpire}</span><span>${lastAjax}</span>`
|
||||
]);
|
||||
let buttonBar = ui.create(`<div></div>`)
|
||||
let btnMsg = ui.create(`<button eicbutton class="icon-coaching message" data-uid="${item.uid}" rounded primary xsmall title="send message"></button>`)
|
||||
btnMsg.addEventListener('click',this.onUserMessage.bind(this))
|
||||
buttonBar.appendChild(btnMsg);
|
||||
|
||||
let intersect = noKickRoles.filter(value => item.userRoles.includes(value));
|
||||
if(intersect.length==0){
|
||||
let btnKick = ui.create(`<button eicbutton class="icon-logoff kick" data-uid="${item.uid}" rounded warning xsmall title="Terminate session"></button>`)
|
||||
btnKick.addEventListener('click',this.onUserKick.bind(this))
|
||||
buttonBar.appendChild(btnKick);
|
||||
|
||||
let btnBan = ui.create(`<button eicbutton class="icon-locked ban" data-uid="${item.uid}" rounded danger xsmall title="Ban from the platform"></button>`)
|
||||
btnBan.addEventListener('click',this.onUserBan.bind(this))
|
||||
buttonBar.appendChild(btnBan);
|
||||
}
|
||||
|
||||
row.querySelector('.cell.actions').appendChild(buttonBar);
|
||||
}
|
||||
}
|
||||
|
||||
fillPlatformState(data) {
|
||||
let article = new Card(this.find('.platform-status'));
|
||||
let alert = this.find('.platform-status > section');
|
||||
|
||||
if(data) {
|
||||
this.fillBannedUsers(data.blockedUUIDs)
|
||||
if(data.platformRestrictions) {
|
||||
article.severity = 'danger';
|
||||
alert.innerHTML = `<span danger xlarge class="icon-locked"></span>
|
||||
<span danger><b>access restricted</b></span>
|
||||
<span success><b>granted access:</b></span>
|
||||
<div class="grants">
|
||||
${data.platformRestrictions.allowedRoles.map(item=>('<span info xsmall eicchip title="Group ' + item + ' allowed"><label>'+item+'</label></span>')).join('')}
|
||||
${data.platformRestrictions.allowedUUIDs.map(item=>('<span primary xsmall eicchip title="User ' + item + ' allowed">'+item+'</span>')).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
ui.eicfy(alert);
|
||||
//buttonsstop stays visible to allow changing allowed roles & uuids
|
||||
this.buttonStop.disabled = false;
|
||||
this.buttonStart.disabled = false;
|
||||
|
||||
} else {
|
||||
article.severity = 'success'
|
||||
alert.innerHTML = `<span success xlarge class="icon-unlocked"></span>
|
||||
<span success><b>no restrictions</b></span>`
|
||||
|
||||
this.buttonStop.disabled = false;
|
||||
this.buttonStart.disabled = true;
|
||||
}
|
||||
} else {
|
||||
article.severity = 'warning'
|
||||
alert.innerHTML = `<span warning xlarge class="icon-bug"></span>
|
||||
<span warning><b>status report unavailable</b></span>`
|
||||
this.buttonStart.disabled = true;
|
||||
this.buttonStop.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
fillDaemonsState(list){
|
||||
for(let daemon of Object.keys(list)){
|
||||
let led = this.find(`.status-${daemon}`)
|
||||
let tmstp = this.find(`.time-${daemon}`)
|
||||
if(led){
|
||||
led.innerHTML = `<span ${list[daemon] ? 'success': 'danger'} small class="icon-big-bullet"></span>`
|
||||
if(list[daemon]){
|
||||
tmstp.innerHTML = `${list[daemon] ? list[daemon].substr(11,8) : ''}`
|
||||
ui.show(tmstp, 'inline-flex')
|
||||
} else {
|
||||
ui.hide(tmstp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fillBannedUsers(list) {
|
||||
if(!list) return
|
||||
this.gridBannedUsers.clear()
|
||||
for(let uid of list) {
|
||||
let row = this.gridBannedUsers.addRow(uid, [
|
||||
uid,
|
||||
]);
|
||||
let buttonBar = ui.create(`<div></div>`)
|
||||
let btnUnban = ui.create(`<button eicbutton class="icon-unlocked unban" data-uid="${uid}" rounded primary xsmall title="Un-ban"></button>`)
|
||||
btnUnban.addEventListener('click',this.onUserUnban.bind(this))
|
||||
buttonBar.appendChild(btnUnban);
|
||||
row.querySelector('.cell.actions').appendChild(buttonBar);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async onStopPlatform(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
let uid = event.currentTarget.dataset.uid;
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'system/admin/platform-control/dialogs/PlatformDownDialog',
|
||||
{ title: `Restricting platform access` },
|
||||
{ platform: this.platform }
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
this.platform.stopPlatform(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async onStartPlatform(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.confirmDialog({
|
||||
title: 'Releasing platform restrictions',
|
||||
message: `<p>You are about to remove restrictions on the EIC platform.</p>
|
||||
<p>All users will be able to connect again.</p>`,
|
||||
okLabel: 'Free access',
|
||||
severity: 'primary',
|
||||
})
|
||||
|
||||
if(result) {
|
||||
this.platform.startPlatform(result);
|
||||
}
|
||||
}
|
||||
|
||||
onUsersUpdate(event) { this.fillUsers(event.data.users); }
|
||||
|
||||
/**
|
||||
* Growls a message to a user through bus
|
||||
*/
|
||||
async onUserMessage(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let uid = event.currentTarget.dataset.uid;
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'system/admin/platform-control/dialogs/PlatformUserMessageDialog',
|
||||
{ title: `Warn user ${uid}` },
|
||||
{ }
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
this.platform.growlOne(uid, result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Growls a message to all users through bus
|
||||
*/
|
||||
async onUsersMessage(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'system/admin/platform-control/dialogs/PlatformUserMessageDialog',
|
||||
{ title: `Warn user` },
|
||||
{ }
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
this.platform.growlGlobal(result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kicks a user though bus
|
||||
*/
|
||||
async onUserKick(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let uid = event.currentTarget.dataset.uid;
|
||||
let result = await this.confirmDialog({
|
||||
title: 'Kick user',
|
||||
message: `<p>You are about to kick user <b>${uid}</b> from the platform.</p>
|
||||
<p>All of his current activities will be terminated.</p>`,
|
||||
okLabel: 'Kick',
|
||||
severity: 'danger',
|
||||
okPromise: () => { return(this.platform.kick(uid)) }
|
||||
})
|
||||
|
||||
if(result) {
|
||||
ui.growl.append(`User ${uid} has been kicked !`, 'success', 10000)
|
||||
if(!this.refreshUserActive) this.refreshUsers()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kicks all users though bus
|
||||
*/
|
||||
async onUsersKick(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.confirmDialog({
|
||||
title: 'Kick all users',
|
||||
message: `<p>You are about to kick <b>ALL</b> users from the platform.</p>
|
||||
<p>All of their current activities will be terminated.</p>`,
|
||||
okLabel: 'Kick',
|
||||
severity: 'danger',
|
||||
okPromise: () => { return(this.platform.kickAll()) }
|
||||
})
|
||||
|
||||
if(result) {
|
||||
ui.growl.append(`All users (except Admins & Devs) have been kicked !`, 'success', 10000)
|
||||
if(!this.refreshUserActive) this.refreshUsers()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bans a user from the platform though bus
|
||||
*/
|
||||
async onUserBan(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let uid = event.currentTarget.dataset.uid;
|
||||
let result = await this.confirmDialog({
|
||||
title: 'Ban user',
|
||||
message: `<p>You are about to <b>ban</b> user <b>${uid}</b> from the platform.</p>
|
||||
<p>All of his current activities will be terminated
|
||||
and he won't be able to login again.</p>`,
|
||||
okLabel: 'Ban',
|
||||
severity: 'danger',
|
||||
okPromise: () => { return(this.platform.ban(uid)) }
|
||||
})
|
||||
|
||||
if(result) {
|
||||
ui.growl.append(`User ${uid} has been banned !`, 'success', 10000)
|
||||
if(!this.refreshPlatformActive) this.refreshPlatform()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bans a user from the platform though bus
|
||||
*/
|
||||
async onUserUnban(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let uid = event.currentTarget.dataset.uid;
|
||||
let result = await this.confirmDialog({
|
||||
title: 'Unban user',
|
||||
message: `<p>You are about to allow <b>${uid}</b> back on the platform.</p>`,
|
||||
okLabel: 'Unban',
|
||||
severity: 'success',
|
||||
okPromise: () => { return(this.platform.unban(uid)) }
|
||||
})
|
||||
|
||||
if(result) {
|
||||
ui.growl.append(`User ${uid} has been unbanned !`, 'success', 10000)
|
||||
if(!this.refreshPlatformActive) this.refreshPlatform()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
app.registerClass('AdminAccessControlView', AdminAccessControlView);
|
||||
@@ -0,0 +1,10 @@
|
||||
<style>
|
||||
.config-snipet{
|
||||
background-color: #000;
|
||||
padding: 5px;
|
||||
width: 100vh;
|
||||
}
|
||||
</style>
|
||||
<div class="system-message-form">
|
||||
<pre class="config-snipet"><code class="view-config language-json"></code></pre>
|
||||
</div>
|
||||
@@ -0,0 +1,27 @@
|
||||
class PlatformConfigViewDialog extends EICDialogContent {
|
||||
|
||||
actions = [
|
||||
{
|
||||
label: 'ok',
|
||||
onclick: this.confirm.bind(this),
|
||||
severity: 'primary',
|
||||
role: 'confirm'
|
||||
}
|
||||
]
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
ui.eicfy(this.el);
|
||||
this.codeBox = this.find('.view-config')
|
||||
let snipet = hljs.highlight(JSON.stringify(options.config, null, 4), { 'language':'json'} );
|
||||
this.codeBox.innerHTML = snipet.value
|
||||
}
|
||||
|
||||
confirm(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.commit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
app.registerClass('PlatformConfigViewDialog', PlatformConfigViewDialog);
|
||||
@@ -0,0 +1,18 @@
|
||||
<style>
|
||||
.system-message-form .allowed-users{ /* room for the popup-input */
|
||||
min-height: calc(8*var(--base-font-size));
|
||||
}
|
||||
</style>
|
||||
<div class="system-message-form">
|
||||
<p danger>You are about to restrict the EIC platform access.</p>
|
||||
<p>All users will be disconnected and unable to login anymore.</p>
|
||||
<p>Only specific roles and users selected below will still have access.</p>
|
||||
<div>
|
||||
<label>Allowed roles:</label>
|
||||
<select eicselect name="allowedRoles" multiple></select>
|
||||
</div>
|
||||
<div class="allowed-users">
|
||||
<label>Allowed EU-logins:</label>
|
||||
<select eicselect name="allowedUUIDs" editable data-type="array" class="" placeholder="Type EUlogin-ID and press enter" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,72 @@
|
||||
class PlatformDownDialog extends EICDialogContent {
|
||||
|
||||
actions = [
|
||||
{
|
||||
label: 'cancel',
|
||||
onclick: this.cancel.bind(this),
|
||||
severity: 'secondary',
|
||||
role: 'cancel'
|
||||
},
|
||||
{
|
||||
label: 'Restrict access',
|
||||
onclick: this.confirm.bind(this),
|
||||
severity: 'danger',
|
||||
role: 'confirm'
|
||||
}
|
||||
]
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
let components = ui.eicfy(this.el)
|
||||
|
||||
this.RolesSelect = components.find(component => component.el.name == 'allowedRoles')
|
||||
this.UUIDsSelect = components.find(component => component.el.name == 'allowedUUIDs')
|
||||
|
||||
options.platform.getPlatformStatus().then(this.fillSelects.bind(this))
|
||||
|
||||
this.form = new Form(this.el);
|
||||
}
|
||||
|
||||
fillSelects(data){
|
||||
let allowedRoles = []
|
||||
let allowedUUIDs = []
|
||||
if(data.platformRestrictions && Array.isArray(data.platformRestrictions.allowedRoles)) {
|
||||
allowedRoles = data.platformRestrictions.allowedRoles
|
||||
}
|
||||
if(data.platformRestrictions && Array.isArray(data.platformRestrictions.allowedUUIDs)) {
|
||||
allowedUUIDs = data.platformRestrictions.allowedUUIDs
|
||||
}
|
||||
|
||||
if(allowedRoles.indexOf('EIC_Dev')<0) allowedRoles.push('EIC_Dev') // Voluntarily hard-coded, other securities also BE-side
|
||||
|
||||
// Would be cool, but css issues & currently can't avoid getting the group item in the values.
|
||||
// let roleOptions = app.meta.toOptionsRecurse(app.meta.getCollection('user-roles'), allowedRoles, true)
|
||||
// this.RolesSelect.el.innerHTML = roleOptions ;
|
||||
|
||||
let RolesChildren = app.meta.getCollection('user-roles').reduce((acc, item)=>{ acc = [...acc, ...item.children]; return(acc)},[])
|
||||
let rolesOptions = app.meta.toOptions(RolesChildren, allowedRoles)
|
||||
rolesOptions.forEach(item => this.RolesSelect.el.append(item))
|
||||
//having several options selected (in a multiple) doesn't work when appending, but works on setting the value as an Array
|
||||
this.RolesSelect.value = allowedRoles
|
||||
this.UUIDsSelect.value = allowedUUIDs
|
||||
}
|
||||
|
||||
cancel(event){
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
this.RolesSelect.closeCatalog()
|
||||
super.cancel(event)
|
||||
}
|
||||
|
||||
confirm(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.RolesSelect.closeCatalog()
|
||||
this.actions.forEach(item=> {item.button.loading=true; item.button.disabled=true} )
|
||||
if(this.form.validate()) {
|
||||
this.commit(this.form.value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
app.registerClass('PlatformDownDialog', PlatformDownDialog);
|
||||
@@ -0,0 +1,32 @@
|
||||
<div class="system-message-form">
|
||||
<label>Please provide the message you want to send:</label>
|
||||
|
||||
<div>
|
||||
<label>Predefined messages:</label>
|
||||
<select eicselect name="predefined"></select>
|
||||
</div>
|
||||
|
||||
<div class="cols-2">
|
||||
<div>
|
||||
<label>severity:</label>
|
||||
<select eicselect name="growlSeverity">
|
||||
<option value="secondary">secondary</option>
|
||||
<option value="primary">primary</option>
|
||||
<option value="danger">danger</option>
|
||||
<option value="success">success</option>
|
||||
<option value="warning">warning</option>
|
||||
<option value="info">info</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>duration:</label>
|
||||
<input eicinput type="number" name="growlTime" value="5" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>message:</label>
|
||||
<textarea eictextarea name="growlMessage" data-path="" class="required" rows="6"></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,58 @@
|
||||
class PlatformUserMessageDialog extends EICDialogContent {
|
||||
|
||||
actions = [
|
||||
{
|
||||
label: 'cancel',
|
||||
onclick: this.cancel.bind(this),
|
||||
severity: 'secondary',
|
||||
role: 'cancel'
|
||||
},
|
||||
{
|
||||
label: 'send',
|
||||
onclick: this.confirm.bind(this),
|
||||
severity: 'primary',
|
||||
role: 'confirm'
|
||||
}
|
||||
]
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
let components = ui.eicfy(this.el);
|
||||
this.messagesSelect = components.find(component => component.el.name == 'predefined')
|
||||
this.growlSeverity = components.find(component => component.el.name == 'growlSeverity')
|
||||
this.growlTime = components.find(component => component.el.name == 'growlTime')
|
||||
this.growlMessage = components.find(component => component.el.name == 'growlMessage')
|
||||
|
||||
let messagesSelectOptions = app.meta.toOptions(app.meta.getCollection('user-messages'),[],true)
|
||||
messagesSelectOptions.forEach(item => this.messagesSelect.el.append(item))
|
||||
this.messagesSelect.change(this.onMessageChange.bind(this));
|
||||
this.form = new Form(this.el);
|
||||
}
|
||||
|
||||
confirm(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.actions.forEach(item=> {item.button.loading=true; item.button.disabled=true} )
|
||||
if(this.form.validate()) {
|
||||
this.commit(this.form.value);
|
||||
} else {
|
||||
this.actions.forEach(item=> {item.button.loading=false; item.button.disabled=false} )
|
||||
}
|
||||
}
|
||||
|
||||
onMessageChange(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if(this.messagesSelect.value) {
|
||||
let msgData = app.meta.getCollection('user-messages').find(item=>item.id==this.messagesSelect.value)
|
||||
this.growlSeverity.value = msgData.severity
|
||||
this.growlTime.value = msgData.duration
|
||||
this.growlMessage.value = msgData.message.join('\n')
|
||||
} else {
|
||||
this.growlSeverity.value = 'secondary'
|
||||
this.growlTime.value = 5
|
||||
this.growlMessage.value = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('PlatformUserMessageDialog', PlatformUserMessageDialog);
|
||||
Reference in New Issue
Block a user