Back to SharePoint basics: create a list for leavers

Standard

I receive too often this request (from my sister this week;-):

Can I create something in SharePoint to let my colleagues log their own holiday so that I can later export that list to Excel ?

So here is a quick video with step I made, following this should give you just that.

Add to this the out-of-the-box SharePoint Alerts and you will receive a notification each time a new leave is registered.

Nothing much to it, no workflow approval plugged-in, but nice and simple SharePoint OOB No-Code solution that is quite often overlooked.

(sorry for the Audio which is not great, as I said, it’s a quick and dirty tip)

Sharepoint 2013 “mark task complete”

Standard

A great new feature in 2013 is the ability for a user to “mark task complete” straight from the task list without having to edit the item and click.

Screenshot below illustrates what this is

task

The issue I had today is that -somehow- the column to tick that task was actually not showing anymore from the task views. The list is definitely a Tasks list but the content type although originally inheriting from the out-of-the-box Task parent Site Collection CT has some custom columns in the site Content Type level.

Cause of issue (why the column was gone): not found yet, may be the “completed” column was edited by a user with design rights, but the formula inside was correct.

Solution: that’s the bad news.

  • Re-adding the column again didn’t work as it would not show the column as a Check Box magically but as a value “Yes/No” .. not useful.
  • Recreating the view and making it exactly like an out-of-the-box task view did not work, same as above.
  • Only solution was to re-create the list fully, and re-add the Custom Task Content Type, then remove the default “Task” Content Type. Once done task can be copy across if needed, as long as there no workflow in progress users will not be disturb as you may even change the URL to the original list later on.

Not a great finding on this feature I am afraid, but since nothing came up on a quick internet search I thought I would share it.

Have you ever wondered how to re-use this “mark as complete” in a fully customised list (not tasks App originally) ? One would think it’s the whole point of having such column, to be able to re-use it somewhere else, right. I could not.

Anyone  ?

 

Collection of #Powershell Scripts for Sharepoint #ContentType #CTHub #Sites #Lists

Standard

In my current client project I had to design a Sharepoint portal with many lists and libraries that will eventually be re-usable across the organisation (ie the same lists structure will be used by several sites, sub-sites under several site collections and potentially in separate web applications), therefore using the SharePoint Content Type Hub was the obvious built-in solution to keep the Content Types centrally managed. However you will find out that using the browser to perform the tasks to edit the content types and publish them to all sites can become a major time consuming and if like me you don’t like to click twice to achieve the same thing, the need to automate those edits is paramount. Powershell to the rescue, here are the main scripts I use for manipulating Content Types, pushing them, but also others such as create new web application, new site collection, managed path, content type and more.

(click each “expand source” section to grab the scripts)
  • Add Sharepoint Snap-In to Powershell
(the basis if using PowerGui, which I definitely recommend, thanks Paul  for recommending it to me, this tool totally changed my opinion on Powershell)
Add-PsSnapin Microsoft.SharePoint.PowerShell
 [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SharePoint')
  • Activate Taxonomy feature option under Site Collection Administration
 (to receive Content Type Hub Published content types, missing from Blank Template)
# Activate Content TypeHub option under Site Collection Administration
stsadm -o activatefeature -id 73EF14B1-13A9-416b-A9B5-ECECA2B0604C -url http://MySharePointSrv.com/sites/sitecol1
  • Unpublish All Content Types containing Custom Group Name

$HubUrl = “http://cthub.MySharePointSrv.com”
$HubSite = Get-SPSite $HubUrl
$HubWeb = $HubSite.RootWeb
$Publisher = New-Object Microsoft.SharePoint.Taxonomy.ContentTypeSync.ContentTypePublisher($HubSite)
$ContentTypes = $HubWeb.ContentTypes
foreach ($ContentType in $ContentTypes)
{
        $contentTypeGroup = $ContentType.Group
        $contentTypeName = $ContentType.Name

		# only publish the nMySharePointSrv Conten Types
		if ( $contentTypeGroup  -like '*MyCustomGroupName*')
		{
			 $Publisher.Unpublish($ContentType)
             echo "UNPUBLISHED [CONTENT TYPE GROUP] $contentTypeGroup, $contentTypeName"
		}
 }
$HubSite.Dispose()
  • (re)Publish All Content Types containing Custom Group Name to Subscribing web application

$HubUrl = “http://cthub.MySharePointSrv.com”
$HubSite = Get-SPSite $HubUrl
$HubWeb = $HubSite.RootWeb
$Publisher = New-Object Microsoft.SharePoint.Taxonomy.ContentTypeSync.ContentTypePublisher($HubSite)
$ContentTypes = $HubWeb.ContentTypes
foreach ($ContentType in $ContentTypes)
{
        $contentTypeGroup = $ContentType.Group
        $contentTypeName = $ContentType.Name

		# only publish the nMySharePointSrv Conten Types
		if ( $contentTypeGroup  -like '*MyCustomGroupName*')
		{
			 $Publisher.Publish($ContentType)
             echo "PUBLISHED [CONTENT TYPE GROUP] $contentTypeGroup, $contentTypeName"
		}

    }

$HubSite.Dispose()
  • Force the Publish content types from Content Type Hub to a target web application (instead of waiting for job to start on schedule
# http://get-spscripts.com
#Run the Content Type Hub timer job
$ctHubTJ = Get-SPTimerJob "MetadataHubTimerJob"
$ctHubTJ.RunNow()

#Run the Content Type Subscriber timer job for a specific Web Application
$ctSubTJ = Get-SPTimerJob "MetadataSubscriberTimerJob" -WebApplication http://MySharePointSrv.com
$ctSubTJ.RunNow()
 echo "Metadata Hub Job initiated"

  • Read a text file that lists some files to be used as Content Types, iterates through each files and its create contents

The IMPORTFILES.TXT has this format

doc1 – Document title1.dotx
doc2 – Document title2.dotx
doc3 – Document title3.dotx
<# Create Content Type
#http://jaclynsaito.wordpress.com/2011/06/07/create-a-sharepoint-2010-content-type-using-powershell/
#>
# Set Variables
$url = "http://cthub.MySharePointSrv.com"
$cGroup = "MyContentTypes Group"
# Read list of document template to create CT
$DocumentTemplatesArray = Get-Content "C:\temp\IMPORTFILES.txt"
$CTHubSiteColl =  "http://cthub.MySharePointSrv.com"

$site = get-spsite $url

$web = $site.openweb()

$ctypeParent = $web.availablecontenttypes["Document"]

 # Loop thru the number of CT to create
$i = 0
$MaxCT = $DocumentTemplatesArray.count  # total CT to create
 write-host "--------" + $DocumentTemplatesArray.count total lines read from file + "-------"+
foreach ($line in $DocumentTemplatesArray)
{
    $templateURL = "/DocumentTemplates/" + $ctypeName   # URL of templatefile with extension
    $ctypeName = $line.replace(".doc","").replace(".xls","").replace(".vst","").replace(".xltx","")   #name of CT without extension

    Write-Host $i + ": " + $ctypeName

    $ctype = new-object Microsoft.SharePoint.SPContentType($ctypeParent, $web.contenttypes, $ctypeName)

    $web.contenttypes.add($ctype)

    # ADD CUSTOM COLUMN
     $web.fields.add(“myField”, ([Type]“Microsoft.SharePoint.SPFieldType”)::Text, $false)
     $field = $web.fields.getfield(“myField”)
     $fieldLink = new-object Microsoft.SharePoint.SPFieldLink($field)
     $ctype.fieldlinks.add($fieldLink)

    #   set the current document template file to be the document tmeplate
    $ctype.DocumentTemplate = $templateURL
    $ctype.Group = $cGroup  # give GroupName

    <# Publish Content Type  #http://www.jeffholliday.com/2011/08/powershell-script-create-content-type.html #>#
    write-Host "---- now publish to : " + $CTHubSiteColl
    $Publisher = New-Object Microsoft.SharePoint.Taxonomy.ContentTypeSync.ContentTypePublisher($CTHubSiteColl)

    $Publisher.Publish($ctype);

    #   update the content type
    $ctype.Update()
     $i++
 }

 # END OF LOOP TO CREATE CT
$web.Dispose()
$site.Dispose()
  • Create a new Web Application
# Declare  Variables
$siteName = “SP - MyWebApp.sharepoint.com”
 $port = 80
 $hostHeader = “MyWebApp.sharepoint.com”
 $path = “d:\dedicateddrive\MyWebApp.sharepoint.com”
 $url = “http://MyWebApp.sharepoint.com:80”
 $appPoolName = “SP - MyWebApp.sharepoint.com”
 $managedAccount = “sharepoint\Sharepoint-WebApp-MA”
 $dbServer = “Sharepoint-SQL”
 $dbName = “Sharepoint-MyWebApp”
 $allowAnonymous = $false
 $authenticationMethod = “NTLM”
 $ssl = $false
#Create the Web app
New-SPWebApplication -Name $siteName -Port $port -HostHeader $hostHeader -Path $Path -URL $url -ApplicationPool $appPoolName -ApplicationPoolAccount (Get-SPManagedAccount “$managedAccount”) -DatabaseName $dbName -DatabaseServer $dbServer -AllowAnonymousAccess: $allowAnonymous -AuthenticationMethod $authenticationMethod -SecureSocketsLayer:$ssl
</div>
  • Convert existing Web Application from classic-mode to claims-based authentication 
 # http://technet.microsoft.com/en-us/library/gg251985(v=office.14).aspx

#set the specified user account as an administrator for the site
$WebAppName = "http://MySharePointSrv.com"
$wa = get-SPWebApplication $WebAppName
$wa.UseClaimsAuthentication = $true
$wa.Update()

#configure the policy to enable the user to have full access
$account = "MySharePointSrv\SPSetup"
$account = (New-SPClaimsPrincipal -identity $account -identitytype 1).ToEncodedString()
$wa = get-SPWebApplication $WebAppName
$zp = $wa.ZonePolicies("Default")
$p = $zp.Add($account,"PSPolicy")
$fc=$wa.PolicyRoles.GetSpecialRole("FullControl")
$p.PolicyRoleBindings.Add($fc)
$wa.Update()

#Migrate users
$wa.MigrateUsers($true)

$wa.ProvisionGlobally()
  • Create a new Managed Path
$ManagedPath = "/teams"
$WebApplication= "http://MySharePointSrv.com"
 New-SPManagedPath -RelativeURL $ManagedPath -WebApplication $WebApplication
  • Create a new Site Collection
# ---- CONFIGURATION BLOCK ----
$WebApplication= "http://MySharePointSrv.com"
$url = "http://MySharePointSrv.com"
$ContentDatabase = "SP-ContentDB1"
$WebsiteName = "MyNewWebApp"
$WebsiteDesc = ""
$Template = "STS#1"
    # STS#0 Team site
	# STS#1 Blank site
    # enter the command GET-SPWebTemplate to choose different template

# the username, display name, and email address
$PrimaryLogin = "MySharePointSrv\SPSetup"
$PrimaryDisplay = "SPSetup"
$PrimaryEmail = "Sharepoint@MySharePointSrv.com"

# Information about the secondary site collection administrator (Secondary Owner)
$SecondaryLogin = "MySharePointSrv\SPAdmin"
$SecondaryDisplay = "SPAdmin"
$SecondaryEmail = "Sharepoint@MySharePointSrv.com"

# Names of the default Members and Viewers groups
$MembersGroup = "$WebsiteName Members"
$ViewersGroup = "Viewers"
# ---- END OF VARIABLES ----

# You should not have to change any of the remaining code
# Unless you want to change the functionality of the script itself

# Create New Managed Path /
# New-SPManagedPath -RelativeURL "/site1" -WebApplication $WebApplication

Write-Host "Creating ContentDatabase "$ContentDatabase "......"
# Create new Content DB
New-SPContentDatabase -Name $ContentDatabase -WebApplication $WebApplication

Write-Host "Creating SiteCollection "$url"......"
# Create New Site Collection in same Content Database
New-SPSite -Url $url –ContentDatabase $ContentDatabase -Name $WebsiteName –Description $WebsiteDesc  -Template $Template -OwnerAlias $PrimaryLogin –OwnerEmail $PrimaryEmail -SecondaryOwnerAlias $SecondaryLogin -SecondaryEmail $SecondaryEmail

# default groups (Visitor, Members, and Owners)
$web = Get-SPWeb $url
$web.CreateDefaultAssociatedGroups($PrimaryLogin,$SecondaryLogin,"")

$PrimaryAdmin = Get-SPUser $PrimaryLogin -Web $url
$PrimaryAdmin.Name = $PrimaryDisplay
$PrimaryAdmin.Update()
$SecondaryAdmin = Get-SPUser $SecondaryLogin -Web $url
$SecondaryAdmin.Name = $SecondaryDisplay
$SecondaryAdmin.Update()

# Finish by disposing of the SPWeb object to be a good PowerShell citizen
$web.Dispose()
  • Create a Sites and Sub-sites from an XML file

source: http://geekswithblogs.net/Norgean/archive/2012/04/12/creating-sharepoint-sites-from-xml-using-powershell.aspx Create the structure in websites.XML:

<Sites>
<Site Name="Test 1" Url="Test1" />
<Site Name="Test 2" Url="Test2" >
<Site Name="Test 2 1" Url="Test21" >
<Site Name="Test 2 1 1" Url="Test211" />
<Site Name="Test 2 1 2" Url="Test212" />
</Site>
</Site>
<Site Name="Test 3" Url="Test3" >
<Site Name="Test 3 1" Url="Test31" />
<Site Name="Test 3 2" Url="Test32" />
<Site Name="Test 3 3" Url="Test33" >
<Site Name="Test 3 3 1" Url="Test331" />
<Site Name="Test 3 3 2" Url="Test332" />
</Site>
<Site Name="Test 3 4" Url="Test34" />
</Site>
</Site>

Read this structure in Powershell, and recursively create the sites. with a progress dialog barre, too. (enter the command GET-SPWebTemplate to choose different template)

&lt;/pre&gt;

$url = &quot;http://MySharePointSrv.com&quot;
$PathFile = &quot;C:\Powershell\websites.xml&quot;
$SiteTemplate = &quot;BLANKINTERNET#2&quot; &lt;# BLANKINTERNET#2 #&gt;

$snap = Get-PSSnapin | Where-Object { $_.Name -eq &quot;Microsoft.SharePoint.Powershell&quot; }
if ($snap -eq $null)
{
 Add-PSSnapin &quot;Microsoft.SharePoint.Powershell&quot;
}

function CreateSites($baseUrl, $sites, [int]$progressid)
{
 $sitecount = $sites.ChildNodes.Count
 $counter = 0
 foreach ($site in $sites.Site)
 {
 Write-Progress -ID $progressid -Activity &quot;Creating sites&quot; -status &quot;Creating $($site.Name)&quot; -percentComplete ($counter / $sitecount*100)
 $counter = $counter + 1

Write-Host &quot;Creating $($site.Name) $($baseUrl)/$($site.Url)&quot;
 New-SPWeb -Url &quot;$($baseUrl)/$($site.Url)&quot; -AddToQuickLaunch:$false -AddToTopNav:$true -Confirm:$false -Name &quot;$($site.Name)&quot; -Template &quot;$SiteTemplate&quot; -UseParentTopNav:$true
 if ($site.ChildNodes.Count -gt 0)
 {
 CreateSites &quot;$($baseUrl)/$($site.Url)&quot; $site ($progressid +1)
 }
 Write-Progress -ID $progressid -Activity &quot;Creating sites&quot; -status &quot;Creating $($site.Name)&quot; -Completed
 }
}
# read an xml file
$xml = [xml](Get-Content $PathFile)
$xml.PreserveWhitespace = $false

CreateSites $url $xml.Sites 1
&lt;pre&gt;
  • Delete sites listed in XML file

source: http://geekswithblogs.net/Norgean/archive/2012/04/12/creating-sharepoint-sites-from-xml-using-powershell.aspx Create the structure in websites.XML:

<Sites>
<Site Name="Test 1" Url="Test1" />
<Site Name="Test 2" Url="Test2" >
<Site Name="Test 2 1" Url="Test21" >
<Site Name="Test 2 1 1" Url="Test211" />
<Site Name="Test 2 1 2" Url="Test212" />
</Site>
</Site>
<Site Name="Test 3" Url="Test3" >
<Site Name="Test 3 1" Url="Test31" />
<Site Name="Test 3 2" Url="Test32" />
<Site Name="Test 3 3" Url="Test33" >
<Site Name="Test 3 3 1" Url="Test331" />
<Site Name="Test 3 3 2" Url="Test332" />
</Site>
<Site Name="Test 3 4" Url="Test34" />
</Site>
</Site>

Read this structure in Powershell, and recursively delete the sites. this time from the latest down the hierarchy and up .  

&lt;/pre&gt;

$url = &quot;http://MySharePointSrv.com&quot;
$PathFile = "C:\Powershell\websites.xml"

$url = "http://icon-dev.norgine.com"
$PathFile = "C:\CPS\Powershell\websites.xml"

$snap = Get-PSSnapin | Where-Object { $_.Name -eq "Microsoft.SharePoint.Powershell" }
if ($snap -eq $null)
{
Add-PSSnapin "Microsoft.SharePoint.Powershell"
}

function DeleteSites($baseUrl, $sites, [int]$progressid)
{
$sitecount = $sites.ChildNodes.Count
$counter = 0
foreach ($site in $sites.Site)
{
Write-Progress -ID $progressid -Activity "Deleting sites" -status "Deleting $($site.Name)" -percentComplete ($counter / $sitecount*100)
$counter = $counter + 1

if ($site.ChildNodes.Count -gt 0)
{
DeleteSites "$($baseUrl)/$($site.Url)" $site ($progressid +1)
}
Write-Host "Deleting $($site.Name) $($baseUrl)/$($site.Url)"
		$WebSiteIdentity = "$($baseUrl)/$($site.Url)"
	   	Remove-SPWeb -Identity $WebSiteIdentity
Write-Progress -ID $progressid -Activity "Deleting sites" -status "Deleting $($site.Name)" -Completed
}
}
# read an xml file
$xml = [xml](Get-Content  $PathFile)
$xml.PreserveWhitespace = $false

DeleteSites $url $xml.Sites 1
  • Change a site collection Master Page to a Custom.master
$url = &quot;http://MySharePointSrv.com&quot;
$Scollection = &quot;&quot;
$NewMasterPage = &quot;$Scollection/_catalogs/masterpage/Custom.master&quot;

$web = Get-SPWeb $url
Write-Host $NewMasterPage
$web.CustomMasterUrl = $NewMasterPage
$web.MasterUrl = $NewMasterPage

$web.Update()
Write-Host &quot;...done.&quot;

$url = &quot;http://MySharePointSrv.com/sites/sitecol1/site1&quot;
$Scollection = &quot;/sites/sitecol1&quot;
$NewMasterPage = &quot;$Scollection/_catalogs/masterpage/custom.master&quot;

$web = Get-SPWeb $url
Write-Host $NewMasterPage
$web.CustomMasterUrl = $NewMasterPage
$web.MasterUrl = $NewMasterPage

$web.Update()
Write-Host &quot;...done.&quot;

$url = &quot;http://MySharePointSrv.com/teams/IT&quot;
$Scollection = &quot;/teams/IT&quot;
$NewMasterPage = &quot;$Scollection/_catalogs/masterpage/custom.master&quot;

$web = Get-SPWeb $url
Write-Host $NewMasterPage
$web.CustomMasterUrl = $NewMasterPage
$web.MasterUrl = $NewMasterPage

$web.Update()
Write-Host &quot;...done.&quot;

$url = &quot;http://MySharePointSrv.com/sites/sitecol1&quot;
$Scollection = &quot;/sites/sitecol1&quot;
$NewMasterPage = &quot;$Scollection/_catalogs/masterpage/custom.master&quot;

$web = Get-SPWeb $url
Write-Host $NewMasterPage
$web.CustomMasterUrl = $NewMasterPage
$web.MasterUrl = $NewMasterPage

$web.Update()
Write-Host &quot;...done.&quot;

$url = &quot;http://MySharePointSrv.com/sites/sitecol1/template&quot;
$Scollection = &quot;/sites/sitecol1&quot;
$NewMasterPage = &quot;$Scollection/_catalogs/masterpage/custom.master&quot;

$web = Get-SPWeb $url
Write-Host $NewMasterPage
$web.CustomMasterUrl = $NewMasterPage
$web.MasterUrl = $NewMasterPage

$web.Update()
Write-Host &quot;...done.&quot;

  • Remove site columns
# Set Variables
$url = &quot;http://cthub.MySharePointSrv.com&quot;
$siteColumnsList = &quot;myField&quot;  # Specify a list of Site Column Names to be deleted, seperated by ;

$site = new-object Microsoft.SharePoint.SPSite($url)
 $array = $siteColumnsList.Split(&quot;;&quot;)

$site = get-spsite $url

$web = $site.openweb()
 # go thru each content type to remove column
    foreach ($ctype in $web.ContentTypes)
     {
        foreach($colms in $array)
         {
          try
          {
            #Get link to the columnn from the web
            $spFieldLink = New-Object Microsoft.SharePoint.SPFieldLink ($web.Fields[$colms])

            #Remove the column from the content type and update
            $ct.FieldLinks.Delete($spFieldLink.Id)
            $ct.Update()

             # below 2 lines is to delete from Site, when not in a ContentType
           # $column = $site.rootweb.Fields[$colms]
           # $site.rootweb.Fields.Delete($column)
           Write-Host $column.Title &quot;deleted successfully.&quot;
          }
          catch [System.Exception]
          {
           Write-Host $column.Title &quot;deleted failed.&quot;
           #Best Attempt to Remove Site Columns
          }
         }
     }
$site.Dispose()