Отправка Электронной почты с вложенными рисунками
Давнишняя проблема CRM – стандартными средствами нельзя создавать рассылку электропочты содержащую красиво оформленные (в том числе с помощью рисунков) сообщения. Частично с этим можно бороться с помощью добавления рисунков в HTML со ссылкой на общедоступный сервер, а сложного динамического форматирования можно добисться с помощью JavaScript, либо кастомного Бизнес-процесса. Но если конечный получатель не имеет доступа к общедоступному серверу (по любой причине), то вместо рисунков он увидит пустые квадратики! Поэтому это только наполовину решало проблему. С технической точки зрения можно создать сообщения электронной почты, включающие как само сообщение, так и использованные в нем рисунки (в виде аттачей). А чтобы такие рисунки корректно отбражались в сообщении (которое должно быть написано с помощью HTML), в нем необходимо использовать тег IMG такого вида:
<img src="cid:id_рисунка" />
где cid это уникальный идентификатор приаттаченого рисунка, который задается при формировании электронного сообщения.
Итак, возможность есть – реализуем! 🙂
Логика работы решения будет следующей:
- Юзвер создает самое обычное сообщение Электронной почты в CRM – задает отправителя, получателей и тему сообщения;
- Далее нужно добавить во вложения все рисунки, которые будут использоваться в птсьме. А в само тело сообщения, в то место (точнее во все места), где должен распологаться какой-либо рисунок, вставить полное название рисунка (т.е. вместе с расширением);
- Сохраняет запись, но не отправляет ее;
- Нажать на кастомную кнопку на панели инструментов – «Отправить суперэлектропочту» – откроется кастомное ASPX-приложение, которое создаст и отправит Ваше сообщение со вложенными рисунками с помщью .Net кода.
Начнем:
- Откройте VS и создайте новый новый веб-сайт (ASP.NET);
- Подключите к сайту стандартные SDK сборки;
- Добавьте на страницу Default.aspx.cs такой код:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Net.Mail; using System.Net.Mime; using Microsoft.Crm.Sdk; using Microsoft.Crm.SdkTypeProxy; using Microsoft.Crm.Sdk.Query; using System.IO; using System.Text; using System.Collections.ObjectModel; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { // Получаем GUID Электронной почты из строки запроса if (Request.QueryString["id"] == null) return; string emailId = Request.QueryString["id"].Replace("{", "").Replace("}", ""); // Конфигурируем CrmService CrmAuthenticationToken myToken = new CrmAuthenticationToken(); myToken.OrganizationName = "superfirma"; myToken.AuthenticationType = 0; CrmService service = new CrmService(); service.Credentials = System.Net.CredentialCache.DefaultCredentials; service.Url = "http://win-n22hj23d1b1/MSCrmServices/2007/CrmService.asmx"; service.CrmAuthenticationTokenValue = myToken; // Возвращаем из CRM необходимые поля Электронной почты ColumnSet cols = new ColumnSet(); cols.Attributes.Add("from"); cols.Attributes.Add("to"); cols.Attributes.Add("cc"); cols.Attributes.Add("bcc"); cols.Attributes.Add("subject"); cols.Attributes.Add("description"); email email = (email)service.Retrieve("email", new Guid(emailId), cols); activityparty[] To = email.to; activityparty[] From = email.from; activityparty[] Cc = email.cc; activityparty[] Bcc = email.bcc; // Запрашиваем в CRM вложения Электронной почты, которые относятся к нужной записи QueryExpression query = new QueryExpression(); query.EntityName = "activitymimeattachment"; ColumnSet columns = new ColumnSet(); // Указываем какие поля вложения необходимо вернуть columns.Attributes.Add("body"); columns.Attributes.Add("filename"); columns.Attributes.Add("mimetype"); query.ColumnSet = columns; ConditionExpression condition = new ConditionExpression(); condition.AttributeName = "activityid"; condition.Operator = ConditionOperator.Equal; condition.Values = new string[] { emailId }; // GUID нужной записи Электронной почты ConditionExpression condition2 = new ConditionExpression(); condition2.AttributeName = "mimetype"; condition2.Operator = ConditionOperator.In; condition2.Values = new string[] { "image/gif", "image/jpeg", "image/png" }; // Отбираем только вложения рисунков GIF, JPEG и PNG FilterExpression filter = new FilterExpression(); filter.FilterOperator = LogicalOperator.And; filter.Conditions.Add(condition); filter.Conditions.Add(condition2); query.Criteria = filter; RetrieveMultipleRequest retrieve = new RetrieveMultipleRequest(); retrieve.Query = query; RetrieveMultipleResponse retrieved = (RetrieveMultipleResponse)service.Execute(retrieve); BusinessEntityCollection attachments = retrieved.BusinessEntityCollection; // Создаем новое сообщение электронной почты MailMessage mailMessage = new MailMessage(); foreach (var FromItem in From) // Заполняем поле От { MailAddress FromAddress = new MailAddress(FromItem.addressused.ToString(), FromItem.partyid.name); mailMessage.From = FromAddress; } foreach (var ToItem in To) // Заполняем поле Кому { MailAddress ToAddress = new MailAddress(ToItem.addressused.ToString(), ToItem.partyid.name); mailMessage.To.Add(ToAddress); } foreach (var CcItem in Cc) // Заполняем поле Копия { MailAddress CcAddress = new MailAddress(CcItem.addressused.ToString(), CcItem.partyid.name); mailMessage.CC.Add(CcAddress); } foreach (var BccItem in Bcc) // Заполняем поле Скрытая копия { MailAddress BccAddress = new MailAddress(BccItem.addressused.ToString(), BccItem.partyid.name); mailMessage.Bcc.Add(BccAddress); } mailMessage.Subject = email.subject; // Заполняем поле Тема string mailBody = email.description; // Получаем тело письма // Просматриваем все рисунки приложенные к письму Collection<LinkedResource> linkedCollection = new Collection<LinkedResource>(); foreach (activitymimeattachment attachment in attachments.BusinessEntities) { // Если имя рисунка встречается в письме, то if (mailBody.IndexOf(attachment.filename) != -1) { // ... заменяем его на тег IMG mailBody = mailBody.Replace(attachment.filename, "<img src=\"cid:" + attachment.filename + "\" />"); // ... и добавляем в коллекцию LinkedResource сам рисунок byte[] fileBuffer = Convert.FromBase64String(attachment.body); MemoryStream fileStream = new MemoryStream(fileBuffer); LinkedResource img = new LinkedResource(fileStream, attachment.mimetype); img.ContentId = attachment.filename; linkedCollection.Add(img); } } // Создаем объект AlternateView и прикрепляем к нему рисунки из коллекции linkedCollection AlternateView av = AlternateView.CreateAlternateViewFromString(mailBody, null, MediaTypeNames.Text.Html); foreach (LinkedResource lr in linkedCollection) { av.LinkedResources.Add(lr); } mailMessage.AlternateViews.Add(av); mailMessage.IsBodyHtml = true; // Настраиваем SMTP сервер SmtpClient mailSender = new SmtpClient("smtp.mail.ru", 2525); try { // Отправляем Электропочту mailSender.Send(mailMessage); // Выводим сообщение об успешной отправке Response.Clear(); Response.Write("Сообщение отправлено"); } catch (SmtpFailedRecipientException error) { Response.Clear(); Response.Write("Ошибка: " + error.Message + "\nНе удалось отправить для: " + error.FailedRecipient); } } }
Разбор полетов:
- Вытаскиваем GUID Электронной почты из URL;
- Настраиваем Crm Service (соответственно измените праметры на Ваши);
- Запрашиваем по GUID’у поля Электронной почты;
- Запрашиваем вложения Электронной почты (но только рисунки GIF, JPEG, PNG);
- Создаем с помощью .Net кода новое сообщение электронной почты (экземпляр класса MailMessage);
- Заполняем поля От, Кому, Копия, Скрытая копия;
- Затем просматриваем все возвращенные вложения и:
- Если имя рисунка содержится в теле сообщения, то заменяем его на тег IMG ссылающийся на CID равным имени рисунка;
- Прикрепляем рисунок к письму и задаем для него CID равным имени рисунка.
- Добавляем в сообщение электроннойпочты HTML код (полученный из тела сообщения Электронной почты CRM и измененной при просмотре вложений);
- Настраиваем SMTP сервер (соответственно укажите свой – я использовал Mail.ru);
- Отправляем электронную почту и выводим сообщение пользователю.
- Перейдите Build — Publish Web Site — укажите путь к папке sendmail (ее Вам нужно предварительно создать), расположенную в папке ISV сайта CRM;
- Выгрузите ISV.config и добавьте на форму элетропочты такую кнопку:
<Button Icon="/_imgs/ico_18_debug.gif" JavaScript=" var width = 400; var height = 200; var left = (screen.width - width)/2; var top = (screen.height - height)/2; var params = 'width='+width+', height='+height; params += ', top='+top+', left='+left; params += ', directories=no'; params += ', location=no'; params += ', menubar=no'; params += ', resizable=no'; params += ', scrollbars=no'; params += ', status=no'; params += ', toolbar=no'; window.open('../../ISV/sendmail/Default.aspx?id=' + crmForm.ObjectId,"Суперэлектропочта", params); "> <Titles> <Title LCID="1049" Text="Отправить суперэлектропочту" /> </Titles> <ToolTips> <ToolTip LCID="1049" Text="Отправить суперэлектропочту" /> </ToolTips> </Button>
Кнопка просто открывает нашу кастомную страницу и передает ей в URL GUID текущей записи Электронной почты;
- Готово. Тестируем: создайте новыую запись Электронной почты (причем в получателях укажите адрес электронной почты, к которому можете получить доступ), приложите к ней рисунки, добавьте в тело сообщения имена рисунков (вместе с расширениями). Сохраните запись и нажмите кнопку «Отправить суперэлектропочту». Ну, и идите смотреть это письмо! 🙂
З.Ы. А если открыть HTML код полученного сообщения, то можете увидеть как рисунки ссылаются на CID.
Интересный способ. Можно попробовать упростить и ссылки на CID вставлять скриптом прямо в окно CRM.
В качестве альтернативного метода отправки картинок ещё будет интересно рассмотреть способ с data URI scheme. Для такой реализации даже не потребуется прикреплять картинки к письму, т.к. изображения будут закодированы в base64 прямо в теле сообщения.
Пример:
Код элемента
В письме будет выглядеть как:
Прикольно 🙂 нада будет как-нибудь попробовать 🙂
П.С. движок блога убил Ваш код 🙂 Чтобы этого не происходило необходимо использовать BB теги
,
🙂
Собственно, код был с Википедии:
Товарищь гуру, подскажите плиз, как при изменении значение атрибута (в моем случае атрибут в ввиде списка), формировалось автоматически письмо и отправлялось указанному в письме адресату.
Спасибо!!
Гуру стесняются, отвечу я : )
Изобретать ничего не надо, вам необходимо Workflow-правило на изменение поля. С единственным шагом: Отправить эл. почту.
Гуру не стесняется 🙂 Гуру пишет «Войну и мир» 🙂 А в это время все так и наровят обидеть гуру 🙂
Добрый день, а можно использовать данный способ для массовой рассылки сообщений?
наверника можно 🙂 только этот вопрос нада исследовать 🙂
Добрый день, так что по поводу массовой рассылки? Я так понимаю нужно прописывать все смтп для каждого почтового сервиса, или нет?
Не совсем понял вопроса. Какие смтп вы собрались прописывать и где?
Строка 131, 132. Настройка смтп. А при массовой рассылке на разные почтовые серверы, как быть? прописывать каждый?
Ну, я Вашей ситуации не знаю. Но если они у Вас в каждом письме меняются. То можно завести специальные поля на форме электропочты, которые оператор будет заполнять. И они будут передаваться в стоке URL на страничку.
Ситуация такая, есть около 10000 контактов, естественно с разными адресами. По ним хочу провести рассылку, через бизнес-процесс, либо через маркетинговую компанию, не важно. Важно то, как быть с развёрнутыми изображениями в письме в таком случае =( ? Может у вас есть ещё какие либо варианты?
Ну, здесь Вам нужно применить какой-либо другой подход. Например настроить шаблон электропочты (по тому же принципу, что описан в статье) и по нему производить рассылку. А на событие preSend повесить плагин, который будет производить модификацию письма, описанную в статье.
Тока все равно не понимаю причем тут SMTP сервер… он вполне может быть один.
Спасибо за ответы, с смтп вопрос отпадает, просто изначально немного не правильно Вас понял)
Появилась другая проблема при создании плагина, скажите может я что то не правильно делаю?
Создаю новый проект
выбираю Class Library (Framework 3.5)
Подключаю сборки: System.Web.dll, microsoft.crm.sdk.dll, microsoft.crm.sdktypeproxy.dll
И получаю такие ошибки, http://clip2net.com/s/2Bt4r
Подскажите пожалуйста, где я ошибся
Почему Framework 3.5? у Вас 4 CRM?
Да, 4. При выборе Framework 4.0 то же самое =(
Убедитесь что в свойствах проекта установлен .Net Framework 3.5, а не .Net Framework 3.5 Client Profile.