Завдання в Sharepoint виведені в окремий "клас" елементів. Вони не просто мають переднастроєні поля і "служать" робочим процесам. Вони також мають переднастроєні шаблони повідомлень електронної пошти, які крім власне оповіщення, мають можливість також і редагувати завдання прямо з поштового клієнта (звичайно, якщо останній підтримує таку можливість).
Дана особенонсть дуже зручна, тому що користувачеві не потрібно відкривати браузер для виконання якихось рутинних операцій. При цьому завантажуються як стандартні форми редагування завдання, так і створені в InfoPath (в тому числі з вбудованим кодом).
Це все чудово до тих пора, поки нам не захочеться кастомизировать текст листа. Зробити це м'яко скажемо не просто, особливо якщо потрібна залежність тексту від поточного сайту і т.д. Крім цього, за замовчуванням оповіщення відправляються на створення, зміна та закриття завдання. Часто необхідні тільки якісь певні події. Тому багато відключають автоматичні оповіщення, і створюють власну систему оповіщень. Реалізацій кілька (на базі Workflow або обробка подій елемента списку), але всі вони мають один недолік - втрачається функціонал редагування завдань з поштового клієнта.
Оскільки наші користувачі звикли редагувати завдання з Outlook, довелося розбиратися, як же все це працює.
Виявилося, що все досить просто - редагування завдань побудовано на протоколі [MS-OSALER]: Alerts Interop Protocol Specification.
2 /// Функція відправляє повідомлення про завдання відповідно до протоколу MS-OSALER
4 /// поточний вузол
5 /// текст листа
6 /// Кому
7 /// Приклад завдань
8 ///
9 public object [] SendMail (SPWeb web, string HtmlBody. String To, SPListItem TaskItem)
13 // Дана секція формується відповідно до довідки на System.Net.MailAddress
15 // Отримуємо настройки сайту. З них нам понадобяться настройки вихідної пошти
16 SPWebApplication webApp = web.Site.WebApplication;
17 // Формуємо поштове повідомлення
18 MailMessage mess = new MailMessage ();
20 mess.From = new MailAddress (webApp.OutboundMailSenderAddress, web.Title);
22 mess.ReplyTo = new MailAddress (webApp.OutboundMailReplyToAddress);
23 // тіло у нах в форматі UTF8
24 mess.BodyEncoding = Encoding .UTF8;
25 // і в форматі HTML
26 mess.IsBodyHtml = true;
27 // Записуємо тіло листа, воно може абсолютно будь-яким. хоч порожнім
28 mess.Body = HtmlBody;
29 // власне кому ми відправляємо лист
31 // Кодування теми листа
32 mess.SubjectEncoding = Encoding .UTF8;
33 // І сама тема, може бути будь-хто.
34 mess.Subject = "Завдання -" + TaskItem.Title;
36 // Далі іждут обов'язкові парамети, які служт для формування заголовків згідно MS-OSALER
37 // Attachment - це реалізація MIME, так що використовуємо пусте вкладення для формування заголовків MIME
38 Attachment at = new Attachment (new System.IO. MemoryStream (0), "");
39 at.ContentType = new System.Net.Mime.ContentType ( "text / html; charset = utf-8");
40 at.TransferEncoding = System.Net.Mime.TransferEncoding.QuotedPrintable;
41 // Отримуємо домен для формування MessageId. Ви можете поміняти це значення.
42 string domain = webApp.OutboundMailSenderAddress.Remove (0, webApp.OutboundMailSenderAddress.LastIndexOf ( '@'));
43 // Формуємо MessageID. Воно складається з обов'язкової частини і випадкового ВД повідомлення
44 // (останнє робить поштовий сервер, якщо MessageID явно не вказано в заголовках)
45 mess.Headers.Add ( "Message-Id". "<3BD50098E401463AA228377848493927" + Guid .NewGuid().ToString( "D" )+domain+ ">");
47 // Цей параметр теж можна змінити, в протоколі він зазначений як рекомендваний (SHOULD), але де він використовується я не знайшов
48 // тому зробив також, як в Sharepoint за замовчуванням - опис завдання
49 mess.Headers.Add ( "X-Sharing-Title". This .ToBase64 (TaskItem [ "Body"] .ToString ()));
50 // А ось далі йдуть параметри, змінювати які не можна.
51 mess.Headers.Add ( "X-AlertTitle". This .ToBase64 ( "System"));
52 mess.Headers.Add ( "X-AlertId". "# 123; 93A2F525-F664-4B02-9AD6-07851B1381C4 # 125;: # 123; 791979F1-2AB1-427D-9722-41B08012172B # 125;");
53 mess.Headers.Add ( "Content-Class". "MSWorkflowTask");
55 mess.Headers.Add ( "X-AlertWebUrl". This .ToBase64 (web.Url));
56 mess.Headers.Add ( "X-AlertServerType". "STS");
58 mess.Headers.Add ( "X-AlertWebSoap". This .ToBase64 (web.Url + "/_vti_bin/alerts.asmx"));
59 mess.Headers.Add ( "X-Sharing-Config-Url". "Stssync: // sts /? Ver = 1.1type = taskscmd = add-folderbase-url =" + Uri.EscapeDataString (web.Url) + " list-url = "+ Uri.EscapeDataString (" Lists / Tasks ") +" guid = "+ Uri.EscapeDataString (TaskItem.ParentList.ID.ToString (" D ")));
60 mess.Headers.Add ( "X-Sharing-Remote-Uid". TaskItem.ParentList.ID.ToString ( "D"));
61 mess.Headers.Add ( "X-Sharing-WssBaseUrl". This .ToBase64 (web.Url));
62 mess.Headers.Add ( "X-Sharing-ItemId". This .ToBase64 (TaskItem.ID.ToString ()));
64 // Тема сформований, можна відправляти.
66 SmtpClient client = new SmtpClient (webApp.OutboundMailServiceInstance.Server.Address);
67 client.Credentials = CredentialCache.DefaultNetworkCredentials;
69 // Це тестова функція, так що обробка вкрай примітивна
70 return new object [] # 123; true # 125 ;;
72 catch (Exception er)
73 # 123; return new object [] # 123; false, er.Message # 125 ;; # 125;
77 string ToBase64 (string InputString)
79 // Грубо. Може є і більш красива реалізація. Але ця точно працює.
80 // також не погано було б зробити обробку винятків
81 return "=? Utf-8? B?" + System. Convert .ToBase64String (UTF8Encoding .UTF8.GetBytes (InputString)) + "? =";