Кастомизация
23
Сен
22

Картинки на форме 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>&lt;fetch count='1' mapping='logical'&gt;&lt;entity name='annotation'&gt;&lt;attribute name='annotationid'/&gt;&lt;order attribute='createdon' descending='true'/&gt;&lt;filter type='and'&gt;&lt;condition attribute='objectid' operator='eq' value='" + crmForm.ObjectId + "'/&gt;&lt;/filter&gt;&lt;filter type='and'&gt;&lt;condition attribute='filename' operator='like' value='logo%'/&gt;&lt;/filter&gt;&lt;/entity&gt;&lt;/fetch&gt;</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('&lt;','<');
    		resultSet.replace('&gt;','>');
    		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);
    
    			}
    
    		}
    
    	}
        
    }
    
    }
    

Пробуем:

  1. Создайте новую запись Вашего объекта
  2. Прикрепите к нему вложенный рисунок, который обязательно начинается с «logo»
  3. Перейдите на вкладку с нашим айфреймом.



Разбор полетов:

Сначала мы отслеживаем событие щелчка вкладке с айфреймом и если он произошел вызываем функцию 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>&lt;fetch count='1' mapping='logical'&gt;&lt;entity name='annotation'&gt;&lt;attribute name='annotationid'/&gt;&lt;order attribute='createdon' descending='true'/&gt;&lt;filter type='and'&gt;&lt;condition attribute='objectid' operator='eq' value='" + crmForm.ObjectId + "'/&gt;&lt;/filter&gt;&lt;filter type='and'&gt;&lt;condition attribute='filename' operator='like' value='logo%'/&gt;&lt;/filter&gt;&lt;/entity&gt;&lt;/fetch&gt;</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('&lt;','<');
		resultSet.replace('&gt;','>');
		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-тег с нужным нам рисунком.

Комментарии (22)
  • Анатолий 23.09.2009

    Здравствуйте.
    Подскажите пожалуйста как быть с типом объекта для кустомной сущности а также повлияет как то если для отображения iframe используется 1 точнее 0-ая вкладка?

  • Анатолий 23.09.2009

    Извините, неправильно указал свой обратный e-mail.

  • slivka_83 23.09.2009

    Добрый день 🙂
    Я не понял вопроса 🙂
    > как быть с типом объекта для кустомной сущности
    Что значит как быть? проблема в чем? 🙂
    > повлияет как то если для отображения iframe используется 1 точнее 0-ая вкладка
    Да, не должно. iFrame это как бы отдельная страничка внутри другиой странички… т.е. почти изолированный сайт 🙂

  • Анатолий 23.09.2009

    Спасибо.
    Я все понял по поводу iframe, дело в том что ни какого события щелчка по вкладке не происходит изначально открывается нулевая вкладка а в ней тот самый iframe.
    По поводу кустомной сущности какой тип объекта указывать в строке
    page += prependOrgName(«/Activities/Attachment/download.aspx?AttachmentType=5&AttachmentId=») + annotationid;
    Потому что в первоначальном виде в варианте 1.2 вроде как ни какая страница в iframe вообще не создается, все остальное вроде как проверил и все что вы указали заменил.
    Спасибо

  • slivka_83 23.09.2009

    Если картинка в iFrame должна подставлятся при загрузке, то скопируйте на онлоад все что внутри function loadlogo(), а также первую строчку (это из примера 1.2) 🙂

    Всякие системные значения (типа кода объекта) можно узнать по ссылке http:///sdk/list.aspx

  • Анатолий 23.09.2009

    Т.е. все что между
    var iFrame = crmForm.all.IFRAME_logo;
    и
    if (crmForm.ObjectId != null) { …….. }
    удалить?

    И по поводу ссылки http:///sdk/list.aspx
    у меня нет доступа на сам сервер, я арендатор, как быть в этом случае?

  • Анатолий 23.09.2009

    С первой частью вопроса — ответ получил поэксперементировав все получилось!!!!!
    А вот как вытащить код объекта для кустомной сущности? не имея доступа к самому серверу.

  • slivka_83 23.09.2009

    а как Вы тогда скрипт помещаете на сервер?
    ну можете например открыть любою запись кастомного оъекта и нажать Ctrl + N, откроется новое окно браузера с той же записью, но уже с адресной строкой 🙂 в этой адресной строке и будет код одъекта 🙂

  • Анатолий 23.09.2009

    Спасибо.
    Уже разобрался, просто забыл написать.

  • Анатолий 23.09.2009

    В варианти 1.2 все таки рисунок ну ни как не загружается, при этом выдает значек с урлой https://организация.сервер.ru/Activities/Attachment/download.aspx?AttachmentType=5&AttachmentId={E65CCFB8-D942-DF11-8C26-000C2973C826}

    рисунок с расширением называется map.jpg при этом в строке
    operator=’like’ value=’logo%’ я поменял ‘logo%’ на ‘map%’
    может это связано с расширением, или где то что то еще нужно поменять, кроме того что описано коментами в коде. Сущность кустомная.

  • Анатолий 23.09.2009

    С помщью средства разработчика IE я нашел на странице вложения следующую строку

    то есть AttachmentId вроде как правильный при этом картинка не отображается, что может быть не правильно?
    Помогите пожалуйста.
    Спасибо.

  • Анатолий 23.09.2009

    Строка AttachmentId
    input name=»AttachmentId» type=»hidden» value=»{E65CCFB8-D942-DF11-8C26-000C2973C826}»

  • slivka_83 23.09.2009

    Если все правилно, то тут возможны два варианта:
    1. Тут постарался один из ролапа — читал где то что после какого ролапа начала сбоить загрузка файлов;
    2. Что-то не так с доступом при IFD развертывании. Тут уже ничем помочь не могу, т.к. никогда не работал с ним и тестовой машины у меня нету 🙁

    Я бы на Вашем месте реализовал это с помощью отчетов: http://mmcrm.ru/?p=932
    и подставил бы этот отчет в iFrame 🙂

  • slivka_83 23.09.2009

    Еще я собираюсь написать статью, как подгражать приаттаченные файлы с помощью ASPX-страничек 🙂 возможно этот способ решит Вашу проблему 🙂 на написание поста займет некоторое время 🙂

  • Анатолий 23.09.2009

    Большое спасибо.
    Собственно очень хоца чтобы создавалась именно html страница, которую можно и напечатать на А4 и опубликовать в вебе с некоторым колвом рисунков и данных именно с одной сущности. Ну очень нужно.
    Еще раз большое спасибо.

  • slivka_83 23.09.2009

    Если так, то это http://mmcrm.ru/?p=932 + http://mmcrm.ru/?p=812 для по-моему идеальный вариант 🙂

  • Анатолий 23.09.2009

    Я арендатор, если на что то не сложное типа добавить микропапку на сервер еще можно договориться, то на SQL server меня точно никто не пустит, в этой связи приходиться ограничиваться только тем что можно всунуть в о настройку объектов.

  • Анатолий 23.09.2009

    Хотя похоже есть смысл (изначально не прочитал внимательно sorry) ведь я так понимаю в отчети можно подставить вообще любую таблицу самостоятельного изготовления?

  • slivka_83 23.09.2009

    Ну, в общем да 🙂 хотя в своей практике я встречал очень сложные (перекрестные) требования которые не удавалось рализовать 🙂 но, в Вашем случаи пока никаких проблем не вижу 🙂

  • Анатолий 23.09.2009

    Здравствуйте.
    И все же, насколько я понимаю, для создания кастомных отчетов, нужен доступ к SQL серверу, где стоит CRM??? :(((

  • Анатолий 23.09.2009

    А никак нельзя эти картинки динамически подгружать в заранее созданную на сайте CRM HTML или ASP страницу, по принципу — как у Вас описано в http://mmcrm.ru/?p=852

  • slivka_83 23.09.2009

    1. Не обязательно 🙂 отчет можно написать и на какой-нибудь тестовой машине, а затем в готовом виде перенести на прмышленный сервер;
    2. В ASP точно можно 🙂 вот только надо это как-то реализовать 🙂

*

code