SharePoint и SMS. Часть 1. Создание OMS веб-сервиса

SharePoint из коробки позволяет пользователям получать уведомления не только по средствам электронной почты, но и в виде SMS-сообщений. Для конечных пользователей этот функционал доступен из коробки и не требует никаких дополнительных действий со стороны ИТ.

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

Первый пост серии о том, как реализовать отправку SMS-сообщений с портала под управлением SharePoint 2010/2013.

СМС-сообщения, отправленные с портала SharePoint

OMS-сервис

Первое, что понадобится - это OMS (Office Mobile Service) веб-сервис, который будет реализовывать отправку SMS и MMS сообщений. Создание собственного OMS-сервиса обусловлено некоторыми причинами:

  • SMS-провайдер не поддерживает отправку сообщений по средствам OMS-сервиса
  • Создание промежуточного сервиса для контроля отправки сообщений
  • Написать об этом пост

По той или иной причине мы создаем свой OMS-сервис, который в первом приближении выглядит примерно следующим образом:

  1. [WebService(Namespace="http://schemas.microsoft.com/office/Outlook/2006/OMS"),
  2.  ToolboxItem(false),
  3.  ScriptService)]
  4. public class Service : WebService
  5. {
  6.     [WebMethod]
  7.     public string DeliverXms(string xmsData);
  8.     [WebMethod]
  9.     public string DeliverXmsBatch(string packageXml);
  10.     [WebMethod]
  11.     public string GetServiceInfo();
  12.     [WebMethod]
  13.     public string GetUserInfo(string xmsUser);
  14. }

Методы DeliverXms и DeliverXmsBatch отвечают непосредственно за саму отправку сообщений, GetServiceInfo должен возвращать информацию о сервисе (описание, авторизация, поддерживаемые форматы, размер пакета), GetUserInfo - за авторизацию.

GetServiceInfo

Первым рассмотрим метод GetServiceInfo. Его задача вернуть информацию об OMS-сервисе в виде XML. Вот такого ответа будет достаточно для SharePoint:

  1. <serviceInfo xmlns="http://schemas.microsoft.com/office/Outlook/2006/OMS/serviceInfo">
  2.   <!-- Провайдер -->
  3.   <serviceProvider>Vitaly Zhukov</serviceProvider>
  4.   <!-- URL-адрес -->
  5.   <serviceUri>https://sms.vitalyzhukov.ru/oms/service.asmx</serviceUri>
  6.   <!-- Язык -->
  7.   <targetLocale>1049</targetLocale>
  8.   <!-- Название на указанном языке -->
  9.   <localName>SMS шлюз</localName>
  10.   <!-- Название на английском языке -->
  11.   <englishName>SMS gateway</englishName>
  12.   <!-- Тип аутентификации -->
  13.   <authenticationType>other</authenticationType>
  14.   <!-- Размер пакета -->
  15.   <batchSize>10</batchSize>
  16.   <!-- Поддерживаемые сервисы -->
  17.   <supportedService>
  18.     <!-- Отправка СМС -->
  19.     <SMS_SENDER maxRecipientsPerMessage="50"
  20.                 maxMessagesPerSend="20"
  21.                 maxSbcsPerMessage="140"
  22.                 maxDbcsPerMessage="70">
  23.       <LONG_SMS_SENDER maxRecipientsPerMessage="10"
  24.             maxMessagesPerSend="5"
  25.             maxSbcsPerMessage="700"
  26.             maxDbcsPerMessage="350"/>
  27.     </SMS_SENDER>
  28.     <!-- Отправка MMS -->
  29.     <MMS_SENDER supportSlide="true"
  30.       maxRecipientsPerMessage="50"  maxSizePerMessage="30000"  maxSlidesPerMessage="10" />
  31.   </supportedService>
  32. </serviceInfo>

Теперь сервис может сообщить информацию о себе. Следующий шаг - авторизация пользователя. SharePoint передает OMS-сервису информацию о пользователе в следующем виде:

  1. <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">
  2.   <soap:Body>
  3.     <GetUserInfo xmlns="http://schemas.microsoft.com/office/Outlook/2006/OMS">
  4.       <xmsUser>
  5.         &lt;?xml version="1.0" encoding="utf-16"?&gt;
  6.           &lt;xmsUser client="Microsoft SharePoint" 
  7.             xmlns="http://schemas.microsoft.com/office/Outlook/2006/OMS"&gt;
  8.             &lt;userId&gt;USERLOGIN&lt;/userId&gt;
  9.             &lt;password&gt;USERPASSWORD&lt;/password&gt;
  10.         &lt;/xmsUser&gt;
  11.       </xmsUser>
  12.     </GetUserInfo>
  13.   </soap:Body>
  14. </soap:Envelope>

Что соответствует информации, введенной в центре администрирования SharePoint.

Остается в методе GetUserInfo сервиса реализовать проверку прав и вернуть XML с информацией о пользователе и, если необходимо, о наличии ошибок авторизации. В случае успешной авторизации будет достаточно вот такого XML:

  1. <userInfo>
  2.   <replyPhone>+79991234567</replyPhone>
  3.   <smtpAddress>vzhukov@live.ru</smtpAddress>
  4.   <error code="ok"></error>
  5. </userInfo>

Этот метод вызывается SharePoint при задании информации о сервисе в центре администрирования. В дальнейшем информация о пользователе (логин и пароль) будет передаваться каждый раз при вызове методов DeliverXms и DeliverXmsBatch.

Полный перечень ошибок, информацию о которых можно передать SharePoint'у можно посмотреть в Таблице 3 руководства по созданию OMS-сервисов (Office 2010 Mobile Service Guidelines (Part 3 of 3)) на MSDN.

Как сообщить SharePoint о возможностях OMS-сервиса и авторизовать пользователя, от имени которого SharePoint будет к этому сервису обращаться разобрались. Дальше самое интересное - обработка запросов на отправку сообщений.

DeliverXms и DeliverXmsBatch

Отличие в принимаемых аргументах методов DeliverXms и DeliverXmsBatch заключается лишь в том, что первый из них принимает строку, содержащую XML с единственным элементом xmsData, а второй - набор элементов XmsData, уложенных в элемент xmsBatch. Поэтому рассмотрим только случай пакетной отправки сообщений.

Для удобства при реализации OMS-сервиса я описал содержимое пакета отправки следующими простыми классами (код будет ниже):

Каждый пакет (XmsBatch) содержит коллекцию данных (XmsData). Данные для отправки (XmsData) содержат информацию о пользователе (UserInfo), заголовок (XmsHead) и тело (XmsBody) с включенным в него содержимым (XmsContent). Всё крайне просто.

В виде XML это выглядит вот так:

  1. <xmsBatch client="Microsoft SharePoint" xmlns="http://schemas.microsoft.com/office/Outlook/2006/OMS">
  2.   <xmsData Id="0">
  3.     <user>
  4.       <userId>USERLOGIN</userId>
  5.       <password>USERPASSWORD</password>
  6.       <customData />
  7.     </user>
  8.     <xmsHead>
  9.       <requiredService>SMS_SENDER</requiredService>
  10.       <sourceType>spAlert</sourceType>
  11.       <to>
  12.         <recipient>+79991234567</recipient>
  13.       </to>
  14.     </xmsHead>
  15.     <xmsBody format="SMS">
  16.       <content        contentType="text/plain"
  17.         contentId="Att0.txt@506c432f7a63428eb627d792d4557100"
  18.         contentLocation="Att0.txt">
  19.         Vitaly Zhukov modified the item: sdafasdf in SmsList
  20.       </content>
  21.     </xmsBody>
  22.   </xmsData>
  23.   <xmsData Id="1">
  24.     <user />
  25.     <xmsHead/>
  26.     <xmsBody />
  27.   </xmsData>
  28. </xmsBatch>

По содержимому xmsData можно определить, что отправителем является SharePoint, а именно служба SharePoint Alert - в заголовке указан источник (sourceType) spAlert.

В качестве ответа сервис должен возвратить строку, содержащую XML с информацией о статусе отправки сообщений. Для успешной отправки ответ должен выглядеть примерно вот так:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <xmsResponse xmlns="http://schemas.microsoft.com/office/Outlook/2006/OMS">
  3.     <error code="ok" severity="neutral"/> 
  4. </xmsResponse>

Проще просто некуда. И напоследок моё любимое: весь код OMS-сервиса для SharePoint без реализации следующего функционала: аутентификация, формирования ответного xml при отправке сообещний, отправка СМС. Меньше 200 строк кода:

  1. [WebService(Namespace = "http://schemas.microsoft.com/office/Outlook/2006/OMS"),
  2.     ToolboxItem(false),
  3.     ScriptService,
  4.     WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
  5. public class Service : WebService
  6. {
  7.     private string _xmlOmsNamespace = "http://schemas.microsoft.com/office/Outlook/2006/OMS";
  8.  
  9.     [WebMethod]
  10.     public string DeliverXms(string xmsData)
  11.     {
  12.         new XmsData(XDocument.Parse(xmsData).Root).Send();
  13.         return string.Empty; //TODO: Return response xml
  14.     }
  15.  
  16.     [WebMethod]
  17.     public string DeliverXmsBatch(string packageXml)
  18.     {
  19.         var batch = new XmsBatch(packageXml);
  20.         foreach (var data in batch.XmsDataCollection)
  21.         {
  22.             data.Send();
  23.         }
  24.         return string.Empty; //TODO: Return response xml
  25.     }
  26.  
  27.     [WebMethod]
  28.     public string GetServiceInfo()
  29.     {
  30.         var ns = XNamespace.Get(_xmlOmsNamespace + "/serviceInfo");
  31.         var element = new XElement(ns + "serviceInfo",
  32.             new XElement("serviceProvider""Vitaly Zhukov"),
  33.             new XElement("serviceUri""https://sms.vitalyzhukov.ru/oms/service.asmx"),
  34.             new XElement("targetLocale", 1049),
  35.             new XElement("localName""SMS шлюз"),
  36.             new XElement("englishName""SMS gateway"),
  37.             new XElement("authenticationType""other"),
  38.             new XElement("batchSize", 10),
  39.             new XElement(ns + "supportedService",
  40.                 new XElement(ns + "SMS_SENDER",
  41.                     new XAttribute("maxRecipientsPerMessage", 50),
  42.                     new XAttribute("maxMessagesPerSend", 20),
  43.                     new XAttribute("maxSbcsPerMessage", 140),
  44.                     new XAttribute("maxDbcsPerMessage", 70)),
  45.                 new XElement("LONG_SMS_SENDER",
  46.                     new XAttribute("maxRecipientsPerMessage", 10),
  47.                     new XAttribute("maxMessagesPerSend", 5),
  48.                     new XAttribute("maxSbcsPerMessage", 700),
  49.                     new XAttribute("maxDbcsPerMessage", 350))));
  50.         return element.ToString();
  51.     }
  52.  
  53.     [WebMethod]
  54.     public string GetUserInfo(string xmsUser)
  55.     {
  56.         //TODO: Validate xmsUser
  57.         var element = new XElement(XNamespace.Get(_xmlOmsNamespace) + "userInfo",
  58.             new XElement("replyPhone""+79991234567"),
  59.             new XElement("smtpAddress""vzhukov@live.ru"),
  60.             new XElement("error"new XAttribute("code""ok")));
  61.         return element.ToString();
  62.     }
  63. }
  64.  
  65. public class XmsBatch
  66. {
  67.     public XmsBatch(string xmlPackage)
  68.     {
  69.         var document = XDocument.Parse(xmlPackage);
  70.         Client = document.Root.Attribute("client").Value;
  71.         var list = document.Root
  72.             .Elements()
  73.             .Select(element => new XmsData(element))
  74.             .ToList();
  75.         XmsDataCollection = list;
  76.     }
  77.  
  78.     public string Client { getprivate set; }
  79.     public IEnumerable<XmsData> XmsDataCollection { get; }
  80. }
  81. public class XmsData
  82. {
  83.     public XmsData(XElement element)
  84.     {
  85.         var ns = XNamespace.Get("http://schemas.microsoft.com/office/Outlook/2006/OMS");
  86.         Id = element.Attribute("Id").Value;
  87.         UserInfo = new UserInfo(element.Element(ns + "user"));
  88.         Head = new XmsHead(element.Element(ns + "xmsHead"));
  89.         Body = new XmsBody(element.Element(ns + "xmsBody"));
  90.     }
  91.  
  92.     public void Send()
  93.     {
  94.         //TODO: Send SMS
  95.     }
  96.  
  97.     public XmsBody Body { getprivate set; }
  98.     public XmsHead Head { getprivate set; }
  99.     public string Id { getprivate set; }
  100.     public UserInfo UserInfo { getprivate set; }
  101. }
  102. public class XmsHead
  103. {
  104.     public XmsHead(XElement element)
  105.     {
  106.         XNamespace ns = XNamespace.Get("http://schemas.microsoft.com/office/Outlook/2006/OMS");
  107.         RequiredService = element.Element(ns + "requiredService").Value;
  108.         SourceType = element.Element(ns + "sourceType").Value;
  109.         var list = element
  110.             .Element(ns + "to")
  111.             .Elements(ns + "recipient")
  112.             .Select(x => x.Value)
  113.             .ToList();
  114.         Recipients = list.ToArray<string>();
  115.     }
  116.  
  117.     public string[] Recipients { getprivate set; }
  118.     public string RequiredService { getprivate set; }
  119.     public string SourceType { getprivate set; }
  120. }
  121. public class UserInfo
  122. {
  123.     public UserInfo(XElement element)
  124.     {
  125.         var ns = XNamespace.Get("http://schemas.microsoft.com/office/Outlook/2006/OMS");
  126.         Login = element.Element(ns + "userId").Value;
  127.         Password = element.Element(ns + "password").Value;
  128.         CustomData = element.Element(ns + "customData").Value;
  129.     }
  130.  
  131.     public string CustomData { getprivate set; }
  132.     public string Login { getprivate set; }
  133.     public string Password { getprivate set; }
  134. }
  135. public class XmsBody
  136. {
  137.     public XmsBody(XElement element)
  138.     {
  139.         var ns = XNamespace.Get("http://schemas.microsoft.com/office/Outlook/2006/OMS");
  140.         Format = element.Attribute("format").Value;
  141.         Content = new XmsContent(element.Element(ns + "content"));
  142.     }
  143.  
  144.     public XmsContent Content { getprivate set; }
  145.     public string Format { getprivate set; }
  146. }
  147. public class XmsContent
  148. {
  149.     public XmsContent(XElement element)
  150.     {
  151.         Id = element.Attribute("contentId").Value;
  152.         Type = element.Attribute("contentType").Value;
  153.         Location = element.Attribute("contentLocation").Value;
  154.         Value = element.Value;
  155.     }
  156.  
  157.     public string Id { getprivate set; }
  158.     public string Location { getprivate set; }
  159.     public string Type { getprivate set; }
  160.     public string Value { getprivate set; }
  161. }

Аутентификацию я не рассматривал, потому что она индивидуальна в каждом случае при реализации OMS-сервиса. Это же касается и самой отправки SMS: в моем случае она происходит по средствам простых HTTP-запросов. Что же касается формирования ответного xml, то, во-первых, можно вернуть пустую строку - SharePoint это устроит, а, во-вторых, и так много XML получилось.


Поделиться

Коментарии