Продуктивность — это, пожалуй, один из самых популярных трендов последних лет, и решение 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, вам важна кроссплатформенность и мобильность, а также вы подумываете об интеграции, надеемся, что наш опыт будет вам полезен.

Комментарии (5)


  1. yanchenko
    19.10.2015 17:17
    +2

    Заглавная картинка классная! Я правильно понял, что плиты в центре приветствуют Evernote?


  1. excoder
    20.10.2015 00:29
    -1

    Логичным шагом было бы для Microsoft купить Evernote :) Сделали бы всё отлично наконец-то.


    1. BigD
      21.10.2015 10:41

      Зачем? OneNote сам по себе очень и очень неплох.


  1. mihmig
    20.10.2015 10:09
    -1

    Видите ли — «накидать» в заметку текста/картинок а потом отправить это нижестоящему менеджменту — это продуктивно только руководителю. работать же с контентом (читай: форматировать) — подчинённым. А это скажу я вам — тот ещё адъ. Ну убогий у Evernote редактор (даже левое форматирование убрать не может).
    К сожалению не могу рекомендовать это тем людям, которые хотели бы увеличить свою продуктивность.


  1. Juggernaut
    20.10.2015 15:33
    +1

    Evernote для Outlook уже существует и называется он MS OneNote. Я это к чему упомянул: мне кажется неправильным, что статья полностью игнорирует уже существующего конкурента с готовой интеграцией от производителя.