Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
370ec81c33 | ||
|
|
5e7df5e55c | ||
|
|
cfca5bfe3b |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pulse-signage",
|
"name": "pulse-signage",
|
||||||
"version": "1.4.2",
|
"version": "1.4.5",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "Pulse Signage application with MySQL and media uploads",
|
"description": "Pulse Signage application with MySQL and media uploads",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -202,6 +202,9 @@ function getLegacyPermissionTargets(permissionKey) {
|
|||||||
|
|
||||||
const sectionKey = parts[0];
|
const sectionKey = parts[0];
|
||||||
const actionKey = parts[1];
|
const actionKey = parts[1];
|
||||||
|
if (normalizedKey === 'screens.allow') {
|
||||||
|
return ['clients.allow'];
|
||||||
|
}
|
||||||
if (actionKey === 'view') {
|
if (actionKey === 'view') {
|
||||||
return [`${sectionKey}.read`];
|
return [`${sectionKey}.read`];
|
||||||
}
|
}
|
||||||
@@ -265,7 +268,7 @@ async function backfillLegacyRbacSchema(pool) {
|
|||||||
for (const row of rolePermissionRows || []) {
|
for (const row of rolePermissionRows || []) {
|
||||||
const currentKey = getPermissionKey(row);
|
const currentKey = getPermissionKey(row);
|
||||||
const targetKeys = getLegacyPermissionTargets(currentKey);
|
const targetKeys = getLegacyPermissionTargets(currentKey);
|
||||||
if (currentKey.endsWith('.view') || currentKey.endsWith('.manage')) {
|
if (currentKey.endsWith('.view') || currentKey.endsWith('.manage') || currentKey === 'screens.allow') {
|
||||||
for (const targetKey of targetKeys) {
|
for (const targetKey of targetKeys) {
|
||||||
addRoleTarget(Number(row.role_id), targetKey);
|
addRoleTarget(Number(row.role_id), targetKey);
|
||||||
}
|
}
|
||||||
@@ -276,7 +279,7 @@ async function backfillLegacyRbacSchema(pool) {
|
|||||||
|
|
||||||
for (const row of permissionRows || []) {
|
for (const row of permissionRows || []) {
|
||||||
const currentKey = getPermissionKey(row);
|
const currentKey = getPermissionKey(row);
|
||||||
if (currentKey.endsWith('.view') || currentKey.endsWith('.manage')) {
|
if (currentKey.endsWith('.view') || currentKey.endsWith('.manage') || currentKey === 'screens.allow') {
|
||||||
legacyPermissionRowIds.push(Number(row.id));
|
legacyPermissionRowIds.push(Number(row.id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ const PERMISSION_SECTIONS = [
|
|||||||
actions: [
|
actions: [
|
||||||
{ key: 'read', name: 'Read', description: 'View the screen list and open screen details.' },
|
{ key: 'read', name: 'Read', description: 'View the screen list and open screen details.' },
|
||||||
{ key: 'create', name: 'Create', description: 'Create new screens.' },
|
{ key: 'create', name: 'Create', description: 'Create new screens.' },
|
||||||
{ key: 'edit', name: 'Update', description: 'Edit screens and send screen commands.' },
|
{ key: 'edit', name: 'Update', description: 'Edit screens.' },
|
||||||
{ key: 'delete', name: 'Delete', description: 'Delete screens.' }
|
{ key: 'delete', name: 'Delete', description: 'Delete screens.' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
+19
-1
@@ -12,7 +12,7 @@ const registerAdminPagesRoutes = require('./web/routes/admin-pages');
|
|||||||
const registerAdminAccountRoutes = require('./web/routes/admin-account');
|
const registerAdminAccountRoutes = require('./web/routes/admin-account');
|
||||||
const registerAdminUsersRoutes = require('./web/routes/admin-users');
|
const registerAdminUsersRoutes = require('./web/routes/admin-users');
|
||||||
const registerAdminManageRoutes = require('./web/routes/admin-manage');
|
const registerAdminManageRoutes = require('./web/routes/admin-manage');
|
||||||
const registerAdminScreenCommandRoutes = require('./web/routes/admin-screen-commands');
|
const registerAdminScreenCommandRoutes = require('./web/routes/admin-client-commands');
|
||||||
const registerAdminContentRoutes = require('./web/routes/admin-content');
|
const registerAdminContentRoutes = require('./web/routes/admin-content');
|
||||||
const { createWebBootstrap } = require('./web/bootstrap');
|
const { createWebBootstrap } = require('./web/bootstrap');
|
||||||
const { requirePermission } = require('./rbac');
|
const { requirePermission } = require('./rbac');
|
||||||
@@ -225,6 +225,24 @@ async function start() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
const pathName = String(req.originalUrl || '');
|
||||||
|
const wantsHtml = !pathName.startsWith('/api/') && (!req.accepts || req.accepts('html'));
|
||||||
|
|
||||||
|
if (!wantsHtml) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(404).send(pages.renderErrorPage({
|
||||||
|
statusCode: 404,
|
||||||
|
title: 'Not found',
|
||||||
|
errorTitle: 'Oops! Page not found.',
|
||||||
|
message: 'We could not find the page you were looking for.',
|
||||||
|
backUrl: req.currentUser ? '/admin' : '/login',
|
||||||
|
backLabel: req.currentUser ? 'Back to dashboard' : 'Sign in'
|
||||||
|
}, req.currentUser));
|
||||||
|
});
|
||||||
|
|
||||||
app.use(function (error, req, res, _next) {
|
app.use(function (error, req, res, _next) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const statusCode = Number(error && (error.statusCode || error.status)) || 500;
|
const statusCode = Number(error && (error.statusCode || error.status)) || 500;
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
var reloadConfirmMessage = 'Reloading will restart the player page. Continue?';
|
var reloadConfirmMessage = 'Reloading will restart the player page. Continue?';
|
||||||
var blackoutCommandValue = blackout ? 'false' : 'true';
|
var blackoutCommandValue = blackout ? 'false' : 'true';
|
||||||
|
|
||||||
return '<div class="d-flex flex-wrap gap-1"><form method="post" action="/admin/screens/' + encodeURIComponent(client.screen_slug) + '/commands" class="d-inline-block m-0" data-confirm-message="' + escapeHtml(reloadConfirmMessage) + '" data-async-command><input type="hidden" name="command" value="reload" /><input type="hidden" name="connectionId" value="' + escapeHtml(client.id) + '" /><button type="submit" class="btn btn-sm btn-danger" data-action="reload"><i class="bi bi-arrow-repeat me-1" aria-hidden="true"></i>Reload</button></form><form method="post" action="/admin/screens/' + encodeURIComponent(client.screen_slug) + '/commands" class="d-inline-block m-0" data-async-command><input type="hidden" name="command" value="previous" /><input type="hidden" name="connectionId" value="' + escapeHtml(client.id) + '" /><button type="submit" class="btn btn-sm btn-outline-secondary" data-action="previous" aria-label="Previous slide"><i class="bi bi-skip-backward-fill" aria-hidden="true"></i></button></form><form method="post" action="/admin/screens/' + encodeURIComponent(client.screen_slug) + '/commands" class="d-inline-block m-0" data-async-command><input type="hidden" name="command" value="next" /><input type="hidden" name="connectionId" value="' + escapeHtml(client.id) + '" /><button type="submit" class="btn btn-sm btn-outline-secondary" data-action="next" aria-label="Next slide"><i class="bi bi-skip-forward-fill" aria-hidden="true"></i></button></form><form method="post" action="/admin/screens/' + encodeURIComponent(client.screen_slug) + '/commands" class="d-inline-block m-0" data-async-command><input type="hidden" name="command" value="pause" /><input type="hidden" name="connectionId" value="' + escapeHtml(client.id) + '" /><button type="submit" class="' + pauseButtonClass + '" data-action="pause"><i class="bi bi-' + pauseButtonIcon + ' me-1" aria-hidden="true"></i>' + pauseButtonLabel + '</button></form><form method="post" action="/admin/screens/' + encodeURIComponent(client.screen_slug) + '/commands" class="d-inline-block m-0" data-async-command><input type="hidden" name="command" value="blackout" /><input type="hidden" name="blackout" value="' + blackoutCommandValue + '" /><input type="hidden" name="connectionId" value="' + escapeHtml(client.id) + '" /><button type="submit" class="' + blackoutButtonClass + '" data-action="blackout"><i class="bi bi-' + blackoutButtonIcon + ' me-1" aria-hidden="true"></i>' + blackoutButtonLabel + '</button></form></div>';
|
return '<div class="d-flex flex-wrap gap-1"><form method="post" action="/admin/clients/' + encodeURIComponent(client.screen_slug) + '/commands" class="d-inline-block m-0" data-confirm-message="' + escapeHtml(reloadConfirmMessage) + '" data-async-command><input type="hidden" name="command" value="reload" /><input type="hidden" name="connectionId" value="' + escapeHtml(client.id) + '" /><button type="submit" class="btn btn-sm btn-danger" data-action="reload"><i class="bi bi-arrow-repeat me-1" aria-hidden="true"></i>Reload</button></form><form method="post" action="/admin/clients/' + encodeURIComponent(client.screen_slug) + '/commands" class="d-inline-block m-0" data-async-command><input type="hidden" name="command" value="previous" /><input type="hidden" name="connectionId" value="' + escapeHtml(client.id) + '" /><button type="submit" class="btn btn-sm btn-outline-secondary" data-action="previous" aria-label="Previous slide"><i class="bi bi-skip-backward-fill" aria-hidden="true"></i></button></form><form method="post" action="/admin/clients/' + encodeURIComponent(client.screen_slug) + '/commands" class="d-inline-block m-0" data-async-command><input type="hidden" name="command" value="next" /><input type="hidden" name="connectionId" value="' + escapeHtml(client.id) + '" /><button type="submit" class="btn btn-sm btn-outline-secondary" data-action="next" aria-label="Next slide"><i class="bi bi-skip-forward-fill" aria-hidden="true"></i></button></form><form method="post" action="/admin/clients/' + encodeURIComponent(client.screen_slug) + '/commands" class="d-inline-block m-0" data-async-command><input type="hidden" name="command" value="pause" /><input type="hidden" name="connectionId" value="' + escapeHtml(client.id) + '" /><button type="submit" class="' + pauseButtonClass + '" data-action="pause"><i class="bi bi-' + pauseButtonIcon + ' me-1" aria-hidden="true"></i>' + pauseButtonLabel + '</button></form><form method="post" action="/admin/clients/' + encodeURIComponent(client.screen_slug) + '/commands" class="d-inline-block m-0" data-async-command><input type="hidden" name="command" value="blackout" /><input type="hidden" name="blackout" value="' + blackoutCommandValue + '" /><input type="hidden" name="connectionId" value="' + escapeHtml(client.id) + '" /><button type="submit" class="' + blackoutButtonClass + '" data-action="blackout"><i class="bi bi-' + blackoutButtonIcon + ' me-1" aria-hidden="true"></i>' + blackoutButtonLabel + '</button></form></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateClientActionCell(cell, client) {
|
function updateClientActionCell(cell, client) {
|
||||||
@@ -119,7 +119,7 @@
|
|||||||
if (connectionInput) {
|
if (connectionInput) {
|
||||||
connectionInput.value = client.id || '';
|
connectionInput.value = client.id || '';
|
||||||
}
|
}
|
||||||
pauseForm.action = '/admin/screens/' + encodeURIComponent(client.screen_slug) + '/commands';
|
pauseForm.action = '/admin/clients/' + encodeURIComponent(client.screen_slug) + '/commands';
|
||||||
}
|
}
|
||||||
|
|
||||||
var reloadButton = cell.querySelector('button[data-action="reload"]');
|
var reloadButton = cell.querySelector('button[data-action="reload"]');
|
||||||
@@ -132,7 +132,7 @@
|
|||||||
if (reloadInput) {
|
if (reloadInput) {
|
||||||
reloadInput.value = client.id || '';
|
reloadInput.value = client.id || '';
|
||||||
}
|
}
|
||||||
reloadForm.action = '/admin/screens/' + encodeURIComponent(client.screen_slug) + '/commands';
|
reloadForm.action = '/admin/clients/' + encodeURIComponent(client.screen_slug) + '/commands';
|
||||||
reloadForm.setAttribute('data-confirm-message', 'Reloading will restart the player page. Continue?');
|
reloadForm.setAttribute('data-confirm-message', 'Reloading will restart the player page. Continue?');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,7 +162,7 @@
|
|||||||
if (blackoutConnectionInput) {
|
if (blackoutConnectionInput) {
|
||||||
blackoutConnectionInput.value = client.id || '';
|
blackoutConnectionInput.value = client.id || '';
|
||||||
}
|
}
|
||||||
blackoutForm.action = '/admin/screens/' + encodeURIComponent(client.screen_slug) + '/commands';
|
blackoutForm.action = '/admin/clients/' + encodeURIComponent(client.screen_slug) + '/commands';
|
||||||
}
|
}
|
||||||
|
|
||||||
var previousButton = cell.querySelector('button[data-action="previous"]');
|
var previousButton = cell.querySelector('button[data-action="previous"]');
|
||||||
@@ -182,7 +182,7 @@
|
|||||||
if (previousConnectionInput) {
|
if (previousConnectionInput) {
|
||||||
previousConnectionInput.value = client.id || '';
|
previousConnectionInput.value = client.id || '';
|
||||||
}
|
}
|
||||||
previousForm.action = '/admin/screens/' + encodeURIComponent(client.screen_slug) + '/commands';
|
previousForm.action = '/admin/clients/' + encodeURIComponent(client.screen_slug) + '/commands';
|
||||||
}
|
}
|
||||||
|
|
||||||
var nextButton = cell.querySelector('button[data-action="next"]');
|
var nextButton = cell.querySelector('button[data-action="next"]');
|
||||||
@@ -202,10 +202,32 @@
|
|||||||
if (nextConnectionInput) {
|
if (nextConnectionInput) {
|
||||||
nextConnectionInput.value = client.id || '';
|
nextConnectionInput.value = client.id || '';
|
||||||
}
|
}
|
||||||
nextForm.action = '/admin/screens/' + encodeURIComponent(client.screen_slug) + '/commands';
|
nextForm.action = '/admin/clients/' + encodeURIComponent(client.screen_slug) + '/commands';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function syncClientActionCell(row, client, hasActionsColumn) {
|
||||||
|
if (!row || !row.cells) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasActionsColumn) {
|
||||||
|
if (row.cells.length > 6) {
|
||||||
|
row.deleteCell(row.cells.length - 1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var actionCell = row.cells.length > 6 ? row.cells[6] : null;
|
||||||
|
if (!actionCell) {
|
||||||
|
actionCell = row.insertCell(-1);
|
||||||
|
actionCell.setAttribute('data-label', 'Actions');
|
||||||
|
actionCell.className = 'text-end';
|
||||||
|
}
|
||||||
|
|
||||||
|
updateClientActionCell(actionCell, client);
|
||||||
|
}
|
||||||
|
|
||||||
function renderClientRow(client, hasActionsColumn) {
|
function renderClientRow(client, hasActionsColumn) {
|
||||||
var connectedAt = client.connectedAt ? '<div>' + escapeHtml(client.connectedAtLabel || formatDashboardDate(client.connectedAt) || client.connectedAt) + '</div>' + (client.lastSeenAt ? '<div class="subtle">' + escapeHtml(client.lastSeenAtLabel || formatDashboardDate(client.lastSeenAt) || client.lastSeenAt) + '</div>' : '') : '<span class="empty">Unknown</span>';
|
var connectedAt = client.connectedAt ? '<div>' + escapeHtml(client.connectedAtLabel || formatDashboardDate(client.connectedAt) || client.connectedAt) + '</div>' + (client.lastSeenAt ? '<div class="subtle">' + escapeHtml(client.lastSeenAtLabel || formatDashboardDate(client.lastSeenAt) || client.lastSeenAt) + '</div>' : '') : '<span class="empty">Unknown</span>';
|
||||||
var clientIpValue = normalizeDisplayIp(client.clientIp);
|
var clientIpValue = normalizeDisplayIp(client.clientIp);
|
||||||
@@ -317,9 +339,7 @@
|
|||||||
row.cells[3].innerHTML = clientIp;
|
row.cells[3].innerHTML = clientIp;
|
||||||
row.cells[4].innerHTML = viewport;
|
row.cells[4].innerHTML = viewport;
|
||||||
row.cells[5].innerHTML = connectedAt;
|
row.cells[5].innerHTML = connectedAt;
|
||||||
if (hasActionsColumn && row.cells.length >= 7) {
|
syncClientActionCell(row, client, hasActionsColumn);
|
||||||
updateClientActionCell(row.cells[6], client);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var referenceNode = tbody.children[index] || null;
|
var referenceNode = tbody.children[index] || null;
|
||||||
@@ -396,7 +416,7 @@
|
|||||||
body.append('deviceId', String(deviceId || '').trim());
|
body.append('deviceId', String(deviceId || '').trim());
|
||||||
body.append('clientName', String(clientName || '').trim());
|
body.append('clientName', String(clientName || '').trim());
|
||||||
|
|
||||||
return fetch('/admin/screens/' + encodeURIComponent(String(screenSlug || '').trim()) + '/commands', {
|
return fetch('/admin/clients/' + encodeURIComponent(String(screenSlug || '').trim()) + '/commands', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
|
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ module.exports = function registerAdminScreenCommandRoutes(app, deps) {
|
|||||||
const withClientNameReservation = deps.withClientNameReservation;
|
const withClientNameReservation = deps.withClientNameReservation;
|
||||||
const requirePermission = deps.requirePermission;
|
const requirePermission = deps.requirePermission;
|
||||||
|
|
||||||
app.post('/admin/screens/:slug/commands', requirePermission('clients.allow'), async function (req, res, next) {
|
app.post('/admin/clients/:slug/commands', requirePermission('clients.allow'), async function (req, res, next) {
|
||||||
try {
|
try {
|
||||||
const slug = String(req.params.slug || '').trim();
|
const slug = String(req.params.slug || '').trim();
|
||||||
const command = String((req.body && req.body.command) || req.query.command || '').trim().toLowerCase();
|
const command = String((req.body && req.body.command) || req.query.command || '').trim().toLowerCase();
|
||||||
@@ -234,7 +234,14 @@ module.exports = function registerAdminRbacRoutes(app, deps) {
|
|||||||
return res.status(404).send('Role not found.');
|
return res.status(404).send('Role not found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
res.send(pages.renderRbacEditPage(viewModel.role, req.query.message ? String(req.query.message) : '', req.currentUser, viewModel.permissionGroups, viewModel.users));
|
const currentUserId = req.currentUser ? Number(req.currentUser.id) : null;
|
||||||
|
const users = Array.isArray(viewModel.users)
|
||||||
|
? viewModel.users.filter(function (user) {
|
||||||
|
return Number(user && user.id) !== currentUserId;
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
res.send(pages.renderRbacEditPage(viewModel.role, req.query.message ? String(req.query.message) : '', req.currentUser, viewModel.permissionGroups, users));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,29 +60,29 @@
|
|||||||
{{#if (hasPermission currentUser 'clients.allow')}}
|
{{#if (hasPermission currentUser 'clients.allow')}}
|
||||||
<td data-label="Actions" class="text-end">
|
<td data-label="Actions" class="text-end">
|
||||||
<div class="actions justify-content-end">
|
<div class="actions justify-content-end">
|
||||||
<form method="post" action="/admin/screens/{{screen_slug}}/commands" class="inline-form" data-confirm-message="Reloading will restart the player page. Continue?" data-async-command>
|
<form method="post" action="/admin/clients/{{screen_slug}}/commands" class="inline-form" data-confirm-message="Reloading will restart the player page. Continue?" data-async-command>
|
||||||
<input type="hidden" name="command" value="reload" />
|
<input type="hidden" name="command" value="reload" />
|
||||||
<input type="hidden" name="connectionId" value="{{id}}" />
|
<input type="hidden" name="connectionId" value="{{id}}" />
|
||||||
<button type="submit" class="btn btn-sm btn-danger">Reload</button>
|
<button type="submit" class="btn btn-sm btn-danger">Reload</button>
|
||||||
</form>
|
</form>
|
||||||
<form method="post" action="/admin/screens/{{screen_slug}}/commands" class="inline-form" data-async-command>
|
<form method="post" action="/admin/clients/{{screen_slug}}/commands" class="inline-form" data-async-command>
|
||||||
<input type="hidden" name="command" value="previous" />
|
<input type="hidden" name="command" value="previous" />
|
||||||
<input type="hidden" name="connectionId" value="{{id}}" />
|
<input type="hidden" name="connectionId" value="{{id}}" />
|
||||||
<button type="submit" class="btn btn-sm btn-warning" aria-label="Previous slide">◀</button>
|
<button type="submit" class="btn btn-sm btn-warning" aria-label="Previous slide">◀</button>
|
||||||
</form>
|
</form>
|
||||||
<form method="post" action="/admin/screens/{{screen_slug}}/commands" class="inline-form" data-async-command>
|
<form method="post" action="/admin/clients/{{screen_slug}}/commands" class="inline-form" data-async-command>
|
||||||
<input type="hidden" name="command" value="next" />
|
<input type="hidden" name="command" value="next" />
|
||||||
<input type="hidden" name="connectionId" value="{{id}}" />
|
<input type="hidden" name="connectionId" value="{{id}}" />
|
||||||
<button type="submit" class="btn btn-sm btn-warning" aria-label="Next slide">▶</button>
|
<button type="submit" class="btn btn-sm btn-warning" aria-label="Next slide">▶</button>
|
||||||
</form>
|
</form>
|
||||||
<form method="post" action="/admin/screens/{{screen_slug}}/commands" class="inline-form" data-async-command>
|
<form method="post" action="/admin/clients/{{screen_slug}}/commands" class="inline-form" data-async-command>
|
||||||
<input type="hidden" name="command" value="pause" />
|
<input type="hidden" name="command" value="pause" />
|
||||||
<input type="hidden" name="connectionId" value="{{id}}" />
|
<input type="hidden" name="connectionId" value="{{id}}" />
|
||||||
<button type="submit" class="btn btn-sm btn-info">
|
<button type="submit" class="btn btn-sm btn-info">
|
||||||
<i class="bi bi-pause-fill me-1" aria-hidden="false"></i>Pause
|
<i class="bi bi-pause-fill me-1" aria-hidden="false"></i>Pause
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<form method="post" action="/admin/screens/{{screen_slug}}/commands" class="inline-form" data-async-command>
|
<form method="post" action="/admin/clients/{{screen_slug}}/commands" class="inline-form" data-async-command>
|
||||||
<input type="hidden" name="command" value="blackout" />
|
<input type="hidden" name="command" value="blackout" />
|
||||||
<input type="hidden" name="connectionId" value="{{id}}" />
|
<input type="hidden" name="connectionId" value="{{id}}" />
|
||||||
<button type="submit" class="btn btn-sm {{#if blackout}}btn-success{{else}}btn-secondary{{/if}}">
|
<button type="submit" class="btn btn-sm {{#if blackout}}btn-success{{else}}btn-secondary{{/if}}">
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
<td>
|
<td>
|
||||||
<input class="form-check-input" type="checkbox" name="user_ids[]" value="{{id}}" {{#if isSelected}}checked{{/if}} />
|
<input class="form-check-input" type="checkbox" name="user_ids[]" value="{{id}}" {{#if isSelected}}checked{{/if}} />
|
||||||
</td>
|
</td>
|
||||||
<td><strong>{{name}}</strong></td>
|
<td>{{name}}</td>
|
||||||
<td class="text-body-secondary">{{username}}</td>
|
<td class="text-body-secondary">{{username}}</td>
|
||||||
<td class="text-body-secondary">{{#if roleNames}}{{roleNames}}{{else}}No roles assigned{{/if}}</td>
|
<td class="text-body-secondary">{{#if roleNames}}{{roleNames}}{{else}}No roles assigned{{/if}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
Reference in New Issue
Block a user