mirror of
https://github.com/avecms/AVE.cms.git
synced 2026-08-01 08:45:44 +00:00
693 lines
32 KiB
JavaScript
693 lines
32 KiB
JavaScript
(function (window, document) {
|
|
'use strict';
|
|
|
|
var Adminx = window.Adminx || (window.Adminx = {});
|
|
|
|
Adminx.PublicPresentations = {
|
|
form: null,
|
|
drawer: null,
|
|
dirty: false,
|
|
loading: false,
|
|
needsReload: false,
|
|
currentRevision: 0,
|
|
activeEditor: 'draft_item_markup',
|
|
targetCatalog: {},
|
|
|
|
init: function () {
|
|
this.form = document.getElementById('publicPresentationForm');
|
|
this.drawer = document.getElementById('publicPresentationDrawer');
|
|
if (!this.form || !this.drawer) { return; }
|
|
this.targetCatalog = this.readJson('[data-presentation-targets]');
|
|
this.targetTypeChanged();
|
|
var self = this;
|
|
|
|
document.addEventListener('click', function (event) {
|
|
var target = event.target;
|
|
if (target.closest('[data-presentation-new]')) { self.openNew(); return; }
|
|
var edit = target.closest('[data-presentation-edit]');
|
|
if (edit) { self.open(Number(edit.getAttribute('data-presentation-edit'))); return; }
|
|
var history = target.closest('[data-presentation-revisions]');
|
|
if (history) { self.open(Number(history.getAttribute('data-presentation-revisions')), 'revisions'); return; }
|
|
var copy = target.closest('[data-presentation-copy]');
|
|
if (copy) { self.copy(Number(copy.getAttribute('data-presentation-copy'))); return; }
|
|
var remove = target.closest('[data-presentation-delete]');
|
|
if (remove) { self.remove(Number(remove.getAttribute('data-presentation-delete'))); return; }
|
|
var tab = target.closest('[data-presentation-tab]');
|
|
if (tab) { self.tab(tab.getAttribute('data-presentation-tab')); return; }
|
|
if (target.closest('[data-presentation-save]')) { self.save().catch(function () {}); return; }
|
|
if (target.closest('[data-presentation-publish]')) { self.publish(); return; }
|
|
if (target.closest('[data-presentation-lint]')) { self.lint(); return; }
|
|
if (target.closest('[data-presentation-preview]')) { self.preview(); return; }
|
|
if (target.closest('[data-presentation-diagnose]')) { self.diagnose(); return; }
|
|
var fullscreen = target.closest('[data-presentation-fullscreen]');
|
|
if (fullscreen) { self.fullscreen(fullscreen.getAttribute('data-presentation-fullscreen')); return; }
|
|
var variable = target.closest('[data-presentation-variable]');
|
|
if (variable) { self.insertVariable(variable.getAttribute('data-presentation-variable')); return; }
|
|
if (target.closest('[data-presentation-assignment-save]')) { self.saveAssignment(); return; }
|
|
var assignmentDelete = target.closest('[data-presentation-assignment-delete]');
|
|
if (assignmentDelete) { self.deleteAssignment(Number(assignmentDelete.getAttribute('data-presentation-assignment-delete'))); return; }
|
|
var revision = target.closest('[data-presentation-revision]');
|
|
if (revision) { self.showRevision(Number(revision.getAttribute('data-presentation-revision'))); return; }
|
|
if (target.closest('[data-presentation-revision-restore]')) { self.restoreRevision(); return; }
|
|
if (target.closest('[data-presentation-revision-delete]')) { self.deleteRevision(); return; }
|
|
if (target.closest('[data-presentation-revisions-clear]')) { self.clearRevisions(); }
|
|
});
|
|
|
|
document.addEventListener('click', function (event) {
|
|
var close = event.target.closest('[data-close-drawer]');
|
|
if (!close || !self.drawer.contains(close)) { return; }
|
|
if (self.dirty) {
|
|
event.preventDefault();
|
|
event.stopImmediatePropagation();
|
|
self.confirmDiscard();
|
|
return;
|
|
}
|
|
if (self.needsReload) { window.location.reload(); }
|
|
}, true);
|
|
|
|
document.addEventListener('keydown', function (event) {
|
|
if ((event.ctrlKey || event.metaKey) && String(event.key).toLowerCase() === 's' && self.drawer.classList.contains('show')) {
|
|
event.preventDefault();
|
|
self.save().catch(function () {});
|
|
return;
|
|
}
|
|
if (event.key === 'Escape' && self.dirty && self.drawer.classList.contains('show')) {
|
|
event.preventDefault();
|
|
event.stopImmediatePropagation();
|
|
self.confirmDiscard();
|
|
}
|
|
}, true);
|
|
|
|
this.form.addEventListener('input', function (event) {
|
|
if (!self.loading && !event.target.closest('.public-presentation-assignment-form')) { self.setDirty(true); }
|
|
});
|
|
this.form.addEventListener('change', function (event) {
|
|
if (!self.loading && !event.target.closest('.public-presentation-assignment-form') && event.target.name !== 'preview_document_id') {
|
|
self.setDirty(true);
|
|
}
|
|
if (event.target.name === 'assignment_target_type') { self.targetTypeChanged(); }
|
|
});
|
|
this.form.addEventListener('submit', function (event) {
|
|
event.preventDefault();
|
|
self.save().catch(function () {});
|
|
});
|
|
setTimeout(function () { self.bindEditors(); }, 120);
|
|
},
|
|
|
|
base: function () { return this.form.getAttribute('data-base') || ''; },
|
|
resource: function () { return this.base() + '/public-site/presentations'; },
|
|
revisionResource: function () { return this.base() + '/public-site/presentation-revisions'; },
|
|
assignmentResource: function () { return this.base() + '/public-site/presentation-assignments'; },
|
|
id: function () { return Number(this.field('id').value) || 0; },
|
|
field: function (name) { return this.form.querySelector('[name="' + name + '"]'); },
|
|
editor: function (name) {
|
|
var field = this.field(name);
|
|
return field && field._adminxCodeMirror ? field._adminxCodeMirror : null;
|
|
},
|
|
|
|
request: function (url, options) {
|
|
return Adminx.Ajax.request(url, options || {}).then(function (response) {
|
|
var payload = response.data || {};
|
|
if (!response.ok || !payload.success) {
|
|
var error = new Error(payload.message || ('HTTP ' + response.status));
|
|
error.payload = payload;
|
|
throw error;
|
|
}
|
|
return payload;
|
|
});
|
|
},
|
|
|
|
bindEditors: function () {
|
|
var self = this;
|
|
['draft_item_markup', 'draft_wrapper_markup', 'draft_empty_markup', 'draft_css'].forEach(function (name) {
|
|
var editor = self.editor(name);
|
|
if (!editor || editor._publicPresentationBound) { return; }
|
|
editor._publicPresentationBound = true;
|
|
editor.on('focus', function () { self.activeEditor = name; });
|
|
editor.on('change', function () {
|
|
if (!self.loading) { self.setDirty(true); }
|
|
});
|
|
});
|
|
},
|
|
|
|
openNew: function () {
|
|
this.loading = true;
|
|
this.form.reset();
|
|
this.field('id').value = '';
|
|
this.field('title').value = 'Новое представление';
|
|
this.field('code').value = 'presentation_' + String(Date.now()).slice(-8);
|
|
this.field('kind').value = 'card';
|
|
this.setCode('draft_item_markup', this.defaultValue('item'));
|
|
this.setCode('draft_wrapper_markup', this.defaultValue('wrapper'));
|
|
this.setCode('draft_empty_markup', this.defaultValue('empty'));
|
|
this.setCode('draft_css', '');
|
|
this.loading = false;
|
|
this.needsReload = false;
|
|
this.currentRevision = 0;
|
|
this.setStatus({is_published: false, version: 0, has_changes: true});
|
|
this.renderAssignments([]);
|
|
this.clearRevisionView();
|
|
this.clearPreview();
|
|
this.clearDiagnostics();
|
|
this.setDirty(false);
|
|
this.tab('item');
|
|
Adminx.Drawer.open('publicPresentationDrawer');
|
|
this.refreshEditors();
|
|
},
|
|
|
|
open: function (id, panel) {
|
|
var self = this;
|
|
Adminx.Loader.show();
|
|
this.request(this.resource() + '/' + id).then(function (payload) {
|
|
self.fill(payload.data || {});
|
|
self.tab(panel || 'item');
|
|
Adminx.Drawer.open('publicPresentationDrawer');
|
|
self.refreshEditors();
|
|
if (panel === 'revisions') { self.loadRevisions(); }
|
|
}).catch(function (error) {
|
|
self.fail(error);
|
|
}).finally(function () {
|
|
Adminx.Loader.hide();
|
|
});
|
|
},
|
|
|
|
fill: function (item) {
|
|
this.loading = true;
|
|
['id', 'title', 'code', 'kind', 'description', 'draft_settings_json'].forEach(function (name) {
|
|
var field = this.field(name);
|
|
if (field) { field.value = item[name] == null ? (name === 'draft_settings_json' ? '{}' : '') : item[name]; }
|
|
}, this);
|
|
['draft_item_markup', 'draft_wrapper_markup', 'draft_empty_markup', 'draft_css'].forEach(function (name) {
|
|
this.setCode(name, item[name] || '');
|
|
}, this);
|
|
this.loading = false;
|
|
this.needsReload = false;
|
|
this.currentRevision = 0;
|
|
this.setStatus(item);
|
|
this.renderAssignments(item.assignments || []);
|
|
this.clearRevisionView();
|
|
this.clearPreview();
|
|
this.clearDiagnostics();
|
|
this.setDirty(false);
|
|
},
|
|
|
|
defaultValue: function (name) {
|
|
var field = document.querySelector('[data-presentation-default="' + name + '"]');
|
|
return field ? field.value : '';
|
|
},
|
|
|
|
setCode: function (name, value) {
|
|
var field = this.field(name), editor = this.editor(name);
|
|
if (field) { field.value = value || ''; }
|
|
if (editor) { editor.setValue(value || ''); }
|
|
},
|
|
|
|
sync: function () {
|
|
if (Adminx.CodeEditor) { Adminx.CodeEditor.syncAll(this.form); }
|
|
},
|
|
|
|
refreshEditors: function () {
|
|
this.bindEditors();
|
|
setTimeout(function () {
|
|
if (Adminx.CodeEditor) { Adminx.CodeEditor.refreshAll(); }
|
|
}, 80);
|
|
},
|
|
|
|
tab: function (name) {
|
|
this.drawer.querySelectorAll('[data-presentation-tab]').forEach(function (button) {
|
|
var active = button.getAttribute('data-presentation-tab') === name;
|
|
button.classList.toggle('is-active', active);
|
|
button.setAttribute('aria-selected', active ? 'true' : 'false');
|
|
});
|
|
this.drawer.querySelectorAll('[data-presentation-panel]').forEach(function (panel) {
|
|
panel.hidden = panel.getAttribute('data-presentation-panel') !== name;
|
|
});
|
|
if (name === 'item') { this.activeEditor = 'draft_item_markup'; }
|
|
if (name === 'wrapper') { this.activeEditor = 'draft_wrapper_markup'; }
|
|
if (name === 'empty') { this.activeEditor = 'draft_empty_markup'; }
|
|
if (name === 'css') { this.activeEditor = 'draft_css'; }
|
|
if (name === 'revisions') { this.loadRevisions(); }
|
|
this.refreshEditors();
|
|
},
|
|
|
|
save: function (quiet) {
|
|
var self = this;
|
|
this.sync();
|
|
var id = this.id();
|
|
this.setSaveState('Сохранение...', '');
|
|
return this.request(this.resource() + (id ? '/' + id : ''), {
|
|
method: 'POST',
|
|
body: new FormData(this.form)
|
|
}).then(function (payload) {
|
|
var savedId = Number((payload.data || {}).id) || id;
|
|
self.field('id').value = savedId;
|
|
self.needsReload = true;
|
|
self.setDirty(false);
|
|
self.setSaveState('Черновик сохранён', 'ok');
|
|
if (!quiet) { Adminx.Toast.show(payload.message || 'Черновик сохранён', 'success'); }
|
|
return savedId;
|
|
}).catch(function (error) {
|
|
self.setSaveState('Не сохранено', 'error');
|
|
self.fail(error);
|
|
throw error;
|
|
});
|
|
},
|
|
|
|
publish: function () {
|
|
var self = this;
|
|
this.save(true).then(function (id) {
|
|
var data = new FormData();
|
|
data.set('_csrf', Adminx.csrf());
|
|
return self.request(self.resource() + '/' + id + '/publish', {method: 'POST', body: data});
|
|
}).then(function (payload) {
|
|
self.setStatus(payload.data || {});
|
|
self.setSaveState('Опубликовано', 'ok');
|
|
self.needsReload = true;
|
|
Adminx.Toast.show(payload.message || 'Представление опубликовано', 'success');
|
|
}).catch(function () {});
|
|
},
|
|
|
|
lint: function () {
|
|
var self = this;
|
|
this.sync();
|
|
var data = new FormData(this.form);
|
|
this.setLint('Проверка...', '');
|
|
this.request(this.resource() + '/lint', {method: 'POST', body: data}).then(function (payload) {
|
|
self.setLint(payload.message || 'Twig-синтаксис корректен', 'ok');
|
|
}).catch(function (error) {
|
|
self.setLint(error.message, 'error');
|
|
});
|
|
},
|
|
|
|
preview: function () {
|
|
var self = this;
|
|
this.sync();
|
|
Adminx.Loader.show();
|
|
this.request(this.resource() + '/preview', {method: 'POST', body: new FormData(this.form)}).then(function (payload) {
|
|
var result = payload.data || {};
|
|
self.drawer.querySelector('[data-presentation-preview-frame]').srcdoc = result.html || '';
|
|
self.drawer.querySelector('[data-presentation-preview-json]').textContent = result.json || '{}';
|
|
var state = self.drawer.querySelector('[data-presentation-preview-state] span');
|
|
if (state) {
|
|
state.textContent = result.document && result.document.title
|
|
? 'Показан материал «' + result.document.title + '» · ID ' + result.document.id
|
|
: 'Предпросмотр обновлён.';
|
|
}
|
|
}).catch(function (error) {
|
|
self.fail(error);
|
|
}).finally(function () {
|
|
Adminx.Loader.hide();
|
|
});
|
|
},
|
|
|
|
clearPreview: function () {
|
|
var frame = this.drawer.querySelector('[data-presentation-preview-frame]');
|
|
if (frame) { frame.removeAttribute('srcdoc'); }
|
|
var json = this.drawer.querySelector('[data-presentation-preview-json]');
|
|
if (json) { json.textContent = 'После запуска здесь появятся данные, доступные шаблону.'; }
|
|
var state = this.drawer.querySelector('[data-presentation-preview-state] span');
|
|
if (state) { state.textContent = 'Выберите материал и запустите предпросмотр.'; }
|
|
},
|
|
|
|
fullscreen: function (name) {
|
|
var editor = this.editor(name);
|
|
if (editor && Adminx.CodeEditor) { Adminx.CodeEditor.toggleFullscreen(editor); }
|
|
},
|
|
|
|
insertVariable: function (value) {
|
|
var editorName = this.activeEditor === 'draft_css' ? 'draft_item_markup' : this.activeEditor;
|
|
var tab = {draft_item_markup: 'item', draft_wrapper_markup: 'wrapper', draft_empty_markup: 'empty'}[editorName] || 'item';
|
|
this.tab(tab);
|
|
var editor = this.editor(editorName);
|
|
if (editor) {
|
|
editor.replaceSelection(value || '');
|
|
editor.focus();
|
|
editor.save();
|
|
}
|
|
},
|
|
|
|
saveAssignment: function () {
|
|
var id = this.id(), self = this;
|
|
if (!id) {
|
|
Adminx.Toast.show('Сначала сохраните представление', 'warning');
|
|
return;
|
|
}
|
|
var data = new FormData();
|
|
data.set('_csrf', Adminx.csrf());
|
|
data.set('context_code', this.field('assignment_context').value);
|
|
data.set('target_type', this.field('assignment_target_type').value);
|
|
data.set('target_key', this.field('assignment_target_key').value || '0');
|
|
data.set('mode', this.field('assignment_mode').value);
|
|
this.request(this.resource() + '/' + id + '/assignments', {method: 'POST', body: data}).then(function (payload) {
|
|
self.renderAssignments((payload.data || {}).assignments || []);
|
|
self.needsReload = true;
|
|
Adminx.Toast.show(payload.message || 'Назначение сохранено', 'success');
|
|
}).catch(function (error) {
|
|
self.fail(error);
|
|
});
|
|
},
|
|
|
|
deleteAssignment: function (id) {
|
|
var self = this;
|
|
Adminx.Confirm.open({
|
|
kind: 'warning',
|
|
title: 'Удалить назначение?',
|
|
message: 'Само представление и публичный шаблон не изменятся.',
|
|
confirmLabel: 'Удалить',
|
|
onConfirm: function () {
|
|
var data = new FormData();
|
|
data.set('_csrf', Adminx.csrf());
|
|
self.request(self.assignmentResource() + '/' + id + '/delete', {method: 'POST', body: data}).then(function () {
|
|
self.reloadAssignments();
|
|
self.needsReload = true;
|
|
}).catch(function (error) {
|
|
self.fail(error);
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
reloadAssignments: function () {
|
|
var self = this, id = this.id();
|
|
if (!id) { return; }
|
|
this.request(this.resource() + '/' + id).then(function (payload) {
|
|
self.renderAssignments((payload.data || {}).assignments || []);
|
|
}).catch(function (error) {
|
|
self.fail(error);
|
|
});
|
|
},
|
|
|
|
renderAssignments: function (items) {
|
|
var list = this.drawer.querySelector('[data-presentation-assignment-list]');
|
|
var contexts = {
|
|
content_list: 'Список материалов',
|
|
compact_list: 'Компактный список',
|
|
slider: 'Слайдер',
|
|
search_results: 'Результаты поиска',
|
|
related: 'Похожие материалы',
|
|
favorites: 'Избранное',
|
|
viewed: 'Просмотренные материалы',
|
|
catalog_filters: 'Фильтры каталога',
|
|
document_page: 'Страница материала'
|
|
};
|
|
if (!items.length) {
|
|
list.innerHTML = '<div class="empty-state">Назначений пока нет.</div>';
|
|
return;
|
|
}
|
|
var self = this;
|
|
list.innerHTML = items.map(function (item) {
|
|
var modeClass = item.mode === 'native' ? 'badge-green' : (item.mode === 'preview' ? 'badge-amber' : 'badge-gray');
|
|
var targetClass = item.target_available === false ? ' is-missing' : '';
|
|
var target = '<span class="public-presentation-assignment-target' + targetClass + '"><b>' + self.escape(item.target_title || item.target_key) + '</b><small>' + self.escape(item.target_meta || '') + '</small></span>';
|
|
if (item.target_url) {
|
|
target = '<a class="public-presentation-assignment-target" href="' + self.escape(self.base() + item.target_url) + '"><b>' + self.escape(item.target_title || item.target_key) + '</b><small>' + self.escape(item.target_meta || '') + '</small></a>';
|
|
}
|
|
return '<div class="public-presentation-assignment"><span><b>' + self.escape(contexts[item.context_code] || item.context_code) + '</b><small>' + self.escape(item.updated_label || '') + '</small></span>' + target + '<span class="badge ' + modeClass + '">' + self.escape(item.mode_label || item.mode) + '</span><button class="btn btn-ghost btn-icon btn-sm is-danger" type="button" data-presentation-assignment-delete="' + Number(item.id) + '" aria-label="Удалить"><i class="ti ti-trash"></i></button></div>';
|
|
}).join('');
|
|
},
|
|
|
|
targetTypeChanged: function () {
|
|
var type = this.field('assignment_target_type').value || 'global';
|
|
var select = this.field('assignment_target_key');
|
|
var current = select.value;
|
|
var items = this.targetCatalog[type] || [];
|
|
var self = this;
|
|
select.innerHTML = items.map(function (item) {
|
|
return '<option value="' + self.escape(item.key) + '">' + self.escape(item.title) + ' · ' + self.escape(item.meta) + '</option>';
|
|
}).join('');
|
|
if (!items.length) {
|
|
select.innerHTML = '<option value="">Доступных целей нет</option>';
|
|
select.disabled = true;
|
|
} else {
|
|
select.disabled = false;
|
|
select.value = items.some(function (item) { return String(item.key) === String(current); }) ? current : items[0].key;
|
|
}
|
|
var hint = this.drawer.querySelector('[data-presentation-target-hint]');
|
|
if (hint) {
|
|
hint.textContent = type === 'global'
|
|
? 'Общее назначение для всех подходящих мест.'
|
|
: (items.length ? 'Выберите цель по названию. ID вводить не нужно.' : 'Сначала создайте или включите подходящую цель.');
|
|
}
|
|
},
|
|
|
|
diagnose: function () {
|
|
var self = this;
|
|
this.save(true).then(function (id) {
|
|
self.sync();
|
|
self.setDiagnosticState('Проверяем...', 'loading');
|
|
return self.request(self.resource() + '/' + id + '/diagnose', {
|
|
method: 'POST',
|
|
body: new FormData(self.form)
|
|
});
|
|
}).then(function (payload) {
|
|
self.renderDiagnostics(payload.data || {});
|
|
self.tab('diagnostics');
|
|
}).catch(function (error) {
|
|
self.setDiagnosticState('Проверка не выполнена', 'error');
|
|
self.fail(error);
|
|
});
|
|
},
|
|
|
|
renderDiagnostics: function (result) {
|
|
var summary = result.summary || {};
|
|
var list = this.drawer.querySelector('[data-presentation-diagnostic-list]');
|
|
var self = this;
|
|
this.setDiagnosticState(
|
|
result.ready
|
|
? (Number(summary.warning || 0) > 0
|
|
? 'Ошибок нет · предупреждений: ' + Number(summary.warning || 0)
|
|
: 'Готово к публикации')
|
|
: 'Ошибок: ' + Number(summary.error || 0) + ' · предупреждений: ' + Number(summary.warning || 0),
|
|
result.ready ? (Number(summary.warning || 0) > 0 ? 'warning' : 'ok') : 'error'
|
|
);
|
|
list.innerHTML = (result.checks || []).map(function (check) {
|
|
var icon = check.status === 'ok' ? 'ti-circle-check' : (check.status === 'error' ? 'ti-alert-circle' : 'ti-alert-triangle');
|
|
var action = check.url ? '<a class="btn btn-ghost btn-sm" href="' + self.escape(self.base() + check.url) + '"><i class="ti ti-arrow-up-right"></i>Открыть</a>' : '';
|
|
return '<article class="public-presentation-diagnostic is-' + self.escape(check.status) + '"><i class="ti ' + icon + '"></i><span><b>' + self.escape(check.title) + '</b><small>' + self.escape(check.message) + '</small></span>' + action + '</article>';
|
|
}).join('') || '<div class="empty-state">Результатов проверки нет.</div>';
|
|
},
|
|
|
|
clearDiagnostics: function () {
|
|
this.setDiagnosticState('Проверка ещё не запускалась', '');
|
|
var list = this.drawer.querySelector('[data-presentation-diagnostic-list]');
|
|
if (list) { list.innerHTML = '<div class="empty-state">Сохраните представление и запустите диагностику.</div>'; }
|
|
},
|
|
|
|
setDiagnosticState: function (message, state) {
|
|
var summary = this.drawer.querySelector('[data-presentation-diagnostic-summary]');
|
|
if (!summary) { return; }
|
|
var badge = state === 'ok'
|
|
? 'badge-green'
|
|
: (state === 'warning' ? 'badge-amber' : (state === 'error' ? 'badge-red' : (state === 'loading' ? 'badge-blue' : 'badge-gray')));
|
|
summary.innerHTML = '<span class="badge ' + badge + '">' + this.escape(message || '') + '</span>';
|
|
},
|
|
|
|
copy: function (id) {
|
|
var self = this;
|
|
Adminx.Confirm.open({
|
|
kind: 'info',
|
|
title: 'Создать копию?',
|
|
message: 'Будет создан новый неопубликованный черновик без назначений.',
|
|
confirmLabel: 'Создать копию',
|
|
onConfirm: function () {
|
|
var data = new FormData();
|
|
data.set('_csrf', Adminx.csrf());
|
|
self.request(self.resource() + '/' + id + '/copy', {method: 'POST', body: data}).then(function () {
|
|
window.location.reload();
|
|
}).catch(function (error) {
|
|
self.fail(error);
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
remove: function (id) {
|
|
var self = this;
|
|
Adminx.Confirm.open({
|
|
kind: 'error',
|
|
title: 'Удалить представление?',
|
|
message: 'Черновик, опубликованная версия и ревизии будут удалены. Назначенное представление удалить нельзя.',
|
|
confirmLabel: 'Удалить',
|
|
confirmClass: 'btn-danger',
|
|
onConfirm: function () {
|
|
var data = new FormData();
|
|
data.set('_csrf', Adminx.csrf());
|
|
self.request(self.resource() + '/' + id + '/delete', {method: 'POST', body: data}).then(function () {
|
|
window.location.reload();
|
|
}).catch(function (error) {
|
|
self.fail(error);
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
loadRevisions: function () {
|
|
var id = this.id(), self = this;
|
|
var list = this.drawer.querySelector('[data-presentation-revision-list]');
|
|
if (!id) {
|
|
list.innerHTML = '<div class="empty-state">Сначала сохраните представление.</div>';
|
|
return;
|
|
}
|
|
list.innerHTML = '<div class="empty-state">Загрузка...</div>';
|
|
this.request(this.resource() + '/' + id + '/revisions').then(function (payload) {
|
|
var items = (payload.data || {}).revisions || [];
|
|
self.drawer.querySelector('[data-presentation-revisions-count]').textContent = items.length ? ('Снимков: ' + items.length) : 'Снимков пока нет.';
|
|
self.drawer.querySelector('[data-presentation-revisions-clear]').disabled = !items.length;
|
|
list.innerHTML = items.length ? items.map(function (item) {
|
|
return '<button type="button" data-presentation-revision="' + Number(item.id) + '"><span><b>' + self.escape(item.action_label) + '</b><small>' + self.escape(item.comment || item.author_name || '') + '</small></span><span><em class="badge ' + self.escape(item.badge) + '">' + self.escape(item.action_label) + '</em><small class="mono">' + self.escape(item.created_label) + '</small></span></button>';
|
|
}).join('') : '<div class="empty-state">Ревизий пока нет.</div>';
|
|
}).catch(function (error) {
|
|
self.fail(error);
|
|
});
|
|
},
|
|
|
|
showRevision: function (id) {
|
|
var self = this;
|
|
this.request(this.revisionResource() + '/' + id).then(function (payload) {
|
|
var item = payload.data || {}, snapshot = item.snapshot || {};
|
|
self.currentRevision = Number(item.id) || 0;
|
|
self.drawer.querySelectorAll('[data-presentation-revision]').forEach(function (button) {
|
|
button.classList.toggle('is-active', Number(button.getAttribute('data-presentation-revision')) === self.currentRevision);
|
|
});
|
|
self.drawer.querySelector('[data-presentation-revision-meta]').innerHTML = '<b>' + self.escape(item.action_label || '') + '</b><small>' + self.escape(item.created_label || '') + ' · ' + self.escape(item.author_name || 'system') + '</small>';
|
|
self.drawer.querySelector('[data-presentation-revision-code]').textContent = snapshot.draft_item_markup || '';
|
|
self.drawer.querySelector('[data-presentation-revision-restore]').disabled = false;
|
|
self.drawer.querySelector('[data-presentation-revision-delete]').disabled = false;
|
|
}).catch(function (error) {
|
|
self.fail(error);
|
|
});
|
|
},
|
|
|
|
restoreRevision: function () {
|
|
if (!this.currentRevision) { return; }
|
|
var self = this, revision = this.currentRevision;
|
|
Adminx.Confirm.open({
|
|
kind: 'warning',
|
|
title: 'Восстановить черновик?',
|
|
message: 'Опубликованная версия публичного сайта не изменится.',
|
|
confirmLabel: 'Восстановить',
|
|
onConfirm: function () {
|
|
var data = new FormData();
|
|
data.set('_csrf', Adminx.csrf());
|
|
self.request(self.revisionResource() + '/' + revision + '/restore', {method: 'POST', body: data}).then(function () {
|
|
self.needsReload = true;
|
|
self.open(self.id(), 'revisions');
|
|
Adminx.Toast.show('Ревизия восстановлена в черновик', 'success');
|
|
}).catch(function (error) {
|
|
self.fail(error);
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
deleteRevision: function () {
|
|
if (!this.currentRevision) { return; }
|
|
var self = this, data = new FormData();
|
|
data.set('_csrf', Adminx.csrf());
|
|
this.request(this.revisionResource() + '/' + this.currentRevision + '/delete', {method: 'POST', body: data}).then(function () {
|
|
self.clearRevisionView();
|
|
self.loadRevisions();
|
|
}).catch(function (error) {
|
|
self.fail(error);
|
|
});
|
|
},
|
|
|
|
clearRevisions: function () {
|
|
var self = this, id = this.id();
|
|
if (!id) { return; }
|
|
Adminx.Confirm.open({
|
|
kind: 'error',
|
|
title: 'Удалить все ревизии?',
|
|
message: 'Текущий черновик и опубликованная версия сохранятся.',
|
|
confirmLabel: 'Удалить все',
|
|
confirmClass: 'btn-danger',
|
|
onConfirm: function () {
|
|
var data = new FormData();
|
|
data.set('_csrf', Adminx.csrf());
|
|
self.request(self.resource() + '/' + id + '/revisions/delete', {method: 'POST', body: data}).then(function () {
|
|
self.clearRevisionView();
|
|
self.loadRevisions();
|
|
}).catch(function (error) {
|
|
self.fail(error);
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
clearRevisionView: function () {
|
|
this.currentRevision = 0;
|
|
var meta = this.drawer.querySelector('[data-presentation-revision-meta]');
|
|
var code = this.drawer.querySelector('[data-presentation-revision-code]');
|
|
if (meta) { meta.textContent = 'Выберите снимок выше.'; }
|
|
if (code) { code.textContent = ''; }
|
|
this.drawer.querySelectorAll('[data-presentation-revision-restore],[data-presentation-revision-delete]').forEach(function (button) {
|
|
button.disabled = true;
|
|
});
|
|
},
|
|
|
|
setStatus: function (item) {
|
|
var badge = this.drawer.querySelector('[data-presentation-status]');
|
|
badge.textContent = item.is_published ? ('Опубликовано · v' + (item.version || 1)) : 'Черновик';
|
|
badge.className = 'badge ' + (item.is_published ? (item.has_changes ? 'badge-amber' : 'badge-green') : 'badge-gray');
|
|
this.drawer.querySelector('[data-presentation-title]').textContent = item.title || this.field('title').value || 'Новое представление';
|
|
},
|
|
|
|
setDirty: function (value) {
|
|
this.dirty = !!value;
|
|
this.drawer.classList.toggle('has-unsaved-changes', this.dirty);
|
|
if (this.dirty) { this.setSaveState('Есть несохранённые изменения', 'dirty'); }
|
|
},
|
|
|
|
setSaveState: function (message, state) {
|
|
var element = this.drawer.querySelector('[data-presentation-save-state]');
|
|
element.textContent = message || '';
|
|
element.className = 'public-presentation-save-state' + (state ? ' is-' + state : '');
|
|
},
|
|
|
|
setLint: function (message, state) {
|
|
this.drawer.querySelectorAll('[data-presentation-lint-result]').forEach(function (element) {
|
|
element.textContent = message || '';
|
|
element.className = 'field-hint' + (state ? ' is-' + state : '');
|
|
});
|
|
},
|
|
|
|
confirmDiscard: function () {
|
|
var self = this;
|
|
Adminx.Confirm.open({
|
|
kind: 'warning',
|
|
title: 'Закрыть без сохранения?',
|
|
message: 'Изменения текущего черновика будут потеряны.',
|
|
confirmLabel: 'Закрыть',
|
|
onConfirm: function () {
|
|
self.setDirty(false);
|
|
if (self.needsReload) { window.location.reload(); return; }
|
|
Adminx.Drawer.close(self.drawer);
|
|
}
|
|
});
|
|
},
|
|
|
|
readJson: function (selector) {
|
|
var node = document.querySelector(selector);
|
|
if (!node) { return {}; }
|
|
try { return JSON.parse(node.textContent || '{}'); }
|
|
catch (error) { return {}; }
|
|
},
|
|
|
|
escape: function (value) {
|
|
var node = document.createElement('div');
|
|
node.textContent = value == null ? '' : String(value);
|
|
return node.innerHTML;
|
|
},
|
|
|
|
fail: function (error) {
|
|
Adminx.Toast.show((error && error.message) || 'Не удалось выполнить действие', 'error');
|
|
}
|
|
};
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', function () { Adminx.PublicPresentations.init(); });
|
|
} else {
|
|
Adminx.PublicPresentations.init();
|
|
}
|
|
})(window, document);
|