Меняем адрес страницы с комментариями в Sharepoint 2010 без установки SP1

Комментарии – одна из новых возможностей, добавленных как часть социальных вычислений Sharepoint 2010. Они позволяют пользователям сайта, построенном на Sharepoint, оставлять отзывы и комментарии к контенту. Однако при этом есть проблема — технически комментарии привязаны к странице с использованием абсолютного адреса данной страницы. Это означает, что если вы перенесете страницу с одного сайта на другой, или измените доменное имя сайта – комментарии не будут отображаться (но не потеряются – они по-прежнему будут храниться в базе данных, как мы увидим ниже).

В этом году Microsoft выпустили пакет обновлений (Service pack 1) для Sharepoint 2010. Вместе с ним появился новый метод MergeSocialNotes() в классе SocialDataManager. Также появился новый cmdlet для PowerShell Move-SPSocialComments – обертка над указанным выше методом. Следующий пост описывает другие улучшения и новые фичи: Changes to Social Computing features in SharePoint Server 2010 Service Pack 1. Все это полезно, но что если на ваших серверах SP1 не установлен, но вам нужно изменить адреса страниц комментариев? Такая ситуация вполне возможна, например если заказчик еще не принял решение устанавливать пакет обновлений или нет (кто работал с крупными заказчиками знает, сколько времени может потребоваться на принятие решения и исполнение). В этом посте я расскажу, как можно перенести страницы, не потеряв при этом комментарии.

Прежде всего, нужно знать, что комментарии хранятся в отдельной базе данных для социальных вычислений – в таблице SocialComments:

Как видно на рисунке данная таблица содержит UrlID – внешний ключ к таблице Urls, в которой хранятся абсолютные урлы:

Теперь давайте посмотрим, как реализован метод SocialDataManager.MergeSocialNotes():

internal static bool MergeSocialNotes(UserProfileApplication userProfileApplication,
Guid partitionID, string oldUrl, string newUrl)
{
    if (null == userProfileApplication)
    {
        throw new ArgumentNullException("userProfileApplication");
    }
    if (string.IsNullOrEmpty(oldUrl))
    {
        throw new ArgumentNullException("oldUrl");
    }
    if (string.IsNullOrEmpty(newUrl))
    {
        throw new ArgumentNullException("newUrl");
    }
    if (!userProfileApplication.CheckAdministrationAccess(
UserProfileApplicationAdminRights.ManageSocialData))
    {
        throw new UnauthorizedAccessException();
    }
    using (SqlCommand command = new SqlCommand("dbo.proc_SocialData_MergeSocialNotes"))
    {
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add("@partitionID", SqlDbType.UniqueIdentifier).Value = partitionID;
        command.Parameters.Add("@correlationId", SqlDbType.UniqueIdentifier).Value = ULS.CorrelationGet();
        command.Parameters.Add("@oldUrl", SqlDbType.VarChar).Value = oldUrl;
        command.Parameters.Add("@newUrl", SqlDbType.VarChar).Value = newUrl;
        SqlParameter parameter = new SqlParameter("@success", SqlDbType.Bit);
        parameter.Direction = ParameterDirection.Output;
        command.Parameters.Add(parameter);
        userProfileApplication.SocialDatabase.SqlSession.ExecuteNonQuery(command);
        if (!((bool) command.Parameters["@success"].Value))
        {
            return false;
        }
    }
    return true;
}

Реализация достаточно простая: он вызывает хранимую процедуру proc_SocialData_MergeSocialNotes с переданными параметрами. Эта процедура создается в скриптах socialsrp.sql и socialup.sql, расположенными в папке 14\Template\SQL\SPS (они обновляются в SP1, т.е. в предшествующей версии Sharepoint указанной хранимой процедуры в них нет). В обоих скриптах код хранимый процедуры одинаковый:

IF  EXISTS (SELECT * FROM dbo.sysobjects
WHERE id = OBJECT_ID(N'[dbo].[proc_SocialData_MergeSocialNotes]')
AND OBJECTPROPERTY(id,N'IsProcedure') = 1)
DROP PROCEDURE [dbo].[proc_SocialData_MergeSocialNotes] 
GO

CREATE PROCEDURE [dbo].[proc_SocialData_MergeSocialNotes] 
    @partitionID uniqueidentifier,
    @oldUrl varchar(2048),
    @newUrl varchar(2048),
    @success bit = null output,
    @correlationId uniqueidentifier = null
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @oldUrlID bigint
    DECLARE @newUrlID bigint

    exec proc_Social_GetUrlID @partitionID, @oldUrl, @oldUrlID output, @correlationId
    exec proc_Social_GetUrlID @partitionID, @newUrl, @newUrlID output, @correlationId

    IF (@oldUrlID is null)
    BEGIN
        -- nothing to merge
        set @success = 1
        return
    END

    IF (@newUrlID is null)
    BEGIN
        EXEC proc_Social_EnsureUrlID @partitionID = @partitionID, @url = @newUrl,
@urlID = @newUrlID output, @correlationId = @correlationId;
    END

    IF (@newUrlID is null)
    BEGIN
        -- not able to create new Url - error
        set @success = 0
        return
    END

    UPDATE SocialComments
    SET UrlID = @newUrlID
    WHERE UrlID = @oldUrlID

    -- we succeed, even if there is no data to migrate
    set @success = 1
END
GO

Как видно из примера, используется код двух других хранимых процедур: proc_Social_GetUrlID и proc_Social_EnsureUrlID (которые существовали до установки пакета обновлений). Код, приведенный выше тоже достаточно простой: он проверяет, что newUrl существует в таблице Urls (если нет, то добавляет), и обновляет внешний ключ UrlID в таблице SocialComments для того, чтобы связать комментарий с новым урлом.

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

Например, предположим, что нам нужно переместить новости с подсайта http://example.com/Departments/IT на родительский сайт http://example.com/Departments. Во время миграции новостей, комментарии должны быть тоже обновлены так, чтобы они по-прежнему были видны на новом сайте. Это можно сделать с помощью следующего скрипта:

declare @comment nvarchar(4000), @url nvarchar(2048), @newUrl nvarchar(2048)
declare @partitionId uniqueidentifier
declare @b bit

set @partitionId = CAST('...' as uniqueidentifier)

declare CommentsCursor cursor for
select sc.Comment, u.Url from dbo.SocialComments sc
    inner join dbo.Urls u on sc.UrlID = u.UrlID
where Url like '%/Departmens/IT/%'

open CommentsCursor

FETCH NEXT FROM CommentsCursor 
INTO @comment, @url

while @@FETCH_STATUS = 0
begin
    print @url + '        ' + @comment
    
    set @newUrl = replace(@url, '/Departmens/IT/', '/Departmens/')
    if @newUrl <> @url
    begin
        print 'Change url ' + @url + ' -> ' + @newUrl
        exec proc_SocialData_MergeSocialNotes @partitionId, @url, @newUrl, @b output
        print 'Result: ' + str(@b)
    end
    
    FETCH NEXT FROM CommentsCursor 
    INTO @comment, @url
end

close CommentsCursor
deallocate CommentsCursor

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

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

Последний вопрос – это где взять значение для @partitionId. Простейший способ найти его – это выполнить следующий запрос в базе данных социальных вычислений:

select sc.Comment, sc.PartitionID, u.Url, u.PartitionID from dbo.SocialComments sc
    inner join dbo.Urls u on sc.UrlID = u.UrlID

и посмотреть какой PartitionID используется в вашем веб-приложении.

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

Понятно, что указанный метод является наполовину хэком: он манипулирует данными в базе данных социальных вычислений напрямую в обход объектной модели. Однако, как мы видели, объектная модель – это всего лишь тонкая оболочка над хранимой процедурой. Также этот подход не требует установки пакета обновлений на production-сервера, что может быть важным в реальной жизни. В любом случае, надеюсь что эта информация окажется полезной для вас.

Реклама

Об авторе sadomovalex

Старший инженер, team lead, консультант. Работаю в стеке .Net. Последние несколько лет занимаюсь разработкой enterprise приложений под Sharepoint, чему и будет в основном посвящена тематика этого блога. Также активно использую и интересуюсь ASP.Net MVC, DDD, TDD, Agile. Активно участвую в жизни многих профессиональных сообществ, SPb .Net UG, SPb ALT.Net, rsdn, Finland SP UG и др.
Запись опубликована в рубрике Migration, Sharepoint, Social computing. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s