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. Как это работает

Новый сайт группы SharePoint. Как это работает

SharePoint 2019 Preview

SharePoint 2019 Preview

Управление большими списками/библиотеками в SharePoint

Управление большими списками/библиотеками в SharePoint

SharePoint 2013/2016. Настройка хлебных крошек

SharePoint 2013/2016. Настройка хлебных крошек

SharePoint 2016. Кастомизация меню SuiteBar

SharePoint 2016. Кастомизация меню SuiteBar

SharePoint 2016. Кастомизация SuiteBar

SharePoint 2016. Кастомизация SuiteBar

Домашняя страница сайта SharePoint

Домашняя страница сайта SharePoint

Презентация и скрипты со встречи сообщества по SharePoint и Office 365

Презентация и скрипты со встречи сообщества по SharePoint и Office 365

PowerShell DSC. SharePoint

PowerShell DSC. SharePoint

PowerShell DSC. Сертификат для шифрования пароля

PowerShell DSC. Сертификат для шифрования пароля

PowerShell DSC. Учетные записи

PowerShell DSC. Учетные записи

Встреча сообщества по SharePoint и Office 365

Встреча сообщества по SharePoint и Office 365

SharePoint 2016. QR-code

SharePoint 2016. QR-code

Возможности SharePoint 2010/2013/2016

Возможности SharePoint 2010/2013/2016

Gmail IIS SMTP relay

Gmail IIS SMTP relay

SharePoint 2016. Извещения мозаикой

SharePoint 2016. Извещения мозаикой

Сертификация SharePoint по-новому

Сертификация SharePoint по-новому

SharePoint. Получение списка сайтов

SharePoint. Получение списка сайтов

Планирование фермы SharePoint

Планирование фермы SharePoint

Обзор SharePoint Education. Часть 3

Обзор SharePoint Education. Часть 3

SharePoint Excel Services. Отображение диаграммы на странице

SharePoint Excel Services. Отображение диаграммы на странице

SharePoint 2016 Release Candidate

SharePoint 2016 Release Candidate

Обзор SharePoint Education. Часть 2

Обзор SharePoint Education. Часть 2

Обзор SharePoint Education. Часть 1

Обзор SharePoint Education. Часть 1

SharePoint 2013 Образование. Установка и настройка

SharePoint 2013 Образование. Установка и настройка

SharePoint 2016 IT Preview

SharePoint 2016 IT Preview

SharePoint и SMS. Часть 2. Регистрация OMS-сервиса

SharePoint и SMS. Часть 2. Регистрация OMS-сервиса

SharePoint Day: 14 декабря, Москва

SharePoint Day: 14 декабря, Москва

RuSUG 17.10.2013. Презентация с моего доклада о новом поиске в SharePoint 2013

RuSUG 17.10.2013. Презентация с моего доклада о новом поиске в SharePoint 2013

SharePoint 2013 Ribbon API. Версии 14.3 и 15.4

SharePoint 2013 Ribbon API. Версии 14.3 и 15.4

Встреча RuSUG 17-го октября

Встреча RuSUG 17-го октября

SharePoint 2013. Служба ServiceDesk за 8 часов либо правильный проект

SharePoint 2013. Служба ServiceDesk за 8 часов либо правильный проект

SharePoint 2010/2013. Локализация данных

SharePoint 2010/2013. Локализация данных

Список возможностей SharePoint 2013

Список возможностей SharePoint 2013

SharePoint 2013. Типы полей

SharePoint 2013. Типы полей

SharePoint 2013 Enterprise Search. Часть 2. Создание обработчика контента

SharePoint 2013 Enterprise Search. Часть 2. Создание обработчика контента

SharePoint 2013 Enterprise Search. Часть 1. Логическая архитектура

SharePoint 2013 Enterprise Search. Часть 1. Логическая архитектура

SharePoint Ribbon. Создание многоуровневого меню

SharePoint Ribbon. Создание многоуровневого меню

SharePoint Ribbon. Использование ColorPicker'а

SharePoint Ribbon. Использование ColorPicker'а

Разработка для SharePoint. Как это было и как это будет

Разработка для SharePoint. Как это было и как это будет

SharePoint. История социализма за 10 лет

SharePoint. История социализма за 10 лет

SharePoint 2013 Preview

SharePoint 2013 Preview

Использование штрихкодов в SharePoint 2010

Использование штрихкодов в SharePoint 2010

Unable to locate the xml-definition for FieldName

Unable to locate the xml-definition for FieldName

SharePoint 2010. Апрельский накопительный пакет обновлений

SharePoint 2010. Апрельский накопительный пакет обновлений

Не удалось найти XML-файл в указанном расположении

Не удалось найти XML-файл в указанном расположении

Служба синхронизации профилей пользователей. Ошибки

Служба синхронизации профилей пользователей. Ошибки

Делаем сайт на SharePoint 2010. Построение иерархии страниц

Делаем сайт на SharePoint 2010. Построение иерархии страниц

Делаем сайт на SharePoint 2010. Брендинг Wiki-страниц

Делаем сайт на SharePoint 2010. Брендинг Wiki-страниц

Делаем сайт на SharePoint 2010. Оптимизация

Делаем сайт на SharePoint 2010. Оптимизация

Делаем сайт на SharePoint 2010. Брендинг

Делаем сайт на SharePoint 2010. Брендинг

Обработка большого количества элементов в SharePoint

Обработка большого количества элементов в SharePoint

Получение уникальных значений поля списка

Получение уникальных значений поля списка

DeskWork 5. Функциональность (продолжение)

DeskWork 5. Функциональность (продолжение)

Сокрытие информации о пользователе создавшем/изменившем элемент

Сокрытие информации о пользователе создавшем/изменившем элемент

Error : Code blocks are not allowed in this file

Error : Code blocks are not allowed in this file

SharePoint Client Object Model. Управляемый код

SharePoint Client Object Model. Управляемый код

Использование контрола HtmlEditor. Часть 3

Использование контрола HtmlEditor. Часть 3

SharePoint 15 SDK

SharePoint 15 SDK

PeopleEditor и Internet Explorer 9

PeopleEditor и Internet Explorer 9

Использование контрола HtmlEditor. Часть 2

Использование контрола HtmlEditor. Часть 2

Использование контрола HtmlEditor. Часть 1

Использование контрола HtmlEditor. Часть 1

Пропадающий контрол выбора представления списка

Пропадающий контрол выбора представления списка

Развертывание библиотеки документов с файлами

Развертывание библиотеки документов с файлами

Отключенные учетные записи и PeoplePicker

Отключенные учетные записи и PeoplePicker

Пропадающий TextBox в Telerik Reporting

Пропадающий TextBox в Telerik Reporting

Длительные операции в SharePoint. Request timed out

Длительные операции в SharePoint. Request timed out

Длительные операции в SharePoint. Изнутри

Длительные операции в SharePoint. Изнутри

Длительные операции в SharePoint. Снаружи

Длительные операции в SharePoint. Снаружи

DeskWork. Версия 5.1

DeskWork. Версия 5.1

DeskWork 5. Часть 2. Функциональность

DeskWork 5. Часть 2. Функциональность

DeskWork 5. Часть 1. Установка

DeskWork 5. Часть 1. Установка

SharePoint 2010. Random ListItem

SharePoint 2010. Random ListItem

Custom ListDefinition. Отключаем диалоги

Custom ListDefinition. Отключаем диалоги

SharePoint 2010 UpdatePanel. Request Notification

SharePoint 2010 UpdatePanel. Request Notification

Получение размера вложений SPListItem'а

Получение размера вложений SPListItem'а

SharePoint 2010. Переопределение форм типа содержимого

SharePoint 2010. Переопределение форм типа содержимого

SharePoint 2007/2010. Привязываем EventReceiver к типу содержимого

SharePoint 2007/2010. Привязываем EventReceiver к типу содержимого

Изменяем appSettings в config-файле

Изменяем appSettings в config-файле

SharePoint 2010. Добавляем сборку в пакет

SharePoint 2010. Добавляем сборку в пакет

SharePoint 2007/2010. The security validation for this form is invalid

SharePoint 2007/2010. The security validation for this form is invalid

SharePoint 2010. JavaScript IntelliSence

SharePoint 2010. JavaScript IntelliSence

SharePoint 2010. Локализация SiteDefinition

SharePoint 2010. Локализация SiteDefinition

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

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

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

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

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

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

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

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

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

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