Wednesday, 14 January 2015

Powershell: Recusively Check In and Publish files in a SharePoint Document Library

I have combined the scripts from Paul King (here) and Brijendra Gautam (here) to create my own version of the script.

Function CheckInAndPublishFolderItemsRecusively( [Microsoft.SharePoint.SPFolder] $folder )
{
    # Create query object
    $query = New-Object Microsoft.SharePoint.SPQuery
    $query.Folder = $folder

    # Get SPWeb object
    $web = $folder.ParentWeb

    # Get SPList
    $list = $web.Lists[$folder.ParentListId]

    # Get a collection of items in the specified $folder
    $itemCollection = $list.GetItems($query)

    # Iterate through each item in the $folder
    foreach ($item in $itemCollection)
    {
        # If the item is a folder
        if ($item.Folder -ne $null)
        {
            # Call the Get-Items function recursively for the found sub-solder
            CheckInAndPublishFolderItemsRecusively $item.Folder
        }
        else
        {
if ($item.File.Level -ne "Published")
{
           if ($item.File.CheckOutType -ne "None")
           {
               if ($item.File.Versions.Count -eq 0)
               {
                   # Check in the file
                    $item.File.CheckIn("Checked in By Administrator", [Microsoft.SharePoint.SPCheckinType]::MajorCheckIn)
$item.File.Publish('Published using Powershell')
}
                }
}
        }
    }

    $web.dispose()

    $web = $null
}

The script is invoked as follows:
$web = Get-SPWeb $siteUrl
$folder = $web.GetFolder("Style Library")
CheckInAndPublishFolderItemsRecusively $folder

Tuesday, 13 January 2015

SharePoint Online: How do I extract list changes with PowerShell?

Import-Module 'C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell' -DisableNameChecking

$loadInfo1 = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
$loadInfo2 = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")

$webUrl = "https://mysite.sharepoint.com/"
$username = "richard.leeman@mydomain.onmicrosoft.com"
$password = Read-Host -Prompt "Password for $username" -AsSecureString

$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($webUrl)
$ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $password)
$web = $ctx.Web
$webs = $web.Webs
$lists = $web.Lists

$ctx.Load($lists)
$ctx.Load($webs)
$ctx.Load($web)
$ctx.ExecuteQuery()

$list = $lists| Where-Object { $_.Title -eq "My Documents"}

$dt = Get-Date
$dtUTC = $dt.ToUniversalTime()
$changeQuery = New-Object Microsoft.SharePoint.Client.ChangeQuery($true, $true)
$changeQuery.ChangeTokenStart = New-Object Microsoft.SharePoint.Client.ChangeToken
$changeQuery.ChangeTokenStart.StringValue = "1;3;" + $list.Id.ToString() + ";" + $dtUTC.AddHours(-10).Ticks.ToString() + ";-1"
$changeQuery.ChangeTokenEnd = New-Object Microsoft.SharePoint.Client.ChangeToken
$changeQuery.ChangeTokenEnd.StringValue = "1;3;" + $list.Id.ToString() + ";" + $dtUTC.Ticks.ToString() + ";-1"

$changes = $list.GetChanges($changeQuery)
$ctx.Load($changes)
$ctx.ExecuteQuery()

$addedCount = 0
$updatedCount = 0
$deleteCount = 0

if ($changes.AreItemsAvailable -eq $true)
{
$fileName = "sampleData.txt"

$changes > $fileName


$changes | ForEach-Object {
if (($_.ItemId -ne "") -and ($_.ItemId -ne $null))
{
if ($_.ChangeType -eq "Add") { $addedCount = $addedCount + 1 }
if ($_.ChangeType -eq "Update") { $updatedCount = $updatedCount + 1 }
if ($_.ChangeType -eq "DeleteObject") { $deleteCount = $deleteCount + 1 }
}
}

Start-Process notepad  -ArgumentList $fileName
}

Write-Host "$addedCount items added"
Write-Host "$updatedCount items updated"
Write-Host "$deleteCount items deleted"

SharePoint Online: How do I configure the Site Collection Audit Settings from CSOM?

My current project requires that the Site Collection Audit Settings be provisioned with every new OneDrive for Business site. The problem is that there is not existing API (in CSOM or PowerShell) to set these values. Problem? Yes. Insurmountable? No.

I was pointed in the direction of this fantastic blog. The basic idea behind the code is to replicate the 'POST' event that is created by using the UI. The code is magnificent but (as is continually stressed in the video) is not supported by Microsoft. This does not mean that is will not work, it just means that should the name of the underlying fields change, the code will fail unceremoniously.

Anyway, back to the solution. 
Firstly, enable the 'Reporting' Site Collection feature. The feature Guid is "7094bd89-2cfe-490a-8c7e-fbace37b4a34" and should be activated as a FARM level feature

Secondly, here is my add-in code to the set values.

using System.Globalization;

namespace Contoso.Remote.Core.HttpCommands
{
    public class RequestAuditSettings : RemoteOperation
    { 
                #region CONSTRUCTORS

      public RequestAuditSettings(string TargetUrl, AuthenticationType authType, string User, string Password, string Domain = "")
            : base(TargetUrl, authType, User, Password, Domain)
        {
            TrimLogs = false;
            RetentionInDays = 0;
            EditingItems = false;
            CheckinAndCheckout = false;
            MovingAndCopying = false;
            DeletingAndRestoring = false;
            EditContentTypesAndColumns = false;
            SearchSiteContent = false;
            EditUsersAndPermissions = false;
            StorageDocumentLibraryName = "";
        }


        #endregion

        #region PROPERTIES

        public override string OperationPageUrl
        {
            get
            {
                return "/_layouts/15/auditsettings.aspx";
            }
        }

        public bool TrimLogs { get; set; }
        public int RetentionInDays { get; set; }
        public bool EditingItems { get; set; }
        public bool CheckinAndCheckout { get; set; }
        public bool MovingAndCopying { get; set; }
        public bool DeletingAndRestoring { get; set; }
        public bool EditContentTypesAndColumns { get; set; }
        public bool SearchSiteContent { get; set; }
        public bool EditUsersAndPermissions { get; set; }
        public string StorageDocumentLibraryName { get; set; }

        #endregion

        #region METHODS

        public override void SetPostVariables()
        {
            // Set operation specific parameters
            PostParameters.Add("__EVENTTARGET", "ctl00$PlaceHolderMain$BtnOk");
            PostParameters.Add("ctl00$PlaceHolderMain$ctl00$IfcTrimAuditLog$trimAuditLog",
                TrimLogs ? "RadTrimAuditLogYes" : "RadTrimAuditLogNo");

            if (RetentionInDays >= 0)
            {
                PostParameters.Add("ctl00$PlaceHolderMain$ctl00$IfcTrimRetention$TxtTrimRetention",
                    RetentionInDays.ToString(CultureInfo.InvariantCulture));
            }

            PostParameters.Add("ctl00$PlaceHolderMain$ctl00$ctl04$TxtReportStorageLocation", StorageDocumentLibraryName);
            //PostParameters.Add("ctl00$PlaceHolderMain$ctl00$ctl04$TxtReportStorageLocation", "");
            if (EditingItems) { PostParameters.Add("ctl00$PlaceHolderMain$ctl01$ctl01$CheckBoxAuditEdit", "on"); }
            if (CheckinAndCheckout) { PostParameters.Add("ctl00$PlaceHolderMain$ctl01$ctl01$CheckBoxAuditCheckInOut", "on"); }
            if (MovingAndCopying) { PostParameters.Add("ctl00$PlaceHolderMain$ctl01$ctl01$CheckBoxAuditMoveCopy", "on"); }
            if (DeletingAndRestoring) {PostParameters.Add("ctl00$PlaceHolderMain$ctl01$ctl01$CheckBoxAuditDeleteRestore", "on");}
            if (EditContentTypesAndColumns) { PostParameters.Add("ctl00$PlaceHolderMain$ctl02$ctl01$CheckBoxAuditColumnsContentType", "on");}
            if (SearchSiteContent) { PostParameters.Add("ctl00$PlaceHolderMain$ctl02$ctl01$CheckBoxAuditSearch","on"); }
            if (EditUsersAndPermissions) { PostParameters.Add("ctl00$PlaceHolderMain$ctl02$ctl01$CheckBoxAuditPerms", "on"); }
        }

        #endregion

    }
}

and here is the invocation:

 private bool SetSiteAuditing(string oneDriveUrl, string documentLibraryAsRelativeUrl, string adminUserName, string adminPassword)
        {
            try
            {
                // Enable the audit settings
                var auditing = new RequestAuditSettings(oneDriveUrl, AuthenticationType.Office365, adminUserName, adminPassword)
                {
                    TrimLogs = true,
                    RetentionInDays = 30,
                    CheckinAndCheckout = true,
                    MovingAndCopying = true,
                    DeletingAndRestoring = true,
                    EditUsersAndPermissions = true,
                    StorageDocumentLibraryName =  documentLibraryAsRelativeUrl
                };

                auditing.Execute();
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }