Archive
SQL Server Reporting Services Reports error when large numbers of parameters are selected
You may encounter an exception when running certain SQL Server Reporting Services (SSRS) reports.
The exception message will generally say something along the lines of Operation is not valid due to the current state of the object.
You may encounter this either in reports with a large number of parameters, or in reports where the parameter counts are low but the parameters themselves are multiple choice and have a large number of items selected.
The cause of this is a security fix released by Microsoft under Security Bulletin MS11-100. The fix, among other things, limits the number of keys that ASP.NET applications can parse as part of a request; large numbers of keys could potentially allow a denial of service to occur.
Unfortunately the effect of this is that our large parameter set is blocked, causing the exception and presenting a nasty error to our end users.
Luckily there is a solution to this.
The trick is to add a section to your web.config inside the <appSettings /> section.
<appSettings> ... <add key="aspnet:MaxHttpCollectionKeys" value="5000" /> ... </appSettings>
The actual value used should reflect the number of keys you believe will be used, but 5000 is a reasonable starting point.
If you are using SSRS in native mode, the web.config file is located within the reporting services installation path, usually C:\Program Files\Microsoft SQL Server\MSSQL\Reporting Services\ReportServer.
If you are using SSRS in SharePoint integration mode, the modification needs to be made to the web.config of the application pool where you are running your reports. For this I would recommend that you create a solution that utilises the SPWebConfigModification class as part of a feature receiver, rather than making the changes directly. This will ensure that the change is made across your SharePoint farm simultaneously without the human error that could come from doing the change manually on multiple servers.
Note: In both cases, these modifications either require a restart of the reporting server or in the case of SharePoint a recycle of the Application Pool. This will result in short outages so you will want to schedule this as part of maintenance accordingly.
For more information I would recommend reading the relevant KB article.
via Chris on SharePoint http://spchris.com/2013/05/sql-server-reporting-services-reports-errors-when-large-numbers-of-parameters-are-selected-2/
|
SharePoint and Project Server Consultant
This article has been cross posted from spchris.com (original article) |
Managing path context in SharePoint
When working with SharePoint’s UI, it is often difficult to work out where in the URL structure you are.
You may for instance be working at the root of a site collection, but that itself may be several layers deep.
Take the following example.
You have a site collection for your company, but each team maintains its own site collection under the managed path (with wildcard inclusion) /teams.
Some of the team sites use a template that includes a master page that references an image gallery script. This gallery script has to load images out of a document library local to each team site.
The problem of course is determining the URL to the code and images, when they could be located at /teams/team-a/gallery images or /teams/team-b/gallery images
Of course in a server-side solution this is simple, SharePoint provides the handy shortcut of ~sitecollection. This works both directly in master pages:
<SharePoint:ScriptLink runat="server" Name="~sitecollection/Style Library/js/galleryscript.js" Language="javascript"/>
Or using a custom action within a SharePoint solution, sandboxed or otherwise:
<?xml version="1.0" encoding="utf-8"?> <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <CustomAction ScriptSrc="~sitecollection/Style Library/js/galleryscript.js" Location="ScriptLink" /> </Elements>
But how do we achieve the same within the front end itself?
Introducing _spPageContextInfo
The _spPageContextInfo object is a really wonderful little thing. It is added by the SPWebPartManager control, which is included in all of the out of the box master pages (including default.master and system.master) and I really strongly recommend is added to any custom master pages you create.
Because of it’s essentially omnipresent nature it makes using _spPageContextInfo a really simple matter of, well, using it.
It is a small object, containing only a few properties of the page, the site and the site collection it is part of. Here is a JSON representation of what it contains.
{
alertsEnabled: true,
allowSilverlightPrompt: "True",
currentLanguage: 1033,
pageItemId: 1,
pageListId: "{6300d68a-4512-49f0-85b9-d3671855e31c}",
siteServerRelativeUrl: "/teams/team-a",
userId: 1,
webLanguage: 1033,
webServerRelativeUrl: "/teams/team-a/subsite",
webUIVersion: 4
}
Here’s a brief explanation of the properties in it.
alertsEnabled
This one is simple, is SharePoint’s alerts email notification service enabled for the current web application. This maps to the SPWebApplication.AlertsEnabled property.
allowSilverlightPrompt
Will the user be prompted to download Silverlight if they do not already have it when they attempt to use Silverlight based UI elements. This maps to the SPWebApplication.AllowSilverlightPrompt property.
currentLanguage
This is the current locale being used by the user, if they have not changed it to a custom one, then this maps to the SPWeb.Language property.
pageItemId
This is the ID of the current page’s item within the document library containing it.
pageListId
This is the GUID of the document library containing the current page.
siteServerRelativeUrl
This is the path of the site collection, relative to the web application.
userId
This is the user’s ID for the current site collection.
webLanguage
This is the default language for the current web. This maps to the SPWeb.Language property.
webServerRelativeUrl
This is the path of the web, relative to the web application.
webUIVersion
This is the UI version of the current web, determining if the master page and components in use and available is compatible with SharePoint 2007 (3), SharePoint 2010 (4) or SharePoint 2013 (5)
Note: The actual properties available varies depending upon where you happen to be. For instance the pageItemId and pageListId properties do not exist within system pages, as they do not have IDs and do not live within document libraries.
That’s pretty cool, but how do we use it?
Well, as they are properties we can pretty much use them however we wish. Let’s take a look at a simple way of referencing files in our gallery images document library, for example referencing a logo to use on the homepage.
(function() {
// The path to the image.
var imagePath = '/gallery%20images/logo.jpg';
// The relative URL is usually without the trailing /
// however if we are on the root site collection then it
// is simply / on its own and must be handled.
if (_spPageContextInfo.siteServerRelativeUrl == '/') {
imagePath = imagePath.substring(1);
}
// Build our full image path.
imagePath = _spPageContextInfo.siteServerRelativeUrl + imagePath;
// Finally set our image's source to the images path
$('img#logoImage').attr('src', imagePath);
})();
Of course this is a rather contrived example, and you should probably be using the client side object model (CSOM) to pull items from a document library. However I hope it demonstrates how the object can be used.
A common use I use this for is is actually with the CSOM.
var ctx = new SP.ClientContext(_spPageContextInfo.webServerRelativeUrl);
I do it this way because I have had issues with concurrency when using the get_Current() method in SP.ClientContext when there is other code in use on pages that I do not have control over.
via Chris on SharePoint http://spchris.com/2013/05/managing-path-context-in-sharepoint/
|
SharePoint and Project Server Consultant
This article has been cross posted from spchris.com (original article) |
Dr Popup: Or How I learned to stop worrying and love the modal dialog
One of the many pieces of functionality added in SharePoint 2010 was the dialog window, this is a nice little control implemented in JavaScript that allows us to display a “lightbox” style dialog, containing either another page within our site or direct HTML.
This post will explain how to pass information back and forth between the page creating the dialog and the page displayed within that dialog.
Firstly lets take a look at the JavaScript on our calling page. This could be added through any of the normal methods of inserting JavaScript into a SharePoint page.
(function() {
// Use jQuery to wait for the DOM to be ready
$(document).ready(function(){
// Use SharePoint's SOD (script on demand) to wait for SharePoint to be ready
ExecuteOrDelayUntilScriptLoaded(function() {
//Create an options object (valid options properties reference - http://msdn.microsoft.com/en-us/library/ff410058(v=office.14).aspx):
var options = SP.UI.$create_DialogOptions();
options.title = "My dialog";
options.autoSize = true;
options.url = "/pages/myPage.aspx";
//arguments to pass in to dialog (standard anonymous object)
options.args = {
someKey: "Some Value",
someOtherKey: "Some Other Value"
}
//callback to handle the result from the dialog, you process the result of your
//dialog from the origin page.
options.dialogReturnValueCallback = function(result, arguments) {
if (result == SP.UI.DialogResult.OK) {
// Our arguments parameter contains the arguments originally passed to the dialog, so we could
// reference arguments.someOtherKey here for instance and it would contain the value "Some Other Value"
SP.UI.Notify.addNotification("OK Button clicked");
} else if (result == SP.UI.DialogResult.Cancel) {
SP.UI.Notify.addNotification("Cancel button clicked");
}
}
// Create the dialog with options.
SP.UI.ModalDialog.showModalDialog(options);
}, "SP.UI.Dialog.js");
});
})();
As you can see, there are 3 major parts to this, the options object, the args object and the callback.
The options object is created using the SP.UI.$create_DialogOptions() factory, although I have seen people using an empty object without apparent issue.
Into this you set various options affecting the way the dialog is presented, such as title, size and the url to the page to be displayed. A list of available options is posted on MSDN.
The args object is actually a property of the options object, and can be any sort of JavaScript object, be it an anonymous object like in our example above, or a closure, or a data model or anything else you may wish to send. This object will be available within the page shown in your dialog.
The last part is the callback, this is also a property set against the options object, and is a callback method executed when the DialogClose() method is called from within the parent page.
It takes two parameters, the result which should be one of the members of the SP.UI.DialogResult enumeration and the arguments, which can be any kind of variable or object returned from the dialog page.
Additionally in here you can call SP.UI.ModalDialog.RefreshPage() – this method takes a single parameter and is designed to be called using the result value from your callback. This method ensures that the page refreshes if the result is equal to SP.UI.DialogResult.OK
With these three sections we can define how our dialog looks, what data it takes in, and what happens once it is closed and how to process the data that comes back from it.
Lastly the dialog itself is opened using the call to SP.UI.ModalDialog.showModalDialog()
Now lets take a look at the other side of the coin, here’s the JavaScript code to run within the dialog page.
(function() {
// As on the parent page, wait for the DOM and SharePoint to be ready before doing anything.
$(document).ready(function() {
ExecuteOrDelayUntilScriptLoaded(function() {
// retrieve a reference to the page that called us.
var _parent = SP.UI.ModalDialog.get_childDialog();
// If this is false, then chances are you have browsed to the page directly rather than
// loaded it in a modal window.
if (_parent) {
// Pull through the arguments sent to the dialog.
var args = _parent.get_args();
// set a red alert status line with our argument.
var status = SP.UI.Status.addStatus("someKey:", args.someKey);
SP.UI.Status.setStatusPriColor(status, "red");
// Add handlers for our ok and cancel buttons
$("#okButton").click(function() {
// This closes the modal dialog and runs the callback method defined on the parent page.
SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK, args);
});
$("#cancelButton").click(function() {
SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.Cancel, "cancel clicked");
});
}
}, "SP.UI.Dialog.js");
});
})();
This again is fairly simple, we first of all get a reference to the calling page in the variable _parent. Then if that has been successful we pull in the args object from our options variable above.
Once at this point the world is your oyster, you can use the arguments from the parent page however you wish to display the dialog or interact with your users.
To close the dialog you can use SP.UI.ModalDialog.commonModalDialogClose(). This again takes two parameters, the first should be a member of the SP.UI.DialogResult enumeration, the second can be any object to send back, these map to the variables sent to our callback function above.
Additionally in here I have made use of some of the other neat parts of the SP.UI namespace such as the status bar and notification system. These are beyond the scope of this post but they’re pretty awesome and you should go read about them.
So there we go, by now you should be able to add true SharePoint style dialogs to your pages with minimal effort.
via Chris on SharePoint http://spchris.com/2013/04/dr-popup-or-how-i-learned-to-stop-worrying-and-love-the-modal-dialog/
|
SharePoint and Project Server Consultant
This article has been cross posted from spchris.com (original article) |
Collection of #Powershell Scripts for Sharepoint #ContentType #CTHub #Sites #Lists
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
Add-PsSnapin Microsoft.SharePoint.PowerShell
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SharePoint')
- Activate Taxonomy feature option under Site Collection Administration
# 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)
</pre>
$url = "http://MySharePointSrv.com"
$PathFile = "C:\Powershell\websites.xml"
$SiteTemplate = "BLANKINTERNET#2" <# BLANKINTERNET#2 #>
$snap = Get-PSSnapin | Where-Object { $_.Name -eq "Microsoft.SharePoint.Powershell" }
if ($snap -eq $null)
{
Add-PSSnapin "Microsoft.SharePoint.Powershell"
}
function CreateSites($baseUrl, $sites, [int]$progressid)
{
$sitecount = $sites.ChildNodes.Count
$counter = 0
foreach ($site in $sites.Site)
{
Write-Progress -ID $progressid -Activity "Creating sites" -status "Creating $($site.Name)" -percentComplete ($counter / $sitecount*100)
$counter = $counter + 1
Write-Host "Creating $($site.Name) $($baseUrl)/$($site.Url)"
New-SPWeb -Url "$($baseUrl)/$($site.Url)" -AddToQuickLaunch:$false -AddToTopNav:$true -Confirm:$false -Name "$($site.Name)" -Template "$SiteTemplate" -UseParentTopNav:$true
if ($site.ChildNodes.Count -gt 0)
{
CreateSites "$($baseUrl)/$($site.Url)" $site ($progressid +1)
}
Write-Progress -ID $progressid -Activity "Creating sites" -status "Creating $($site.Name)" -Completed
}
}
# read an xml file
$xml = [xml](Get-Content $PathFile)
$xml.PreserveWhitespace = $false
CreateSites $url $xml.Sites 1
<pre>
- 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 .
</pre>
$url = "http://MySharePointSrv.com"
$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 = "http://MySharePointSrv.com" $Scollection = "" $NewMasterPage = "$Scollection/_catalogs/masterpage/Custom.master" $web = Get-SPWeb $url Write-Host $NewMasterPage $web.CustomMasterUrl = $NewMasterPage $web.MasterUrl = $NewMasterPage $web.Update() Write-Host "...done." $url = "http://MySharePointSrv.com/sites/sitecol1/site1" $Scollection = "/sites/sitecol1" $NewMasterPage = "$Scollection/_catalogs/masterpage/custom.master" $web = Get-SPWeb $url Write-Host $NewMasterPage $web.CustomMasterUrl = $NewMasterPage $web.MasterUrl = $NewMasterPage $web.Update() Write-Host "...done." $url = "http://MySharePointSrv.com/teams/IT" $Scollection = "/teams/IT" $NewMasterPage = "$Scollection/_catalogs/masterpage/custom.master" $web = Get-SPWeb $url Write-Host $NewMasterPage $web.CustomMasterUrl = $NewMasterPage $web.MasterUrl = $NewMasterPage $web.Update() Write-Host "...done." $url = "http://MySharePointSrv.com/sites/sitecol1" $Scollection = "/sites/sitecol1" $NewMasterPage = "$Scollection/_catalogs/masterpage/custom.master" $web = Get-SPWeb $url Write-Host $NewMasterPage $web.CustomMasterUrl = $NewMasterPage $web.MasterUrl = $NewMasterPage $web.Update() Write-Host "...done." $url = "http://MySharePointSrv.com/sites/sitecol1/template" $Scollection = "/sites/sitecol1" $NewMasterPage = "$Scollection/_catalogs/masterpage/custom.master" $web = Get-SPWeb $url Write-Host $NewMasterPage $web.CustomMasterUrl = $NewMasterPage $web.MasterUrl = $NewMasterPage $web.Update() Write-Host "...done."
- Remove site columns
# Set Variables
$url = "http://cthub.MySharePointSrv.com"
$siteColumnsList = "myField" # Specify a list of Site Column Names to be deleted, seperated by ;
$site = new-object Microsoft.SharePoint.SPSite($url)
$array = $siteColumnsList.Split(";")
$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 "deleted successfully."
}
catch [System.Exception]
{
Write-Host $column.Title "deleted failed."
#Best Attempt to Remove Site Columns
}
}
}
$site.Dispose()
via François on Sharepoint http://sharepointfrancois.wordpress.com/2013/04/23/collection-of-powershell-scripts-for-sharepoint-contenttype-cthub-sites-lists/
|
French native Sharepoint Consultant living in London. A crossway between a designer, developer and system architect. Prefers stretching the limit of out-of-the-box features rather than breaking them into code. When not working with Microsoft Sharepoint François is often found on Web2.0 News sites and related social networking tools.
This article has been cross posted from sharepointfrancois.wordpress.com/ (original article) |
Using Knockout.js in a SharePoint Context
The excellent Knockout.js library is an MVVM (Model, View, ViewModel) library.
Using it you can completely abstract the logic from the presentation in your web applications, allowing dynamic and responsive UIs to be created without having to manage all of the fiddly UI updates directly.
The Knockout site has a great set of tutorials on how to use it, but I thought I would bring them into a SharePoint context.
Specifically, I am going to cover their 5th tutorial, Loading and Saving Data. In this tutorial they give the example of a simple task list that a user can update and delete dynamically. If you do not know Knockout at all, I recommend you go through this tutorial before continuing. Go ahead, I can wait.
Back with us? Excellent. Hopefully by now you have some idea of the power of Knockout, so lets try and reproduce this using a SharePoint list as the data source.
The first thing to do is to create our list. For this I am using a simple custom list named ‘My Tasks’ with an additional Completed Yes/No field, and I have pre-populated it with some entries.

Next we need to consider how we are going to create our view within our site. For my example I have chosen to simply create the view as a file in a document library, then link to it using a Content Editor Web Part. However you could do this a number of ways, you could embed your JavaScript into the master page and put your data bindings directly into a page layout, or you could create a visual web part. The possibilities are endless, this is SharePoint after all!
I put the view, along with the script references to Knockout, the ever useful jQuery and my View Model into a single file and save it into the document library. I have also put some style information in too. Here is the view model as I created it:
<style type="text/css">
#taskContainer {
width: 500px;
}
#errorBox {
text-align: center;
border: 1px solid #600;
margin-bottom: 10px;
padding: 10px;
background: #f99;
color: #600;
}
#saveBox {
text-align: center;
border: 1px solid #060;
margin-bottom: 10px;
padding: 10px;
background: #9f9;
color: #060;
}
#taskContainer ul {
padding: 10px;
background: #eaeaea;
padding: 0;
}
#taskContainer ul li {
list-style: none;
padding: 3px;
}
#taskContainer input[type=text] {
width: 390px;
}
</style>
<div id="taskContainer">
<h3>Tasks</h3>
<div id="errorBox" data-bind="text: errorMessage, visible: errorMessage"></div>
<div id="saveBox" data-bind="text: saveMessage, visible: saveMessage"></div>
Add task: <input data-bind="value: newTaskText" placeholder="What needs to be done?"/>
<button data-bind="click: addTask">Add</button>
<ul id="myTaskBox" data-bind="foreach: tasks, visible: tasks().length > 0">
<li>
<input type="checkbox" data-bind="checked: Completed" />
<input data-bind="value: Title, disable: Completed" />
<a href="#" data-bind="click: $parent.removeTask">Delete</a>
</li>
</ul>
You have <b data-bind="text: incompleteTasks().length"> </b> incomplete task(s)
<span data-bind="visible: incompleteTasks().length == 0"> - it's beer time!</span>
<button data-bind="click: save">Save</button>
</div>
<script type="text/javascript" src="../webdevdocuments/knockout.js"></script>
<script type="text/javascript" src="../webdevdocuments/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="../webdevdocuments/ViewModel.js"></script>
Note the addition of some message boxes, the original tutorial does not include them but I felt they were nicer than simple alert boxes.
Also note that, unlike the original, I have not used a form element for the new task area. This is because form elements can cause havoc in SharePoint pages, as the entire page is wrapped in a form for post-backs.
Other than that, our view is mostly unchanged.
Now, on to the JavaScript!
The main differences here lie in the way that we send and receive data from SharePoint. In their examples they use jQuery to query and post data to REST APIs, and while SharePoint 2010 and 2013 do come with their own suite of REST / oData APIs, there are some issues in using them.
- Update and Delete calls can only update or delete one item per call, to do the bulk updating this system indicates we would have to make a request per item. Not ideal!
- Due to the way SharePoint handles concurrency, you have to pass around an eTag so that SharePoint can determine if an item has been updated since requested. While this is great for high concurrency systems with multiple people editing the same data, for a personal tasks list this is not a nice feature.
Luckily REST is not our only option. SharePoint has the wonderful Client Side Object Model (CSOM), which can do all of our loading and saving for us!
Here is my View Model, updated to use the CSOM.
(function() {
function Task(data) {
this.Title = ko.observable(data.Title);
this.Completed = ko.observable(data.Completed);
// An additional reference to store the SharePoint list item id.
this.Id = ko.observable(data.Id);
}
function TaskListViewModel() {
// Data
var self = this;
self.tasks = ko.observableArray([]);
self.newTaskText = ko.observable();
// Additional bindings to use for error and saved messages.
self.saveMessage = ko.observable(false);
self.errorMessage = ko.observable(false);
self.incompleteTasks = ko.computed(function() {
return ko.utils.arrayFilter(self.tasks(), function(task) { return !task.Completed() && !task._destroy});
});
// Operations
self.addTask = function() {
self.tasks.push(new Task({ Title: this.newTaskText(), Completed: false, Id: "New" }));
self.newTaskText("");
};
self.removeTask = function(task) {
self.tasks.destroy(task)
};
self.save = function() {
for (var task in self.tasks()) {
var createdTasks = [];
// Build a request up to send with the CSOM.
if (self.tasks()[task]._destroy) {
// Handle deleted objects
// Deleted items that are marked "new" have never been saved to SharePoint to start with,
if (self.tasks()[task].Id() != "New") {
var listItem = taskList.getItemById(self.tasks()[task].Id());
listItem.deleteObject();
}
} else if (self.tasks()[task].Id() == "New") {
// Handle new objects to be created.
var createInfo = new SP.ListItemCreationInformation();
var listItem = taskList.addItem(createInfo);
listItem.set_item("Title", self.tasks()[task].Title());
listItem.set_item("Completed", self.tasks()[task].Completed());
listItem.update();
// Save a reference to both the SP.ListItem object and the KO Object so we can update
// the latter with the former's ID once the object has been created.
createdTasks.push({
spItem: listItem,
koItem: self.tasks()[task]
});
ctx.load(listItem);
} else {
// The item is neither new nor deleted, handle it as an update.
var listItem = taskList.getItemById(self.tasks()[task].Id());
listItem.set_item("Title", self.tasks()[task].Title());
listItem.set_item("Completed", self.tasks()[task].Completed());
listItem.update();
}
}
// Nowe we have built our request, send it to the server for processing.
ctx.executeQueryAsync(function() {
// Our save was successful. Now we need to itterate through our newly
// created items and ensure that Knockout knows that the ID has changed.
for(var item in createdTasks) {
createdTasks[item].koItem.Id(createdTasks[item].spItem.get_id());
}
// Set our saved message.
self.saveMessage("Saved successfully");
}, function(sender, args) {
// Our save failed, set the error message to show then log the actual error
// to the JavaScript console if it exists.
self.errorMessage("Error updating list items");
if (typeof console != "undefined") {
console.log(args.get_message());
}
});
};
// Load the data from SharePoint
// Get a context to the current site.
var ctx = new SP.ClientContext(_spPageContextInfo.webServerRelativeUrl);
var web = ctx.get_web();
var taskList = web.get_lists().getByTitle("My Tasks");
// Limit our task list to 50 tasks.
var query = new SP.CamlQuery();
query.set_viewXml("<View><RowLimit>50</RowLimit></View>");
var taskItems = taskList.getItems(query);
// Ensure the fields we want to retrieve are returned
ctx.load(taskItems, "Include(ID,Title,Completed)");
// Send our query to the server for processing.
ctx.executeQueryAsync(function() {
var tasks = [];
var taskItemEnumerator = taskItems.getEnumerator();
// Iterate through our retrieved data set and build an array of JSON objects containing
// the relevent properties.
while (taskItemEnumerator.moveNext()) {
tasks.push(
new Task({
Title: taskItemEnumerator.get_current().get_item("Title"),
Completed: taskItemEnumerator.get_current().get_item("Completed"),
Id: taskItemEnumerator.get_current().get_item("ID")
})
);
}
// Update the Knockout tasks array with our data from the server.
self.tasks(tasks);
});
}
$(document).ready(function() { // I use jQuery for this, but you could add an event listener to the document object instead.
EnsureScriptFunc("sp.js", "SP.ClientContext", function() {
ko.applyBindings(new TaskListViewModel());
});
});
})();
So there you go, once you put it all together you should end up with something looking like this:

Cool, huh? It is by no means perfect. The two biggest issues with the implementation as it stands are:
- Currently I am limiting the entire system to only showing 50 records, which is not ideal. This would be best handled by adding some pagination.
- When you hit save it updates every item in the view model, regardless of whether it needs updating or not. This would be resolvable by subscribing to the Task Knockout object and keeping track of which need updating and which do not.
Additionally you could use some jQuery animation and the animated transitions example on the Knockout site to hide the save and error boxes once they are shown. Currently once they are visible they remain visible indefinitely.
Have fun with it, it’s a neat tool and can give quite powerful results.
via Chris on SharePoint http://spchris.com/2013/04/using-knockout-js-in-a-sharepoint-context/
|
SharePoint and Project Server Consultant
This article has been cross posted from spchris.com (original article) |
Mastering the SharePoint 2013 Suite Bar
Today I had a requirement to alter the title in the left hand side of the Suite bar in SharePoint 2013.
This is the bar appearing at the very top of the page, by default containing the word SharePoint on the left and links to things like SkyDrive, the Newsfeed and sites you follow on the right.

In this particular scenario I was interested in changing the word SharePoint to something more meaningful, preferably customisable on a per web basis, or based upon the site collection root web’s title.
My first thought on trying to change it was to look into the master page but I could find no reference to it there, so I did a little digging and found some surprising details.
- The entire bar is a delegate control
- The left hand part of the bar is stored as a property against the Web Application
- There is no way of modifying the right hand links without writing a farm solution in .NET, or adding custom JavaScript to each page to modify the links on the fly.
While these problems are not insurmountable, it left me a little nonplussed, especially the idea that it was not possible to modify the branding uniquely per site collection or per web. However I bravely fought on, as I was really quite determined to get that heading working the way I wished.
My first test was to see what the property against the web application was called, and what it contains by default. Let’s break out our trusty SharePoint Management Console and use the following bit of PowerShell.
$webApp = Get-SPWebApplication -Identity "http://mywebapplicationurl" $webApp.SuiteBarBrandingElementHtml <div class="ms-core-brandingText">SharePoint</div>
This seems straight forward enough, the property is a string containing the HTML for the heading, so let’s go and update it to something else using the same window from our previous example (Re-do the Get-SPWebApplication call again if you have closed it)
$webApp.SuiteBarBrandingElementHtml = '<div class="ms-core-brandingText">My Custom Branding</div>' $webApp.Update()
Note the call to the Update() method, without this your changes will not be applied to the Web Application.
Once done, refresh your page and you will see that your branding has updated accordingly.

So to recap, we can modify this string only on the Web application level, but when we do so, we can put HTML tags in directly, and they will be rendered to the page. Interesting!
Let’s talk about JavaScript
The ECMAScript Object Model first came into its own with SharePoint 2010, and with 2013 has gone from strength to strength. Lets see if we can’t use it to meet our requirement of at least customising the Suite bar based on the current Site Collection’s root web title.
To do this is very simple in code, take a look at the following JavaScript example:
(function() {
// Set a default title to use if we cannot retrieve one.
var defaultTitle = 'SharePoint';
// Ensure that both the DOM and the Object Model are ready for us
document.addEventListener('DOMContentLoaded', function() {
EnsureScriptFunc('sp.js', 'SP.ClientContext', function() {
// Instantiate our renamer.
var t = new titleBarRenamer();
});
});
function titleBarRenamer() {
// Get and load a reference to the root web of the current site collection.
var ctx = new SP.ClientContext.get_current();
var site = ctx.get_site();
var rootWeb = site.get_rootWeb();
ctx.load(rootWeb);
// Send our query off to SharePoint.
ctx.executeQueryAsync(function() {
var title = false;
// Pull the title into a variable.
// Default to the default title if it fails.
try {
title = rootWeb.get_title();
title = title || defaultTitle;
} catch (e) {
title = defaultTitle;
}
// Update our title to our results.
document.getElementById('webTitleContainer').innerHTML = title;
}, function() {
// If all else fails, set the title back to our default.
document.getElementById('webTitleContainer').innerHTML = defaultTitle;
});
}
})();
That’s cool, wherever this code is run it will set an HTML element with an ID of ‘webTitleContainer’ to the root web’s title.
Putting it all together
So how do we combine these two together?
Again this is pretty simple, take a look at the following PowerShell:
Add-PSSnapIn Microsoft.SharePoint.PowerShell
$containerCode = @"
<div id='webTitleContainer' class='ms-core-brandingText'></div>
<script type='text/javascript'>
(function() {
// Set a default title to use if we cannot retrieve one.
var defaultTitle = 'SharePoint';
// Ensure that both the DOM and the Object Model are ready for us
document.addEventListener('DOMContentLoaded', function() {
EnsureScriptFunc('sp.js', 'SP.ClientContext', function() {
// Instantiate our renamer.
var t = new titleBarRenamer();
});
});
function titleBarRenamer() {
// Get and load a reference to the root web of the current site collection.
var ctx = new SP.ClientContext.get_current();
var site = ctx.get_site();
var rootWeb = site.get_rootWeb();
ctx.load(rootWeb);
// Send our query off to SharePoint.
ctx.executeQueryAsync(function() {
var title = false;
// Pull the title into a variable.
// Default to the default title if it fails.
try {
title = rootWeb.get_title();
title = title || defaultTitle;
} catch (e) {
title = defaultTitle;
}
// Update our title to our results.
document.getElementById('webTitleContainer').innerHTML = title;
}, function() {
// If all else fails, set the title back to our default.
document.getElementById('webTitleContainer').innerHTML = defaultTitle;
});
}
})();
</script>
"@
$webApp = Get-SPWebApplication -Identity "http://mywebapplicationurl"
$webApp.SuiteBarBrandingElementHtml = $containerCode
$webApp.Update()
As you can see, the bulk of this is our JavaScript code from above, with the addition of a div for our title to end up in and the required script tags for the code.
Once you have this, you can save it as a .ps1 file and execute it in PowerShell. You may need to set the execution policy to RemoteSigned before you are able to do this.
And there you go. You could do some fairly interesting things with this, particularly as it takes effect right across the entire web application. You also have to be aware that this is stored within the Configuration Database rather than the Content Database, so will not be migrated if you move content databases from one farm to another.
via Chris on SharePoint http://spchris.com/2013/04/mastering-the-sharepoint-2013-suite-bar/
|
SharePoint and Project Server Consultant
This article has been cross posted from spchris.com (original article) |
SharePoint Evolution Conference–things to remember / learn
So today was the last of the 3 day Evolution conference that has been running at the QEII centre in Westminster. http://www.sharepointevolutionconference.com/
The whole conference has been fantastic with a very good selection of speakers
My highlights were Andrew Connell’s (http://www.andrewconnell.com/blog) Talk on single page applications or SPA’s. I am sure this will appear on his blog soon
the other was Chris O’Brien’s (http://www.sharepointnutsandbolts.com) talk on SharePoint App’s and why they make a lot of sense. He did a mock conversation, which i have gone through myself, on how SharePoint hosted apps are not powerful enough and we want to use c# in a provider hosted app which then leads down the rabbit whole of either Azure costs or the maintenance and DR of our own server. This all leads back to the question “What was wrong with SharePoint hosted apps anyway?”
The big take away from the conference was that there is not that much you cannot do with SharePoint hosted app if you really think about it. So we need to get outside of our nice comfy C# sofa and start learning JavaScript.
By this I mean REALLY learn JavaScript and not the hacking about we have been doing for the last few years. This means that we need to treat JavaScript as a real language (I realise that it is) and write the code properly using proper patterns. So that when we look at it all in a weeks time or have to show it in a demo it will make sense and that other people can actually support the code.
This seems daunting given the limited tooling that exists for JavaScript, especially when we think how lucky we are with the debugging tools we get for .NET code.
Someone, I forget who, came up with a brilliant analogy for what JavaScript is and it was “JavaScript is assembly language for the web”
When you think about it this makes a lot of sense. In the end the C# code we write is assembly when it finally gets executed.
This means that to write good JavaScript we need to use extra tools and frameworks that other people have provided.
The obvious one is jQuery which i now think is synonymous with JavaScript. If you are not using jQuery in you JS then you are just making life difficult.
The other frameworks that we should look to use and the main reason for this post are the following:
- TypeScript (http://www.typescriptlang.org/)
- TypeScript is a typed superset of JavaScript that compiles (YAY) to plain JavaScript.
- This looks very easy to write, especially as a C# person
- I am not sure there is a reason why you wouldn’t use this for everything.
- Modernizr (http://modernizr.com/)
- Modernizr is a JavaScript library that detects HTML5 and CSS3 features in the user’s browser.
- Knockout (http://knockoutjs.com/)
- Implements a MVVM pattern
- Durandal (http://durandaljs.com/)
- a Single Page App framework
- Bootstrap (http://twitter.github.io/bootstrap/index.html)
- Makes pages look nice
- Font Awesome (http://fortawesome.github.io/Font-Awesome/)
- Made for Bootstrap and provides icons and animations all through CSS and JS
- This is very cool
- LinqJS (http://linqjs.codeplex.com)
- I have actually used this in an app I have been working on and it is very useful
- Toastr (https://github.com/CodeSeven/toastr#readme)
- SharePoint style non-blocking notifications
Using some or all of the above does make the learning a little more daunting but after seeing the results this is clearly the way to move forward. When you see how little code you actually have to write it is just amazing what can be done.
As well as the extra frameworks the other key JavaScript things to learn are namespaces and promises.
Something else to note. Make sure you have the latest version of the SharePoint development tools installed and make sure you have the “Web Essentials 2012” extension installed as well. This will make life easier.
Please do not ask me for any code samples or video’s of the event as I do not have any and if I did I wouldn’t be allowed anyway.
via Buzz Blog http://paulbuzzblog.wordpress.com/2013/04/18/sharepoint-evolution-conferencethings-to-remember-learn/
|
Paul is a an expert SharePoint and Project Server developer and is responsible for designing and implementing custom solutions on client systems using the latest SharePoint and .NET technologies.
Paul has extensive experience with SharePoint systems across all sizes of implementation, ranging from small to large farms and has an excellent understanding of all the elements of SharePoint. This article has been cross posted from paulbuzzblog.wordpress.com (original article) |
ForeFront Identify Manager Client
The User Profile Service is an incredibly powerful part of SharePoint. When used correctly it vastly improves the experience of your users by adding a real sense of personal ownership of your sites and their content.
Unfortunately like any complex system, identifying and fixing issues can be a painful process. There is minimal information provided in the site and almost none regarding the connection between the service and Active Directory.
In SharePoint 2010, the user profile sync is controlled by the ForeFront Identity Manager service, this is a Windows service that handles the connections between Active Directory, any BDC connections defined and the SharePoint site collections and sites.
The workings of the service are somewhat impenetrable, but if you wish to see what it is doing and follow the sync process, it comes with a nice GUI interface.
This can be run directly on the server from the path X:\Program Files\Microsoft Office Servers\14.0\Synchronization Service\UIShell\miisclient.exe
When running during a sync you can see any issues encountered such as permissions problems.
via Chris on SharePoint http://spchris.com/2013/04/forefront-identify-manager-client/
|
SharePoint and Project Server Consultant
This article has been cross posted from spchris.com (original article) |
Adding a default image for remote served images
Quite often when working with data within a SharePoint environment, we wish to link the data to images hosted within a third party system.
This is often seen in the form of staff pictures that are hosted on a third party HR platform rather than within Active Directory, so the User Profile service images are of no use to us.
Unfortunately there is no real way of detecting if the image on the third party system actually exists, which means that rather than seeing the nice friendly missing profile image, you get a horrible missing image box.
This can be overcome with some simple JavaScript:
(function(){
var defaultImage = "/_layouts/15/images/PersonPlaceholder.200x150x32.png";
var profileImage = document.createElement("img");
// Define an error handler, this will fire if there is an
// error loading the URL for the staff image and redirect
// it to the missing user profile image in the hive.
profileImage.onError = function() {
this.src = defaultImage;
}
// Point the image element to our staff images then attach
// it to the DOM.
profileImage.src = "http://hr/staffimages/" + employeeId + ".png";
// Note that the employeeId variable referenced above is to be
// provided externally and is beyond the scope of this post.
var container = document.getElementById("profileImageContainer");
if (container) {
container.appendChild(profileImage);
}
})();
With this code in place any missing images will automatically serve the default image instead.
This method will work anywhere you need to serve images and have no way of detecting if the image to be served exists or not.
|
SharePoint and Project Server Consultant
This article has been cross posted from spchris.com (original article) |
Custom Navigation in a SharePoint Hosted App in SharePoint 2013
I have been playing round with a SharePoint hosted app to learn about how it all works. This has been going well but i ran into more problems than i though i would when trying to implement a menu for my App.
If you are using the auto hosted or developer hosted app model then this is easy (ish) you use the new chrome control, create you menu links in JS and you are done. MS have some documentation on this that works great.
But i am creating a SharePoint hosted app. So this causes a few “problems”. The first one being that if i add a chrome control to my page i get 2 menu which is just silly.
Google wasn’t much help although i did find this post here
http://www.intrateam.com/gb/blogpost/sharepoint-2013-app-master-page
which pointed me in the right direction.
My first issue was actually getting hold of a copy of the master page that app’s use. I could not work out how to do this only having an office 365 tenant. In the end I grabbed the app.master and default.master from the GLOBAL folder in the hive on a on premise install.
The first thing i worked out is that the reference to “~masterurl/default.master” seems to translate to the app.master from the GLOBAL folder in the HIVE.
So i made a copy of the app.master and added it to my project. Changing the elements.xml file as per the post linked above.
I also changed the link in my ASPX pages from ~masterurl/default.master to ~site/_catalogs/masterpage/<Name of my master page>.master
This assumes you have you elements file setup like
While doing this I noticed that there is a content place holder with an id of PlaceHolderLeftNavBar. This isn’t hidden.
So I added
into my Default.aspx page and loaded it into SharePoint.
Bingo I have a menu in the standard place in SharePoint. What is also nice is that if the user hits the “Focus on Content” button then the menu gets hidden.
But wait a minute we just setup our own master page, what was the point? Well at the moment you do not need you own master page but this does now mean you could move any of the other Content Place Holders to put a menu in a different location. It also helps get rid of some of the warnings in visual studio as it now knows the master you are using.
I do have an issue that have to copy my <asp:menu> content onto all the pages. I tried using the XML data source but this appears to not be supported but there may be another way to store the menu logic centrally.
The next problem to solve was that we need to pass the query string values around the system, so these have to be added to the menu.
This can be done with some simple jQuery.
Just make sure this will run on all of the pages and your querystring values will be maintained between page loads
Sorry for the images but my code plugin was not working.
Hope this helps someone.
via Buzz Blog http://paulbuzzblog.wordpress.com/2013/02/10/custom-navigation-in-a-sharepoint-hosted-app-in-sharepoint-2013/
|
Paul is a an expert SharePoint and Project Server developer and is responsible for designing and implementing custom solutions on client systems using the latest SharePoint and .NET technologies.
Paul has extensive experience with SharePoint systems across all sizes of implementation, ranging from small to large farms and has an excellent understanding of all the elements of SharePoint. This article has been cross posted from paulbuzzblog.wordpress.com (original article) |









You must be logged in to post a comment.