Update to the Chrome Startpage

Chrome has been the browser of my choice for quite some time now. But starting with version 29.x the startpage (when opening a new tab) changed quite a bit – instead of showing my apps, the page would show the omni-searchbox.

This would not be that bad, but I was missing my other devices tab quite badly.

Even more strange was the fact, that on some devices the startpage would show the omni-searchbox while other devices would show the “classic” startpage – all devices are running the same version of Chrome on Windows 7 or Window 8 or Windows Server 2012. Strange.

So after a while I figured: it’s in the settings. Obviously some version of Chrome would change the default setting. If I would skip a version on upgrade this setting would not be changed and thus lead to different behavior on different devices, depending on the upgrade-path I took.

So to get back a consistend look-and-feel I just changed chrome://flags/#enable-instant-extended-api to disabled on all browsers and I got me used look-and-feel.

Updating Visual Studio Extensions

Today I wanted to do some overdue updating of Visual Studio extensions. Among others there where an update of NuGet from version 1.6 to 1.7 and for the Visual Studio Achievements from Version 1.6 to 2.0.

But unfortunatly the updated didn’t work as expected. For the Visual Studio Achievements I got an error, that the digital signature did not match and therefore the update could not be installed. For NuGet the message was different, but with the same result. I just got a message, that the update could not be installed.

Doing some quick google research revealt for NuGet, that problemes during updates are somewhat expected. The recommended solution is to uninstall NuGet and to reinstall the new version of NuGet.

I found however a hotfix for Visual Studio, which is supposed to resolve the update problem with the not matching signatures I encountered with the Visual Studio Achievements. This hotfix actually did resolve the problem for the NuGet update as well :)

Populating sharepoint discussion boards using code

I recently was tasked to import a legacy discussion board into SharePoint 2010. So I wrote a small application, that could dump the old discussions to XML and then import them into SharePoint 2010 using the SharePoint Object model. Nothing more easy than this. A discussion-board is just some kind of list after all.

Well, kinda. If you look closely, you will notice, that discussion-boards differ in some details from regular lists. For one: a discussion-board has threads and replies. The replies can be shown threaded, so I have to maintain which post is a reply to what other post.

SharePoint does some strange stuff to manage this. Each discussion thread is a folder. With this knowledge one might expect to find all replies in this folder, which sound total reasonable to me. But that’s not the SharePoint way. Instead all replies are siblings. Folders are just the SharePoint-way to distinguish threads from replies. I don’t really know how SharePoint manages the reply-hierarchy, but it does work – somehow.

Well, after these discoveries, let’s write some code. I assume the new discussion board already exists and all there is to do is to import the existing posts from the old forum. First I have to create a new thread like this:

var newThread = SPUtility.CreateNewDiscussion(discussionBoard.Items, oldPost.Title);
newThread[SPBuiltInFieldId.Body] = oldPost.Text;
newThread[SPBuiltInFieldId.Created] = FormatDate(oldPost.Date);
string author = CheckUser(oldPost.Creator, web);
newThread[SPBuiltInFieldId.Author] = author;
newThread[SPBuiltInFieldId.Editor] = author;
newThread.Update();

Then I check the oldPost for replies and append them to my newly created thread.

foreach (var oldReply in oldPost.Replies)
{
    var newReply = SPUtility.CreateNewDiscussionReply(newThread);
    string replyAuthor = CheckUser(oldReply.Creator, web);
    newReply[SPBuiltInFieldId.Body] = oldReply.Text;
    newReply[SPBuiltInFieldId.Created] = FormatDate(oldReply.Date);
    newReply[SPBuiltInFieldId.Author] = replyAuthor ;
    newReply[SPBuiltInFieldId.Editor] = replyAuthor ;
    newReply[SPBuiltInFieldId.Title] = oldReply.Title;
    newReply.Update();
    newThread.Update();
}

The replies can be created recursively for replies to replies as well.

This is actually all that is needed. So with some little magic of the SPUtility namespace I was able to import a couple of thousand messages in no time.

Going multi-tenancy with SharePoint 2010

I recently needed to do a multi-tenancy installation of SharePoint 2010 for a customer. I was very impressed by the AutoSPInstaller scripts to do a full install of SharePoint, so I figured: I want to use this!

So I modified the script to handle multi-tenancy installations as well. This was actually not that complicated. I altered the Service-Applications to have an additional attribute “PartitionMode”, which can be true or false. This will trigger the corresponding PowerShell switch for creating the Service-Application.

When doing a multi-tenancy installation I also modified the installation of the root web-application for the hosting as described by Spence Harbar in his Rational Guide to Multi Tenancy.

I also added new Configuration-Elements to setup tenants (aka Subscriptions).

This code was originally based on the 2.5.5 version and was recently re-based to 2.5.7.

The patch can be found on the CodePlex project site.

Google, Ads and opt-out

Since Google is putting a new Privacy Policy in place by 1st of march I took a more close look at the new policies.

I noticed, that Google is gathering quite some information about what kind of websites I’m visiting. I already noticed this, because recently I was surfing the web on my tablet researching some products and then a while later google would only show me this kind of products on ads. I was at first quite suprised, that Google as well as other sites only had ads for certain products, especially this kind of products I was recently interessted in. I then figured, that this is because of some ad-cookies that where stored on my tablet.

Well, looking at Googles new policies I noticed, that I could deactivate Googles gathering of products and using this information to personalize ads. This can be done via Googles-Settings page.

I also noted, that Network Advertising Initiative (NAI) is offering an opt-out mechanism for various ad-plattforms. So I did opt-out on all my devices from Google as well as NAI.

Besides opt-out I still use plugins such as AdBlock for Google or AdBlock Plus for Firefox. But at least for my tablet and my phone I don’t have such plugins, and I thing opt-out is a valid alternative.

Localizing SpecFlow

On a recent usergroup meeting I got introduced to SpecFlow. This opened a whole new world in formulating tests ans specifications. Although I’ve been trying to formulate my tests in a BDD manor, inspired by JP Boodhoo’s and Stefan Liesern’s BDD examples this feels much better.

So the next logical step would be to move to a natural german specification instead of having the original given-when-then syntax.

Turns out, that switching the language is actually really easy. Even though I didn’t seem to find anything on the web … You just have to adjust the app.config like this

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="specFlow"
      type="TechTalk.SpecFlow.Configuration.ConfigurationSectionHandler,
            TechTalk.SpecFlow"/>
  </configSections>
  <specFlow>
    <language feature="de-DE" tool="" />
  </specFlow>
</configuration>

And that’s all that’s to it.

Windows 7 Backup encountered a problem

I just recently started to backup my Windows 7 machine using the buildin backup capabilities of Windows 7.

However this doesn’t work as expected. Event though everything seemed to be backed up, I still got an error that a file was skipped during back.

Backup encountered a problem while backing up file C:\Windows\System32\config\systemprofile\Podcasts. Error:(The system cannot find the file specified. (0x80070002))

This is strange. The file does not exists, but why does Windows want to backup that file if it doesn’t exist? There are just some mysteries still to be solved.

However, there seems to be a KB article for this problem. I suppose the problem has something to do with me uninstalling Zune from my system, which most likely created a podcasts folder for me (which I didn’t want in the first place).

So I searched in my profile and found a podcasts folder which I removed.

Batch Parameter

Even though PowerShell is actually becoming the de-facto standard for scripting, there are still a whole lot of batch-scripts which are used for every day tasks and need to be maintained. And event there are always new things to discover.

To pass parameters to a batch-file isn’t really something new. With %1, %2 and so on you can access those parameters within the script. Noteworthy might be the fact, that %1 is the first parameter, while %0 is the name of the script itself.

What I didn’t know so far where some extra variables like %~f0, %~d0, %~p0 … as well as combinations like %~dp0; but then again, I’m not really so much into batch-scripting.

Here is a little overview about what I recently discovered, maybe it helps for someone else:

Description Example
%~f complete path inlc. filename C:\winnt\system32\x.cmd
%~d drive letter C
%~p path \winnt\system32
%~dp complete path C:\winnt\system32
%~sp short path C:\Progra~1\Intern~1 (for C:\Programme\Internet Explorer)
%~x file extension .cmd
%~nx filename incl. extension x.cmd
%~sx short file extension .doc (for .document)
%~a file attributes
%~t date and time of a file
%~z size of a file

Parameterized queries in MySQL

In order to do SQL right in the .Net world, you just don’t concaternate a search-term with a static search-string, because this will open all gates to SQL-injection. So the following schould not be used:

MySqlConnection connection = new MySqlConnection(_connectionString);
MySqlCommand command = connection.CreateCommand();
command.CommandText = "SELECT * FROM Forum where name = '" + forumName + "'";

Instead you should use a parameterized query. Easy, you might say. Just add a placeholder to the SQL-statement and off you go.

MySqlConnection connection = new MySqlConnection(_connectionString);
MySqlCommand command = connection.CreateCommand();
MySqlParameter forumNameParameter = new MySqlParameter("@forumName", forumName);
command.Parameters.Add(forumNameParameter);
command.CommandText = "SELECT * FROM Forum where name = @forumName";

Unfortunatly this doesn’t seem to work alright. At least I didn’t get any results, even though my search-term did exist.

Looking at the actual SQL that was being executed on the server something became obvious.

SELECT * FROM Forum where name = @forumName

That’s not the SQL I was expecting. Somehow the parameter was not being substitued by the actual value. But why?

Just a short test: the same code does work on a MS-SQL database!

Solution

@ is not a valid character for a placeholder in MySQL. Instead  ? should be used. The correct code should look like this:

MySqlConnection connection = new MySqlConnection(_connectionString);
MySqlCommand command = connection.CreateCommand();
MySqlParameter forumNameParameter = new MySqlParameter("?forumName", forumName);
command.Parameters.Add(forumNameParameter);
command.CommandText = "SELECT * FROM Forum where name = ?forumName";

So this finally worked.