SharePoint Solutions vs Apps

Если вы еще не в курсе что такое apps (приложения), то прочтите предыдущий пост.

На этот рас попробуем разобраться когда надо использовать приложения, а когда стоит делать решения.

Приложения и решения не пересекаются вообще. Для приложений важна изоляция, чтобы приложение не повиляло на ферму и не делало то, чего делать нельзя. Для решений важна интеграция с платформой. Решения могут использовать возможности SharePoint, дополнять и улучшать их.

Microsoft предлагает всем делать приложения, в соответствии с cloud-first политикой. Считается что если сделать приложение, то оно заработает в Office 365 и onprem. Но именно в этом заключается первое заблуждение относительно приложений.

Первое заблуждение

Люди думают что onprem приложения также хорошо работают, как в облаке. На самом деле приложения, сделанные для Office 365 сложно использовать onprem.

Во-первых provider-hosted app просто так не заработают onprem, потому что onprem не работает OAuth. Можно настроить интеграцию onprem с Office 365, но это порядка на два сложнее и затратнее, чем сделать приложение.

Во-вторых можно настроить Server-to-Server Trust (S2S), но для такого приложения потребуется изменение исходного кода, деплой во внутренней сети, powershell скрипт для установки, который требует farm admin привилегий.

В-третьих – поддержка apps в onpremise варианте требует тщательной настройки  - приложения службы, DNS, сертификаты. Особенно часто возникают проблемы если надо выставить ферму с apps в интернет.

Единственный тип приложений, которые можно поставить из маркета, без дополнительных телодвижений – SharePoint-hosted. Но и тут есть проблемы...

Второе заблуждение

Люди считают что SharePoint-hosted app удобно использовать чтобы задействовать возможности SharePoint. На самом это возможно в очень ограниченных сценариях.

Напомню что SharePoint-hosted app – это артефакты SharePoint и JavaScript, размещаемые на отдельном сайте (AppWeb). Подробнее тут - http://www.instantquick.com/index.php/sharepoint-2013-app-web-versus-host-web-redux?c=elumenotion-blog-archive/sharepoint-2013-and-office-365-apps.

Проблемы:

Третье заблуждение

Люди считают что авторизация приложений (разрешения) поможет сделать эти приложения более безопасными. На самом деле разрешения зачастую работают против разработчиков и не способствуют повышению безопасности.

Во-первых для некоторых действий требуется слишком много разрешений. Например запись в социальную ленту требует прав записи в весь Тенант (в Office365) или во всю ферму (в onprem).  Такой высокий уровень разрешений позволяет делать приложению что угодно с вашими данными.

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

В-третьих в маркет не принимают приложения, требующие full control. То есть серьезное приложение, использующее потенциал SharePoint, поставить из маркета невозможно.

Выводы

  1. SharePoint-hosted приложения подходят только для небольшого изолированного функционала. Например информеров погоды\пробок и (не дай бог) дней рождения.
  2. Provider-hosted в onprem имеет смысл только для интеграции существующего приложения в SharePoint. Создавать новое слишком накладно.
  3. В office 365 имеет смысл делать provider-hosted (с хостингом в Auzre Websites). Но мало что удастся получить приложению от SharePoint. Поэтому скорее будет вариант построить веб-приложение, а потом интегрировать с SharePoint. Примерно так работает новый LightSwitch.

Вкратце приложения имеет смысл делать или для чего-то простого, или для интеграции отдельных веб-приложений в SharePoint. А для всего остального...

Используйте Sandboxed Solution

Если вы начали возмущаться, говорить что Microsoft запретил Sandbox, то будете правы, почти. Microsoft запретил серверный код в Sandboxed решениях. И то “запретил” – громко сказано, сейчас он работает как в облаке, так и onprem.

Но вы можете в вашем решении разворачивать JavaScript код, типы контента, списки, страницы, шаблоны сайтов, workflow (в 2013 они декларативные), схему поиска и прочие радости. При этом также сохраняя стабильность фермы, даже если вы запускаете серверный код. Хотя потребность в серверном коде в SharePoint 2013 гораздо меньше.

Все новые возможности SharePoint 2013 (CSOM\REST, Client-Side Rendering) доступны для решений, также как для приложений. При этом для установки достаточно быть sitecollection администратором. Пока еще работает серверный код в Sandbox, вы можете легко делать Event Receiver.

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

Когда возможностей sandbox не хватит для реализации функционала вы можете сделать FullTrust решение, которые выставляет User Code Proxy и веб-части, а ваше Sandbox решение их использует. А в Office365, в случае нехватки возможностей Sandbox, можно в iframe показывать внешний сайт и делать Remote Event Receivers.

Заключение

По странному стечению обстоятельств новые возможности SharePoint вдохнули жизнь в концепцию, которую Microsoft пытался похоронить. Sandboxed решения свободны от недостатков SharePoint-hosted приложений, при этом гораздо проще, чем provider-hosted. Именно с них стоит начинать разработку для SharePoint, кроме самых простых случаев.

Кстати маркет можно использовать для привлечения клиентов, сделав прототип\демо вашего приложения в SharePoint-hosted варианте, и предлагая установить sandboxed ршение уже вне маркета.



SharePoint Apps Intro

С момента релиза SharePoint 2013 прошел ровно год. Я специально не писал ничего про Apps, собирал информацию и анализировал опыт.

Что такое Apps

Краткий дисклеймер для тех, кто не в курсе что такое Apps. Идея apps заключается в том, что пользователи смогут самостоятельно устанавливать приложения для SharePoint из Market или внутреннего каталога компании. Примерно также, как это происходит на мобильных устройствах. Но, в отличии от мобильных устройств, любой посторонний код для SharePoint очень опасен. Поэтому нужно этот код максимально изолировать, ограничить в правах и жестко контролировать.

Кажется мы то уже слышали в 2010 году, и тогда это были Sandbox Solutions? Именно! Мотивация такая же, как и раньше – увеличить стабильность фермы и дать больше возможности устанавливать решения пользователям.

Но Microsoft решил пойти шире и сказал что apps вообще не должны работать на серверах SharePoint. Это может быть JavaScript в браузере, или внешний веб-сервис, общающийся с SharePoint с помощью клиентской объектной модели.

Так как код становится внешним, по отношению к SharePoint, то там можно использовать любые языки и библиотеки. Angular или Backbone на клиенте, ASP.NET MVC+EF или даже PHP+MySQL на сервере.

Архитектура Apps

Здесь и далее я буду называть apps - приложениями, а solutionsрешениями.

Приложения ставятся на сайт (SPWeb) из того же интерфейса, из кодоторого создаются списки. Более того, теперь списки тоже называются приложениями, но это не более чем маркетинговый ход.

Технически приложения для SharePoint представляют манифест, который описывает как можно обратиться к App. Способов на самом деле немного:

  • Переход по ссылке на сайт приложения, это обязательно для любого приложения.
  • Отображение в iframe на странице SharePoint, примерно как веб-часть в решении.
  • Переход по ссылке или отображение в iframe в виде диалога из Ribbon или меню, примерно как custom action в решении.

Еще можно привязать внешний event receiver, но это уже нужно делать в коде приложения.

А теперь главный вопрос: где же будет работать приложение?

Тут два варианта:

  • Внешний, по отношению к SharePoint, веб-сервер. Его разработчик должен самостоятельно развернуть, настроить SSL, обеспечить доступность, изоляцию между экземплярами приложений, и прочие радости. Это называется Provider-hosted app. Есть еще вариант автоматического развертывания сайта в Azure из пакета приложения (AutoHosted), но он, спустя год после релиза, толком не работает в Office 365, а onprem скорее всего никогда не будет.
  • Подсайт в SharePoint. Для экземпляра приложения может быть создан отдельный сайт (appweb). На сайт можно разворачивать обычные артефакты SharePoint – страницы, файлы, списки, workflows. Серверный код нельзя деплоить, поэтому доступен только код на JavaScript. AppWeb автоматически удаляется при удалении приложения. Это называется SharePoint-hosted.

Эти два варианта не взаимоисключающие. Вы можете иметь отдельный сервер, где будет работать код на любимом вами языке, при этом приложение будет создавать AppWeb и писать данные туда. Или наоборот, код у вас будет на JavaScript в AppWeb и будет обращаться к веб-сервисам на внешнем сайте.

Маркет и монетизация приложений

Главная завлекаловка для разработчиков – возможность монетизировать приложения. Приложение может быть плантым и иметь разные варианты лицензирования. Платные приложения можно распространять только через маркет, при этом приложения проверяются людьми до попадания в маркет и требуется соблюдение правил (правила).

Маркет очень удобен для пользователей, они могут сами находить и ставить приложения. Не требуется качать файлы, запускать скрипты и делать другие непонятные вещи.

Что может делать приложение

Потенциально почти все. Клиентская объектная модель покрывает очень много возможностей – работа с артефактами SharePoint (сайтами, списками, элементами), поиск, таксономия, профили пользователей, публикация, социальные возможности, BCS и даже Project Server.

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

Еще есть app-only policy, когда права пользователя не учитываются, учитываются только разрешения приложения. Это как повышение привилегий, которого так не хватало в Sandbox. Но, увы, не работает в JavaScript коде.

Стоит ли использовать apps?

Спустя год вполне можно оценить насколько apps получили распространение, достаточно заглянть в маркет: http://office.microsoft.com/en-us/store/results.aspx?avg=osu150. Приложений не просто мало, а очень мало. Немалая часть из них – не живые. Также можно найти аналитику по использованию приложений -  http://www.instantquick.com/index.php/one-month-since-release-observations-lessons-learned-and-future-plans.

Среди моих знакомых мало кто пишет apps, несмотря на то что половина новых проектов стартует на SharePoint 2013.

Есть мнение что apps вообще не приносят ничего полезного для разработчиков: http://blog.furuknap.net/sharepoint-2013-app-model-solves-non-problems-only.

Все это составляет грустную картину про приложения. В следующий раз постараюсь проанализировать недостатки apps и дать развернутый ответ для чего стоит использовать приложения и решения SharePoint...



Материалы со встречи RuSUG 17.10.2013

Мой доклад про TypeScript  в SharePoint



Доклад Виталия Жукова про поиск:



Спасибо всем, кто пришел.



Встреча RuSUG 17-го октября. Наконец-то...

Мы долго ждали и верили и наконец-то... Встреча Russian SharePoint User Group сотоится 17-го котября 2013 года в Microsoft Technology Center на Белорусской в 19:00.

На встрече будут два доклада: мой про TypeScript и применение его в разработке для SharePoint, доклад Виталия Жукова (http://blog.vitalyzhukov.ru/) про Поиск в SharePoint 2013.

Ссылка на эвент на фейсбуке https://www.facebook.com/events/220294124804312

Регистрация обязательна (иначе вас в Microsoft Technology Center не пустят): http://rusug.timepad.ru/event/85241/



Развертывание полей таксономии в SharePoint

Поля таксономии, также известные как поля управляемых метаданных (managed metadata), появились в SharePoint 2010. Метаданные позволяют создавать иерархии терминов, которые можно использовать как справочные значения (c typeahead в UI) и, начиная с SharePoint 2013, для навигации.

Метаданные поддерживают множество языков, синонимы и дополнительные свойства. Можно организовать навигацию по метаданным в библиотеке или фильтрацию по поддереву в поиске SharePoint.

И все было бы прекрасно, но развернуть в составе WSP пакета поле метаданных в списке – очень нетривиальная задача. Можно конечно сделать с помощью кода, некоторые даже очень любят этот подход, но в большом масштабе будет много копипасты и статический анализ делать гораздо сложнее.

Если деплоить с помощью CAML, то возникает две проблемы, но об этом по порядку

Проблема первая – схема поля

Если вы погуглите, то найдете минимум четыре статьи как деплоить таксономические поля:

Все рецепты приведут к неверному результату. Ближе всех к правде оказался последний пост, но и в нем есть проблемы.

Ни в одном посте не указано как поле попадает в список. А именно от этого зависит как поле будет работать. В прошлом посте я писал, что методы полей срабатывают, только если используется ContenTypeRef в схеме List Definition. Эти методы выполняют много работы – добавляют поля в список, привязывают event receiver_ы для синхронизации значений полей таксономии и catchall поля.

Итак правильная схема таксономического поля:

  <Field
       ID="{defbf0ed-377a-4e62-a980-0493ac0ef42e}"
       Name="TaxonomyColumn"
       DisplayName="Taxonomy Column"
       Type="TaxonomyFieldType"
       Required="FALSE"
       Group="Custom Site Columns"
       DisplaceOnUpgrade="TRUE" 
       Overwrite="TRUE"
  >
  </Field>

И все УлыбкаНет, это не шутка.

Если нужны множественные значения, то указать Mult=”TRUE” и Type="TaxonomyFieldTypeMulti".

Далее необходимо поле добавить в тип контента, а тип контента в список. Как писал в прошлом посте. Сделаете по-другому – не заработает.

На что стоит обратить внимание:

  • TaxonomyFieldType унаследован от Lookup, но атрибут ShowField нельзя указывать в схеме, это делается при добавлении поля в список. Если очень хочется указать, то ShowField=”Term$Resources:core,Language” и только так.
  • Не надо указывать Id связанного текстового поля. В случае когда Id не указан – генерируется автоматически.
  • Связанное поле деплоить не надо. Скорее всего вы это сделаете неправильно и у вас перестанет работать поиск. Кстати алгоритм создания связанного поля в 2010 и 2013 отличается.
  • В элементе Customizations можно указать атрибуты поля по-умолчанию. Это проще всего сделать поправив свойства поля на сайте, а потом аккуратно скопировав в схему.

К сожалению на этом месте возникает вторая проблема.

Проблема вторая – привязка к набору терминов

Мало задеплоить поле, надо еще привязать его к набору терминов. Для этого у поля нужно задать два свойства SspId и TermSetId, соответственно Id службы управляемых метаданных и Id набора терминов. Эти два идентификатора не являются инвариантными, то есть могут быть разными в разных фермах. Имена групп и наборов терминов тоже могут варьироваться в зависимости от текущего языка.

По сути есть два сценария развертывания:

TermSet уровня коллекции сайтов

Для каждой коллекции сайтов в хранилище Managed Metadata создается отдельная группа, видимая только в пределах этой коллекции сайтов. При при активации фичи с полем можно создавать (или импортировать) TermSet  в локальную группу, а при деактивации – удалять.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    var site = properties.Feature.Parent as SPSite;
    var web = site.RootWeb;
    var field = web.Fields[FieldId] as TaxonomyField;

    var session = new TaxonomySession(site);

    var store = session.DefaultSiteCollectionTermStore;
    //sp2010
    //var group = store.Groups 
    //                 .OfType<Group>()
    //                 .First(g => g.IsSiteCollectionGroup);

    //sp2013
    var group = store.GetSiteCollectionGroup(site);

    bool allTermsAdded;
    string errorMessage;
    var termSet = store.GetImportManager()
                       .ImportTermSet(group, 
                                      textReader, 
                                      out allTermsAdded, 
                                      out errorMessage);
    if (!allTermsAdded)
    {
        termSet.Delete();
        store.CommitAll();
        throw new InvalidOperationException(errorMessage);
    }
            
    field.SspId = store.Id;
    field.TermSetId = termSet.Id;
    field.Update(true);
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
    var site = properties.Feature.Parent as SPSite;
    var web = site.RootWeb;
    var field = web.Fields[FieldId] as TaxonomyField;

    var session = new TaxonomySession(site);

    var store = session.DefaultSiteCollectionTermStore;
    store.GetTermSet(field.TermSetId).Delete();
    store.CommitAll();
}


Это самый простой метод. Вместо импорта можете использовать любой способ создания набора терминов.

Недостатков у этого метода два:

  • Нельзя создавать наборы терминов с фиксированными Id, так как фича может быть активирована на нескольких сайтах, а Id в рамках одной службы метаданных должны быть уникальны.
  • Набор терминов виден только в коллекции сайтов и не может быть использован в других коллекциях, например для XSP.

Внимание! Код выше не является referenece-кодом, его нельзя бездумно копипастить в свои решения. Для того чтобы он работал надо как минимум добавить логирование и проверку, что поле не настроено. FeatureActivated вызывается как минимум один раз для каждого экземпляра фичи, может быть и больше.

TermSet уровня службы

Если же вы хотите чтобы набор терминов не был привязан к одной коллекции сайтов, то нужно код развертывания набора терминов отделить от кода настройки поля.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    var site = properties.Feature.Parent as SPSite;
    var web = site.RootWeb;
    var field = web.Fields[FieldId] as TaxonomyField;

    var termStoreId = new Guid((string)site.WebApplication.Properties[field.InternalName + ".SspId"]);
    var termSetId = new Guid((string)site.WebApplication.Properties[field.InternalName + ".TermSetId"]);

    field.SspId = termStoreId;
    field.TermSetId = termSetId;
    field.Update(true);
}

Осталась только указать нужные SspId и TermSetId при установке WSP до активации фичи.  Проще всего это сделать в PowerShell скрипте установки. Причем скриптом можно создать свое приложение-службу таксономии и полностью перенести все термы из среды разработки. Это легко сделать с помощью командлетов Export-SPMetadataWebServicePartitionData и Import-SPMetadataWebServicePartitionData.

Этим же способом можно привязывать поле к уже существующему в целевой ферме набору терминов.

Заключение

Как и в прошлом посте, в этом я выкладываю правила для SPCAF (http://www.spcaf.com/) , которые помогут контролировать правильность развертывания таксономии.

PS. Кто заинтересовался как устроена таксономия внутри предлагаю почитать http://www.andrewconnell.com/sharepoint-2010-managed-metadata-in-depth-look-into-the-taxonomy-parts