Pages

Friday, April 8, 2011

How to programmatically copy Web Level, List level and List Item Level Security Information for SharePoint Sites

 

[Note : This post is part of  my main blog “Moving Large MOSS 2007 Sites” and “SharePoint 2010 Migration”]

Some Background

[See my other blog on Drawbacks of current List Export and Import MOSS 2007 Content Migration APIs]

How to fix this gap?

Well, there are SharePoint APIs you can use to copy all the necessary missing security information. But there needs to be an order to perform the process most efficiently. Follow below order of steps:

  • First you will be creating a new site collection, or you may already have your target importing site collection existing.
  • In any case before you perform your Import, you want to first copy over the Custom Permission Level.
  • Next you want to perform your usual Import with All security Included. (settings.IncludeSecurity = SPIncludeSecurity.All)
  • Above Import process will ensure to import all the Users and SharePoint Groups and any OOTB Permission Levels as applicable.
  • Now you want to Copy all the Web level Permission Assignments. Having all ready  copied the custom permission levels, this step will ensure all the permission assignments are appropriately copied over.
  • Next you can attend for each of the lists and libraries you have imported, copy the Permission Assignments.
  • Next you can attend for each Items in your lists and libraries you have imported, copy the Item Level Permission Assignments.

 

Below are the methods for each of these above tasks:

Your call order:

1. PrepareNewSiteCollection();//Here either create the new site collection in preparation for Import, or simply establish your site collection SPSite object.

2. CopyWebRoleAssignments(); //Here copy the web level permission levels.

3. PerformListExportImporting(); //Here perform your List Export and Import.

4. CopyWebRoleAssignments();//Here perform your Web level Permission Assignments copy.
5. CopyListRoleAssignments(); //Here finally copy your List and List item level permissions.

 

Common Methods:

        public static void CopyWebRoles(SPWeb sourceWeb, SPWeb destinationWeb)
{

//First copy Source Web Role Definitions to the Destination Web
foreach (SPRoleDefinition roleDef in sourceWeb.RoleDefinitions)
{
//Skip WSS base permission levels
if (roleDef.Type != SPRoleType.Administrator
&& roleDef.Type != SPRoleType.Contributor
&& roleDef.Type != SPRoleType.Guest
&& roleDef.Type != SPRoleType.Reader
&& roleDef.Type != SPRoleType.WebDesigner
)
{
//handle additon of existing permission level error
try { destinationWeb.RoleDefinitions.Add(roleDef); }
catch (SPException) { }
}
}


}


public static void CopyWebRoleAssignments(SPWeb sourceWeb, SPWeb destinationWeb)
{

//Copy Role Assignments from source to destination web.
foreach (SPRoleAssignment sourceRoleAsg in sourceWeb.RoleAssignments)
{
SPRoleAssignment destinationRoleAsg = null;

//Get the source member object
SPPrincipal member = sourceRoleAsg.Member;

//Check if the member is a user
try
{
SPUser sourceUser = (SPUser)member;
SPUser destinationUser = destinationWeb.AllUsers[sourceUser.LoginName];
if (destinationUser != null)
{
destinationRoleAsg = new SPRoleAssignment(destinationUser);
}
}
catch
{ }

if (destinationRoleAsg == null)
{
//Check if the member is a group
try
{
SPGroup sourceGroup = (SPGroup)member;
SPGroup destinationGroup = destinationWeb.SiteGroups[sourceGroup.Name];
destinationRoleAsg = new SPRoleAssignment(destinationGroup);
}
catch
{ }
}

//At this state we should have the role assignment established either by user or group
if (destinationRoleAsg != null)
{

foreach (SPRoleDefinition sourceRoleDefinition in sourceRoleAsg.RoleDefinitionBindings)
{
try { destinationRoleAsg.RoleDefinitionBindings.Add(destinationWeb.RoleDefinitions[sourceRoleDefinition.Name]); }
catch { }
}

if (destinationRoleAsg.RoleDefinitionBindings.Count > 0)
{
//handle additon of an existing permission assignment error
try { destinationWeb.RoleAssignments.Add(destinationRoleAsg); }
catch (ArgumentException) { }
}

}

}

//Finally update the destination web
destinationWeb.Update();

}


public static void CopyListRoleAssignments(SPList sourceList, SPList destinationList)
{
//First check if the Source List has Unique permissions
if (sourceList.HasUniqueRoleAssignments)
{

//Break List permission inheritance first
destinationList.BreakRoleInheritance(true);

//Remove current role assignemnts
while (destinationList.RoleAssignments.Count > 0)
{
destinationList.RoleAssignments.Remove(0);
}


//Copy Role Assignments from source to destination list.
foreach (SPRoleAssignment sourceRoleAsg in sourceList.RoleAssignments)
{
SPRoleAssignment destinationRoleAsg = null;

//Get the source member object
SPPrincipal member = sourceRoleAsg.Member;

//Check if the member is a user
try
{
SPUser sourceUser = (SPUser)member;
SPUser destinationUser = destinationList.ParentWeb.Users.GetByEmail(sourceUser.Email);
destinationRoleAsg = new SPRoleAssignment(destinationUser);
}
catch
{ }

if (destinationRoleAsg == null)
{
//Check if the member is a group
try
{
SPGroup sourceGroup = (SPGroup)member;
SPGroup destinationGroup = destinationList.ParentWeb.SiteGroups[sourceGroup.Name];
destinationRoleAsg = new SPRoleAssignment(destinationGroup);
}
catch
{ }
}

//At this state we should have the role assignment established either by user or group
if (destinationRoleAsg != null)
{

foreach (SPRoleDefinition sourceRoleDefinition in sourceRoleAsg.RoleDefinitionBindings)
{
try { destinationRoleAsg.RoleDefinitionBindings.Add(destinationList.ParentWeb.RoleDefinitions[sourceRoleDefinition.Name]); }
catch { }
}

if (destinationRoleAsg.RoleDefinitionBindings.Count > 0)
{
//handle additon of an existing permission assignment error
try { destinationList.RoleAssignments.Add(destinationRoleAsg); }
catch (ArgumentException) { }
}

}

}

//Does not require list update
//destinationList.Update();
}
else
//No need to assign permissions
return;



}

public static void CopyListItemsRoleAssignments(SPList sourceList, SPList destinationList)
{
foreach (SPListItem sourceListitem in sourceList.Items)
{
CopyListItemRoleAssignments(sourceListitem, destinationList.GetItemById(sourceListitem.ID));
}

}
public static void CopyListItemRoleAssignments(SPListItem sourceListItem, SPListItem destinationListItem)
{
//First check if the Source List has Unique permissions
if (sourceListItem.HasUniqueRoleAssignments)
{

//Break List permission inheritance first
destinationListItem.BreakRoleInheritance(true);
destinationListItem.Update();

//Remove current role assignemnts
while (destinationListItem.RoleAssignments.Count > 0)
{
destinationListItem.RoleAssignments.Remove(0);
}
destinationListItem.Update();

//Copy Role Assignments from source to destination list.
foreach (SPRoleAssignment sourceRoleAsg in sourceListItem.RoleAssignments)
{
SPRoleAssignment destinationRoleAsg = null;

//Get the source member object
SPPrincipal member = sourceRoleAsg.Member;

//Check if the member is a user
try
{
SPUser sourceUser = (SPUser)member;
SPUser destinationUser = destinationListItem.ParentList.ParentWeb.AllUsers[sourceUser.LoginName];
if (destinationUser != null)
{
destinationRoleAsg = new SPRoleAssignment(destinationUser);
}
}
catch
{ }

//Not a user, try check if the member is a Group
if (destinationRoleAsg == null)
{
//Check if the member is a group
try
{
SPGroup sourceGroup = (SPGroup)member;
SPGroup destinationGroup = destinationListItem.ParentList.ParentWeb.SiteGroups[sourceGroup.Name];
if (destinationGroup != null)
{
destinationRoleAsg = new SPRoleAssignment(destinationGroup);
}
}
catch
{ }
}

//At this state we should have the role assignment established either by user or group
if (destinationRoleAsg != null)
{

foreach (SPRoleDefinition sourceRoleDefinition in sourceRoleAsg.RoleDefinitionBindings)
{
try { destinationRoleAsg.RoleDefinitionBindings.Add(destinationListItem.ParentList.ParentWeb.RoleDefinitions[sourceRoleDefinition.Name]); }
catch { }
}

if (destinationRoleAsg.RoleDefinitionBindings.Count > 0)
{
//handle additon of an existing permission assignment error
try { destinationListItem.RoleAssignments.Add(destinationRoleAsg); }
catch (ArgumentException) { }
}

}

}

//Ensure item update metadata is not affected.
destinationListItem.SystemUpdate(false);
}
else
//No need to assign permissions
return;



}



 





Technorati Tags:

Drawbacks of current List Export and Import MOSS 2007 Content Migration APIs

 

[Note : This post is part of  my main blog “Moving Large MOSS 2007 Sites” and “SharePoint 2010 Migration”]

Some Background.

Suppose you are considering using the SharePoint Content Migration API for copying your SharePoint content from one site to another site. Consider below tested scenarios on what you will actually get out of the API usage results  for your implementation.

When you are using SharePoint SPWeb Object as source to Export/Import API Process, you will get the SharePoint Permissions, SharePoint Users and the SharePoint Groups as well as Site Permissions. In additionally since all the List and list items are part of the larger scope of the SPWeb, you will also get the entire list and list item level permissions copied as well.

Other than security all of your metadata, date and time stamps is all retained. Hence you are covered for the most part. (Excluding any of your customizations, workflows, alerts etc. which you are responsible to appropriately make the transition).

I have experienced a lack of security data being transferred appropriately when it comes to Export/Importing anything  below the SPWeb object.

What security information does SharePoint List Export/Import API Process copy?

  • Copies only the Users/AD Groups  and the SharePoint Groups and their members.
Source Target
image image
Users
image
Users
image
Users are missing.
Permission Levels
image
Permission levels
image
Custom Permission Level(s) are missing
(Publishing Portal specific Permission Levels will also be missed if the target site collection is not a Publishing Site which is fine).

 

What security information does SharePoint List Export/Import API Process not copy?

  • Does not copy Web Level Permission Assignments.
  • Does not copy List/Lib Level Permission Assignments (Other than the OOTB Groups and OOTB Permission Levels).
  • Does not copy List/Lib Item/Folder Level Permission Assignments (Other than the OOTB Groups and permission levels).
Source Target
Web Level Permissions
image
Web level Permissions
image

All Web Level permission assignments are missing.
Library Level Permissions
image
Library Level Permissions
image

All Library Level permission assignments are missing.
Library Item Level Permissions
image
Library Item Level Permissions
image
All Library Item Level permission assignments are missing.

 

How to fix this gap?

See my other blog on How to programmatically copy Web Level, List level and List Item Level Security Information for SharePoint Sites

Thursday, April 7, 2011

SharePoint (MOSS 2007) Export and Import API specifics (PRIME)

 

[Note : This post is part of  my main blog “Moving Large MOSS 2007 Sites” and “SharePoint 2010 Migration”]

[Also see my other blog on Drawbacks of current List Export and Import Content Migration APIs]

[Also see my other blog on Copying Web Level, List level and List Item Level Security Information]

Overview

There are several good references (refer to my main blog on SharePoint 2010 Migration) when it comes to SharePoint Export and Import API. I still wanted to call out as reference to all of the options that will need to be appropriately set to get your export and import going.

Export Specifics

Below I am listing all the possible Export Settings properties following by my comments:

Basic Object definition properties to be set:

SPExportObject exportObject = new SPExportObject();
exportObject.Id = web.ID;
exportObject.IncludeDescendants = SPIncludeDescendants.All;
exportObject.Type = SPDeploymentObjectType.Web;

 

Export Specific settings:

SPExportSettings settings = new SPExportSettings();

//Standard Options
settings.SiteUrl = web.Url;
settings.FileLocation = exportFolderPath;
string exportImportSubFolder = YourExportFolder;

 

Export Log File settings:
string exportLogFile = YourExportLogFilename;
if(System.IO.File.Exists(YourExportLogFilename))
{
    File.Delete(YourExportLogFilename);
}
settings.LogFilePath = YourExportLogFilename;


Export general Settings:
settings.FileCompression = false;
settings.ExcludeDependencies = true;
settings.OverwriteExistingDataFile = true;

Export events to be disabled Settings:
settings.HaltOnNonfatalError = false;
settings.HaltOnWarning = false;


Export configurable Settings:
settings.IncludeSecurity = SPIncludeSecurity.All;
settings.IncludeVersions = .All;
settings.CommandLineVerbose = true; 
settings.ExportMethod = SPExportMethodType.ExportAll;


Note this  settings if you want to mock-run the export:                   
settings.TestRun = true;  
                    
As best practice run the validate, your try catch can catch early error much before the Export is kicked off:
settings.Validate();


Final Export Run Settings:
settings.ExportObjects.Add(exportObject);
SPExport export = new SPExport(settings);
export.Run();

 

Import Specifics

Basic Object definition properties to be set:
SPImportSettings settings = new SPImportSettings();
settings.SiteUrl = YourNewSiteCollection.Url;
//Assign the importing parent web URL
settings.WebUrl = DestinationWebURL;

Set the file locations for where the exported data is located:
settings.FileLocation = YourImportFolderPath;

Set the Import log file name. Delete first if exists from he previous run, otherwise it will append and could get large:
if (System.IO.File.Exists(YourImportLogFile))
{
    File.Delete(YourImportLogFile);
}
settings.LogFilePath = YourImportLogFile;

No need to set compressed since we will be importing from an uncompressed export:
settings.FileCompression = false;

Set to false to be able reparent in a new site collection:
settings.RetainObjectIdentity = false; 

You want all the security to be included (See my other blog on .... ):
settings.IncludeSecurity = SPIncludeSecurity.All;

You want to set to all to import all user date time into:
settings.UserInfoDateTime = SPImportUserInfoDateTimeOption.ImportAll;

You want to supress the events to increase your import performance:
settings.HaltOnNonfatalError = false;
settings.HaltOnWarning = false;
settings.SuppressAfterEvents = false;

You want to get all the versions:
settings.UpdateVersions = SPUpdateVersions.Append;

Verbose log will generate detailed log while running:
settings.CommandLineVerbose = true;

As best practice run the validate, your try catch can catch early errors much before the Import is kicked off:
settings.Validate();

Final Import Run Settings:
SPImport import = new SPImport(settings);
import.Run();

How to programmatically lock SharePoint Site Collection while Exporting and other maintenance work

 

[Note : This post is part of  my main blog “Moving Large MOSS 2007 Sites” and “SharePoint 2010 Migration”]

In order to ensure your export remains consistent through the process of Export and then till the completion of Import, consider Read Locking your Exporting Site Collection.  

Lets see how these  UI settings for the locks map to the code options:

SPSite.WriteLocked

image_thumb1

SPSite.ReadOnly

image_thumb2

SPSite.ReadLocked

image_thumb3

Below I am sharing my code base for implementing the ReadOnly locks. Few things to note:

  • When you are assigning any lock, it is important that you first assign the LockHint before setting the Lock as the site collection becomes the respective lock mode.
  • When you are removing any lock, it is important that you first setting the given Lock false. as the site collection becomes released from the respective lock mode. Then clear off the lock hint.

Your Call order

1. LockSiteCollection(SiteCollection);
2. Perform Your Export/Import here
3. UnLockSiteCollection(SiteCollection);

Common Methods you can use:

 

       private void LockSiteCollection(SPSite Site)
{
//Try lock the Portal Site Collection
try
{
Site.LockIssue = “Your Lock reason”;
}
//handle prior left out read only locks due to errors.
catch (UnauthorizedAccessException)
{
Site.ReadOnly = false;
Site.LockIssue = “Your Lock reason”;
}
Site.ReadOnly = true;

}


private void UnLockSiteCollection(SPSite Site)
{

Site.ReadOnly = false;
Site.ReadLocked = false;
Site.WriteLocked = false;
Site.LockIssue = "";
}




Technorati Tags:

Site Collection Creation and re-parenting

[Note : This post is part of  my main blog “Segregating Large MOSS 2007 Sites” and “SharePoint 2010 Migration”]

When you are importing a SPWeb or an SPWeb tree in to a new Site Collection, you will want to first create new site collection.  Now moving a given SPWeb as Root Web in to a new Site Collection is called re-parenting. From the above references you will get the more on this. But something I wanted to emphasize and clarify why we should create a new Site Collection via an STSADM  createsite as supposed to SPWebApplication.CreateSite().

SPWebApplication.CreateSite().

Result: 

  • Site Collection
  • -->Rootweb is created with given Template Type (STS/MPS/Custom)

What happens here: you can only import on top of the RootWeb. So the end result is that your imported web is always a sub web of the rootweb.

 

STSADM  createsite

Result: 

  • Site Collection
  • -->Rootweb is created with Blank Template Type (NOT STS/MPS/Custom)

What happens here: you can import in to the RootWeb. So the end result is that your imported web can be at rootweb level.

Tuesday, April 5, 2011

How to find SharePoint List and Libraries true Size?

 

Overview

I wanted to get size of list and libraries for my portal in a reporting fashion. For Publishing Portal site collection you can access the list and library size from site actions> under the Site Collection Administration group, you have access to “Storage space allocation”. This is great info. But not something I can consume the same information programmatically.

image 

If you notice this page is server through /_layouts/storman.aspx. Further digging on this page gives me the inherited  name space “Microsoft.SharePoint.ApplicationPages.StorMan” which is obfuscated and hence not sure how this above data is retrieved.

 

Further exploration

So still hoping to find the little nugget that is producing this information, I started diggings in to the Content Database Stored Procedures.

Then I came across two interesting stored procedures: proc_GetListSizes and proc_GetDocLibrarySizes

Both stored procedures take the Site Collection GUID. When test ran, the result was the same as the above Storage space allocation page.

 

So what I did with these stored procedures?

I took these two stored procedures, ran against the content database of my portal site collection. Merged the results of both stored procedures. Used some rudimentary caching  to store and retain the results set. Now you can list or get size for your given SPList object in MB.

Warning

This is not recommended against the production system as this is not a Microsoft Supported operation. Also be aware that the results could be huge and may impact your system.

Well here is all you are waiting for… the code base:

I have tested this against the MOSS 2007 and have not had chance to validate for any other SharePoint versions yet.

  • I have also included the Web Lists Size GetWebSizeWithUnitMeasure().
  • I have also included the Lists Size GetListSizeInBytes().
  • Then I have also included a function to determined the best way to represent the size in Bytes or KB or MB or GB or TB DefineSizeWithUnitMeasure

 

Your calls examples

To get size of a list:

string SizeWithUnitMeasure;
double webSizeInBytes = GetWebSizeWithUnitMeasure(web, out withUnitMeasure);
//Use the SizeWithUnitMeasure to print with the measure.


To get size of a web:


string listSizeWithMeasure;
double listSizeInBytes = GetListSizeWithUnit(list, out listSizeWithMeasure);
//Use the listSizeWithMeasureto print with the measure.



Common Function




 





public static double DefineSizeWithUnitMeasure(double sizeInBytes, out string unitMeasure)
{
unitMeasure = "Bytes";
double size = sizeInBytes;

if (size > 1024)
{
size = sizeInBytes / 1024d;//KB
unitMeasure = "KB";
}
if (size > 1024)
{
size = size / 1024d;//MB
unitMeasure = "MB";
}
if (size > 1024)
{
size = size / 1024d; //GB
unitMeasure = "GB";
}

if (size > 1024)
{
size = size / 1024d; //TB
unitMeasure = "TB";
}

return size;
}

public static double GetWebSizeWithUnitMeasure(SPWeb web, out string withUnitMeasure)
{

double storageUsage = 0d;


foreach (SPList list in web.Lists)
{
storageUsage += (double) GetListSizeInBytes(list);
}



string unitMeasure = "";
double webSize = DefineSizeWithUnitMeasure(storageUsage, out unitMeasure);

withUnitMeasure = string.Format("{0} {1}", webSize.ToString("f"), unitMeasure);

return storageUsage;
}





public static double GetListSizeWithUnit(SPList list, out string withUnitMeasure )
{
double listSizeinBytes = (double) GetListSizeInBytes(list);
string unitMeasure = "";
double listSize = DefineSizeWithUnitMeasure(listSizeinBytes, out unitMeasure);

withUnitMeasure=string.Format("{0} {1}", listSize.ToString("f"), unitMeasure);

return listSizeinBytes;
}



        public static long GetListSizeInBytes(SPList list)
{
long listSize = 0;

string filter = string.Format("tp_id='{0}'", list.ID);

DataTable myDataTable = GetCachedSiteCollectionListSizes(list.ParentWeb.Site);
DataRow[] dataRows = myDataTable.Select(filter);

if (dataRows.Length > 0)
{
listSize = (long)dataRows[0]["TotalSize"];
}

return listSize;
}



private static DataTable m_SiteCollectionListSizes;
private static Guid m_SiteCollectionListSizesSiteID;

private static DataTable GetCachedSiteCollectionListSizes(SPSite site)
{
if (m_SiteCollectionListSizes == null || m_SiteCollectionListSizesSiteID != site.ID)
{
m_SiteCollectionListSizes = GetSiteCollectionListSizes(site);
m_SiteCollectionListSizesSiteID = site.ID;
}

return m_SiteCollectionListSizes;

}

private static DataTable GetSiteCollectionListSizes(SPSite site)
{

DataTable dataTable = GetDocLibSizes(site);
//Combine both list and doc lib size results
dataTable.Merge(GetListSizes(site));

return dataTable;

}

private static DataTable GetDocLibSizes(SPSite site)
{

string connectionString = site.WebApplication.ContentDatabases[site.ContentDatabase.Id].DatabaseConnectionString;


           string storedProcName = "proc_GetDocLibrarySizes";

System.Data.SqlClient.SqlConnection connection = null;
System.Data.SqlClient.SqlDataReader reader = null;
DataTable dataTable = null;

try
{
connection = new System.Data.SqlClient.SqlConnection(connectionString);
connection.Open();

System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand(storedProcName, connection);
command.CommandType = CommandType.StoredProcedure;

command.Parameters.Add(new System.Data.SqlClient.SqlParameter("@SiteId", site.ID.ToString()));

reader = command.ExecuteReader();

dataTable = new DataTable();
dataTable.Load(reader);

}
finally
{
if (reader != null)
reader.Close();
if (connection != null)
connection.Close();
}
return dataTable;
}

private static DataTable GetListSizes(SPSite site)
{

string connectionString = site.WebApplication.ContentDatabases[site.ContentDatabase.Id].DatabaseConnectionString;
string storedProcName = "proc_GetListSizes";

System.Data.SqlClient.SqlConnection connection = null;
System.Data.SqlClient.SqlDataReader reader = null;
DataTable dataTable = null;

try
{
connection = new System.Data.SqlClient.SqlConnection(connectionString);
connection.Open();

System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand(storedProcName, connection);
command.CommandType = CommandType.StoredProcedure;

command.Parameters.Add(new System.Data.SqlClient.SqlParameter("@SiteId", site.ID.ToString()));

reader = command.ExecuteReader();

dataTable = new DataTable();
dataTable.Load(reader);

}
finally
{
if (reader != null)
reader.Close();
if (connection != null)
connection.Close();
}
return dataTable;
}



 





Technorati Tags: