Microsoft Office

Исследование JavaScript API for Office: почтовые приложения

Анжела Чу-Хатоун

Исходный код можно скачать по ссылке.

Продукты и технологии:

JavaScript API for Office

В статье рассматриваются:

  • контекстно-зависимая активация;
  • использование общеизвестных сущностей;
  • хранилище данных;
  • работа с вложениями и формами;
  • аутентификация;
  • веб-сервисы обмена сообщениями.

Эта статья — четвертая часть подробного исследования JavaScript API for Office, и в ней основное внимание уделяется той части API, которая доступна почтовым приложениям, поддерживаемым Outlook и Outlook Web App. Я исхожу из того, что у вас есть общее понимание приложений для Office. Если вы в этом не уверены, прочитайте на странице документации Dev Center «Overview of apps for Office» (bit.ly/12nBWHG) — она наставит вас на путь истинный. В первой части из этой серии, «Exploring the New JavaScript API for Office» (msdn.microsoft.com/magazine/jj891051), был дан высокоуровневый обзор объектной модели, в которой корневым является объект Office. Объект Mailbox, доступный из Office.context, предоставляет точку входа для доступа к большей части специфической функциональности почтовых приложений в объектной модели, и именно с этого места мы и продолжим.

Главный посыл этой статьи: вы (как разработчик) можете делать с JavaScript API for Office в почтовых приложениях. Кроме того, для этой части я написала сопутствующую онлайновую статью, в которой пошагово разбираю пример почтового приложения с полным исходным кодом (msdn.microsoft.com/magazine/dn205107). А теперь мы обсудим различные средства JavaScript API for Office и начнем с некоторых наиболее базовых и часто применяемых, а потом перейдем к более сложным концепциям.

Фундаментальные средства JavaScript API for Office

Объект Mailbox обеспечивает доступ к профилю пользователя, элементу, выбранному пользователем в данный момент, формам для отображения этого элемента и к подмножеству Exchange Web Services (EWS) для манипуляций с элементами и папками в почтовом ящике. Объект Item представляет выбранное сообщение или встречу (appointment item). Через этот объект можно получить доступ к встроенным и пользовательским свойствам, а также к вложениям в этом элементе.

Управление активацией на основе типа элемента Вы можете определять правила в манифесте, чтобы задать момент активации почтового приложения. Элемент, выбранный в данный момент пользователем и указанный Mailbox.item, может быть объектом Message (включая приглашения на собрание [meeting requests], ответы на приглашения [responses] и уведомления об отмене [cancellations]) или Appointment. В зависимости от сценария вы можете ограничивать почтовое приложение активацией только для элемента определенного типа. Следующий пример XML показывает правило активации ItemIs, которое ограничивает почтовое приложение активацией лишь для сообщений:

<Rule xsi:type="ItemIs" ItemType="Message" />

С помощью JavaScript API for Office можно использовать свойство itemType для проверки типа выбранного элемента:

Office.context.mailbox.item.itemType

Доступ к элементу и его свойствам Одна из базовых функций, предлагаемых почтовыми приложениями, — доступ к текущему выбранному элементу и его встроенным свойствам.

Следующий пример на JavaScript показывает, как обратиться к выбранному элементу и его встроенному свойству subject:

// Функция initialize необходима для всех приложений
Office.initialize = function () {
  // Проверяем, требуется ли загрузка DOM
  $(document).ready(function () {
    var item = Office.context.mailbox.item;
    var subject = item.subject;
    // Продолжаем обработку subject.
  });
}

Общеизвестные сущности Exchange Server распознает некоторые сущности, доступные независимо от того, использовали вы эти сущности для активации почтового приложения или нет. Outlook и Outlook Web App извлекают эти сущности, если они существуют в свойстве subject или body выбранного элемента, и делает их доступными через следующие методы JavaScript API:

  • getEntities;
  • getEntitiesByType(entityType);
  • getFilteredEntitiesByName(name).

К поддерживаемым сущностям относятся адрес, контакт, адрес электронной почты и др.

Заметьте, что Outlook и Outlook Web Apps извлекают строки только на английском независимо от региональных стандартов по умолчанию, указанных вами в манифесте приложения. Они не могут извлекать сущности в папке Sent Items. Кроме того, они извлекают предложения о встречах в сообщениях, но не в согласованных встречах.

Получение совпадений, которые приводят к контекстно-зависимой активации Вы можете задать правила активации, зависимые от совпадений с результатами регулярных выражений (правила ItemHasRegularExpressionMatch) или совпадений сущностей (правила ItemHasKnownEntity) в выбранном элементе. Для получения совпадений на основе регулярных выражений можно использовать перечисленные ниже методы. Эти методы доступны в родительском объекте Item, который может быть сообщением или встречей:

  • getRegExMatches;
  • getRegExMatchesByName(name).

Для получения совпадений на основе определенных сущностей применимы методы, перечисленные в подразделе «Общеизвестные сущности». Заметьте: вы можете всегда использовать эти методы для получения соответствия с любой сущностью независимо от типа правил активации, применяемых вами для почтового приложения.

Примеры Ниже дан пример правила ItemHasRegularExpressionMatch с именем VideoURL, которое активирует почтовое приложение, если выбранный элемент является сообщением и в теле сообщения содержится URL видеоролика на YouTube:

<Rule xsi:type="RuleCollection" Mode="And">
  <Rule xsi:type="ItemIs" ItemType="Message"/>
  <Rule xsi:type="ItemHasRegularExpressionMatch"
    RegExName="VideoURL"
    RegExValue="http://www\.youtube\.com/watch\?v=[a-zA-Z0-9_-]{11}"
    PropertyName="Body"/>
</Rule>

Следующий пример JavaScript-кода демонстрирует, как с помощью метода Item.getRegExMatches получить совпадения при проверке предыдущего регулярного выражения VideoURL:

Office.initialize = function () {
  // Проверяем, надо ли загружать DOM
  $(document).ready(function () {
    // Получаем массив строк, совпадающих с регулярным
    // выражением VideoURL, как указано в манифесте
    var matches = Office.context.mailbox.item.getRegExMatches().VideoURL;
    // Продолжаем обработку совпадений
    // с этим регулярным выражением
  });
}

А вот пример правила ItemHasKnownEntity, которое активирует почтовое приложение, если в теме или теле сообщения присутствует какой-либо адрес:

<Rule xsi:type="RuleCollection" Mode="And">
  <Rule xsi:type="ItemIs" ItemType="Message"/>
  <Rule xsi:type="ItemHasKnownEntity" EntityType="Address" />
</Rule>

Заметьте, что Outlook и Outlook Web Apps извлекают строки только на английском независимо от региональных стандартов по умолчанию, указанных вами в манифесте приложения.

Следующий пример JavaScript-кода иллюстрирует, как использовать метод getEntitiesByType, чтобы получить массив строк, являющихся почтовыми адресами в теме или теле текущего выбранного сообщения:

Office.initialize = function () {
  // Проверяем, надо ли загружать DOM
  $(document).ready(function () {
    var item = Office.context.mailbox.item;
    // Получаем массив строк, представляющих
    // почтовые адреса в выбранном элементе
    var addresses = item.getEntitiesByType(
      Office.MailboxEnums.EntityType.Address);
    // Продолжаем обработку массива адресов
  });
}

Активация почтового приложения, основанная на контекстах (особенно на совпадениях с регулярным выражением или наличии общеизвестных сущностей), обеспечивает широкие возможности, но может оказаться сложной в отладке. Советы по отладке проблем активации см. в статье из MSDN Library «Troubleshooting mail apps activation» по ссылке bit.ly/11C0H30.

Хранилище данных, индивидуальное для каждого элемента вашего приложения Объект CustomProperties позволяет сохранять специфичные для элемента данные, доступные только вашему приложению, в виде пользовательского свойства этого элемента для применения в последующем сеансе. Такие данные представляются парами «ключ-значение». Если приложение рассчитано на выполнение в нескольких клиентах Outlook, эти пользовательские свойства передаются вместе с поддерживаемым клиентом Outlook.

Пользовательские свойства элемента доступны через метод Item.loadCustomPropertiesAysnc. Это асинхронный метод, который принимает в качестве параметра метод обратного вызова. При загрузке пользовательские свойства доступны через свойство asyncResult.value, передаваемое методу обратного вызова в качестве входного параметра. На рис. 1 показано, как загружать, получать, задавать и сохранять пользовательские свойства для текущего элемента.

Рис. 1. Работа с пользовательскими свойствами для текущего элемента

Office.initialize = function () {
  var mailbox = Office.context.mailbox;
  mailbox.item.loadCustomPropertiesAsync(customPropsCallback);
}
function customPropsCallback(asyncResult) {
  if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
    var customProps = asyncResult.value;
    var myProp = customProps.get("myProp");
    customProps.set("otherProp", "value");
    customProps.saveAsync(saveCallback);
  }
  else {
    write (asyncResult.error.message);
  }
}
function saveCallback(asyncResult) {
  // Метод обратного вызова
  // для сохранения пользовательских свойств
}

Хранилище данных, индивидуальное для каждого почтового ящика вашего приложения Объект RoamingSettings позволяет хранить пользовательские данные почтового ящика, к которым ваше почтовое приложение может обращаться независимо от того, где именно оно выполняется — на настольном компьютере, планшете или смартфоне. Следующий пример JavaScript-кода демонстрирует, как получить специфичные для почтового ящика данные в обработчике события initialize почтового приложения:

var _mailbox;
var _settings;
Office.initialize = function () {
  // Проверяем, надо ли загружать DOM,
  // используя jQuery-функцию ready
  $(document).ready(function () {
    // Инициализируем переменные экземпляра
    // для доступа к API-объектам
    _mailbox = Office.context.mailbox;
    _settings = Office.context.roamingSettings;
    // Здесь помещаем код, специфичный для приложения
  });
}

Профиль пользователя Профиль пользователя доступен через свойство Office.context.mailbox.userProfile. С помощью объекта UserProfile вы можете получить отображаемое имя (display name), SMTP-адрес и местный часовой пояс вошедшего пользователя. Следующий пример JavaScript-кода показывает, как обратиться к свойству UserProfile.emailAddress текущего пользователя, вошедшего в Outlook или Outlook Web App:

Office.initialize = function () {
  // Проверяем, надо ли загружать DOM
  $(document).ready(function () {
    var userProfile = Office.context.mailbox.userProfile;
    var SMTPAddress = userProfile.emailAddress;
    // Продолжаем обработку SMTP-адреса пользователя
  });
}

Отображение форм Outlook В Outlook пользователь может указывать формы по умолчанию для отображения встреч и сообщений. С помощью следующих методов в JavaScript API for Office почтовое приложение может выводить список встреч и сообщений, применяя те же формы, что и Outlook:

  • Mailbox.displayAppointmentForm(itemId);
  • Mailbox.displayMessageForm(itemId).

Код на рис. 2 проверяет, выбрал ли пользователь встречу, и отображает ее в Outlook-форме встреч по умолчанию.

Рис. 2. Проверка того, выбрал ли пользователь какую-нибудь встречу

var myOm;
var myItem;
Office.initialize = function () {
  $(document).ready(function () {
    myOm = Office.context.mailbox;
    // Получаем выбранный элемент
    myItem = myOm.item;
    // Отображаем выбранную встречу
    displaySelectedAppointment();
  });
}
function displaySelectedAppointment() {
  // Показываем выбранный элемент, только если это встреча
  if (myitem.itemType == Office.MailboxEnums.ItemType.Appointment)
  {
    myOm.displayAppointmentForm(myItem.itemId);
  }
  else
  {
    // Соответственно обрабатываем условие
  }
}

Метод Mailbox.displayNewAppointmentForm позволяет назначать начения определенным полям в форме встречи для нового собрания и отображать эту форму пользователю, где тот может заполнить поля, относящиеся к встрече. При вызове этого метода укажите эти поля как список пар «строка-значение» (рис. 3).

Рис. 3. Присваивание значений определенным полям формы новой встречи

var myOm;
Office.initialize = function () {
  $(document).ready(function () {
    myOm = Office.context.mailbox;
    // После загрузки DOM можно показать предварительно
    // заполненную форму новой встречи
    displayFormForNewAppointment();
  });
}
function displayFormForNewAppointment(){
  var formParameters =
  {
    "requiredAttendees" : ["wendy@contoso.com", "derek@contoso.com"],
    "optionalAttendees" : ["shane@contoso.com", "michael@contoso.com"],
    "start" : new Date("September 27, 2012 11:15:00"),
    "end" : new Date("("September 27, 2012 12:30:00"),
    "location" : "Conf room 200",
    "resources" : ['sound', 'lighting', 'recording'],
    "subject" : "Next rehearsal",
    "body" : "This is about the next rehearsal."
  }
  // Отображаем форму для создания встречи
  // с указанными полями в форме
  myOm.displayNewAppointmentForm(formParameters);
}

Перечисленные ниже методы, отображающие формы ответов на приглашение на встречу или другое сообщение, обладают наибольшей гибкостью в настройке:

  • Appointment.displayReplyAllForm(htmlBody);
  • Appointment.displayReplyForm(htmlBody);
  • Message.displayReplyAllForm(htmlBody);
  • Message.displayReplyForm(htmlBody).

Каждый из этих методов позволяет передавать HTML-строку, которая представляет тело формы ответа. В примере на рис. 4 отображается форма ответа всем адресатам сообщения.

Рис. 4. Задание HTML в форме «ответить всем» для сообщения

var myOm;
var myItem;
Office.initialize = function () {
  // Проверяем, надо ли загружать DOM
  $(document).ready(function () {
    myOm = Office.context.mailbox;
    // Получаем выбранный элемент
    myItem = myOm.item;
    // После загрузки DOM выводит форму ответа
    displayReply();
  });
}
function displayReply(){
  // Отображаем форму ответа отправителю и адресатам,
  // только если выбранный элемент является сообщением
  if (myitem.itemType == Office.MailboxEnums.ItemType.Message)
  {
      myItem.displayReplyAllForm
      ("<html><head><head><body>This is the body of " +
       "the email reply.</body></html>");
  }
}

Более сложные концепции

Получение вложения из элемента Почтовое приложение может использовать JavaScript API for Office и EWS-маркеры обратного вызова для доступа к вложениям в текущем выбранном сообщении или встрече в почтовом ящике пользователя. Применение маркера обратного вызова (callback token) позволяет серверному коду почтового приложения (или сторонним веб-сервисам) вызывать EWS-операцию GetAttachment, которая и получает конкретные вложения.

Ниже описывается, как почтовое приложение получает вложения (этот процесс представлен на рис. 5):

  1. Почтовое приложение запрашивает через Mailbox.getCallbackTokenAsync маркер обратного вызова, вызывает Item.attachments, чтобы получить метаданные о вложениях в выбранной встрече или сообщении (включая идентификаторы вложений), и использует Mailbox.ewsUrl для получения URL конечной точки EWS применительно к текущей учетной записи электронной почты.
  2. Почтовое приложение передает маркер обратного вызова, идентификаторы вложений и URL конечной точки EWS коду на серверной стороне.
  3. Код на серверной стороне вызывает EWS-операцию GetAttachment, чтобы реально получить вложения из хранилища Exchange.

Почтовые приложения обращаются к вложениям с Exchange Server
Рис. 5. Почтовые приложения обращаются к вложениям с Exchange Server

Exchange Server Exchange Server
Mail App Running on Host Application (Outlook Web App) Почтовое приложение, выполняемое в хост-приложении (Outlook Web App)
Server-Side Code Код на серверной стороне

Заметьте, что код на серверной стороне должен обращаться к EWS, напрямую создавая EWS-запросы. Вы не можете использовать метод Mailbox.makeEwsRequestAsync для доступа к операции GetAttachment. На момент написания этой статьи доступ к вложениям поддерживается, если почтовое приложение выполняется в Outlook Web App. Эта функциональность недоступна, когда почтовое приложение работает в Outlook.

Как обращаться к вложениям из почтового приложения, см. в примере MSDN «Mail apps for Office: Get attachments from an Exchange server» (bit.ly/11dG9fS).

Маркер пользователя для единого входа (single sign-on, SSO) Для аутентификации и авторизации пользователей ваше почтовое приложение может применять распространенные веб-методы аутентификации. Если вам нужно поддерживать аутентификацию для единого входа пользователей между Outlook и Outlook Web App, задействуйте маркеры идентификации Exchange, вызвав метод Mailbox.getUserIdentityTokenAsync.

SSO базируется на том, что Exchange Server выдает маркер идентификации учетной записи электронной почты пользователя на этом Exchange Server. Этот маркер содержит несколько полей, в том числе сигнатуру для проверки маркера и уникальный идентификатор, представляющий учетную запись электронной почты в Exchange.

В целом, почтовое приложение, которое аутентифицирует пользователей, может взаимодействовать с веб-сервисом (это может быть код на серверной стороне для данного почтового приложения или сторонний веб-сервис), применяющим собственный провайдер идентификации (Identity Provider, IdP) для проверки идентификации пользователя в федеративной системе идентификации. Ключ к SSO — сопоставление, поддерживаемое веб-сервисом между уникальным идентификатором (из маркера идентификации Exchange) и идентификацией пользователя (известной IdP).

На рис. 6 дана высокоуровневая схема применения маркеров идентификации Exchange для аутентификации.

Аутентификация с применением маркеров идентификации Exchange
Рис. 6. Аутентификация с применением маркеров идентификации Exchange

Exchange Server Exchange Server
Mail App Running on Host Application (Outlook Web App) Почтовое приложение, выполняемое в хост-приложении (Outlook Web App)
Web Service (Back End or Third Party) Веб-сервис (серверный или сторонний код)
Mapping Token -> User 1 Token -> User 2 Сопоставление
Маркер -> Пользователь 1
Маркер -> Пользователь 2

Давайте суммируем этот процесс.

Почтовое приложение вызывает Mailbox.getUserIdentityTokenAsync, чтобы запросить маркер идентификации Exchange для пользователя, и указывает метод обратного вызова, запускаемый по окончании асинхронной операции получения маркера.

Функция обратного вызова получает этот маркер и передает его веб-сервису для аутентификации.

Веб-сервис получает открытый ключ от Exchange Server для проверки маркера.

В зависимости от того, входил ли ранее данный пользователь, веб-сервис продолжает свою работу следующим образом.

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

Затем сервис создает сопоставление между уникальным идентификатором, предоставленным маркером, и идентификацией пользователя из удостоверений, которые известны IdP. На основе удостоверений сервис может зарегистрировать пользователя.

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

  1. Почтовое приложение вызывает getUserIdentityTokenAsync и получает тот же уникальный идентификатор в маркере.
  2. Почтовое приложение передает маркер веб-сервису.
  3. Веб-сервис проверяет маркер.
  4. Веб-сервис просматривает таблицу сопоставлений, находит пользователя и авторизует доступ.

Проверив маркер, вы можете запомнить вошедшего пользователя, даже когда он открывает почтовое приложение из другого хост-приложения или с другого устройства.

Детали аутентификации и проверки довольно сложны. Для упрощения процесса можно задействовать EWS Managed API Validation Library. С помощью этой Validation Library веб-сервис создаст объект, извлечет маркер и поместит его в объект, проверит, допустима ли его подпись, и, если да, поместит уникальный идентификатор в базу данных сопоставлений.

Подробнее об использовании маркеров идентификации Exchange для аутентификации см. следующую документацию в Dev Center:

  • «Authenticating a mail app by using Exchange identity tokens» (bit.ly/ZYaZ9v);
  • «Inside the Exchange identity token» (bit.ly/ZxRogq);
  • «How to: Call a service by using an identity token in Exchange» (bit.ly/12jqxcU);
  • «How to: Use the Exchange token validation library» (bit.ly/11akmEg);
  • «How to: Validate an Exchange identity token» (bit.ly/15im6SO);
  • «How to: Authenticate a user with an identity token for Exchange» (bit.ly/13gZ36T).

Примеры кода вы найдете по следующим ссылкам.

  • Вы можете обратиться к примеру почтового приложения и веб-сервиса, которые используют маркеры идентификации Exchange для аутентификации «Mail apps for Outlook: Use a client identity token»; этот пример написан на C# для Visual Studio 2012 (bit.ly/ZxS85b).
  • Пример того, как использовать .NET Framework для проверки маркеров идентификации Exchange клиента, см. в «Mail apps for Outlook: Validate a client identity token using the .NET Framework» (bit.ly/17j9FSZ).

Доступ к подмножеству EWS Вызывая метод Mailbox.makeEwsRequestAsync, почтовое приложение может обращаться к подмножеству EWS-операций для создания, поиска, получения, обновления, перемещения или отправки элемента или папки. Например, вы можете использовать операцию CreateItem для создания встречи, сообщения или контакта. Операция FindItem позволяет искать элементы в почтовом ящике, а операция SendItem — отправлять сообщение или приглашение на собрание. Поддерживаемое подмножество операций относится только к почтовому ящику пользователя. Другие EWS-операции, относящиеся к организации в целом, такие как доступ к Global Address List или перебор всех сотрудников в организации, недоступны почтовым приложениям. Кроме того, поскольку поддерживаемое подмножество операций дает весьма широкие возможности, почтовые приложения должны запрашивать разрешение на чтение и запись в почтовом ящике через свои манифесты, и лишь администраторы могут устанавливать почтовые приложения, требующие такого разрешения.

Вызывая метод Mailbox.makeEwsRequestAsync, вы запрашиваете EWS на Exchange Server, где размещен почтовый ящик пользователя. До вызова makeEwsRequestAsync укажите входные аргументы для следующих параметров:

  • data — создание запроса XML SOAP для EWS-операции, которую вы намереваетесь вызывать;
  • callback — метод обратного вызова, который будет обрабатывать результат операции;
  • userContext — любые входные данные для метода обратного вызова.

Когда операция завершается, инфраструктура приложений для Office запускает метод обратного вызова с одним аргументом — объектом AsyncResult; это делается по аналогии с другими асинхронными методами, которые вы видели в первой части этой серии статей. Метод обратного вызова может обратиться к свойству AsyncResult.value, чтобы получить XML SOAP-ответ, содержащий релевантные для операции данные. Этот метод может также обратиться к свойству AsyncResult.asyncContext для доступа к любому другому входному параметру, переданному через параметр userContext. Затем метод обратного вызова разбирает XML в SOAP-ответе, чтобы получить релевантные для своих целей данные.

Полный список поддерживаемых EWS-операций и более подробную информацию см. в документации Dev Center «Calling Web services from a mail app for Outlook» (bit.ly/12jtH0z). Пример почтового приложения, демонстрирующий вызов операции GetItem, см. в примере «Mail apps for Outlook: Make an EWS request», написанном на C# для Visual Studio 2012 (bit.ly/Zv3hEQ).

Заключение

На этом я заканчиваю рассмотрение того, как создаются почтовые приложения, а также серию статей по JavaScript API for Office. Предыдущие статьи из этой серии и сопутствующую онлайновую статью для этой части см. по следующим ссылкам:


Анжела Чу-Хатоун (Angela Chu-Hatoun) — начинала как разработчик ПО, а затем переключилась на написание документации, поясняющей, как работает то или иное программное обеспечение. В течение 12 лет была техническим писателем в Office Division и писала для разработчиков документацию по созданию приложений для Office и других решений для Outlook. Любит читать о текущем состоянии дел в отрасли, возиться в саду, путешествовать, готовить пищу и следить за модой.

Выражаю благодарность за рецензирование статьи экспертам Microsoft: Эндрю Саламатову (Andrew Salamatov) и Тиму Уэну (Tim Wan).