Продуктивность — это, пожалуй, один из самых популярных трендов последних лет, и решение Evernote является ярким примером в этой нише. Отличная новость для пользователей Evernote — теперь доступна возможность работать с решением и в Outlook, о чем дальше и пойдет речь.
Я хочу поблагодарить за подготовку статьи Родиона Насакина (Market Development Director Evernote в России и СНГ), а также компанию Actimind, явлющуюся экспертом в разработке плагинов для различных приложений, включая приложения Microsoft Office.
В этой статье мы хотим рассказать о том, зачем Evernote понадобилось создавать дополнение для Outlook и поделиться опытом его разработки.
Зачем Evernote понадобился Outlook
Сервис Evernote стал популярным во многом за счет своей универсальности, позволяя собирать, синхронизировать и упорядочивать разнообразную информацию. Это могут быть текстовые заметки, рукописные наброски, сканы, фотографии, скопированные из Интернета статьи, списки задач, голосовые записи и многое другое.
70% пользователей используют Evernote в работе и бизнесе, и очевидно, что существенная часть нужной информации поступает к ним по email. Для Evernote, в свою очередь, важно, чтобы пользователи могли быстро собирать в одном месте всю информацию, относящуюся к рабочему проекту, и работать с ней, не покидая приложения.
Поэтому в Evernote помимо набора мобильных и десктоп-приложений есть сразу несколько инструментов для работы с входящими email. Это, в частности, копирование цепочек писем из Gmail с плагином Evernote Web Clipper и пересылка на специальный адрес Evernote. И, конечно, как только в Office 365 появилась поддержка сторонних дополнений (add-in), команда сервиса задумалась над специальным решением для такого заметного инструмента.
Разработку Evernote для Outlook поручили независимой команде Actimind, нашим соотечественникам из Санкт-Петербурга. Ранее ребята уже работали над дополнениями Evernote Web Clipper для браузеров, так что имели представление об Evernote API и специфике сервиса в целом.
С Evernote для Outlook пользователи получили возможность скопировать в свой аккаунт письмо, переписку целиком или ее часть в Evernote, указав блокнот назначения и нужные метки. Корпоративные блокноты и метки для пользователей решения Evernote Business также поддерживаются.
По умолчанию все вложенные в письма файлы также сохраняются в Evernote как вложения в заметке. При этом печатный и рукописный текст во вложенных изображениях становится доступным для поиска. Можно отправить в Evernote и только цитату из письма, выделив нужный текст.
Приложение работает и «в обратную сторону» — можно быстро добавить заметки из Evernote в создаваемое в Outlook письмо, чтобы поделиться ими с коллегами.
Как устроено дополнение для Outlook
Дополнение или надстройка (add-in) для Outlook представляет собой файл манифеста, а также одну или несколько веб-страниц. Манифест это XML-файл, в котором хранится некоторая информация о приложении, в том числе адрес, с которого Outlook загрузит стартовую страницу. Страницы же отвечают непосредственно за основную логику и интерфейс приложения. Они будут открыты в защищенном фрейме на сайте Outlook, и смогут общаться с сервисом через специально предоставленный API.
В отличие от классических дополнений для Outlook, новые приложения запускаются только вручную, в контексте одного элемента. Пока приложение не запущено, оно не имеет никакого представления о действиях пользователя. Кроме того, новые приложения могут работать только с письмами и приглашениями. Поддержки задач, журнальных записей и календарных событий в онлайн-версии Outlook пока нет. Зато новые приложения обладают кроссплатформенностью и могут работать в любых клиентах Outlook.com (Outlook 2013 или 2016, Outlook Online), включая мобильные.
Среда разработки
Разрабатывать надстройку можно в любом HTML+JS+CSS редакторе, но мы бы особо выделили два варианта.
Во-первых, это Napa Office 365 Development Tools (www.napacloudapp.com), облачное решение, которое отлично подходит для быстрого прототипирования.
Здесь можно быстро познакомиться с основными настройками манифеста и опробовать JS API в деле. Napa хостит файлы приложения и автоматически обновляет манифест при его изменении. Когда перестанет хватать возможностей онлайн редактора, проект можно экспортировать, например, в Visual Studio.
Visual Studio, начиная с 2012 версии, поддерживает возможность разработки надстроек для Office 365: обновляет манифест, поднимает IIS с файлами, позволяет проводить отладку через IE. Однако для работы нужно будет установить дополнительный компонент, о котором подробнее можно почитать здесь.
В других редакторах может присутствовать встроенный веб-сервер, но манифест, скорее всего, придется загружать и обновлять вручную, через пункт меню Outlook Manage add-ins. Впрочем, это требуется не так часто.
Процесс разработки
В качестве основы была выбрана связка mithril+require.js, код писался в основном на СoffeeScript и Less. Сборка осуществлялась с помощью Grunt.
За основу манифеста мы взяли экспортированный файл из Napa — там уже есть всё необходимое.
<?xml version="1.0" encoding="utf-8"?>
<OfficeApp xsi:type="MailApp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/office/appforoffice/1.1">
<Id>e8df9484-022a-479e-8175-c08d2d540bdb</Id>
...
<DisplayName DefaultValue="Evernote DEV"/>
<IconUrl DefaultValue="https://localhost:44555/app/images/app-icon.png" />
...
<FormSettings>
<Form xsi:type="ItemRead">
<DesktopSettings>
<SourceLocation DefaultValue="https://localhost:44555/app/html/home-read.html" />
<RequestedHeight>350</RequestedHeight>
</DesktopSettings>
<TabletSettings>
<SourceLocation DefaultValue="https://localhost:63342/app/html/home-read.html?mobile" />
<RequestedHeight>350</RequestedHeight>
</TabletSettings>
<PhoneSettings>
<SourceLocation DefaultValue="https://localhost:44555/app/html/home-read.html?mobile" />
</PhoneSettings>
</Form>
<Form xsi:type="ItemEdit">
<DesktopSettings>
<SourceLocation DefaultValue="https://localhost:44555/app/html/home-compose.html" />
</DesktopSettings>
...
</Form>
</FormSettings>
<Permissions>ReadWriteMailbox</Permissions>
...
</OfficeApp>
Основные части манифеста включают ID приложения, его название и описание, а также режимы работы и ссылки. Приложение Evernote работает в режиме чтения (ItemRead) и в режиме редактирования письма (ItemEdit), на десктопах, планшетах и мобильных телефонах. При желании, можно указать разные ссылки для каждого из этих случаев.
Поскольку HTTPS для всех ресурсов является обязательным требованием, понадобится сертификат. На первое время и для локальной разработки хватит самоподписанного, но для полноценной проверки в мобильных клиентах нужен будет настоящий.
После добавления манифеста приложения в аккаунт Outlook над письмом в режиме чтения (или на верхней панели в режиме редактирования) появится иконка приложения.
Как упоминалось выше, все надстройки запускаются вручную, в открывающемся фрейме сверху (или сбоку, в режиме редактирования) от тела письма. В него будет загружена соответствующая страница из манифеста. Страница должна содержать ссылку на office.js, который создаст глобальный объект Office и проведет его инициализацию. Останется только поместить логику запуска приложения в Office.initialize.
Office.initialize = (reason) =>
language = Office.context.displayLanguage
context.init()
После вызова initialize можно начинать пользоваться JS API, например, получить язык интерфейса, выбранный пользователем. Кстати, в наличии хорошая поддержка локализации, в том числе и полей манифеста.
Мы использовали схему с несколькими версиями надстройки — для разработки, для тестирования сборок, и одну стабильную для презентации. С точки зрения Outlook это разные приложения, у каждого из них — свой манифест с уникальным ID, и ссылаются они на разные места. Таким образом можно вести разработку и тестирование, не мешая друг другу. В нашем случае манифест DEV-версии указывает на путь вида «localhost:44555», что удобно для совместной разработки. Но при необходимости каждый разработчик может создать себе свою версию манифеста для удобства или экспериментов.
Как упоминалось в начале, в режиме чтения письма приложение должно уметь сохранять текущее письмо или всю переписку, а также вложения этих писем.
При сохранении письма мы также сохраняем все его атрибуты. В JS API получить их очень легко:
item = Office.context.mailbox.item
mail.subject = item.subject
mail.sender = item.from.displayName + " ("+ item.from.emailAddress+")"
С телом письма сложнее, в режиме чтения его получить не выйдет. К счастью, при наличии у приложения уровня доступа ReadWriteMailbox разрешается выполнять метод makeEwsRequestAsync, который позволяет выполнить вручную сформированный SOAP-запрос к сервису, правда, не любой, а из ограниченного списка. С помощью этого метода от сервиса можно получить некоторые вещи, которые пока недоступны через JS API.
Office.context.mailbox.makeEwsRequestAsync(
@getItemUniqueBodyRequest(Office.context.mailbox.item.id),
(asyncResult) -> @processMailSoap(asyncResult, callback))
getItemUniqueBodyRequest формирует SOAP-запрос на основе идентификатора письма, processMailSoap разбирает присланный ответ. makeEwsRequestAsync, как и многие другие потенциально длительные методы JS API, является асинхронным. На асинхронные методы распространяется ограничение — одновременно могут выполняться не более трех, кроме того, максимальный размер возвращаемого результата ограничен одним мегабайтом.
Таким же образом можно получить цепочку писем, а вот с вложениями возникнут проблемы. JS API не предоставляет прямого доступа к телам вложений. Но способ получить их имеется, для этого нужно вызвать функцию Office.context.mailbox.getCallbackTokenAsync, получить от нее краткодействующий токен доступа, и передать его на сервер вместе с ссылкой на сервис Exchange, которая находится в Office.context.mailbox.ewsUrl. Серверная часть имеет возможность выполнить ограниченный набор SOAP-запросов к Exchange с помощью токена, в том числе и получить данные вложений. Набор запросов, которые сервис может выполнить с токеном, шире, чем доступный клиентской части с makeEwsRequestAsync, к тому же на него не распространяется ограничение в 1MB. Но, к сожалению, токен ограничен доступом к одному элементу, в контексте которого была открыта надстройка, даже если в ее манифесте указано разрешение ReadWriteMailbox, поэтому сохранить цепочку писем с вложениями пока невозможно.
Пользователи имеют возможность задать блокнот и метки Evernote, которые будут использоваться по умолчанию. Для хранения этих настроек JS API предоставляет объект Office.context.roamingSettings, который синхронизируется для конкретной надстройки между всеми устройствами этого пользователя Outlook. Значения хранятся в форме JSON.
notebook = Office.context.roamingSettings.get(defaultNotebookSettingName)
Office.context.roamingSettings.set(defaultTagsSettingName, ["tag1","tag2"])
Office.context.roamingSettings.saveAsync(callback)
В режиме написания письма пользователь может добавить ссылку на любую из своих заметок. Получатели смогут открыть заметку в браузере, даже если они не являются пользователями Evernote.
Outlook JS API поддерживает методы для вставки фрагментов HTML в тело. Попутно определяем, в каком формате тело письма — есть текст и HTML, RTF в качестве формата писем в Outlook 365 недоступен.
Office.context.mailbox.item.body.getTypeAsync (result) =>
if result.status == Office.AsyncResultStatus.Failed then return
switch result.value
when Office.MailboxEnums.BodyType.Html
noteUrl = @getNoteShareUrl(note.guid, notebook, shareKey)
imgSrc = @getNoteShareThmbUrl(note.guid, notebook, shareKey)
html = @generateHtml(note, createdBy, noteUrl, imgSrc)
Office.context.mailbox.item.body.setSelectedDataAsync(innerHtml, { coercionType: Office.MailboxEnums.BodyType.Html })
when Office.MailboxEnums.BodyType.Text
noteUrl = remote.getNoteShareUrl(note.guid, notebook, shareKey) + "\n"
Office.context.mailbox.item.body.setSelectedDataAsync(noteUrl, { coercionType: Office.MailboxEnums.BodyType.Text })
К письму можно добавлять вложения, если у вас есть прямая ссылка на них. Outlook сам загрузит и прикрепит файл. А вот возможность вставки inline-элементов ограничена. СontentID не поддерживается, а base64-ссылки Outlook фильтрует. Можно ухитриться и создать вложение, а потом сослаться на него через имя, но этот способ ненадежен, к тому же результат появится не сразу, а только после обновления и сохранения письма. Остаётся только использовать абсолютные ссылки на изображения. На данный момент иконка пропадает, если пользователь закроет доступ к заметке, в силу особенностей работы этого механизма в Evernote.
Трудности
В целом, Office JS API достаточно хорошо документирован, но без сложностей, конечно, не обошлось. Некоторые моменты, достойные упоминания, приведены ниже.
Практически сразу же мы столкнулись с проблемой same-origin policy. Хорошо, если у вас есть возможность сразу разместить файлы надстройки вместе с ее серверной частью, либо имеется доступ к настройкам CORS. У нас такой возможности во время разработки не было. Для экономии времени мы вели разработку параллельно с подготовкой серверной части.
Самым простым способом обойти same-origin policy является Chrome, запущенный с флагом --disable-web-security. Аналогичные возможности существуют практически для всех основных браузеров, но для полноценного тестирования и поддержки мобильных устройств нам пришлось поднимать небольшой прокси-сервер.
Отладка приложения, работающего в настольном браузере, не представляет проблемы. На ваш выбор есть средства IDE и отладчик браузера. Но вот отладка на мобильных устройствах и в десктопном клиенте может вызвать определенные затруднения, поскольку приложение спрятано внутри клиента Outlook. Самое интересное, что поведение приложения в мобильном клиенте может отличаться от поведения приложения, открытого в онлайн-версии Outlook на том же устройстве (в основном страдают стили, конечно). Немного помогает внутренний лог с выводом в интерфейс, но полноценного отладчика в таких условиях все же иногда не хватает, но недавний анонс выхода Office UI Fabric — интерфейс отладчика, по словам команды разработчиков Office 365, должен заполнить данный пробел.
Add-In Evernote для Outlook
Однако, в целом, у нас получилось создать достаточно функциональное и удобное решение на уровне расширений Evernote Web Clipper для браузеров. Так что если ваша аудитория активно работает с Outlook, вам важна кроссплатформенность и мобильность, а также вы подумываете об интеграции, надеемся, что наш опыт будет вам полезен.