Wednesday, 25 June 2014

SharePoint 2010/2013: Enable-SPFeature Error (Failed to create receiver object from assembly)

So I had another lovely error recently, I had a wasp which had some external assemblies, I accidentally forgot to add one to the package manifest and when it deployed I got:

Enable-SPFeature : Failed to create receiver object from assembly "ASSEMBLYNAME, Version=1.0.0.0, Culture=neutral, PublicKeyToken=eaf1b7820cf1
fb30", class "CLASSNAME" for feature "FEATURENAME" (ID: 08284496-c68e-4f6c-befe-5777d14ad74b).
: System.ArgumentNullException: Value cannot be null.

So I instantly realised I was being silly and added the reference to the package manifest, packaged it, cabbed it, The Dll was in there, Great! I deployed the wsp and tried The deployment script again, it failed on the same point, I checked the gas, it was there alright, everything was looking as it should, but I couldn't activate the feature.

What was wrong? I tried deploying once more, did an observer for good measure and still nothing.

I then thought I would reopen the PowerShell window and try once again on the off chance that something was borked, so I restarted it, ran the script and it worked!

For some reason, the PowerShell session had cached the old error response from the feature activation, I don't know why but that's what it seemed to do.

Rule of thumb for the future, open a new ps session if you get an unexplained issue with a ps script

Tuesday, 17 June 2014

SharePoint 2013/365: Get current page properties/metadata with REST

Scenario

Retrieve the current page layout name via the page metadata on Office 365, this approach must be maintainable, flexible and not have any extra costs


Investigation

There are a number of ways to get the current page metadata with full trust code, simply using the SPContext.Current.ListItem allows you to get the collection, of course that wont work going forward with O365.

The most flexible way of working with any form of SharePoint object in O365 is to use JavaScript, thankfully there are a number of ways that can be done:

  • Use the JSOM - there are a number of blog posts currently on how to return the current items with JavaScript
  • Use Search - mapping properties and matching the search result by url is also a possibility, this is fast and allows easy optimisation
  • Use REST - there isn't much on this approach out there, it looks like a pretty obvious choice so i decided to try this approach.

Solution

The architectural approach is as such:
On Page Load

  1. Using the current context object data form a rest query
  2. Query the ListItem REST endpoint
  3. Get the page fields out of the returned data

Here is the finished codeblock:

Saturday, 7 June 2014

Powershell | Get SharePoint 2010 Custom Error and Access Denied pages

A simple script, run this on any SharePoint server in a farm and it will return all set error and access denied pages for all web applications:

Scenario: Audit of farm setup

Script:
<#
.Synopsis
   Gets all set error and access denied pages for all webapps in the current farm
.EXAMPLE
   get-sperrorpages
#>

function get-sperrorpages()
{
    $snapin = Get-PSSnapin | Where-Object { $_.Name -eq 'Microsoft.SharePoint.Powershell'}
    if ($snapin -eq $null)
    {      
        Write-Host "Loading SharePoint Powershell Snapin"
        Add-PSSnapin "Microsoft.SharePoint.Powershell"
    }

    Start-SPAssignment -global
get-spwebapplication | ForEach-Object {
Write-Host "Web Application: $_" -foregroundcolor Green
Write-Host " - Error Page Set:" $_.GetMappedPage([Microsoft.SharePoint.Administration.SPWebApplication+SPCustomPage]::Error)
Write-Host " - Access Denied Page Set:" $_.GetMappedPage([Microsoft.SharePoint.Administration.SPWebApplication+SPCustomPage]::AccessDenied)
Write-Host ""
}

    Stop-SPAssignment –global
}

Output:
PS C:\dev\scripts\audit> .\get-errorpages.ps1
Web Application: SPWebApplication Name=intranet.devnet.local
 - Error Page:
 - Access Denied Page:

Web Application: SPWebApplication Name=extranet.devnet.local
 - Error Page: /_layouts/Devnet.Local/Error.aspx
 - Access Denied Page: /_layouts/Devnet.Local/AccessDenied.aspx

Web Application: SPWebApplication Name=internet.devnet.local
 - Error Page: /_layouts/Devnet.Local/Error.aspx
 - Access Denied Page: /_layouts/Devnet.Local/AccessDenied.aspx

Web Application: SPWebApplication Name=edit.internet.devnet.local
 - Error Page: /_layouts/Devnet.Local/Error.aspx
 - Access Denied Page: /_layouts/Devnet.Local/AccessDenied.aspx

As you can see from that execution my farm has four web applications, three are publicly accessible so i have set custom error and access denied pages, the fourth is internal only so there is no need for any custom branded pages

Powershell | Get all active RDP sessions for a username

For any IT admin/dev, the ecosystem involves connecting and managing multiple remote desktop sessions to different servers and computers, this can get very cumbersome to maintain and make sure you are disconnecting, many of you probably use mRemote or Remoter Desktop Connection manager to help you manage all of your different connections (if you don't, you really should, its a godsend to be able to categorise and organise all of your connections).

The problem comes in when you have an unexpected crash or an issue and either your computer crashes or the RDP app crashes, you can loose track of which connection you have open, that can lead to leaving sessions open taking up valuable resources or hogging sessions other people need.

That problem can be solved with this powershell script:

<#
.Synopsis
   Gets all server entries from an RDPC connection file and shecks for any open sessons
.EXAMPLE
   get-rdpsessions "C:\resources\rdpSettings.rdg" 'benjamin.dev'
#>
function get-rdpsessions()
{
    Param(
        # link to the RDPC connections file
        $rdcManagerFile,
        # default username to use incase the conneciton entry doesnt have a username set, this should be without the domain
        $defaultUsername)
    
    Select-Xml -XPath '//server' -Path "C:\resources\rdpSettings.rdg" | %{
        $server = $_.node.name
        $userName = $defaultUsername
        if ($_.Node.logonCredentials.HasChildNodes)
        {
            $userName =  $_.Node.logonCredentials.userName
        }
    
        $queryResults = (qwinsta /server:$server | foreach { (($_.trim() -replace “\s+”,”,”))} | ConvertFrom-Csv)
        $queryResults |  %{
            if ($_.SESSIONNAME -eq $userName)
            { 
                write-host 'User:' $_.SESSIONNAME 'is active on' $server -ForegroundColor green
            }
        }
    }
}

get-rdpsessions "C:\resources\rdpSettings.rdg" 'benjamin.dev'

This will output something like the following:

PS C:\Users\benjamin.dev> C:\Users\benjamin.dev\SkyDrive\Code\PS\get-rdpsessions.ps1
User: benjamin.dev is active on devnet-ad.devnet.local
User: benjamin.dev is active on devnet-sql.devnet.local
User: benjamin.dev is active on devnet-sp14.devnet.local
User: benjamin.dev is active on devnet-sp15.devnet.local

This script can be easily modified to integrate with whatever system you use, for example, if you run a development house or consultancy, this could be modified to run through all servers for all users at 4:50 in the afternoon every day and email anyone who has an active session to remind them they need to log off

Saturday, 15 June 2013

SP2013: Calling SP.ClientContext for an anonymous user causes "object doesnt support this method"

Good Morning!, and what a wonderful Saturday morning it is, the scent of strawberries are in the air!

So I've been working on a public facing 365 website lately and making a few customisations, one request I received was to have the ability for users to add an item to a "mailing list" list, and they wanted maximum portability, so I thought, perfect for the JS-CSOM!

Such a simple idea, add an item to a list, just needs one field adding in very straight forward, or so I thought, so I created the js function and hooked it up, for auth users it worked like a charm but I ran into a problem when accessing it anonymously, I got the error "object doesn't support this method" when calling a new "SP.ClientContext", its a straight forward error meaning it cant find the function in any loaded libraries, so I thought right, gotta be SP not loading sp.js, simple, add a script link in there... no luck the same error came up, then I thought right, gotta be that the method is running before SP.js is properly loaded so I used "ExecuteOrDelayUntilScriptLoaded()" to encapsulate my code and make sure it runs after the code is loaded, published the js and checked it again... no luck!

So it has to be sp.js not loading correctly, remembering the SoD SharePoint is so fond of I then tried the trusty "SP.SOD.executeFunc('sp.js', 'SP.ClientContext', addToMailingList)", and it worked perfectly!

Code:

$("#ben-mailinglist-confirm").click(function () {
    SP.SOD.executeFunc('sp.js', 'SP.ClientContext', addToMailingList)
});

function addToMailingList(metadata) {
    var metadata = $("#ben-mailinglist-email").val();
    if (metadata != "") {
        var clientContext = new SP.ClientContext("/");
        var list = clientContext.get_web().get_lists().getByTitle('MailingList');

        var itemCreateInfo = new SP.ListItemCreationInformation();
        var listItem = list.addItem(itemCreateInfo);
        listItem.set_item('Title', metadata);
        listItem.update();

        clientContext.load(listItem);
        clientContext.executeQueryAsync(
       Function.createDelegate(this, function () { $(".ben-mailinglist-success").show(); $(".ben-mailinglist-form").hide(); }),
       Function.createDelegate(this, function () { $(".ben-mailinglist-failure").show(); })
   );
    }
    else {
        // validation
    }
}

Wednesday, 5 June 2013

SharePoint Online (Office365): Profile images broken / not coming up

Today I had a brilliant issue, I have a 365 authenticated site that uses profile images, these are surfaced in the "ContactFieldControl", I added profile pictures to the profiles and navigating to the mysites worked fine and the profile picture on the contactfieldcontrol worked too but then I opened it in a different browser and the profile image was broken in the webpart, that was strange I thought maybe an MS Job had deleted it so I went to the mysite and the image was there!, so I navigated back to the site and once again it shows up

So it appears if the image is loaded through the contact field webpart it shows as a broken image but if you navigate to the mysite or the image directly it worked

So how to fix it?

this issue appears to be in the first load of an image using the direct path to the image, i was hoping a cache clear or an update of the profiles would fix but but to my knowledge there is no OOTB way of fixing this so i resorted to using a dev's best friend, jQuery!

First I tried running a piece of jQuery on the page to preload the image using an ajax call but that didn't work

Then I noticed something interesting, whilst fiddling the page I saw that it also calls the following URL "/_layouts/15/userphoto.aspx?size=S&url=********", this URL links directly to the image, I thought this was interesting so I wrote a piece of jquery to take the source of the image and transform it into a URL like the one above and hey presto it worked!!

Code:
function checkProfileImages() {
    $(".ben-imgborder img").attr("src", "/_layouts/15/userphoto.aspx?size=S&url=" + $(".ben-imgborder img").attr("src"))
}

Monday, 6 May 2013

Installing SharePoint Designer 2013 Issue: You must uninstall office pro plus first....

I came across this issue a while back while setting up a new development environment, i had installed VS2012, Office 2013 and was trying to install SPD 2013, I ran the setup and during the preflight checks it failed with the following message:


Why? why on earth must I un-install office to install SPD?, well as it turns out Office 2013 added a registry key(typical...) that the preflight checks, this key looks as though its not needed but is still added, possibly something that was meant to be deleted but wasn't.

So to fix it!:
Open regedit.exe (Run -> regedit -> Enter)

Find and delete the following key: HKEY_CLASSES_ROOT\Installer\Products\00005102110000000100000000F01FEC 
Note: if you can't find the above key, just find the key with prefix: 00005102, and suffix: F01FEC, and delete it. (Before deleting it, backup this key by exporting it, never a good idea to mess with the registry before backing up the original state).

Now that that's gone, try the installer again, hopefully it should now let you install SPD unhindered!