Welcome!

So, this is it.

My first post on what is, for me, a long overdue foray into the world of work-related social media. It’s something i’ve been meaning to do for a while. Since I started coding back in 2005 I always imagined I’d host a humble site dedicated to the things I’ve encountered along the way. Back then it was .NET. Predominantly Winforms and then bridging into the online world of ASP.NET. It never happened.

A few years ago my career path changed and I entered into the wonderful world of SharePoint. That was surely the motivation I needed to start up a personal blog. There was definitely enough to write about. It never happened.

So I’ve taken the opportunity to start one up now. Over a year after SharePoint 2010 reached RTM, in an environment where SharePoint blogs and the dissemination of information is more common than ever before. So why now?

I’ve definitely encountered a lot in my time with the product to date. The breadth of experience i’ve been lucky to have has opened my eyes to 2 things. Firstly, There’s a hell of a lot to write about, and a hell of a lot I have personal opinions on. Secondly, while the information always seems to be out there eventually, it’s not always that easy to find. I figure the more sources there are the better, and I’ll get to put my 2 cents in along the way.

My goal is that someone manages to get something out of the things I write here. Noble huh? I may even hope as far as thinking some of the things I write may spark some debate and increase my own levels of knowledge.

Enjoy.

Implementing redirects when upgrading a site to SharePoint

Late last month we launched the best site i’ve ever had the pleasure of working on, Education + Training International (ETI). The pure size and scope of the site, particularly everything which drives it behind the scenes, is something which everyone who has worked on (and will continue to work on) it should be proud of. Having taken the site from inception to launch, the number of hurdles we came across along the way would be enough to fill this blog for the year. This post will touch on just one small aspect of it – ensuring that the URLs from the old site accurately redirected to those in the new.

I actually touched on this topic in some sense a couple of years ago in my post 301 vs 302 Redirects in SharePoint so it was good to get the opportunity to revisit it again, explore options and implement a solution. This post is quite long, so I’ve broken it up into headings outlining what is being discussed – feel free to skip ahead to what interests you.

Requirements and Caveats

There were 2 main considerations for requiring the redirects (and we wanted all URLs redirected – there wasn’t a ridiculous number we’d need to handle – around 130 or so). First and perhaps most importantly was user experience. The last thing I wanted was for our users to be consistently hitting a generic 404 page. The other consideration was for Search Engine Optimisation – ETI had previously invested in SEO on their existing site and we did not want to lose those search engine gains which had been realised.

There was a caveat to the decision making process (isn’t there always). The infrastructure team was not prepared to take on the burden of managing the redirects and had reservations anyway around the feasibility of managing them at the reverse proxy level.

I also personally wanted to avoid any manual deployment steps both out of principle and for disaster recovery and future maintenance reasons – those kinds of steps are often lost over time as people leave and documentation becomes outdated and ignored.

Finally, ideally the process would be able to be managed down the track, preferably through a SharePoint list within the site.

Rejected Solution #1 – custom web part on PageNotFound

My first investigation centred around the concept of placing a custom web part on the 404 PageNotFound page (having easy, editable access to this page from within SharePoint is a great improvement over previous versions). The idea was that a list would exist which managed the old URLs and the mapping to the new URL, if the ?requestUrl= found a match, we’d serve up a 301 redirect to the new URL, otherwise we’d provide the next best thing – perhaps a search within the site for some of the terms within the URL. The user experience was actually seemless with this approach, however a quick look at Fiddler showed that the 404 response was still served before my code hijacked it and served up the 301, meaning the search engines would assume the pages were gone. Back to the drawing board.

Rejected Solution #2 – storing URL rewrite mappings in a separate file

It was at this point that I gave up on my desire to have a user-editable list of redirects (perhaps prematurely). Our specific requirement was simply to redirect the old URLs to the new, future redirections were more of a nicety I was trying to provide. I knew a bit about managing redirects in IIS (I referenced Jeremy Thake’s post How we did it: 301 Redirects in IIS 7.5 on Windows Server 2008 R2 in my previous blog post) so figured I’d explore that track (I did have some reservations about blowing out the web.config particularly in relation to the 250kb file size cap, so I intended to use Ruslan’s Storing URL rewrite mappings in a separate file - I figured this would also allieviate the ‘adminstrative burden’ of managing them through IIS). The approach worked great – I could manage a set of old URL/new URL pairs in a separate configuration file, but to avoid the manual deployment steps I’d want it managed and deployed via our solution.

Unfortunately, that separate file had to exist along side the web.config in the IIS virtual directory for the site. I wasn’t able to reference an absolute path and deploy my configuration file somewhere within the SharePoint layouts directory. I investigated the ability to Deploy Files to SharePoint Web Application Virtual Directories At Feature Activation via Brian Jackett but the noted flaws in that system and the complexity around it quickly turned me off. Back to the drawing board.

Implemented Solution – writing URL rewrite mappings to web.config via feature

My next approach was to take a look at managing the redirects within the web.config itself. I did have concerns regarding blowing out the file and the maximum size limit for it – but tests soon proved that we wouldn’t even get close to that figure, so it wasn’t as much of a concern as I had made it out to be. So this approach worked fine too, the challenge was to remove the manual administration/deployment steps out of the process. Enter Using SPWebConfigModification to Update the Web.config in SharePoint 2013. I think i’ve explained how to do that reasonably well in that post (which i’ve also now updated to include some new information) so i’ll jump straight into some code snippets you can use in combination with that post.

            SPWebConfigModification modification = new SPWebConfigModification();
            modification.Path = "configuration/system.webServer";
            modification.Name = "rewrite";
            modification.Sequence = 0;
            modification.Owner = "ETIWebConfigModifications";
            modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
            modification.Value = "<rewrite />";
            webApp.WebConfigModifications.Add(modification);

            modification = new SPWebConfigModification();
            modification.Path = "configuration/system.webServer/rewrite";
            modification.Name = "rewriteMaps";
            modification.Sequence = 0;
            modification.Owner = "ETIWebConfigModifications";
            modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
            modification.Value = "<rewriteMaps />";
            webApp.WebConfigModifications.Add(modification);

            modification = new SPWebConfigModification();
            modification.Path = "configuration/system.webServer/rewrite/rewriteMaps";
            modification.Name = "rewriteMap";
            modification.Sequence = 0;
            modification.Owner = "ETIWebConfigModifications";
            modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
            modification.Value = "<rewriteMap name='Redirects' />";
            webApp.WebConfigModifications.Add(modification);

            modification = new SPWebConfigModification();
            modification.Path = "configuration/system.webServer/rewrite/rewriteMaps/rewriteMap";
            modification.Name = "redirect-1";
            modification.Sequence = 0;
            modification.Owner = "ETIWebConfigModifications";
            modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
            modification.Value = "<add key='/eti-overview/profile-of-education-training-international.html' value='/about-eti' />";
            webApp.WebConfigModifications.Add(modification);

            modification = new SPWebConfigModification();
            modification.Path = "configuration/system.webServer/rewrite";
            modification.Name = "rules";
            modification.Sequence = 0;
            modification.Owner = "ETIWebConfigModifications";
            modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
            modification.Value = "<rules />";
            webApp.WebConfigModifications.Add(modification);

            modification = new SPWebConfigModification();
            modification.Path = "configuration/system.webServer/rewrite/rules";
            modification.Name = "rule";
            modification.Sequence = 0;
            modification.Owner = "ETIWebConfigModifications";
            modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
            modification.Value = "<rule name='Redirect rule1 for Redirects' />";
            webApp.WebConfigModifications.Add(modification);

            modification = new SPWebConfigModification();
            modification.Path = "configuration/system.webServer/rewrite/rules/rule";
            modification.Name = "match";
            modification.Sequence = 0;
            modification.Owner = "ETIWebConfigModifications";
            modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
            modification.Value = "<match url='.*' />";
            webApp.WebConfigModifications.Add(modification);

            modification = new SPWebConfigModification();
            modification.Path = "configuration/system.webServer/rewrite/rules/rule";
            modification.Name = "conditions";
            modification.Sequence = 0;
            modification.Owner = "ETIWebConfigModifications";
            modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
            modification.Value = "<conditions />";
            webApp.WebConfigModifications.Add(modification);

            modification = new SPWebConfigModification();
            modification.Path = "configuration/system.webServer/rewrite/rules/rule/conditions";
            modification.Name = "add";
            modification.Sequence = 0;
            modification.Owner = "ETIWebConfigModifications";
            modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
            modification.Value = "<add input='{Redirects:{REQUEST_URI}}' pattern='(.+)' />";
            webApp.WebConfigModifications.Add(modification);

            modification = new SPWebConfigModification();
            modification.Path = "configuration/system.webServer/rewrite/rules/rule";
            modification.Name = "action";
            modification.Sequence = 0;
            modification.Owner = "ETIWebConfigModifications";
            modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
            modification.Value = "<action type='Redirect' url='{C:1}' appendQueryString='false' />";
            webApp.WebConfigModifications.Add(modification);

The only concerns I had left were around the performance implications of managing so many redirects within IIS/the web.config, but they were soon allayed via How to check for performance in URL Rewriting and IIS Rewrite Module rewrite map performance.

Was there a better option?

In the end I was pretty happy with the end result. I managed to take the burden off the infrastructure team (activating a web-app scoped feature is no significant task), managed to take the manual administrative steps (such as deploying files to specific locations which would need to be repeated if new WFE’s were added to the farm) out of the process and provided a seemless experience for the users while maintaining good SEO practices. The only thing I didn’t achieve was providing the user the ability to manage the redirects and I couldn’t help wonder if that was possible.

Thankfully I have the pleasure of working with someone who’s seen it all before and had a chance to try a few different approaches to this problem over time! Faced with this same challenge (but at a much larger scale), his solution was to create a custom IIS module which inspected the 404 requests going to the redirect page and converted them to 301′s, then a custom web part on that page did another 301 if it found an item in the old-new URL list, otherwise it wrote 404 to the response header for the nicely branded 404 error page.

The only downside to this approach I could see was the need to install the custom IIS module on each server – however is that really worse than having to install the IIS rewrite module on each server? Probably not. The approach also had a significant unexpected upside – by monitoring the analytics on the 404 page they were able to identify legacy links which were still being used across the web, and had the ability to redirect those to a new URL on the fly and provide ongoing functionality to redirect merged/renamed/moved pages on the ever evolving web site.

So hopefully this post has given you a few ideas regarding how to implement your redirects when upgrading a legacy site into SharePoint. As always there are pro’s and con’s to each approach but there are definitely options available to achieve a decent result to meet a number of different requirements.

Why wouldn’t you use the App model for On-Premises SharePoint solutions?

It’s been a while since my last post, and due to a mini-hiatus I decided to take over the Christmas/New Years period in relation to certification studying, I figured it was time for a little something different on this blog. Truth be known, this is a conversation I’ve had with a number of pro-App model colleagues as far back as October last year. I tend not to be able to help myself when it comes to playing the devil’s advocate role (in fact I’ve been given the ‘black hat‘ on too many occasions to think it a coincidence) so this position did come perhaps a little too naturally for me, even with my bias towards heavily branded public facing solutions.

I was actually inspired to write about this from a recent #CollabTalk thread via a question posed by Christian Buckley (@buckleyplanet)

buckley

My favourite responses came from a cloud-focused Microsoft employee on the SharePoint team, Mark Kashman (@mkashman)

kashman

And a SharePoint Certified Master, Chris Beckett (@teknirvana)

beckett

I found the honesty of the responses refreshing.

It’s no secret Microsoft’s position on this one. You only have to read the official documentation on MSDN such as Deciding between apps for SharePoint and SharePoint solutions or Apps for SharePoint compared with SharePoint solutions to realise they’re pushing the App model in a big way. A couple of ‘tell us what you really think’ quotes that spring to mind from the aforementioned articles include ‘With these considerations in mind, apps should be looked at as the primary choice, and full-trust solutions should be looked at when the capability of the app model does not meet the business requirement.’ and ‘The most important guidance we can give you is to develop an app for SharePoint instead of a farm solution or NCSS whenever you can’.

The cynic in me however still believes there may be an ulterior motive to this position. Microsoft has been ‘all-in‘ for the cloud for a long time and there are a number of very good reasons why they would prefer clients be on Office 365 rather than managing their own on-premises farms. It therefore makes sense that they would want to remove as many barriers to entry to that transition as possible, full trust solutions being a significant one.

So as I always do, I set out to read as many decent articles as I could on the topic. I had remembered some time ago reading the SP-king-of-controversy, Bjørn Furuknap’s SharePoint 2013 App Model Solves Non-Problems Only? article and even though it was one of the only ones that would potentially back up my argument, it seemed to be arguing against the necessity of the App model rather than why it perhaps shouldn’t be used. That article introduced me to Doug Ware’s posts SharePoint 2013 Preview – Apps or Crapps?The SharePoint 2013 App Model is better than Farm Solutions and An Architecture for Provider SharePoint 2013 Hosted Apps on Azure which definitely argue the counter-point well.

Alaa Mostafa’s SharePoint 2013 Development (Apps versus Solutions) was a decent read which pushed the App model as was Jeremy Thake’s SharePoint Apps Playbook Series: Part 1 – SharePoint Apps vs SharePoint Solutions (was looking forward to the series, lets hope for more!). There was also a logged discussion SPChat transcript: App Model vs Solution Model with Jeremy Thake which helped frame my thoughts.

So what are those thoughts? Well, in a perfect world it’s clear that Apps would be the way to go. There are a number of things that concern me though looking from a wider consultancy view rather than just my own (where did I put that ‘these thoughts are my own’ disclaimer..):

  • The learning curve of developing with the App model against sticking to what most already know. The MSDN article I referenced before actually counters this point, arguing that it is not the only factor to consider and that it may be outweighed by the extensive lifecycle management process required for full-trust solutions. Not everyone is a senior consultant capable of making this transition on a client site however.
  • The ‘new’ nature of the beast. Anyone who has been around SharePoint long enough (and this has been particularly relevant with SP2013) would know that things rarely ‘just work’ in the first iteration. I can definitely see this causing a lot of unnecessary headaches on projects.
  • The added development time the above points could lead to. Increased complexity + less familiarity is sure to equal more $$. When you’re competing for work it’s not always best to ‘theoretically’ offer the best-MS-approved solution if you don’t get to implement it for being cost noncompetitive. It also wouldn’t paint the most rosy of pictures of any existing consultant banging their heads against the wall for a seemingly easy requirement.
  • Getting the App model up and running on-premises isn’t a trivial thing, which again adds added complexity and cost to any project which hasn’t already set up their environment for it.

I also wonder if its just me that thinks that this shouldn’t be a purely technical decision and should be one that involves the business. I believe that as consultants we should advise the client on the best way forward by giving them all the facts (and perhaps even some opinions) but allowing them to make a business decision with that information in front of them. Why should we push the App model if the client has no desire to ever move to the cloud? My argument is if you’re on-premises and you have no short-to-medium aspirations to head to the cloud, then where’s the justification for the steep learning curve, increased complexity and increased cost of building Apps.

Now that I feel I’ve made my point, i’m going to do a complete 180. The App model in my opinion should be on any consultant-worth-their-salt’s priority learning list. The direction is clear, and it will become THE way to develop for SharePoint sometime in the future. I’ve put my money where my mouth is in this case, registering for SPC14 with the aim to attend a number of cloud and App related sessions as well as Sonya Koptyev, Kirk Evans, and Richard diZerega‘s pre-conference sessions on Migrating Traditional SharePoint Solutions to the App Model and Refactoring Business Solutions into Apps for Office. One more excellent blog that should also be on the must-read list I feel I need to point out is Vesa Juvonen‘s – definitely worth following for those wanting to transition to the App model.

I’m curious to see if my view changes over time, particularly after SPC14 and hopefully after a number of healthy debates in regards to whether the App model should be chosen over full-trust solutions for on-premises SharePoint installations. If you care to add to the debate i’d be more than happy to hear from you through any medium you wish – i’m definitely prepared to be convinced either way.

How I passed 70-331

Last week I sat and passed the exam for 70-331 - Core Solutions of Microsoft SharePoint Server 2013. After having sat a couple of the prerequisite exams for the SharePoint certification streams it was good to get back to reading and learning about SharePoint and the content for this exam didn’t disappoint – it’s always valuable when the study material for a given exam allows you to learn new things and the content for this one opened my eyes up to some pieces of information I wasn’t previously familiar with. For all of the detractors out there regarding Microsoft certifications, this is probably the most valuable argument for why becoming certified has merit (of course you could read this material without sitting an exam, but for me it really helps to focus areas of study – otherwise there’s just so much information out there to get through!).

My preparation for this exam was quite significant. I had a bit of time up my sleeve so I went down the route of both reading as many relevant TechNet articles as possible and also reading Exam Ref 70-331: Core Solutions of Microsoft SharePoint Server 2013. The book started off fairly dry and I was wondering if I’d get any benefit from it but i’m glad I stuck with it, by the end I was very happy with the content I learnt from reading it and it was actually one of the better books I’ve read for learning about SharePoint recently which was somewhat surprising for what I expected would be just a manual to assist with the exam preparation. In the end I probably dedicated about 50h of study time for this one.

Overall I found the exam quite difficult even though I went in fairly confident. I found Alex Dean’s post 70-331 Insights to be a fairly accurate account of my experience also so I figured I’d link to that rather than duplicate it all here.

I mentioned that I read a number of TechNet articles – I primarily used the Born to Learn site’s Server Certification Study Group wiki to determine what to read, however a number of other options exist including Vlad Catrinescu’s Study Guide for Exam 70-331 Core Solutions of Microsoft SharePoint Server 2013, Gavin McKay’s Studying for Exam 70-331: Core Solutions of Microsoft SharePoint 2013 and Becky Bertram’s currently under construction Exam 70-331 Study Guide. To be honest the first 3 I’ve listed are very similar (even to the point where the mistakes are the same!) so I don’t know who should get the ultimate credit but otherwise all will likely serve the purpose. Finding out about the Born to Learn site was a great positive of this process – I feel it’s going to be a heavily visited site for me in the future!

While I didn’t watch any videos this time around there are a number of options out there for those that wish to pursue that way of learning. Some of the options include Pluralsite’s SharePoint Server 2013 Core Solutions (70-331), CBT Nugget’s Microsoft SharePoint Server 2013 70-331, Channel 9′s Exam Prep: 70-331 and 70-332 – MCSE: SharePoint (Microsoft SharePoint Server 2013) and TechNet’s SharePoint 2013 training for IT pros.

Another valuable resource you should be aware of is the Microsoft Virtual Academy site. I did find a page for Core Solutions of Microsoft SharePoint Server 2013: exame 70-331 however it is in another language so perhaps not so useful for those of us that only speak English! Plus it wouldn’t actually let me access the content. Regardless – I think this is going to be an ever improving and valuable site to keep an eye on for future certification efforts.

So that about covers everything. I enjoyed the content of this exam and the process of studying for it which is always a plus when setting off on the path to gain the overall SharePoint certifications. Next on the list will be 70-332 so I’m hoping that one is similarly well created content wise.

As always, best of luck for passing this exam!

How I passed 70-417

Hot on the heels of writing about How I passed 70-480 I decided to start studying for the exam 70-417 – Upgrading Your Skills to MCSA Windows Server 2012. This was probably the most difficult exam i’ve approached for quite some time having historically been a SharePoint developer first, a SharePoint administrator second and a Windows administrator a very distant third. So maybe it’s just me, but this exam was hard. It doesn’t help that if you fail any one of the 3 sub-sections to the exam, you’ll fail the lot. I was lucky to scrape by with one of my scores barely making the cut, even if the other 2 scores were much better.

But before I get into the details of how I passed 70-417, I want to comment a little about my overall thoughts of the exam, or more particularly the suitability for it to exist as part of the SharePoint 2013 administration certification stream. Feel free to skip this rant and find the ‘Resources I used for 70-417′ heading to cut to the chase.

My thoughts about 70-417 for a SharePoint certification

So I could only make wild presumptions in regards to why we now have to complete these pre-requisites to gain the overall certification. The cynic in me would suggest its a convenient way to increase the amount of course and exam fees they’ll rake in from this decision. The realist in me would appreciate that for anyone to be certified in SharePoint (and for that certification to mean something) they probably need a broader skillset than the specifics required for the 2007-2010 certification streams. Having pre-requisites are a reasonable way to demonstrate that.

The problem I have though is with the MCITP: SharePoint Administrator 2010 certification being a valid pre-requisite for 70-417 and then that being the basis for showing the required level of knowledge for a SharePoint Administrator qualification.

Point 1: I wasn’t upgrading my knowledge at all. I was learning the majority of this from scratch. Now I would NOT have been happy if I had to essentially become a certified Windows administrator as a pre-requisite to becoming a certified SharePoint administrator (that is, completing all 3 individual exams which make up 70-417 – costing over $600 just to sit them in the process) but i’m not sure this was the right compromise.

Point 2: I’ve now essentially demonstrated that I know a bit about the new features of Server 2012 and how this has changed from previous Server versions. While this knowledge was interesting, it was not all particularly relevant to being the best SharePoint administrator I could be.

Point 3: I still feel like I have a lot to learn. I feel my study could have been far more productive if it was more specifically focussed on content which was more relevant.

What would I have liked to have seen? I think rather than taking the easy (lazy) option of having us do 70-417, they should have created a SharePoint-specific exam which could truely prove the candidate had the required Windows administrator skillset most relevant to being a SharePoint administrator. This would not necessarily have been upgraded skills, because i’d suggest a number of people would have been in my boat – had the SharePoint knowledge and perhaps experience without ever really having to dive into the Windows administrator world (most teams I’ve been involved in have already had these specific team members).

Anyway that’s probably enough of that, on to the good stuff!

Resources I used for 70-417

It seems every exam I do these days I take a different study path. For this one, because I wasn’t at all confident with the material, I invested a fair amount of time (easily over 40 hours). I watched some study videos again after having some success with that method when I passed 70-668 (although I now have learnt the time-saving capabilities of playing videos at 1.8x speed in VLC player!). The video resources i’m aware of include Pluralsight’s Windows Server category (which includes videos for the individual exams 410, 411 & 412), CBT Nugget’s more targeted 70-417 Upgrading Skills to Server 2012 and Channel 9′s Exam Prep’s for 70-410/70-417, 70-411/70-417 and 70-412/70-417. There are also Jump Start videos at Windows Server 2012 Upgrade Jump Start.

I also for the first time read one of Microsoft Publishing’s exam references Exam Ref 70-417: Upgrading Your Skills to MCSA Windows Server 2012. To be perfectly honest, if I hadn’t had read this book, I probably would have failed. I would perhaps only read them in these circumstances in the future where I wasn’t at all comfortable with the content, but it has definitely opened my eyes to the option.

I also read a tonne of TechNet and other articles relating to topics I still wasn’t particularly comfortable with, however I didn’t do this in my usual structured way – more so via search engines. But that’s not particularly helpful to you, so I’d suggest checking out Enduria’s Preparation resources for the exam 70-417 for individually linked TechNet articles.

The final resources I believe are worth referencing are Vlad Catrinescu’s I passed 70-417, MCSA Windows Server 2012! and Free Windows Server 2012 Learning Material & Resources! Part 1 and Keith Mayer’s “Early Experts” Challenge – Apprentice Quest. All 3 posts are worth reading through and link to some decent resources for this exam.

To wrap things up i’d have to say my overall feeling is one of relief – relief that I passed and can finally put this lot behind me and get back into learning about SharePoint more specifically. My experience wasn’t as negative as Patrick Kremer’s MCSA 2012 upgrade exam experience – MS 70-417 but I can see where he’s come from and to an extent I agree about the value of this certification, if perhaps for different reasons. I wouldn’t say don’t bother though – chances are you want that MSCE SharePoint Certification and this one is a means to an end. It did have some value, but there’s room for improvement. I believe Microsoft have the right idea including a wider range of topics for an overall SharePoint certification, but I hope they change the way they go about certifying it.

Good luck!

How I passed 70-480

Over the course of last year and the beginning of this one I went about studying for and completing the 4 SharePoint 2010 certification exams and blogging how I went about it. It’s been half a year or so since I last targeted a certification so I figured it was about time I got back on the wagon and started attempting to gain the SharePoint 2013 certifications. Considering the ‘How to’ posts for 2010 were some of my most popular it would be remiss of me not to document my journey through the 2013 stream in the same way. Kicking things off was the Programming in HTML5 with JavaScript and CSS3 exam.

It was a little different approaching this exam – on the one hand it isn’t really SharePoint per se so it may seem a little out of our collective comfort zones. For me however, it’s what I’ve been doing for a fair while now so I went in to it with confidence. Before going into the resources I used to pass this I thought it worth noting that while I did very well, it was one of the longest exams I feel I’ve sat for – I think it was important to carefully read the questions and work through the answers more than usual, or maybe that was just me. Just a heads up anyway that it probably paid to be a little more careful than usual.

So on to the resources out there to help you pass. To be honest, there is a lot. I was quite impressed with the range of options available – maybe because this was a more generic topic, the range of people completing it (and blogging about it) was greater. Perhaps it’s just because it’s a pretty interesting topic and enjoyable to comprehensively study for.

I took a couple of different angles at it. Firstly I read the articles linked to by Microsoft themselves in the skills measured section of the page I linked to previously. My first impression was that the content was pretty thin, until I got to the CSS section! In hindsight though I think rather than thin it is actually quite targeted – perhaps not comprehensive enough to guarantee a pass, but it covers enough of the topics that you can expect to come up against. I’d suggest they’re worth reading, which is good to know seeing it was the first time I’ve relied on the links provided by Microsoft for a study plan.

The other resource I used was the training videos created by Jeremy Foster and Michael Palermo – Developing HTML5 Apps Jump Start. I couldn’t recommend these highly enough – they were interesting, fun to watch and weren’t ridiculously long (the jump start training series based on these videos can also be completed here). It helped reinforce that the articles linked to above were actually well targeted, and also touched on a wider spread of content, most of it still particularly relevant. Seeing I had a bit of time after I also read Matthew Hughes’ accompanying blog series to which you can find all the links on Michael’s page HTML5 Apps 70-480. I’d suggest it’s particularly wise to read up further on some of the more complex topics you’ll find in the last post – Matthew does a good job linking to extra resources worth reading.

That’s about did it – I found that was a decent amount of study to target my learning to the exam and pass relatively comfortably. I do have a fair bit of experience in these technologies however, so anyone with less may be interested in delving into the topics further using the resources below.

The first type I tracked down were the blogs which broke down the skills measured and targeted links to quality resources to study for them. There was an abundance of these and considering I didn’t use a particular one, I thought I’d list a few for consideration. You could use Chris Myers’ Microsoft Exam 70-480 Study Guide, Becky Bertram’s Exam 70-480 Study Guide, the post Exam 70-480 Study Material: Programming in HTML5 with JavaScript and CSS3, Adnan Masood’s Study Notes for 70-480 Programming HTML5 and CSS 3 Exam, Xtian’s Microsoft Exam 70-480 Certification Tips, Martin Bodocky’s 70-480 Programming in HTML5 with JavaScript and CSS3 – Preparation links or the forum post by Robert Kaucher 70-480 – Programming in HTML5 with JavaScript and CSS3. The list probably goes on and on, like I said there’s a lot of quality resources out there.

If clicking a million links isn’t your thing then Aidan Ryan created a series starting at Microsoft Exam 70-480: Programming in HTML5 With JavaScript and CSS3 where you can follow through reading his own study notes in the one place.

Then there’s always the paid options if you find you’re really stuck – the book Exam Ref 70-480: Programming in HTML5 with JavaScript and CSS3 by Rick Delorme and the nugget series Microsoft Visual Studio 2012 70-480 Programming in HTML5 with JavaScript and CSS3 by Garth Schulte of CBT Nuggets. Unfortunately for these guys, with the amount of free content which is available for this exam i’m not sure how necessary these options would be.

So that about covers it – as you can see there is no shortage of quality resources for this exam, and the content is actually quite interesting and enjoyable to learn so I’d encourage you to dive right into it, play around and complete the exam with a pretty short turn around time. Until next time – good luck!

Harnessing SignalR in SharePoint 2013 (Office 365)

Over a year ago I wrote a post on Harnessing SignalR in SharePoint. Back then, SignalR was fairly new and the amount of information out there was limited. Getting SignalR to work with SharePoint posed some challenges, all of which are outlined in that post, however eventually we got it working. Since then I’ve had little to do with the technology however the colleague I worked with on that proof of concept, Elliot Wood, has continued to keep on top of the changes to the library and the implications that may have for SharePoint integration. While i’m posting this to my blog, i’m doing so on behalf of Elliot who is responsible for all of the magic soon to be displayed.

Even though there was a fair amount of excitement in regards to SignalR, and particularly the work we did around getting it working in SharePoint, the number of subsequent posts along the same lines was fairly limited. One person who carried the torch was Maximilian Melcher – his post SignalR in SharePoint 2013 – the real-time web is coming! outlined that integrating SignalR in SharePoint 2013 wasn’t as easy as one might expect – even though we were now on the .NET 4.0 framework which should have made life much easier. Max ended up getting his solution working and subsequently posted a codeplex solution, however the post was enough to pose the challenge to Elliot to get it working as easily as possible.

The solution presented uses an autohosted App for SharePoint 2013 hosted on Office 365. With Microsoft’s direction seemingly steering away from full-trust and sandboxed solutions and straight towards the App model, this can be considered the most viable and future-proof solution to the ‘SharePointR’ dream.

To ensure the focus of this post remains focussed on integrating SignalR with SharePoint i’m going to avoid the specifics about SignalR and how to program with it. I’m also going to avoid specifics on how to create and deploy SharePoint Apps. We’ve come a long way with SignalR and the information out there is much better – I’d recommend reading ASP.NET SignalR Hubs API Guide – Server (C#) if you need an overview of programming with SignalR. For those that need a primer on SharePoint Apps, particularly in relation to provider-hosted Apps on Office 365 (the more ‘production-safe’ way of hosting Apps on Office 365), then head over to Chris O’Brien’s post Deploying SP2013 provider-hosted apps/Remote Event Receivers to Azure Websites (for Office 365 apps) for a quick read.

Now that you’re comfortable with the theory, it’s on to the solution. There are 2 pieces to the autohosted App puzzle – the SharePoint App and the SharePoint App Web.

SharePoint App

The purpose of this project is to define the Remote Event Receiver (RER) and also configure the Installed Event Endpoint for the App. The former essentially sets the types of events that will trigger the remote service and points the App to that service, the latter points to the service which will trigger when the App is installed.

Adding the RER is as simple as right-clicking the project and adding a new Remote Event Receiver and selecting the events you want to listen out for. Everything else will be taken care of for you including creating the Package, Feature and tying the RER to its class which will be hosted in the SharePoint App Web.

Configuring the Installed Event Endpoint requires you to edit the properties of the SharePoint App project and set the Handle App Installed property to true. Once again Visual Studio takes care of the rest.

The final step is to access the Permissions tab in the AppManifest.xml editor and add the necessary permissions to the Web and List to ensure your App has the required levels of access to your site.

That’s all there is to it.

SharePoint App Web

Setting up the SharePoint App Web project is a little more involved than the above however is still relatively simple. The first step is to add a SignalR Hub Class to the project – having this native in Visual Studio is fantastic and greatly simplifies the process of getting up and running with SignalR ensuring all necessary references are added to the solution (note that you must have installed the ASP.NET and Web Tools 2012.2 update before you can add this class natively). Alternatively you can add SignalR via the Package Manager Console.

Elliot has also decided to implement the hub using a Singleton instance for optimum performance. For this you’ll need to add another class and insert the following code:

    public class SharePointR
    {
        // Singleton instance
        private readonly static Lazy<SharePointR> _instance = new Lazy<SharePointR>(() =>
            new SharePointR(GlobalHost.ConnectionManager.GetHubContext<SharePointRHub>().Clients));

        public SharePointR(IHubConnectionContext clients)
        {
            Clients = clients;
        }

        public static SharePointR Instance
        {
            get
            {
                return _instance.Value;
            }
        }

        private IHubConnectionContext Clients
        {
            get;
            set;
        }

        public void NotifyDataChanged(string ListName, string Event)
        {
            Clients.Group(ListName).dataChanged(Event);
        }
    }

Once that’s been created we can edit the Hub we added earlier and insert:

    [HubName("SharePointRHub")]
    public class SharePointRHub : Hub
    {
        private readonly SharePointR _sharePointR;

        public SharePointRHub() : this(SharePointR.Instance) { }

        public SharePointRHub(SharePointR sharePointR)
        {
            _sharePointR = sharePointR;
        }

        public void Subscribe(string ListName)
        {
            Groups.Add(Context.ConnectionId, ListName);
        }

        public void UnSubscribe(string ListName)
        {
            Groups.Remove(Context.ConnectionId, ListName);
        }

        public void NotifyDataChanged(string ListName, string Event)
        {
            _sharePointR.NotifyDataChanged(ListName, Event);
        }
    }

One final piece to the SignalR puzzle is to add a Global.asax file which should include the following:

        protected void Application_Start(object sender, EventArgs e)
        {
            var hubConfiguration = new HubConfiguration();
            hubConfiguration.EnableCrossDomain = true;
            hubConfiguration.EnableDetailedErrors = true;
            RouteTable.Routes.MapHubs("/signalr", hubConfiguration);
        }

Now we have our SignalR plumbing we can set up our event receivers. The first cab off the rank is the AppEventReceiver which will fire when the App is installed. What we want to achieve here is to manually hook up the item event receivers to a list in the host web, for instance an Announcements list.

    public class AppEventReceiver : IRemoteEventService
    {
        private string ReceiverName = "RemoteEventReceiver";
        private List<EventReceiverType> EventType = new List<EventReceiverType>()
        {
            EventReceiverType.ItemAdded,
            EventReceiverType.ItemAdding,
            EventReceiverType.ItemUpdated,
            EventReceiverType.ItemUpdating,
            EventReceiverType.ItemDeleted,
            EventReceiverType.ItemDeleting
        };
        private string ListTitle = "Announcements";

        public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)
        {
            SPRemoteEventResult result = new SPRemoteEventResult();

            using (ClientContext clientContext = TokenHelper.CreateAppEventClientContext(properties, false))
            {
                Web hostWeb = clientContext.Web;
                clientContext.Load(hostWeb);

                List docLib = clientContext.Web.Lists.GetByTitle(ListTitle);

                string opContext = OperationContext.Current.Channel.LocalAddress.Uri.AbsoluteUri.Substring(0,
                   OperationContext.Current.Channel.LocalAddress.Uri.AbsoluteUri.LastIndexOf("/"));
                string remoteUrl = string.Format("{0}/RemoteEventReceiver.svc", opContext);

                if (properties.EventType == SPRemoteEventType.AppInstalled)
                {
                    foreach (var eventType in EventType)
                    {
                        EventReceiverDefinitionCreationInformation newEventReceiver = new EventReceiverDefinitionCreationInformation()
                        {
                            EventType = eventType,
                            ReceiverAssembly = Assembly.GetExecutingAssembly().FullName,
                            ReceiverClass = "SignalRProxyService.Services.RemoteEventReceiver",
                            ReceiverName = ReceiverName + eventType.ToString(),
                            ReceiverUrl = remoteUrl,
                            SequenceNumber = 1000
                        };
                        docLib.EventReceivers.Add(newEventReceiver);
                    }
                    clientContext.ExecuteQuery();
                }
                else if (properties.EventType == SPRemoteEventType.AppUninstalling)
                {
                    IEnumerable<EventReceiverDefinition> receivers = clientContext.LoadQuery(docLib.EventReceivers
                        .Where(e => e.ReceiverName == ReceiverName));

                    foreach (var rec in receivers)
                    {
                        rec.DeleteObject();
                    }
                    clientContext.ExecuteQuery();
                }
            }
            return result;
        }

        public void ProcessOneWayEvent(SPRemoteEventProperties properties)
        {
            // This method is not used by app events
        }
    }

We then want to code up the Remote Event Receiver to handle the events triggered on the site and push a message via SignalR to our page. This is a very simplistic example that shows that the item affected in the list we’ve subscribed to is what is causing the update on the page.

    public class RemoteEventReceiver : IRemoteEventService
    {
        private SharePointRHub client = new SharePointRHub();

        public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)
        {
            client.NotifyDataChanged(properties.ItemEventProperties.ListTitle, properties.EventType.ToString());
            return new SPRemoteEventResult();;
        }

        public void ProcessOneWayEvent(SPRemoteEventProperties properties)
        {
        }
    }

The final step is to code up the page. There are a number of aspects here which should be completed to get the branding right for the App – these are covered in greater detail in the how-to guide listed at the end of this post. For now i’ll highlight the main code needed to hook up SignalR on the page (note that references to scripts not yet mentioned in this post are also covered in that guide).

    <!--Script references. -->
    <script src="//ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js"></script>
    <script src="/Scripts/jquery-1.8.2.min.js"></script>
    <script src="/Scripts/json2.js"></script>
    <script src="/Scripts/chromeLoader.js"></script>
    <script src="/Scripts/jquery.signalR-1.0.0.js"></script>
    <script src="/signalr/hubs"></script>

    <!--Add script to update the page and send messages.-->
    <script type="text/javascript">
        $(function () {
            var connection = $.hubConnection();
            proxy = connection.createHubProxy('SharePointRHub')
            proxy.on('dataChanged', function (eventType) {
                $('#MainContent').append('<li>Data changed - ' + eventType + '</li>');
            });
            connection.start()
                .done(function () {
                    //Subscribe to Announcements list
                    proxy.invoke('Subscribe', "Announcements");
                    $('#MainContent').append('<span>Now connected, connection ID=' + connection.id + '</span>');
                })
                .fail(function () {
                    $('#MainContent').append('<span>Could not Connect!</span>');
                });
        });
    </script>

And that’s about it! The image below gives a sneak peak into how an event triggered via a SharePoint list can be reflected on a separate page in real time. It is far more impressive however seeing it in person, so I’d encourage you to follow the guide provided below and get ‘SharePointR’ up and running for yourself – the possibilities this technology opens up is limitless and it will be exciting to see the kinds of solutions designed which leverage SignalR within SharePoint in the near future.

PreviewDownload: Walkthrough-Deploying a SignalR autohosted App to Office 365

How my latest mobile-optimised site was built using SharePoint 2013

About a year and a half ago now I wrote about How the Career Centre mobile site was built using SharePoint. Having recently worked on another large scale public facing website with the requirement for mobile-optimised capabilities the opportunity has presented itself to compare how far the product has come and offer up another technique to build your mobile-optimised sites in SharePoint. This post will be made up of 2 parts – the background information and decision making process which lead to the solution, and the solution itself. If you’re only interested in the latter then look for the heading ‘The Solution’ further down the page.

The Decision

Due to the site not yet having been released I’ve decided to keep the specifics of the site under wraps which is why this post will not include any screenshots of the design or links to view the site itself, however this should not detract from its content. Once the site does go live I may come back to this post and fill in the blanks.

The first thing that came to mind when faced with this requirement was Device Channels and Image Renditions. Whenever a new version of the platform comes out it’s spruiked as the saviour for a number of different use cases however in reality things tend to fall short when it comes to actually implementing them in the real world, so I was interested to see how it would go. I figured that seeing I had some success with the Career Centre site technique I could always fall back to that option if need be.

I started out as I usually do, looking for any and as much information I could find regarding building mobile optimised sites on SharePoint 2013. Surprisingly, content was fairly sparse. As usual with anything WCM related, Waldek Mastykarz was one of the first names I came across. His article on Optimizing SharePoint 2013 websites for mobile devices is one that definitely should be read when comparing the techniques of Device Channels and Responsive Designs. I’d also recommend reading his articles on Device Channels in SharePoint 2013 and Image Renditions in SharePoint 2013. For a bit of context or counter-argument then Shawn’s SharePoint 2013 and WCXM post should also be read.

Although its targeted at SharePoint 2010, Mike Hacker has written a good article on Mobile Access for SharePoint 2010 Internet Sites which provides a number of other alternate options for consideration.

As always when faced with a particular problem there are always some requirements which will dictate the solution. In this case there was a few. The mobile design was being provided with no consideration to the previous desktop design’s HTML, there would be a requirement to shift from the mobile site to the desktop site and vice versa (via the user clicking a link), all pages would require a mobile equivalent however the mobile site may contain many more pages or a different structure of pages depending on the content being displayed. Just to make it trickier, the exception to the ‘all pages require a mobile equivalent’ rule existed for a couple of pieces of functionality that needed to remain desktop-only.

Responsive layouts were immediately ruled out considering the designs had been created without consideration to the other – the original view was designed to perform reasonably on mobile devices, however we also needed to implement a completely mobile-optimised experience.

Altering the existing design using client-side technologies was also ruled out. This approach had been used by some colleagues of mine in the past however time was of the essence and I feared how long that would take to implement, let alone the complexity of customisation that would need to be maintained.

Device channels did not immediately look promising. The requirement to be able to switch to a desktop view immediately concerned me as I figured SharePoint would automatically take the mobile device to the mobile optimised page. The structure of the mobile site also differing concerned me a bit too seeing Device Channels appeared to be suited for a one to one mapping of pages. I’d also read a few other things which concerned me about Device Channels, particularly the lack of support for web part zones within Device Panels. The following quote was taken from How to: Add a Web Part zone snippet in SharePoint 2013.

You cannot insert a Web Part zone inside a Device Channel Panel. If you want to allow authors to add Web Parts to a page, and if you are not concerned about the page weight for mobile devices, you can add a Rich Text Editor page field to a Device Channel Panel, and then instruct authors to add Web Parts there. You can add Web Parts directly to a Device Channel Panel (without a Web Part zone).

That seemed like a deal-breaker, as this was a highly complex site and web parts were used throughout. It seemed I was left with the Career Centre option but I was going cold on that one too – I had concerns around redirecting the user (this was primitive in the previous implementation and only worked at the site’s root URL – not something I wanted to repeat or even could repeat given I was using friendly URLs). I had misgivings around the manual maintenance of pages, ‘mobile links’ and the client side code which altered content links and rendered the popup warning the user about non-optimised pages. I decided to bite the bullet and push on with Device Channels and see how I could make it work regardless of its shortfalls.

The Solution

Where there’s a will there’s a way. The mobile experience for the site was successfully implemented using Device Channels in SharePoint 2013.

The first piece of the puzzle was being able to immediately target all mobile devices by default, seeing we weren’t going to go down the path of optimising for each individual device. The user agent string $FALLBACKMOBILEUSERAGENTS; came in use here, you can read more about how it works in Waldek’s Device Channels post linked above.

The concern around not being able to switch between a mobile and desktop view was also countered. It is possible to override the device channel in a number of different ways. For testing purposes, specifying ?DeviceChannel=channel is a great way to achieve it, however you can also use cookies for a more permanent and lasting result. I placed a link in the footer of both the mobile and desktop master page linking to the other, which when clicked ran the following code (Default or Mobile depending on the current state):

Response.Cookies["deviceChannel"].Value = "Default";
Response.Redirect(Request.RawUrl);

This was wrapped in an UpdatePanel so that the post back and redirect didn’t appear to the user to be 2 separate things, however its probable I could have also achieved this solely using client side code. The redirect was necessary as the link event is handled too late in the page lifecycle for it to take affect in that load.

The differing structure between the mobile and desktop sites was also easily remedied. Firstly, the mobile-only pages were created and given managed navigation nodes with friendly URLs, however were removed from the Global and Current navigations. This ensured that the links would only appear where they were specifically placed on the mobile version. To counter the user going back to the desktop from one of these mobile-specific pages I added the following code to the redirect code above:

string rawUrl = Request.RawUrl;

// Some of the mobile specific pages don't have equivalent desktop versions - so we need to manipulate the redirect to the base location
if (rawUrl.Contains("xxx")) Response.Redirect("/parent-of-xxx");
else if (rawUrl.Contains("/m/aaa") || rawUrl.Contains("/m/bbb") || rawUrl.Contains("/m/ccc")) Response.Redirect("/");
else Response.Redirect(Request.RawUrl);

The web part zone issue was actually not as limiting as you’d first think. The main issue is that when a web part exists in a web part zone, but that zone does not exist in the other device panel, it will automatically be moved to the next available web part zone. This as you can imagine causes havoc to your layouts. The solution of adding the web parts to a rich text zone (or the Publishing HTML zone by default) is all good and well if you want that web part displaying on both the desktop and mobile versions – however this particular site often had significantly different displays (and HTML) for the same list-driven content and thus 2 separate web parts were required.

The solution was actually quite simple. The web part zones CAN be placed within a device panel – just place them in one (the default panel). You can place all your desktop-specific web parts there. I included 2 extra Publishing HTML fields in the mobile channel to serve as psuedo-web part zones for the mobile channel, and all mobile specific web parts were placed there. The final resulting layouts looked something like this:

<asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server">
    <WebPartPages:SPProxyWebPartManager runat="server" id="ProxyWebPartManager">
    </WebPartPages:SPProxyWebPartManager>
    <PublishingWebControls:MobilePanel runat="server" IncludedChannels="DEFAULT">
        <div class="html-structure-has-been-removed">
	    <webpartpages:webpartzone runat="server" id="WebPartZoneCentreTop" title="Webparts Centre Top" partchrometype="None">
                    <zonetemplate></zonetemplate>
            </webpartpages:webpartzone>
	    <PublishingWebControls:RichHtmlField ID="PublishingPageContent" FieldName="PublishingPageContent" runat="server" />
	    <webpartpages:webpartzone runat="server" id="WebPartZoneCentreBottom" title="Webparts Centre Bottom" partchrometype="None">
                    <zonetemplate></zonetemplate>
            </webpartpages:webpartzone>
	</div>
    </PublishingWebControls:MobilePanel>
    <PublishingWebControls:MobilePanel runat="server" IncludedChannels="Mobile">
        <div class="html-structure-has-been-removed">
            <%-- Need to use publishing fields rather than web part zones so existing web parts don't automatically
                 move across into the mobile display --%>
            <PublishingWebControls:RichHtmlField ID="MobileContentTop" FieldName="MobileContentTop" runat="server" />
            <PublishingWebControls:RichHtmlField ID="MobilePublishingPageContent" FieldName="PublishingPageContent" runat="server" />
            <PublishingWebControls:RichHtmlField ID="MobileContentBottom" FieldName="MobileContentBottom" runat="server" />
        </div>
    </PublishingWebControls:MobilePanel>
</asp:Content>

Ensuring particular pages were desktop-only was also relatively simple. All that was required was to have one or more page layouts which were dedicated to those pages specifically which overrode the Master Page setting for that layout. This can be achieved with the following code snippet in the page layout:

<script runat="server">
protected override void OnPreInit(EventArgs e)
{
    base.OnPreInit(e);
    this.MasterPageFile = "DefaultChannel.master";
}
</script>

While I mentioned previously that separate mobile-specific web parts were created to generate the mobile-specific HTML, it wasn’t the only customisation required. Mobile-specific display templates were also created to ensure any content-by-search or search-result web parts rendered out the correct mobile-friendly HTML. These steps wouldn’t have been as necessary if the original design was based on a common HTML structure.

The last piece of the puzzle was handling image functionality for the mobile version of the site – either large images which appeared on the page within content or an image carousel web part. The latter I decided to only include in web part zones rather than within page content to ensure they wouldn’t display on the mobile version (this sometimes resulted in also having to maintain the page heading in a content editor web part and separately in the top mobile content zone to achieve the right look and feel). The former is where I figured image renditions would come in.

Image renditions have been touted as a great feature for mobile-optimised sites, as have Device Channels, so I would have assumed they’d work perfectly together. Unfortunately, they don’t. As the image renditions are displayed via a query string value in the source link stored in content, this is not adjustable out of the box depending on which device channel is being displayed. This was hugely disappointing in my opinion and it seemed like a great opportunity had been missed – in the end I just decided that smaller, web optimised images could remain in content and any large images could exist within web part zones so they didn’t appear on the mobile site. Waldek has actually provided a work-around for this issue with his Responsive Image Renditions with SharePoint 2013 functionality however its something I decided I could do without.

So overall the approach I was able to take in SharePoint 2013 to build a mobile-optimised site in my opinion ended up being miles ahead of what I managed to achieve in MOSS 2007. The amount of time it took to implement the entire site was minimal compared to the impact it has – the majority of mobile-optimised web parts only required changing the HTML rendered in ASP.NET repeaters and was able to leverage the same cached data as the desktop site, meaning performance is good. While there were some hurdles to jump, a number of these would have been easily mitigated if both the desktop and mobile designs were based on the same HTML structure – a happy medium between a fully responsive design and two completely separate sites.

If you’re tasked with building a mobile optimised version of your public facing website I’d definitely recommend looking into Device Channels to implement your solution and hope that some of the techniques listed here help you on your journey.

Follow

Get every new post delivered to your Inbox.