' + esc(d.error || 'Could not load the grants') + '
';
+ return;
+ }
+ var active = chooseTarget ? d.active : d.grants.filter(function (g) { return g.status === 'active'; });
+ var history = chooseTarget ? d.history : d.grants;
+ el.querySelector('[data-rows="active"]').innerHTML = rows(active, { showTarget: chooseTarget, empty: 'Nothing granted or denied.' });
+ el.querySelector('[data-rows="history"]').innerHTML = rows(history, { showTarget: chooseTarget, empty: 'No grants yet.' });
+ if ((chooseTarget || d.canManage) && !formHost.firstChild) setUpForm();
+ });
+ }
+
+ function setUpForm() {
+ formHost.innerHTML = form(chooseTarget);
+ var f = formHost.querySelector('form');
+ var kind = f.querySelector('[data-kind]');
+ nameSelect = SearchSelect(f.querySelector('[data-name]'), {
+ search: function (text) {
+ return loadCatalog().then(function (d) { return choices(d, kind.value, text); }).catch(function (e) { toast(e.message, 'danger'); return []; });
+ }
+ });
+ kind.addEventListener('change', function () { nameSelect.set('', ''); });
+ if (chooseTarget) {
+ var type = f.querySelector('[data-target-type]');
+ targetSelect = SearchSelect(f.querySelector('[data-target]'), { search: function (text) { return searchTargets(type.value, text); } });
+ type.addEventListener('change', function () { targetSelect.set('', ''); });
+ }
+ f.addEventListener('submit', function (e) {
+ e.preventDefault();
+ var who = chooseTarget ? { type: f.querySelector('[data-target-type]').value, id: targetSelect.input.dataset.value } : target;
+ var name = nameSelect.input.dataset.value;
+ if (!who.id) return toast('Pick an account or character', 'warning');
+ if (!name) return toast('Pick what to grant', 'warning');
+ var expires = f.querySelector('[data-expires]').value;
+ var body = { targetType: who.type, target: who.id, kind: kind.value, name: name, deny: f.querySelector('[data-deny]').value === '1',
+ expiresAt: expires ? Math.floor(new Date(expires).getTime() / 1000) : 0, note: f.querySelector('[data-note]').value };
+ api.action('/api/grants', body).then(function (r) {
+ toast(r.message, 'success');
+ nameSelect.set('', '');
+ f.querySelector('[data-note]').value = '';
+ load();
+ }).catch(function () {});
+ });
+ }
+
+ el.addEventListener('click', function (e) {
+ var button = e.target.closest('[data-remove]');
+ if (!button || !confirm('Remove this grant? It stops applying at once.')) return;
+ api.action('/api/grants/' + button.dataset.remove + '/remove', {}).then(function (r) { toast(r.message, 'success'); load(); }).catch(function () {});
+ });
+ if (window.Live) Live.on('grants', Live.throttle(load, 500));
+ load();
+ }
+ };
+})();
diff --git a/dDashboardServer/templates/account-view.jinja2 b/dDashboardServer/templates/account-view.jinja2
index 264591ee7..6027da824 100644
--- a/dDashboardServer/templates/account-view.jinja2
+++ b/dDashboardServer/templates/account-view.jinja2
@@ -307,6 +307,16 @@
{% endif %}
+{% if is_self or can.grants_manage %}
+
+
Permission grants
+
+
Dashboard permissions and in-game commands given to{% if can.grants_manage %} (or taken from){% endif %} this account on top of what its GM level allows. They count on the dashboard and in game, on every character; each character can have its own too (on its page).
+
+
+
+{% endif %}
+
{% if is_self or (can.api_keys_manage and can.accounts_view and manage.tools) %}
+ {% if can.grants_manage or character.is_own %}
+
+
Permission grants
+
+
In-game commands and permissions given to{% if can.grants_manage %} (or taken from){% endif %} this character on top of its GM level, while it is logged in. The account's own grants count too.
+
+
+
+ {% endif %}
+
Loading related data…
{% endif %}
@@ -760,4 +770,11 @@ function rescueCharacter() {
+
+
{% endblock %}
diff --git a/dDashboardServer/templates/header.jinja2 b/dDashboardServer/templates/header.jinja2
index d3c838828..e4f526a3d 100644
--- a/dDashboardServer/templates/header.jinja2
+++ b/dDashboardServer/templates/header.jinja2
@@ -120,7 +120,7 @@
{% endif %}
{% set adminPages = ["settings", "settings_history", "permissions", "tasks", "play_keys", "backups", "webhooks", "client_assets", "ugc"] %}
- {% if can.settings or can.permissions_manage or can.tasks_view or can.play_keys_manage or can.backups or can.webhooks or can.client_files or can.ugc_manage %}
+ {% if can.settings or can.permissions_manage or can.grants_manage or can.tasks_view or can.play_keys_manage or can.backups or can.webhooks or can.client_files or can.ugc_manage %}