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

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

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

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

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

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

OMS-сервис

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

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

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

[WebService(Namespace="http://schemas.microsoft.com/office/Outlook/2006/OMS"),
 ToolboxItem(false),
 ScriptService)]
public class Service : WebService
{
    [WebMethod]
    public string DeliverXms(string xmsData);
    [WebMethod]
    public string DeliverXmsBatch(string packageXml);
    [WebMethod]
    public string GetServiceInfo();
    [WebMethod]
    public string GetUserInfo(string xmsUser);
}

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

GetServiceInfo

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

<serviceInfo xmlns="http://schemas.microsoft.com/office/Outlook/2006/OMS/serviceInfo">
  <!-- Провайдер -->
  <serviceProvider>Vitaly Zhukov</serviceProvider>
  <!-- URL-адрес -->
  <serviceUri>https://sms.vitalyzhukov.ru/oms/service.asmx</serviceUri>
  <!-- Язык -->
  <targetLocale>1049</targetLocale>
  <!-- Название на указанном языке -->
  <localName>SMS шлюз</localName>
  <!-- Название на английском языке -->
  <englishName>SMS gateway</englishName>
  <!-- Тип аутентификации -->
  <authenticationType>other</authenticationType>
  <!-- Размер пакета -->
  <batchSize>10</batchSize>
  <!-- Поддерживаемые сервисы -->
  <supportedService>
    <!-- Отправка СМС -->
    <SMS_SENDER maxRecipientsPerMessage="50"
                maxMessagesPerSend="20"
                maxSbcsPerMessage="140"
                maxDbcsPerMessage="70">
      <LONG_SMS_SENDER maxRecipientsPerMessage="10"
            maxMessagesPerSend="5"
            maxSbcsPerMessage="700"
            maxDbcsPerMessage="350"/>
    </SMS_SENDER>
    <!-- Отправка MMS -->
    <MMS_SENDER supportSlide="true"
      maxRecipientsPerMessage="50"  maxSizePerMessage="30000"  maxSlidesPerMessage="10" />
  </supportedService>
</serviceInfo>

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

<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">
  <soap:Body>
    <GetUserInfo xmlns="http://schemas.microsoft.com/office/Outlook/2006/OMS">
      <xmsUser>
        &lt;?xml version="1.0" encoding="utf-16"?&gt;
          &lt;xmsUser client="Microsoft SharePoint" 
            xmlns="http://schemas.microsoft.com/office/Outlook/2006/OMS"&gt;
            &lt;userId&gt;USERLOGIN&lt;/userId&gt;
            &lt;password&gt;USERPASSWORD&lt;/password&gt;
        &lt;/xmsUser&gt;
      </xmsUser>
    </GetUserInfo>
  </soap:Body>
</soap:Envelope>

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

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

<userInfo>
  <replyPhone>+79991234567</replyPhone>
  <smtpAddress>vzhukov@live.ru</smtpAddress>
  <error code="ok"></error>
</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 это выглядит вот так:

<xmsBatch client="Microsoft SharePoint" xmlns="http://schemas.microsoft.com/office/Outlook/2006/OMS">
  <xmsData Id="0">
    <user>
      <userId>USERLOGIN</userId>
      <password>USERPASSWORD</password>
      <customData />
    </user>
    <xmsHead>
      <requiredService>SMS_SENDER</requiredService>
      <sourceType>spAlert</sourceType>
      <to>
        <recipient>+79991234567</recipient>
      </to>
    </xmsHead>
    <xmsBody format="SMS">
      <content        contentType="text/plain"
        contentId="Att0.txt@506c432f7a63428eb627d792d4557100"
        contentLocation="Att0.txt">
        Vitaly Zhukov modified the item: sdafasdf in SmsList
      </content>
    </xmsBody>
  </xmsData>
  <xmsData Id="1">
    <user />
    <xmsHead/>
    <xmsBody />
  </xmsData>
</xmsBatch>

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

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

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

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

[WebService(Namespace = "http://schemas.microsoft.com/office/Outlook/2006/OMS"),
    ToolboxItem(false),
    ScriptService,
    WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : WebService
{
    private string _xmlOmsNamespace = "http://schemas.microsoft.com/office/Outlook/2006/OMS";
 
    [WebMethod]
    public string DeliverXms(string xmsData)
    {
        new XmsData(XDocument.Parse(xmsData).Root).Send();
        return string.Empty; //TODO: Return response xml
    }
 
    [WebMethod]
    public string DeliverXmsBatch(string packageXml)
    {
        var batch = new XmsBatch(packageXml);
        foreach (var data in batch.XmsDataCollection)
        {
            data.Send();
        }
        return string.Empty; //TODO: Return response xml
    }
 
    [WebMethod]
    public string GetServiceInfo()
    {
        var ns = XNamespace.Get(_xmlOmsNamespace + "/serviceInfo");
        var element = new XElement(ns + "serviceInfo",
            new XElement("serviceProvider", "Vitaly Zhukov"),
            new XElement("serviceUri", "https://sms.vitalyzhukov.ru/oms/service.asmx"),
            new XElement("targetLocale", 1049),
            new XElement("localName", "SMS шлюз"),
            new XElement("englishName", "SMS gateway"),
            new XElement("authenticationType", "other"),
            new XElement("batchSize", 10),
            new XElement(ns + "supportedService",
                new XElement(ns + "SMS_SENDER",
                    new XAttribute("maxRecipientsPerMessage", 50),
                    new XAttribute("maxMessagesPerSend", 20),
                    new XAttribute("maxSbcsPerMessage", 140),
                    new XAttribute("maxDbcsPerMessage", 70)),
                new XElement("LONG_SMS_SENDER",
                    new XAttribute("maxRecipientsPerMessage", 10),
                    new XAttribute("maxMessagesPerSend", 5),
                    new XAttribute("maxSbcsPerMessage", 700),
                    new XAttribute("maxDbcsPerMessage", 350))));
        return element.ToString();
    }
 
    [WebMethod]
    public string GetUserInfo(string xmsUser)
    {
        //TODO: Validate xmsUser
        var element = new XElement(XNamespace.Get(_xmlOmsNamespace) + "userInfo",
            new XElement("replyPhone", "+79991234567"),
            new XElement("smtpAddress", "vzhukov@live.ru"),
            new XElement("error", new XAttribute("code", "ok")));
        return element.ToString();
    }
}
 
public class XmsBatch
{
    public XmsBatch(string xmlPackage)
    {
        var document = XDocument.Parse(xmlPackage);
        Client = document.Root.Attribute("client").Value;
        var list = document.Root
            .Elements()
            .Select(element => new XmsData(element))
            .ToList();
        XmsDataCollection = list;
    }
 
    public string Client { get; private set; }
    public IEnumerable<XmsData> XmsDataCollection { get; }
}
public class XmsData
{
    public XmsData(XElement element)
    {
        var ns = XNamespace.Get("http://schemas.microsoft.com/office/Outlook/2006/OMS");
        Id = element.Attribute("Id").Value;
        UserInfo = new UserInfo(element.Element(ns + "user"));
        Head = new XmsHead(element.Element(ns + "xmsHead"));
        Body = new XmsBody(element.Element(ns + "xmsBody"));
    }
 
    public void Send()
    {
        //TODO: Send SMS
    }
 
    public XmsBody Body { get; private set; }
    public XmsHead Head { get; private set; }
    public string Id { get; private set; }
    public UserInfo UserInfo { get; private set; }
}
public class XmsHead
{
    public XmsHead(XElement element)
    {
        XNamespace ns = XNamespace.Get("http://schemas.microsoft.com/office/Outlook/2006/OMS");
        RequiredService = element.Element(ns + "requiredService").Value;
        SourceType = element.Element(ns + "sourceType").Value;
        var list = element
            .Element(ns + "to")
            .Elements(ns + "recipient")
            .Select(x => x.Value)
            .ToList();
        Recipients = list.ToArray<string>();
    }
 
    public string[] Recipients { get; private set; }
    public string RequiredService { get; private set; }
    public string SourceType { get; private set; }
}
public class UserInfo
{
    public UserInfo(XElement element)
    {
        var ns = XNamespace.Get("http://schemas.microsoft.com/office/Outlook/2006/OMS");
        Login = element.Element(ns + "userId").Value;
        Password = element.Element(ns + "password").Value;
        CustomData = element.Element(ns + "customData").Value;
    }
 
    public string CustomData { get; private set; }
    public string Login { get; private set; }
    public string Password { get; private set; }
}
public class XmsBody
{
    public XmsBody(XElement element)
    {
        var ns = XNamespace.Get("http://schemas.microsoft.com/office/Outlook/2006/OMS");
        Format = element.Attribute("format").Value;
        Content = new XmsContent(element.Element(ns + "content"));
    }
 
    public XmsContent Content { get; private set; }
    public string Format { get; private set; }
}
public class XmsContent
{
    public XmsContent(XElement element)
    {
        Id = element.Attribute("contentId").Value;
        Type = element.Attribute("contentType").Value;
        Location = element.Attribute("contentLocation").Value;
        Value = element.Value;
    }
 
    public string Id { get; private set; }
    public string Location { get; private set; }
    public string Type { get; private set; }
    public string Value { get; private set; }
}

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

Виталий Жуков

Виталий Жуков

SharePoint архитектор, разработчик, тренер, Microsoft MVP (Office Development). Более 15 лет опыта работы с SharePoint, Dynamics CRM, Office 365, и другими продуктами и сервисами Microsoft.

Смотрите также

SharePoint 2007. Проверка на наличие элемента в списке

SharePoint 2007. Проверка на наличие элемента в списке

SharePoint 2007. База данных содержимого

SharePoint 2007. База данных содержимого

SharePoint 2007. Свой контрол на панели свойств веб-парта

SharePoint 2007. Свой контрол на панели свойств веб-парта

SharePoint 2007. Максимальное/минимальное значение поля в списке

SharePoint 2007. Максимальное/минимальное значение поля в списке

SharePoint 2007. Получение данных из нескольких списков и узлов

SharePoint 2007. Получение данных из нескольких списков и узлов