пятница, 30 марта 2012 г.

Redirect из EventReceiver’а

Как оказалось, это довольно частая задача. Но практически нигде не разрешённая.
Итак, у нас есть обработчик события создания элемента списка (ItemAdding); и нам необходимо сразу после создания перейти на другую страницу.

На первый взгляд задача простая: у нас есть и «Page.Response.Redirect(…)» и «SPUtility.Redirect(…)».
Но… Для первого случая у нас нет объекта Page! А второй метод требует указания HttpContext, который в обработчиках события пуст.

Решение!
Как всегда хак! :)

Как известно, создание элементов списка можно отменять (для этого обработчики событий видимо изначально и создавались). При этом, отменяя, мы можем указать адрес, на который нам следует перейти (чтобы посмотреть причину отмены) (доступно только в SharePoint 2010).
То есть, мы можем отменить создание элемента, и сделать искомый редирект на нужную страницу. Нам нужно только пересоздать отменённый элемент. Вот тут и кроется ограничение этого метода – у нас нет доступа к вложениям элемента при его создании.

Пример кода реализации данного метода:

public override void ItemAdding(SPItemEventProperties properties)
{
    base.ItemAdding(properties);

    //Отключаем сам обработчик, чтобы избежать зацикливания
    EventFiringEnabled = false;

    var list = properties.List;

    //Создаём элемент списка и заполняем его свойствами из только что созданного пользователем элемента
    var ni = list.Items.Add();

    //Выбираем все созданные пользователем свойства доступные для редактирования, а также свойство Title (Название)
    foreach (var ff in list.Fields.Cast<SPField>().Where(spf => !spf.ReadOnlyField && (spf.FromBaseType == false || spf.InternalName == "Title")))
        ni[ff.InternalName] = properties.AfterProperties[ff.InternalName];
    ni.SystemUpdate();

    //Включаем обработчик
    EventFiringEnabled = true;

    //Отменяем создание элемента созданного пользователем
    properties.Cancel = true;
    //Устанавливаем действие при отмене - переход на другую страницу
    properties.Status = SPEventReceiverStatus.CancelWithRedirectUrl;
    //Устанавливаем адрес страницы, на которую делаем переход. В данном случае - страница редактирования только что созданного элемента
    properties.RedirectUrl = string.Format("/{0}?ID={1}", properties.List.Forms[PAGETYPE.PAGE_EDITFORM].Url, ni.ID);
}

Событие ItemAdded для этого метода не подходит, т.к. там элемент списка уже создан и отменить это действие уже нельзя.

пятница, 2 марта 2012 г.

Обзор средств разработки для SharePoint в Visual Studio 11 Beta


Visual Studio 11 Beta включает набор новых и улучшенных существующих средств SharePoint Developer Tools для разработки для SharePoint 2010, позволяющих увеличить вашу продуктивность.
Рассмотрим их по порядку.

SharePoint List Designer для списков SharePoint и типов содержимого
Теперь у вас есть возможность создавать ваши списки SharePoint и типы содержимого в визуальном редакторе.

Шаблон для создания Site Column
Создание столбцов сайта стало проще. Для этого создан специальный шаблон, который генерирует GUID для вашего столбца и позволяет вам установить для него наиболее используемые атрибуты.
(P.S. Я не знаю ни одного человека, который бы использовал столбцы сайта :))

Шаблон веб-части Silverlight
Новый шаблон веб-части Silverlight позволит вам сфокусироваться на дизайне и программировании Silverlight, не думая о самой веб-части. Он поможет вам добавить приложение Silverlight в ваше решение SharePoint и развернуть его на сайте в один шаг.

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

Возможность публикации решений SharePoint на удалённые сервер SharePoint
Теперь вы можете публиковать решения на удалённые сервера SharePoint с помощью Visual Studio! Также у вас есть возможность выкачивать пакеты SharePoint из удалённой системы.

Утилиты профилирования для тестирования производительности решений SharePoint
Вам будет проще узнать узкие места в производительности ваших решений SharePoint.

Улучшенная поддержка IntelliSense в изолированных (sandboxed) решениях
Теперь вы сможете полностью доверять IntelliSense при разработке изолированных решений, т.к. IntelliSense отображает только подходящие для вашего проекта методы и свойства. К примеру, вам не нужно думать какие методы и свойства можно использовать в изолированных решениях.

Улучшенная поддержка отладки JavaScript и IntelliSense для JavaScript
Вы сможете писать код JavaScript в ваших решениях SharePoint с полной поддержкой IntelliSense, а также пользоваться отладкой JavaScript кода в Visual Studio. Вы даже сможете ссылаться на JavaScript-файлы, расположенные в базе данных содержимого SharePoint.

Реорганизация шаблонов проектов и элементов SharePoint
Last but not least (последнее по списку, но не по значению): шаблоны проектов и элементов SharePoint теперь организованы более логично, так что теперь вы сможете без труда найти то что вам нужно.

Шаблоны проектов для SharePoint 2007 (для рабочих процессов) теперь удалены из списка проектов Visual Studio 11. Если вы захотите продолжить использование данных проектов, то вам необходимо мигрировать их в SharePoint 2010.

Улучшенный диалог добавления ссылок
Свершилось! Наконец-то в диалоге добавления ссылок появился поиск. Из нововведений также стоит отметить группировку по категориям и указание библиотек, ссылки на которые уже присутствуют в проекте.



Ссылка на пошаговые руководства по описанным нововведениям Visual Studio 11.

Нововведения в Visual Studio 11 Beta.

Наслаждайтесь!

Предварительные сведения о SharePoint 15

Всех поздравляю с наступлением новой эры! Эры Office 15 и, в частности, SharePoint 15!
Готовимся и ждём (скрестив пальцы)...

Описание нововведений:
Что нового в SharePoint 15

пятница, 13 января 2012 г.

Работа с тегами и заметками (комментариями)


Теги и заметки позволяют помечать страницы сайта SharePoint общедоступными соответственно тегами и комментариями.
Данные социальные объекты приписываются к определённому адресу и содержат в себе заголовок (не отображается на сайте), содержимое и владельца (создателя).
Программный доступ к ним осуществляется через объекты SocialTagManager и SocialCommentManager, а также объект SocialRatingManager для рейтинга. Далее будем рассматривать работу с комментариями, работа с тегами и рейтингом реализовывается аналогичным образом.

Получение списка комментариев. Рассмотрим следующий метод:
using (var site = new SPSite(SPContext.Current.Site.ID))
{
    var spServiceContext = SPServiceContext.GetContext(site);
    var socialContextManager = new SocialCommentManager(spServiceContext);
    var comments = socialContextManager.GetComments(new Uri("http://mysite/default.aspx", UriKind.Absolute));
}

Проблемы приведённого кода в том, что он позволяет получить комментарии только определённого пользователя. Для получения всех комментариев всех пользователей для определённого Url необходимо произвести операцию обнуления контекста! Например, следующим образом:
using (var site = new SPSite(SPContext.Current.Site.ID))
{
    var httpContext = HttpContext.Current;
    try
    {
        var spServiceContext = SPServiceContext.GetContext(site);
        var currentWeb = SPContext.Current.Web;
        HttpContext.Current = null; //Обнуляем текущий контекст
        var socialContextManager = new SocialCommentManager(spServiceContext);
        var comments = socialContextManager.GetComments(new Uri("http://mysite/default.aspx", UriKind.Absolute));
    }
    finally
    {
        HttpContext.Current = httpContext; //Восстанавливаем текущий контекст
    }
}

Добавление нового комментария. Пожалуй, единственная операция, которая работает без дополнительных костылей! J
var spServiceContext = SPServiceContext.GetContext(SPContext.Current.Site); //Получение контекста
var socialContextManager = new SocialCommentManager(spServiceContext); //Создание объекта менеджера комментариев
socialContextManager.AddComment(new Uri("http://mysite/default.aspx", UriKind.Absolute), "Мой комментарий!", false, "Заголовок комментария"); //Создание и добавление комментария

Изменение и удаление комментария. Рассмотрим код:
using (var site = new SPSite(SPContext.Current.Site.ID))
{
    var spServiceContext = SPServiceContext.GetContext(site); //Получаем контекст
    var socialContextManager = new SocialCommentManager(spServiceContext);
    var comments = socialContextManager.GetComments(new Uri("http://mysite/default.aspx", UriKind.Absolute)); //Получаем список комментариев

    comments[0].Comment = "Изменённый комментарий"; //Изменяем комментарий
    comments[0].Update(); //Применяем изменения

    comments[1].Delete(); //Удаляем комментарий
}

Проблема данного кода, в том, что таким образом можно удалить только «свои» комментарии, т.е. те которые созданы текущим пользователем. Для удаления комментариев, созданных другими пользователями, необходимо изменить текущего пользователя в контексте. Сделать это можно используя рефлексию:
var siteUrl = SPContext.Current.Site.Url;
using (var site = new SPSite(siteUrl))
{
    var socialCommentManager = new SocialCommentManager(SPServiceContext.GetContext(HttpContext.Current));
    var comments = socialCommentManager.GetComments(CommentsControl.CurrentUri).ToList(); //Получаем список комментариев
    var commentOwner = comments[0].Owner["AccountName"].ToString(); //Получаем логин пользователя создавшего комментарий
   
    using (var web = site.OpenWeb())
    {
        var user = web.EnsureUser(commentOwner);//Получаем нужного пользователя
        web.AllowUnsafeUpdates = true;
        var request = new HttpRequest("", siteUrl, "");
        HttpContext.Current = new HttpContext(request, new HttpResponse(new StringWriter(CultureInfo.CurrentCulture)));
        HttpContext.Current.Items["HttpHandlerSPWeb"] = web;
        var identity = WindowsIdentity.GetCurrent(); //Получаем сущность текущего пользователя
        typeof (WindowsIdentity).GetField("m_name", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(identity, user.LoginName);
        HttpContext.Current.User = new GenericPrincipal(identity, new string[0]); //Изменяем текущего пользователя текущего контекста

        var socialCommentManager2 = new SocialCommentManager(SPServiceContext.GetContext(HttpContext.Current));
        var commentsList = socialCommentManager.GetComments(CommentsControl.CurrentUri).ToList(); //Получаем список комментариев

        commentsList[0].Comment = "Изменённый комментарий"; //Изменяем комментарий
        commentsList[0].Update(); //Применяем изменения

        web.AllowUnsafeUpdates = false;
    }
}

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

Удачной социализации!

пятница, 5 августа 2011 г.

Программный доступ к элементам навигации SharePoint

Начну с того что настройка навигации осуществляется на странице «Параметры структуры переходов узла».


Доступ к этой странице можно получить через параметры сайта перейдя к пункту «Переходы», либо (при включённой публикации) через пункт главного меню «Изменить структуру переходов».

По отношению к сайтам навигация бывает
  • Глобальная
  • Текущая
По расположению на странице, навигация делится на
  • Верхнюю панель навигации
  • Панель быстрого запуска
Глобальная навигация — навигация общая для всех сайтов, в текущей коллекции сайтов.
Текущая навигация — это, соответственно, навигация, присущая текущему сайту.
Верхняя панель навигации — панель, находящаяся под заголовком и логотипом, обычно отображающая названия сайтов в коллекции:
Панель быстрого запуска — левая панель, содержащая ссылки на библиотеки документов, ссылки и т.п.:

Программный доступ к навигации осуществляется через объект Navigation, находящийся в SPWeb.
//Глобальная навигация
var globalNodes = web.Navigation.GlobalNodes;
//Ветвь домашней страницы
var homeNode = web.Navigation.Home;
//Быстрый запуск
var quickLaunch = web.Navigation.QuickLaunch;
//Верхняя панель навигации
var topNavigationBar = web.Navigation.TopNavigationBar;

Каждый из этих элементов (кроме Home) представляет собой коллекцию ветвей навигации (SPNavigationNode).
Рассмотрим основные их свойства:
foreach (SPNavigationNode node in topNavigationBar)
{
    //Название
    var nodeTitle = node.Title;
    //URL-адрес
    var nodeUrl = node.Url;
    //Видимость
    var nodeVisibility = node.IsVisible;
    //Локальность
    var nodeTargetLocation = node.IsExternal;
    //Аудитория
    var nodeAudience = node.Properties["Audience"] == null ? string.Empty : node.Properties["Audience"].ToString();
    //Тип
    var nodeType = node.Properties["NodeType"] == null ? string.Empty : node.Properties["NodeType"].ToString();
    //Описание
    var nodeDescription = node.Properties["Description"] == null ? string.Empty : node.Properties["Description"].ToString();
    //Вариант открытия ссылки
    var nodeTarget = node.Properties["Target"] == null ? string.Empty : node.Properties["Target"].ToString();
    //Вложенные ветви
    var subNodes = node.Children;
}

Рассмотрим каждый пункт отдельно.
Название, URL-адрес, Вариант открытия ссылки, Описание и Аудиторию мы можем увидеть в окне создания/изменения ссылки на сайте.

Вариант открытия ссылки — возвращает либо пустую строку или «_self», и тогда ссылка будет открываться в том же окне, либо возвращает значение «_blank» и ссылка будет открываться в новом окне.
Про аудитории мы уже говорили, см. предыдущие посты.
Видимость —  данные параметр должен определять, скрыта ли ссылка, но он, к сожалению не работает. 
Локальность — определяет, указывает ли ссылка на локальный ресурс или внешний (true — внешний)
Тип ветви — может принимать следующие значения (все возможные значения можно найти в перечислении NodeTypes):
  • Area — для элементов типа «Сайт»:
  • Heading — для элементов типа «Папка»:
  • AuthoredLinkPlain — для элементов типа «Ссылка»:
  • Page — для элементов типа «Страница»:
Теперь поговорим о настройках навигации, о программном доступе к ним:
Для доступа к ним нам понадобится объект PublishingWeb (находится в библиотеке Microsoft.SharePoint.Publishing).
var publishingWeb = PublishingWeb.GetPublishingWeb(web);

//Показать дочерние узлы
var showSubSites = publishingWeb.IncludeSubSitesInNavigation;
//Отображать страницы
var showPages = publishingWeb.IncludePagesInNavigation;
//Тип сортировки
var ordering = publishingWeb.NavigationOrderingMethod;
//Поле сортировки
var sotringField = publishingWeb.NavigationAutomaticSortingMethod;
//Направление сортировки
var sortDirection = publishingWeb.NavigationSortAscending;

(для SharePoint 2010 эти свойства располагаются в объекте publishingWeb.Navigation)
Отображать страницы — определяет, надо ли отображать страницы из системной библиотеки «Страницы»
Тип сортировки — перечисление, может принимать значения:
  • OrderingMethod.Automatic — Сортировать автоматически
  • OrderingMethod.Manual — Сортировать вручную
  • OrderingMethod.ManualWithAutomaticPageSorting — Сортировать страницы автоматически
Поле сортировки — перечисление, может принимать значения:
  • AutomaticSortingMethod.Title — Сортировка по названию
  • AutomaticSortingMethod.CreatedDate — Сортировка по дате создания
  • AutomaticSortingMethod.LastModifiedDate — Сортировка по дате изменения
Направление сортировки — по алфавиту или в обратном порядке (true — по алфавиту)
Кроме этих свойств, PublishingWeb даёт доступ к текущей структуре навигации:
var currentNodes = publishingWeb.CurrentNavigationNodes;

А ещё проверить видимость элемента. Так например проверяется видимость страниц:
if (node.GetProperty("NodeType") == NodeTypes.Page.ToString())
{
    if (!publishingWeb.IncludePagesInNavigation)
        return false;

    var pPages = publishingWeb.GetPublishingPages();
    var pPage = pPages[nn.Url];
    if (pPage != null && !pPage.IncludeInGlobalNavigation)
        return false;
} 

Всем приятного программирования!