Custom list view by using the “JS Link” property.

Customizing a list view could be done by using XSLT in previous version of SharePoint. In SharePoint 2013 this van be done by using the “JS Link” functionality. The “JS Link” functionality is included in list view web parts of SharePoint.

You can use this property for adding specific JavaScript files. You can add 1 or multiple when you want to add multiple you will have to separate them using the “|”  symbol. On the internet you can find a lot of articles on how to use the “JS Link” functionality. Normally the “JS Links”  functionality is used to adjust the rendering of a specific field type in my scenario I had to change the complete list view.

In this article I will show you how to define a custom list view using the “JS Link” functionality according to a specific scenario: On project sites we want to follow the status of site migrations (for example SharePoint sites). The status of these migrations can be saved within SharePoint lists:

clip_image002

In this list you will need to have two fields:

  • Title: The title of the site that needs to be migrated.
  • Status: The status of the migration. In this example we will have three statuses: “Not started”, “In progress” en “Done”.

Using this list we can manage the status of the migrations. At this moment we only have to add functionality to see the migration status of all sites in one view without the use of a farm solution.

clip_image004

In the above image you can see the same list but a altered view with the use of “JS LInk”. This view shows a doughnut view with three different colours. So how can we achieve this without creating a full trust solution.

First off all we searched for a JavaScript library to be able to create a doughnut chart. We choose the following library:

Now that we have a library we can start writing a JavaScript file that we need to load within the “JS Link” property. Within the JavaScript file we first off all register a namespace to not get in the way of other JavaScript code besides that we also define a view variables that we can  use within the namespace.

var migrationStatus = migrationStatus || {};

migrationStatus.InProgressString = 'In progress';
migrationStatus.DoneString = 'Done';
migrationStatus.NotStartedString = 'Not started'; 
migrationStatus.ListName = 'Migration';
migrationStatus.ItemsDone = 0;
migrationStatus.ItemsInProgress = 0;
migrationStatus.ItemsNotStarted = 0;

To be able to define template you will need to define specific overrides. These overrides can be defined by using the “RegisterTemplateOverrides” method on the “SPClientTemplates.TemplateManager” object. This method needs one object that defines what needs to be overridden. We will define this within the method: “CustomizeViewRendering”.

// Create a function for customizing the view rendering of the list
migrationStatus.CustomizeViewRendering = function () {
    var migrationStatusContext = {};
    migrationStatusContext.Templates = {};
    migrationStatusContext.Templates.View = migrationStatus.RenderMigrationViewBodyTemplate;
    migrationStatusContext.OnPostRender = migrationStatus.OnMigrationViewPostRender;
    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(migrationStatusContext);
};

In the “migrationStatusContext” object needs to be defined which rendering methods needs to be overridden. As you can see in the above snippet the “OnPostRender” and the “Templates.View” will be overridden. These methods we will discuss in the upcoming paragraphs.

Note:When you override the rendering methods, this will be for every list view on a page. If you want to override the view rendering of one specific list you will have to build in a check if you are performing the action on the correct list.

migrationStatus.RenderMigrationViewBodyTemplate = function (ctx) {
    if (ctx.Templates.Body == '') {
        return RenderViewTemplate(ctx);
    }

    if (ctx.ListTitle == migrationStatus.ListName) {
        var listData = ctx.ListData;

        for (var idx in listData.Row) {
            var listItem = listData.Row[idx];

            if (listItem.Status == migrationStatus.InProgressString) {
                migrationStatus.ItemsInProgress++;
            } else if (listItem.Status == migrationStatus.NotStartedString) {
                migrationStatus.ItemsNotStarted++;
            } else if (listItem.Status == migrationStatus.DoneString) {
                migrationStatus.ItemsDone++;
            }
        }

        var htmlOutput = [];
        htmlOutput.push('<div id="listData"><a href="' + ctx.listUrlDir + '"> View this list</a></div>');
        htmlOutput.push('<div id="migrationstatusView" style="height:400px;width:400px;margin-top:10px;float:left;margin-bottom:10px;display:block;">');
        htmlOutput.push('<canvas id="myChart" width="250" height="250"></canvas>');
        htmlOutput.push('<div id="chartLegend" style="width:150px;float:right;" />');
        htmlOutput.push('</div>');
        

        var retVal = htmlOutput.join('');
        return retVal;
    }
    else {
        return RenderViewTemplate(ctx);
    }
}

The “migrationStatus.RenderMigrationViewBodyTemplate” function needs to be used to adjust the view. This method also receives the context (ctx) object in which all information regarding the context (in this example list) is saved. From this context we can retrieve all of the list data.

But first we will have to check if the body of the template isn’t empty. When it is empty we will have to call the default renderer. After that we can check if the list we are performing the actions for is our migration list: “ctx.ListTitle == migrationStatus.ListName” if this isn’t the case we also call the default rendering function:

return RenderViewTemplate(ctx);

When this list is our list we count all of the statuses and place them in the specific variables. When all this is done we write out the HTML we need for the rendering of the graph and legend and we return this.

De “PostRender” function will be called as last. In this function the graph will be loaded with the correct data and besides that we will also load the legend of the graph.

migrationStatus.OnMigrationViewPostRender = function (ctx) {
    if (ctx.ListTitle == migrationStatus.ListName) {

        var pieData = [
        {
            value: migrationStatus.ItemsNotStarted,
            color: "#F7464A",
            highlight: "#FF5A5E",
            label: migrationStatus.NotStartedString
        },
        {
            value: migrationStatus.ItemsDone,
            color: "#46BFBD",
            highlight: "#5AD3D1",
            label: migrationStatus.DoneString
        },
        {
            value: migrationStatus.ItemsInProgress,
            color: "#FDB45C",
            highlight: "#FFC870",
            label: migrationStatus.InProgressString
        }];
        
        var options = {
            //Boolean - Whether we animate the rotation of the Doughnut
            animateRotate: true,
            //String - A legend template
            legendTemplate: '<ul style="list-style-type:none;" class="<%=name.toLowerCase()%>-legend"><% for (var i=0; i<segments.length; i++){%><li><span style="width:20px;height:10px;display:block;background-color:<%=segments[i].fillColor%>"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>'
        };

        //get the chart element and setup the doughnut chart
        var ctxChart = document.getElementById("myChart").getContext("2d");
        var myPie = new Chart(ctxChart).Doughnut(pieData, options);

        //generate the legend html
        var legend = document.getElementById("chartLegend");
        legend.innerHTML = myPie.generateLegend();
    }
};

In the “PostRender” function we also need to check if we are performing the action for the correct list.

The final thing we need to adjust to the JavaScript file is adding the following line:

migrationStatus.CustomizeViewRendering();

This line of code will be called on the moment the file is loaded and by calling this function is will make sure that the overrides for the list views will be registered.

Know that the files are ready we only need to specify the files in the “JS Link” property of the list view.

clip_image006

In the “JS Link” we have specified the following value:

/siteassets/Migration/Chart.js|/siteassets/Migration/MigrationStatus.js
  • /siteassets/Migration/Chart.js: JavaScript library for the generation of graphs.
  • /siteassets/Migration/MigrationStatus.js: JavaScript file for the list view.
Share Button

User automatically removed from SharePoint Group

During my current project we received an access request from a user. We did what we normally do, we checked the request and added the user in the appropriate SharePoint Group and notified the user.

After 15 minutes the user told us that it wasn’t working. We looked at the User Group and found out that the user was not in the group. So we added the user again because we thought that we forgot this step. After some time we received another mail from the user telling us that it still wasn’t working.

We requested the account details from the user and tried it our self, we found out that the user was missing again and added the account again. We logged in with the user opened a document with Office Web Applications and took another look at the members of the specific SharePoint group. WTF the user was gone!

After digging into the ULS files and looking in the Event log we did not found a clue of what was happening. After a small conversation with the user she told us that everything worked fine before and that she had several problems since see re-joined the company.

What happened was the following the user was removed from the active directory and when she got back a new account was created with the same login name.

When the user re-joined the account had the same login name but did not have the same SID. SharePoint saves information about an account by using the login name and the SID (Security Identifier) of a user. Because of the mismatch SharePoint was removing the applied security rights.

This issue can be fixed by using different commands:

The commands look as followed:

$user = Get-SPUser -Web [SiteUrl] -Identity [Login Name]
Move-SPUser -Identity $user -NewAlias [Login Name] -IgnoreSID

For the PowerShell commando you will first need to retrieve the specific user.When retrieving the user and setting the new alias you should always use the claim login name: “i:0#.w|Domain\user”.

stsadm -o migrateuser -oldlogin [Login Name] -newlogin [Login Name] -ignoresidhistory

The STSADM command looks almost the same as the PowerShell command if you also look at the parameters they both have something to Ignore the SID history. The parameter is included because if you do not include the parameter it will check the SID references and as we all know they do not match.

Possible Problem: When performing one of these options you can receive a “Object reference not set to an instance of an object”. The solution to this problem is pretty simple, When it happens your user does not have enough rights. Try it with another account or give your current user rights to the User Profile Service Application.”

Share Button

Warm Up Form Based Web Application

Normal warm up scripts for SharePoint open a site object or web object and make a request to that specific site. When you use form based authentication and do not allow anonymous access, you would only warm up the login page.

In order to warm up a application form based SharePoint site you would have to apply your credentials. All off this can be done by using PowerShell and specific COM objects. This script can then be scheduled with “Task Scheduler” that is included in windows.

function Warm-FBASite{
    [CmdletBinding()]
    param (
        [string] $RootUrl = $(Read-Host -prompt "Root Url"),
        [string] $LoginUrl = $(Read-Host -prompt "Login Url"),
        [string] $SiteUrl = $(Read-Host -prompt "Site Url"),
        [string] $UserName = $(Read-Host -prompt "Username"),
        [string] $Password = $(Read-Host -prompt "Password"),
        [bool] $loggedIn = $(Read-Host -prompt "Already logged in")
    )
    $ieMain=New-Object -ComObject "InternetExplorer.Application";
    if($loggedIn -ne $true){
        Login-FBA -RootUrl $RootUrl -LoginUrl $LoginUrl -UserName $UserName -Password $Password -Browser $ieMain;
    }
    Navigate-Site -RootUrl $RootUrl -SiteUrl $SiteUrl;
    $ieMain.Quit();

}

The Warm-FBASite function is the main function that calls other functions. This function needs the following parameters:

  • RootUrl: The root URL of the application you would like to warm up.
  • LoginUrl: The relative URL to the login page.
  • SiteUrl: The relative URL to the site you would like to warm up.
  • UserName: The username off the forms user.
  • Password: The password off the forms user.
  • LogginIn: Boolean value whether the user has already been logged in.

In this function we open a Com object of internet explorer and provide it as a parameter to the function “Login-FBA”.

function Login-FBA{
    [CmdletBinding()]
    param (
        [string] $RootUrl = $(Read-Host -prompt "Root Url"),
        [string] $LoginUrl = $(Read-Host -prompt "Login Url"),
        [string] $UserName = $(Read-Host -prompt "Username"),
        [string] $Password = $(Read-Host -prompt "Password"),
        $Browser = $(Read-Host -prompt "Browser")
    )

    $url = $RootUrl + $LoginUrl;

    Write-Host "Trying to login the user:" $UserName " on the Url: " $url -ForegroundColor Green
    $Browser.navigate($url);
    $Browser.visible=$false;

    if ((Wait-Browser $Browser -Url $url ) -eq $false){
        Write-Host "Something went wrong with requesting the page";
        return
    }else{
        $doc=$Browser.Document
    }

    $txtUsername=$doc.getElementByID("ctl00_PlaceHolderMain_signInControl_UserName"); #Replace by your own ID
    $txtPassword=$doc.getElementByID("ctl00_PlaceHolderMain_signInControl_password"); #Replace by your own ID
    $btnSubmit=$doc.getElementByID("ctl00_PlaceHolderMain_signInControl_login"); #Replace by your own ID
    
    $txtUsername.value=$UserName;
    $txtPassword.value=$Password;
    $btnSubmit.click();
    if ((Wait-Browser $Browser -Url $url ) -eq $false){
        return;
    }
}

This function tries to open the login page in the com object you send in the parameters. When this page is loaded it inserts the username and password in the login controls by finding these controls by there id.

In this function you will have to insert your own Id’s of the username textbox, password textbox and submit button. In this function you also see a reference to the “Wait-Browser” function. This function is created to make sure the site you are requesting in the COM object is fully loaded.

function Wait-Browser{
    [CmdletBinding()]
    param (
            $Browser = $(Read-Host -prompt "Browser"),
            $Url = $(Read-Host -prompt "Url")
    )

    $maxRetries=50;
    $retry = 2;
    $retryCount = 0;

    while ($Browser.Busy -eq $true){  
        Write-Host "Waiting for browser: " $URl  -ForeGroundColor Yellow;
        
        if ($retryCount -gt $maxRetries){
            return $false;
        }
        
        $retryCount++;
        start-sleep $retry ;
    } 

    return $true;
}

When the login request succeeds it is time to open the site and let that document load within the COM object. When the site is loaded it will write the page title to the PowerShell window.

function Navigate-Site{
    [CmdletBinding()]
    param (
        [string] $RootUrl = $(Read-Host -prompt "Root Url"),
        [string] $SiteUrl = $(Read-Host -prompt "Site Url")
    )

    $url = $RootUrl + $SiteUrl;

    $iePage=New-Object -ComObject "InternetExplorer.Application";
    $iePage.navigate($url);
    $iePage.visible=$false;
    $retryCounter=0
    
     if ((Wait-Browser $iePage -Url $url ) -eq $false){
        Write-Host "Something went wrong with requesting the page" -ForegroundColor Red;
        return
    }else{
        $doc=$iePage.Document
        Write-Host "Site Loaded: " $doc.title -ForegroundColor Green;
    }

    $iePage.Quit();    
}

To make use of these functions you only have to add these functions in PowerShell and call the “Warm-FBASite” function just like below.

Warm-FBASite -RootUrl "https://sharepointsite.com"  -LoginUrl "/_layouts/15/login.aspx" -SiteUrl "/client/35" -UserName "[form account name]" -Password "[password]" -LoggedIn $false;
Share Button

Limitations of a Public Facing Website in Office 365

When you buy an Office 365 tenant you get the option to build a Public website within the SharePoint administration panel.

Creating a website will give you the following URL by default:

  • http://[tenant]-public.sharepoint.com.

Within SharePoint 2013 they have extended the options to build massive websites where you had to do a lot of customizations in SharePoint 2010. There are already many site available for example (http://www.motion10.nl ) but most off these websites are build on SharePoint 2013 on premise. This is all because building a public website within Office 365 has a lot of limitations. In this blog post I will try to list the limitations I know. If you find any other limitations or they have changed something on the Office 365 platform to make it work and I haven’t alter the blog post please add a comment or send me a email.

Site Settings

Multiple site settings screens are missing from the administration menu. Some of these pages can be accessed by the Url and some of them don’t even exist.

Site Setting Public Website Office 365

  1. Manage Site Collection Features: The link and the page are missing for a public website.
  2. Manage Site Features: The link and the page are missing for a public website.
  3. Site Content Types: The link and the page are missing for a public website.
  4. Site Columns: The links is missing for a public website you can access the page by using the following URL: [URL]/_layouts/15/mngfield.aspx.
  5. Solution Gallery: The links is missing for a public website you can access the page by using the following URL: [URL]/_catalogs/solutions/Forms/AllItems.aspx.
  6. Web Part Gallery: The links is missing for a public website you can access the page by using the following URL: [URL]/_catalogs/wp/Forms/AllItems.aspx.
  7. List Template Gallery: The links is missing for a public website you can access the page by using the following URL: [URL]/_catalogs/lt/Forms/AllItems.aspx.
  8. Term store management: The link and the page are missing for a public website.

If you would like to activate or see which features are activated on the SharePoint Public site take a look at this blog post:

Variations

Many website on the internet are created in multiple languages. Looking at SharePoint you would then maybe would like to use Variations. In Office 365 variations cannot be used.

Managed Metadata

As you can see on point 8 within the site settings section you are not able to access the Term Store Management. This also makes it that one off the amazing new features off SharePoint 2013 taxonomy-based navigation cannot be used on a Office 365 public website.

Besides that more taxonomy based features do not work as for example the product catalog functionality.

Sub Sites

From the UI you cannot create a sub site.  You will have to use SharePoint Designer in order to create a sub site. But even then you are not able to use every site template. In my situation I tried to create a Publishing Site and it got immediately deleted from my tenant.

The only templates that can be used on the online version are:

  • Team Site
  • Basic Search Site
  •  

    Content Search Web Part

    One of the web parts you had to develop for previous version of SharePoint (At least I did) now came to SharePoint 2013 and that is the “Content Search Web Part”. This web part is not available on the public web site of Office 365.

    Online you can find great tips to deal with the limitations of the Public website:

    Share Button

    Data Deduplication in Windows Server 2012 R2

    After reading a lot of information about Windows Server 2012 R2 I found out that it has a really nice feature if you are into virtualization as much as me. When using virtualization you may have a lot of virtual disks that contain a lot of the same data. For example you have four machines that have an installation of Windows Server 2012.

    All together this will take up a lot off disk space. I this situation you could also create a base image and use this base image for every virtual machine. But you will then still have the updates and you will have to maintain the base image.

    In Windows Server 2012 you already had the feature off Data Deduplication for File and Storage Servers. What it does is the following: It finds and removes duplication within data on a volume while ensuring that the data remains correct and complete. This makes it possible to store more file data in less space on the volume.

    A nice image created by Guido van Brakel in the following blog post: Data Deduplication in Windows Server 2012 really explains it:

    Data Deduplication

    Data Deduplication

    In Windows Server 2012 R2 they have added a new functionality called:

    • Data deduplication for remote storage of Virtual Desktop Infrastructure (VDI) workloads

    This means you can use the Data Deduplication for your virtual machines when you are running Windows Server 2012 R2 meaning that when you have four virtual machines running the same operating system you will not have to take up all that amount off disk space.

    Take a look at the following articles if you want to get started with Data Deduplication:

    Share Button

    My daughter Fiene Vlinder van der Gaag

    The last couple of days I haven’t been blogging as much as I would like. I also think in the upcoming month I will not have much time to blog.

    This is all because my amazing girlfriend gave birth to a really beautifull daughter on 24-02-2014, Fiene Vlinder. As soon as I have some more time I will pick up blogging again and will be posting some interesting Microsoft stuff.

    Fiene Vlinder

    Fiene Vlinder

    Share Button

    Unable to start User Profile Synchronization Service

    A few days ago I wrote a post about loading modules in PowerShell by default: “Load modules by default when opening PowerShell”. Today we had to setup a new SharePoint Farm and to perform some default operations we created the PowerShell profile described in that post.

    The complete configuration of the server went well until we wanted to start the “User Profile Synchronization Service”. The Service moves to starting and the timer job that configures the service also starts but after 5 seconds it already stops.

    Looking at the ULS log we found this error message:

    “UserProfileApplication.SynchronizeMIIS: Failed to configure MIIS post database, will attempt during next rerun. Exception: System.Runtime.InteropServices.SEHException (0×80004005): External component has thrown an exception.
    at Microsoft.Office.Server.UserProfiles.Synchronization.ILMPostSetupConfiguration.ConfigureMiisStage2()
    at Microsoft.Office.Server.Administration.UserProfileApplication.SetupSynchronizationService(ProfileSynchronizationServiceInstance profileSyncInstance).”

    This message did not gave us a clue at all. At the same time we were working on the PowerShell Profiles and had temporary removed our custom profile from the service.

    Provisioning the “User Profile Synchronization Service” without the custom profile resulted in success. So remember when you want to provision the “User Profile Synchronization Service” make sure you are not using a custom PowerShell profile.

    Share Button

    Load modules by default when opening PowerShell

    At the moment we are working a lot with PowerShell in combination with SharePoint. Over time we created a lot of usable functions that were not availible within the default SharePoint Module.

    With these functions we created our own PowerShell Module in order to easily load the function within our PowerShell instance. When you would like to know more on how to create a PowerShell Module you can read this TechNet article:

    When you have written a module it is relatively easy to load it in the PowerShell instance by using the import-module comment.

    import-module [Path To Your Module]\Module.psm1 -WarningAction Ignore

    As you can see in the above script we have added the “–WarningAction ignore” because loading a module mostly generate a warning for unapproved Verbs:

    “WARNING: The names of some imported commands from the module ‘Module’ include unapproved verbs that might make them less discoverable. To find the commands with unapproved verbs, run the Import-Module command again with the Verbose parameter. For a list of approved verbs, type Get-Verb.”

    If you want to stick with the approved Verbs you can take a look at the approved verb list on TechNet:

    Now the question still is how do we load it in every PowerShell instance by default. You can accomplish this by creating a “profile.ps1” file. This file needs to be added in the following directory:

    C:\Windows\System32\WindowsPowerShell\v1.0\

    In the directory “C:\Windows\System32\WindowsPowerShell\v1.0\Example” there is a example profile.ps1 file. This file is empty. For our module we had created the following profile.ps1 file:

    #  Copyright (c) Microsoft Corporation.  All rights reserved.
    #  
    # THIS SAMPLE CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
    # WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
    # WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
    # IF THIS CODE AND INFORMATION IS MODIFIED, THE ENTIRE RISK OF USE OR RESULTS IN
    # CONNECTION WITH THE USE OF THIS CODE AND INFORMATION REMAINS WITH THE USER.
    
    if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) {
        Write-Host "Loading Microsoft SharePoint PowerShell" -ForegroundColor Green
        Add-PSSnapin "Microsoft.SharePoint.PowerShell"
    }
    
    Write-Host "Loading Custom SharePoint PowerShell Module" -ForeGroundColor Green
    import-module D:\SCRIPTMODULE\Module.psm1 -WarningAction Ignore

    Using the profile.ps1 file make sure that the profile is loaden in every powershell window. You could also use this filename: “Microsoft.PowerShellISE_profile.ps1″ to only load within the PowerShell ISE window.

    Important: When using custom profiles on your SharePoint servers in combination the the Userprofile Synchronization Service and use the profile.ps1 approach you could be getting in a situation that your Forefront Identity Manager Service and ForeFron Identity Manager Synchronization Service do not start any more after a reboot. This is because of the profile.ps1 file. Remove the profile and it will start again.

    Share Button

    Filtering SharePoint Search Results by Content Source

    Working for one my projects we were looking for a solution to only retrieve search results for a specific Content Source. In our scenario we had a External Content Type that was linked to a “FilteredEMail” view of a CRM database.

    We connected CRM to SharePoint in this scenario because searching in CRM for a specific email was a big problem and slowed down the complete CRM system.

    What there was implemented on CRM was a small button to open a window  / frame to a SharePoint Search page. On this SharePoint search page you could search for the specific Mail and then the result would link you back to CRM.

    Creating the External Content Type wasn’t that hard. The main thing here If you would also like to search trough your external content type is to define a field as a Timestamp field in order to enable incremental search.

    Timestamp field

    For us this was very important because we were dealing with more than a million items. After we had the external content type in place and created the profile pages we crawled the complete set of emails.

    We than needed to create a result source in order to retrieve only the information from the external content type. On the Search Service Application page we clicked on the ‘Result Sources’ menu option and created a new Search Result Source.

    New Result Source

    After setting these properties we opened the Query Builder. Within the query builder you do not have a default option of some sort to start filtering the results by a specific content source.

    When I took a closer look at the property filter drop down after selecting “— Show All Managed properties —“ you have a property called “ContentSource”. We selected this and filled in the value of our content source.

    Query Builder

    After that we used the default options for the rest of the properties and saved the result source. Following this hunch resulted in a result source that only returns items of a specific content source.

    New Result Source 2

    After that we created a “Display Template” to refer back to CRM if you are curious on how we accomplished that please let me know.

    Share Button

    PowerShell Scripts

    During my current project we are using a lot of PowerShell scripts to administrate the environment and make development easier for us.

    In this post that I will try to update frequently, I will share some of our useful PowerShell scripts.

    Publish files within a site collection

    In some situations you would like to have the ability to publish all files within a specific site collection. For example right before going live with a SharePoint publishing portal you need to be sure that every file is published or else end users could  get a access denied on certain resources.

    ###
    # Function for Publishing files in SharePoint
    ###
    function Publish-FilesInList(){
        param
        (
            [string] $siteUrl = $(Read-Host -prompt "Site Url"),
            [bool] $checkIn = $(Read-Host -prompt "CheckIn when file is CheckedOut"),
            [bool] $debug = $(Read-Host -prompt "Run Debug mode")
        ) 
    
        Start-SPAssignment -Global
        $site = Get-SPSite $siteUrl
        Write-Host "- Publishing files for the Site Collection:" $site.Url -ForegroundColor Cyan
    
        foreach($web in $site.AllWebs){
        
            Write-Host " - Publishing files for the Web:" $web.Url -ForegroundColor Cyan
            
            foreach($list in $web.Lists){
                if(!$list.Hidden -And !$list.IsPrivate -AND !$list.IsApplicationList -AND $list.BaseType -eq 1){
                    Write-Host "  - Publishing files for the List:" $list.Title -ForegroundColor Cyan
                    ProcessFolderGetFiles $list.RootFolder $debug
                }else{
                    Write-Host "List is Private, ApplicationList, Hidden or no document library: " $list.Title -ForegroundColor Magenta
                }
            }
        }
    
        Stop-SPAssignment -Global
    }
    
    function ProcessFolderGetFiles(){
        param(
            $folder,
            $debug
        )
        if($debug){Write-Host "    - Processing Folder:" $folder.Name -ForegroundColor Cyan}
    
        foreach($file in $list.RootFolder.Files){
            if($debug){Write-Host "     - Publishing file:" $file.Name -ForegroundColor Cyan}
            if ($file.Level -ne "Published"){
                if (($file.Level -eq "Checkout" -or $file.CheckOutStatus -ne "None")-and $checkIn){
                    Write-Host "      - File is checkedout" -ForegroundColor Yellow
                    if($checkIn){
                        Write-Host "     - CheckingIn File" -ForegroundColor Yellow
                        $file.CheckIn("CheckingIn", [Microsoft.SharePoint.SPCheckinType]::MajorCheckIn);
                        $file.Publish('Published using Motion10 Powershell Module');
                        Write-Host "      - File Published" -ForegroundColor Yellow
                    }
                }else{
                    $file.Publish('Published using Motion10 Powershell Module');
                    Write-Host "      - File Published" -ForegroundColor Yellow
                }
    
                $file.Update();
            }else{
                if($debug){Write-Host "      - File already published" -ForegroundColor Cyan}
            }
        }
        # Use recursion to loop through all subfolders.
        foreach ($subFolder in $folder.SubFolders){
           ProcessFolderGetFiles($Subfolder)
        }
    }
    

    Unregister a Custom Claim Provider for a specific zone in a web application

    There are situations were for you will develop custom claim providers. These claim providers can be installed globally on the farm but can also be installed for a specific zone in a web application. The following script will allow you to unregister a custom claim provider for a zone.

    function Unregister-CustomClaimProvider{
    	param(
    		  [string] $DisplayName = $(Read-Host -prompt "Claim Provider Display Name"),
    		  [string] $Url = $(Read-Host -prompt "Web Application Identity"),
    		  [string] $InternalName = $(Read-Host -prompt "Claim Provider Internal Name"),
    		  [string] $Zone = $(Read-Host -prompt "Zone")
    	)
    
    	
    	$WebApplication = Get-SPWebApplication $Url 
    	if ($WebApplication.IisSettings.ContainsKey($Zone)){
    		$settings = $WebApplication.GetIisSettingsWithFallback($zone)
    		$providers = $settings.ClaimsProviders;
    		if($providers.Contains($InternalName)) {
    			Write-Host "Claim Provider is registerd in this zone trying to remove..." -ForegroundColor Green
    			$providers.Remove($InternalName);
    			Set-SPWebApplication -Identity $WebApplication -Zone $Zone -AdditionalClaimProvider $providers
    		}else{
    			Write-Host "$DisplayName is not registered on $($WebApplication.Url) in zone $Zone" -ForegroundColor Yellow
    		}
    	}
    }
    

    Disable Browser Based Editing for Sites under a Specific Url

    In certain situations you would like to disable all browser based editing for specific sites within SharePoint. With the below PowerShell script you can disable browser based editing.

    Get-SPSite -WebApplication [Web Application] -Limit ALL | where {$_.Url.StartsWith("[Url start With]") } | Foreach-Object { 
        Write-Host "Activating Feature Client Based Editing for site" $_.Url
        Enable-SPFeature –identity 8a4b8de2-6fd8-41e9-923c-c7c3c00f8295 -URL $_.Url
    }
    

    Warm Up SharePoint 2013 Site Collections

    When you maintain SharePoint environments you would like to warm up the environments for example once a day. The first load on SharePoint still takes a lot of time. For this situation I have developed a warm up script that also take in account your extended Host Named Site Collections.

    function WarmUp-Url([string]$url){
        Write-Host "Warming up " $url -ForegroundColor DarkYellow;
        Invoke-WebRequest $url -UseDefaultCredentials -UseBasicParsing; 
    }
    
    foreach ($WebApp in (Get-SPWebApplication -IncludeCentralAdministration)){
        $sites = $WebApp | Get-SPSite -Limit All | Get-SPWeb -Limit All
            foreach($site in $sites){
                $html= WarmUp-Url -url $site.Url 
            
                if($site.IsRootWeb){
                    $extendedurls = Get-SPSiteURL $site.Url  -ErrorAction SilentlyContinue;
    
                    foreach($url in $extendedurls){
                        if($url.Url -ne $site.Url){                        
                            $html = WarmUp-Url -url $url.Url;
                        }
                    }
                }
            $site.Dispose();
        }   
    }
    

    Publish Content Types for Specific group

    When you start working with the Content Type Hub in SharePoint you will get in situations were you would like to publish multiple content types at once. In these situations you could use the below function to publish all the content types in a specific group.

    Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
    function Publish-ContentTypesForGroup {
        param
        (
            [parameter(mandatory=$true)][string]$hubUrl,
            [parameter(mandatory=$true)][string]$group
        ) 
    
        $site = Get-SPSite $hubUrl
    
        if(!($site -eq $null))
        {
            $contentTypePublisher = New-Object Microsoft.SharePoint.Taxonomy.ContentTypeSync.ContentTypePublisher ($site)
            $site.RootWeb.ContentTypes | ? {$_.Group -match $group} | % {
    
                if($_.ReadOnly){
                    Write-Host "Content Type" $_Name " is Read Only" -ForegroundColor Yellow;
                    $_.ReadOnly = $false;
                    $_.Update();
                }
                $contentTypePublisher.Publish($_)
                write-host "Content type" $_.Name "has been republished" -foregroundcolor Green
            }
        }
    }

    Starting the Content Type Subscriber Job

    When you work a lot with the Content Type Hub in SharePoint you would also like to start the Content Type Subscriber job by using PowerShell. The below script does the job the web application you specify.

    Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
    function Run-SPContentTypeHubJob([string]$url) 
    { 
        $job = Get-SPTimerJob -WebApplication $url | ?{ $_.Name -like "MetadataSubscriberTimerJob"} 
    
        if ($job -ne $null)  
        {   
            Write-host "The timerjob will be started....." -ForeGroundColor Green
            $job | Start-SPTimerJob 
    
            Write-host  "Run the admin job..." -ForeGroundColor Green
            Start-SPAdminJob -ErrorAction SilentlyContinue              
        }
        else{
    	Write-Host "The Job could not be found" -ForeGroundColor Red
        }
    }

    Delete all items in a List

    During many projects I had situations were I had to remove all items in a list. In this situation you can choose for removing the complete list en recreating it or you could delete every single item. With the below function you can delete every item in the list.

    Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
    function Clear-SPList {
        param
        (
            [parameter(mandatory=$false)][string]$siteUrl,
            [parameter(mandatory=$false)][string]$listname
        )
    
        $site = Get-SPSite $siteUrl;
        $web = $site.OpenWeb();
        $list = $web.Lists[$listname];
    
        write-host "Clearing SharePoint list: " $list.Title -ForeGroundColor Green
    
        $collListItems = $list.Items;
        $count = $collListItems.Count - 1;
        for($intIndex = $count; $intIndex -gt -1; $intIndex--)
        {
                $collListItems.Delete($intIndex);
        } 
    }

    Finding content types that use a specific field

    It ofton occurs that you would like to delete a site column but can’t because it is used by a specific content type. It really takes a long time then to find the right content type. Using the below PowerShell function it is easy and fast to find your content type.

    Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
    function Find-ContenttypesByField {
        param
        (
            [parameter(mandatory=$false)][string]$siteUrl,
            [parameter(mandatory=$false)][string]$fieldname
        )
    
      $site = Get-SPSite $siteUrl
      $web = $site.RootWeb;
      $fields = $web.Fields;
    
      $guid = $fields[$fieldname].id;
    
      $ct = $web.AvailableContentTypes 
      for ($i=0; $i -lt $ct.count; $i++) 
      {
        for ($j=0; $j -lt $ct[$i].fields.count; $j++) 
        {
          if ($ct[$i].fields[$j].id -eq $guid)
          {
            Write-host $ct[$i].Name " has column";
          }
        }
      }
    }

    Setting up Ping and SharePoint Trust Federation

    For a environment we wanted to setup a SSO environment with for example K2. The most know situation is using ADFS. But at our client we were required to use Ping. The configuration an the SharePoint site looks very much the same as ADFS. For a reference I have included the PowerShell script in this post.

    Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
    
    $certpath  = "[Ping Certification Path]"
    $stsname   = "Auth-iLink"
    $stsdesc   = "PingFederate Claims Provider SharePoint"
    $stsrealm  = "sharepoint:ping:con"  #realm configured on Ping
    $signinurl = "[URL Ping]/idp/prp.wsf"
    
    Write-host "Importing Certification on SharePoint Server (Trust relation)." -ForegroundColor Green
    New-SPTrustedRootAuthority -Name "PingFederate IP-STS Sharepoint" -Certificate $certpath
    
    Write-Host "Getting the Certification Object." -ForegroundColor Green
    $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certpath)
    
    Write-Host "Setting up Claim Mapping for SharePoint Authentication" -ForegroundColor Green
    $mappingRole = New-SPClaimTypeMapping -IncomingClaimType "http://schemas.xmlsoap.org/claims/Group" -IncomingClaimTypeDisplayName "Role" -LocalClaimType "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
    $mappingName = New-SPClaimTypeMapping -IncomingClaimType "http://schemas.xmlsoap.org/claims/CommonName" -IncomingClaimTypeDisplayName "GivenName" -LocalClaimType "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
    
    Write-Host "Setting Up Token Issuer"
    New-SPTrustedIdentityTokenIssuer -Name $stsname -Description $stsdesc -Realm $stsrealm -ImportTrustCertificate $cert -ClaimsMappings $mappingRole,$mappingName -SignInUrl $signinurl -IdentifierClaim $m2.InputClaimType
    
    Write-Host "Trusted Identity Token Issuer is set up."

    Delete empty sub folders

    For a migration we had a large set of folder with also empty folders in it. During the migration we iterated trough the folders also going trough the empty folders took a lot of time for the migration process. That way we wanted a PowerShell script to delete the empty sub folders. (Remove the –whatif to actually perform the script).

    Get-ChildItem -recurse | Where {$_.PSIsContainer -and `
    @(Get-ChildItem -Lit $_.Fullname -r | Where {!$_.PSIsContainer}).Length -eq 0} |
    Remove-Item -recurse -whatif

     

    Delete site that start with a specific URL.

    When you are using host named site collections within your environment and also build some kind provisioning mechanism (how this can be done with host named site collections can be read here). You will get in situations that you would like to remove a huge number of site collections that start with a specific URL. By using this script you can:

    Get-SPSite -Limit ALL | where {$_.Url.StartsWith("http://spdev.com/sites") } | Foreach-Object { Remove-SPSite $_.Url –Confirm:$False
    }

    Flushing the blob cache

    Because we are using the blob cache within our environment, we also need a easy option to clear the blob cache when we have update our style sheets or maybe JavaScript files.

    Using this function you can easily clear the blob cache for a specific web application.

    function Flush-SPBlobCache{
            param(
              [string] $Url = $(Read-Host -prompt "Web Application Identity")
            )        
            $webApp = Get-SPWebApplication $Url
            [Microsoft.SharePoint.Publishing.PublishingCache]::FlushBlobCache($webApp)
            Write-Host "Flushed the BLOB cache for:" $webApp
    }

     

    Feature upgrading

    For upgrades on our solutions we use the standard upgrading framework of SharePoint. You can read about it here this is an article on the upgrading framework for SharePoint 2010 but it is also something for SharePoint 2013.

    Using the following function you are able to upgrade a specific feature within a web application.

    function Upgrade-SpecificFeatures{
            param(
              [string] $Url = $(Read-Host -prompt "Web Application Identity"),
              [Guid] $ID = $(Read-Host -prompt "Feature ID")
            )
    
            $SPWebApp = Get-SPWebApplication $Url;
    
            foreach ($site in $SPWebApp.Sites){
                if ($site -ne $null){
    
                    Write-Host "Upgrading site features for site:" $site -ForegroundColor Green;
                    $count = 0;
                    $features = $site.QueryFeatures($ID, $true);
                    Foreach($feature in $features) {
                            $oldVersion = $feature.Version;
                            Write-Host "Upgrading feature:" $feature.Definition.DisplayName;
                            Write-Host "  Current version:" $oldVersion;
                            $feature.Upgrade($true);
                            $newVersion = $feature.Version;
                            Write-Host "      New version:" $newVersion;
                            if ($oldVersion -eq $newVersion) {
                                Write-Host "  Something went wrong upgrading this feature!" -ForegroundColor Red;
                            } else {
                                Write-Host "  Upgrade successful." -ForegroundColor Green;
                                $count++;
                            }
                    }
    
                if ($count -eq 0) {
                    Write-Host "No features found that needed upgrading for site:" $site -ForegroundColor Yellow;
                } else {
                    Write-Host "Done upgrading" $count "feature(s). for site:" $site -ForegroundColor Green;
                }
            }
       }
    }
    Share Button