В одной из своих предыдущих статей я рассмотрел вопросы, связанные с использованием PowerShell для инвентаризации объектов SharePoint высшего уровня. Я показал, как создаются списки компонентов ферм, веб-приложения, коллекции сайтов и сайты. Вы узнали о том, как из системы SharePoint извлекается информация о списках, библиотеках, файлах и страницах. Одна из статей («Инвентаризация учетных записей пользователей и групп SharePoint с помощью PowerShell», опубликована в Windows IT Pro/RE № 3 за 2015 год) была посвящена запросам — пользовательским и связанным с обеспечением безопасности. На этот раз мы займемся углубленным изучением более сложных запросов с целью выявления и перезагрузки отключенных от шаблонов или индивидуализированных страниц.

Для ознакомления с базовыми требованиями по безопасности при работе с PowerShell обратитесь к предыдущим статьям серии (см. Windows IT Pro/RE № 8 за 2013 год). При выполнении сценариев, публикуемых в этой статье, нагрузки на сервер могут существенно возрастать, что в свою очередь может отразиться на работе пользователей. Возможно, запускать эти сценарии целесообразно только в нерабочее время. Они предназначены для версий SharePoint, устанавливаемых на локальном оборудовании, и не функционируют на платформе Office 365 или SharePoint Online.

Страницы, подключенные к шаблонам и отключенные от шаблонов

Во времена преобладания SharePoint 2007 в ходу были два странных термина, с помощью которых мы описывали изменения, вносимые в страницы SharePoint в процессе их индивидуализации с помощью конструктора SharePoint Designer. И несмотря на попытки представителей Microsoft подкорректировать эти термины, мы до сих пор говорим о Ghosted (подключенных к шаблону) и Unghosted (отключенных от шаблона) страницах. Сегодня страницы Un-ghosted следует называть Customized («индивидуализированными»), а Ghosted страницы — Un-customized («неиндивидуализированными»).

Индивидуализированные или настраиваемые?

Когда владелец сайта редактирует страницу в браузере с помощью меню Settings или с использованием параметров ленты, он осуществляет «безопасную индивидуализацию» с помощью процесса, который можно просто назвать настройкой. Все внесенные в браузере изменения можно отменить в том же браузере. А что делать, если возникнет необходимость в изменениях, которые нельзя внести внутри браузера? Мы открываем сайт в SharePoint Designer. И хотя многие изменения, реализуемые в этом конструкторе, подобны изменениям, которые вносятся в браузере, в нашем распоряжении имеется возможность выполнения дополнительных операций, изменяющих процесс сохранения и извлечения страницы внутри SharePoint.

Неиндивидуализированные (подключенные к шаблону) страницы:

  • Определение страницы хранится на дисковых накопителях сервера SharePoint или в файловой системе.
  • Страницы, извлекаемые из файловой системы, могут быть быстро визуализированы и сохранены в кэше.
  • Эти страницы часто допускают редактирование в браузере с помощью меню Settings, Edit Page или лент PAGE. Такого рода изменения меняют настройки страницы, но не базовое определение. При работе со страницами веб-частей мы можем добавлять, настраивать или удалять веб-части, но не изменять компоновку зон веб-частей. Если же речь идет о страницах Wiki, мы можем настраивать текст, веб-части и число столбцов, но не можем изменять язык HTML основного макета страницы.

Индивидуализированные (отключенные от шаблона) страницы:

  • Когда страница индивидуализируется, или отсоединяется от шаблона, она копируется из файловой системы в базу данных SQL Server.
  • Для выполнения запроса на индивидуализированную страницу необходимо выполнить запрос к таблице SQL, что повышает непроизводительные затраты при каждой загрузке страницы.
  • Возможно, индивидуализированные страницы будут непригодны к использованию в следующей версии SharePoint.
  • Применение индивидуализированных страниц может снижать производительность сайта и сервера.
  • Изменения, индивидуализирующие страницы, редко документируются владельцами сайтов и потому создают для следующего владельца, наследующего этот сайт, трудности в организации поддержки.
  • Индивидуализирующие страницу изменения могут даже препятствовать загрузке страницы. Таким образом, вы можете дробить страницы.

Что можно индивидуализировать?

Существует обстоятельство, которое в наибольшей степени осложняет окончательный сценарий PowerShell. Речь идет о числе мест, где имеются файлы, которые можно индивидуализировать с помощью конструктора SharePoint Designer:

  • Файлы в коллекции SPWeb.Files (пример: /Training/default.aspx);
  • Файлы страниц в библиотеках, таких как Site Pages и Pages (пример: /Training/SitePages/Home.aspx; /EnterpriseSearch/Pages/videoresults.aspx);
  • Файлы. ASPX и. MASTER, размещающиеся по маршруту _catalogs (пример: /_catalogs/masterpage/seattle.master; /Search/_catalogs/masterpage/Display Templates/Search/Item_PowerPoint.html);
  • Формы списков и библиотек, такие как newform.aspx и editform.aspx (пример: /Lists/Announcements/NewForm.aspx).

Как индивидуализировать страницу

Обычно страницы индивидуализируются с помощью SharePoint Designer. Это делается следующим образом.

  1. Загрузите и установите SharePoint Designer, он распространяется бесплатно. Загружать можно версию 2010 или 2013 — в зависимости от того, с какой версией SharePoint вы работаете. Пользователям Office 365 и SharePoint 2016 потребуется версия SPD 2013.
  2. Запустите конструктор и откройте свой сайт.
  3. Откройте страницу для редактирования и нажмите кнопку Advanced Mode на ленте HOME.
  4. Добавьте пробел, пустую строку или внесите другую редакторскую правку (то есть внесение реальных изменений не требуется).
  5. Нажмите кнопку Save.

Итак, ваша страница индивидуализирована или отключена от шаблона. Теперь она будет загружаться чуть медленнее (как правило, разница слишком мала, чтобы ее можно было заметить); возможно, она не будет обновляться до уровня следующей версии SharePoint, а может быть, и вообще не будет загружаться.

Обратный процесс

Страницу нельзя вновь подсоединить к шаблону или «деиндивидуализировать» путем простого удаления с нее внесенных индивидуализирующих изменений. Страницу нужно привести в исходное состояние, что можно сделать с помощью браузера, конструктора SharePoint Designer или оболочки PowerShell.

Перезагрузка страницы с помощью браузера выполняется так:

  1. Перейдите на сайт и выберите пункты Settings (gear), Site Settings.
  2. В разделе Site Actions выберите пункт Reset to Site Definition. Вместо этого можете изменить указатель URL и перейти по адресу: _layouts/15/reghost.aspx (смотрите, опять появилось знакомое «привидение» — ghost!).
  3. Введите URL перезагружаемой страницы и нажмите кнопку Reset.
  4. Нажмите кнопку OK. Файл будет перезагружен, а все индивидуализирующие изменения в нем отменены.

Имейте в виду, что вы можете также привести в исходное состояние все индивидуализированные страницы сайта с этой страницы.

Перезагрузка страницы с помощью SharePoint Designer производится следующим образом:

  1. Откройте сайт в окне SharePoint Designer.
  2. В области Navigation выберите пункт All Files.
  3. Пройдя через все промежуточные уровни, выйдите на интересующую вас страницу и обратите внимание на символ «i», обозначающий, что данная страница была индивидуализирована.
  4. Щелкните на файле страницы правой кнопкой мыши и в открывшемся меню выберите пункт Reset to Site Definition.

Файл будет перезагружен и с индивидуализированного файла будет снята резервная копия.

Перезагрузка страницы с помощью PowerShell или с использованием кода

Если вы хотите за один раз привести в исходное состояние все страницы сайта, можете воспользоваться методом RevertAllDocumentContentStreams объекта SPWeb (см. листинг 1).

Чтобы перезагрузить отдельную страницу, можно использовать метод RevertContentStream объекта SPFile. Пример для формы списка приведен в листинге 2.

Обратите внимание: чтобы реализовать это изменение, вам не потребуется вызывать. Update () по каждому объекту; в этом отношении данное обновление отличается от большинства других обновлений объектов SharePoint (см. листинг 3).

Обнаружение всех индивидуализированных или отключенных от шаблона страниц

Теперь мы переходим к сценариям. Первый сценарий относится к категории «безопасных» (см. листинг 4). Он не предусматривает внесения каких-либо изменений. Но при всей его безопасности не забывайте, что при выполнении сценария в рабочей сети может снизиться ее производительность.

Примечания к сценарию:

  • С помощью данного сценария вы можете обрабатывать всю ферму, одну коллекцию сайтов или отдельный сайт.
  • В каждом разделе сценария тестируется свойство файла. CustomizedPageStatus.
  • В некоторых разделах тестируется расширение файла (.Name.EndsWith («.aspx»)).
  • Доступ к каталогу главной страницы осуществляется с помощью команды $web.GetCatalog (116).
  • В каждом разделе определяются пользовательские столбцы, так чтобы мы могли объединять результаты в единый выходной файл, который может быть перенаправлен в Out-GridView, Select *, Export-CSV и т. д.
  • При извлечении форм списков необходимо выполнить дополнительную операцию с целью получения объекта файла.

Перезагрузка всех индивидуализированных или отсоединенных от шаблона страниц

А теперь мы переходим к опасному сценарию. При выполнении приведенного сценария результаты труда специалистов компьютерной графики и консультантов, оплаченные тысячами долларов, могут обратиться в ничто. Перед запуском этого сценария тщательно взвесьте все обстоятельства и, если возможно, выполняйте его на резервной копии фермы. Из соображений безопасности я закомментировал те строки, выполнение которых приводит к изменениям.

Приведенный в листинге 5 сценарий отличается от сценария в листинге 4 только тем, что в каждом его разделе имеется одна дополнительная строка. Эта строка вызывает метод RevertContentStream для каждого найденного индивидуализированного файла.

Примечания к сценарию:

  • НЕ ЗАПУСКАЙТЕ ДАННЫЙ СЦЕНАРИЙ, ЕСЛИ ПРЕДВАРИ­ТЕЛЬНО НЕ СНЯЛИ РЕЗЕРВНЫЕ КОПИИ СО ВСЕХ ИМЕЮЩИХСЯ ДАННЫХ!
  • Проявляйте исключительную осторожность при возвращении к исходному состоянию файлов из Master Page Gallery ($_.GetCatalog (116)), ведь вы, вероятно, заплатили тысячи долларов создавшему их специалисту. Большая часть вашей работы по формированию фирменной символики выполняется на основе файлов из этой галереи.
  • В приведенном в листинге 5 сценарии строки, вносящие изменения, закомментированы. Удаляйте из них символы комментирования только после выполнения сценария из листинга 4. Это поможет вам определить, что именно будет изменено.
  • ВЫ ВЫПОЛНЯЕТЕ ЭТОТ СЦЕНАРИЙ НА СВОЙ СТРАХ И РИСК!

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

Листинг 1. Перезагрузка всех страниц сайта
$web = Get-SPWeb "http://yourServer/sites/yourSite/yourSubsite"
$web.RevertAllDocumentContentStreams()
Листинг 2. Перезагрузка формы списка
$web = Get-SPWeb "http://yourServer/sites/yourSite/yourSubsite"
$file = $web.GetFile("http://yourServer/sites/yourSite/yourSubsite/Lists/yourList/newform.aspx")
$file.RevertContentStream()
Листинг 3. .Update() не используется
$file = $web.GetFile("http:// yourServer/sites/yourSite/yourSubsite /SitePages/home.aspx")
$file.RevertContentStream()
Листинг 4. Сценарий обнаружения обновленных объектов
***## Выберите источник... Уберите символы комментария перед одним (!)
     из следующих источников:
## Для всех сайтов:
#Get-SPSite -Limit All | Get-SPWeb -Limit All |
## или для одной коллекции сайтов
#Get-SPSite http://yourServer/sites/yourSite | Get-SPWeb -Limit All |
## или для одного веба
Get-SPWeb http://yourServer/sites/yourSite |
## обрабатываем вебы по одному...
ForEach {
  #
  # Находим все файлы уровня SPWeb
  #
  $_ |
  Select -ExpandProperty Files |
  Where { $_.Name.EndsWith(".aspx") -AND
          $_.CustomizedPageStatus -eq "Customized" } |
  Select @{l='Location'; e={'WebFile'}}, ServerRelativeUrl,
         TimeCreated, TimeLastModified, ModifiedBy
  #
  # Находим все файлы Catalog (116 = Master Page Gallery)
  #
  $_.GetCatalog(116).Items  |
  Select -ExpandProperty File |
  Where { $_.CustomizedPageStatus -eq "Customized" } |
  Select @{l='Location'; e={'MP Gallery'}}, ServerRelativeUrl,
         TimeCreated, TimeLastModified, ModifiedBy
  #
  # Находим все библиотечные файлы
  #
  $LibrariesToCheck = "Site Pages", "Pages"
  $_ |
  Select -ExpandProperty Lists |
  where {$LibrariesToCheck -contains $_.Title} |
  Select -ExpandProperty Items |
  Select -ExpandProperty File |
  Where { $_.Name.EndsWith(".aspx") -AND
          $_.CustomizedPageStatus -eq "Customized" } |
  Select @{l='Location'; e={'Library'}}, ServerRelativeUrl,
         TimeCreated, TimeLastModified, ModifiedBy
  #
  # Преобразуем в файлы все формы списков
  #
  $w = $_; #сохраняем веб-объект для использования в следующем конвейере
  $_.Lists.forms |
  foreach {$w.getfile($_.url)} |
  Where { $_.CustomizedPageStatus -eq "Customized" } |
  Select @{l='Location'; e={'List Form'}}, ServerRelativeUrl,
         TimeCreated, TimeLastModified, ModifiedBy
} |
# Выбираем объект назначения
Select * | Format-Table -AutoSize
#Out-GridView
#Export-Csv C:\test\ListOfCustomizedPages.CSV
Листинг 5. Обновление объектов
## Выберите источник... Раскомментируйте один и только один из следующих источников
## Для всех сайтов:
#Get-SPSite -Limit All | Get-SPWeb -Limit All |
## или для одной коллекции сайтов
#Get-SPSite http://yourServer/sites/yourSite | Get-SPWeb -Limit All |
## или для одного веба
#Get-SPSite http://maxsp2013wfe/sites/training | Get-SPWeb -Limit All |
## или для одного веба
Get-SPWeb http://maxsp2013wfe/sites/training |
## обрабатываем вебы по одному...
ForEach {
  #
  # Находим все файлы уровня SPWeb
  #
  $_ |
  Select -ExpandProperty Files |
  Where { $_.Name.EndsWith(".aspx") -AND
          $_.CustomizedPageStatus -eq "Customized" } |
  ####### Следующая строка выполняет операцию Reset to Site Definition!
  ###foreach {$_.RevertContentStream(); $_} |
  ###foreach {$_.RevertContentStream(); $_} |
  Select @{l='Location'; e={'WebFile'}}, ServerRelativeUrl,
         TimeCreated, TimeLastModified, ModifiedBy
  #
  # Находим все файлы Catalog (116 = Master Page Gallery)
  #
  $_.GetCatalog(116).Items  |
  Select -ExpandProperty File |
  Where { $_.CustomizedPageStatus -eq "Customized" } |
  ####### Следующая строка выполняет операцию Reset to Site Definition!
  ###foreach {$_.RevertContentStream(); $_} |
  Select @{l='Location'; e={'MP Gallery'}}, ServerRelativeUrl,
         TimeCreated, TimeLastModified, ModifiedBy
  #
  # Находим все библиотечные файлы
  #
  $LibrariesToCheck = "Site Pages", "Pages"
  $_ |
  Select -ExpandProperty Lists |
  where {$LibrariesToCheck -contains $_.Title} |
  Select -ExpandProperty Items |
  Select -ExpandProperty File |
  Where { $_.Name.EndsWith(".aspx") -
AND
          $_.CustomizedPageStatus -eq "Customized" } |
  ####### Следующая строка выполняет операцию Reset to Site Definition!
  ###foreach {$_.RevertContentStream(); $_} |
  Sel
ect @{l='Location'; e={'Library'}}, ServerRelativeUrl,
         TimeCreated, TimeLastModified, ModifiedBy
  #
  # Преобразуем в файлы все формы списков
  #
  $w = $_;
  $_.Lists.forms |
  foreach {$w.getfile($_.url)} |
  Where { $_.CustomizedPageStatus -eq "Customized" } |
  ####### Следующая строка выполняет операцию Reset to Site Definition!
  ###foreach {$_.RevertContentStream(); $_} |
  Select @{l='Location'; e={'List Form'}}, ServerRelativeUrl,
         TimeCreated, TimeLastModified, ModifiedBy
} |
# Выбираем объект назначения
Select * | Format-Table -AutoSize
#Out-GridView
#Export-Csv C:\test\ListOfCustomizedPages.CSV