воскресенье, 9 декабря 2012 г.

CAML-запросы с расширенной фильтрацией по пользователям и группам

Давно забытое старое!
Речь сегодня пойдёт об использовании тега Membership в CAML-запросах.
Этого тега нет ни в CAML Builder ни в CAML Designer, поэтому не все знают о его существовании.
Тем не менее, на MSDN присутствует описание этого тега, хоть и без примеров.
Итак, запрос с использованием этого тега может выглядеть так:
<Where>
  <Membership Type='CurrentUserGroups'>
    <FieldRef Name='Person' />
  </Membership>
</Where>

Тег Membership содержит атрибут Type, указывающий тип фильтрации:
  • SPWeb.AllUsers – Выбор элементов, содержащих в указанном поле пользователя
  • SPWeb.Groups - Выбор элементов, содержащих в указанном поле группу
  • SPWeb.Users - Выбор элементов, содержащих в указанном поле пользователя, не содержащегося в группе 
  • CurrentUserGroups - Выбор элементов, содержащих в указанном поле группу, в которую входит текущий пользователь
  • SPGroup - Выбор элементов, содержащих в указанном поле указанную группу; группа (ID) указывается отдельным атрибутом тега Membership: ID=’1’

Пример.
Допустим имеем такой список:
image

Тестовое (консольное) приложение:
static void Main(string[] args)
{
    var values = new Dictionary<string, int>
        {
            {"SPWeb.AllUsers", 7},
            {"SPWeb.Groups", 7},
            {"SPWeb.Users", 7},
            {"CurrentUserGroups", 7},
            {"SPGroup", 1073741823}
        };

    foreach (var value in values)
        RunQuery(value.Key, value.Value);

    Console.WriteLine();
    Console.WriteLine("Completed!");
    Console.ReadKey(true);
}

static void RunQuery(string membership, int userId)
{
    using (var site = new SPSite("http://x33/", GetUserToken(userId)))
    {
        using (var web = site.OpenWeb())
        {
            var list = web.Lists["CAMLTest"];

            var query = new SPQuery {Query = string.Format("<Where><Membership Type='{0}' ID='4'><FieldRef Name='Person' /></Membership></Where>", membership)};
            var items = list.GetItems(query);

            Console.WriteLine("Current user: {0}; Membership type: {1}; Items count: {2}", web.CurrentUser.Name, membership, items.Count);
            items.Cast<SPListItem>().ToList().ForEach(li => Console.WriteLine("ID - {0}; Title - {1}; Person - {2}", li.ID, li.Title, li["Person"]));
            Console.WriteLine("----------");
            Console.WriteLine();
        }
    }
}

static SPUserToken GetUserToken(int userId)
{
    using (var site = new SPSite("http://x33/"))
    {
        using (var web = site.OpenWeb())
        {
            return web.SiteUsers.GetByID(userId).UserToken;
        }
    }
}

Результат работы приложения:
image

Наиболее интересный тип фильтрации: CurrentUserGroups.
Допустим в списке у нас проставлены группы. С помощью данного запроса мы можем выбрать все элементы, которые содержат группу текущего пользователя, т.е. элементы, доступные участником тех же групп, что и для текущего пользователя! (аналог фильтрации по аудиториям)

четверг, 20 сентября 2012 г.

Обзор get-параметров в адресах SharePoint

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

1. Contents=1 (http://site/default.aspx?contents=1). Переход к странице обслуживания веб-частей.
2. IsDlg=1 (http://site/default.aspx?IsDlg=1). Открытие страницы с использованием главной страницы (master page) minimal.master, т.е. без панели быстрого запуска, частично без ленты, шапки и т.п. Часто используется в диалоговых окнах.
image
3. Filter=1 (http://site/lists/AllItems.aspx?Filter=1). Открытие представления списка с выпадающими фильтрами полей.
image
4. ToolPaneView=2 (http://site/defailt.aspx?ToolPaneView=2). Открытие страницы с панелью добавления веб-частей. В зависимости от значения параметра открывается различная вкладка: 2 – Обзор, 3 – Поиск, 5 – Импорт.
image
5. DisplayMode=Design (http://site/default.aspx?DisplayMode=Design). Открытие страницы в режиме редактирования.
6. Mobile=1 (http://site/default.aspx?Mobile=1). Отображение в виде для мобильных устройств.
7. DeveloperDashboard=true (http://site/default.aspx?DeveloperDashboard=true). Отображение Developer Dashboard. (Должна быть включена возможность такого отображения: OnDemand).

Кроме этого, есть ещё Dynamic Link Libraries – библиотеки, указывая параметры которым можно получать различные результаты (owssrv.dll, admin.dll и другие). Подробнее о них можно найти в Overview of the SharePoint Team Services Architecture.

понедельник, 20 августа 2012 г.

Разбиение запроса SPQuery на страницы

Ещё один способ получить большое количество данных – это использовать разбиение запроса на страницы, т.е. получение элементов списка порциями по несколько элементов.

Для этого надо указать объекту класса SPQuery значения для свойств ListItemCollectionPosition и RowLimit.

RowLimit – указывает сколько элементов списка необходимо получить.

Свойство ListItemCollectionPosition – это объект класса SPListItemCollectionPosition принимающий в конструкторе строковой параметр, в котором указано с какого элемента необходимо получить данные (точнее следующего за ним элемента). Выглядит это так: “Paged=TRUE&p_ID=5” – получить элементы после элемента с ID равным 5.

Например, сделаем такой метод:

private static SPListItemCollection GetPagedItems(SPList list, string pagingInfo, uint rowLimit)
{

            var query = new SPQuery
                {
                    RowLimit = rowLimit,
                    ListItemCollectionPosition = new SPListItemCollectionPosition(pagingInfo)
                };
                    
            return list.GetItems(query);
}

 

Метод возвращает заданное количество элементов (rowLimit) из указанной страницы (pagintInfo).

Если в pagingInfo передавать пустую строку, то метод вернёт первую страницу.

Возвращаемые методом данные (типа SPListItemCollection) содержат в себе данные для получения следующей страницы – свойство ListItemCollectionPosition.

 

Таким образом, мы можем организовать постраничную загрузку и обработку элементов списка, например следующим образом (используя вышеприведённый метод):

using (var site = new SPSite("http://mysite/"))
{
    using (var web = site.OpenWeb())
    {
        var list = web.Lists["TestList"];

        var pagingInfo = string.Empty; //Здесь храним информацию о странице
        var pageNumber = 1; //Номер страницы, для оформления вывода
        SPListItemCollection items = null; //Полученные данные

        do
        {
            if (items != null && items.ListItemCollectionPosition != null)
                pagingInfo = items.ListItemCollectionPosition.PagingInfo; //Получаем данные о странице из предыдущего запроса

            Console.WriteLine("Page {0}, Info '{1}'", pageNumber, pagingInfo);

            items = GetPagedItems(list, pagingInfo, 5); //Получаем по 5 элементов
            foreach (SPListItem item in items)
                Console.WriteLine(item.Title);

            Console.WriteLine();
            pageNumber++;
        } while (items.ListItemCollectionPosition != null);
    }
}

 

Для моего списка результат выглядит следующим образом (консольное приложение):

image

пятница, 17 августа 2012 г.

Получение большого количества данных из SharePoint или использование ContentIterator

Как правило для получения данных из списков SharePoint мы используем SPQuery. Но если количество возвращаемых элементов велико, то мы можем получить SPQueryThrottleException.

Чтобы этого избежать, необходимо использовать появившийся в SharePoint 2010 класс ContentIterator!

Кроме элементов списков, он также умеет перебирать файлы, списки, сайты, коллекции сайтов, а также элементы или файлы в указанной папке.

Использовать данный класс предельно просто!

Пример:

using (var site = new SPSite("http://x33/"))
{
    using (var web = site.OpenWeb())
    {
        var list = web.Lists["TestList"];

        var itemsTitles = new List<string>();
        
        var iterator = new ContentIterator();
        iterator.ProcessListItems(list, spListItem => itemsTitles.Add(spListItem.Title), (spListItem, exception) => true);
    }
}

 

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

Класс находится в Microsoft.Office.Server.Utilites.

При необходимости ему также можно указать SPQuery, чтобы выбирать только нужные элементы.

Методы перебора других сущностей SharePoint аналогичны.

воскресенье, 22 июля 2012 г.

Internet Explorer 10: открытие последних вкладок при запуске

Таки свершилось! Сколько лет (скорее десятков) ждали!

IE (версии 10) теперь умеет сохранять вкладки при закрытии и открывать их при запуске!

IE10 IE10Eng

По умолчанию данное поведение выключено, необходимо включать в параметрах самостоятельно.

четверг, 19 июля 2012 г.

Ошибка при установке Prerequisites для SharePoint 2013

В самом начале установки Prerequisites для SharePoint 2013 столкнулся с ошибкой:

image

Ошибка установки ролей сервера приложений и веб-сервера.

Обзор журнала (Review the log file) показал, что происходит запуск нескольких команд. Последней из которых оказалась:

"C:\Windows\system32\cscript.exe" "C:\Windows\system32\iisext.vbs" /enext "ASP.NET v4.0.30319"

Пробую её отдельно, и получаю ошибку:

“Error while configuration application or extention.

The extention does not exist in the restriction list.”

При чём, при попытке заменить v4.0.30319 на v2.0.50727 ошибки не возникло.

Поиск по самому тексту ошибки ничего не дал, но к месту, где нужно копать всё же привёл.

 

Итак, решение.

Заходим в Диспетчер IIS (Установщик саму роль должен был установить), идём ветке локального веб-сервера и переходим к пункту Ограничения ISAPI и CGI (ISAPI or CGI Restrictions):

image

Видим два пункта, относящихся к ASP.NET v4.0.30319, но имеющих немного другие имена (с постфиксом 32-bit).

Соответственно решение и состоит в том, чтобы переименовать данные пункты в нужное имя.

image

вторник, 29 мая 2012 г.

Улучшения для разработчиков SharePoint

Хочу, наконец-то, поделиться своими небольшими улучшениями для разработчиков (в основном) SharePoint.

 

Улучшения касаются возможности быстро выполнить некоторые действия, которые иногда приходится нам делать – добавить библиотеку в GAC (и выполнить IISReset), установить решение – двойным кликом по соответствующему файлу:

 

image

image

 

По установке: решение состоит из файлов реестра, bat-файлы (пришлось использовать для комплексных действий) необходимо размещать в папке “C:\\Program Files\Microsoft SDKs\Windows\v7.0A\Bin\” (или подправить соответствующим образом файлы реестра).

 

Скачать можно тут.

суббота, 19 мая 2012 г.

Получение данных об изменениях на сайте - использование SPChangeQuery

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

Для осуществления запроса вызывается метод .GetChanges() объекта SPSite, SPWeb, SPList или SPContentDatabase.

 

Рассмотрим возможные отслеживаемые действия:

var query = new SPChangeQuery(false, false)
                {
                    Add = true, // Добавление объекта
                    Delete = true, // Удаление объекта
                    Update = true, // Обновление объекта
                    SystemUpdate = true, // Системное обновление элемента
                    GroupMembershipAdd = true, // Добавление пользователя в группу
                    GroupMembershipDelete = true, // Добавление пользователя в группу
                    Move = true, // Перемещение объекта
                    Rename = true, // Переименование объекта
                    Restore = true, //Восстановление объекта из корзины или архивной копии
                    RoleAssignmentAdd = true, // Назначение роли пользователю или группе
                    RoleAssignmentDelete = true, // Удаление роли
                    RoleDefinitionAdd = true, // Назначение разрешений роли
                    RoleDefinitionDelete = true, // Удаление разрешений роли
                    RoleDefinitionUpdate = true, // Обновление разрешений роли
                };

и возможные отслеживаемые объекты:

var query = new SPChangeQuery(false, false)
                {
                    Alert = true, // Оповещения
                    ContentType = true, // Типы содержимого
                    Field = true, // Поля списка
                    File = true, // Файлы
                    Folder = true, // Папки
                    Group = true, // Группы
                    Item = true, // Элементы списков
                    List = true, // Списки
                    Navigation = true, // Элементы навигации
                    SecurityPolicy = true, // Политики безопасности
                    Site = true, // Коллекция сайтов
                    User = true, // Пользователи
                    View = true, // Представления списков
                    Web = true, // Сайт
                };

Также позволяет задавать начальную (ChangeTokenStart) или конечную (ChangeTokenEnd) дату возвращаемых изменений.

 

Пример использования:

using (var site = new SPSite("http://site/"))
{
    using (var web = site.OpenWeb())
    {
        var list = web.Lists["TestList"];
        var timeZone = web.RegionalSettings.TimeZone;

        var query = new SPChangeQuery(false, false)
                        {
                            FetchLimit = 500, // Limit
                            Item = true, // Object type
                            Add = true, // Change type
                            Delete = true, // Change type
                            Update = true, // Change type
                            ChangeTokenStart = new SPChangeToken(SPChangeCollection.CollectionScope.List, list.ID, new DateTime(2012, 05, 11)) //From date
                        };

        Console.WriteLine("{0,-20}{1, -10}{2}", "Title/ID", "Action", "Time");

        while (true)
        {
            var changes = list.GetChanges(query);

            foreach (var change in changes.OfType<SPChangeItem>())
            {
                string itemTitle;
                try
                {
                    itemTitle = "Item Title: " + list.Items.GetItemById(change.Id).Title;
                }
                catch (Exception)
                {
                    itemTitle = "Item Id: " + change.Id;
                }

                Console.WriteLine("{0,-20}{1, -10}{2}", itemTitle, change.ChangeType, timeZone.UTCToLocalTime(change.Time));
            }

            // Go to next batch or break
            if (changes.Count < query.FetchLimit)
                break;
            query.ChangeTokenStart = changes.LastChangeToken;
        }
    }
}

 

Результат работы программы (консольное приложение):

image

понедельник, 14 мая 2012 г.

Upload Control – Работа с элементом управления загрузки множества файлов

Сразу покажу скриншоты, о чём идёт речь:
image
image
Позволяет загружать файлы в папку или библиотеку документов на веб-сервере. В работе не сложен, но есть много нюансов.
Итак, исходный код.
<script type="text/jscript">
    function DocumentUpload() {
        var uploadCtl = document.getElementById("idUploadCtl");
        uploadCtl.MultipleUpload();
    }
</script>

<input type="hidden" name="Cmd" value="Save" />
<input type="hidden" name="putopts" value="true" /> <!-- Overwrite files -->
<input type="hidden" name="Confirmation-URL" value="<%= Page.Request.Url.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped) %>" />
<input type="hidden" name="PostURL" value="" />
<input type="hidden" name="VTI-GROUP" value="0" />
<input type="hidden" name="destination" id="destination" value="/Documents"> <!-- Files destination path, must already exist -->

<asp:Panel runat="server" Width="100%">
    <script>
        try {
            if (new ActiveXObject("STSUpld.UploadCtl"))
                document.write("<OBJECT id=\"idUploadCtl\" name=\"idUploadCtl\" CLASSID=\"CLSID:07B06095-5687-4d13-9E32-12B4259C9813\" WIDTH=\"100%\" HEIGHT=\"350px\"></OBJECT>");
        }
        catch (error) {
        }
    </script>
    <asp:Button runat="server" accesskey="O" id="OKButton" CssClass="ms-ButtonHeightWidth" Text="Загрузить файлы" UseSubmitBehavior="False" OnClientClick="DocumentUpload(); return false;"/>
    <asp:Button runat="server" accesskey="C" id="CancelButton" CssClass="ms-ButtonHeightWidth" Text="Отмена" UseSubmitBehavior="False" style="display: none;"/>
</asp:Panel>
Что мы имеем:
  1. Скрипт запуска загрузки файлов
  2. Набор параметров, организованных как скрытые поля с атрибутом name
  3. Панель с ActiveX и объектом, отображающими основной интерфейс
  4. Две кнопки – ok и отмена
Все пункты являются обязательными. Если чего-то будет не хватать, то вы получите неприятное окно с ошибкой:
image
1. Скрипт. Тут всё понятно. Находим сгенерированный позже объект и вызываем его метод. Начинается загрузка файлов.
2. Параметры.
  • putopts – указывает необходимость перезаписи файлов, при совпадении имён. Иначе файл не будет загружен
  • destination – указывает путь к папке или библиотеке документов куда необходимо загружать файлы
  • PostURL – теоретически, сюда нужно указать url страницы, которой нужно пересылать файлы, чтобы страница сама определила дальнейшую их судьбу; но на практике это не заработало (компонент сам загружает файлы, по пути указанному в destination)
Параметры можно изменять на лету (JavaScript’ом) перед непосредственным вызовом метода загрузки.
3. ActiveX. Единственный момент тут – данный ActiveX компонент (“STSUpld.UploadCtl”) не должен быть отключен в браузере.
4. Кнопки. Данные кнопки должны существовать. Вы можете их не использовать, скрыть и т.п., но они должны быть. Они должны быть с type=”button” (не “submit”) и у них должны быть проставлены свойства AccessKey.
Ещё несколько особенностей (скажем спасибо Wictor Wilén):
  • Все кнопки на странице должны иметь атрибут AccessKey
  • Должна быть кнопка с AccessKey=”O” (для OK) и AccessKey=”С” (для Cancel)
  • Значение для destination изначально не должно быть пустым, при необходимости его можно изменить позже