SharePoint и SMS. Часть 1. Создание OMS веб-сервиса
SharePoint из коробки позволяет пользователям получать уведомления не только по средствам электронной почты, но и в виде SMS-сообщений. Для конечных пользователей этот функционал доступен из коробки и не требует никаких дополнительных действий со стороны ИТ.
Для разработчиков есть богатая объектная модель, позволяющая отправлять не только SMS-сообщений, но и MMS, организовывать пакетную рассылку сообщений.
Первый пост серии о том, как реализовать отправку SMS-сообщений с портала под управлением SharePoint 2010/2013.
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>
<?xml version="1.0" encoding="utf-16"?>
<xmsUser client="Microsoft SharePoint"
xmlns="http://schemas.microsoft.com/office/Outlook/2006/OMS">
<userId>USERLOGIN</userId>
<password>USERPASSWORD</password>
</xmsUser>
</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, а второй - набор элементов
Для удобства при реализации 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 получилось.