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.

image

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

image

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" ?>
<Console>
  <references>
    <reference TagPrefix="cms"
      assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
      namespace="Microsoft.SharePoint.Publishing.WebControls.EditingMenuActions" />
 
  </references>
  <structure>
 
   
 
    <ConsoleNode
       DisplayText="Ashish's Extended Menu"
       Description="Helps you navigate to different areas of the site."
       UseResourceFile="false"
       MenuGroupId="310"
       Sequence="211"
       ImageUrl="/_layouts/images/ActionsSettings.gif"
       UserRights="BrowseDirectories|ManagePermissions|ManageWeb|ManageSubwebs|AddAndCustomizePages|ApplyThemeAndBorder|ManageAlerts|ManageLists|ViewUsageData"
       RequiredRightsMode="Any"
       PermissionContext="CurrentSite"
       NavigateUrl="javascript:"
       UIVersion="4"
       ID="ASHISH_Manage">
      <ConsoleNode NavigateUrl="_layouts/AreaNavigationSettings.aspx"
           DisplayText="Quick Launch Links"
           Description="Manage Quick Launch Area Links"
           PermissionContext="CurrentSite"
           UseResourceFile="false"
           IsSiteRelative="true"
           ImageUrl="/_layouts/images/ActionsSettings.gif"
           ID="NavSettings"
           MenuGroupId="310"
           Sequence="101"
           UserRights="ManagePermissions|ManageWeb|ManageSubwebs|AddAndCustomizePages|ApplyThemeAndBorder|ManageAlerts|ManageLists|ViewUsageData"
           RequiredRightsMode="Any"/>
      <ConsoleNode NavigateUrl="PublishingImages/Forms/Thumbnails.aspx"
          DisplayText="Images"
          Description="Takes you to images library"
          PermissionContext="CurrentSite"
          UseResourceFile="false"
          IsSiteRelative="true"
          ImageUrl="/_layouts/images/ital.png"
          ID="BrowseImages"
          MenuGroupId="310"
          Sequence="102"
          UserRights="AddListItems|EditListItems"
          RequiredRightsMode="Any"/>
      <ConsoleNode NavigateUrl="Lists/faqs"
             DisplayText="Frequently Asked Querstions"
             Description="Manage FAQs displayed on the site."
             PermissionContext="CurrentSite"
             UseResourceFile="false"
             IsSiteRelative="true"
             ImageUrl="/_layouts/images/itgen.png"
             ID="BrowseFAQS"
             MenuGroupId="310"
             Sequence="103"
             UserRights="AddListItems|EditListItems"
             RequiredRightsMode="Any"/>
      <ConsoleNode NavigateUrl="Lists/Announcements"
         DisplayText="Announcements"
         Description="Manage Announcements Displayed on the site."
         PermissionContext="CurrentSite"
         UseResourceFile="false"
         IsSiteRelative="true"
         ImageUrl="/_layouts/images/announce.gif"
         ID="BrowseAnnouncements"
         MenuGroupId="310"
         Sequence="104"
         UserRights="AddListItems|EditListItems"
         RequiredRightsMode="Any"/>
      
      <ConsoleNode NavigateUrl="Pages/Forms/AllItems.aspx"
            DisplayText="Pages"
            Description="Takes you to pages library"
            PermissionContext="CurrentSite"
            UseResourceFile="false"
            IsSiteRelative="true"
            ImageUrl="/_layouts/images/itdl.png"
            ID="BrowsePages"
            MenuGroupId="310"
            Sequence="106"
            UserRights="AddListItems|EditListItems"
            RequiredRightsMode="Any"/>
 
 
    </ConsoleNode>
 
  </structure>
 
</Console>




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: https://github.com/johnpapa/angularjs-styleguide

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: http://angular-ui.github.io/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:

SNAGHTML26a9d4b

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


















ParameterPurpose
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>



image


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();
                   
return;
               
}


               
$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>'
+
                        '</span>'
       
};
       
return directive;

   
}]);

 

Please leave your comments!


del.icio.us 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:

SNAGHTML16daf3

the solution:

SNAGHTML1a0e78

But what about this page in edit mode?

SNAGHTML223795

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;
               
this.ShouldRender();
           
}

           
if (_shouldRender)
           
{
               
base.AddParsedSubObject(obj);

           
}
       
}

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

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


               
}
           
}
           
catch (Exception ex)
           
{
               
AppContext.FromSPContext().Logger.LogException(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!

Thursday, February 12, 2015

Better SharePoint Content Authoring – Page Creating Experience – Part 1

There is no doubt that Publishing is one of the greatest feature of SharePoint, but its unfortunate that authoring content (or should I say most) is not easy in SharePoint. Although Authors can be trained, I wish SharePoint provided a better authoring experience. I have worked in several publishing sites now and I try to implement several “authoring patterns” which makes the life of Authoring folks easier.

How about the most important thing for any Publishing Site – the experience of creating a page? Here is what I think about it!

SNAGHTMLbd811f 

So what’s wrong with this:

1. There is no way to choose what type of page you want to create.

2. There is no choice to choose layout of the page.

What that means is that, SharePoint will use the default content type and page layout to create the page. The author will then need to choose the right page layout from the ribbon. Seriously, I can understand the pain here, I wonder what was the SharePoint team thinking when making “New Page” functionality available from “Site Actions” menu. Sure, there are different ways to create a page which allows you to choose the page layout but then, why two different experiences?

Anyways, I like the Authors to create the new page from Site Actions Menu, so I generally prefer to replace that page with my own page. Essentially, I allow the users to choose the content type and optionally a page layout. The end result looks like this:

SNAGHTML10292b3

I will blog about the implementation of this pattern in Part 2 of this series. If you are curious now, it’s a two step process: 1) Create a custom Application Page with code-behind to create the page and redirect to edit page 2) Replace the “New Page” link in the “Site Actions” menu.