Внешние компоненты / Модуль менеджера

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

#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда

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

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

// СтандартныеПодсистемы.ГрупповоеИзменениеОбъектов

// Возвращает реквизиты объекта, которые разрешается редактировать
// с помощью обработки группового изменения реквизитов.
//
// Возвращаемое значение:
//  Массив из Строка
//
Функция РеквизитыРедактируемыеВГрупповойОбработке() Экспорт
	
	Результат = Новый Массив;
	Возврат Результат;
	
КонецФункции

// Конец СтандартныеПодсистемы.ГрупповоеИзменениеОбъектов

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

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

#Область СлужебныеПроцедурыИФункции

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

#Область ОбработчикиОбновления

// Регистрирует на плане обмена ОбновлениеИнформационнойБазы объекты,
// которые необходимо обновить на новую версию.
//
Процедура ЗарегистрироватьДанныеКОбработкеДляПереходаНаНовуюВерсию(Параметры) Экспорт
	
	Если ОбщегоНазначенияКлиентСервер.СравнитьВерсии(Параметры.ВерсияПодсистемыНаНачалоОбновления, "3.1.9.221") < 0 Тогда
	
		ТекстЗапроса ="ВЫБРАТЬ
		|	ВнешниеКомпоненты.Ссылка
		|ИЗ
		|	Справочник.ВнешниеКомпоненты КАК ВнешниеКомпоненты";
		
		Запрос = Новый Запрос(ТекстЗапроса);
		
		ОбновлениеИнформационнойБазы.ОтметитьКОбработке(Параметры, Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка"));

	ИначеЕсли ОбновитьПараметрыКомпонентыСканирования(Параметры.ВерсияПодсистемыНаНачалоОбновления) Тогда
	
		ТекстЗапроса ="ВЫБРАТЬ
		|	ВнешниеКомпоненты.Ссылка КАК Ссылка
		|ИЗ
		|	Справочник.ВнешниеКомпоненты КАК ВнешниеКомпоненты
		|ГДЕ
		|	ВнешниеКомпоненты.Идентификатор = &Идентификатор
		|	ИЛИ ВнешниеКомпоненты.Идентификатор = &Идентификатор2 И ВнешниеКомпоненты.Версия ПОДОБНО ""3.1.0.%""";
		
		Запрос = Новый Запрос(ТекстЗапроса);
		Запрос.УстановитьПараметр("Идентификатор", "AddInNativeExtension");
		Запрос.УстановитьПараметр("Идентификатор2", "ImageScan");
		
		ОбновлениеИнформационнойБазы.ОтметитьКОбработке(Параметры, Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка"));
		
	КонецЕсли;
	
КонецПроцедуры

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

	Выборка = ОбновлениеИнформационнойБазы.ВыбратьСсылкиДляОбработки(Параметры.Очередь, "Справочник.ВнешниеКомпоненты");
	Если Выборка.Количество() > 0 Тогда
		ОбработатьВнешниеКомпоненты(Выборка, Параметры.ВерсияПодсистемыНаНачалоОбновления);
	КонецЕсли;

	ОбработкаЗавершена = ОбновлениеИнформационнойБазы.ОбработкаДанныхЗавершена(Параметры.Очередь,
		"Справочник.ВнешниеКомпоненты");
	Параметры.ОбработкаЗавершена = ОбработкаЗавершена;
	
КонецПроцедуры

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

	Пока Выборка.Следующий() Цикл
		
		ПолучаемыеРеквизиты = Новый Массив;
		
		Если ОбновитьПоддерживаемыеПлатформы Тогда
			ПолучаемыеРеквизиты.Добавить("ХранилищеКомпоненты");
			ПолучаемыеРеквизиты.Добавить("ЦелевыеПлатформы");
		КонецЕсли;
		
		Если ОбновитьПараметрыКомпонентыСканирования Тогда
			ПолучаемыеРеквизиты.Добавить("Идентификатор");
			ПолучаемыеРеквизиты.Добавить("Версия");
		КонецЕсли;
		
		РеквизитыКомпоненты = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Выборка.Ссылка, ПолучаемыеРеквизиты);

		Если ОбновитьПоддерживаемыеПлатформы Тогда
			
			ОбновитьПоддерживаемыеПлатформыКомпоненты = Истина;
			
			ДвоичныеДанныеКомпоненты = РеквизитыКомпоненты.ХранилищеКомпоненты.Получить();
			
			Если ТипЗнч(ДвоичныеДанныеКомпоненты) <> Тип("ДвоичныеДанные") Тогда
				ОбновитьПоддерживаемыеПлатформыКомпоненты = Ложь;
			Иначе
					
				ИнформацияОКомпонентеИзФайла = ВнешниеКомпонентыСлужебный.ИнформацияОКомпонентеИзФайла(
					ДвоичныеДанныеКомпоненты, Ложь);
				Если Не ИнформацияОКомпонентеИзФайла.Разобрано Тогда
					ОбновитьПоддерживаемыеПлатформыКомпоненты = Ложь;
				Иначе
					Реквизиты = ИнформацияОКомпонентеИзФайла.Реквизиты;
					ЦелевыеПлатформы = РеквизитыКомпоненты.ЦелевыеПлатформы.Получить();
					Если ЦелевыеПлатформы <> Неопределено И ОбщегоНазначения.КоллекцииИдентичны(ЦелевыеПлатформы,
						Реквизиты.ЦелевыеПлатформы) Тогда
						ОбновитьПоддерживаемыеПлатформыКомпоненты = Ложь;
					КонецЕсли;
				КонецЕсли;

			КонецЕсли;
			
		Иначе
			ОбновитьПоддерживаемыеПлатформыКомпоненты = Ложь;
		КонецЕсли;
		
		Если Не ОбновитьПоддерживаемыеПлатформыКомпоненты 
			И Не (ОбновитьПараметрыКомпонентыСканирования И (РеквизитыКомпоненты.Идентификатор = "AddInNativeExtension"
				Или РеквизитыКомпоненты.Идентификатор = ИдентификаторКомпонентыСканирования 
				И СтрНачинаетсяС(РеквизитыКомпоненты.Версия, "3.1.0."))) Тогда
			ОбновлениеИнформационнойБазы.ОтметитьВыполнениеОбработки(Выборка.Ссылка);
			ОбъектовОбработано = ОбъектовОбработано + 1;
			Продолжить;
		КонецЕсли;
		
		ПредставлениеСсылки = Строка(Выборка.Ссылка);
		НачатьТранзакцию();
		Попытка

			Блокировка = Новый БлокировкаДанных;
			ЭлементБлокировки = Блокировка.Добавить("Справочник.ВнешниеКомпоненты");
			ЭлементБлокировки.УстановитьЗначение("Ссылка", Выборка.Ссылка);
			ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
			Блокировка.Заблокировать();

			КомпонентаОбъект = Выборка.Ссылка.ПолучитьОбъект(); // СправочникОбъект.ВнешниеКомпоненты
			Если ОбновитьПоддерживаемыеПлатформыКомпоненты Тогда
				КомпонентаОбъект.ЦелевыеПлатформы = Новый ХранилищеЗначения(Реквизиты.ЦелевыеПлатформы, Новый СжатиеДанных(9));
			КонецЕсли;
			
			Если ОбновитьПараметрыКомпонентыСканирования И РеквизитыКомпоненты.Идентификатор = "AddInNativeExtension" Тогда
				КомпонентаОбъект.Идентификатор = ИдентификаторКомпонентыСканирования;
			КонецЕсли;
			
			Если ОбновитьПараметрыКомпонентыСканирования И СтрНачинаетсяС(РеквизитыКомпоненты.Версия, "3.1.0.") Тогда
				КомпонентаОбъект.Версия = СтрЗаменить(РеквизитыКомпоненты.Версия, "3.1.0.", "3.0.1.");
			КонецЕсли;
			
			ОбновлениеИнформационнойБазы.ЗаписатьОбъект(КомпонентаОбъект);

			ОбъектовОбработано = ОбъектовОбработано + 1;
			ЗафиксироватьТранзакцию();

		Исключение

			ОтменитьТранзакцию();
			// Если не удалось обработать компоненту, повторяем попытку снова.
			ПроблемныхОбъектов = ПроблемныхОбъектов + 1;

			ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось обработать компоненту %1 по причине:
					 |%2'"), ПредставлениеСсылки, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));

			ЗаписьЖурналаРегистрации(ОбновлениеИнформационнойБазы.СобытиеЖурналаРегистрации(),
				УровеньЖурналаРегистрации.Предупреждение, Выборка.Ссылка.Метаданные(), Выборка.Ссылка, ТекстСообщения);

		КонецПопытки;

	КонецЦикла;

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

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

Функция ОбновитьПараметрыКомпонентыСканирования(ВерсияПодсистемыНаНачалоОбновления)
	Возврат ОбщегоНазначенияКлиентСервер.СравнитьВерсии(ВерсияПодсистемыНаНачалоОбновления, "3.1.10.179") < 0
		И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаСФайлами")
КонецФункции

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

#КонецЕсли

Связанные материалы