Introducing app script part pattern for Office365 app model

Office365 or SharePoint app model has numerous different patterns to modify the end user experience. One of the typical structures is the usage of app parts, which are really well document in the MSDN. These app parts are basically iframes on steroids, since they provide for example additional capabilities for parameterize the individual instances. One of the down sides of the app parts is however that iframes do not work that well in responsive design scenarios and browsers could in general have some issues with them.

Using app parts is not however the only model on how to provide user experience customizations to the SharePoint sites. Completely valid alternatives are for example following models which are all demonstrated in the Office AMS examples. We have tremendous capabilities in the JSOM or REST APIs, which we can take advantage without any server side code as well.

  • JavaScript injection
    • Add custom action to the host web to execute JavaScript as part of the page execution. Good demonstration can be found from the Office AMS package under Samples\Core.JavaScriptInjection
  • Place script part or content editor part to the page
    • Place script to the script part or content editor web part located in the page which users are accessing. Downside is that you would have multiple instances of the script if the script is directly added to the web parts
  • Script in page layout
    • Place needed JavaScript to the page layout as embedded JavaScript. Suitable model if needed code is only needed in single page layout or you have limited page layout visibility to custom one’s which all reference same centralized file.
  • Script in custom master page
    • Place or reference JavaScript from the master page. In general we do recommend to avoid custom master pages when possible (will write more on this soon), so not really the best pattern to use, but would work.

Following chapters are introducing a pattern which is already used by multiple customers. I’ve named this as app script part pattern, but this is just my personal terminology for trying to differentiate the approach from other options.

 

App script part pattern

I’ve been lately working closely with the Yammer capabilities and one of the things Yammer provides is easy way of integrating their feeds to the web sites by simply adding few lines of html to the pages and this got me thinking on the possibilities of taking advantage of this in app model customizations. What if we would actually relocate or reference the script on the page content in similar ways as within the case of Yammer, so that we don’t execute the code in the different iframe in first place.

 

Here’s a simple logical architecture of the pattern.

 

image

 

  1. SharePoint environment in Office365 or in on-premises
  2. App Script part referencing external JavaScript file and having the div for marking the location where the information is injected
  3. Actual JavaScript file stored in the provider hosted app side
  4. Provider hosted app platform could be Microsoft Azure or any other platform where the JavaScript file can be hosted in away that it can be reached from the pages or by the browser when page html is processed

This pattern is pretty commonly used cross the industry to integrate systems between the others using JavaScript embedding capabilities. This scenario sample shows how to achieve the similar structure using typical SharePoint provider hosted pattern and how we can provide our extension to be easily available for end users using the web part gallery.

Good examples of the pattern usage is for example the mentioned Yammer embedding mechanism or how interactive maps are integrated to the sites using Bing or Google. More on those patterns from following locations.

In each of the above scenarios we reference to JavaScript and we use specific div for actually dynamically then contain the actual referenced functionality. Here’s an example of Yammer embed command with JavaScript reference and the div marker to define the location of the capability in the page.

 <script type="text/javascript" src="https://assets.yammer.com/assets/platform_embed.js"></script>
 <div id="embedded-feed" style="height:400px;width:500px;"></div> 
 <script>
     yam.connect.embedFeed(
       { container: '#embedded-feed',
         network: 'veskuonline.com'  // network permalink
     });
 </script>
  

This provides more seamless and dynamic integration option than using app part in iframe. This also means that this is suitable option for example for responsive user interface design. We could also just simply deploy redefined script web parts to the SharePoint sites which would have the reference and needed html for provider hosted app reference. This would give the end users opportunity to add additional functionalities to the sites as needed using simply normal SharePoint user experience with add a web part capability, but the actual code would be still coming from provider hosted app side.

One down side of this approach is that if you would need to provide complex parameterization for each instance on the page, this could be complex to achieve, but not impossible. You could pretty easily recognize when the page is in edit mode and then provide needed parameterization options from embedded JavaScript. Any configuration could be stored for example to the provider hosted app side. One additional thing to notice is that since we are injecting new web part option to the web part gallery, deployment of the web part definition (.webpart file) requires tenant administration permissions, so this model is not available for apps hosted in the app store. This tenant permission requirement is simply to avoid any security issues by injecting malicious JavaScript to pages.

In production we could be running these scripts easily from Windows Azure or from any other centralized location where they can be referenced from the SharePoint pages. This also gives us easy way to update the script, since it’s not stored in the actual SharePoint page, it’s rather loaded completely from the provider hosted environment.

 

End user experience

In the case of this sample, we are provisioning app script part to the web part gallery by demand from the provider hosted app, but just as well you could be doing this automatically as part of your provisioning logic or alternatively push the app script part to the site collections as disconnected remote operation.

image

Once the modification has been executed, we can move to the host web and start editing the page for adding a Web Part. Notice that we have new category called “App Script Part” and we can locate new “User Profile Information” web part under that category. I simply used custom group name and provided a custom icon for the user profile information script part to make it look polished.

image

 

After adding the app script part to the page, we are able to see some user profile information from the particular user like in following picture.

image

 

Notice that since we are actually executing the JavaScript from provider hosted app in context of the page, we have full control of the page layout. This means that output can be used with responsive sites or it scales in general based on the layout it is used. This is obviously completely up to the JavaScript which is responsible of rendering the output, but you don’t have to do anything specific for providing automatic resizing of the layout, like you would need to do with app parts.

image

If we take the page in edit mode and have a closer look on what is that app script part, we are able to see that it’s actually a predefined script web part, which has simply a reference to the JavaScript located in the provider hosted app side.

image

Liked noted already, usage of the local host is not obviously something which would work in the production. You could deploy your JavaScript file to some centralized location similarly as shown with Yammer or Bing maps. This could be just as well Azure web site where you host your provider hosted app eventually for the Office365 sites.

Wait? End user could break it! – Yes, they certainly could do that. They could modify the web part and break it and most likely someone will do that. They could get the functionality however back easily by adding web part again from the web part gallery as many time as needed. If end users have edit page capability, they can certainly modify the page and get rid of web parts or app parts in general.

 

Actual code for deploying the app script part to the host web using CSOM is pretty simple and straight forward. We have web part file definition (.webpart file) with predefined content, which is uploaded to the web part gallery using FileCreationInformation object like in any remote provisioning pattern.

 var folder = clientContext.Web.Lists.GetByTitle("Web Part Gallery").RootFolder;
 clientContext.Load(folder);
 clientContext.ExecuteQuery();
  
 //upload the web part file to web part gallery
 using (var stream = System.IO.File.OpenRead(
                 Server.MapPath("~/userprofileinformation.webpart")))
 {
     FileCreationInformation fileInfo = new FileCreationInformation();
     fileInfo.ContentStream = stream;
     fileInfo.Overwrite = true;
     fileInfo.Url = "userprofileinformation.webpart";
     File file = folder.Files.Add(fileInfo);
     clientContext.ExecuteQuery();
 }

 

After the file has been uploaded, we do a small adjustment for the group metadata so that we can easily locate the web part from the web part gallery. Code could be optimized for getting the exact item with query filter, but I was lazy. Sorry for that.

 // Let's update the group for just uploded web part
 var list = clientContext.Web.Lists.GetByTitle("Web Part Gallery");
 CamlQuery camlQuery = CamlQuery.CreateAllItemsQuery(100);
 Microsoft.SharePoint.Client.ListItemCollection items 
                                         = list.GetItems(camlQuery);
 clientContext.Load(items);
 clientContext.ExecuteQuery();
 foreach (var item in items)
 {
     // Just random group name to diffentiate it from the rest
     if (item["FileLeafRef"].ToString().ToLowerInvariant() 
                                 == "userprofileinformation.webpart")
     {
         item["Group"] = "App Script Part";
         item.Update();
         clientContext.ExecuteQuery();
     }
 }

 

Code to gain access to user profile properties

Here’s the code we use in the JavaScript file in the provider hosted app side to gain access on the user profile and to output the needed html. I’m not really a JavaScript developer, so code is not that clean, but does demonstrate the pattern. We use user profile JSOM to access the user profile store and whole html structure is dynamically created.

  
 RegisterModuleInit("userprofileinformation.js", sharePointReady); //MDS registration
 SP.SOD.executeFunc('sp.js', 'SP.ClientContext', sharePointReady);
  
 // Create an instance of the current context.
 function sharePointReady() {
     clientContext = SP.ClientContext.get_current();
  
     var fileref = document.createElement('script');
     fileref.setAttribute("type", "text/javascript");
     fileref.setAttribute("src", "/_layouts/15/SP.UserProfiles.js");
     document.getElementsByTagName("head")[0].appendChild(fileref)
  
     SP.SOD.executeOrDelayUntilScriptLoaded(function () {
  
         //Get Instance of People Manager Class       
         var peopleManager = new SP.UserProfiles.PeopleManager(clientContext);
  
         //Get properties of the current user
         userProfileProperties = peopleManager.getMyProperties();
         clientContext.load(userProfileProperties);
         clientContext.executeQueryAsync(Function.createDelegate(this, function (sender, args) {
             var firstname = userProfileProperties.get_userProfileProperties()['FirstName'];
             var name = userProfileProperties.get_userProfileProperties()['PreferredName'];
             var title = userProfileProperties.get_userProfileProperties()['Title']
             var aboutMe = userProfileProperties.get_userProfileProperties()['AboutMe'];
             var picture = userProfileProperties.get_userProfileProperties()['PictureURL'];
  
             var html = "<div><h2>Welcome " + firstname + "</h2></div><div><div style='float: left; margin-left:10px'><img style='float:left;margin-right:10px' src='" + picture + "' /><b>Name</b>: " + name + "<br /><b>Title</b>: " + title + "<br />" + aboutMe + "</div></div>";
  
             document.getElementById('UserProfileAboutMe').innerHTML = html;
         }), Function.createDelegate(this, function (sender, args) {
             console.log('The following error has occured while loading user profile property: ' + args.get_message());
         }));
     }, 'SP.UserProfiles.js');
 }

 

Summary

I called this approach as a app script part pattern, but as you could see from the demonstration and from the code, this was simply a predefined script web part, which had a reference to the JavaScript file hosted from the provider hosted app. Pretty simple scenario, but at the same time it shows an alternative option to control the rendering logic with still remaining control with single file, which will help on any future updates.

One valid alternative would be to place the script to the individual script part instances, but this would mean challenge for the future maintenance or updates, since you’d face to iterate all the pages in site collections to update all of the script parts. By having script part just referencing the file outside of the SharePoint, we have a single location to update any future updates or enhancement for our code.

App parts have their use scenario as well, but this blog post was for demonstrating alternative route for achieving pretty similar user experience without iframes. There are obviously advantages and disadvantages on each approach, but it’s good to have different patterns which we can take based on the actual business requirements. App parts could be something what suites for one requirement and embedded JavaScript for another.

 

References

Here’s some useful references on the covered topics.