Работаем с ошибкой "Отсутствуют зависимости на стороне сервера" (Missing server side dependencies)

В данном посте я привожу свою методологию решения проблемы "Отсутствуют зависимости на стороне сервера" (Missing server side dependencies).
В данном примере используется SharePoint 2010, но принцип остается тем же самым и в новых версиях (хотя честно говоря, на SP2016 я не проверял). Важно отметить, что в данном решении нам необходим доступ непосредственно в базу.

В анализаторе SharePoint

ошибка выглядит так:

На секунду задумаемся о том, что же это за ошибка и откуда она берется?

Допустим, у нас имеется серверное решение. В решении есть артефакт - ghosted file, или может быть веб часть. Веб часть могла быть установлена в галерею веб частей, или может быть добавлена на страницу. После чего по разным причинам веб часть удалили (обычно ее переименовывает кто нибудь). Но в базе SharePoint остается запись о старой веб части.

Что же делать?
Сначала нужно найти, где же на нашем сайте ссылка на "отсутствующий" компонент.

Обычно я делаю это с помощью SQL.
Возьмем скрипт, который пройдет по всем таблицам базы данных контента , и найдет там указанную строчку (в нашем случае ИМЯФАЙЛАВЕБЧАСТИ.webpart)
(В своей практике я несколько раз создавал и терял данный скрипт, так что пора его указать в блоге).

Скрипт взят отсюда и слегка модифицирован.

https://stackoverflow.com/questions/591853/search-for-a-string-in-all-tables-rows-and-columns-of-a-db

--

use WSS_Content -- could be your database name here

DECLARE

@search_string VARCHAR(100),

@table_name SYSNAME,

@table_schema SYSNAME,

@column_name SYSNAME,

@sql_string VARCHAR(2000)

SET @search_string = 'yourwebpart.webpart'

DECLARE tables_cur CURSOR FOR SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'

OPEN tables_cur

FETCH NEXT FROM tables_cur INTO @table_schema, @table_name

WHILE (@@FETCH_STATUS = 0)

BEGIN

DECLARE columns_cur CURSOR FOR SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @table_schema AND TABLE_NAME = @table_name AND COLLATION_NAME IS NOT NULL -- Only strings have this and they always have it

OPEN columns_cur

FETCH NEXT FROM columns_cur INTO @column_name

WHILE (@@FETCH_STATUS = 0)

BEGIN

-- SET @sql_string = 'IF EXISTS (SELECT * FROM ' + QUOTENAME(@table_schema) + '.' + QUOTENAME(@table_name) + ' WHERE ' + QUOTENAME(@column_name) + ' LIKE ''%' + @search_string + '%'') PRINT ''' + QUOTENAME(@table_schema) + '.' + QUOTENAME(@table_name) + ', ' + QUOTENAME(@column_name) + ''''

SET @sql_string = 'IF EXISTS (SELECT * FROM ' + QUOTENAME(@table_schema) + '.' + QUOTENAME(@table_name) + ' WHERE ' + QUOTENAME(@column_name) + ' LIKE ''%' + @search_string + '%'') SELECT * FROM ' + QUOTENAME(@table_schema) + '.' + QUOTENAME(@table_name) + ' WHERE ' + QUOTENAME(@column_name) + ' LIKE ''%' + @search_string + '%'''

EXECUTE(@sql_string)

FETCH NEXT FROM columns_cur INTO @column_name

END

CLOSE columns_cur

DEALLOCATE columns_cur

FETCH NEXT FROM tables_cur INTO @table_schema, @table_name

END

CLOSE tables_cur

DEALLOCATE tables_cur

--

Вот что получилось после запуска скрипта :

Итак, мы знаем, что сайт с идентификатором 089D94D0-7BF4-4A51-9E29-2BACABC30DF4 содержит запись об отсутствующей веб части в галерее, и мы можем решить данную проблему с помощью PowerShell или вручную .

Вот пример другой веб части. В данном случае у нас некорректная веб часть находится в двух местах - на основном сайте и на личном сайте администратора.

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

SELECT Webs.FullUrl, Webs.Title, AllDocs.DirName, AllDocs.LeafName, AllWebParts.tp_DisplayName, AllWebParts.tp_ID
FROM AllDocs, Sites, AllWebParts, Webs
WHERE Webs.Id = Sites.RootWebId AND AllDocs.Id = AllWebParts.tp_PageUrlID
AND Sites.Id = AllDocs.SiteId AND tp_WebPartTypeId IN (
SELECT DISTINCT tp_WebPartTypeId FROM AllWebParts (NOLOCK)
WHERE tp_WebPartTypeId IS NOT NULL AND tp_Assembly IS NULL AND tp_Class IS NULL)
-- if you add this line, the script will find your specific web part instead of giving you all web parts on all pages
--AND (AllWebParts.tp_DisplayName = 'yourwebpartname')

Вот что я получил при запуске скрипта:

Pic4.

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

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

К счастью, я вспомнил про еще один хороший прием. Дело в том, что если к любой странице ?contents=1

Вот таким образом можно сбросить все "личные" копии страницы.

А вот так - удалить "некорректную" веб часть.