Friday, January 23, 2015

Single Page Apps in SharePoint – Lessons Learnt…

 

Recently I developed a single page SharePoint Hosted App with Angular JS and SharePoint Client Side Object Model. In this post, I am sharing the lessons learnt and some design suggestions to those who are interested in taking this route.

Feel free to leave your comments, if you have any questions about these ideas.

Get rid of the complex Url…

First, an Angular SPA’s routing mechanism requires the views to be loaded with {Your Main Page}#{view name} syntax. But when SharePoint launches the App, it provides several parameters which are required to call back to SharePoint services. Honestly, I hate those parameters so first time my app is launched, I store those into cookies and redirect to my SPA home page without those parameters. This makes the App URLs much more SPA friendly. It’s hard to put the code here because there are many moving pieces, but if you are interested in this approach, I recommend following course by Andrew:

Building SharePoint Apps as Single Page Apps with AngularJS

In addition to what Andrew’s approach, I also provided an deep linking ability in my app so that I can include links to item pages in emails being sent out from the App.

Store Configuration in Hidden List…

I prefer to store some absolutely critical but configurable items in a hidden configuration list which the support people (including me in some cases) can modify to slightly tweak the application behavior. Example of such configuration is given below:

SNAGHTML5a6c971

As you can see, I store the configuration about my Managed Metadata Controls, shared mail box, subject and body of my mailto links, whether or not the names are lync enabled and so on… I wrote the app to take these values instead of hard-coding them into the app. After all, I feel bad if the business has to come to me for every little change that they want to make so I always try to find a balance between amount of time it takes to make it configurable vs. the value it provides.

Store Data outside app-web (personal choice)…

You can store the data inside the app-web or outside of the app web, for instance, into the Host Web. I know this is a big topic and there are lot of conflicting opinions. Honestly, it drives me crazy that the data inside the app web is not searchable, that it gets deleted every time you re-install the app and that you lose rich functionality like Alerts. I don’t know about you, but these limitations are not acceptable to me. Why should I lose my data every time I redeploy the app into my development site? Am I supposed to enter the test data again (of course, you can write some script to insert the test data)?

Since I stored the data outside the app, I included links to this lists from my an administration page in my App (see below). Navigating through different things is not easy in SharePoint, I have heard a lot of complaints about it, so providing a one click access to all important things goes a long way, my users loved it.

SNAGHTML5b644b2

The question is how do these lists get created and configured? A truly independent app can provide a configuration page from which you can trigger JavaScript (REST/JSOM) scripts and have these lists created/configured. If you want to be more sophisticated, you can check for a flag on host web (Hint: Web Property) indicating a missing configuration and  then complete run the configuration script and turn on the flag so that it does not run again.

Considering tight deadlines though, I used custom PowerShell Scripts which uses a CSOM object model to create this lists and libraries. You may wonder why CSOM object model? It’s because I wanted it run them without accessing the servers’ powershell console, imagine O365!

Get hold of People Picker and Managed Metadata Controls…

There is a shortage of SPA friendly People Picker and the Managed Metadata pickers. I chose to get Javascript based implementation of these controls from /OfficeDev/Pnp (link below)

https://github.com/OfficeDev/PnP/tree/master/Components

And then, I wrote Angular JS Directives on top of these controls. If you want to know more about this, leave a comment, I can share some code pieces.

Centralize SharePoint Data Access…

While this is a no-brainer, I still see developers writing random JavaScript on scattered across the pages. Make a point to centralize all data access in logically separated repositories. Angular provides a great way to build Services, make a use of them and put your logic in repository services created in Angular.

Cache.. Cache.. Cache..

The data that is not frequently updated should be cached. I liked Angular Local Storage module (link below) which allowed me to store things like quick links, contacts, currently logged in user’s info and many other things into a local session store. It also allows you to use both HTML5 local storage and traditional cookie storage, use them as per your need.

https://github.com/grevory/angular-local-storage

This greatly improved the responsiveness of my pages. I also put refresh icon where such data was displayed on screen which allowed users to refresh the data from server on-demand.

Allow Business to change Email Templates

Emails are so common into a custom SharePoint Apps and yet, many developers hard code the email templates into the code. I believe email templates should be allowed to be changed by power users (let your power users feel powerful), so I built custom admin pages which allows my users to change the body of my email templates. On the email template edit screens, I provided them a list of special placeholders (example $$CURRENT_USER_ID$$, $$ITEM_TITLE$$, etc) which my application replaces with real values before sending out emails. That way, they had complete control over the subject and body of emails, the well-documented placeholders allowed them to make those template truly dynamic and useful.

How to display gentle reminders for incomplete SharePoint User Profile?

Ability to find people in organization and skills is one of my favorite feature of SharePoint. However, it's obvious that it's useful only when employees complete and keep their profiles up to date. At minimum, I see a great value in completing user's office location, department, contact information, manager information, profile picture, "About Me" and skills information. While some of these pieces of information (like department, manager, office location etc.) comes from Active Directory (or other any source for that matter), information such as "About Me", Profile Picture, "Ask me about" are best filled (and should beJ) out by users. But many employees don't take a few moments to provide this information and there is no way (as far as I know) to send them automatic reminders about their incomplete profile. Well, who likes to be overwhelmed with those reminders?

So I thought of giving them a gentle reminder when they visit a certain page, like an intranet home page. The reminder could be in the form of short-living (5 seconds), one liner message that appears on top right corner when one or more profile property is not filled out. Example below:

I created a Web Part which can be dropped on the page where you want the users to see these gentle reminders. The web part can be configured with:

  1. Tokenized Error Message. Allowed tokens are {0} which will be replaced with link to user profile and {1} which will be replaced with comma separated list of properties which are NOT filled out
  2. Comma separated list of profile properties that you want to check for completeness

Well, it can be enhanced with other things, but enough to get started right now:

  1. Start Date and End Date (imagine you are running a "profile complete" campaign)
  2. Time Frame (display messages only during mornings?)

Most of the code resides in JavaScript, a snippet of which is given below:

   1:  <SharePoint:ScriptLink ID="ScriptLink3" name="SP.Runtime.js" runat="server"
   2:   
   3:     ondemand="false" localizable="false" loadafterui="true" />  
   4:   
   5:  <SharePoint:ScriptLink ID="ScriptLink1" name="SP.js" runat="server"
   6:   
   7:      ondemand="false" localizable="false" loadafterui="true" />  
   8:   
   9:  <SharePoint:ScriptLink ID="ScriptLink2" name="SP.UserProfiles.js" runat="server"
  10:   
  11:      ondemand="false" localizable="false" loadafterui="true" />  
  12:   
  13:  <script type="text/javascript">  
  14:   
  15:  "use strict";  
  16:   
  17:      $(function () {  
  18:   
  19:  var personProperties;  
  20:   
  21:  var propertiesToCheck = "COMMA SEPARATED LIST OF PROPS";  
  22:   
  23:  var notifyMessage = "TOKENIZED ERROR MESSAGE GOES HERE";//
  24:   
  25:  // Ensure that the SP.UserProfiles.js file is loaded before the custom code runs.
  26:   
  27:          SP.SOD.executeOrDelayUntilScriptLoaded(getUserProperties, 'core.js');  
  28:   
  29:   
  30:  function getUserProperties() {  
  31:   
  32:   
  33:  // Get the current client context and PeopleManager instance.
  34:   
  35:  var clientContext = new SP.ClientContext.get_current();  
  36:   
  37:  var peopleManager = new SP.UserProfiles.PeopleManager(clientContext);  
  38:   
  39:   
  40:  // Get user properties for the target user.
  41:   
  42:  // To get the PersonProperties object for the current user, use the
  43:   
  44:  // getMyProperties method.
  45:   
  46:   
  47:              personProperties = peopleManager.getMyProperties();  
  48:   
  49:   
  50:  // Load the PersonProperties object and send the request.
  51:   
  52:              clientContext.load(personProperties);  
  53:   
  54:              clientContext.executeQueryAsync(onRequestSuccess, onRequestFail);  
  55:   
  56:          }  
  57:   
  58:  // This function runs if the executeQueryAsync call succeeds.
  59:   
  60:  function onRequestSuccess() {  
  61:   
  62:  var emptyProps = new Array();  
  63:   
  64:              $.each(propertiesToCheck, function (index, prop) {  
  65:   
  66:  if (SP.ScriptUtility.isNullOrEmptyString(personProperties.get_userProfileProperties()[prop.name])) {  
  67:   
  68:                      emptyProps.push(prop.title);  
  69:   
  70:                  }  
  71:   
  72:              });  
  73:   
  74:  if (emptyProps.length > 0) {  
  75:   
  76:                  SP.UI.Notify.addNotification(notifyMessage.replace("{0}", personProperties.get_userUrl()).replace("{1}", emptyProps.join(", ")), false)  
  77:   
  78:              }  
  79:   
  80:   
  81:   
  82:          }  
  83:   
  84:   
  85:  // This function runs if the executeQueryAsync call fails.
  86:   
  87:  function onRequestFail(sender, args) {  
  88:   
  89:   
  90:              SP.UI.Notify.addNotification("Error in Profile Notify Widget: " + args.get_message());  
  91:   
  92:   
  93:          }  
  94:   
  95:      });  
  96:   
  97:   
  98:  </script>  





  1.  


I think this is a simple but very powerful technique which may help our clients get the most out of SharePoint User Profiles. Hope this helps!

Monday, February 3, 2014

SharePoint 2013: Workflow Manager Installation and configuration issues…

Installing and configuring Workflow Manager 1.0 can be frustrating, if you do not follow the right steps. I installed Workflow Manager 1.0 and ran the default configuration successfully. After installation, I figured that I need to install following cumulative update in the order specified below.

SNAGHTML206d45

After installation of above CUs, I was able to create a WF Manager based site workflow and publish it successfully and I thought I cracked it!

But when I tried to browse the site workflow, the page threw an error. The ULS log suggested that there was an error in Data Access Layer. So I fired up the SQL Server Profiler and figured out some database entries were missing in following table. See the screen below for the table and the error text:

image 

Microsoft.Workflow.TestServiceHost.exe Error: 0 : System.IO.InvalidDataException: A required Workflow Configuration 'WorkflowServiceScopeSnapshotProcessBatchSize' is not present. Please add this configuration value.

I put the entry my self, don’t ask how I came up with values, I put random large numbers because I couldn’t wait to see my workflow working. I put about 4 entries in that table before and finally, the error changed to something else. To be precise, now the error was a missing parameter in a Stored Procedure and finally, I gave up thinking that there must be something terribly wrong here… I decided to uninstall it


Uninstalling Workflow Manager in Single Server environment:


http://social.msdn.microsoft.com/Forums/windowsazure/en-US/86162367-5d43-4f8e-81d4-12440d9dbcde/error-when-running-workflow-form-list-item-workflow-manager-sharepoint-2013?forum=wflmgr


Steve mentioned the steps to uninstall Workflow Manager from the dev environment and I was able follow the steps and uninstall it. For the sake of completeness, I provide a screenshot of un-installation steps below (thank to Steve)


image


followed by successful installation…


After uninstalling, I tried following video tutorial to install and configure Workflow Manager Successfully!


Please consider every single step in this tutorial as important, if you want to see the workflow manager up and running in your dev environment.


http://msdn.microsoft.com/en-US/library/dn201724

Thursday, January 16, 2014

SharePoint 2013: Toggling between minimized and debugged version of java scripts

There is no doubt that including a minimized versions of your scripts (and css files) is best practice, not only for SharePoint but for any Web Application. However, this practice is often ignored by many programmers. Minimized version of script significantly reduces the page’ payload, especially for large javascript (and css files). Just to give you a comparison, debug version of JQuery 2.0.3 version is 246KB but its minimized counterpart is only 82KB.

On the other hand, debugged version of scripts are convenient for developers because setting breakpoints and debugging using developer toolbars is easier.

Now, what if we could archive best of both the worlds! What if we could toggle between debugged version and minimized version without re-deploying anything? Well, I would love it and so I thought about using the technique that I described here.

Note following though:

  • Even though this post uses some SharePoint 2013, with just a little tweak it can be applied to SharePoint 2010 as well.
  • The technique here shows only one JavaScript file, but as many JavaScripts as you want can be included.
  • Similar technique can be used for CSS files as well
  • I have used a couple of site collection level FEATURES which are visible on Site Collection Features page. But if you prefer, you may make them hidden and activate them using PowerShell.

Let’s go to the business now!

The first step is to create a UserControl in your solution and put it under ControlTemplates mapped folder as shown below:

image

Define a property in your code behind of the User Control as shown below:

   1:   private bool _minimized = false;
   2:   
   3:          public bool Minimized
   4:          {
   5:              get { return _minimized; }
   6:              set { _minimized = value; }
   7:          }



Now refer your scripts on in the .ascx file as shown below:

<% if (Minimized == true) {%>
<script type="text/javascript" src="/_layouts/MyApp/scripts/jquery-2.0.3.min.js"></script>
<!-- Other files goes here-->
<% }else{ %>
<script type="text/javascript" src="/_layouts/MyApp/scripts/jquery-2.0.3.js"></script>
<!-- Other files goes here-->
<%} %>




You may have to provide your own script paths..



Next, create two FEATURES and Add two empty elements using VS.NET’s “Empty Element” project item as shown below:


image


image


Add one empty element to each feature. You may use your own naming conventions for your FEATUREs and Elements.


The content of MimimizedScript –> Elements.xml file would look like this:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Control Id="AdditionalPageHead" ControlSrc="/_controltemplates/15/MyApp/MyAppScriptReferences.ascx" Sequence="20">
<Property Name="Minimized">true</Property>
</Control>
</Elements>



And the content of RegularScripts—>Elements.xml file would look like this:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Control Id="AdditionalPageHead" ControlSrc="/_controltemplates/15/MyApp/MyAppScriptReferences.ascx" Sequence="21">
<Property Name="Minimized">false</Property>
</Control>
</Elements>



Note the path to my user control, in SharePoint 2010, the path would look different.


Deploy your solution and get ready to see it in action. I have two FEATURES and I have activated the minimized version as shown below:


image 


When minimized version FEATURE is activated, I enjoy the performance benefits of minimized version of my scripts.


image


If I want to debug my JavaScript, all I need to do is deactivate the old feature and activate the feature which provisions regular debug version of scripts. As soon as I do that, regular JavaScript becomes available as shown below:


image


I think this is very convenient but it may not be necessarily the best option. If you find this useful or have a better idea, please leave comments below.


Tuesday, January 14, 2014

SharePoint Productivity Tip: Windows Search Service

As a SharePoint Developer, I often need to look into the SharePoint hive to look for Features, List Templates, Layouts etc. For instance, if you know the Feature ID and want to locate the Feature in SharePoint hive (14-hive for SP10, 15-hive for SP13), you are out of luck if you search for the Feature ID using windows explorer. Below is the screenshot:

SNAGHTML1a9e135

So what do we do? It seems you have to depend on window’s search infrastructure which is not installed by default on most SharePoint development server.

At high level, you need to install the Windows Search Service and Add 14/15 hive to the indexed location and let the search service finish the indexing.

Here is what I did on Windows Server 2012:

  • Go to Server Manager and Select “Add Roles and Features” from Manage option in top right corner.
  • Select the Windows Search Service option and finish the installation

image

  • You may have to restart the server at this time
  • Locate the “Indexing Options” in the windows settings
  • Add 14/15 hive to the indexing options as shown below

image

  • Let windows finish the indexing. On my PC it took less than 5 minutes, the exact time may vary depending on your machine configuration!

Now, its time to get excited! Look for the original search term that you were looking for and see it yourself!

image

Here is another example: I wanted to see how does SharePoint use AdditionalPageHead delegate control and so I looked for that word now in 15-hive (another tip is to add 14/15 hives into Libraries as highlighted below in green) and wow here is exactly what I was looking for!

image

Trust me! Serious SharePoint developers need this frequently and I am sure it will increase your productivity! Good Luck…

And yes, feel free to leave comments and tell me how it helped you…

Spinning SharePoint Timer Jobs from Site Collection Configuration

My recent content management engagement required to have several long running jobs or tasks that must run in background. For instance:

1. Analyzing Checked out files and sending reminders to the individuals if the number of days (since the file was checked out) exceed certain threshold limits

2. Removing links from other content when a particular content is removed or archived from the system

3. User wants to see all the alerts that he subscribed in all webs in the site collection

4. Sending a reminders to authors to review the content when a review time was specified in the content and that date is approaching

wow, the list goes on…

Well, we could have created one timer job for each scenario and scheduled them at different time. While that is perfectly fine, had some additional design goals for which this approach fall short. Our primary goals were:

1. Ability to monitor the background job and check its status right from site collection, without logging on to server on which it was run and go through ULS logs

2. Ability to run the job on demand without requiring farm level permissions

3. Ability to look into all individual tasks that the job performed and receive an email if anything unusual happened during the execution.

4. Ability to spin up a one time timer job based on an action by an end user and the timer job sends the required information or processes the request later.

So, I came up with a custom architecture which satisfies all of the above goals and achieve even more. While, I won’t be able to share the code because of confidentiality, I can share the details of the architecture. Please feel free to leave comments or contact me if you like the approach and need additional detail.

Here are the main components of this timer framework:

1. When a feature is enabled at Site Collection level, the framework creates two lists: Task Definition List, Task Trace List.

In task The Task definition list contains at least following information:

  • Title of the Task as it appears in the timer job definitions on central admin
  • GUID to track this definition
  • Schedule – to store an XML based schedule definition for the timer job
  • Schedule Type: matches with SharePoint Schedule Types such as one time, hourly, weekly etc.
  • Trace Level – one of the few trace level that my framework supports. A concept similar to OTB diagnostic logging
  • Provisioning Status – a status which shows whether a corresponding SharePoint timer job is created or not
  • Assembly – Full Name of the assembly which contains the execution code of this timer job
  • Class – Full name of the class which is derived from our custom interface ITimerTask interface
  • TaskData – A random XML data that your timer job needs. Framework will automatically make this data available when above Class is called when the timer job runs
  • NotifyTo – Can contains email address of person(s) who is interested in knowing the status of the timer job. The framework takes care of sending the email notification when developer sets the status of the job to anything other than “Success”

The Task Trace list contains at least following columns:

  • GUID – Identifies the corresponding timer job definition
  • ExecutionDateTime – Date and time when the timer job was executed
  • Status – Custom Status such as Success, Warning, Failed, SuccessWithWarning, FailedWithWarning etc.
  • Trace – Contains detailed trace information registered by developer while implementing the execute method of custom ITimerTask interface. Framework takes care of putting the trace information in this list. All that developer needs to to is call a method. For example, .RegisterTrace (“required list was not found”, TraceLevel.Warning)

2. A Web Application level feature is installed by the framework which creates two timer job definitions at Web Application level: Timer Task Scheduler Job and Timer Task Cleanup job.

Let’s see what exactly they do!

TimerSchedulerJob runs at a regular interval (say 5 minutes) and looks for any un-provisioned job in the The Task definition list that I mentioned above. Once it finds, it create a SharePoint Timer Job and schedules it as specified in the definition list. This newly created timer job is a generic timer job, all it does is loads the assembly and the class specified in the definition, prepares the required data for the job and calls the execute method. That means developer’s implementation of Execute method of my custom interface gets called.

Developer of this framework is responsible for:

  • Getting the required data passed as Execute method parameter, along with lot of other information like an instance of SPSite object in which the definition was created
  • Write core logic
  • Set the final status of the job
  • Register all required trace messages by calling method parameter’s RegisterMessage method

After calling the execute method, framework looks at the registered trace messages and the status of the task set by developer. The framework sends email notification specified in the definition. The framework also creates an entry in the Timer Task Trace list at the site collection level that I specified above.

Aha! so finally all dots are connected!

Finally, the Timer Task Trace clean up job removes the trace older than pre configured number of days.

So here is the experience of creating a new timer job for a developer, once the framework is enabled using Web App and Site Collection level feature:

  1. Create a class derived from ITimerTask class of my framework which has just one method called Execute
    • The core responsibility of a developer in order to use the framework effectively are specified above..
  2. Deploy the assembly to GAC
  3. Create a definition in the Timer Task Definition list at site collection level. Alternatively, create the onetime definition dynamically on click of a user action
  4. Monitor the log and see what happened when the framework executed the timer job defined in the definition.

This is one of the most useful framework that I have built and the developers in the team love it because it opens up a lot of possibilities. My developers don’t need to learn how to develop a timer job but they are able to create one and execute them with so much ease. Without a doubt this is my favorite too!

Tuesday, July 17, 2012

SharePoint: Showing Last Published Version to Authors

Quick 5 minute post! Hope it helps!

Recently, our content authors wanted to see the most recent published version without going to Version History. Unfortunately, they hate to use SharePoint’s Version History and let me mention – they hate the ribbon too Sad smile.

Anyways, they were okay with a button put in the side bar of the page which can only be displayed to Authors. And they wanted to view the page in a new window so that they can compare with draft version (thank god, they were okay with comparing the content manually Smile)

Rather than accessing the versions on server side, we decided to access the version information only when a button is clicked. Once again the wonderful Client Side Object model saved a lot of work for us. The script on click of button would look this (and that’s it for today)



this.ctx = SP.ClientContext.get_current();
this.page = ctx.get_web().getFileByServerRelativeUrl(window.location.pathname);
ctx.load(page);

ctx.executeQueryAsync(Function.createDelegate(
this, this.ShowVersion), Function.createDelegate(this, this.ShowError));
function ShowVersion() {
var versionID = page.get_uiVersion();
if (versionID < 512)
SP.UI.Notify.addNotification(
'This page was never published.', false);
else if (versionID % 512 == 0)
SP.UI.Notify.addNotification(
'You are currently viewing Published Version.', false)
else{
window.open(window.location
+ '?PageVersion=' + 512*(versionID % 512), '_blank')
}

}
function ShowError() {
SP.UI.Notify.addNotification(
'Unable to perform the action. Please report the issue.', false)
}