+ {/foreach}
+{/block}
diff --git a/Web/routes.yml b/Web/routes.yml
index f4323dcae..880c3e8bc 100644
--- a/Web/routes.yml
+++ b/Web/routes.yml
@@ -395,6 +395,8 @@ routes:
handler: "Admin->chandlerGroup"
- url: "/admin/chandler/users/{slug}"
handler: "Admin->chandlerUser"
+ - url: "/admin/config"
+ handler: "Admin->config"
- url: "/noSpam"
handler: "NoSpam->index"
- url: "/al_abuse/search"
diff --git a/Web/static/js/yaml.js b/Web/static/js/yaml.js
new file mode 100644
index 000000000..db69fcddd
--- /dev/null
+++ b/Web/static/js/yaml.js
@@ -0,0 +1,102 @@
+function convertFormToYAML(handler, csrfToken) {
+ const form = $(".aui");
+ let formData = [];
+
+ form.find('input, select').each(function () {
+ let key = $(this).attr("name");
+ let value = $(this).val();
+ let level = $(this).attr("level") ?? 0;
+
+ if ($(this).is('select')) {
+ value = (value === 'true');
+ }
+
+ if ($(this).attr("disabled") && !(key == "allowEditConfig" && level == 1)) {
+ return;
+ }
+
+ if ($(this).attr("noValue")) {
+ formData.push({indentation: level, key: key, value: value, noValue: true});
+ } else {
+ formData.push({indentation: level, key: key, value: value});
+ }
+ });
+
+ const yaml = restoreYAMLFromJS(formData);
+ $.ajax({
+ type: "POST",
+ url: handler,
+ data: {
+ yaml: yaml,
+ hash: csrfToken
+ },
+ success: (data) => {
+ if (data.success) {
+ window.location.reload();
+ } else {
+ alert(data.error ?? "Неизвестная ошибка");
+ }
+ }
+ })
+}
+
+function restoreYAMLFromJS(formData) {
+ let yamlData = '';
+ let stack = [];
+ let currentIndent = 0;
+ let addDashForNextKey = false;
+ let nextSpacesOverride = null;
+
+ formData.forEach((_key, i) => {
+ let {indentation, key} = _key;
+ let value = _key.value;
+ const level = parseInt(indentation);
+
+ console.log(_key);
+
+ let spaces = ' '.repeat(nextSpacesOverride ?? (level * 4));
+ nextSpacesOverride = null;
+
+ while (level < currentIndent) {
+ stack.pop();
+ currentIndent -= 1;
+ }
+
+ if (_key?.noValue) {
+ yamlData += `${spaces}${key}:\n`;
+ } else {
+ if (/\d$/.test(key) && value.length === 0) {
+ addDashForNextKey = true;
+ } else {
+ if (addDashForNextKey) {
+ let _s = `${spaces}- ${key}: \"${value}\"\n`;
+ nextSpacesOverride = _s.split(key)[0].length;
+ yamlData += _s;
+ addDashForNextKey = false;
+ } else if (/\d: ".*"/.test(`${key}: \"${value}\"`)) {
+ yamlData += `${spaces}- ${value}\n`;
+ addDashForNextKey = false;
+ } else {
+ if (value.length === 0) {
+ yamlData += `${spaces}${key}: \"\"\n`;
+ } else if (typeof value === 'boolean') {
+ yamlData += `${spaces}${key}: ${value}\n`;
+ } else if (value === 'null' || value == " ") {
+ yamlData += `${spaces}${key}: null\n`;
+ } else if (value.startsWith('"') && value.endsWith('"')) {
+ yamlData += `${spaces}${key}: ${value}\n`;
+ } else {
+ yamlData += `${spaces}${key}: \"${value}\"\n`;
+ currentIndent = level + 1;
+
+ stack.push(yamlData);
+ stack.push(level + 1);
+ stack.push(key);
+ }
+ }
+ }
+ }
+ });
+
+ return yamlData;
+}
diff --git a/locales/en.strings b/locales/en.strings
index be5b6efcd..7fadf6532 100644
--- a/locales/en.strings
+++ b/locales/en.strings
@@ -1870,6 +1870,120 @@
"admin_overview_chandler_groups_missing" = "Permissions groups are necessary for full access to instance management (Helpdesk, moderation, noSpam)";
"admin_overview_chandler_groups_create_missing" = "Create missing permissions groups";
+"save_all" = "Save all";
+"admin_tuning_cfg_file_write_err" = "Configuration file could not be written, editing is blocked";
+"admin_tuning_cfg_tmp_read_fail_yaml" = "Error when reading temporary file: possibly incorrect YAML was passed";
+"admin_tuning_cfg_write_fail" = "Error writing a configuration file";
+"admin_settings_config" = "Config";
+
+"admin_settings_tuning_openvk" = "OpenVK";
+"admin_settings_tuning_debug" = "Debug mode";
+"admin_settings_tuning_appearance" = "Appearance";
+"admin_settings_tuning_name" = "Name";
+"admin_settings_tuning_motd" = "Instance description";
+"admin_settings_tuning_preferences" = "Settings";
+"admin_settings_tuning_femaleGenderPriority" = "Female gender priority";
+"admin_settings_tuning_nginxCacheTime" = "nginx Cache time";
+"admin_settings_tuning_uploads" = "Uploads";
+"admin_settings_tuning_disableLargeUploads" = "Disable large uploads";
+"admin_settings_tuning_mode" = "Mode";
+"admin_settings_tuning_api" = "API";
+"admin_settings_tuning_maxFilesPerDomain" = "Max files per domain";
+"admin_settings_tuning_maxFileSize" = "Max file size";
+"admin_settings_tuning_shortcodes" = "Shortcodes";
+"admin_settings_tuning_minLength" = "Min length";
+"admin_settings_tuning_forbiddenNames" = "Forbidden names";
+"admin_settings_tuning_array_element" = "Array Item";
+"admin_settings_tuning_photos" = "Photos";
+"admin_settings_tuning_upgradeStructure" = "Upgrade structure";
+"admin_settings_tuning_photoSaving" = "Photo saving";
+"admin_settings_tuning_videos" = "Videos";
+"admin_settings_tuning_disableUploading" = "Disable uploading";
+"admin_settings_tuning_apps" = "Apps";
+"admin_settings_tuning_withdrawTax" = "Withdraw tax";
+"admin_settings_tuning_security" = "Security";
+"admin_settings_tuning_requireEmail" = "Require Email";
+"admin_settings_tuning_requirePhone" = "Require phone";
+"admin_settings_tuning_forcePhoneVerification" = "Force phone verification";
+"admin_settings_tuning_forceEmailVerification" = "Force Email verification";
+"admin_settings_tuning_forceStrongPassword" = "Force strong password";
+"admin_settings_tuning_disablePasswordRestoring" = "Disable password restoring";
+"admin_settings_tuning_enableSu" = "Allow user substitution";
+"admin_settings_tuning_rateLimits" = "Rate limits";
+"admin_settings_tuning_actions" = "Actions";
+"admin_settings_tuning_time" = "Time";
+"admin_settings_tuning_maxViolations" = "Max violations";
+"admin_settings_tuning_maxViolationsAge" = "Max violations age";
+"admin_settings_tuning_autoban" = "Auto ban";
+"admin_settings_tuning_registration" = "Registration";
+"admin_settings_tuning_enable" = "Enable";
+"admin_settings_tuning_disablingReason" = "Disabling reason";
+"admin_settings_tuning_support" = "Support";
+"admin_settings_tuning_supportName" = "Default responder name";
+"admin_settings_tuning_adminAccount" = "Admin account";
+"admin_settings_tuning_messages" = "Messages";
+"admin_settings_tuning_wall" = "Wall";
+"admin_settings_tuning_anonymousPosting" = "Anonymous posting";
+"admin_settings_tuning_account" = "Account";
+"admin_settings_tuning_postSizes" = "Post sizes";
+"admin_settings_tuning_maxSize" = "Max size";
+"admin_settings_tuning_processingLimit" = "Предел обработки";
+"admin_settings_tuning_emojiProcessingLimit" = "Emoji processing limit";
+"admin_settings_tuning_commerce" = "Commerce enabled";
+"admin_settings_tuning_susLinks" = "Suspicious links";
+"admin_settings_tuning_warnings" = "Warnings";
+"admin_settings_tuning_showReason" = "Show the reason";
+"admin_settings_tuning_maintenanceMode" = "Maintenance";
+"admin_settings_tuning_all" = "All";
+"admin_settings_tuning_messenger" = "Messenger";
+"admin_settings_tuning_user" = "User";
+"admin_settings_tuning_group" = "Groups";
+"admin_settings_tuning_comment" = "Comments";
+"admin_settings_tuning_gifts" = "Gifts";
+"admin_settings_tuning_notes" = "Notes";
+"admin_settings_tuning_notification" = "Notifications";
+"admin_settings_tuning_topics" = "Topics";
+"admin_settings_tuning_ton" = "TON";
+"admin_settings_tuning_enabled" = "Enabled";
+"admin_settings_tuning_menu" = "Menu";
+"admin_settings_tuning_links" = "Links";
+"admin_settings_tuning_about" = "About";
+"admin_settings_tuning_url" = "URL";
+"admin_settings_tuning_adPoster" = "Ad poster";
+"admin_settings_tuning_src" = "Source";
+"admin_settings_tuning_caption" = "Caption";
+"admin_settings_tuning_link" = "Link";
+"admin_settings_tuning_defaultMobileTheme" = "Default mobile theme";
+"admin_settings_tuning_telemetry" = "Telemetry";
+"admin_settings_tuning_piwik" = "Piwik";
+"admin_settings_tuning_matomo" = "Matomo";
+"admin_settings_tuning_container" = "Container";
+"admin_settings_tuning_site" = "Site";
+"admin_settings_tuning_credentials" = "Credentials";
+"admin_settings_tuning_smsc" = "SMSC";
+"admin_settings_tuning_client" = "Client";
+"admin_settings_tuning_layer" = "Layer";
+"admin_settings_tuning_secret" = "Secret key";
+"admin_settings_tuning_token" = "Token";
+"admin_settings_tuning_helpdeskChat" = "Helpdesk chat";
+"admin_settings_tuning_telegram" = "Telegram";
+"admin_settings_tuning_eventDB" = "eventDB";
+"admin_settings_tuning_kafka" = "Kafka";
+"admin_settings_tuning_database" = "Database";
+"admin_settings_tuning_dsn" = "DSN";
+"admin_settings_tuning_password" = "Password";
+"admin_settings_tuning_notificationsBroker" = "Notifications broker";
+"admin_settings_tuning_domain" = "Domain";
+"admin_settings_tuning_server" = "Server";
+"admin_settings_tuning_address" = "Address";
+"admin_settings_tuning_testnet" = "Testnet";
+"admin_settings_tuning_regex" = "Regex";
+"admin_settings_tuning_hint" = "Hint";
+"admin_settings_tuning_addr" = "Address";
+"admin_settings_tuning_port" = "Port";
+"admin_settings_tuning_topic" = "Topic";
+"admin_settings_tuning_allowEditConfig" = "Config editor allowed";
+
/* Paginator (deprecated) */
"paginator_back" = "Back";
diff --git a/locales/ru.strings b/locales/ru.strings
index fae10c236..a906901b4 100644
--- a/locales/ru.strings
+++ b/locales/ru.strings
@@ -1787,6 +1787,120 @@
"admin_overview_chandler_groups_missing" = "Группы прав необходимы для полноценного доступа к управлению инстансом (Helpdesk, модерация, noSpam)";
"admin_overview_chandler_groups_create_missing" = "Создать отсутствующие группы прав";
+"save_all" = "Сохранить все";
+"admin_tuning_cfg_file_write_err" = "Не удалось записать файл конфигурации, редактирование заблокировано";
+"admin_tuning_cfg_tmp_read_fail_yaml" = "Ошибка при чтении временного файла: возможно, передан некорректный YAML";
+"admin_tuning_cfg_write_fail" = "Ошибка при записи файла конфигурации";
+"admin_settings_config" = "Конфиг";
+
+"admin_settings_tuning_openvk" = "OpenVK";
+"admin_settings_tuning_debug" = "Режим отладки";
+"admin_settings_tuning_appearance" = "Внешний вид";
+"admin_settings_tuning_name" = "Название";
+"admin_settings_tuning_motd" = "Описание инстанса";
+"admin_settings_tuning_preferences" = "Настройки";
+"admin_settings_tuning_femaleGenderPriority" = "Приоритет женского пола";
+"admin_settings_tuning_nginxCacheTime" = "Время кэширования nginx";
+"admin_settings_tuning_uploads" = "Загрузки";
+"admin_settings_tuning_disableLargeUploads" = "Отключить большие загрузки";
+"admin_settings_tuning_mode" = "Режим";
+"admin_settings_tuning_api" = "API";
+"admin_settings_tuning_maxFilesPerDomain" = "Максимальное количество файлов на домен";
+"admin_settings_tuning_maxFileSize" = "Максимальный размер файла";
+"admin_settings_tuning_shortcodes" = "Короткие адреса";
+"admin_settings_tuning_minLength" = "Минимальная длина";
+"admin_settings_tuning_forbiddenNames" = "Запрещенные значения";
+"admin_settings_tuning_array_element" = "Элемент списка";
+"admin_settings_tuning_photos" = "Фотографии";
+"admin_settings_tuning_upgradeStructure" = "Улучшение структуры";
+"admin_settings_tuning_photoSaving" = "Сохранение фото";
+"admin_settings_tuning_videos" = "Видео";
+"admin_settings_tuning_disableUploading" = "Отключить загрузку";
+"admin_settings_tuning_apps" = "Приложения";
+"admin_settings_tuning_withdrawTax" = "Комиссия на вывод";
+"admin_settings_tuning_security" = "Безопасность";
+"admin_settings_tuning_requireEmail" = "Требуется электронная почта";
+"admin_settings_tuning_requirePhone" = "Требуется номер телефона";
+"admin_settings_tuning_forcePhoneVerification" = "Принудительная верификация телефона";
+"admin_settings_tuning_forceEmailVerification" = "Принудительная проверка электронной почты";
+"admin_settings_tuning_forceStrongPassword" = "Принудительное использование надежного пароля";
+"admin_settings_tuning_disablePasswordRestoring" = "Отключить восстановление пароля";
+"admin_settings_tuning_enableSu" = "Включить замену пользователя";
+"admin_settings_tuning_rateLimits" = "Рейт-лимиты";
+"admin_settings_tuning_actions" = "Действия";
+"admin_settings_tuning_time" = "Время";
+"admin_settings_tuning_maxViolations" = "Максимальное количество нарушений";
+"admin_settings_tuning_maxViolationsAge" = "Максимальный возраст нарушений";
+"admin_settings_tuning_autoban" = "Автоматический бан";
+"admin_settings_tuning_registration" = "Регистрация";
+"admin_settings_tuning_enable" = "Включить";
+"admin_settings_tuning_disablingReason" = "Причина отключения";
+"admin_settings_tuning_support" = "Поддержка";
+"admin_settings_tuning_supportName" = "Имя отвечающего по умолчанию";
+"admin_settings_tuning_adminAccount" = "Аккаунт администратора";
+"admin_settings_tuning_messages" = "Сообщения";
+"admin_settings_tuning_wall" = "Стена";
+"admin_settings_tuning_anonymousPosting" = "Анонимные публикации";
+"admin_settings_tuning_account" = "Аккаунт";
+"admin_settings_tuning_postSizes" = "Размеры постов";
+"admin_settings_tuning_maxSize" = "Максимальные размеры";
+"admin_settings_tuning_processingLimit" = "Предел обработки";
+"admin_settings_tuning_emojiProcessingLimit" = "Предел обработки Emoji";
+"admin_settings_tuning_commerce" = "Коммерция включена";
+"admin_settings_tuning_susLinks" = "Подозрительные ссылки";
+"admin_settings_tuning_warnings" = "Предупреждения";
+"admin_settings_tuning_showReason" = "Показывать причину";
+"admin_settings_tuning_maintenanceMode" = "Технические работы";
+"admin_settings_tuning_all" = "Все";
+"admin_settings_tuning_messenger" = "Мессенджер";
+"admin_settings_tuning_user" = "Пользователь";
+"admin_settings_tuning_group" = "Сообщества";
+"admin_settings_tuning_comment" = "Комментарии";
+"admin_settings_tuning_gifts" = "Подарки";
+"admin_settings_tuning_notes" = "Заметки";
+"admin_settings_tuning_notification" = "Уведомления";
+"admin_settings_tuning_topics" = "Обсуждения";
+"admin_settings_tuning_ton" = "TON";
+"admin_settings_tuning_enabled" = "Включено";
+"admin_settings_tuning_menu" = "Меню";
+"admin_settings_tuning_links" = "Ссылки";
+"admin_settings_tuning_about" = "О сайте";
+"admin_settings_tuning_url" = "URL";
+"admin_settings_tuning_adPoster" = "Рекламный баннер";
+"admin_settings_tuning_src" = "Источник";
+"admin_settings_tuning_caption" = "Подпись";
+"admin_settings_tuning_link" = "Ссылка";
+"admin_settings_tuning_defaultMobileTheme" = "Тема для мобильных устройств по умолчанию";
+"admin_settings_tuning_telemetry" = "Телеметрия";
+"admin_settings_tuning_piwik" = "Piwik";
+"admin_settings_tuning_matomo" = "Matomo";
+"admin_settings_tuning_container" = "Контейнер";
+"admin_settings_tuning_site" = "Сайт";
+"admin_settings_tuning_credentials" = "Данные";
+"admin_settings_tuning_smsc" = "SMSC";
+"admin_settings_tuning_client" = "Клиент";
+"admin_settings_tuning_layer" = "Слой";
+"admin_settings_tuning_secret" = "Секретный ключ";
+"admin_settings_tuning_token" = "Токен";
+"admin_settings_tuning_helpdeskChat" = "Чат Helpdesk";
+"admin_settings_tuning_telegram" = "Telegram";
+"admin_settings_tuning_eventDB" = "eventDB";
+"admin_settings_tuning_kafka" = "Kafka";
+"admin_settings_tuning_database" = "База данных";
+"admin_settings_tuning_dsn" = "DSN";
+"admin_settings_tuning_password" = "Пароль";
+"admin_settings_tuning_notificationsBroker" = "Брокер уведомлений";
+"admin_settings_tuning_domain" = "Домен";
+"admin_settings_tuning_server" = "Сервер";
+"admin_settings_tuning_address" = "Адрес";
+"admin_settings_tuning_testnet" = "Testnet";
+"admin_settings_tuning_regex" = "Regex";
+"admin_settings_tuning_hint" = "Подсказка";
+"admin_settings_tuning_addr" = "Адрес";
+"admin_settings_tuning_port" = "Порт";
+"admin_settings_tuning_topic" = "Тема";
+"admin_settings_tuning_allowEditConfig" = "Редактор конфига разрешён";
+
/* Paginator (deprecated) */
"paginator_back" = "Назад";
diff --git a/openvk-example.yml b/openvk-example.yml
index c1b483ac9..e1613361d 100644
--- a/openvk-example.yml
+++ b/openvk-example.yml
@@ -1,5 +1,6 @@
openvk:
debug: true
+ allowEditConfig: true
appearance:
name: "OpenVK"
motd: "Yet another OpenVK instance"