Запуск Screen Flow в модальном окне в Salesforce Classic

Добрый день!

Клиент работает исключительно в классическом интерфейсе Salesforce, любимая фраза в больших компаниях: “исторически так сложилось…”.

Была поставлена задача, которая легко и быстро была реализована с помощью всеми нами любимыми Flows. После чего на объекте создаем кастомную кнопку, помещаем туда ссылку на наш Flow с параметрами и размещаем её на странице. Пока ничего сверх естественного.

Логика Flow для демонстрации упрощена и надуманна :slight_smile:

Заходим на страницу нашего объекта кликаем на кнопку и … наблюдаем грустную картину - одинокое поле, которое требует заполнения (наша задача была действительно довольно простой) и далеко, далеко (особенности моего 16" экрана и разрешения) в правом углу одиноко грустит стандартная кнопка Next (Finish). Всё работает как и задумывалось, но визуализация хромает: куча пустого места, отдаленность элементов друг от друга и т.п.

Увидев эту удручающую картину я задался вопросом, как сделать это всё так чтобы было ещё и красиво/компактно. На ум сразу приходит визуализация в модальном окне. Но, как я писал выше, клиент работает в классическом интерфейсе, и Action Button в modal window в этом случае не работает, или я чего-то не знаю (подскажите в комментариях).

Через три часа гугления на просторах интернета я объединил прочитанную информацию и реализовал следующую идею.

  1. Cоздаем JavaScript Custom Button вот с таким кодом, создающим модальное окно отображающим наш Flow:
let box = new SimpleDialog("id" + Math.random(), true); 

window.parent.box = box; 
box.createDialog(); 
box.setWidth('50%');
box.setContentInnerHTML("<p align='center'><iframe src='/flow/TestFlow?recordId={!Contact.Id}&retURL=/apex/ForceClose' scrolling='auto' width='95%' height='200px' frameborder='no'/></p>"); 
box.setupDefaultButtons(); 
box.show();

window.onmessage = (event) => {
	if (event.data === 'close') {
		window.parent.box.hide();
		window.location.reload();
	}
}

  1. Создаем Visualforce page “ForceClose” - данная страница будет загружена сразу после того как наш Screen Flow закончит свою работу (пользователь нажмет кнопку Next (Finish) или Cancel). Ссылка на эту страницу определена как параметр ссылки на Flow:
/flow/TestFlow?recordId={!Contact.Id}&retURL=/apex/ForceClose

Код ForceClose:

<apex:page showChat="false" showHeader="false" sidebar="false" applyBodyTag="false" applyHtmlTag="false">
    <html>
        <body onload="window.top.postMessage('close', '*')"/>
    </html>
</apex:page>

Эта станица делает очень важную часть работы, а именно позволяет скрыть наше модальное окно путем отсылки сообщения со словом ‘close’ (можно использовать любое слово :slight_smile: ) изнутри iframe на верхний уровень в контекст где создан сам SimpleDialog.
В родительском контексте реализован слушатель сообщений (см. выше JavaScript код для нашей кастомной кнопки):

window.onmessage = (event) => {
	if (event.data === 'close') {
		window.parent.box.hide();
		window.location.reload();
	}
}

который при получении соответствующего сообщения скроет модельное окно и обновит страницу на объекте, чтобы отобразить результат выполнения нашего Flow.

Ну вот собственно и всё.

Как всегда без недостатков не обошлось:

  1. Если Flow глюкнет по какой либо непредвиденной нами ошибке и пользователю выскочит страшное предупреждение что всё пропало, в этом случае Flow будет считаться не завершенным и дальнейшая магия с закрытием модального окна через Visualforce page уже не сработает. Для этого случая конечно имеет смысл реализовать отдельную кнопку может даже красивую в виде крестика (с помощью CSS) чтобы была возможность закрыть модальное окно напрямую.
  2. Из-за жестких требований безопасности браузера Safari iframe - не визуализирует flow. Подробнее можно почитать в интернете. В FireFox, Chrome и Edge этот подход работает.

Спасибо за внимание!

В дополнение, если кому интересно, улучшеная версия JavaScript кода кнопки:
с кнопкой скрытия модального окна (решение первого недостатка см. выше) и
определения браузера для запуска Flow без модального окна в Safari (обход второго недостатка см. выше):

let isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(navigator.userAgent);

let flowURL = `/flow/TestFlow?recordId={!Contact.Id}&retURL=/${isSafari ? '{!Contact.Id}' : '/apex/ForceClose'}`;

if (isSafari) {
	window.open(flowURL, '_self');
} else {

	let box = new SimpleDialog("id" + Math.random(), true); 

	window.parent.box = box; 

	box.createDialog(); 
	box.setTitle("<button class='slds-button slds-button_icon' style='float: right' onclick='window.parent.box.hide()'>&#10060;</button>");
	box.setWidth('50%');
	box.setContentInnerHTML(`<p align='center'><iframe src='${flowURL}' width='95%' height='175px' frameborder='no'/></p>`); 
	box.setupDefaultButtons(); 
	box.show();
	
	window.onmessage = (event) => {
		if (event.data === 'close') {
			window.parent.box.hide();
			window.location = window.location;  //лайфхак для обновления страницы объекта после окончания работы Flow (Firefox выдавал странное предупреждение когда я использовал просто window.location.reload())
		}
	}
}
7 Вподобань