Adding a Device Channel from code in SharePoint 2013
Recently I was working on a project where I would have liked the ability to add a device channel, including setting the device channel master page from code.
As there was no public api to do so, and there was no time in the project to do further investigation on this, I decided to try and solve this puzzle in
my spare time. I did some research and found, among others, the following useful blog posts:
http://sp2013.blogspot.nl/2012/07/channels-in-sharepoint-2013.html
http://blog.mastykarz.nl/device-channels-sharepoint-2013/
http://blogs.architectingconnectedsystems.com/blogs/cjg/archive/2013/08/26/Programmatically-working-with-Device-Channels.aspx
After my investigation I came to the conclusion that although you can activate the PublishingMobile feature, only publishing pages will use a device channel.
So forget about the device channel for your DispForm.aspx or your views like AllItems.aspx. In my case with the mysites you don't have publishing pages by default.
This is when I decided that as there was no easy way to enable device channels on mysites, so I didn't need code to add and set the device channel. As I could also
place the needed functionality using a custom webpart referencing the users mysite on a publishing page, so that was the solution I preferred.
Still I would have liked to get some code working to set device channels and the device master page. The last mentioned blog had some code and I started from there.
To add the device channel itself was the easy part, I used the code from the blog and changed it so it would work on sites with a different language selected. Then
I started to work on manipulating the xml inside the _catalogs/masterpage/__DeviceChannelMappings.aspx page. This was a bit more work, but I got that too.
Then I noticed this was not all there was to it. Summed up the following issues came up:
- Not only the xml changes, but also a Reference with a VirtualPath is added for each mapping
- Each sub site has it's own __DeviceChannelMappings.aspx
- The custom master page might not be inherited by the sub site, then you shouldn't update the xml (and sub sites xml)
- When Follow Default Channel is selected a property '__NoMobileMapping[+ webId]' is set on the rootweb, if set, the xml is overruled
- The defaultChannelMapping should be updated when setting the __DeviceChannelMappings.aspx
So first of all, before I go any further, a warning. As stated in the last two blog posts, as there is no public api, and no documentation, the implementation might be changed.
Therefor you can argue that it is not supported and you should not use code to change these settings. That said, personally I don't think this implementation will change in this version
of SharePoint with a CU or a SP, because then our friends from MS not only need to change the code but also preform an update to all content databases to change already configured sites. Still
you need to be warned, because MS might still change things.
So if you really need device channels on (publishing) sites (site collections) that are automatically created, only then you should think of using the code in the sample solution I will provide below. In my sample solution I add
one device channel and set the master page for this channel and recursively update all sub sites if they inherit the custom master page. I also added some cleanup code on feature deactivation to cleanup the device channel
and the xml in the __DeviceChannelMappings.aspx. There are two things I did not take care of, so if you need it, you probably would have to add some code and do even more testing yourself. These two things are
the 'alternateCssUrl' and the 'themedCssFolderUrl'.
One last word of caution, I have tested (and fixed issues along the way) this code, but that will not guarantee that it will work in your situation. Also I might have missed something, so the code is AS IS.
If you use this code, please first do some testing to confirm for yourself that it is working for you. I would recommend testing all code you pick of the web or even from books, especially if it ends up in production code.
You can download my DeviceChannelSample solution. There you will find a feature that is not automatically activated. This feature contains the
code to add and remove the device channel on activation and deactivation respectively. The feature receiver uses the DeviceChannelHelper class that does all the work. After feature activation you can use
the Preview button on the Page tab to preview the added channel named Device.
Update: 7/3/2014
Thanks to Rick T. for finding two issues with this code, I have updated the sample code to fix this. That is why you always should check code.
First issue was that if approval was configured the update was not approved. To fix this you need to add the following to UpdateChannelPageContents:
if (file.DocumentLibrary.EnableModeration)
file.Approve("");
The second issue was with the defaultChannelMapping in the __DeviceChannelMappings.aspx, these where not set correctly. The following statement needed to be moved in SetDeviceChannelMasterPage (3 parameters version), this didn't show up as an issue when testig, but is none the less a mistake:
_mappingText = xdoc.ToString();
This needed to be moved to the bottom of the following using statement:
using (TextReader reader = new StringReader(_mappingText))
Really silly mistakes, but again I didn't use this in production code, so I only did my own testing.