четверг, 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 изначально не должно быть пустым, при необходимости его можно изменить позже

четверг, 3 мая 2012 г.

Программное создание страниц веб-частей

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

Решать её необходимо с помощью ProcessBatchData:

/// <summary>
/// Create new webpart page
/// </summary>
/// <param name="list">List to keep page</param>
/// <param name="pageTitle">Page title</param>
/// <param name="layoutTemplate">Layout template id</param>
/// <returns></returns>
private static string CreateWebPartPage(SPList list, string pageTitle, int layoutTemplate)
{
    const string newItemTemplate = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                                    "<Method ID=\"0,NewWebPage\">" +
                                    "<SetList Scope=\"Request\">{0}</SetList>" +
                                    "<SetVar Name=\"Cmd\">NewWebPage</SetVar>" +
                                    "<SetVar Name=\"ID\">New</SetVar>" +
                                    "<SetVar Name=\"Type\">WebPartPage</SetVar>" +
                                    "<SetVar Name=\"WebPartPageTemplate\">{2}</SetVar>" +
                                    "<SetVar Name=\"Overwrite\">true</SetVar>" +
                                    "<SetVar Name=\"Title\">{1}</SetVar>" +
                                    "</Method>";
    var newItemXml = string.Format(newItemTemplate, list.ID, pageTitle, layoutTemplate);

    return list.ParentWeb.ProcessBatchData(newItemXml);
}

 

Применение:

var web = SPContext.Current.Web;
var pagesLibrary = web.GetList("Pages");
CreateWebPartPage(pagesLibrary, "MyPage", 2);

Номера шаблонов для LayoutTemplate:

  1. Целая страница, по вертикали
  2. Верхний колонтитул, нижний колонтитул, 3 столбца
  3. Верхний колонтитул, левый столбец, текстовая область
  4. Верхний колонтитул, правый столбец, текстовая область
  5. Верхний колонтитул, нижний колонтитул, 2 столбца, 4 строки
  6. Верхний колонтитул, нижний колонтитул, 4 столбца, верхняя строка
  7. Левый столбец, верхний колонтитул, нижний колонтитул, верхняя строка, 3 столбца
  8. Правый столбец, верхний колонтитул, нижний колонтитул, верхняя строка, 3 столбца

или тут.