///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2024, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область ПрограммныйИнтерфейс

// Параметры для вызова ВнешниеКомпонентыСервер.ПодключитьКомпоненту.
//
// Возвращаемое значение:
//  Структура:
//    * ИдентификаторыСозданияОбъектов - Массив из Строка - идентификаторы экземпляров модуля объекта,
//              используется только для компонент, у которых есть несколько идентификаторов создания объектов.
//              При задании параметр Идентификатор будет использоваться только для определения компоненты.
//    * Изолированно - Булево  - если Истина, компонента должна подключаться изолированно, 
//                               в этом случае она загружается в отдельный процесс операционной системы.
//                               Если Ложь, то в этом случае компонента будет выполняться в том же процессе 
//                               операционной системы, который выполняет код встроенного языка. 
//              - Неопределено - (по умолчанию Неопределено) соответствует поведению платформы:
//                               не изолированно - если компонентой поддерживается только этот режим, 
//                               изолированно - в остальных случаях.
//                               См. https://its.1c.eu/db/v83doc#bookmark:dev:TI000001866
//    * ПолноеИмяМакета - Строка - полное имя макета конфигурации с ZIP-архивом, в котором хранится компонента.
//
Функция ПараметрыПодключения() Экспорт
	
	Параметры = Новый Структура;
	Параметры.Вставить("ИдентификаторыСозданияОбъектов", Новый Массив);
	Параметры.Вставить("Изолированно");
	Параметры.Вставить("ПолноеИмяМакета");
	
	Возврат Параметры;
	
КонецФункции

// Подключает на сервере 1С:Предприятия внешнюю компоненту из хранилища внешних компонент,
// выполненную по технологии Native API или COM.
// В модели сервиса разрешено только подключение общих внешних компонент, одобренных администратором сервиса.
//
// Параметры:
//  Идентификатор - Строка - идентификатор объекта внешней компоненты.
//  Версия        - Строка - версия компоненты.
//  ПараметрыПодключения - см. ПараметрыПодключения.
//
// Возвращаемое значение:
//   Структура - результат подключения компоненты:
//     * Подключено - Булево - признак подключения;
//     * ПодключаемыйМодуль - ОбъектВнешнейКомпоненты - экземпляр объекта внешней компоненты;
//                          - ФиксированноеСоответствие из КлючИЗначение - экземпляры объектов внешней компоненты,
//                            указанные в ПараметрыПодключения.ИдентификаторыСозданияОбъектов:
//                            ** Ключ - Строка - идентификатор,
//                            ** Значение - ОбъектВнешнейКомпоненты - экземпляр объекта внешней компоненты.
//     * ОписаниеОшибки - Строка - краткое описание ошибки. 
//
Функция ПодключитьКомпоненту(Знач Идентификатор, Версия = Неопределено, ПараметрыПодключения = Неопределено) Экспорт
	
	Если ПараметрыПодключения = Неопределено Тогда
		Изолированно = Неопределено;
	Иначе
		Изолированно = ПараметрыПодключения.Изолированно;
	КонецЕсли;
	
	РезультатПроверки = ВнешниеКомпонентыСлужебный.ПроверитьПодключениеКомпоненты(Идентификатор, Версия, ПараметрыПодключения);
	Если Не ПустаяСтрока(РезультатПроверки.ОписаниеОшибки) Тогда
		Результат = Новый Структура;
		Результат.Вставить("Подключено", Ложь);
		Результат.Вставить("ПодключаемыйМодуль", Неопределено);
		Результат.Вставить("Версия", Версия);
		Результат.Вставить("ОписаниеОшибки", РезультатПроверки.ОписаниеОшибки);
		Возврат Результат;
	КонецЕсли;
	
	Возврат ОбщегоНазначения.ПодключитьКомпонентуПоИдентификатору(
		РезультатПроверки.Идентификатор, РезультатПроверки.Местоположение, Изолированно);
	
КонецФункции

// Возвращает информацию о внешней компоненте по идентификатору и версии.
//
// Параметры:
//  Идентификатор - Строка - идентификатор объекта внешней компоненты.
//  Версия - Строка - версия компоненты. 
//
// Возвращаемое значение:
//  Структура:
//      * Существует - Булево - признак отсутствия компоненты.
//      * ДоступноРедактирование - Булево - признак того, что компоненту может изменить администратор области.
//      * ОписаниеОшибки - Строка - краткое описание ошибки.
//      * Идентификатор - Строка - идентификатор объекта внешней компоненты.
//      * Версия - Строка - версия компоненты.
//      * Наименование - Строка - наименование и краткая информация о компоненте.
//
// Пример:
//
//  Результат = ВнешниеКомпонентыСервер.ИнформацияОКомпоненте("InputDevice", "8.1.7.10");
//  Если Результат.Существует Тогда
//      Идентификатор = Результат.Идентификатор;
//      Версия        = Результат.Версия;
//      Наименование  = Результат.Наименование;
//  Иначе
//      ОбщегоНазначенияКлиентСервер.СообщитьПользователю(Результат.ОписаниеОшибки);
//  КонецЕсли;
//
Функция ИнформацияОКомпоненте(Знач Идентификатор, Знач Версия = Неопределено) Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("Существует", Ложь);
	Результат.Вставить("ДоступноРедактирование", Ложь);
	Результат.Вставить("Идентификатор", Идентификатор);
	Результат.Вставить("Версия", "");
	Результат.Вставить("Наименование", "");
	Результат.Вставить("ОписаниеОшибки", "");
	
	Информация = ВнешниеКомпонентыСлужебный.ИнформацияОСохраненнойКомпоненте(Идентификатор, Версия);
	
	Если Информация.Состояние = "НеНайдена" Тогда
		Результат.ОписаниеОшибки = НСтр("ru = 'Внешняя компонента не существует'");
		Возврат Результат;
	КонецЕсли;
	
	Если Информация.Состояние = "ОтключенаАдминистратором" Тогда
		Результат.ОписаниеОшибки = НСтр("ru = 'Внешняя компонента отключена'");
		Возврат Результат;
	КонецЕсли;
	
	Результат.Существует = Истина;
	Результат.ДоступноРедактирование = Истина;
	
	Если Информация.Состояние = "НайденаВОбщемХранилище" Тогда
		Результат.ДоступноРедактирование = Ложь;
	КонецЕсли;
	
	Результат.Версия = Информация.Реквизиты.Версия;
	Результат.Наименование = Информация.Реквизиты.Наименование;
	
	Возврат Результат;
	
КонецФункции

#Область ДляВызоваИзДругихПодсистем

// ИнтернетПоддержкаПользователей.ПолучениеВнешнихКомпонент

// Возвращает таблицу описаний внешних компонент.
//
// Параметры:
//  Вариант - Строка - может принимать значения:
//    "ДляОбновления" - компоненты с установленным признаком обновления из Интернета.
//    "ДляЗагрузки"   - компоненты, используемые в конфигурации.
//    "Поставляемые"  - для определения поставляемых компонент в модели сервиса.
//
// Возвращаемое значение:
//   - ТаблицаЗначений:
//      * Идентификатор - Строка - уникальный идентификатор внешней компоненты,
//                                 который указан в базе публикаций.
//      * Версия        - Строка - версия внешней компоненты.
//      * Наименование  - Строка - наименование компоненты.
//      * ДатаВерсии    - Дата - дата версии (сборки) внешней компоненты.
//      * ОбновлятьАвтоматически - Булево - признак автоматического обновления внешней компоненты.
//   - Массив из Строка - идентификаторы поставляемых компонент, если Вариант = "Поставляемые".
//
Функция ИспользуемыеКомпоненты(Вариант) Экспорт
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("ИнтернетПоддержкаПользователей.ПолучениеВнешнихКомпонент") Тогда
		ВызватьИсключение НСтр("ru = 'Действие недоступно.'");
	КонецЕсли;
	
	Если Вариант = "Поставляемые" Тогда
		Возврат ВнешниеКомпонентыСлужебный.ПоставляемыеКомпоненты();
	КонецЕсли;
	
	МодульПолучениеВнешнихКомпонент = ОбщегоНазначения.ОбщийМодуль("ПолучениеВнешнихКомпонент");
	ОписаниеВнешнихКомпонент = МодульПолучениеВнешнихКомпонент.ОписаниеВнешнихКомпонент();
	
	ДанныеВнешнихКомпонент = ВнешниеКомпонентыСлужебный.ДанныеВнешнихКомпонент(Вариант);
	Для Каждого ОписаниеКомпоненты Из ДанныеВнешнихКомпонент Цикл
		НоваяСтрока = ОписаниеВнешнихКомпонент.Добавить();
		ЗаполнитьЗначенияСвойств(НоваяСтрока, ОписаниеКомпоненты);
	КонецЦикла;
	
	Возврат ОписаниеВнешнихКомпонент;
	
КонецФункции

// Выполняет обновление внешних компонент.
//
// Параметры:
//  ДанныеВнешнихКомпонент - ТаблицаЗначений:
//    * Идентификатор - Строка
//    * Версия - Строка
//    * ДатаВерсии - Строка
//    * Наименование - Строка
//    * ИмяФайла - Строка
//    * АдресФайла - Строка
//    * КодОшибки - Строка
//  АдресРезультата - Строка - адрес временного хранилища. Если указан, в него будет помещен результат операции.
//      Структура:
//       # Результат - Булево - Ложь, если есть ошибки.
//       # Ошибки - Соответствие:
//         ## Ключ - Строка - идентификатор внешней компоненты.
//         ## Значение - Строка - текст сообщения.
//       # Успех - Соответствие:
//         ## Ключ - Строка - идентификатор внешней компоненты.
//         ## Значение - Строка - текст сообщения.
//
Процедура ОбновитьВнешниеКомпоненты(ДанныеВнешнихКомпонент, АдресРезультата = Неопределено) Экспорт
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("ИнтернетПоддержкаПользователей.ПолучениеВнешнихКомпонент") Тогда
		ВызватьИсключение НСтр("ru = 'Действие недоступно.'");
	КонецЕсли;
	
	РезультатВыполнения = Новый Структура;
	РезультатВыполнения.Вставить("Результат", Ложь);
	РезультатВыполнения.Вставить("Ошибки", Новый Соответствие);
	РезультатВыполнения.Вставить("Успех", Новый Соответствие);
	
	Запрос = Новый Запрос;
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ВнешниеКомпоненты.Ссылка КАК Ссылка,
		|	ВнешниеКомпоненты.Идентификатор КАК Идентификатор,
		|	ВнешниеКомпоненты.Версия КАК Версия,
		|	ВнешниеКомпоненты.ДатаВерсии КАК ДатаВерсии
		|ИЗ
		|	Справочник.ВнешниеКомпоненты КАК ВнешниеКомпоненты
		|ГДЕ
		|	ВнешниеКомпоненты.Идентификатор В(&Идентификаторы)";
	
	Запрос.УстановитьПараметр("Идентификаторы", ДанныеВнешнихКомпонент.ВыгрузитьКолонку("Идентификатор"));
	Выборка = Запрос.Выполнить().Выбрать();
	
	ИспользуемыеВнешниеКомпоненты = Неопределено;
	Для каждого СтрокаРезультата Из ДанныеВнешнихКомпонент Цикл
		
		ПредставлениеКомпоненты = ВнешниеКомпонентыСлужебный.ПредставлениеКомпоненты(
			СтрокаРезультата.Идентификатор, СтрокаРезультата.Версия);
		
		КодОшибки = СтрокаРезультата.КодОшибки;
		
		Если ЗначениеЗаполнено(КодОшибки) Тогда
			
			Если КодОшибки = "АктуальнаяВерсия" Тогда
				РезультатВыполнения.Успех.Вставить(СтрокаРезультата.Идентификатор,
					СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = '%1 - актуальная версия.'"), ПредставлениеКомпоненты));
				Продолжить;
			КонецЕсли;
			
			ИнформацияОбОшибке = "";
			Если КодОшибки = "ОтсутствуетКомпонента" Тогда 
				ИнформацияОбОшибке = НСтр("ru = 'В сервисе отсутствует данная внешняя компонента.'");
			ИначеЕсли КодОшибки = "ФайлНеЗагружен" Тогда 
				ИнформацияОбОшибке = НСтр("ru = 'Файл внешней компоненты не загружен из сервиса.'");
			КонецЕсли;
			
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось загрузить внешнюю компоненту %1 из сервиса:
				           |%2'"),
				ПредставлениеКомпоненты, ИнформацияОбОшибке);
			
			РезультатВыполнения.Ошибки.Вставить(СтрокаРезультата.Идентификатор, ТекстОшибки);
		
			ЗаписьЖурналаРегистрации(НСтр("ru = 'Обновление внешних компонент'", ОбщегоНазначения.КодОсновногоЯзыка()),
				УровеньЖурналаРегистрации.Ошибка,,,	ТекстОшибки);
			
			Продолжить;
		КонецЕсли;
		
		ДвоичныеДанные = ПолучитьИзВременногоХранилища(СтрокаРезультата.АдресФайла);
		Информация = ВнешниеКомпонентыСлужебный.ИнформацияОКомпонентеИзФайла(ДвоичныеДанные, Ложь);
		
		Если Не Информация.Разобрано Тогда
			
			РезультатВыполнения.Ошибки.Вставить(СтрокаРезультата.Идентификатор, ПредставлениеКомпоненты + " - "
				+ Информация.ОписаниеОшибки + ?(Информация.ИнформацияОбОшибке = Неопределено, "", ": "
				+ ОбработкаОшибок.КраткоеПредставлениеОшибки(Информация.ИнформацияОбОшибке)));

			ЗаписьЖурналаРегистрации(НСтр("ru = 'Обновление внешних компонент'",
				ОбщегоНазначения.КодОсновногоЯзыка()), УровеньЖурналаРегистрации.Ошибка, , ,
				Информация.ОписаниеОшибки);
			Продолжить;
		КонецЕсли;
		
		// Поиск ссылки.
		Отбор = Новый Структура("Идентификатор", СтрокаРезультата.Идентификатор);
		Выборка.Сбросить();
		Если Выборка.НайтиСледующий(Отбор) Тогда 
			// Когда уже загружена компонента по дате более свежая, чем на Портале 1С:ИТС, обновление не следует выполнять.
			Если Выборка.ДатаВерсии > СтрокаРезультата.ДатаВерсии Тогда 
				ПредставлениеКомпоненты = ВнешниеКомпонентыСлужебный.ПредставлениеКомпоненты(
					СтрокаРезультата.Идентификатор, Выборка.Версия);
				
				РезультатВыполнения.Успех.Вставить(СтрокаРезультата.Идентификатор,
					СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = '%1 - в приложении более новая версия, чем в сервисе (%2 от %3).'"), 
						ПредставлениеКомпоненты, СтрокаРезультата.Версия, Формат(СтрокаРезультата.ДатаВерсии, "ДЛФ=Д")));
				Продолжить;
			КонецЕсли;
			
			СсылкаНаОбъект = Выборка.Ссылка;
		Иначе
			СсылкаНаОбъект = Неопределено;
			Если ИспользуемыеВнешниеКомпоненты = Неопределено Тогда
				ИспользуемыеВнешниеКомпоненты = ВнешниеКомпонентыСлужебный.ИспользуемыеВнешниеКомпоненты();
			КонецЕсли;
			СтрокаКомпоненты = ИспользуемыеВнешниеКомпоненты.Найти(СтрокаРезультата.Идентификатор, "Идентификатор");
			Если СтрокаКомпоненты <> Неопределено Тогда
				ОбновлятьАвтоматически = СтрокаКомпоненты.ОбновлятьАвтоматически;
			Иначе
				ОбновлятьАвтоматически = Ложь;
			КонецЕсли;
		КонецЕсли;
			
		НачатьТранзакцию();
		Попытка
			
			Блокировка = Новый БлокировкаДанных;
			ЭлементБлокировки = Блокировка.Добавить("Справочник.ВнешниеКомпоненты");
			Если СсылкаНаОбъект <> Неопределено Тогда
				ЭлементБлокировки.УстановитьЗначение("Ссылка", СсылкаНаОбъект);
			КонецЕсли;
			Блокировка.Заблокировать();
			
			Если СсылкаНаОбъект <> Неопределено Тогда
				Объект = СсылкаНаОбъект.ПолучитьОбъект();
				Объект.Заблокировать();
			Иначе
				Объект = Справочники.ВнешниеКомпоненты.СоздатьЭлемент();
				Объект.ОбновлятьСПортала1СИТС = ОбновлятьАвтоматически;
				Объект.Использование = Перечисления.ВариантыИспользованияВнешнихКомпонент.Используется;
			КонецЕсли;
			
			ЗаполнитьЗначенияСвойств(Объект, Информация.Реквизиты); // По данным манифеста.
			ЗаполнитьЗначенияСвойств(Объект, СтрокаРезультата);     // По данным с сайта.
			
			Объект.ЦелевыеПлатформы = Новый ХранилищеЗначения(Информация.Реквизиты.ЦелевыеПлатформы, Новый СжатиеДанных(9));
			Объект.ДополнительныеСвойства.Вставить("ДвоичныеДанныеКомпоненты", Информация.ДвоичныеДанные);
			
			Объект.ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Загружена с Портала 1С:ИТС. %1.'"),
				ТекущаяДатаСеанса());
			
			Объект.Записать();
			
			РезультатВыполнения.Успех.Вставить(СтрокаРезультата.Идентификатор, 
				СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = '%1 - успешно обновлена.'"), ПредставлениеКомпоненты));
			
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			
			РезультатВыполнения.Ошибки.Вставить(СтрокаРезультата.Идентификатор, ПредставлениеКомпоненты
				+ " - " + ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
				
			ЗаписьЖурналаРегистрации(НСтр("ru = 'Обновление внешних компонент'",
					ОбщегоНазначения.КодОсновногоЯзыка()),
				УровеньЖурналаРегистрации.Ошибка,,, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
		КонецПопытки;
		
	КонецЦикла;
	
	РезультатВыполнения.Результат = РезультатВыполнения.Ошибки.Количество() = 0;
	
	Если ЗначениеЗаполнено(АдресРезультата) Тогда 
		ПоместитьВоВременноеХранилище(РезультатВыполнения, АдресРезультата);
	КонецЕсли;
	
КонецПроцедуры

// Возвращаемое значение:
//  Структура:
//      * Идентификатор - Строка
//      * Версия - Строка
//      * ДатаВерсии - Дата
//      * Наименование - Строка
//      * ИмяФайла - Строка
//      * ПутьКФайлу - Строка
//
Функция ОписаниеПоставляемойОбщейКомпоненты() Экспорт
	
	Описание = Новый Структура;
	Описание.Вставить("Идентификатор");
	Описание.Вставить("Версия");
	Описание.Вставить("ДатаВерсии");
	Описание.Вставить("Наименование");
	Описание.Вставить("ИмяФайла");
	Описание.Вставить("ПутьКФайлу");
	Возврат Описание;
	
КонецФункции

// Выполняет обновление общих внешних компонент.
//
// Параметры:
//  ОписаниеКомпоненты - см. ОписаниеПоставляемойОбщейКомпоненты.
//
Процедура ОбновитьОбщуюКомпоненту(ОписаниеКомпоненты) Экспорт
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ВнешниеКомпонентыВМоделиСервиса") Тогда
		МодульВнешниеКомпонентыВМоделиСервисаСлужебный = ОбщегоНазначения.ОбщийМодуль("ВнешниеКомпонентыВМоделиСервисаСлужебный");
		МодульВнешниеКомпонентыВМоделиСервисаСлужебный.ОбновитьОбщуюКомпоненту(ОписаниеКомпоненты);
	КонецЕсли;
	
КонецПроцедуры

// Для версий БИП старше 2.7.2.0 следует использовать ИспользуемыеКомпоненты("ДляОбновления").
// Возвращает таблицу описаний внешних компонент, которые требуется обновлять автоматически с Портала 1С:ИТС.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//    * Идентификатор - Строка - содержит уникальный идентификатор внешней
//                   компоненты, который указывается пользователем в базе публикаций;
//    * Версия        - Строка - содержит версию внешней компоненты;
//    * Наименование  - Строка - содержит наименование компоненты;
//    * ДатаВерсии    - Дата - содержит дату версии (сборки) внешней компоненты.
//    * ОбновлятьАвтоматически - Булево - признак автоматического обновления внешней компоненты.
//
Функция АвтоматическиОбновляемыеКомпоненты() Экспорт
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("ИнтернетПоддержкаПользователей.ПолучениеВнешнихКомпонент") Тогда
		ВызватьИсключение НСтр("ru = 'Действие недоступно.'");
	КонецЕсли;
	
	МодульПолучениеВнешнихКомпонент = ОбщегоНазначения.ОбщийМодуль("ПолучениеВнешнихКомпонент");
	ОписаниеВнешнихКомпонент = МодульПолучениеВнешнихКомпонент.ОписаниеВнешнихКомпонент();
	
	ДанныеВнешнихКомпонент = ВнешниеКомпонентыСлужебный.ДанныеВнешнихКомпонент("ДляОбновления");
	Для Каждого ОписаниеКомпоненты Из ДанныеВнешнихКомпонент Цикл
		НоваяСтрока = ОписаниеВнешнихКомпонент.Добавить();
		ЗаполнитьЗначенияСвойств(НоваяСтрока, ОписаниеКомпоненты);
	КонецЦикла;
	
	Возврат ОписаниеВнешнихКомпонент;
	
КонецФункции

// Конец ИнтернетПоддержкаПользователей.ПолучениеВнешнихКомпонент

#КонецОбласти

#КонецОбласти

