При разработке внешних обработок (удобно через инструмент отладки и написания кода в Предприятии) в 1С 8.3 на управляемых формах часто возникает потребность в сохранении их настроек. Однако, если конфигурация не использует Библиотеку стандартных подсистем (БСП) и необходимо, чтобы эти настройки были доступны всем пользователям, а не только текущему, задача усложняется. Рассмотрим, какие подходы можно применить для решения этой проблемы, учитывая отсутствие готовых механизмов БСП и особенности безопасного режима.
Один из наиболее гибких и надежных способов хранения общих настроек — это создание собственных объектов конфигурации. Этот подход позволяет полностью контролировать структуру и способ хранения данных, а также обходить ограничения, связанные с безопасным режимом внешних обработок — поможет инструмент поиска по метаданным и проверки синтаксиса.
Мы предлагаем создать в вашей конфигурации новый справочник или регистр сведений — для этого подойдёт редактор записей регистров и Хранилища значения. Допустим, мы создадим справочник под названием «НастройкиВнешнихОбработок».
Создание объекта конфигурации:
Создайте новый справочник (или регистр сведений) в конфигурации. Например, назовем его НастройкиВнешнихОбработок. Если вы хотите привязать настройки к конкретной внешней обработке, этот справочник может содержать элементы, соответствующие каждой обработке.
Добавление реквизита типа «ХранилищеЗначения»:
В этот справочник (или регистр) добавьте реквизит с именем, например, ХранилищеНастроек, и установите его тип данных ХранилищеЗначения. Тип ХранилищеЗначения позволяет сохранять в базе данных произвольные данные (структуры, массивы, таблицы значений и т.д.) в сериализованном виде. Это идеальный вариант для хранения сложных настроек, состоящих из нескольких параметров.
Механизм сохранения настроек:
Из вашей внешней обработки мы будем собирать все необходимые параметры в структуру. Затем эту структуру мы поместим в ХранилищеЗначения и сохраним в соответствующий элемент нашего нового справочника.
Предположим, что мы определили уникальный ключ для нашей обработки, например, её имя (ИмяФайлаОбработки или GUID). Мы можем использовать этот ключ для поиска или создания элемента справочника.
&НаКлиенте
Процедура СохранитьНастройки()
Настройки = Новый Структура;
Настройки.Вставить("Параметр1", Объект.Реквизит1);
Настройки.Вставить("Параметр2", Объект.Реквизит2);
// Добавляем все нужные параметры обработки в структуру
СохранитьНастройкиНаСервере(Настройки);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура СохранитьНастройкиНаСервере(Настройки)
// Имя или идентификатор вашей обработки, по которому будут храниться настройки
КлючОбработки = "МояВнешняяОбработка";
ЭлементНастроек = Справочники.НастройкиВнешнихОбработок.НайтиПоНаименованию(КлючОбработки);
Если ЭлементНастроек = Неопределено Тогда
ЭлементНастроек = Справочники.НастройкиВнешнихОбработок.СоздатьЭлемент();
ЭлементНастроек.Наименование = КлючОбработки;
КонецЕсли;
ЭлементНастроек.ХранилищеНастроек = Новый ХранилищеЗначения(Настройки);
Попытка
ЭлементНастроек.Записать();
Исключение
Сообщить("Ошибка при сохранении настроек: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Механизм загрузки настроек:
При открытии обработки или по требованию пользователя, мы будем загружать структуру настроек из того же элемента справочника и раскладывать значения по реквизитам формы.
&НаКлиенте
Процедура ПриОткрытии(Отказ)
Настройки = ЗагрузитьНастройкиНаСервере();
Если Настройки <> Неопределено Тогда
Если Настройки.Свойство("Параметр1") Тогда
Объект.Реквизит1 = Настройки.Параметр1;
КонецЕсли;
Если Настройки.Свойство("Параметр2") Тогда
Объект.Реквизит2 = Настройки.Параметр2;
КонецЕсли;
// Заполняем остальные реквизиты формы
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ЗагрузитьНастройкиНаСервере()
КлючОбработки = "МояВнешняяОбработка";
ЭлементНастроек = Справочники.НастройкиВнешнихОбработок.НайтиПоНаименованию(КлючОбработки);
Если ЭлементНастроек <> Неопределено И ЭлементНастроек.ХранилищеНастроек <> Неопределено Тогда
Возврат ЭлементНастроек.ХранилищеНастроек.Получить();
Иначе
Возврат Неопределено;
КонецЕсли;
КонецФункции
Преимущества данного подхода:
Платформа 1С предоставляет механизм ХранилищеОбщихНастроек, который является встроенным инструментом для сохранения настроек. Теоретически, он позволяет сохранять настройки для любого пользователя, включая некий "общий" контекст. Однако, при работе с внешними обработками, особенно в контексте безопасного режима, возникают существенные ограничения.
Функции ХранилищеОбщихНастроек позволяют сохранять и загружать настройки, указывая КлючОбъекта, КлючНастроек и опционально ИмяПользователя. Чтобы настройки были общими, мы могли бы попытаться указать специальное имя пользователя, например, "Общее" или "ВнешниеОбработки".
&НаКлиенте
Процедура СохранитьНастройкиХОН()
Настройки = Новый Структура;
Настройки.Вставить("Параметр1", Объект.Реквизит1);
// ...
СохранитьНастройкиХОННаСервере(Настройки);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура СохранитьНастройкиХОННаСервере(Настройки)
КлючОбъекта = "МояВнешняяОбработка";
КлючНастроек = "ОсновныеНастройки";
ИмяОбщегоПользователя = "Общее"; // Или другое условное имя
Попытка
// Для сохранения настроек для другого пользователя или общего контекста
// часто требуется привилегированный режим.
УстановитьПривилегированныйРежим(Истина);
ХранилищеОбщихНастроек.Сохранить(КлючОбъекта, КлючНастроек, Настройки, , ИмяОбщегоПользователя);
Сообщить("Настройки сохранены в ХранилищеОбщихНастроек.");
Исключение
Сообщить("Ошибка при сохранении настроек в ХранилищеОбщихНастроек: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
&НаКлиенте
Процедура ЗагрузитьНастройкиХОН()
Настройки = ЗагрузитьНастройкиХОННаСервере();
Если Настройки <> Неопределено Тогда
Если Настройки.Свойство("Параметр1") Тогда
Объект.Реквизит1 = Настройки.Параметр1;
КонецЕсли;
// ...
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ЗагрузитьНастройкиХОННаСервере()
КлючОбъекта = "МояВнешняяОбработка";
КлючНастроек = "ОсновныеНастройки";
ИмяОбщегоПользователя = "Общее";
Попытка
// Для загрузки настроек, сохраненных другим пользователем или в общем контексте,
// также может требоваться привилегированный режим.
УстановитьПривилегированныйРежим(Истина);
Возврат ХранилищеОбщихНастроек.Загрузить(КлючОбъекта, КлючНастроек, , ИмяОбщегоПользователя);
Исключение
Сообщить("Ошибка при загрузке настроек из ХранилищаОбщихНастроек: " + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
КонецФункции
Проблема безопасного режима и привилегированных прав:
Ключевым препятствием для использования ХранилищаОбщихНастроек является то, что внешние обработки по умолчанию запускаются в безопасном режиме — для его настройки есть расширение для настройки логики и поведения форм. Этот режим предназначен для предотвращения выполнения потенциально опасных действий. Одной из его особенностей является отмена привилегированного режима: даже если мы попытаемся установить его командой УстановитьПривилегированныйРежим(Истина) внутри внешней обработки, для обычных пользователей он не будет действовать в отношении доступа к данным, требующим повышенных прав.
Это означает, что обычный пользователь, запускающий внешнюю обработку в безопасном режиме, не сможет сохранить или загрузить настройки для "Общего" пользователя, поскольку это действие будет расценено как попытка доступа к данным другого пользователя, что требует привилегированного режима.
Мы можем провести простой тест:
&НаСервереБезКонтекста
Функция ПроверитьПривилегированныйРежим()
УстановитьПривилегированныйРежим(Истина);
Возврат ПривилегированныйРежим();
КонецФункции
&НаКлиенте
Процедура Команда1(Команда)
Если ПроверитьПривилегированныйРежим() Тогда
Сообщить("Привилегированный режим установлен (в рамках проверки).");
Иначе
Сообщить("Привилегированный режим НЕ установлен.");
КонецЕсли;
КонецПроцедуры
При запуске этой обработки обычным пользователем, мы увидим сообщение "Привилегированный режим НЕ установлен", подтверждающее, что в безопасном режиме попытка установки привилегированного режима не приводит к фактическому повышению прав для работы с данными.
Возможные решения проблемы безопасного режима для ХранилищаОбщихНастроек:
Если вы всё же хотите использовать ХранилищеОбщихНастроек, вам потребуется явным образом разрешить необходимые действия:
Отключение защиты от опасных действий для пользователя:
Если вы полностью доверяете внешней обработке и знаете, что она не содержит вредоносного кода, можно отключить свойство "Защита от опасных действий" для профиля пользователя, под которым запускается обработка. Этот подход не рекомендуется для широкого круга пользователей, так как он снижает общую безопасность системы.
Настройка профилей безопасности кластера сервера 1С:
Для серверных баз можно разрешить доступ к привилегированному режиму исполнения кода для безопасного режима через настройку профилей безопасности кластера сервера 1С. Это более тонкая настройка, которая позволяет выборочно разрешать определенные действия.
Подключение обработки с параметром БезопасныйРежим = Ложь:
При программном подключении внешней обработки с помощью метода Подключить(), например, из какой-либо фоновой задачи или из кода конфигурации, можно явно указать БезопасныйРежим = Ложь. Это отключит безопасный режим для конкретного сеанса работы с обработкой.
ВнешняяОбработка = ВнешниеОтчетыИОбработки.Создать("ПутьКФайлуОбработки.epf", Ложь, Ложь, "ИмяОбработки");
// Ложь, Ложь - это первый БезопасныйРежим (Защита от опасных действий) и второй (Для работы с данными)
Однако, это не применимо, если пользователь запускает обработку вручную из меню "Файл - Открыть".
Использование разрешений в СведенияОВнешнейОбработке:
Если ваша внешняя обработка подключается как дополнительная (через подсистему "Дополнительные отчеты и обработки"), в функции СведенияОВнешнейОбработке можно указать необходимые разрешения. В частности, для разрешения привилегированного режима в безопасном режиме может использоваться разрешение РаботаВБезопасномРежиме.РазрешениеНаИспользованиеПривилегированногоРежима.
// В модуле объекта внешней обработки, в функции СведенияОВнешнейОбработке
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = Новый Структура;
// ... другие параметры ...
ДополнительныеСвойства = Новый Структура;
ДополнительныеСвойства.Вставить("Разрешения", Новый Массив);
ДополнительныеСвойства.Разрешения.Добавить(РаботаВБезопасномРежиме.РазрешениеНаИспользованиеПривилегированногоРежима);
ПараметрыРегистрации.Вставить("ДополнительныеСвойства", ДополнительныеСвойства);
Возврат ПараметрыРегистрации;
КонецФункции
Это самый предпочтительный способ для дополнительных обработок, так как он предоставляет гранулированный контроль над разрешениями.
Вывод по ХранилищуОбщихНастроек: Механизм удобен, но требует глубокого понимания и настройки прав доступа/безопасного режима, что может быть сложным для обычных внедрений без БСП или для пользователей без административных прав.
Альтернативным вариантом, полностью отвязывающим настройки от базы данных, является сохранение их в файл. Этот подход напоминает работу консоли запросов, которая сохраняет свои параметры в файлы с расширением .sel.
Механизм сохранения:
Мы собираем все необходимые параметры обработки в структуру или массив. Затем эту структуру сериализуем (например, в XML или JSON, или просто сохраняем как ХранилищеЗначения в двоичный файл) и записываем в файл на диске — в этом поможет универсальная выгрузка данных в JSON и XML.
&НаКлиенте
Процедура СохранитьВФайлНастройки()
Настройки = Новый Структура;
Настройки.Вставить("Параметр1", Объект.Реквизит1);
Настройки.Вставить("Параметр2", Объект.Реквизит2);
// ...
ДиалогСохранения = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
ДиалогСохранения.ПолноеИмяФайла = "НастройкиМоейОбработки.json"; // Предлагаемое имя файла
ДиалогСохранения.Фильтр = "Файлы настроек (*.json)|*.json";
Если ДиалогСохранения.Выбрать() Тогда
Попытка
// Для примера используем сериализацию в JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Настройки);
СтрокаJSON = ЗаписьJSON.Закрыть();
ТекстовыйДокумент = Новый ТекстовыйДокумент;
ТекстовыйДокумент.УстановитьТекст(СтрокаJSON);
ТекстовыйДокумент.Записать(ДиалогСохранения.ПолноеИмяФайла, КодировкаТекста.UTF8);
Сообщить("Настройки успешно сохранены в файл: " + ДиалогСохранения.ПолноеИмяФайла);
Исключение
Сообщить("Ошибка сохранения настроек в файл: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецПроцедуры
Механизм загрузки:
При необходимости мы предоставляем пользователю возможность выбрать файл с настройками. Затем считываем содержимое файла, десериализуем его обратно в структуру и применяем настройки к реквизитам обработки.
&НаКлиенте
Процедура ЗагрузитьИзФайлаНастройки()
ДиалогОткрытия = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
ДиалогОткрытия.Фильтр = "Файлы настроек (*.json)|*.json";
Если ДиалогОткрытия.Выбрать() Тогда
Попытка
ТекстовыйДокумент = Новый ТекстовыйДокумент;
ТекстовыйДокумент.Прочитать(ДиалогОткрытия.ПолноеИмяФайла, КодировкаТекста.UTF8);
СтрокаJSON = ТекстовыйДокумент.ПолучитьТекст();
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
Настройки = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();
Если Настройки <> Неопределено Тогда
Если Настройки.Свойство("Параметр1") Тогда
Объект.Реквизит1 = Настройки.Параметр1;
КонецЕсли;
Если Настройки.Свойство("Параметр2") Тогда
Объект.Реквизит2 = Настройки.Параметр2;
КонецЕсли;
// Заполняем остальные реквизиты формы
Сообщить("Настройки успешно загружены из файла.");
КонецЕсли;
Исключение
Сообщить("Ошибка загрузки настроек из файла: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецПроцедуры
Ограничения безопасного режима для доступа к файловой системе:
Как и в случае с ХранилищемОбщихНастроек, безопасный режим накладывает ограничения на работу с файлами. Внешним обработкам в безопасном режиме запрещен прямой доступ к произвольным каталогам файловой системы (за исключением временных файлов и некоторых системных). Чтобы обойти это, нам потребуется:
ХранилищаОбщихНастроек).СведенияОВнешнейОбработке, чтобы разрешить чтение/запись в конкретные папки.Местоположение файла:
Для того чтобы настройки были общими для всех пользователей, файл с настройками должен храниться в общедоступном месте, например:
Если файл будет храниться на локальной клиентской машине, то настройки не будут общими для всех пользователей.
Вывод по сохранению в файл: Этот метод подходит, когда требуется полная автономность настроек от базы данных или когда настройки носят скорее "экспортный" характер. Однако он требует тщательной проработки вопросов доступа к файловой системе в безопасном режиме и организации общего хранилища файлов.
Независимо от выбранного подхода, мы настоятельно рекомендуем использовать тип данных ХранилищеЗначения для упаковки сложных структур настроек. Это позволяет сохранить произвольные данные любой сложности (структуры, массивы, таблицы значений и т.д.) в одном реквизите, что значительно упрощает логику сохранения и загрузки.
Когда вы создаете свои объекты конфигурации (справочники или регистры) для хранения настроек, всегда отдавайте предпочтение реквизиту типа ХранилищеЗначения. Это не только упрощает код, но и делает вашу систему более гибкой к изменениям в составе настроек, поскольку нет необходимости менять структуру базы данных при добавлении новых параметров.
Выбирая метод сохранения настроек внешней обработки независимо от пользователя, мы рекомендуем прежде всего рассмотреть создание собственного регистра сведений или справочника с реквизитом типа ХранилищеЗначения. Этот метод является наиболее надежным, гибким и не требует сложных манипуляций с правами доступа или безопасным режимом.
Использование ХранилищаОбщихНастроек возможно, но требует серьезной настройки безопасности и разрешений, что может быть неоправданно сложно в отсутствие БСП. Сохранение в файл является жизнеспособной альтернативой при определенных условиях, но также имеет свои ограничения, особенно касающиеся безопасности файловой системы.
В любом случае, важно четко определить требования к доступу и сохранности настроек, чтобы выбрать наиболее подходящий и безопасный метод.