Saturday, November 21, 2015

Tiny but mighty Angular Directive: Record Status Indicator

In posts with similar title, I am planning to show some small Angular Directives that are not huge in terms of code but provide very useful functionality and greatly enhances the user experience.

You may have come across business applications in which records can be soft deleted – means they exists in the system but users are not allowed (or encouraged) to select those records. Such data preservation is helpful especially when historical data is required to be maintained in the system.

So I thought it would be nice to visually indicate if a particular record that user is looking at on screen is active or not. Consider the list of records below. The tiny status indicator just before the indicator clearly shows id record is active or inactive.


Another example in the system, when user is trying to select a Carrier Authority in a dropdown:


The dropdown includes all values but clearly shows that users should not use Inactive records. Like I said, the code for showing such status indicator is very tiny but brings a lot of value for the end users.
The code for the status indicator is provided below, you need to keep two things in mind:
1. It requires an entity with isActive property set to true or false depending in status of the record in the system
2. It uses Font Awesome icons and some color styles. You need to make sure Font Awesome and appropriate styles are included in your project.
app.directive('stStatusIndicator', function () {
return {
restrict: 'A',
scope: {
entity: '=stStatusIndicator'
link: function (scope, element, attr, ctrl) {
scope.$watch('entity', function (newValue, oldValue) {
if (newValue == undefined || newValue == null) {
scope.$watch('entity.isActive', function (newValue, oldValue) {
if (newValue == undefined)

var node = document.createElement("i");
if (newValue == true) {
node.className = "fa fa-circle text-green hidden-print";
node.title = "Active";
else {
node.className = "fa fa-ban text-danger hidden-print";
node.title = "Inactive";


Such visual indications can definitely be extended to other concepts such as indicating status of record in workflow, recently updated record so on and so forth. Hope you enjoyed the technique!

Thursday, November 19, 2015

Have TypeScript working with VS.NET 2013/2015 SharePoint Project Template

Assuming that…

1) You have latest TypeScript for Visual Studio installed

2) You have a SharePoint project and you add TypeScript file to it. Everything appears fine but you can’t generate .js file when you save it.


Following steps should solve the problem:

Step 1

Go to

C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\SharePointTools (For Visual Studio 2013, that is: C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v12.0\SharePointTools) and open file in notepad

Step 2

Add following just before the <Import Project="$(CustomBeforeSharePointTargets)" …… line in the targets file

<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets')" />

Step 3

Save the file and re-open VS.NET with SharePoint project. Try saving the .ts files and now you should have corresponding .js files generated.

Basically, the above target calls the Microsoft.TypeScript.targets which eventually will run the TypeScript compiler when .ts files are saved.

Step 4 (Optional)

Although the targets calls the right version of compiler, you might following problem:

After installing TypeScript 1.6 for VS.NET, typing “tsc –v” in package manager should result in 1.6.x. If not, it’s very likely that you had older version of VS.NET which created a PATH variable with old TypeScript location.

To fix it:

Open PATH variable value of the computer, remove old value of TypeScript location and replace with following:

C:\Program Files (x86)\Microsoft SDKs\TypeScript\1.6

Reopen VS.NET and you should be good!

Friday, March 27, 2015

Building a Flexible SMS Response System

Building SMS response system was my long pending dream! A few years back, I worked with SMS Service provider. Back then, the rates were high and the interfaces were not so easy.  But today, most SMS services offer cheap rates and very intuitive and easy to use, well documented APIs. Twillio is my choice for today. Let’s begin with a problem that’s being addressed here:


My service needs to receive an SMS message from customer and process it and respond with an SMS message. The ask is very simple but if you can imagine, processing these SMS messages requires a flexible and configurable architecture, instead of a lots of if..else statements. I am not tying to do text analytics here, I expect my users to send specific keywords in SMS. For example, if they need help, they may send “help me”, “options”, or even “*”! If they need a list of nearby bars, we expect them to send “bars near me” or “nearby bars”….. you got the idea!

My Design Goals:

1. Ability to process incoming message and determine a response

2. The logic should be flexible and configurable. For instance, if I expect my users to send “help me” in order to receive a help message from us, it should be easy and configurable so that I can provided additional phrases that users can send without changing the code.

3. Break the problems into smaller chunks and make sure each piece of code has single responsibility

Possible Solutions

Given the problem and the design goals, I can think of two possible solutions.

Alternate Solution 1: Have separate classes created and each understands only a specific incoming message and processes it. Consider each class as a node which has ability to understand the request and provide response. If you like, consider it as an HTTP Module. Then build some sort of framework which passes the incoming message to each node in sequential fashion until it finds a node which can process the message.

Alternate Solution 2: Leverage a Service Bus architecture. A message can be put in a queue and then you can one subscriber which can process the message. And yes, the subscriber can pull the message from Queue and use the Alternate Solution 1 (above) to process the message. You may also have multiple subscribers and use the Topics mechanism of Azure Service Bus.

DISCLAIMER: If you are looking for a most reliable and scalable way to process the SMS messages, I recommend Alternate Solution 2 but for the low volume and fairly reliable solution, Solution 1 is not a bad choice either. While solution 1 is not the best, I’ll talk about that in this post, leaving solution 2 for another blog post. I also do not claim that these are the only two solutions, there could be many ways to solve such problems at hand. Sending and receiving SMS using Twilio is very simple. I’ll not talk about the mechanics of sending and receiving SMS messages because that depends from one provider to another.

So let’s take a look how does it (Alternate Solution 1) look conceptually:


And let’s zoon in a little bit into the SMS Processing Pipeline component:


The SMS Node processor in our case is a singleton class which initializes (and caches in its on state – it’s a Singleton, after all) the pipeline (series of processing nodes). It also has a method to receive a message and send response by routing the message through processing node.

A processing node implements a contract

   1:  interface ISmsProcessorNode
   2:      {
   3:          void ProcessMessage(SmsInfo message, SmsProcessingEventProperties eventProps);
   4:      }

The message represents everything that a typical incoming SMS message may include. For example, Twilio passes you following information along with some other information which we will not care:

   1:   public class SmsInfo
   2:      {
   3:          public string Message { get; set; }
   4:          public string ReceivedFrom { get; set; }
   5:          public string FromCity { get; set; }
   6:          public string FromCountry { get; set; }
   7:          public string FromState { get; set; }
   8:          public string FromZip { get; set; }
   9:      }

SmsProcessingEventProperties serves as a mechanism to receive configuration from the Node Processor and also as a mechanism to send response/status back to the node processor.

   1:  public enum SmsProcessingEventStatus
   2:      {
   3:          Processed = 0,
   4:          Skipped = 1, 
   5:          Warning =2,
   6:          Error = 3
   8:      }
   9:      public class SmsProcessingEventProperties
  10:      {
  11:          public SmsProcessingEventStatus Status { get; set; }
  12:          public string ErrorMessage { get; set; }
  13:          public XElement Configuration { get; set; }
  14:          public string Response { get; set; }
  15:      }

Finally, here is the event processor looks like:

   1:   public class XmlNodePipelineProcessor
   2:      {
   3:          private static volatile XmlNodePipelineProcessor instance;
   4:          private static object syncRoot = new Object();
   6:          List<SmsProcessingNode> _nodes = new List<SmsProcessingNode>();
   8:          private XmlNodePipelineProcessor() { }
  10:          public static XmlNodePipelineProcessor Instance
  11:          {
  12:              get
  13:              {
  15:                  if (instance == null)
  16:                  {
  17:                      lock (syncRoot)
  18:                      {
  19:                          var processorFile = HttpContext.Current.Server.MapPath("~/App_Data/SmsNodeProcessors.xml");
  20:                          if (instance == null)
  21:                          {
  22:                              instance = new XmlNodePipelineProcessor();
  23:                              instance.InitializeFromXml(processorFile);
  25:                          }
  27:                      }
  28:                  }
  30:                  return instance;
  32:              }
  35:          }
  37:          #region Public Members
  38:          public IEnumerable<SmsProcessingNode> GetProcessingNodes()
  39:          {
  40:              return _nodes;
  41:          }
  42:          public string GetResponseFor(SmsInfo incomingMessage)
  43:          {
  44:              string response = string.Empty;
  46:              foreach (SmsProcessingNode node in _nodes)
  47:              {
  48:                  try
  49:                  {
  50:                      Assembly assembly = Assembly.Load(node.Assembly);
  51:                      Object classInstance = assembly.CreateInstance(node.ClassName);
  52:                      ISmsProcessorNode processingNode = classInstance as ISmsProcessorNode;
  53:                      if (processingNode == null)
  54:                      {
  55:                          Trace.WriteLine(string.Format("Assembly:{0}, Class{1} is not an implementation of {2}.  Node-{3} will be ignored.", assembly, node.ClassName, typeof(ISmsProcessorNode).FullName, node.Name));
  56:                          continue;
  57:                      }
  58:                      SmsProcessingEventProperties eventProps = new SmsProcessingEventProperties()
  59:                      {
  60:                          Configuration = node.Configuration,
  61:                          ErrorMessage = string.Empty,
  62:                          Status = SmsProcessingEventStatus.Skipped
  63:                      };
  64:                      processingNode.ProcessMessage(incomingMessage, eventProps);
  66:                      // Check the modified eventProps and take actions
  67:                      if (eventProps.Status == SmsProcessingEventStatus.Skipped)
  68:                          continue;
  70:                      if (eventProps.Status == SmsProcessingEventStatus.Error)
  71:                      {
  72:                          Trace.WriteLine(string.Format("Incoming Msg:{0}, Node:{1}, Error:{2}", incomingMessage.Message, node.Name, eventProps.ErrorMessage));
  73:                          continue;
  74:                      }
  75:                      if (eventProps.Status == SmsProcessingEventStatus.Processed && node.ContinueAfterProcessing)
  76:                      {
  77:                          continue;
  78:                      }
  79:                      if (eventProps.Status == SmsProcessingEventStatus.Processed && !string.IsNullOrEmpty(eventProps.Response))
  80:                      {
  81:                          return eventProps.Response;
  82:                      }
  84:                  }
  85:                  catch (Exception ex)
  86:                  {
  88:                      Trace.WriteLine(string.Format( "Node:{0} failed to process message:'{1}', error message: {2}", node.Name, incomingMessage.Message, ex.Message));
  89:                  }
  91:              }
  93:              return string.Format( "Sorry, we could not process your message: {0}", incomingMessage.Message);
  94:          }
  95:          #endregion
  97:          private void InitializeFromXml(string fileName)
  98:          {
 101:              XmlDocument doc = new XmlDocument();
 102:              try
 103:              {
 104:                  doc.Load(fileName);
 105:              }
 106:              catch (XmlException ex)
 107:              {
 108:                  Trace.Write(ex);
 110:              }
 112:              if (doc.DocumentElement.LocalName != "NodeProcessors")
 113:              {
 114:                  Trace.WriteLine("File does not contain NodeProcessors");
 115:                  return;
 116:              }
 119:              XmlNodeReader nodeReader = new XmlNodeReader(doc.DocumentElement);
 120:              XDocument xDoc = XDocument.Load(nodeReader);
 122:              Dictionary<string, string> assemblyRefs = new Dictionary<string, string>();
 123:              foreach (var item in xDoc.Descendants("Assembly"))
 124:              {
 125:                  //TODO: validate that item has Name and Value attributes
 126:                  assemblyRefs.Add(item.Attribute("Name").Value, item.Attribute("Value").Value);
 127:              }
 129:              foreach (var item in xDoc.Descendants("Node"))
 130:              {
 133:                  if (!item.HasAttributes
 134:                      || item.Attribute("Name") == null
 135:                      || string.IsNullOrEmpty(item.Attribute("Name").Value)
 136:                      || item.Attribute("AssemblyRef") == null
 137:                      || string.IsNullOrEmpty(item.Attribute("AssemblyRef").Value)
 138:                      || !assemblyRefs.ContainsKey(item.Attribute("AssemblyRef").Value)
 139:                      || item.Attribute("Class") == null
 140:                      || string.IsNullOrEmpty(item.Attribute("Class").Value))
 141:                  {
 144:                      continue;
 145:                  }
 146:                  // All data seems to be okay, process it
 147:                  SmsProcessingNode newNode = new SmsProcessingNode()
 148:                  {
 149:                      Assembly = assemblyRefs[item.Attribute("AssemblyRef").Value.Trim()],
 150:                      ClassName = item.Attribute("Class").Value.Trim(),
 151:                      Name = item.Attribute("Name").Value,
 152:                      StopOnError = item.GetAttributeAsBool("StopOnError", false),
 153:                      ContinueAfterProcessing = item.GetAttributeAsBool("ContinueAfterProcessing", false),
 154:                      Configuration = item.Element("Configuration") 
 155:                  };
 157:                  _nodes.Add(newNode);
 158:              }
 162:          }
 163:      }

A node representation is here:

   1:  public class SmsProcessingNode
   2:      {
   3:          public string Name { get; set; }
   4:          public string Assembly { get; set; }
   5:          public string ClassName { get; set; }
   7:          private bool _continueAfterProcessing = false;
   9:          public bool ContinueAfterProcessing
  10:          {
  11:              get { return _continueAfterProcessing; }
  12:              set { _continueAfterProcessing = value; }
  13:          }
  15:          private bool _stopOnError;
  17:          public bool StopOnError
  18:          {
  19:              get { return _stopOnError; }
  20:              set { _stopOnError = value; }
  21:          }
  23:          public XElement Configuration { get; set; }
  25:      }

The XML based Node Configuration will look something like this:

<?xml version="1.0" encoding="utf-8" ?>
      <Assembly Name="PhoneService" Value="FULL NAME OF YOUR ASSEMBLY"></Assembly>
      <Node Name="PersistMessageProcessor" AssemblyRef="PhoneService" Class="FULL NAME OF CLASS TO SAVE MESSAGE IN DATABASE" ContinueAfterProcessing="true" StopOnError="false">
      <Node Name="HelpTextProcessor" AssemblyRef="PhoneService" Class="FULL NAME OF A CLASS TO SEND A RESPONSE WHEN  INCOMING MESSAGE IS TO SEEK HELP" ContinueAfterProcessing="false" StopOnError="false">
            <Phrase>help me</Phrase>
            <Phrase>help please</Phrase>
            <Phrase>what are my options?</Phrase>

As you can see, there are two examples that I have configured. The intention of the first node is to save the message in the database. The intention of 2nd node is to figure out if user is looking for help and if so, send a help response which is also configurable. Note that Configuration element in above XML has particular schema, the framework (node processor) will pass the entire configuration and it will be up to the node implementation to figure out how to use it.

So how does my HelpTextProcessor look like? well, i think that’s a good example which complete the loop in understanding how exactly it works. And here it is:

 public void ProcessMessage(SmsInfo message, Models.SmsProcessingEventProperties eventProps)
            if (eventProps.Configuration == null)
                eventProps.Status = Models.SmsProcessingEventStatus.Skipped;
                foreach (var phrase in eventProps.Configuration.Descendants("Phrase"))
                    if (phrase.Value.ToLower() == message.Message.ToLower().Trim())
                        eventProps.Status = Models.SmsProcessingEventStatus.Processed;
                        eventProps.Response = eventProps.Configuration.Descendants("Response").FirstOrDefault().Value.Trim();
            catch (Exception ex)
                eventProps.Status = Models.SmsProcessingEventStatus.Error;
                eventProps.ErrorMessage = ex.Message;

Thursday, March 26, 2015

SharePoint 2013: Quick way to open all messages in ULS for a Correlation ID

SharePoint often gives a correlation ID which can be looked into ULS Logs to troubleshoot issues. Until recently, I used to go to individual servers and fire up the ULSViewer, to see the logs. Sometime, I used to access logs from other servers and open a multiple instances of ULS viewer. It was not always convenient to open multiple log files.

Recently, I came across a new PowerShell Command to Merge the log based on criteria given. Criteria could be Diagnostic Area, Category, Start Time, End time etc. For the purpose of this post, I’d use the correlation id.

So there is a Merge-SPLogFile Cmdlet to merge all log files from all servers and put it into a single file which can be opened into the ULS viewer.

Well, that’s good but I took it a step further to open the ULSViewer.exe and open the log file directly, saving myself some clicks!

Here is the script look like. It needs path to ULSViewer and prompt you for Correlation ID when you run the script.

   1:  $ulsViewerPath = "c:\Tools\ulsviewer\ulsviewer.exe"  
   3:  $correlationId = Read-Host -Prompt "Enter Correlation ID"  
   6:  $fileName = [String]::Format("C:\temp\{0}-{1}.log", [DateTime]::Now.ToString("yyyyMMddHHmmss"), $correlationId);  
   7:  Merge-SPLogFile -Correlation $correlationId  -Path $fileName  
  10:  [Diagnostics.Process]::Start($ulsViewerPath,$fileName)  

So when I run this script with a correlation that SharePoint just gave me in UI, It saves the log file in C:\temp directory and automatically opens the ULS viewer with relevant log entries as shown below:


Hope that helps you save some time.

Wednesday, February 18, 2015

Better SharePoint Content Authoring:: Improving Navigation To Lists/Libraries

Once again, I’ll start with a Picture that represents the purpose of this Post. As you can imagine, I try to make it easy for my content authors to Navigate to different lists and libraries where the user is supposed to navigate to manage the content.


Sure you can ask the users to go to “View All Site Content” or access some of these links for “Site Settings” but you are able to save thousands of clicks over a period of time by making these links available in “Site Actions” menu. They don’t have to remember to go to “All Site Content” to manage lists and “Site Settings” to manage the term store or the navigation. That’s very inconvenient for my content authors.

Doing this is actual simple. All you need to do is:

1. Understand that the Site Actions can be extended programmatically and declaratively. I’ll talk about declarative extension in this post. You can get a fair idea about what you can do by observing how SharePoint is doing it. And those customizations can be found here


2. You can have your own customizations by putting your entries into Master Page Gallery –> Editing Menu –> CustomSiteAction.xml

For some of above customizations, the content of CustomSiteAction.xml in master page gallery would look like this:

<?xml version="1.0" encoding="utf-8" ?>
    <reference TagPrefix="cms"
      assembly="Microsoft.SharePoint.Publishing, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
      namespace="Microsoft.SharePoint.Publishing.WebControls.EditingMenuActions" />
       DisplayText="Ashish's Extended Menu"
       Description="Helps you navigate to different areas of the site."
      <ConsoleNode NavigateUrl="_layouts/AreaNavigationSettings.aspx"
           DisplayText="Quick Launch Links"
           Description="Manage Quick Launch Area Links"
      <ConsoleNode NavigateUrl="PublishingImages/Forms/Thumbnails.aspx"
          Description="Takes you to images library"
      <ConsoleNode NavigateUrl="Lists/faqs"
             DisplayText="Frequently Asked Querstions"
             Description="Manage FAQs displayed on the site."
      <ConsoleNode NavigateUrl="Lists/Announcements"
         Description="Manage Announcements Displayed on the site."
      <ConsoleNode NavigateUrl="Pages/Forms/AllItems.aspx"
            Description="Takes you to pages library"

Note that you can show/hide these links based on the permissions. I think this is a great way to make a difference in life of your SharePoint content authors.

Saturday, February 14, 2015

AngularJS: Building a Directive that Helps Choose a Date

This is my first AngularJS posts! I am so excited to get it started…

I just finished working on my third AngularJS project. I have used AngularJS in Provider Hosted, SharePoint Hosted and an IIS web application now. I must say that my AngularJS skills and code got better with every project. My last two projects included all best practices mentioned by John Papa:

I wrote a whole bunch of directives and I will talk about them in future posts. I’ll start with very simple (and smart :)) directive. Basically, it helps my users to choose a date quickly. I used a date picker that comes with the UI bootstrap: to get started and provided my custom directive on top of it which helps you select today’s with one click and move the date forward or backward by a configurable interval . See picture below:


You can use the directive like this:

<div st-date-provider st-config="{title:'Today', daysOffset:1}" st-model="vm.filterC.deliveryDate"></div>

The st-config represents a JavaScript object with following properties

titleRepresents the title that you want to display to represent “Today”
daysOffsetNumber of days you want to allow your users to move forward or backward.
minusTitleText you want to display for backward button. Optional.

For example, you can set daysOffset = 7 and set minusTitle to “- Week”
plusTitleText you want to display for forward button. Optional

Another way you can set it up is:

<div st-date-provider st-config="{title:'Today', daysOffset:7,  minusTitle:'Week', plusTitle:'Week'}" st-model="vm.filterC.deliveryDate"></div>


The dependencies for this directive is:

1. moment.js library for date manipulation


And finally the code for the Directive is:

app.controller('stDateProviderCtrl', ['$scope', 'common', function ($scope, common) {
$scope.provideDate = provideDate;
function provideDate(multiply) {
if (angular.isNumber($scope.stConfig.daysOffset)) {
if (!angular.isDate($scope.stModel)) {
$scope.stModel = common.getToday();

$scope.stModel = moment($scope.stModel).add('days', $scope.stConfig.daysOffset * multiply).startOf('day').toDate();

app.directive('stDateProvider', ['config', function (config) {
var directive = {
restrict: 'EA',
controller: 'stDateProviderCtrl',
scope: {
stModel: "=",
stConfig: "="

template:   '<span>' + 
                            '<span class="pointer" ng-click="provideDate(-1)"><i class="fa fa-chevron-circle-left"></i></span>'
                            '<span class="pointer" ng-if="stConfig.minusTitle" ng-click="provideDate(-1)"> {{stConfig.minusTitle}}</span>'
                            '<span class="pointer" style="margin-left:10px;margin-right:10px" ng-click="provideDate(1)">{{stConfig.title}}</span>'
                            '<span class="pointer" ng-if="stConfig.plusTitle" ng-click="provideDate(1)">{{stConfig.plusTitle}} </span>'
                            '<span class="pointer" ng-click="provideDate(1)"><i class="fa fa-chevron-circle-right"></i></span>'
return directive;



Please leave your comments! Tags: ,

Better SharePoint Content Authoring – Hide Sections that have no data

Presenting another trick for an improved SharePoint Content Authoring experience. As they say, picture says thousands words, I will present the problem and the solution in picture first:

Consider this page and the problem:


the solution:


But what about this page in edit mode?


I know some of you may be thinking that this is so easy to do using JavaScript. Personally, I don’t like that approach because you will have to hide it based on element targeting and DOM manipulation. I think that’s not a robust solution because.. 1) it breaks when the element targeting fails (ID, or class name of DOM element changes 2) If you don’t implement it right, you will see “Description” first and then it  will magically disappear. That’s unacceptable for me.

Let’s talk about a better approach…

Well, my approach is it wrap the field heading (“Description” in this case)  into a Panel control which checks if the provided field has a value. If it has a value show the field heading panel, otherwise hide it.

It’s better because…..

1) It does not have two problems that I described above with JQuery approach

2) Unlike JavaScript approach, this solution is reusable. My favorite reason.

Here is my code for the panel look like:

public class FieldHeadingPanel : Panel, INamingContainer, IParserAccessor
private bool _shouldRender = false;
private bool _evaluated = false;

public string FieldGuid { get; set; }
public bool ShowInEditMode { get; set; }
protected override void AddParsedSubObject(object obj)
if (!_evaluated)
_evaluated = true;

if (_shouldRender)


private void ShouldRender()
_shouldRender = false;
if (!PublishingPage.IsPublishingPage(SPContext.Current.ListItem) || string.IsNullOrEmpty(FieldGuid))

_shouldRender = false;
// check if edit mode
                    if (SPContext.Current.FormContext.FormMode == Microsoft.SharePoint.WebControls.SPControlMode.Edit && ShowInEditMode)
_shouldRender = true;
SPField field = SPContext.Current.ListItem.Fields[new Guid(FieldGuid)];
_shouldRender = !string.IsNullOrEmpty(field.GetFieldValueAsHtml(SPContext.Current.ListItem[new Guid(FieldGuid)]));

catch (Exception ex)


Basically, the panel will render content inside it only if the provided field has a value in it. I have tested it with Person, Text, Note and Summary Links field controls and it works. I’ll modify the code if it does not work for certain fields.

This class is part of my SharePoint Reusable Application framework which I use from one project to another.

You will put the following markup in your Page Layout.

   1:  <Framework:PageModePanel runat="server" ShowMode="Display" id="pnlDisplayOnly" >
   2:          <BEX:FieldHeadingPanel runat="server" FieldGuid="9da97a8a-1da5-4a77-98d3-4bc10456e700" ShowInEditMode="true">
   3:          <h2>
   4:              Description
   5:          </h2>
   6:     </Framework:FieldHeadingPanel>

Hope this helped you, feel free to leave comments!