Friday, September 22, 2017

Storage Metrics using PowerShell

Using SharePoint Server 2010, 2013;

Introduction

Storage Metrics in the Site Collection Administration page can be used to access the storage consumed by all your sub-sites and libraries. The same can also be retrieved using Power Shell cmdlet. The following script retrieves storage information of each subsites as the default feature has no option to filter the metrics data. Additional information like node level of the subsite, last modified date, Percentage consumed from total content size and storage size in KB/MB/GB is displayed. All these information are exported to a CSV format file which can be used to filter the data through excel. The script is tested in both SharePoint Server 2010 and 2013. This would be helpful in case if we are planning for a migration from SharePoint 2010 or SharePoint 2013 and we want to know the size of each subsites in a site collection. We used this to report to list down all the subsites with size as we want to do the migration after splitting the subsites to different site collection as the overall size of site collection went up beyond recommended size.

Code

#Get Size of sites and subsite in a SharePoint Site Collection
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")


#Get the size of a site
function GetWebSize($Web)
{
[long]$subtotal = 0

$subtotal = GetFolderSize -Folder $Web.RootFolder
$RecyclebinSize=0
foreach($RecycleBinItem in $Web.RecycleBin)
   {
           $RecyclebinSize += $RecycleBinItem.Size
   }
 
return $subtotal + $RecyclebinSize
}

#Get the size of subsites of a site
function GetSubWebSizes($Web)
{
[long]$subtotal = 0
[long]$RecyclebinSize = 0
foreach ($subweb in $Web.GetSubwebsForCurrentUser())
{
[long]$webtotal = 0
$webtotal += GetFolderSize -Folder $subweb.RootFolder
foreach($RecycleBinItem in $subweb.RecycleBin)
{
$RecyclebinSize += $RecycleBinItem.Size
}
$subtotal += $RecyclebinSize
$subtotal += $webtotal
$subtotal += GetSubWebSizes -Web $subweb
}
return $subtotal+$RecyclebinSize
}

#Get the size of the folder
Function GetFolderSize($folder)
{
   [long]$filesize = 0;
   foreach ($file in $folder.Files)
   {
       $filesize += $file.TotalLength;
      
       foreach ($fileVersion in $file.Versions)
       {
           $filesize += $fileVersion.Size;
       }
   }
   foreach ($subfolder in $folder.SubFolders)
   {
       $filesize += GetFolderSize $subfolder
   }
          
   return $filesize;
}

#Gobal variable to storage the node level of a site/subsite.
$global:nodelevel = 1

#Function to get the node level of a subsite/site
Function GetNodeLevel($web)
{
if($web.ParentWeb -ne $null)
{
[int]$global:nodelevel++
GetNodeLevel $web.ParentWeb
}
$web.Dispose()
}

# Function to format in KB/MB/GB
function FormatBytes ($bytes)
{
   switch ($bytes)
   {
       {$bytes -ge 1TB} {"{0:n$sigDigits}" -f ($bytes/1TB) + " TB" ; break}
       {$bytes -ge 1GB} {"{0:n$sigDigits}" -f ($bytes/1GB) + " GB" ; break}
       {$bytes -ge 1MB} {"{0:n$sigDigits}" -f ($bytes/1MB) + " MB" ; break}
       {$bytes -ge 1KB} {"{0:n$sigDigits}" -f ($bytes/1KB) + " KB" ; break}
       Default { "{0:n$sigDigits}" -f $bytes + " Bytes" }
   }
}

#Set Site URL variable
$siteURL = Read-Host "Enter the Site Collection URL "


#Get the Site instance
$site = Get-SPSite $siteURL

#Get the total storage size of the size collection
$totalSize = $site.Usage.Storage
#Array to hold Storage data for all sites and subsites
$StorageDataCollection = @()
     
foreach($web in $site.AllWebs)
{
if($web -ne $null)
{
#Create an object to hold storage data
$StorageDataResult = New-Object PSObject

#Get the size of site and subsites.
$size = GetWebSize($web)
$size+= GetSubWebSizes($web)

#initialize the level variable
$global:nodelevel = 1
#Get the percentage of storage used by a specific web
$percentage = ($size/$totalSize).tostring("P0")

#Format the size in to KB/MB/GB
$formatSize = FormatBytes $size;
#To get the node level
$val = GetNodeLevel $web;
[string] $level = "" + $global:nodelevel
#Add the properties to show in the excel file.
$StorageDataResult | Add-Member -type NoteProperty -name "Site Name" -value $web.Title
$StorageDataResult | Add-Member -type NoteProperty -name "URL" -value $web.Url
$StorageDataResult | Add-Member -type NoteProperty -name "Size" -value $formatSize
$StorageDataResult | Add-Member -type NoteProperty -name "Last Modified Date" -value $web.LastItemModifiedDate
$StorageDataResult | Add-Member -type NoteProperty -name "Percentage" -value $percentage
$StorageDataResult | Add-Member -type NoteProperty -name "Node Level" -value $level

$StorageDataCollection += $StorageDataResult            
     }
 $web.Dispose()
}
     
#export the detailed storage information to a CSV file
$StorageDataCollection | sort-object "URL" | Export-csv "StorageMetricsReport.csv" -notypeinformation
Write-host "Site Storage Report has been generated!"

#dispose object

$site.dispose()      

Result

Run the script in PowerShell Execute the script in PowerShell

CSV file will be created at the location where the script file has been executed from.
Excel sheel view of Storage Metrics






Monday, March 20, 2017

Use of SharePoint Designer 2013

Following are the high level list of features and functionality that we can do using SharePoint Designer 2013
  1. Edit Site
a.      Create list/libraries/Sites
b.      Create site pages
c.      Create Web Part Pages
d.      Create/edit Master pages
e.      Create custom new form  
f.       Copy/move/delete items
g.      Create content types/columns/external content types/site assets
h.      Site Groups
2. Create Workflows
a.      Workflow Actions
b.      List Workflow  
b.      Reusable Workflow  
c.      Site Workflow
d.      Package workflows  
e.      Loops in Workflow
3.      Create external Data sources
 4.      Integrate the Visio designer into SharePoint

 5.      Call REST Web Services

Friday, March 3, 2017

Personalize this page menu is missing

Version: SharePoint Server 2013

Issue: Personalize this page menu is missing on some pages.

Resolution:

If page is enabled with approval workflow, make sure that the approval status is 'approved'. Personalize this page menu will not show up if the Approval status of the page is in draft or pending status.

Thursday, March 2, 2017

Programmatically creating a field from the Custom field type

Environment: SharePoint Server 2013
Requirement: Programmatically create a field/column from a custom field type.


Following is a simple example with sample code to create a field from custom field type. In this example we are creating a custom field type (derived from SPFieldNumber) and add it to document library.

Note: I would recommend to refer to this MSDN post before this post to understand the entire steps involved to create a custom field type.


Creating the Custom Field Class.
The following example defines a custom control for displaying the custom field of type SPFieldNumber. The example overrides the OnAdded method to contain the logic while adding the custom field type in library/list.


using Microsoft.SharePoint;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Prasath.SP
{
   class SampleHitCountField: SPFieldNumber
   {
       public SampleHitCountField(SPFieldCollection fields, string fieldName)
           : base(fields, fieldName)
       {
       }


       public SampleHitCountField(SPFieldCollection fields, string typeName, string displayName)
           : base(fields, typeName, displayName)
       {
       }


       //Event triggered when new field is added to the document library
       public override void OnAdded(SPAddFieldOptions op)
       {
           base.OnAdded(op);
           Update();


           //Hide the field from new and edit form
           this.ShowInEditForm = false;
           this.ShowInNewForm = false;
           this.DefaultValue = "0";
           this.EnforceUniqueValues = false;
           this.Required = false;
           this.Update();
       }
   }
}


Creating the Field Type Definition
The following example registers the custom field type. This file must be named in the format fldtypes*.xml


<?xml version="1.0" encoding="utf-8" ?>
<FieldTypes>
 <FieldType>
   <Field Name="TypeName">SampleHitCountField</Field>
   <Field Name="TypeDisplayName">Total View Count</Field>
   <Field Name="InternalType">SampleHitCountField</Field>
   <Field Name="TypeShortDescription">Total View Count</Field>
   <Field Name="FieldTypeClass">Prasath.SP.SampleHitCountField, $SharePoint.Project.AssemblyFullName$</Field>
   <Field Name="ParentType">Number</Field>
   <Field Name="Sortable">TRUE</Field>
   <Field Name="Filterable">TRUE</Field>
   <Field Name="UserCreatable">TRUE</Field>
   <Field Name="ShowOnListCreate">FALSE</Field>
   <Field Name="ShowOnSurveyCreate">FALSE</Field>
   <Field Name="ShowOnDocumentLibrary">TRUE</Field>
   <Field Name="ShowOnColumnTemplateCreate">TRUE</Field>
   <Field Name="ShowInListSettings">TRUE</Field>
 </FieldType>
</FieldTypes>


Creating a Field from custom Field Type
Following is the method to create field from custom type
/// <summary>
       /// Create custom field as XML
       /// </summary>
       /// <param name="oId">Field GUID</param>
       /// <param name="strDisplayName">Unique Display Name of field</param>
       /// <param name="Required">Field required property</param>
       /// <param name="strFieldType">Custom field type</param>
       /// <param name="strFieldName">Custom field display name</param>
       /// <returns>string</returns>
       internal static string GetCreateFieldAsXml(Guid oId, string strDisplayName, bool Required, string strFieldType, string strFieldName)
       {
           XmlElement element = new XmlDocument().CreateElement("Field"); // Creating the “Field” element for the Inner XML schema
           element.SetAttribute("ID", oId.ToString()); // Setting the GUID of the field from value passed
           element.SetAttribute("Type", strFieldType); // Setting the Parent Type name of custom type registered in the “Fldtypes*.xml” file
           element.SetAttribute("Name", strFieldName); // Setting the Type Display Name registered in the “Fldtypes*.xml” file
           element.SetAttribute("DisplayName", strDisplayName); // Any unique Display Name
           element.SetAttribute("Required", Required.ToString().ToUpper());
           return element.OuterXml; // Returning the OuterXML to create the field as XML
       }

//Code to custom field as XML from custom field type
String strFieldasXML = GetCreateFieldAsXml(Guid.NewGuid(), “Hit Count Field”, false, "SampleHitCountField", “SampleHitCountField”);
web.Fields.AddFieldAsXml(strFieldasXML); //here 'web' is a SPWeb object