Подключает процедуру-обработчик, вызываемую при нажатии на гиперссылку в сообщении, заданную в свойстве Действия. Допускается наличие нескольких обработчиков. В этом случае при нажатии на гиперссылку вызываются все обработчики. Порядок вызова обработчиков не определен. Повторный вызов метода для подключенного ранее обработчика замещает обработчик. При повторном вызове метода сравнение обработчиков выполняется по равенству значений свойств: ИмяПроцедуры, ИмяПроцедурыОбработкиОшибки, Модуль, МодульОбработкиОшибки.
Синтаксис
ПодключитьОбработчикДействияСообщения(<Обработчик>)Полное описание, параметры и ограничения
МенеджерСистемыВзаимодействия.ПодключитьОбработчикДействияСообщения (CollaborationSystemManager.AttachMessageActionHandler)
МенеджерСистемыВзаимодействия (CollaborationSystemManager)
ПодключитьОбработчикДействияСообщения (AttachMessageActionHandler)
Не рекомендуется использовать, начиная с версии 8.3.18.
Рекомендуется использовать:
ПриВыбореДействияСообщенияСистемыВзаимодействия
Доступен, начиная с версии 8.3.11.
Синтаксис:
ПодключитьОбработчикДействияСообщения(<Обработчик>)
Параметры:
<Обработчик> (обязательный)
Тип: ОписаниеОповещения.
Содержит описание процедуры, которая будет вызвана после интерактивной отправки сообщения в обсуждение со следующими параметрами:
<Сообщение> - тип СообщениеСистемыВзаимодействия.Сообщение, в котором нажата гиперссылка действия. В обработчик передается копия объекта;
<Действие> - тип Произвольный. Значение, заданное для нажатой гиперссылки.
<Дополнительные параметры> - значение, которое было указано при создании объекта ОписаниеОповещения.
Описание:
Подключает процедуру-обработчик, вызываемую при нажатии на гиперссылку в сообщении, заданную в свойстве Действия.
Допускается наличие нескольких обработчиков. В этом случае при нажатии на гиперссылку вызываются все обработчики. Порядок вызова обработчиков не определен.
Повторный вызов метода для подключенного ранее обработчика замещает обработчик.
При повторном вызове метода сравнение обработчиков выполняется по равенству значений свойств:
ИмяПроцедуры,
ИмяПроцедурыОбработкиОшибки,
Модуль,
МодульОбработкиОшибки.
Доступность:
Тонкий клиент, веб-клиент, мобильный клиент, толстый клиент.
См. также:
МенеджерСистемыВзаимодействия, метод ОтключитьОбработчикДействияСообщения
Использование в версии:
Доступен, начиная с версии 8.3.11.
Не рекомендуется использовать, начиная с версии 8.3.18.
Описание изменено в версии 8.3.18.
Методическая информация
Практические примеры
Собственное GUI-меню: подменю, разделитель, пометка, доступность и разные callback-контракты
Учебный разбор реально сохраняемых Действия/коллекции с прямым предупреждением об устаревании. Одна собственная карточка, два действия, одно собственное вложение и GUI-проверка750; серверные записи отделены от локальной отметки меню.
Где и как запускать
Полный модуль своей управляемой формы, управляемый thin/web/thick клиент. Полный поддерживающий код отдельно вставьте в серверный общий модуль УчебноеМеню85. Все real own-data создаются/проверяются сервером в новом пустом стенде, UI-события вызывает платформа после ваших ручных кликов.
Подготовка
Новая пустая база8.5.1.1343, имя конфигурации УчебноеМенюСВ85, русский язык, управляемый интерфейс, совместимость Не использовать. Нет предметных объектов, подписок, фоновых заданий, ботов и расширений. Не запускайте этот код в рабочих УТ/КА/УНФ/БП.
Разверните вручную свой отдельный сервер взаимодействия в собственной изолированной VM: только http://localhost:9094, отдельный собственный абонент и приложение УчебноеМенюСВ85. Без SMTP, публичного доступа, внешних интеграций/общих приложений/чужих аккаунтов. Установка и серверный конфигурационный файл берутся из документации вашей поставки, здесь не выдумываются.
Для реального пункта меню Вложение вручную подключите собственное локальное файловое хранилище/собственный пустой бакет в этой VM. Не используйте реальные S3 ключи/бакеты. ПоддерживаетсяРаботаСВложениями подтверждает подключение, а не доступность/квоты; при его Ложь код отказывает до создания данных.
В конфигураторе вручную создайте ровно одного собственного пользователя Уч85Связь_<ваш новый канонический ненулевой UUID>, полное имя Учебный участник. Реальный IB UUID отличается от метки имени. Права/регистрация/синхронизация задаются вручную только в стенде; код не создает учетные записи или права.
Внутренний пользователь системы взаимодействия должен совпадать с actual IBUser: real ID получен из IBUUID и подтвержден обратно. Нет внешних ID/почты/телефона/ключей сопоставления, пользователь не заблокирован. Свое приложение зарегистрировано с точным именем УчебноеМенюСВ85.
Создайте серверный общий модуль УчебноеМеню85, флаг Сервер включен, Привилегированный/Клиент выключены. Вставьте серверный код целиком. Клиентский код целиком вставьте отдельно в модуль своей внешней обработки/управляемой формы. Не склеивайте эти два модуля в одном месте.
В форме добавьте строковый реквизит Подтверждение длиной100 и булев реквизит ПроверятьСтарыеДействия, первоначально Ложь. Добавьте команды ПодготовитьКарточку, ПодключитьМеню, ОтключитьМеню с одноименными обработчиками; покажите поле подтверждения и флажок старых действий.
Оставьте только свой сеанс. Сначала попробуйте пустое подтверждение: записи не должно быть. Заново откройте форму, прочитайте предупреждения и задайте СВОЕ УЧЕБНОЕ МЕНЮ; нажмите ПодготовитьКарточку один раз. Повтор блокируется до вызова сервера, даже если он завершился ошибкой.
Нажмите ПодключитьМеню. Затем вручную откройте только новое собственное обсуждение по показанному UUID-ключу/заголовку. Само открытие не автоматизировано: код не угадывает внутренние формы/их параметры конкретной конфигурации.
Для современного меню оставьте ПроверятьСтарыеДействия=Ложь. Правой кнопкой по самой своей карточке/вложению/ссылке/действию получите подменю Учебная проверка750. Выберите Проверить: Книга,3×250=750, заново откройте меню и проверьте пометку/недоступность повторной проверки.
Ссылка в карточке — собственный локальный URI http://localhost:9094/?lab=<UUID> только для определения ИсточникКоманд. Это НЕ API-endpoint и не запрос к серверу. Обработчик своего меню убирает native navigation/default и не открывает URI/браузер/файл. Не щелкайте ссылку после отключения учебного обработчика.
Только для разбора legacy включите ПроверятьСтарыеДействия=Истина и левой кнопкой нажмите собственное Проверить750/Показать расчет. Формирователь меню тогда сохраняет стандартное действие и legacy handler должен получить собственную copy-message/action. Это API не рекомендуется с8.3.18; коллекция Действия не рекомендуется с8.3.25.
Не создавайте одновременно современный обработчик ПриВыбореДействияСообщенияСистемыВзаимодействия в модуле управляемого приложения: этот урок изолирует legacy-маршрут, не называет его рекомендованной новой архитектурой. Современные бот-кнопки требуют модуля бота и здесь не имитируются.
Перед закрытием формы отключите именно свои два handler кнопкой ОтключитьМеню. После ошибок нет автоматического повтора/удаления/отката; новый UUID-ключ сохраните для ручного поиска частичного результата. Никаких реальных 1С/GUI/network/files/S3 операций при подготовке этого урока не выполнялось.
Перем СвойПакет85;
Перем НачатаПодготовка85;
Перем Подключено85;
Перем СвоеМеню85;
Перем СвоиДействия85;
Перем Запрос85;
Перем Проверено85;
&НаКлиенте
Процедура ПодготовитьКарточку(Команда)
Если НачатаПодготовка85 = Истина Тогда
ВызватьИсключение "Повтор подготовки запрещен. Проверьте частичный результат вручную";
КонецЕсли;
НачатаПодготовка85 = Истина;
СвойПакет85 = ПодготовитьНаСервере85();
Проверено85 = Ложь;
Сообщить("Своя карточка создана: " + СвойПакет85.Ключ);
КонецПроцедуры
&НаСервере
Функция ПодготовитьНаСервере85()
Возврат УчебноеМеню85.СоздатьСвоюКарточкуМеню85(Подтверждение);
КонецФункции
&НаКлиенте
Процедура ПодключитьМеню(Команда)
ПроверитьСвойКлиент85();
Если Подключено85 = Истина Тогда
ВызватьИсключение "Свой обработчик уже подключен";
КонецЕсли;
ПроверитьНаСервере85(СвойПакет85);
Запрос85 = Строка(Новый УникальныйИдентификатор);
СвоеМеню85 = Новый ОписаниеОповещения("СформироватьСвоеМеню85", ЭтотОбъект, СвоиПараметры85(), "ОшибкаСвоегоМеню85", ЭтотОбъект);
СвоиДействия85 = Новый ОписаниеОповещения("ВыбраноСвоеСтароеДействие85", ЭтотОбъект, СвоиПараметры85(), "ОшибкаСвоегоМеню85", ЭтотОбъект);
Подключено85 = Истина;
Попытка
СистемаВзаимодействия.ПодключитьОбработчикФормированияКоманд(СвоеМеню85);
СистемаВзаимодействия.ПодключитьОбработчикДействияСообщения(СвоиДействия85);
Исключение
Сообщить("Подключение могло выполниться частично. Используйте только явное ОтключитьМеню; повторов нет");
ВызватьИсключение;
КонецПопытки;
Сообщить("Подключен только собственный обработчик. Откройте свою карточку в интерфейсе обсуждений");
КонецПроцедуры
&НаКлиенте
Процедура ОтключитьМеню(Команда)
ПроверитьСвойКлиент85();
Если Подключено85 <> Истина Тогда
ВызватьИсключение "Нет собственного подключенного обработчика";
КонецЕсли;
СистемаВзаимодействия.ОтключитьОбработчикФормированияКоманд(СвоеМеню85);
СистемаВзаимодействия.ОтключитьОбработчикДействияСообщения(СвоиДействия85);
Подключено85 = Ложь;
Запрос85 = Строка(Новый УникальныйИдентификатор);
Сообщить("Отключены именно два своих описания обработчиков; другие не удалялись");
КонецПроцедуры
&НаКлиенте
Процедура СформироватьСвоеМеню85(ПараметрыКоманд, Команды, КомандаПоУмолчанию, ДополнительныеПараметры) Экспорт
Если Не СвоеСобытие85(ДополнительныеПараметры) Тогда
Возврат;
КонецЕсли;
Если ТипЗнч(ПараметрыКоманд) <> Тип("ПараметрыФормированияКомандСистемыВзаимодействия")
Или ТипЗнч(Команды) <> Тип("Массив") Тогда
Возврат;
КонецЕсли;
Обсуждение = ПараметрыКоманд.Обсуждение;
Если ТипЗнч(Обсуждение) <> Тип("ОбсуждениеСистемыВзаимодействия") Тогда
Возврат;
КонецЕсли;
Если Строка(Обсуждение.Идентификатор) <> СвойПакет85.Обсуждение Или Обсуждение.Ключ <> СвойПакет85.Ключ
Или Обсуждение.Участники.Количество() <> 1 Или Обсуждение.Интеграция <> Неопределено
Или Обсуждение.КонтекстОбсуждения <> Неопределено
Или Не Обсуждение.Участники.Содержит(СистемаВзаимодействия.ИдентификаторТекущегоПользователя()) Тогда
Возврат;
КонецЕсли;
Если ПараметрыКоманд.Пользователь <> Неопределено
И Строка(ПараметрыКоманд.Пользователь) <> СвойПакет85.Автор Тогда
Возврат;
КонецЕсли;
Сообщение = ПараметрыКоманд.Сообщение;
Если ТипЗнч(Сообщение) <> Тип("СообщениеСистемыВзаимодействия") Тогда
Возврат;
КонецЕсли;
Если Строка(Сообщение.Идентификатор) <> СвойПакет85.Сообщение
Или Строка(Сообщение.Обсуждение) <> СвойПакет85.Обсуждение
Или Строка(Сообщение.Автор) <> СвойПакет85.Автор
Или Сообщение.Данные <> СвойПакет85.Ключ + "|Книга|3|250|750" Тогда
Возврат;
КонецЕсли;
Если ПараметрыКоманд.Источник = ИсточникКомандСистемыВзаимодействия.Вложение Тогда
Вложение = ПараметрыКоманд.Вложение;
Если ТипЗнч(Вложение) <> Тип("ВложениеСистемыВзаимодействия") Тогда
Возврат;
КонецЕсли;
Если Вложение.Наименование <> СвойПакет85.Ключ + "-guide.txt" Или Вложение.ТипСодержимого <> "text/plain"
Или Вложение.СодержимоеУдалено Или Вложение.Размер <> СвойПакет85.РазмерВложения Тогда
Возврат;
КонецЕсли;
ИначеЕсли ПараметрыКоманд.Источник = ИсточникКомандСистемыВзаимодействия.Действие Тогда
Если ТипЗнч(ПараметрыКоманд.Действие) <> Тип("Строка") Тогда
Возврат;
КонецЕсли;
Если ПараметрыКоманд.Действие <> СвойПакет85.Ключ + "|750"
И ПараметрыКоманд.Действие <> СвойПакет85.Ключ + "|Расчет" Тогда
Возврат;
КонецЕсли;
Если ПроверятьСтарыеДействия Тогда
Возврат;
КонецЕсли;
ИначеЕсли ПараметрыКоманд.Источник = ИсточникКомандСистемыВзаимодействия.НавигационнаяСсылка Тогда
Если ПараметрыКоманд.НавигационнаяСсылка <> СвойПакет85.Ссылка Тогда
Возврат;
КонецЕсли;
ИначеЕсли ПараметрыКоманд.Источник = ИсточникКомандСистемыВзаимодействия.Пользователь Тогда
Если ПараметрыКоманд.Пользователь = Неопределено Тогда
Возврат;
КонецЕсли;
ИначеЕсли ПараметрыКоманд.Источник <> ИсточникКомандСистемыВзаимодействия.Сообщение Тогда
Возврат;
КонецЕсли;
ПроверитьНаСервере85(СвойПакет85);
Подменю = Новый Массив;
Проверка = Новый ОписаниеКомандыСистемыВзаимодействия(Новый ОписаниеОповещения("ПроверитьСумму85", ЭтотОбъект, СвоиПараметры85()), "Проверить сумму");
Проверка.Доступность = Не Проверено85;
Проверка.Картинка = Новый Картинка;
Проверка.Пометка = Проверено85;
Если Проверено85 Тогда
Проверка.Представление = "Сумма750 уже проверена в этом окне";
Иначе
Проверка.Представление = "Проверить: Книга, 3 × 250 = 750";
КонецЕсли;
Если ТипЗнч(Проверка.Команда) <> Тип("ОписаниеОповещения") Тогда
ВызватьИсключение "Собственная команда должна вызывать только указанную процедуру";
КонецЕсли;
Подменю.Добавить(Проверка);
Сброс = Новый ОписаниеКомандыСистемыВзаимодействия(Новый ОписаниеОповещения("СброситьСвоюПометку85", ЭтотОбъект, СвоиПараметры85()), "Снять только локальную пометку");
Сброс.Доступность = Проверено85;
Подменю.Добавить(Сброс);
Разделитель = Новый ОписаниеКомандыСистемыВзаимодействия(Неопределено);
Статус = Новый ОписаниеКомандыСистемыВзаимодействия(Новый ОписаниеОповещения("ПроверитьСумму85", ЭтотОбъект, СвоиПараметры85()), "Серверная карточка750; меню ничего не отправляет");
Статус.Доступность = Ложь;
Команды.Очистить();
Команды.Добавить(Новый ОписаниеКомандыСистемыВзаимодействия(Подменю, "Учебная проверка750"));
Команды.Добавить(Разделитель);
Команды.Добавить(Статус);
КомандаПоУмолчанию = Неопределено;
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьСумму85(ДополнительныеПараметры) Экспорт
Если Не СвоеСобытие85(ДополнительныеПараметры) Тогда
Возврат;
КонецЕсли;
Факт = ПроверитьНаСервере85(СвойПакет85);
Если Факт.Количество * Факт.Цена <> Факт.Сумма Или Факт.Сумма <> 750 Тогда
ВызватьИсключение "Собственная сумма не совпала";
КонецЕсли;
Проверено85 = Истина;
Сообщить(Факт.Книга + ": " + Строка(Факт.Количество) + " × " + Строка(Факт.Цена) + " = " + Строка(Факт.Сумма));
КонецПроцедуры
&НаКлиенте
Процедура СброситьСвоюПометку85(ДополнительныеПараметры) Экспорт
Если Не СвоеСобытие85(ДополнительныеПараметры) Тогда
Возврат;
КонецЕсли;
ПроверитьНаСервере85(СвойПакет85);
Проверено85 = Ложь;
Сообщить("Снята только локальная пометка этой формы. Карточка на сервере не изменена");
КонецПроцедуры
&НаКлиенте
Процедура ВыбраноСвоеСтароеДействие85(Сообщение, Действие, ДополнительныеПараметры) Экспорт
Если Не СвоеСобытие85(ДополнительныеПараметры) Или Не ПроверятьСтарыеДействия Тогда
Возврат;
КонецЕсли;
Если ТипЗнч(Сообщение) <> Тип("СообщениеСистемыВзаимодействия") Или ТипЗнч(Действие) <> Тип("Строка") Тогда
Возврат;
КонецЕсли;
Если Строка(Сообщение.Идентификатор) <> СвойПакет85.Сообщение
Или Строка(Сообщение.Обсуждение) <> СвойПакет85.Обсуждение
Или Строка(Сообщение.Автор) <> СвойПакет85.Автор
Или Сообщение.Данные <> СвойПакет85.Ключ + "|Книга|3|250|750" Тогда
Возврат;
КонецЕсли;
Если Действие <> СвойПакет85.Ключ + "|750" И Действие <> СвойПакет85.Ключ + "|Расчет" Тогда
Возврат;
КонецЕсли;
ПроверитьСумму85(ДополнительныеПараметры);
Сообщить("Это старый native Action handler для разбора совместимости, не современная бот-панель");
КонецПроцедуры
&НаКлиенте
Процедура ОшибкаСвоегоМеню85(ИнформацияОбОшибке, ДополнительныеПараметры) Экспорт
Если СвоеСобытие85(ДополнительныеПараметры) Тогда
Сообщить("Свой обработчик завершился ошибкой. Сырые сведения не выводятся, повторов/удалений нет");
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПроверитьНаСервере85(Пакет)
Возврат УчебноеМеню85.ПроверитьСвоюКарточкуМеню85(Пакет);
КонецФункции
&НаКлиенте
Процедура ПроверитьСвойКлиент85()
Если СвойПакет85 = Неопределено Тогда
ВызватьИсключение "Сначала создайте собственную карточку";
КонецЕсли;
Если Не СистемаВзаимодействия.ИнформационнаяБазаЗарегистрирована()
Или СистемаВзаимодействия.АдресСервера() <> "http://localhost:9094"
Или Строка(СистемаВзаимодействия.ИдентификаторТекущегоПользователя()) <> СвойПакет85.Автор Тогда
ВызватьИсключение "Свой сервер или текущий пользователь изменены";
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Функция СвоеСобытие85(Параметры)
Если СвойПакет85 = Неопределено Или Подключено85 <> Истина Тогда
Возврат Ложь;
КонецЕсли;
Если Параметры.Запрос <> Запрос85 Или Параметры.Форма <> Строка(УникальныйИдентификатор)
Или Параметры.Ключ <> СвойПакет85.Ключ Тогда
Возврат Ложь;
КонецЕсли;
ПроверитьСвойКлиент85();
Возврат Истина;
КонецФункции
&НаКлиенте
Функция СвоиПараметры85()
Возврат Новый Структура("Запрос,Форма,Ключ", Запрос85, Строка(УникальныйИдентификатор), СвойПакет85.Ключ);
КонецФункцииПолный серверный общий модуль УчебноеМеню85: real own UUID-card/legacy-actions/attachment/fresh read
Собственная конфигурация → Общие → Общие модули → УчебноеМеню85.
Серверный общий модуль своей новой конфигурации УчебноеМенюСВ85; не переносить на клиент.
Функция СоздатьСвоюКарточкуМеню85(Подтверждение) Экспорт
Пользователь = ПроверитьСвоегоПользователя85();
Если Подтверждение <> "СВОЕ УЧЕБНОЕ МЕНЮ" Тогда
ВызватьИсключение "Прочитайте подготовку. Собственная карточка не создана";
КонецЕсли;
Если Не СистемаВзаимодействия.ПоддерживаетсяРаботаСВложениями() Тогда
ВызватьИсключение "Для настоящего вложения подготовьте свое локальное файловое хранилище";
КонецЕсли;
UUIDКлюча = Новый УникальныйИдентификатор;
Ключ = "Уч85Меню_" + Строка(UUIDКлюча);
СвояСсылка = "http://localhost:9094/?lab=" + Строка(UUIDКлюча);
Сообщить("Свой новый кандидат ключа: " + Ключ);
Если СистемаВзаимодействия.ПолучитьОбсуждение(Ключ) <> Неопределено Тогда
ВызватьИсключение "Ключ уже существует; прежние данные не меняются";
КонецЕсли;
Обсуждение = СистемаВзаимодействия.СоздатьОбсуждение();
Обсуждение.Ключ = Ключ;
Обсуждение.Заголовок = Ключ + " | Меню карточки750";
Обсуждение.Групповое = Истина;
Обсуждение.Отображаемое = Истина;
Обсуждение.ТолькоПросмотр = Ложь;
Обсуждение.КонтекстОбсуждения = Неопределено;
Обсуждение.Интеграция = Неопределено;
Обсуждение.ВнешнийИдентификатор = "";
Обсуждение.Участники.Добавить(Пользователь.Идентификатор);
Обсуждение.Записать();
Сообщение = СистемаВзаимодействия.СоздатьСообщение(Обсуждение.Идентификатор);
Сообщение.Текст = Ключ + " | Книга: 3 по 250 = 750 | " + СвояСсылка;
Сообщение.Данные = Ключ + "|Книга|3|250|750";
Сообщение.ФорматДанныхСтроки = "text/plain";
Сообщение.Получатели.Добавить(Новый ИдентификаторПользователяСистемыВзаимодействия);
Если Сообщение.Действия.Количество() <> 0 Или Сообщение.Вложения.Количество() <> 0 Тогда
ВызватьИсключение "Новый объект карточки уже заполнен";
КонецЕсли;
ПустаяКартинка = Новый Картинка;
Действия = Сообщение.Действия;
Проверка = Действия.Добавить(Ключ + "|750", "Проверить сумму", ПустаяКартинка);
Расчет = Действия.Вставить(0, Ключ + "|Расчет", "Показать расчет", ПустаяКартинка);
Действия.Добавить(Ключ + "|Черновик", "Лишний черновик", ПустаяКартинка);
Если Действия.Количество() <> 3 Или Действия.Индекс(Проверка) <> 1
Или Действия.Найти(Ключ + "|Расчет") <> Расчет Тогда
ВызватьИсключение "Собственные старые действия не добавлены";
КонецЕсли;
Действия.Сдвинуть(Расчет, 1);
Действия.Сдвинуть(2, -1);
Если Действия.Получить(1).Значение <> Ключ + "|Черновик" Или Действия.Индекс(Проверка) <> 0 Тогда
ВызватьИсключение "Порядок собственных действий не совпал";
КонецЕсли;
Действия.Удалить(1);
Проверка.Текст = "Проверить750";
Проверка.Значение = Ключ + "|750";
Проверка.Картинка = ПустаяКартинка;
Если Действия.Количество() <> 2 Или Действия.Получить(0).Текст <> "Проверить750"
Или Действия.Получить(1).Значение <> Ключ + "|Расчет"
Или Действия.Найти(Ключ + "|Черновик") <> Неопределено
Или ТипЗнч(Проверка.Картинка) <> Тип("Картинка") Тогда
ВызватьИсключение "Окончательная собственная коллекция действий неверна";
КонецЕсли;
Байты = ПолучитьДвоичныеДанныеИзСтроки(Ключ + "|Книга|3|250|750", "UTF-8", Ложь);
ПотокСвоейКарточки = Байты.ОткрытьПотокДляЧтения();
Попытка
Сообщение.Вложения.Добавить(ПотокСвоейКарточки, Ключ + "-guide.txt", "text/plain", Истина);
ПроверитьСвоеОбсуждениеМеню85(Ключ, Строка(Обсуждение.Идентификатор), Пользователь.Идентификатор);
Сообщение.Записать();
Исключение
ПотокСвоейКарточки.Закрыть();
ВызватьИсключение;
КонецПопытки;
ПотокСвоейКарточки.Закрыть();
Пакет = Новый Структура("Ключ,Обсуждение,Сообщение,Автор,Ссылка,РазмерВложения", Ключ,
Строка(Обсуждение.Идентификатор), Строка(Сообщение.Идентификатор), Строка(Пользователь.Идентификатор), СвояСсылка, Байты.Размер());
ПроверитьСвоюКарточкуМеню85(Пакет);
Черновик = СистемаВзаимодействия.СоздатьСообщение(Обсуждение.Идентификатор);
Черновик.Действия.Добавить(Ключ + "|НеОтправлять", "Неотправленный черновик", ПустаяКартинка);
Если Черновик.Действия.Количество() <> 1 Тогда
ВызватьИсключение "Собственный отдельный черновик не заполнен";
КонецЕсли;
Черновик.Действия.Очистить();
Если Черновик.Действия.Количество() <> 0 Тогда
ВызватьИсключение "Собственный отдельный черновик не очищен";
КонецЕсли;
Возврат Пакет;
КонецФункции
Функция ПроверитьСвоюКарточкуМеню85(Пакет) Экспорт
Пользователь = ПроверитьСвоегоПользователя85();
Если ТипЗнч(Пакет) <> Тип("Структура") Или ТипЗнч(Пакет.Ключ) <> Тип("Строка")
Или Лев(Пакет.Ключ, СтрДлина("Уч85Меню_")) <> "Уч85Меню_" Тогда
ВызватьИсключение "Нужен собственный пакет, возвращенный подготовкой";
КонецЕсли;
UUIDКлюча = Новый УникальныйИдентификатор(Сред(Пакет.Ключ, СтрДлина("Уч85Меню_") + 1));
Если Пакет.Ключ <> "Уч85Меню_" + Строка(UUIDКлюча)
Или UUIDКлюча = Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000")
Или Пакет.Ссылка <> "http://localhost:9094/?lab=" + Строка(UUIDКлюча)
Или Пакет.Автор <> Строка(Пользователь.Идентификатор) Тогда
ВызватьИсключение "UUID-маркер, локальная ссылка или собственный автор изменены";
КонецЕсли;
Обсуждение = ПроверитьСвоеОбсуждениеМеню85(Пакет.Ключ, Пакет.Обсуждение, Пользователь.Идентификатор);
Отбор = Новый ОтборСообщенийСистемыВзаимодействия;
Отбор.Обсуждение = Обсуждение.Идентификатор;
Отбор.Количество = 10;
Отбор.НаправлениеСортировки = НаправлениеСортировки.Возр;
Сообщения = СистемаВзаимодействия.ПолучитьСообщения(Отбор);
Если Сообщения.Количество() <> 1 Тогда
ВызватьИсключение "В своем отдельном обсуждении должна оставаться одна карточка";
КонецЕсли;
Факт = Сообщения[0];
Байты = ПолучитьДвоичныеДанныеИзСтроки(Пакет.Ключ + "|Книга|3|250|750", "UTF-8", Ложь);
Если Строка(Факт.Идентификатор) <> Пакет.Сообщение Или Факт.Обсуждение <> Обсуждение.Идентификатор
Или Факт.Автор <> Пользователь.Идентификатор Или Факт.Данные <> Пакет.Ключ + "|Книга|3|250|750"
Или Факт.Текст <> Пакет.Ключ + " | Книга: 3 по 250 = 750 | " + Пакет.Ссылка
Или Факт.Действия.Количество() <> 2 Или Факт.Вложения.Количество() <> 1
Или Пакет.РазмерВложения <> Байты.Размер() Тогда
ВызватьИсключение "Сохраненная собственная карточка изменена";
КонецЕсли;
Если Факт.Действия.Получить(0).Значение <> Пакет.Ключ + "|750"
Или Факт.Действия.Получить(0).Текст <> "Проверить750"
Или Факт.Действия.Получить(1).Значение <> Пакет.Ключ + "|Расчет"
Или Факт.Действия.Получить(1).Текст <> "Показать расчет" Тогда
ВызватьИсключение "Действия своей карточки изменены";
КонецЕсли;
Вложение = Факт.Вложения.Получить(0);
Если Вложение.Наименование <> Пакет.Ключ + "-guide.txt" Или Вложение.ТипСодержимого <> "text/plain"
Или Не Вложение.Отображаемое Или Вложение.СодержимоеУдалено Или Вложение.Размер <> Байты.Размер() Тогда
ВызватьИсключение "Паспорт вложения собственной карточки изменен";
КонецЕсли;
Возврат Новый Структура("Книга,Количество,Цена,Сумма", "Книга", 3, 250, 3 * 250);
КонецФункции
Функция ПроверитьСвоеОбсуждениеМеню85(Ключ, СтрокаИД, ИДПользователя)
Факт = СистемаВзаимодействия.ПолучитьОбсуждение(Ключ);
Если Факт = Неопределено Тогда
ВызватьИсключение "Собственное обсуждение не найдено";
КонецЕсли;
Если Строка(Факт.Идентификатор) <> СтрокаИД Или Факт.Ключ <> Ключ
Или Факт.Заголовок <> Ключ + " | Меню карточки750" Или Не Факт.Групповое
Или Не Факт.Отображаемое Или Факт.ТолькоПросмотр Или Факт.КонтекстОбсуждения <> Неопределено
Или Факт.Интеграция <> Неопределено Или Факт.ВнешнийИдентификатор <> ""
Или Факт.Участники.Количество() <> 1 Или Не Факт.Участники.Содержит(ИДПользователя) Тогда
ВызватьИсключение "Паспорт своего отдельного обсуждения изменен";
КонецЕсли;
Возврат Факт;
КонецФункции
Функция ПроверитьСвоегоПользователя85()
ПроверитьСвойПустойСтенд();
Если ПривилегированныйРежим() Тогда
ВызватьИсключение "Входной привилегированный режим не допускается";
КонецЕсли;
Пользователи = ПользователиИнформационнойБазы.ПолучитьПользователей();
Текущий = ПользователиИнформационнойБазы.ТекущийПользователь();
Если Пользователи.Количество() <> 1 Или Текущий = Неопределено Тогда
ВызватьИсключение "Нужен единственный собственный учебный пользователь";
КонецЕсли;
Если Пользователи[0].УникальныйИдентификатор <> Текущий.УникальныйИдентификатор
Или Лев(Текущий.Имя, СтрДлина("Уч85Связь_")) <> "Уч85Связь_" Тогда
ВызватьИсключение "Учебный пользователь не подтвержден";
КонецЕсли;
UUIDМетки = Новый УникальныйИдентификатор(Сред(Текущий.Имя, СтрДлина("Уч85Связь_") + 1));
Если Текущий.Имя <> "Уч85Связь_" + Строка(UUIDМетки)
Или UUIDМетки = Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000") Тогда
ВызватьИсключение "Имя должно содержать собственный канонический ненулевой UUID";
КонецЕсли;
Если Не СистемаВзаимодействия.ИнформационнаяБазаЗарегистрирована()
Или Не СистемаВзаимодействия.ИспользованиеДоступно() Тогда
ВызватьИсключение "Вручную подготовьте регистрацию собственной учебной базы";
КонецЕсли;
Если СистемаВзаимодействия.АдресСервера() <> "http://localhost:9094" Тогда
ВызватьИсключение "Разрешен только отдельно подготовленный сервер localhost:9094 в своей VM";
КонецЕсли;
Версия = СистемаВзаимодействия.ВерсияСервера();
Если ТипЗнч(Версия) <> Тип("Строка") Или Не ЗначениеЗаполнено(Версия) Тогда
ВызватьИсключение "Версия своего сервера не получена";
КонецЕсли;
ИД = СистемаВзаимодействия.ИдентификаторТекущегоПользователя();
Если ТипЗнч(ИД) <> Тип("ИдентификаторПользователяСистемыВзаимодействия") Тогда
ВызватьИсключение "Не получен реальный идентификатор собственного участника";
КонецЕсли;
Если СистемаВзаимодействия.ПолучитьИдентификаторПользователя(Текущий.УникальныйИдентификатор) <> ИД
Или СистемаВзаимодействия.ПолучитьИдентификаторПользователяИнформационнойБазы(ИД) <> Текущий.УникальныйИдентификатор Тогда
ВызватьИсключение "Двустороннее соответствие своей учетной записи неверно";
КонецЕсли;
Пользователь = СистемаВзаимодействия.ПолучитьПользователя(ИД);
Если Пользователь.Идентификатор <> ИД Или Пользователь.Имя <> Текущий.Имя
Или Пользователь.ПолноеИмя <> Текущий.ПолноеИмя
Или Пользователь.ИдентификаторПользователяИнформационнойБазы <> Текущий.УникальныйИдентификатор Тогда
ВызватьИсключение "Паспорт своего пользователя не совпал с информационной базой";
КонецЕсли;
Если Пользователь.Внешний Или Пользователь.Заблокирован
Или Пользователь.ТипВнешнейСистемы <> ""
Или Пользователь.ИдентификаторПользователяВнешнейСистемы <> Неопределено
Или Пользователь.АдресЭлектроннойПочты <> ""
Или Пользователь.АдресЭлектроннойПочтыДляАутентификации <> ""
Или Пользователь.НомерТелефона <> ""
Или Пользователь.КлючСопоставления <> "" Тогда
ВызватьИсключение "Нужен чистый внутренний пользователь без персональных контактов и объединений";
КонецЕсли;
Если Пользователь.АдминистраторАбонента <> СистемаВзаимодействия.ТекущийПользовательЯвляетсяАдминистраторомАбонента() Тогда
ВызватьИсключение "Признак администратора абонента не согласован";
КонецЕсли;
ВсеПользователи = СистемаВзаимодействия.СтандартныеПользователи.ВсеПользователиПриложения;
Если ИД = ВсеПользователи Или ИД = Новый ИдентификаторПользователяСистемыВзаимодействия Тогда
ВызватьИсключение "Стандартный адресат и Никого не оповещать не являются своей учетной записью";
КонецЕсли;
ВосстановленныйИД = Новый ИдентификаторПользователяСистемыВзаимодействия(Строка(ИД));
Если СистемаВзаимодействия.ПолучитьПользователя(ВосстановленныйИД).Идентификатор <> ИД Тогда
ВызватьИсключение "Восстановленный из строки реальный ID не подтвержден сервером";
КонецЕсли;
Приложение = СистемаВзаимодействия.ПолучитьПриложение(СистемаВзаимодействия.ИдентификаторТекущегоПриложения());
Если Приложение.Идентификатор <> СистемаВзаимодействия.ИдентификаторТекущегоПриложения()
Или Приложение.Наименование <> "УчебноеМенюСВ85" Тогда
ВызватьИсключение "Нужно свое приложение с точным учебным наименованием";
КонецЕсли;
Возврат Пользователь;
КонецФункции
Процедура ПроверитьСвойПустойСтенд()
Если Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
КонецЕсли;
Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
КонецЕсли;
Источники = Новый Массив;
Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
Для Каждого Источник Из Источники Цикл
Расширения = РасширенияКонфигурации.Получить(, Источник);
Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
ВызватьИсключение "Native список расширений должен иметь тип Массив";
КонецЕсли;
Если Расширения.Количество() <> 0 Тогда
ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
КонецЕсли;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
КонецЕсли;
КонецПроцедурыРазбор по шагам
Пустая CF/платформа8.5.1.1343/отсутствие расширений/непривилегированный режим/real user+application/localhost проверяются до создания объектов. localhost и UUID-маркер не заменяют ручную проверку выделенной VM/абонента.
Новое собственное обсуждение содержит ровно собственного пользователя, без контекста/интеграции/внешних ID. Карточка содержит учебный расчет3×250=750 и собственный UUID-маркер; адресат Никого не оповещать — не реальный человек и не ВсеПользователиПриложения.
Добавить/Вставить старой коллекции действительно возвращают ДействиеСообщенияСистемыВзаимодействия. Начальный список [расчет,проверка,черновик] переставляется двумя overload Сдвинуть: по объекту и индексу; затем удаляется только лишний черновик нового сообщения.
Найти сравнивает Значение и возвращает первое совпадение либо Неопределено. Собственный UUID+суффикс отделяет750 от расчета/черновика. Индекс/Получить/Количество проверяют конечный порядок [проверка,расчет].
Текст/Значение/Картинка читаются и записываются для собственного нового действия. Пустая Картинка — документированный noarg constructor, а не строка/файл/выдуманный icon. Явный текст не дает UI показывать произвольное представление Значения.
Собственное вложение создано только из маленькой строки UTF-8 без BOM и собственного памяти-потока. Поток остается открытым до Записать и закрывается при успехе/ошибке. Никакие имеющиеся файлы пользователя не читаются.
Отдельный новый неотправленный черновик имеет одно собственное действие; Очистить проверяет0. Записать для этого черновика отсутствует, сохраненная карточка не очищается.
Свежая серверная проверка читает сообщения только собственного проверенного обсуждения, требует ровно одну карточку и сверяет real IDs, автора, marker/data/text, оба действия и паспорт вложения. Произвольный чужой MessageID напрямую не передается в GetMessage.
Подключение формирователя меню и legacy action-handler сохраняет два конкретных ОписанияОповещения с UUID-запросом/формой/ключом. Отписка использует именно эти описания, не отключение всех обработчиков. Частичный успех подключения допускается и не скрывается.
Формирователь имеет native подпись (ПараметрыКоманд,Команды,КомандаПоУмолчанию,ДополнительныеПараметры). Readonly Params поступает от платформы; мы не создаем его конструктором и не присваиваем поля.
Сначала проверяются own discussion/participants/user и actual own messageID/author/marker. Затем Источник отличает вложение, значение действия, локальную URI, собственный профиль автора и сообщение. Если нет собственного контекста/сообщения — return сохраняет исходные меню/default чужого UI.
Только после всех ownership/source проверок и свежего server-check собственный Массив команд очищается. Native Команда=Массив дает подменю, Команда=Неопределено дает разделитель, Команда=ОписаниеОповещения вызывает процедуру с одним ДополнительныеПараметры.
Своей команде задаются Доступность/Пометка/пустая Картинка/Представление; Команда проверяется как ОписаниеОповещения. КомандаПоУмолчанию=Неопределено показывает меню на левый клик и не выполняет native открытие URI/вложения сразу.
ПроверитьСумму делает свежую серверную проверку и сравниваетКоличество×Цена с750. Локальное Проверено85 меняется только в текущей форме. Снять пометку также не пишет сообщение/файл/профиль; новый вызов меню отражает доступность и пометку.
Legacy action route включается флажком отдельно: собственные действия оставляют native меню/default неизменными; нажимайте их вручную. Handler получает (Сообщение,Действие,ДополнительныеПараметры), проверяет scope и выполняет ту же проверку750 без повторного Записать.
Как проверить результат
После ручной подготовки на сервере одна собственная карточка с двумя старыми действиями и одним собственным вложением; черновик не отправлен. Сумма750 и UUID-маркер отдельно сверены.
Внутри своего контекста настоящее подменю/разделитель/недоступный статус появились из ОписаниеКоманды, а не из массива произвольных строк. Callback команды получает ровно один параметр.
Первый выбор Проверить показывает Книга:3×250=750. Следующий вызов меню отражает Пометка=Истина и Доступность=Ложь для повторной проверки; Снять только локальную пометку возвращает исходный UI-статус без изменения server card.
Readonly Params имеет семь native полей и не меняется. Сообщение/контекст не свои → исходные Команды и КомандаПоУмолчанию сохранены; тела чужих сообщений/ссылки/имена не выводятся.
Свою URI не открываем; собственное вложение не скачиваем/не открываем файлом. Меню только проверяет собственную карточку и локальную отметку; когда handler отключен, native стандартные действия снова могут выполнять открытия.
Флажок старого маршрута дает настоящий manual legacy action event; событие не вызывается прямым вызовом нашей процедуры. Современная бот-панель не заявлена как покрытая.
Если профиль пользователя вызывается без Сообщение/подтвержденного обсуждения, handler его намеренно не меняет. Пользователь — реальный optional ID, не объект профиля; native None не считается ошибкой.
Ожидаемый результат
При ручном запуске своего стенда остается собственная серверная карточка750. GUI формирует реальное безопасное меню, локальная пометка изменяется отдельно от серверных данных, а старый action-route разобран честно как deprecated. Статические проверки не являются подтверждением GUI/runtime1С; здесь реальных запусков нет.
На что обратить внимание
Действия/КоллекцияДействий deprecated с8.3.25, оба глобальных legacy handler deprecated с8.3.18. Это пример чтения/адаптации старого кода, не совет начинать новый проект на них.
Современная РядКнопок.Добавить требует модуль бота. Нельзя подменять старую коллекцию современной и объявлять тот же серверный модуль корректным бот-процессом.
ОписаниеКоманды требует первый аргумент Команда даже для разделителя: передается явное Неопределено. Строка сама по себе командой не является и по native не отображается.
Команда=ОписаниеОповещения вызывает callback с одним ДополнительныеПараметры. Четыре параметра формирователя и три параметра старого action-handler — другой контракт.
КомандаПоУмолчанию намеренно не объявлена параметром Знач: иначе присвоение Неопределено останется локальным и native URI/вложение может открыться.
Прежде чем очищать меню/default, нужно подтвердить собственный контекст и источник. В чужих/неизвестных случаях оставляйте исходные значения, а не удаляйте стандартные команды глобально.
Пометка/Доступность — клиентский UI-статус, не серверное подтверждение, не право доступа и не признак прочтения. После закрытия формы локальное Проверено85 не восстанавливается автоматически.
UUID-метка и localhost не доказывают владение сервером/бакетом. Собственный пустой стенд/абонент/ограниченные права обязательны; ни пароли, ни внешние файлы/контакты не используются.
Некоторые профильные контекстные события не содержат Сообщение; в таком случае этот урок намеренно сохраняет native UI. Не выдумывайте отсутствующее сообщение или параметры события вручную.
Старая action-гиперссылка при включенном legacy-флажке использует свой стандартный маршрут; меню режима проверки и старый маршрут не тестируются одним фиктивным прямым вызовом handler.
После отключения собственного меню локальная URI может снова получить стандартное открытие: не щелкайте ее. Урок ничего не отправляет при выполнении menu callbacks и не запускает браузер/файлы.
Ошибка записи/подключения может оставить частичный собственный результат. Код не повторяет и не удаляет автоматически; восстанавливайте только выделенный учебный сеанс вручную. Новых резервных копий нет.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.