My Tool: Using Gulp and Powershell to Automate File Uploading and Approval for SharePoint 2010

Usges

Automate uploading all changed files to SharePoint

c:\dev\wan\MyProject\gulp default
or
c:\dev\wan\MyProject\gulp spdefault

Automate appoving all files in SharePoint

c:\dev\wan\MyProject\gulp approveall

Automate checking in all files in SharePoint

c:\dev\wan\MyProject\gulp checkinall

Automate checking out all files in SharePoint

c:\dev\wan\MyProject\gulp checkoutall

Note

  • Please ensure Powershell version must be at least 4 ! ($PSVersionTable.PSVersion)
  • To Map Network Drive, WebDAV must be installed (Server Feature/Desktop Experience/Restart Server, enable WebClient service)
  • if the mapped drive Z: is not recognized in powershell or command line, please "net use Z: http://pre.dev.com" instead
  • Please login to Z: first else copying will fail due to no access

 Content of c:/dev/wan/MyProject/gulpfile.js

// https://www.spdavid.com/use-gulp-and-npm-to-automate-2/
// For SP2013 and SP 2016: https://www.definitivelogic.com/2017/02/21/legacy-sharepoint-gulp-robocopy/
// For SP2010: https://www.definitivelogic.com/2017/02/28/legacy-sharepoint-development-2007-and-2010-gulp-and-robocopy/
// http://thomasdaly.net/2018/01/05/sharepoint-quick-deploy-build-process-using-node-gulp-sp-save-part-2/

var gulp = require('gulp')
var spsave = require('gulp-spsave')
var watch = require('gulp-watch')
var cached = require('gulp-cached');
var robocopy = require('robocopy');
var powershell = require('node-powershell');

var sourceDir = "c:/dev/wan/MyProject";
var mapDrive = "Z:";
var rootSiteUrl = "http://pre.dev.com";

var filelist = [
    {
        files: ["EServices.txt"], // source files, separated by ","
        subSiteUrl: "",           // destination subsite url ("" means root site)
        folder: "/Pages",         // destination folder within this subsite (related to subSiteUrl)
        copy: true                // true (default): copy these files; false: don't copy        
    },
    {
        files: ["EServices.js"],
        subSiteUrl: "",
        folder: "/Pages/EServices/js",
        copy: true
    },
    {
        files: ["jquery.smartWizard.fix.js"],
        subSiteUrl: "",
        folder: "/Pages/EServices/js",
        copy: true
    },    {
        files: ["jquery.smartWizard.custom.js"],
        subSiteUrl: "",
        folder: "/Pages/EServices/js",
        copy: true
    },
    {
        files: ["smart_wizard_theme_dots_custom.css"],
        subSiteUrl: "",
        folder: "/Pages/EServices/css",
        copy: true
    },
    {
        files: ["EServices.css"],
        subSiteUrl: "",
        folder: "/Pages/EServices/css",
        copy: true
    },
    {
        files: ["Licence.txt"],
        subSiteUrl: "",
        folder: "/Pages/EServices",
        copy: true
    },
    {
        files: ["Application.txt"],
        subSiteUrl: "/Applications",
        folder: "/Pages",
        copy: true
    },
    {
        files: ["Subsidy.txt", "Calculator.txt"],
        subSiteUrl: "/Services/Subsidy",
        folder: "/Pages",
        copy: true
    }
];

function copyall(filelist) {
    // files must be checked out before being overwritten
    spfileversioningall(filelist, "CheckOut");
    for (var i=0; i< filelist.length; i++) {
        if (typeof filelist[i].copy == typeof udnefined || filelist[i].copy) {
            robocopy({
                source: sourceDir,
                destination: mapDrive + filelist[i].subSiteUrl + filelist[i].folder, 
                files: filelist[i].files,
                copy: {
                    info: 'DT'
                },
                retry: {
                    count: 1,
                    wait: 3
                }
            })
        }
    }
}

function spfileversioningall(filelist, func) {
    for (var i=0; i< filelist.length; i++) {
        if (typeof filelist[i].copy == typeof udnefined || filelist[i].copy) {

            for (var j=0; j < filelist[i].files.length; j++) {
                spfileversioning(func, rootSiteUrl + filelist[i].subSiteUrl, filelist[i].subSiteUrl + filelist[i].folder + "/" + filelist[i].files[j]);
            }
        }
    }
}

function spfileversioning(func, siteUrl, fileServerRelativeUrl) {
    let ps = new powershell({
        executionPolicy: 'Bypass',
        noProfile: true
      });
    ps.addCommand('./SPFileVersioning.ps1', [
            {"function": func},
            {"siteUrl": siteUrl},
            {"fileServerRelativeUrl": fileServerRelativeUrl}
        ])
    ps.invoke()
        .then(output => {
            console.log(output);
            ps.dispose();
        })
        .catch(err => {
            console.log(err);
            ps.dispose();
        });
}

gulp.task('spdefault', function() {
    // runs the spsave gulp command on only files the have 
    // changed in the cached files
    return gulp.src(sourceDir + "/*")
        .pipe(cached('spFiles'))
        .pipe(copyall(filelist));
});


gulp.task('default', function() {
    // create an initial in-memory cache of files
    gulp.src(sourceDir + "/*")
    .pipe(cached('spFiles'));
    
    // watch the src folder for any changes of the files
    gulp.watch([sourceDir + "/*"], ['spdefault']);
});

gulp.task('approveall', function() {
    spfileversioningall(filelist, "approve");
});

gulp.task('checkinall', function() {
    spfileversioningall(filelist, "checkin");
});

gulp.task('checkoutall', function() {
    spfileversioningall(filelist, "checkout");
});

Content of c:/dev/wan/MyProject/SPFileVersioning.ps1

<#
https://bramdejager.wordpress.com/2013/08/02/using-csom-and-powershell-to-query-sharepoint-online-or-on-premise/
https://stackoverflow.com/questions/36585936/sharepoint-csom-powershell-does-not-return-web-folders-property/36671910
http://www.sharepointdiary.com/2016/10/sharepoint-online-how-to-check-in-document-using-powershell.html
https://wprogramming.wordpress.com/2011/07/18/dynamic-function-and-variable-access-in-powershell/
https://social.technet.microsoft.com/wiki/contents/articles/26436.how-to-create-and-use-enums-in-powershell.aspx
#>


param(
   [string] $function = "approve",
   [string] $siteUrl = "http://pre.dev.com",
   [string] $fileServerRelativeUrl = "/Pages/EServices/js/jquery.smartWizard.fix.js"
)

#Add-PSSnapin Microsoft.SharePoint.Powershell #-ErrorAction SilentlyContinue

#We will use CSOM to access SharePoint folders
Add-Type -Path "C:\Program Files\Common Files\microsoft shared\SharePoint Client\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\microsoft shared\SharePoint Client\Microsoft.SharePoint.Client.Runtime.dll"

Add-Type -TypeDefinition @"
   public enum SPModerationStatusType
   {
    Approved,
    Denied,
    Pending,
    Draft,
    Scheduled
   }
"@

Function CheckIn($siteUrl, $fileServerRelativeUrl)
{
    <# 
    To check in a file in root site:

    .\SPFileVersioning.ps1 -function CheckIn -siteUrl "http://pre.dev.com" -fileServerRelativeUrl /Pages/EServices/js/Test.html 

    To check in a file in a sub site:
    ./SPFileVersioning.ps1 -function CheckIn -siteUrl http://pre.dev.com/Services/Subsidy -fileServerRelativeUrl /Services/Subsidy/Calculator.txt
    #>

    $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)
 
    Try {
        $file = $ctx.Web.GetFileByServerRelativeUrl($fileServerRelativeUrl)
        $ctx.Load($file)
        $ctx.ExecuteQuery()

        If($file.CheckOutType -eq "None")
        {
            write-host "$($file.Name) is not checked out and no need to be checked in!" -f Red
        }
        else
        {
            # MajorCheckIn is equivalent to Publish("")
            $file.CheckIn("",[Microsoft.SharePoint.Client.CheckinType]::MajorCheckIn)
            $ctx.ExecuteQuery()
            write-host "$($file.Name) has been checked-in successfully!" -f Green
        }
    }
    Catch {
        write-host -f Red "Error checking-in $($file.Name)!" $_.Exception.Message
    }
}

Function CheckOut($siteUrl, $fileServerRelativeUrl)
{
    $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)
 
    Try {
        $file = $ctx.Web.GetFileByServerRelativeUrl($fileServerRelativeUrl)
        $ctx.Load($file)
        $ctx.ExecuteQuery()
        
        #check if the file is already checked out
        If($file.CheckOutType -ne "None")
        {
            write-host "$($file.Name) is already checked out!" -f Yellow
        }
        else
        {
            $file.CheckOut()
            $ctx.ExecuteQuery()
            write-host "$($file.Name) has been checked out successfully!" -f Green
        }
    }
    Catch {
        write-host -f Red "Error checking out $($file.Name)!" + $_.Exception.Message
    }   
}

Function Approve($siteUrl, $fileServerRelativeUrl)
{
    $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)
 
    Try {
        $file = $ctx.web.GetFileByServerRelativeUrl($fileServerRelativeUrl)
        $item = $file.ListItemAllFields
        $ctx.Load($file)
        $ctx.Load($item)
        $ctx.ExecuteQuery()

        # CSOM ListItem class does not have a ModerationInformation property!

     if( $item["_ModerationStatus"] -eq [SPModerationStatusType]::Approved) {
            write-host "$($file.Name) is already approved and no need to be re-approved" -f Green
        }
     else
        {
            if( $item["_ModerationStatus"] -eq [SPModerationStatusType]::Draft) {
                CheckIn -siteUrl $siteUrl  -fileServerRelativeUrl $fileServerRelativeUrl
            }

            # must re-read $item to prevent error:"Version conflict."
            $ctx.Load($item)
            $ctx.ExecuteQuery()

            $item["_ModerationStatus"] =  [Convert]::ToInt32([SPModerationStatusType]::Approved);
            $item.Update();
            $ctx.ExecuteQuery()
            write-host "$($file.Name) has been approved successfully!" -f Green
        }
    }
    Catch {
        write-host -f Red "Error approving $($file.Name)!" + $_.Exception.Message
    }   
}


& $function -siteUrl $siteUrl -fileServerRelativeUrl $fileServerRelativeUrl

Comments

Popular posts from this blog

Use GnuPG Tools or C# Code for PGP Encryption and Signature

Errors in Net Core Add-Migration

Set up Visual Studio 2017 for .Net Core Application Development