Thursday, October 3, 2013

Custom Callouts in the SharePoint 2013 Metro UI, Part 6: How to update custom callout content on its opening

Previous: Custom Callouts in the SharePoint 2013 Metro UI, Part 5: Modification of the Document Library Item Callout Content
Back to contents.


How to update custom callout content on its opening

Summary

This article describes my approach to modification of the content inside the custom callout in real time - on every opening.

The Problem

Recently I was asked a question: is there any way to update custom callout's content on every opening because it's a quite useful and realistic scenario.

The Solution

I've made some research and have found that there's an addEventCallback method that allows us to subscribe to some events in the callout's lifecycle. Here's my solution: http://pastebin.com/ZM4Xs7Zj Please note that there are 4 available events: 'opening', 'opened', 'closing' and 'closed'. Another important part is that you have to find the element with the specific generated ID to change the real content of the callout without break of the other structure elements such as title and close button.

Sunday, August 25, 2013

Custom Callouts in the SharePoint 2013 Metro UI, Part 5: Modification of the Document Library Item Callout Content


Previous: Custom Callouts in the SharePoint 2013 Metro UI, Part 4: How to use callouts in autohosted app
Back to contents.




Modification of the Document Library Item Callout Content

Summary

This article describes my approach to modification of the content inside the callout that SharePoint 2013 renders for item inside the document library. The result of my research is a little javascript helper I wrote. You can find the code and the documentation on CodePlex at https://sp13doclibcalloutmodification.codeplex.com.

 

The Problem

SharePoint 2013 renders a special type of callout for every item inside the document library:


The quite popular question is – is it possible to modify the content of this callout? I have never seen the answer so I have decided to do a little research.

 

The research

I thought the new SharePoint client rendering templates feature could help to customize this content so I have tried to find the correct template to override. However, I have found that it is not the case and the structure and the data of the content area for this callout is strongly hardcoded inside the out-of-the-box clienttemplates.js file:

function CalloutRenderItemTemplate(renderCtx) {
    var ret = [];

    if (renderCtx.ListSchema.IsDocLib)
        ret.push(CalloutRenderFilePreview(renderCtx));
    ret.push(CalloutRenderLastModifiedInfo(renderCtx));
    ret.push(CalloutRenderSharingStatus(renderCtx));
    ret.push(CalloutRenderSourceUrl(renderCtx));
    return ret.join('');
}



The code of this function leads us to the following facts:

1.       Structure. There are 4 different “sections” of this callout:

  • File content preview area
  • The date of the last file modification
  • The information on the file sharing parameters
  • The absolute URL for downloading this file

2.       Context. This function itself and the section rendering functions inside it use some kind of context – it contains the data about the item we render, the parent list, and the parent web and so on.

3.      No modification. This structure is hardcoded and cannot be modified to hide some sections or add anything new

 

The Solution

My approach to solve this problem is to substitute this function at runtime by slightly modified copy. Of course, it is not perfect to change the out-of-the-box code of SharePoint but it looks we have no other choice there. Besides this function is a simple wrapper to the underlying sections rendering functions so I think it is quite safe to alter it. 

I have made the modified version of the function and have added the ability to configure the following:
  • You can hide any of the four out-of-the-box sections
  • You can specify the callbacks to render the new sections before or after any of the standard sections

 

The Library

I have implemented the code in the new javascript file – it defines all the code for the new function and the code to substitute the original function at runtime. Therefore, you have to do the following actions to let this helper library work:
  1.  reference the library file CalloutRenderItemTemplateCallbacks.js inside the page where you want to add callout modification
  2. define your own javascript code inside the same page to set up the library configuration.
Please note that you don’t need to modify any of the Sharepoint system files at all!

Hiding of the standard sections

If you want to hide any of the standard sections in this callout, you have to set the appropriate flag for target section:

CalloutRenderItemTemplateCallbacks.SharingStatus.renderSection = false;

Rendering of the new section

If you want to add a new section before or after any of the standard section, you have to specify the rendering callback function:
CalloutRenderItemTemplateCallbacks.LastModifiedInfo.Before = function(renderCtx)
{
 // generate the content for the section based on current rendering context
var newSectionHtml = ‘
This is the content of the new callout section.
’ return newSectionHtml; }
It’s the simplest code of the callback made for the sake of simplicity of the example. Now let us see the more realistic example. Now we are rendering the data of the field of the list item we render:

CalloutRenderItemTemplateCallbacks.LastModifiedInfo.Before = function(renderCtx)
{
 // Create the id for the div inside our section markup where we will place new content.
 // We create the dynamic Id based on the item being rendered. We can get the current item from the rendering context.
 // Please note that we can get an info on the item being rendered from the supplied rendering context ('renderCtx' parameter)
 var id = 'calloutAdditionalInfoSection_' + renderCtx.CurrentItem.ID;

 // Let's define the function to render the content of the new section inside the created container div
    function fillSection(sender, args){
     // Find the container inside the created section
     var div = document.getElementById(id);

     // Add the content of the item's field via Client Side Object Model (CSOM)
  div.textContent = oListItem.get_item('DocLibCalloutPreviewTestColumn');
 }

 // Load the item data via rendering context and CSOM
 var clientContext = SP.ClientContext.get_current();
 var oList = clientContext.get_web().get_lists().getById(renderCtx.listName);
 var oListItem = oList.getItemById(renderCtx.CurrentItem.ID);
 clientContext.load(oListItem);
 clientContext.executeQueryAsync(Function.createDelegate(this, fillSection));

 // return the new section HTML
    return  'LastModifiedInfo.Before for file "' + renderCtx.CurrentItem.FileLeafRef +  '"'; 
}
// Hide the out-of-the-box Sharing Info section
CalloutRenderItemTemplateCallbacks.SharingStatus.renderSection = false;


Pleace note that the rendering context (renderCtx param) is quite powerful and data rich. It contains the info about the current item, current list, current web and much more – you can inspect its properties in the browser debugger. Here’s the result (Share Info section is hidden and the new section is added:

The Source

You can find the code of the library on the Codeplex project site I have made for it: https://sp13doclibcalloutmodification.codeplex.com. There you can find the library itself and the documentation. I hope my little research helps someone!


Previous: Custom Callouts in the SharePoint 2013 Metro UI, Part 4: How to use callouts in autohosted appBack to contents.

Thursday, April 25, 2013

Custom Callouts in the SharePoint 2013 Metro UI, Part 4: How to use callouts in autohosted app

Previous: Custom Callouts in the SharePoint 2013 Metro UI, Part 3: CalloutManager 
Next: Custom Callouts in the SharePoint 2013 Metro UI, Part 5: Modification of the Document Library Item Callout Content
Back to contents.



The autohosted application in SharePoint 2013 requires some configuration for using the calouts framework inside the app frame:
1. You need to reference a bunch of javascript files from the hosting portal supporting callouts framework.
2. You need to reference core CSS styles from the hosting portal because they are defining some important properties of the callout markup.

How to Reference the Required Javascript files


Microsoft.Ajax.js

The most base JS file you need to reference is Microsoft.Ajax.js. You don't need a link to this file because the easiest way to render it is to use the ScriptManager control - it renders the content of this file from the embedded resource:

<asp:ScriptManager runat="server"></asp:ScriptManager>


If your app is not written in ASP.NET you can use a CDN version of this file: https://ajax.aspnetcdn.com/ajax/4.5/6/MicrosoftAjax.debug.js

How to get the hosting portal url

In order to reference the callout framework JS files from the hosting portal you need to obtain the portal url. Your app receives it in the "SPHostUrl" parameter in its own url. In ASP.NET it's convenient to save it to the public page property and to reference it from the page markup. Here I save it to the HostWebUrl property:
public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            // The following code gets the client context and Title property by using TokenHelper.
            // To access other properties, you may need to request permissions on the host web.

            var contextToken = TokenHelper.GetContextTokenFromRequest(Page.Request);
            var hostWeb = Page.Request["SPHostUrl"];
            HostWebUrl = hostWeb;

            using (var clientContext = TokenHelper.GetClientContextWithContextToken(hostWeb, contextToken, Request.Url.Authority))
            {
                clientContext.Load(clientContext.Web, web => web.Title);
                clientContext.ExecuteQuery();
                Response.Write(clientContext.Web.Title);
            }
        }

        public string HostWebUrl { get; set; }
    }

The required JS files

Now we can easy reference the required files. I've used the debug versions of these files - of course you can use the release (without .debug suffix). Please note that the order of links is important because some of these files depends on another. There are some localized versions of scripts here- you should use the locale of your hosting portal (in my case it is the English one - 1033):

        <script type="text/javascript" src="<%=HostWebUrl %>/_layouts/15/1033/initstrings.debug.js"></script>
        <script type="text/javascript" src="<%=HostWebUrl %>/_layouts/15/1033/strings.js"></script>
        <script type="text/javascript" src="<%=HostWebUrl %>/_layouts/15/init.debug.js"></script>
        <script type="text/javascript" src="<%=HostWebUrl %>/_layouts/15/core.debug.js"></script>
        <script type="text/javascript" src="<%=HostWebUrl %>/_layouts/15/mquery.debug.js"></script>
        <script type="text/javascript" src="<%=HostWebUrl %>/_layouts/15/callout.debug.js"></script>

How to Reference the Required CSS

The required styles defined in the corev15.css. We can reference it in that way:
<link href="<%=HostWebUrl %>/_layouts/15/1033/styles/Themable/corev15.css" rel="stylesheet" type="text/css"/>

The complete ASP.Net Example

That's all! Here you can find the complete working ASP.Net example: http://yadi.sk/d/QmnPu_Qh4Ka4c. Here's the result page:





Previous: Custom Callouts in the SharePoint 2013 Metro UI, Part 3: CalloutManager 
Next: Custom Callouts in the SharePoint 2013 Metro UI, Part 5: Modification of the Document 
Back to contents.

Thursday, November 8, 2012

SharePoint Foundation 2013 RTM and the 401 Unauthorized error

I've discovered one of the reasons for the 401 Unauthorized error in the SharePoint Foundation 2013 RTM. I lost the access to the home page and my research showed that it was caused by the activation of the "Minimal Download Strategy" feature. The solution is to open the site settings page directly and to disable this feature. Then you will be able to open site home page again. I don't know the real reason for such error but it's the way to restore things back. Hope it helps somebody. :)

Tuesday, August 21, 2012

Custom Callouts in the SharePoint 2013 Metro UI, Part 3: the CalloutManager

Previous: Custom Callouts in the SharePoint 2013 Metro UI, Part 2: Actions
Back to contents.


CalloutManager allows to get or create an instance of the callout and manage its state.


How to Get a Callout

If you need to manage already created callout you have to get a reference to the desired instance. The CalloutManager has several methods to do it.


getFromLaunchPoint

This method allows to get an instance of the callout from its launch point. The only argument is the DOM element that is used as a launch point for the desired callout:
// choose the launch point HTML element
var launchPoint = document.getElementById('calloutDiv');
// get the callout
var callout = CalloutManager.getFromLaunchPoint(launchPoint);

If there's no defined callout for the specified launch point the error will be raised.


getFromLaunchPointIfExists

It's the variation of the getFromLaunchPoint (see above) method. The only difference is that there will be no error if the callout isn't founded. In that case the method simply returns null. it allows to check for the existence of the callout instance silently.

// get the callout
var callout = CalloutManager.getFromLaunchPointIfExists(launchPoint);
if (callout == null)
{
   // create
}

getFromCalloutDescendant

This method allows to get a callout instance from the DOM element located inside it's structure. It allows for example to get the callout from its content. The only argument is the DOM element. If the chosen DOM element is not inside the callout or the callout is not found for the some reason the error will be raised.

// choose the descendant HTML element
var descendant = document.getElementById('descendantDiv');

// get the callount
var callout = CalloutManager.getFromCalloutDescendant(descendant);

How to Create a Callout

There are two methods to create a callout. Both methods accept a callout creation options object and return the created callout instance. For the details of the creation see the first article of the series.


createNew

The method createNew which allows to create a callout is described in the first part of this series.


createNewIfNecessary

The method createNewIfNecessary is almost identical to the createNew but creates the callout only if there's no callout for the target launch point already. If there's a callout it will be returned instead of the creation of the new one.

var callout = CalloutManager.createNewIfNecessary(calloutOptions);

How to Remove a Callout

If you need to remove the callout you need to pass it to the remove method of the CalloutManager:

// get the callout
var launchPoint = document.getElementById('calloutDiv');
var callout = CalloutManager.getFromLaunchPoint(launchPoint);
// remove
CalloutManager.remove(callout);

How to Enumerate All Callouts

If you need to get all the defined callouts you can use the forEach method of the CalloutManager:

// close all the callouts on the page
CalloutManager.forEach(function(callout) {
   // close the current callout
   callout.close();
   }
});

You have to specify a function to apply for each callout. The only argument of this function is the callout instance currently being processed.


How to Close All Opened Callouts

The previous section shows how you can close all the callouts enumerating them by forEach method. But for this particular task the CalloutManager has the helper shortcut function - closeAll:

// close all the callouts;
var isAtLeastOneCalloutClosed = CalloutManager.closeAll();

This function return true if at least one callout was closed.


Previous: Custom Callouts in the SharePoint 2013 Metro UI, Part 2: Actions
Back to contents.

Thursday, August 2, 2012

Custom Callouts in the SharePoint 2013 Metro UI, Part 2: Actions

Previous: Custom Callouts in the SharePoint 2013 Metro UI, Part 1: Basics

Back to contents.

Next: Custom Callouts in the SharePoint 2013 Metro UI, Part 3: CalloutManager


As we can see from the previous part of this series, you can define a set of menu items in your custom callout. These items are called actions.

To define a simple action, you need to:

  1. Configure action creation options
  2. Create an action instance
  3. Add the created instance to the callout

Configure action creation options

Action creation options are being configured via CalloutActionOptions object.

// configure callout action options
var actionOptions = new CalloutActionOptions();
actionOptions.text = 'Action 1';
actionOptions.onClickCallback = function(event, action)
{ 
    // do some work
};

And here’s the result (for the callout creation see the previous article):

Here we’ve defined the required minimum of options – the text and the click event handler.

text

The text is required option. It’s a string.

onClickCallback

It’s the function that handles the action click event. It is required if your action is simple and it doesn’t have any menu items (see below for the menu action details). This function receives two arguments. First argument is the standard HTML DOM event caused by the click on the action. The second parameter is the action itself:

actionOptions.onClickCallback = function(evt, action)
{ 
    // get the event data
    alert(evt.clientX + ' ' + evt.clientY);

    // get the action text
    alert(action.getText());
};

Create an action instance

To create an action instance you need to pass the prepared action options to the Action instance constructor:

// create an action
var calloutAction = new CalloutAction(actionOptions);

Add the action to the callout

The last requirement is to add the created action to the callout’s actions collection:

// add the action to the callout
callout.addAction(calloutAction);

Additional action options

In addition to the required options for the action you can configure some additional but not required details.

tooltip

It defines the standard browser tooltip for the action which shows on the mouse over.

actionOptions.tooltip= 'Action 1 tooltip';

disabledTooltip

It’s the tooltip for the disabled state – it allows providing the information concerning the inaccessibility of your option.

actionOptions.disabledTooltip = 'Action 1 disabled tooltip';

isEnabledCallback

And here’s the way to rule the availability of your option:

actionOptions.isEnabledCallback = function (action) 
{
    // get the action text
    alert(action.getText());

    // set our action as enabled
    return true;
};
The only argument of this function is the action instance itself. The function must return a Boolean value indicating the availability of the action. If the action is disabled it will be grayed out:
If the isEnabledCallback function is not defined the action will be always available. The availability is being evaluated on each opening of the callout.

isVisibleCallback

This function helps to define the visibility of the action. It work exactly the same as the isEnabledCallback (see above) in any detail:

actionOptions.isVisibleCallback = function (action) 
{
    // get the action text
    alert(action.getText());

    // set our action as visible
    return true;
};

Actions with the dropdown menu

The last but not least option for the action configuration is the menuEntries property of the CalloutActionOptions object. It helps to define a special kind of the action – the one with the dropdown menu of options instead of a simple text. It allows combining several actions in the related and compact groups in order to save space, providing some logical structure for the actions list and adhering to the SharePoint Metro UI design principles.

If there are any items in the menuEntries, the action becomes a dropdown menu. In that case you can’t set the action’s onClickCallback handler because the action no more responsible for any real work and only expands its dropdown menu on click event.

menuEntries is just a standard JavaScript array containing CalloutActionMenuEntry instances. Each element corrensponds to a single menu item. Here’s the initialization example:

var menuEntries = new Array();
var menuEntry1 = new CalloutActionMenuEntry('Subaction 1', function (action, selectedEntryIndex)
{
    // Get the selected menu entry from the parent action
    var selectedEntry = action.getMenuEntries()[selectedEntryIndex];
    alert(selectedEntry.text);
});
menuEntries.push(menuEntry1);
var menuEntry2 = new CalloutActionMenuEntry('Subaction 2', function (action, selectedEntryIndex)
{
});
menuEntries.push(menuEntry2);
var menuActionOptions = new CalloutActionOptions();
menuActionOptions.text = 'Menu action';
menuActionOptions.menuEntries = menuEntries;
var menuAction = new CalloutAction(menuActionOptions);
callout.addAction(menuAction);

It’s the result:

If you click this action the dropdown will be opened:

The minimal parameters for each action menu entry are the text and the onClickCallback function. It’s not practical anyway but if you don’t provide them the whole action will not be shown and the error will be raised.

text

It’s the text of the menu entry.

onClickCallback

It’s the handler function for the menu entry click event. This function receives two arguments. First argument is the parent action of the clicked menu entry and the second one is the zero based index of the entry itself inside menuEntries array. The combination of these parameters allows getting the clicked entry:

var menuEntry1 = new CalloutActionMenuEntry('Subaction 1', function (action, selectedEntryIndex)
{
    // Get the selected menu entry from the parent action
    var selectedEntry = action.getMenuEntries()[selectedEntryIndex];
    alert(selectedEntry.text);
});

For the menu action the text option of the CalloutActionOptions is not required. If it doesn’t defined there will be ellipsis image drawn instead of the text:

The common design is to have a single menu at the end of the row but there are no restrictions on the menu position and quantity of menus inside the actions row. You can create multiple menus but I think it doesn’t adhere to the SharePoint Metro UI design.

Unresolved questions

There are some questions that need to be resolved still.

The CalloutActionMenuEntry class accepts four additional parameters: image url, image alt text, “sequence” and “description”. They all results in some HTML markup inside the generated menu item but it is unclear how they are used. For example, if you provide the image url correspondinf image will be rendered in menu item HTML but will be hidden.

There are some out-of-the-box callouts with additional customizations of action dropdown menu. For example, here are the separators:

It appears that the separators generation is implemented outside the “core” callouts UI framework. Can it be reused?



Previous: Custom Callouts in the SharePoint 2013 Metro UI, Part 1: Basics

Back to contents.

Next: Custom Callouts in the SharePoint 2013 Metro UI, Part 3: CalloutManager