Картинки на форме CRM, эпизод II
Желаете отобразить аватар пользователя, логотип компании или фото товара на форме CRM? Сейчас реализуем 🙂 и сделаем это двумя способами (точнее, двумя с половиной 🙂 ) и в обоих задействуем стандартный функционал CRM (и конечно же не по прямому назначению 🙂 )!
Вариант #1.1
В первом воспользуемся частью объекта Электронная почта, а точнее одной из его JavaScript-функций. Когда Вы отправляете электронную почту с добавленным изображением из outlook-клиента, CRM сохраняет этот рисунок как вложение и достает его в тело письма так:
<IMG id=Picture_x0020_1 alt=image001.png src="/MicrosoftCRM/Activities/Attachment/download.aspx?AttachmentType=1001&AttachmentId=4108f5f9-63f7-dd11-b2e0-0003ff2d0264" width=978 height=630>
Заменив тип объекта с Электронной почты (1001) на объект Примечание (код 5) Вы сможете подгрузить вложенную в примичание картинку (да и не только картинку). Например:
<IMG src="/MicrosoftCRM/Activities/Attachment/download.aspx?AttachmentType=5&AttachmentId=D02BFFF3-EFF1-DD11-A6A4-0003FF2D0264">
Теперь реализуем:
- Добавьте новую вкладку на форму CRM;
- В эту вклдку поместите iFrame (именно в нем будем показывать рисунок);
- На онлоад повесьте такой код (поменяйте в нем только номер вкладки на которой расположе iFrame)
var iFrame = crmForm.all.IFRAME_logo; // поменяйте номер вкладки на Ваш (счет начинается слева, с нуля) var tabVar= crmForm.all.tab4Tab; if(typeof(tabVar) != "undefined" && tabVar != null) { // здесь тоже замените номер вкладки crmForm.all.tab4Tab.attachEvent('onclick',loadlogo, false); } function loadlogo() { // проверяем что у объекта есть id - т.к. у не сохраненных записей не может быть вложений if (crmForm.ObjectId != null) { var authenticationHeader = GenerateAuthenticationHeader(); // Создаем SOAP запрос чтобы вытащить айдишник прикрепленного файла var xml = "<?xml version='1.0' encoding='utf-8'?>"+ "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'"+ " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"+ " xmlns:xsd='http://www.w3.org/2001/XMLSchema'>"+ authenticationHeader+ "<soap:Body>"+ "<Fetch xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>"+ "<fetchXml><fetch count='1' mapping='logical'><entity name='annotation'><attribute name='annotationid'/><order attribute='createdon' descending='true'/><filter type='and'><condition attribute='objectid' operator='eq' value='" + crmForm.ObjectId + "'/></filter><filter type='and'><condition attribute='filename' operator='like' value='logo%'/></filter></entity></fetch></fetchXml>"+ "</Fetch>"+ "</soap:Body>"+ "</soap:Envelope>"; var xHReq = new ActiveXObject("Msxml2.XMLHTTP"); xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false); xHReq.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Fetch"); xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8"); xHReq.setRequestHeader("Content-Length", xml.length); xHReq.send(xml); var resultXml = xHReq.responseXML; var errorCount = resultXml.selectNodes('//error').length; if (errorCount != 0) { var msg = resultXml.selectSingleNode('//description').nodeTypedValue; alert(msg); } else { resultSet = new String(); resultSet = resultXml.text; resultSet.replace('<','<'); resultSet.replace('>','>'); var oXmlDoc = new ActiveXObject("Microsoft.XMLDOM"); oXmlDoc.async = false; oXmlDoc.loadXML(resultSet); // Выцепляем нужную на запись из результата SOAP запроса var results = oXmlDoc.getElementsByTagName('result'); var annotationid = results[0].selectSingleNode('./annotationid').nodeTypedValue; // ждем пока наш целевой iframe загрузится iFrame.onreadystatechange = function() { if( iFrame.readyState == "complete" && annotationid != null) { // поменяйте название айфрема если оно у Вас отличается var iframeDoc = document.all.IFRAME_logo.contentWindow.document; var image = iframeDoc.createElement('img'); image.src = prependOrgName("/Activities/Attachment/download.aspx?AttachmentType=5&AttachmentId=") + annotationid; iframeDoc.body.appendChild(image); } } } } }
Пробуем:
- Создайте новую запись Вашего объекта
- Прикрепите к нему вложенный рисунок, который обязательно начинается с «logo»
- Перейдите на вкладку с нашим айфреймом.
Разбор полетов:
Сначала мы отслеживаем событие щелчка вкладке с айфреймом и если он произошел вызываем функцию loadlogo()
После чего на нужно с помощью SOAP запроса узнать гуид вложенного рисунка (начинающегося с «logo»). Для этого формируем fetch-запрос к объекту annotation в котором и хранятся примечания:
<fetch count='1' mapping='logical'> <entity name='annotation'> <attribute name='annotationid'/> <order attribute='createdon' descending='true'/> <filter type='and'> <condition attribute='objectid' operator='eq' value='{D52FE65F-F27C-DE11-8B60-005056B56C76}'/> </filter> <filter type='and'> <condition attribute='filename' operator='like' value='logo%'/> </filter> </entity> </fetch>
Этот запрос отбирает одну запись из всех annotation, у которой имя файла начинается с «logo», objectid равняется гуиду текущей записи и которая была добавлена последней (таким образом Вы можете добавлять сколько угодно рисунков в примечания, которые начинаются с «logo», но будет возвращен всегда последний)!
Ну в конце, если SOAP запрос вернул нам гуид вложенного рисунка, и мы загружаем этот рисунок в iFrame.
Вариант #1.2
Этот выриант на 80% повторяет предыдущий, но отличается одним маленьким нюансом! Вот его код:
var iFrame = crmForm.all.IFRAME_logo; // поменяйте номер вкладки на Ваш (счет начинается слева, с нуля) var tabVar= crmForm.all.tab3Tab; if(typeof(tabVar) != "undefined" && tabVar != null) { // здесь тоже замените номер вкладки crmForm.all.tab3Tab.attachEvent('onclick',loadlogo, false); } function loadlogo() { if (crmForm.ObjectId != null) { var authenticationHeader = GenerateAuthenticationHeader(); // Создаем SOAP запрос чтобы вытащить айдишник прикрепленного файла var xml = "<?xml version='1.0' encoding='utf-8'?>"+ "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'"+ " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"+ " xmlns:xsd='http://www.w3.org/2001/XMLSchema'>"+ authenticationHeader+ "<soap:Body>"+ "<Fetch xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>"+ "<fetchXml><fetch count='1' mapping='logical'><entity name='annotation'><attribute name='annotationid'/><order attribute='createdon' descending='true'/><filter type='and'><condition attribute='objectid' operator='eq' value='" + crmForm.ObjectId + "'/></filter><filter type='and'><condition attribute='filename' operator='like' value='logo%'/></filter></entity></fetch></fetchXml>"+ "</Fetch>"+ "</soap:Body>"+ "</soap:Envelope>"; var xHReq = new ActiveXObject("Msxml2.XMLHTTP"); xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false); xHReq.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Fetch"); xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8"); xHReq.setRequestHeader("Content-Length", xml.length); xHReq.send(xml); var resultXml = xHReq.responseXML; var errorCount = resultXml.selectNodes('//error').length; if (errorCount != 0) { var msg = resultXml.selectSingleNode('//description').nodeTypedValue; alert(msg); } else { resultSet = new String(); resultSet = resultXml.text; resultSet.replace('<','<'); resultSet.replace('>','>'); var oXmlDoc = new ActiveXObject("Microsoft.XMLDOM"); oXmlDoc.async = false; oXmlDoc.loadXML(resultSet); // Выцепляем нужную на запись из результата SOAP запроса var results = oXmlDoc.getElementsByTagName('result'); var annotationid = results[0].selectSingleNode('./annotationid').nodeTypedValue; iFrame.onreadystatechange = function() { if( iFrame.readyState == "complete" && annotationid != null) { // поменяйте название айфрема если оно у Вас отличается var frame = document.getElementById("IFRAME_logo"); var doc = frame.contentDocument; var page; page = "<html><head>"; page += "<style type='text/css'>"; page += "BODY { background-color: #b08a21;}"; page += "</style>"; page += "</head><body>"; page += "<IMG src="; page += prependOrgName("/Activities/Attachment/download.aspx?AttachmentType=5&AttachmentId=") + annotationid; page += " />"; page += "</body></html>"; if (doc == undefined || doc == null) { doc = frame.contentWindow.document; doc.open(); doc.write(page); } } } } } }
Отличие заключается в подстановкие рирунка в iFrame. Здесь мы динамически формируем код HTML-документа и используя тэг IMG вставляем в него рисунок. Как Вы можете видеть я также поменял цвет фона. Подобным образом Вы можете сформировать HTML-документ с любой структурой. Можете давже вытащить все прикрепленные рисунки и изобразить их как каталог (поместив например каждый в ячейку таблицы).
Варинат #2
В предыдущих двух примерах мы использовали iFrame в качестве места расположения рисунков — что в приниципе и правильно — iFrame для этого и предназначен. Если же Вам нужно обойтись без него, например, затисать Ваш рисунок среди полей формы 🙂 Повесьте этот скрипт на онлоад фоормы Бизнес-партнеров:
var Loaded = false; if(crmForm.FormType == 2 || crmForm.FormType == 3 || crmForm.FormType == 4) { // получаем ссылку на объект notescontrol var oNotes = document.getElementById("notescontrol"); // ждем окночания загрузки notecontrol и вызываем функцию LoadLogo oNotes.attachEvent('onreadystatechange', LoadLogo); // обновляем notecontrol oNotes.Refresh(); } function LoadLogo() { if (oNotes.readyState != 'complete') return; var oDoc = (oNotes.contentWindow || oNotes.contentDocument); if (oDoc.document) oDoc = oDoc.document; // заносим в переменную attachment все тэги SPAN (т.е. создаем массив) var attachments = oDoc.body.getElementsByTagName("SPAN"); // перебираем массив attachment for (var i = 0; i < attachments.length; i++) { // let's be certain we have an attachment here // проверяем, что у тэга SPAN есть attachmentId if (attachments[i].attachmentId) { // среди всех attachments находим нужный нам рисунок if (attachments[i].innerHTML.match(/.*logo\.png$/i) && !Loaded) { // получаем ссылку на таблицу содержащую поле name var tr = crmForm.all.name.parentNode.parentNode.parentNode.insertRow(); var td = tr.insertCell(); td.align="center" td.colSpan=4; td.innerHTML = "<img src='" + attachments[i].url.concat("?AttachmentType=", attachments[i].attachmentType, "&AttachmentId=", attachments[i].attachmentId) + "'/>" Loaded = true; } } } }
Затем прикрепите к Вашей записи бизнес-партнера рисунок logo.png. На этом все — на первую вкладку, в конец первой секции, добавится рисунок logo.png.
Как Вы поняли, здесь, с помощью метода innerHTML, мы «грубо» взломали HTML-код и добавили туда IMG-тег с нужным нам рисунком.
Здравствуйте.
Подскажите пожалуйста как быть с типом объекта для кустомной сущности а также повлияет как то если для отображения iframe используется 1 точнее 0-ая вкладка?
Извините, неправильно указал свой обратный e-mail.
Добрый день 🙂
Я не понял вопроса 🙂
> как быть с типом объекта для кустомной сущности
Что значит как быть? проблема в чем? 🙂
> повлияет как то если для отображения iframe используется 1 точнее 0-ая вкладка
Да, не должно. iFrame это как бы отдельная страничка внутри другиой странички… т.е. почти изолированный сайт 🙂
Спасибо.
Я все понял по поводу iframe, дело в том что ни какого события щелчка по вкладке не происходит изначально открывается нулевая вкладка а в ней тот самый iframe.
По поводу кустомной сущности какой тип объекта указывать в строке
page += prependOrgName(«/Activities/Attachment/download.aspx?AttachmentType=5&AttachmentId=») + annotationid;
Потому что в первоначальном виде в варианте 1.2 вроде как ни какая страница в iframe вообще не создается, все остальное вроде как проверил и все что вы указали заменил.
Спасибо
Если картинка в iFrame должна подставлятся при загрузке, то скопируйте на онлоад все что внутри function loadlogo(), а также первую строчку (это из примера 1.2) 🙂
Всякие системные значения (типа кода объекта) можно узнать по ссылке http:///sdk/list.aspx
Т.е. все что между
var iFrame = crmForm.all.IFRAME_logo;
и
if (crmForm.ObjectId != null) { …….. }
удалить?
И по поводу ссылки http:///sdk/list.aspx
у меня нет доступа на сам сервер, я арендатор, как быть в этом случае?
С первой частью вопроса — ответ получил поэксперементировав все получилось!!!!!
А вот как вытащить код объекта для кустомной сущности? не имея доступа к самому серверу.
а как Вы тогда скрипт помещаете на сервер?
ну можете например открыть любою запись кастомного оъекта и нажать Ctrl + N, откроется новое окно браузера с той же записью, но уже с адресной строкой 🙂 в этой адресной строке и будет код одъекта 🙂
Спасибо.
Уже разобрался, просто забыл написать.
В варианти 1.2 все таки рисунок ну ни как не загружается, при этом выдает значек с урлой https://организация.сервер.ru/Activities/Attachment/download.aspx?AttachmentType=5&AttachmentId={E65CCFB8-D942-DF11-8C26-000C2973C826}
рисунок с расширением называется map.jpg при этом в строке
operator=’like’ value=’logo%’ я поменял ‘logo%’ на ‘map%’
может это связано с расширением, или где то что то еще нужно поменять, кроме того что описано коментами в коде. Сущность кустомная.
С помщью средства разработчика IE я нашел на странице вложения следующую строку
то есть AttachmentId вроде как правильный при этом картинка не отображается, что может быть не правильно?
Помогите пожалуйста.
Спасибо.
Строка AttachmentId
input name=»AttachmentId» type=»hidden» value=»{E65CCFB8-D942-DF11-8C26-000C2973C826}»
Если все правилно, то тут возможны два варианта:
1. Тут постарался один из ролапа — читал где то что после какого ролапа начала сбоить загрузка файлов;
2. Что-то не так с доступом при IFD развертывании. Тут уже ничем помочь не могу, т.к. никогда не работал с ним и тестовой машины у меня нету 🙁
Я бы на Вашем месте реализовал это с помощью отчетов: http://mmcrm.ru/?p=932
и подставил бы этот отчет в iFrame 🙂
Еще я собираюсь написать статью, как подгражать приаттаченные файлы с помощью ASPX-страничек 🙂 возможно этот способ решит Вашу проблему 🙂 на написание поста займет некоторое время 🙂
Большое спасибо.
Собственно очень хоца чтобы создавалась именно html страница, которую можно и напечатать на А4 и опубликовать в вебе с некоторым колвом рисунков и данных именно с одной сущности. Ну очень нужно.
Еще раз большое спасибо.
Если так, то это http://mmcrm.ru/?p=932 + http://mmcrm.ru/?p=812 для по-моему идеальный вариант 🙂
Я арендатор, если на что то не сложное типа добавить микропапку на сервер еще можно договориться, то на SQL server меня точно никто не пустит, в этой связи приходиться ограничиваться только тем что можно всунуть в о настройку объектов.
Хотя похоже есть смысл (изначально не прочитал внимательно sorry) ведь я так понимаю в отчети можно подставить вообще любую таблицу самостоятельного изготовления?
Ну, в общем да 🙂 хотя в своей практике я встречал очень сложные (перекрестные) требования которые не удавалось рализовать 🙂 но, в Вашем случаи пока никаких проблем не вижу 🙂
Здравствуйте.
И все же, насколько я понимаю, для создания кастомных отчетов, нужен доступ к SQL серверу, где стоит CRM??? :(((
А никак нельзя эти картинки динамически подгружать в заранее созданную на сайте CRM HTML или ASP страницу, по принципу — как у Вас описано в http://mmcrm.ru/?p=852
1. Не обязательно 🙂 отчет можно написать и на какой-нибудь тестовой машине, а затем в готовом виде перенести на прмышленный сервер;
2. В ASP точно можно 🙂 вот только надо это как-то реализовать 🙂