Using jQuery to give your user a “check all” option in the UI

Say you have a table where each row contains a check-box and you want to be able to check/uncheck every single check-box based on some action the user does. Using jQuery this is very easy. Assume we have the following HTML:

        <table>
            <thead>
                <tr>
                    <th><input type="checkbox" id="HeaderCheckbox"/></th>
                    <th>Name</th>
                </tr>
            </thead>
            <tbody>
                <tr><td><input type="checkbox"/></td><td>Tea</td></tr> 
                <tr><td><input type="checkbox"/></td><td>Coffee</td></tr> 
                <tr><td><input type="checkbox"/></td><td>Cola</td></tr> 
           </tbody>
        </table>

Then the following jQuery snippet will transform the HeaderCheckbox into a control that automatically checks or unchecks all the other check-boxes:

$(document).ready(function () {
    //Setup an eventhandler that fires 
    //when the user clicks on a control whose id = HeaderCheckbox
    $('#HeaderCheckbox').click(function (eventobject) {
       //the DOM element that triggered the event
       var $this = $(this);                       
       //Determine what the requested state is. 
       //I.e is the headercheck box checked or unchecked?
       var checked = $this.prop('checked');
       //Find each checkbox element below a <tr> and set 
       //it to the requested sate       
       $("tr :checkbox").prop('checked', checked) 
    })
})

If you’re using some framework with data-binding (e.g. Knockout) then its way better to simply bind each check box to a property of the the View Model and just set that property. The HTML would look like this

        <table >
            <thead>
                <tr>
                    <th><input type="checkbox" id="HeaderCheckbox" /></th>
                    <th>Name</th>
                </tr>
            </thead>
            <tbody data-bind="foreach: Drinks">
                <tr>
                    <td><input type="checkbox" data-bind="checked: $data.Selected"/></td>
                    <td data-bind="text: $data.Name"></td>
                </tr>
            </tbody>
        </table>

And the associated JavaScript would look like this:

var DrinkViewModel = function () {
    this.Name           = ko.observable('');
    this.Selected       = ko.observable(false);
}

var viewModel = function () {
        var self = this
        //Holds all the drinks we want to list in the table
        this.Drinks = ko.observableArray();
}

$('#HeaderCheckbox').click(function (eventobject) {
    var $this = $(this);
    var checked = $this.prop('checked');
    $.each(MyViewModel.Drinks(), function (index, theDrink) 
    { 
        theDrink.Selected(checked) 
    })
})
Knockout.js logo

knockout.js: Your observable isn’t seeing changes made to text controls until they lose focus

knockout is great library that’s easy to use. One thing I noticed is that changes made in text-controls are only propagated to the observable once that control loses focus.

If you want changes in a text-control to immediately be reflected in your observable, then avoid the value binding and use the textInput binding like below:

<input data-bind="textInput: Name" type="text" value="" />
<script type="text/javascript">
    $(document).ready(function () {
        viewModel = ViewModel()
        ko.applyBindings(viewModel);

    });
    function ViewModel() {
        var self = this;
        self.Name = ko.observable("");
    }
</script>

Entity Framework and the error: Cannot attach the file ‘xxx.mdf’ as database ‘xxx’

Say you’re working on a project that’s using Entity Framework to manage the database storage in a SQL Server Express installation. If you delete the .mdf file you’ll keep on getting the error” Cannot attach the file 'xxx.mdf' as database 'xxx'.

To solve it, in visual studio go to the Package Manager console and run the following commands:

sqllocaldb.exe stop v11.0
sqllocaldb.exe delete v11.0
Update-Database

Setting up MySites in Central Admin and the error “An error has occurred in the claim providers configured from this site collection.”

I was busy configuring the User Profile Service through the link “Setup My Sites”. I couldn’t change anything on that page as it listed NT Authority\Authenticated Users; c:0(.s|true; in the box for “Read Permission Level” and was already printing the error message No exact match was found. using the peoplepicker through the little “Browse” icon showed the error An error has occurred in the claim providers configured from this site collection.

If you run into this error then you can try 2 things:
1) Configure an Alternate Access Mapping for Central Admin
2) Point your webbrowser directly at the server instead of the FQDN that resolves to it

Assume Central Admin running on port 555 of server xxxSP01 and DNS is configured to resolve portal.contoso.com to the server. When you access Central Admin through https://portal.contoso.com:555, then you’ll encounter this problem. If you use https://xxxSP01:555, then there’s no problem.

What to do when SharePoint managed account passwords are already expired

Suddenly your SharePoint installation stops working…
You’re seeing HTTP 500 errors even on Central Admin…
The ULS logs contains errors such as:

Unknown SQL Exception 0 occurred. Additional error information from SQL Server is included below. The target principal name is incorrect. Cannot generate SSPI context

This can happen when the various service accounts for SharePoint are no longer able to authenticate due to expired passwords. By default SharePoint wont proactively change that password even if AD policies require them to be changed. And, as admin you don’t even know what the old password is.

Firstly, you can see which service accounts are used by SharePoint using PowerShell’s Get-SPManagedAccount cmdlet. Use the ‘Active Directory Users and Computers’ tools to reset the password of those accounts to something you know.

Secondly, get Central Admin up-and-running:
RDP to the SharePoint Server
Open IIS Manager
Find the Application Pool that hosts Central Admin and open its advanced settings.
The ‘Identity’ row, lists which managed account is used for Central Admin. Hit the … button and enter the account and its new password
Restart the application pool
Central Admin should be available now. If not, try an IISRESET

Finally, use Central Admin to update the service accounts
Go to SharePoint Central Administration->Security->Configure managed accounts.
Click Edit on each account and do the following:


  1. Select “Change password now”

  2. Click “Use existing password”

  3. Type the password, and then click OK

  4. You might want to enable the option “Enable automatic password change” now. As this will avoid the problem from occurring again

Creating site collections in their own content database

Central Admin doesn’t allow you to choose in which Content Database to create a new Site Collection. With PowerShell its easy:

New-SPSite  `
    –ContentDatabase "WSS_Content_SecondDB" `
    -Url "https://portal.contoso.com/sites/MyNewSiteCollection" `
    -Template "STS#0"  
    -Name "SitecCollectionTest" `
    –Description "A Site Collection created in a separate Content Database"  `
    -OwnerAlias "CONTOSO\Gerben" `
    –OwnerEmail "gerben@contoso.com" `

Migrating from YouTrack to JIRA

Recently I wanted to migrate about 400 issues and 350 attachments from a YouTrack OnDemand instance to a JIRA InCloud instance. JIRA doesn’t provide an importer that is compatible with YouTrack, so I coded a quick .Net C# application that migrated the data for me.

I started with quick list of my must- and nice-to-haves:

Must-haves
Entity Information to migrate
Projects Name
Issues Title, description, state and priority
Issues Attachments belonging to the issue
Issues Comments, including date and author
Nice-to-haves
Entity Information to migrate
Issues Reporter and assignee
Issues Tags
Projects and Issues Components
Issues Affected and fixed version information
Issues Relationship between issues (duplicate/relates-to etc etc)
Issues Historical information such as when the issue was transitioned from one state to another

I didn’t want to migrate or convert between YouTrack’s WIKI formatting used in issue description/comments and the JIRA way of formatting those fields. In fact it turns out that these formats are very similar, so that was a pleasant surpise when I was finished.

The first choice…REST or Import plugin?

The first choice I had to make was between JIRA’s REST API or JIRA’s JSON Import plugin. I opted for the plugin because the REST-API tends to completely ignore information such as state, dates and users. Being able to control the content of these fields is really crucial for data migration.

Getting the issues out of YouTrack…YouTrack and YouTrackSharp challenges

I already blogged how to get issues and attachments out of YouTrack, so there weren’t too many surprises:

  • YouTrackSharp won’t return the description of a project
  • YouTrackSharp won’t return an issue’s tags (a.k.a. labels) or comments. You need to call IssueManager.GetIssue() for each issue returned from instead of IssueManager.GetAllIssuesForProject()
  • The fieldnames and their types are different between the IssueManager.GetIssue() and IssueManager.GetAllIssuesForProject() calls
  • Version numbers associated with an issue in fields affectedVersion and FixedVersion are stored as a CSV string, not as a ICollection in YouTrackSharp
  • The text of a comment is usually returned as the .Text member of the dynamic object. However, I’ve seen a few issues where its returned as a .text member. In C# this difference in case is significant. I used the following approach to handle both cases:
    try {
        ExportComment.Text = Comment.Text;
    }
    catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException) {
        ExportComment.Text = Comment.text;
    }
    

Importing the issues into JIRA

JSON Import documentation

The JSON structure that JIRA can import is documented on Overview and details.

The import plug-in basically does the following. Firstly it will create all the users listed in the JSON. Secondly it will create the projects, components and versions. Thirdly it will import the issues into each project. If the issue contains an attachment, it will download it from the specified URL/webserver into your JIRA instance’s datastore and attach it to the issue. Finally it will create any links between the issues.

Don’t worry about the license limit on the number of users. The plug-in will create them all, but any user above your license limit wont be granted access to the JIRA application and wont count towards the license. Also, after the import is complete, its fine to delete the attachments from your URL.

So the requirements for my application were:

  1. Be able to list all distinct users that are referenced somewhere in an issue, comment or attachment
  2. Per project, be able to list all distinct components that are referenced somewhere in an issue
  3. Per project, be able to list all distinct versions that are referenced somewhere in an issue field
  4. Be able to list all distinct relationships between issues. These relationships could in theory be cross-project
  5. Per issue, be able to translate YouTrack’s values for fields into JIRA’s equivalent. Specifically the following:
    • YouTrack’s state field to JIRA’s state and resolution
    • YouTrack’s default 4 priority values to JIRA’s default 6 priority values
    • YouTrack’s name for Issue types to JIRA’s name for Issue types
  6. Be able to translate YouTrack’s usernames to JIRA’s usernames. I had a few users that existed in both systems with slightly different usernames
  7. Be able to place the downloaded attachments from YouTrack on webserver that JIRA can access and write that URL into the JSON datastructure.

Jira JSON import gotcha’s

  • You are not allowed to supply the resolved date in the issue object. created and updated are fine though
  • The JSON import documentation doesn’t make it clear that you can control what the key of an imported issue should be using the key property of an issue object. If you don’t supply this property, then JIRA will simply give each issue a key equal to the order in which its listed in the JSON. This will almost always be a problem as its very common for issues to in YouTrack to have been deleted
  • YouTrack can handle 1 issue containing multiple attachments with the same name. The JIRA JSON import will throw the following exception and will stop importing more attachments for the issue. I only had 1 issue that had 2 attachments with the same name in YouTrack and I removed one of them
    com.atlassian.jira.plugins.importer.external.ExternalException: com.atlassian.jira.web.util.AttachmentException: Could not save attachment to storage: java.io.FileNotFoundException: /data/service/j2ee_jira/catalina-base/temp/jira-importers-plugin-downloader-2621864330368162205.tmp (No such file or directory)
    	at com.atlassian.jira.plugins.importer.imports.importer.impl.ExternalUtils.attachFile(ExternalUtils.java:354)
    	at com.atlassian.jira.plugins.importer.imports.importer.impl.DefaultJiraDataImporter.createIssue(DefaultJiraDataImporter.java:944)
    	at com.atlassian.jira.plugins.importer.imports.importer.impl.DefaultJiraDataImporter.importIssues(DefaultJiraDataImporter.java:764)
    	at com.atlassian.jira.plugins.importer.imports.importer.impl.DefaultJiraDataImporter.doImport(DefaultJiraDataImporter.java:390)
    ...
    Caused by: com.atlassian.jira.web.util.AttachmentException: Could not save attachment to storage: java.io.FileNotFoundException: /data/service/j2ee_jira/catalina-base/temp/jira-importers-plugin-downloader-2621864330368162205.tmp (No such file or directory)
    	at com.atlassian.jira.issue.managers.DefaultAttachmentManager.createAttachmentOnDisk(DefaultAttachmentManager.java:473)
    ...
    
  • YouTrack and Jira have a different interpretation of the direction of the Duplicate issue links.
                        
                        Assume that in YouTrack the follwing link exists: YouTrack: XXX-27 is duplicated by XXX-1
                        Then the IssueLink object will look like this:
                                SourceId	"XXX-27"	string
    		                    TargetId	"XXX-1"	string
    		                    TypeInward	"duplicates"	string
    		                    TypeName	"Duplicate"	string
    		                    TypeOutward	"is duplicated by"	string
                        If we translate that to JIRA's JSON format 
                        {
                          "name": "Duplicate",
                          "sourceId": "XXX-27",
                          "destinationId": "XXX-1"
                        },
                        Then JIRA will report that XXX-27 duplicates XXX-1. Ergo,for the Duplicate type, we need to swap Source and Target
                        
    
  • If you have a private installation of JIRA, then you can control the format of the project key. However, in OnDemand instances, the key is restricted to only upper- and lowercase letters, you cant change that. I had 2 projects in YouTrack whose key contained numbers. I could have written a few lines of code to replace the numbers with some letters, but in my case it was far easier to modify the project in YouTrack and remove the numbers.

Backing up Azure VMs with PowerShell

When experimenting in my lab environment I want to create a backup of the virtual machines. The following PowerShell script will do just that. I assume you’ve already setup your PowerShell to work with azure by doing the following:

  1. Setup the Azure PowerShell cmdlets (see: https://azure.microsoft.com/en-us/downloads/)
  2. imported your Publish Settings File (see Get-AzurePublishSettingsFile and Import-AzurePublishSettingsFile)
  3. Defined which storageaccount to use with Set-AzureStorageAccount
  4. Shutdown all the Virtual Machines
Import-Module Azure -ErrorAction Stop
$backupContainerName = "backups"
function Backup-Lab
{
    $vms = Get-AzureVM

    if (! (Get-AzureStorageContainer -Name  $backupContainerName -ErrorAction SilentlyContinue) )
    {
        New-AzureStorageContainer -Name $backupContainerName -Permission Off
    }

    foreach ($vm in $vms)
    {
        Write-Host "backing up machine: " $vm.Name
        $disks = @()
        $disks +=  $vm | Get-AzureOSDisk
        $disks +=  $vm | Get-AzureDataDisk

        foreach($disk in $disks)
        {
            $DiskBlobName = $disk.MediaLink.Segments[-1]
            $DiskContainerName = $disk.MediaLink.Segments[-2].Split('/')[0]
            Write-Host "disk: " $disk.DiskName
            #Start an asynchronous copy of the VHD to our backup destination
            Start-AzureStorageBlobCopy -SrcContainer $DiskContainerName -SrcBlob $DiskBlobName -DestContainer $backupContainerName -DestBlob $DiskBlobName
            #Wait for the copy to complete
            Get-AzureStorageBlobCopyState -Blob $DiskBlobName -Container $DiskContainerName -WaitForComplete
        }
    }

Making your SharePoint site available outside your own domain

A while back I created a small SharePoint test lab using Virtual Machines on Azure. I had a Domain Controller, a SQL Server and a SharePoint server. SharePoint was configured to host on portal.contoso.com. All machines were part of the same Virtual Network I defined in Azure.

I was able to access the sites from each of the machines within the Vnet. However I didn’t want to RDP into a server machine just to access my SharePoint sites. After setting up the azure end-point I eagerly entered the URL xxxxxxxx.cloudapp.net but was presented with a standard IIS welcome page and not my SharePoint portal.

IIS 8 Standard Welcome Page

IIS 8 Standard Welcome Page

So what’s up with that? Obviously the web browser is able to communicate with the IIS on the SharePoint server, but its not serving up the SharePoint site. The issue here is that IIS has multiple sites hosted on a single IP/Port combination and it decides which one to serve up based the Host Header that the browser includes in its request. When I’m using the browser on my server it sends portal.contoso.com as host header, but from my home machine it will send xxxxxxxx.cloudapp.net as host header which IIS doesn’t recognize.

This is how I solved it:

  1. using the Microsoft Azure portal, create two new Windows Azure endpoints that map between the internal ports for your site and Central Admin to the external ports on your Azure DNS name (xxxxxxxx.cloudapp.net)
    Screenshot showing Windows Azure endpoints for the SharePoint machine

    Windows Azure endpoints for the SharePoint machine

  2. Log on to Central Admin, go to Manage Web Applications and click on the web application that you want to make available
    managing Web Applications in Central Admin

    managing Web Applications in Central Admin


  3. Now Click on the Extend button in the Ribbon and fill in the port, hostheader and URL (way down at the bottom, not shown in screenshot):
    Screenshot of SharePoints pop-up page for extending the web application to the internet zone

    Extending the web application to the internet zone

    In my case I didn’t change any of the authentication options as I did not want to grant anonymous access to the sites. If you do want this, then this is the place to do it

  4. Don’t panic if Central Admin doesn’t show an extra web application. If you open up IIS Manager, you’ll see it

Managed navigation and the “A Default Managed Metadata Service Connection Hasn’t Been Specified” error

So, you’ve decided to enable managed navigation and when you press the “Create Term Set” button you get the error: Failed to Create Term Set: A Default Managed Metadata Service Connection Hasn’t Been Specified

This is occurs when your environment is running Managed Metadata Service and has one or more Managed Metadata Service connections, but none of them have enabled the setting This service application is the default storage location for column specific term sets.