Tuesday, 21 February 2017

"No items" message in CSR - SharePoint 2013

CSR (Client Side Rendering) is one of the most powerful yet simple tools to modify list views as per the client's wish. Recently while I was implementing a CSR script, during testing I found that when the list filter view had no items, the page was looking empty and did not have any message to inform the user that there are no items.
On some research, it was found that the default "no items" message is part of the footer and not the body. Hence, we need to check the row length and then set the message accordingly in the footer function. Below is the code for the same.

(function () {
    var overrideCtx = {};
    overrideCtx.Templates = {};
    
    overrideCtx.Templates.Footer = CustomFooter;
}}();

function CFooter(ctx) {
var html = "";
if(ctx.ListData.Row.length > 0)
{
html = Your custom footer html comes here
}
else
{
html = "<div>There are no items to show.</div>";
}

This will ensure that the end user will not see an empty page when there are no items.

Tuesday, 24 May 2016

SharePoint apps - Iframe issue and form Save button issue

It is very common for us to try and connect to a default SharePoint page (ex, newform.aspx, editform.aspx) from an app, be it SharePoint/Provider hosted.
I had an Azure hosted app and there is a link in the app page to open the 'New Form' of a custom list. When we click on the link, nothing happens, the form doesnt open in a pop-up.
This is mainly because 'Framing' has been disabled by default. To resolve this, add the below piece of code in your masterpage/page layout

<WebPartPages:AllowFraming runat="server"/>

At this point you should be able to open up the default 'SharePoint pages' as pop-ups from an app.

There was another problem I faced once the new form had opened up. I was unable to hit the 'Save' button after filling the form. This is because the form is unsure which the parent window is, as the app redirection url doesnt have the SharePoint site's url. I was able to solve this by using the below code.
Add it in your 'newform.aspx' page either in a content editor or a script web part.

function PreSaveAction()
{
parent.window.location.href = document.referrer;
return true;
}

Now you should be able to submit default forms from an app!

Wednesday, 4 July 2012

Sharepoint workflow - deploy without Designer!

We all know how powerful and important workflows are in SharePoint 2010. Today, let's create a reusable workflow which can be deployed to a production farm without using SharePoint Designer.

There are a lot of firms which are skeptical about enabling SharePoint Designer on their production farm (due to security reasons supposedly!), but they have requirements that involve developing workflows, invariably SharePoint Designer is the one of THE tools used for such a requirement.

So, let's assume a requirement for our workflow.
1. There is a custom list which inherits from a custom content type on a team site, this list contains multiple columns out of which there exists a "Status" column.
2. When there is a change in the Status column, mails are sent out to certain users with custom content in the body of the mail.

Here is the catch, we have to deploy this solution to production without using Designer.

When I first thought about how this can be achieved, there were a lot of options that were to be looked at while creating the workflow in designer. On investigation, content type association was the best way to implement this, but then again not everything comes without a price. Here, we have to make sure that the content type Id to which the workflow would be associated to would remain constant across Dev, UAT, Test, Prod farms.

Below are steps to be followed when creating such a reusable workflow.

1. Create a new reusable workflow, associate with content type in Sharepoint Designer.
2. Choose the Custom Content type which would have been created.

3. Make changes accordingly to your workflow emails.
4. Save, Publish and "save as template" the workflow, this would get saved a .wsp file.  IMPORTANT!
5. Create the custom content type (that was created in dev) with the same guid in your UAT/Test/Prod farm. I have added some xml code and a powershell script below which will help you do the same. Create a list which inherits from this new custom content type.
Use the below code to create a content type, modify the fields as required and save it as a .xml file. Make sure your copy the right guid for the content type.

<?xml version="1.0" encoding="utf-8"?>

<ContentTypes>
<ContentType ID="0x01001BDC25A2E5635040AF4C0BEB48E7DAF6" Name="CustomWfContent" Group="WorkflowContent" Version="1"><Folder TargetName="_cts/CustomWfContent" /><Fields><Field ID="{c042a256-787d-4a6f-8a8a-cf6ab767f12d}" Name="ContentType" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="ContentType" Group="_Hidden" Type="Computed" DisplayName="Content Type" Sealed="TRUE" Sortable="FALSE" RenderXMLUsingPattern="TRUE" PITarget="MicrosoftWindowsSharePointServices" PIAttribute="ContentTypeID" Customization=""><FieldRefs><FieldRef ID="{03e45e84-1992-4d42-9116-26f756012634}" Name="ContentTypeId" /></FieldRefs><DisplayPattern><MapToContentType><Column Name="ContentTypeId" /></MapToContentType></DisplayPattern></Field><Field ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="Title" Group="_Hidden" Type="Text" DisplayName="Title" Required="TRUE" FromBaseType="TRUE" Customization="" ShowInNewForm="TRUE" ShowInEditForm="TRUE" /><Field Type="Choice" DisplayName="QuestionStatus" Required="FALSE" EnforceUniqueValues="FALSE" Indexed="FALSE" Format="Dropdown" FillInChoice="FALSE" Group="Custom Columns" ID="{f00d1f96-c728-4a09-bfc3-c585af475c1c}" SourceID="{4715a8a4-e48c-4687-9b42-0bb680c97edd}" StaticName="QuestionStatus" Name="QuestionStatus" Customization=""><Default>Unanswered</Default><CHOICES><CHOICE>Unanswered</CHOICE><CHOICE>In Progress</CHOICE><CHOICE>Commercial</CHOICE><CHOICE>Actioned</CHOICE><CHOICE>Complete</CHOICE></CHOICES></Field></Fields><XmlDocuments><XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms"><FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms"><Display>ListForm</Display><Edit>ListForm</Edit><New>ListForm</New></FormTemplates></XmlDocument></XmlDocuments></ContentType> </ContentTypes>

In case you want to generate a Content type xml file like the above one you can use the code below


#Powershell to output the content type to a Xml file
$sourceWeb = Get-SPWeb http://yourwebsite

$xmlFilePath = "C:\Script-SiteContentTypes.xml"
#Create Export File
New-Item $xmlFilePath -type file -force
#Export Content Types to XML file
Add-Content $xmlFilePath "<?xml version=`"1.0`" encoding=`"utf-8`"?>"
Add-Content $xmlFilePath "`n<ContentTypes>"
$sourceWeb.ContentTypes | ForEach-Object {
    if ($_.Name -eq "AgentPortalCustomContentType") {
        Add-Content $xmlFilePath $_.SchemaXml
    }
}
Add-Content $xmlFilePath "</ContentTypes>"
$sourceWeb.Dispose()



Below is the powershell script which creates the content type as saved in the .xml file. 
$destWeb = Get-SPWeb http://yourwebsite
$xmlFilePath = "C:\Script-SiteContentTypes.xml"


#create the columns required for the content type
$fields = $xmlFile.SelectNodes("/ContentTypes/ContentType/Fields/Field")
$fields | Foreach-Object{
$fieldXml = $_.OuterXml.ToString()
$destWeb.Fields.AddFieldAsXml($fieldXml)
write-host "Column " $_.Name "created"
}
$destWeb.update()


#Create Site Content Types
$ctsXML = [xml](Get-Content($xmlFilePath))
$ctsXML.ContentTypes.ContentType | ForEach-Object {
    #Create Content Type object inheriting from parent
    $spContentType = New-Object Microsoft.SharePoint.SPContentType ($_.ID,$destWeb.ContentTypes,$_.Name)
    
    #Set Content Type description and group
    $spContentType.Description = $_.Description
    $spContentType.Group = $_.Group
    
    $_.Fields.Field  | ForEach-Object {
        if(!$spContentType.FieldLinks[$_.DisplayName])
        {             #Create a field link for the Content Type by getting an existing column
            $spFieldLink = New-Object Microsoft.SharePoint.SPFieldLink ($destWeb.Fields[$_.DisplayName])
        
            #Check to see if column should be Optional, Required or Hidden
            if ($_.Required -eq "TRUE") {$spFieldLink.Required = $true}
            if ($_.Hidden -eq "TRUE") {$spFieldLink.Hidden = $true}
        
            #Add column to Content Type
            $spContentType.FieldLinks.Add($spFieldLink)
        }
    }
    
    #Create Content Type on the site and update Content Type object
    $ct = $destWeb.ContentTypes.Add($spContentType)
    $spContentType.Update()
    write-host "Content type" $ct.Name "has been created"
}
$destWeb.Dispose()

6. Go to your Site Settings -> Solutions -> Upload solution and choose the attached .wsp file and activate the solution in the ribbon.
7. Go to your Site Features and activate the feature which starts with “Workflow template”.
8. Now, go back to the list and add a new workflow for the list under the new content type.
9. Choose a name for the workflow and select the option which says the workflow will fire on “New” or “Edit” item and save.

There you go, now we just deployed a custom workflow without using designer!!!

Thursday, 28 June 2012

Performance enhancements in Sharepoint 2010 using Css sprites


Today, let's learn a technique that improves the performance of your web page. It's the CSS sprite!

The idea of this technique is to combine all the images that you use in your web page into one single image and seperate out each image using some simple css code.

So, here's how you get started on the sprites in the world of web.

Step 1:
Google for any site that generates a css sprite image for you, you can also download tools for the same purpose but online is much quicker according to me.

I generally use InstantSprite.com. Once you are in the site, upload all your images on it, it immediately creates the sprite image, the corresponding css code. You can also specify the gap between each image if you want.

 


Step 2:
Save the css sprite image on to your desktop from the site, copy paste the css code into a notepad for future reference.

Step 3:
Now, let's make use of what has been generated. In order to use the new image named csssprite.png for ex in your project, add it to the mapped images folder. Then, copy the txt file which contains the css code also into the mapped layouts folder. Open the .aspx page for ex where you have to use the new image. Now, modify the "src" to point to the new image and provide the style containing the "top","left","width" and "height" values, this the most Important step in the whole process.

Make sure you use the corresponding css code which points to that image, this should be fetched from the .txt file which has the css code.

Typical Css Sprite image


Typical Css Sprite code
.sprite { background: url('sprite.png') no-repeat top left; width: 96px; height: 96px;  }
.sprite.Alien1 { background-position: 0px 0px;  }
.sprite.Alien2 { background-position: 0px -106px;  }
.sprite.Balloon { background-position: 0px -212px;  }


Example of a css sprite image being used in an .aspx page
<img id="imgPictY" src="/_layouts/images/MyWebpart/webpartCssSprite.png"
                runat="server" style="top: -52px;left:0px;position:absolute;" /> 

Step 4:
Once the changes have been made and the solution has been updated, you need to now check that the new images are being loaded. You might need to clear your browser's cache before reloading. To make sure the right image is loaded, i generally use HttpWatch and look at the round trip calls which will have the call made to the new css sprite image.

Advantages of using Css sprite
1. Reduces the number of calls to the server.
2. Easy to use, modifying the code is uncomplicated.
3. All you images are stiched into one, hence images called in different locations are loaded even before the page is accessed.

Disadvantages of using css sprite/ Things to be aware of before creating one
1. Adding too many images together in one css sprite image can increase the image size and hence the load time of the page
2. Updating an existing css sprite image is not the easiest and has to done with precision to make sure the css code doesnt change for old images.

All in all, we found that using this techinque in our project has worked wonderfully well and has reduced the calls to the server by 15%. This is a very powerful technique to improve the performance of your site if used in the right way.

Create custom Ribbon groups and show values in the ribbon

This is my first blog on Sharepoint customisations, finally have the time to give back to the Sharepoint community!

Let's play around with our very own Sharepoint 2010 ribbon today!

So bascially, what we are trying to do is add a custom group to an OOTB ribbon tab, show values related to the selected web part in a label or a textbox.
The challenge here is, unlike simple requirements like button click on the ribbon where we get an event to work with, showing values on load of the ribbon is much more difficult
as there is no particular custom event we can work with easily, the only one available is the ribbon on load, and its OOTB!

Lets get started with it then...
1. First we create the ribbon group within a OOTB Ribbon Tab, lets work with the ribbon available on the Media part for this.
    For this, we need to get the 'Location' value which will be specified in our 'CustomUIDefinition' absolutely right. Append '_children' to the 'location' in order to continue
adding the new group. Now, Add the group with required values like 'template' and 'id'. Then, add the required controls under the 'Controls' tab, here we will use a Label and a 
text box. Along with this we also specify the 'command' and the 'querycommand' which actions any required event.
    Next, very important!, to show the contents of the new group, we need to create a new element specifying the max size, this tells which group will have how much spacing in
order to show the contents with in. Again for this the exact location has to be specified


Code:
<CommandUIDefinition Location="Ribbon.ContextualTabs.Media.Options.Scaling._children">
            <MaxSize Id="Ribbon.ContextualTabs.Media.Options.Scaling.Url" GroupId="Ribbon.ContextualTabs.Media.Options.Url" Size="MediumMedium" Sequence="50"/>
        </CommandUIDefinition>
        <CommandUIDefinition Location="Ribbon.ContextualTabs.Media.Options.Groups._children">
          <Group
              Id="Ribbon.ContextualTabs.Media.Options.Url"
              Sequence="40"
              Template="Ribbon.Templates.Flexible2"
              Title="Url"
              Description="">
            <Controls Id="Ribbon.ContextualTabs.Media.Options.Url.Controls" >
              <Label
                Id="Ribbon.ContextualTabs.Media.Options.Url.UrlLabel"
                Sequence="10"
                LabelText="Url"
                TemplateAlias="o1"
                />
              <TextBox
                Id="Ribbon.ContextualTabs.Media.Options.Url.UrlValue"
                Sequence="20"
                Command="Media.GetUrl"
                QueryCommand="Media.GetUrl.Query"
                TemplateAlias="o2"
                ToolTipTitle="Url"
                ToolTipDescription="To change the url of the video, go to 'Change Media' -> 'From Address'"
                ShowAsLabel="true"
                />
            </Controls>
          </Group>
        </CommandUIDefinition>

This is what you see when the above code is deployed

 
2. The next step is to create the javascript code required to show the values which we need on the ribbon. 
 The first step for this is to load the required javascript file through our CustomAction tab. This would be a loader file which would call all the required methods which in turn loads other javascript files which contains code for the actual work done behind.
One more challenge we might face here is how to load the .js files and when to load them, for us this file has to load only when the media web part is selected and not any where else as that can cause unnecessary memory blocks within the page load. The best way to do this is use _spBodyOnLoadFunctionNames.push() method. Then we need to register the .js file which contains the code for the init(), handlecommands(), etc. In the handlecommand we need to specify the 'command' and 'querycommand' used earlier, here the required code is added to get/set values in
our textbox.


Code:
  <CustomAction ScriptSrc="/_layouts/myRibbon/js/.PageComponent.Loader.js"
        Location="ScriptLink"  
        Sequence="1000" />
Code to be added in PageComponent.Loader.js
function myInit() {
    RegisterSod("myRibbon.pagecomponent.js", "/_layouts/myRibbon/js/myRibbon.PageComponent.js");
    SP.SOD.executeFunc("myRibbon.pagecomponent.js", null, null);
    ExecuteOrDelayUntilScriptLoaded(initRibbon, 'myRibbon.PageComponent.js');
}
function initRibbon() {
    myRibbon.PageComponent.initialize();
}
_spBodyOnLoadFunctionNames.push("myInit");


Code to be added in myRibbon.pagecomponent.js
Type.registerNamespace('myRibbon.PageComponent');
myRibbon.PageComponent = function () {
    myRibbon.PageComponent.initializeBase(this);
};
myRibbon.PageComponent.initialize = function () {
    ExecuteOrDelayUntilScriptLoaded(myRibbon.PageComponent.initializePageComponent, 'SP.Ribbon.js');
};
myRibbon.PageComponent.initializePageComponent = function () {
    var ribbonPageManager = SP.Ribbon.PageManager.get_instance();
    if (null !== ribbonPageManager) {
        ribbonPageManager.addPageComponent(myRibbon.PageComponent.instance);
    }
};
myRibbon.PageComponent.prototype = {
    init: function () {
    },
    getFocusedCommands: function () {
        return ['Media.GetUrl', 'Media.GetUrl.Query'];
    },
    getGlobalCommands: function () {
        return ['Media.GetUrl', 'Media.GetUrl.Query'];
    },
    canHandleCommand: function (commandId) {
        if (commandId === 'Media.GetUrl' ||
            commandId === 'Media.GetUrl.Query') {
            return true;
        } else {
            return false;
        }
    },
    handleCommand: function (commandId, properties, sequence) {
        if (commandId === 'Media.GetUrl.Query') {
            alert('the textbox values will show!')
        }
    },
    isFocusable: function () {
        return true;
    },
    receiveFocus: function () {
        return true;
    },
    yieldFocus: function () {
        return true;
    }
};
myRibbon.PageComponent.registerClass('myRibbon.PageComponent', CUI.Page.PageComponent);
myRibbon.PageComponent.instance = new myRibbon.PageComponent();
NotifyScriptLoadedAndExecuteWaitingJobs('myRibbon.pagecomponent.js');

This is the end result what you see...


Well, thats it for now with Ribbons in Sharepoint 2010.