Pages

Monday, July 26, 2010

SharePoint Document Library: Add document link or Link to a Document

In SharePoint, there’s a way to add document link. Many of use not aware of it. To add the document link you need to add “Link to a Document” Content type in your library. By default content type modification is disabled in any list/library. To enable editing content type you need to go to list/library settings. Once you have enabled the content type editing, you can add/remove content type.

Enable Content Type Modification in List/Library

To enable content type editing go to list/library settings page. Then from the list settings page,  Click “Advanced Settings” and in the advanced settings page, click “Yes” for option “Allow Management of Content Types”. Enabling this option will allow you to modify content type settings. Click Ok after selecting Yes option.

After coming back to list settings page you will find the content modifying option as  shown below:

image

Add Link To Document Content Type to the library

Now from the ‘Add from Existing site content types’ link as shown in the image above, you can add the “Link to Document” content type. click “Add from existing site content types” and from the page you have landed you can the content type as shown below:

image

After adding the content type go back to document library page. You’ll find that when you’ll try to add new item, you’ll get the ‘Link to a Document’ option as shown below:

image

With this link to document item, you can add links to content not only in sharepoint but non-SharePoint content from other sites.

Programmatically Add Link to a document using SharePoint Object Model

Once you enable the ‘link to document’ support in any document library you can add the document programmatically. The following code snippet shows how to add a link to a document content in a library.

public static void AddDocumentLink(string webUrl, string libraryName,string documentPath, string documentName, string documentUrl)
{
    using (var site = new SPSite(webUrl))
    {
        using (var web = site.OpenWeb())
        {
            var contentType = web.AvailableContentTypes["Link to a Document"];
            var docLibrary = web.Lists[libraryName];
                 
            //get full path of the document to add
            var filePath = docLibrary.RootFolder.ServerRelativeUrl;
            if(!string.IsNullOrEmpty(documentPath))
            {
                filePath += "/" + filePath; 
            }
            var currentFolder = web.GetFolder(filePath);

            var files = currentFolder.Files;
            var urlOfFile = currentFolder.Url + "/" + documentName + ".aspx";

            const string format = @"<%@ Assembly Name='{0}' %>
            <%@ Register TagPrefix='SharePoint' Namespace='Microsoft.SharePoint.WebControls' Assembly='Microsoft.SharePoint' %>
            <%@ Import Namespace='System.IO' %>
            <%@ Import Namespace='Microsoft.SharePoint' %>
            <%@ Import Namespace='Microsoft.SharePoint.Utilities' %>
            <%@ Import Namespace='Microsoft.SharePoint.WebControls' %>
                <html>
                    <head> 
                            <meta name='progid' content='SharePoint.Link' /> 
                    </head>
                    <body>
                        <form id='Form1' runat='server'>
                            <SharePoint:UrlRedirector id='Redirector1' runat='server' />
                        </form>
                    </body>
                </html>";

            var builder = new StringBuilder(format.Length + 400);
            builder.AppendFormat(format, typeof(SPDocumentLibrary).Assembly.FullName);

            var properties = new Hashtable();
            properties["ContentTypeId"] = contentType.Id.ToString();

            var file = files.Add(urlOfFile, new MemoryStream(new UTF8Encoding().GetBytes(builder.ToString())), properties, false, false);
            var item = file.Item;
            item["URL"] = documentUrl + ", ";
            item.UpdateOverwriteVersion();
        }
    }
}

The above code snippet is the modified version of what SharePoint does when you add a document link from UI. I have used reflector to view the code and placed here a modified version. I have tested this against SharePoint 2010.

Wednesday, July 14, 2010

SharePoint 2010 exception: The query can not be completed because the number exceeds the contained Lookup enforced by the Administrator Lookup threshold.

SharePoint 2010 can handle very large lists with millions of items. However, Effective indexing and throttling can be a very useful technique to improve performance of large lists. Be default SharePoint provide default settings for the sites limiting how much items/lookup fields can be retrieved per query. If you get the exception “Query exceeds lookup column threshold.” or “The query can not be completed because the number exceeds the contained Lookup enforced by the Administrator Lookup threshold.” you know that you have exceeded the lookup columns limitation imposed by your administrator.

If you have administrative permission then you can modify the limitations To modify the settings go to “SharePoint Central Administrator” => Application Management (from left side navigation) and then “Manage Web Application”. You’ll find the page shown below:

image

Figure 1: Web application lists

 

Now from the web application list select the web application you want to configure and then click “Resource Throttling” from the ribbon as shown below:

image

Figure 2: Resource Throttling menu

Finally change the max number of lookup columns as shown below:

image

 

After setting the value N for the lookup threshold you can at most include N numbers of lookup field in any query.

Saturday, July 10, 2010

SharePoint 2010: Hide Recently Modified Items

In SharePoint 2010 we all are familiar with the following annoying quicklaunch menu:

image

In SharePoint 2010, the recently modified items is one of many less-used feature. In software industry we are familiar with 80-20 principal. If 80% custom wants a feature then we should go with it and if you can satisfy 80% users then you have the successful product. I guess the ‘Recently Modified’ quick link doesn’t even needed by 10% people but the link is enable by default. Maybe this is a security feature to let administrator know what files have been modified recently. But there should have an on/off option as this is less wanted feature and most of the administrators want to hide this link. Justin has already found a way to hide the option by modifying template file as described in his blog. However, we can fix the issue without modifying the shared file. Specially in case of shared hosting we don’t want to modify the files from 14 hive. The two approaches described here is implemented by modifying master page. The first approach is acceptable but may have unknown side effects. On the other hand second approach is much better and less chance of side effects.

 

First solution:

1. Open your master page (default one is V4.master) and find the content place holder ‘PlaceHolderLeftActions’ as shown below:

<asp:ContentPlaceHolder id="PlaceHolderLeftActions" runat="server">                
</asp:ContentPlaceHolder>

2. Now set the content place holder visible property to false. as shown below:

<asp:ContentPlaceHolder id="PlaceHolderLeftActions" runat="server" Visible="false">            
</asp:ContentPlaceHolder>
Why it works?

The above trick works as in the aspx files the recently changed menu is put inside the placeholder. If you open a file in SharePoint Designer  you can find that the recently modified menu is put under the placeholder with id ‘PlaceHolderLeftActions’ as shown below:

<asp:Content ContentPlaceHolderId="PlaceHolderLeftActions" runat="server">
    <SharePoint:RecentChangesMenu runat="server" id="RecentChanges" />
</asp:Content>
Warning

The content placeholder ‘PlaceHolderLeftActions’ mainly used in blog and wiki sites. So if you are using any of this kind of template then hiding the content place holder will hide others related links too. However, I have found that hiding the content placeholder in team site works perfectly.

 

Second and Better Solution

However, the above trick may have side effects, if the same place holder (‘PlaceHolderLeftActions’) is  used by other pages. Another solution (which I think less side effects or no side effects) is to apply a css. As shown in the following screen, the ‘recently modified’ quick launch uses a css class named ‘s4-recentchanges’ (shown with firebug).

image

So we can another property ‘display:none’ to the same css class by adding the following extra attribute to master page’s header section:

<style type="text/css">
    .s4-recentchanges
    {
        display:none;
    }
</style>

You can even put the above section in a css file and refer the file in master page. The following image shows the master page with the css class:

image

Wednesday, July 7, 2010

SharePoint 2010: Use ECMAScript to manipulate (Add/Delete/Update/Get) List Items

In SharePoint 2010 there are three different types of Client Object Model extension you can use. They are Managed Client Object Model, ECMAScript and silverlight extension. In one of my post I have described how to manipulate list items using Managed Client Object Model. Today I’ll go through how to use ECMAScrip to manipulate list items. Fist of all make sure your page/webpart is ready to use ECMAScript as I have described in another post.

How to Get ECMAScript Intellisence

When you’ll use ECMAScript library in Visual Studio, it’s possible to get intellisense for ECMAScript. There are three ways you may need to enable the intellisense:

  1. Get Intellisense in Application Page: In the case you want to put  your javascript in aspx file as inline, you need to add the following lines in the markup file. However, this will not work for webpart file. For webpart file you need to put your javascript in another js file as described in option 2.

    <script type="text/ecmascript" src="/_layouts/SP.debug.js" />
    <
    script type="text/ecmascript" src="/_layouts/SP.Debug.js" />
    <
    script type="text/ecmascript" src="/_layouts/SP.Runtime.Debug.js" />

    <
    script type="text/javascript">
          //you'll get intellisense here
    </script>
  2. Get Intellisense in js file: If you want to get intellisense in js file then you need to add the following lines in the top of the js file. As shown in the snippet below, the first reference is to MicrosoftAjax.js file. This is mandatory to have this js file reference at the top. Then I have added two other references. The two files (SP.Core.Debug.js and SP.debug.js) have basic SharePoint namespaces. However, if you need more functionalities try to add more js file reference from the path “C:/Program Files/Common Files/Microsoft Shared/Web Server Extensions/14/TEMPLATE/LAYOUTS”
    /// <reference name="MicrosoftAjax.js" />
    /// <reference path="file://C:/Program Files/Common Files/Microsoft Shared/Web Server Extensions/14/TEMPLATE/LAYOUTS/SP.core.debug.js" />
    /// <reference path="file://C:/Program Files/Common Files/Microsoft Shared/Web Server Extensions/14/TEMPLATE/LAYOUTS/SP.debug.js" />
    
  3. Get Intellisense in webpart: To get intellisense in webpart you need to add the following two lines in the webpart ascx file:

    <script type="text/javascript" src="/_layouts/MicrosoftAjax.js" ></script>
    <script type="text/javascript" src="/_layouts/SP.debug.js" /> 

    However, I have found the MicrosoftAjax.js file is not located in “/_layouts/MicrosoftAjax.js” and for that the above intellisense will not work. So to get intellisense you need to copy the file in the Layouts folder. You can try to find the MicrosoftAjax.js file from location “C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ProjectTemplatesCache\VisualBasic\Web\1033\EmptyMvcWebApplicationProjectTemplatev2.0.vb.zip\Scripts” if exists. Or you can download the file from the location below where I have uploaded the file: http://cid-04d8f6d0dd4e7214.office.live.com/self.aspx/Public/MicrosoftAjax.js 

A Sample List I have used

For this blog I have used a sample product list with the properties as shown in the following image.

image

Figure 1: Product List

 

Add a new List Item

The code snippet below shows that  you need to get the current SharePoint Context first. Then get the current web from the context. And then you need to get the list from the web. Then I have used ListItemCreationInformation object. There are two important properties of ListItemCreationInforamtion:

  • ListItemCreationInformation.folderUrl: this property defines in which location you want to add the item. The url should start with forward slash(/). For example for the web myweb and list product and for the folder ‘myfolder’ in the product list the url will be  ‘/myweb/Lists/product/myfolder’.
  • ListItemCreationInformation.UnderlyingObjectType: this value identity the type of object to create. The possible values are File,Folder, Web and Invalide. The values can be found in ‘SP.FileSystemObjectType’.

The addProduct method below takes few arguments that represents the product list’s fields.

function addProduct(productName, productDesc, productLaunchDate, productAvailQty, productType) {
    try {
        var context = new SP.ClientContext.get_current();        
        var web = context.get_web();
        var list = web.get_lists().getByTitle('product');

        var listItemCreationInfo = new SP.ListItemCreationInformation();
        var newItem = list.addItem(listItemCreationInfo);
        newItem.set_item('Title', productName);
        newItem.set_item('ProductName', productName);
        newItem.set_item('ProductDescription', productDesc);
        newItem.set_item('LaunchDate', productLaunchDate);
        newItem.set_item('AvailableQuantity', productAvailQty);
        newItem.set_item('ProductType', productType);

        newItem.update();
        context.executeQueryAsync(Function.createDelegate(this, this.success), Function.createDelegate(this, this.failed));
    }
    catch (e) {
        alert('error:' + e.Message);
    }
}

If you look a bit closer in the above code snippet you can find that ClientContext.executeQueryAsync takes two function delegates. The first one will be invoked when the ECMAScript get executed successfully. The second one will be invoked otherwise. The two methods are defined below:

function success() {
    alert('success');
}
function failed(sender, args) {
    alert('failed. Message:' + args.get_message());
}

 

Delete a List Item

To delete a product by product id the following code snippet can be used:

function deleteProduct(productId) {
    var context = new SP.ClientContext.get_current();
    var web = context.get_web();
    var list = web.get_lists().getByTitle('product');
    var itemToDelete = list.getItemById(productId);
    itemToDelete.deleteObject();
    context.executeQueryAsync(Function.createDelegate(this, this.success), Function.createDelegate(this, this.failed));
}

To delete an object in Client Object Model you need to invoke the deleteObject method of that object.

 

Get Item By Id

To get an item using ECMAScript, you need to share a common variable between the method that execute the ECMAScript (getProductById method in the following code snippet) and callback method (productReceived, failed in the snippet below). Only for this reason I have defined a variable product in the first line of the code snippet below.

var product;
function getProductById(productId) {
    try {
        var context = new SP.ClientContext.get_current();
        var web = context.get_web();
        var list = web.get_lists().getByTitle('product');
        this.product = list.getItemById(productId);
        context.load(product, 'ProductName', 'ProductDescription', 'ProductType', 'LaunchDate', 'AvailableQuantity');
        context.executeQueryAsync(Function.createDelegate(this, this.productReceived), Function.createDelegate(this, this.failed));
    }
    catch (e) {
        alert(e);
    }
}
function productReceived() {
    alert('got product');
    gotProduct(this.product);
}
function failed(sender, args) {
    alert('failed. Message:' + args.get_message());
}

In the code snippet above, the Context.Load method has taken the item to load (product) as the first parameter. And a comma separated list of columns to load for this item are passed then to the load method. If you want to load all properties of the item (which is not recommended) you can just call the context.load method with only first parameter.

 

Search Items from a List

In the code snippet below Caml Query is used for searching a product by title. I have used Caml Query to search product by title. Notice here that the load takes a second parameter (wrapped with ‘include’) specifying all properties to load for items.

var productcollection;
function getProducts(title) {
    try {
        var context = new SP.ClientContext.get_current();
        var web = context.get_web();
        var list = web.get_lists().getByTitle('product');
        var query = '<View Scope=\'RecursiveAll\'>'+
                        '<Query>'+
                            '<Where>'+
                            '<Contains>'+
                                '<FieldRef Name=\'ProductName\'/>' +
                                '<Value Type=\'Text\'>' + title +'</Value>'+
                            '</Contains>'+
                            '</Where>'+
                        '</Query>'+
                             '</View>';
        var camlQuery = new SP.CamlQuery();
        camlQuery.set_viewXml(query);

        this.productcollection = list.getItems(camlQuery);
        context.load(this.productcollection, 'Include(ProductName, ProductDescription, ProductType, LaunchDate, AvailableQuantity)');
        context.executeQueryAsync(Function.createDelegate(this, this.productsReceived), Function.createDelegate(this, this.failed));
    }
    catch (e) {
        alert(e);
    }
}
function productsReceived() {
    alert('got products');
    prcessProducts(this.productcollection);
}
function failed(sender, args) {
    alert('failed. Message:' + args.get_message());
}

 

Update a list item

The code snippet below shows how to update a product item. The list item’s set_item(propertyname, propertyvalue) method is used to update the field values.

function updateProduct(productid, productName, productDesc, productLaunchDate, productAvailQty, productType) {
    var context = new SP.ClientContext.get_current();
    var web = context.get_web();
    var list = web.get_lists().getByTitle('product');
    var product = list.getItemById(productid);
    product.set_item('ProductName', productName);
    product.set_item('ProductDescription', productDesc);
    product.set_item('ProductType', productType);
    product.set_item('LaunchDate', productLaunchDate);
    product.set_item('AvailableQuantity', productAvailQty);
    product.update();
    context.executeQueryAsync(Function.createDelegate(this, this.success), Function.createDelegate(this, this.failed));

}

 

Points to Remember

1. FormDigest Control: If you write code that modifies data on server, include a FormDigest control to create a digest for security validation of the page. The formdigest control can be added in master page and then all content pages will get it automatically.

<SharePoint:FormDigest runat="server" />

2. Get and Set Property: For the scrip used, when you need to get a property, you need to add get_ as prefix. For example the web has the property title. So if you want to get the title property of web then you need to use the syntax web.get_title(). Similary, if you want to set the title value then you need to invoke web.set_title(‘title’).

2. ClietContext.Load: ClientContext’s load method is used to load object. The first parameter to the load method is the object to load. The second parameter vary depending on whether the first parameter is a single object or collection.

  • If the first parameter is a single object then the following syntax is used to load properties:

context.load(objectToLoad,’property1’,’property2’,………….,’propertyN’)

  • If the first parameter is a collection then the following syntax is used

context.load(objectCollectionToLoad,’Include(property1, property2,……….,propertyN)’)

Tuesday, June 29, 2010

SharePoint 2010: Attach files to List/Library using Managed Client Object Model

 

Client Objet Model (OM) is a great new addition in SharePoint 2010. I have discussed before how to manipulate lists and list items using Managed Object Model. Today I’ll discuss on how to attach file to list item or add file to library using Managed Client Object Model.

Upload File in Library

The following code snippet shows how to upload file in document library:

public void UploadFileInLibrary(string siteUrl, string webName, string libraryName, string subfolderPath, string fileName)
{
    using (ClientContext clientContext = new ClientContext(siteUrl))
    {

        string uploadLocation = Path.GetFileName(fileName);
        if (!string.IsNullOrEmpty(subfolderPath))
        {
            uploadLocation = string.Format("{0}/{1}", subfolderPath, uploadLocation);
        }
        uploadLocation = string.Format("/{0}/{1}/{2}", webName, libraryName, uploadLocation);
        var list = clientContext.Web.Lists.GetByTitle(libraryName);
        var fileCreationInformation = new FileCreationInformation();
        fileCreationInformation.Content = System.IO.File.ReadAllBytes(fileName);
        fileCreationInformation.Overwrite = true;
        fileCreationInformation.Url = uploadLocation;
        list.RootFolder.Files.Add(fileCreationInformation);
        clientContext.ExecuteQuery();
    }
}

In the above code snippet, I have constructed the full path to the update location by concatenating the subfolderpath with library location. The site url is the spsite location whereas web name is name of the web site. To upload a file I have use FileCreationInformation object which is part of Client Object Model. To use the above method you can call the method as shown below. The instance is the class instance which has the method. Here the subfolderpath is the folder location inside the library where the document will be uploaded.

Instance.UploadFileInLibrary("http://mysite","mywebname", LibraryName,"folder1/folder2", @"c:\myfiles\LibraryItem.xlsx");

 

Download File From Library

The following code snippets shows how to download file from library:

public void DownloadFileFromLibrary(string siteUrl, string webName, string libraryName, string subfolderPath, string fileName, string downloadPath)
{
    using (ClientContext clientContext = new ClientContext(siteUrl))
    {
        string filePath = string.Empty;
        if (!string.IsNullOrEmpty(subfolderPath))
        {
            filePath = string.Format("/{0}/{1}/{2}/{3}", webName, libraryName, subfolderPath, fileName);
        }
        else
        {
            filePath = string.Format("/{0}/{1}/{2}", webName, subfolderPath, fileName);
        }

        var fileInformation = File.OpenBinaryDirect(clientContext, filePath);
        var stream = fileInformation.Stream;
        IList<byte> content = new List<byte>();
        int b;
        while ((b = fileInformation.Stream.ReadByte()) != -1)
        {
            content.Add((byte)b);
        }
        var downloadFileName = Path.Combine(downloadPath, fileName);
        System.IO.File.WriteAllBytes(downloadFileName, content.ToArray());
        fileInformation.Stream.Close();
    }
}

As above code shows, we can use Microsoft.SharePoint.Client.File.OpenBinaryDirect method to download file directly from SharePoint. However, SharePoint open the file as binary stream so you need read the full stream before processing the file. The subfolderpath is same as described in ‘Upload File in Library’ section.

 

Delete File From Library

The following code snippet shows how to delete file from library:

public void DeleteFileFormLibrary(string siteUrl, string webName, string listName, string subfolder, string attachmentFileName)
{
    using (ClientContext clientContext = new ClientContext(siteUrl))
    {
        string attachmentPath = string.Empty;
        if (string.IsNullOrEmpty(subfolder))
        {
            attachmentPath = string.Format("/{0}/{1}/{2}", webName, listName, Path.GetFileName(attachmentFileName));
        }
        else
        {
            attachmentPath = string.Format("/{0}/{1}/{2}/{3}", webName, listName, subfolder, Path.GetFileName(attachmentFileName));
        }
        var file = clientContext.Web.GetFileByServerRelativeUrl(attachmentPath);
        file.DeleteObject();
        clientContext.ExecuteQuery();
    }
}

As the above code , the DeleteObject method is invoked on file object to delete the file.

 

Attach File to ListItem

The following code snippet shows how to attach file to list item. The attachments in list are places in a folder whose location is like “http://siteurl/lists/[listname]/Attachments/[ListItemID]/[filename]”. So I have constructed the attchment location first and use Microsoft.SharePoint.Client.File.SaveBinaryDirect method to upload the file. Here in the method, Item id is the list item id that I want to attach the file to.

public void AttachFileToListItem(string siteUrl, string webName, string listName, int itemId, string fileName, bool overwrite)
{
    using (ClientContext clientContext = new ClientContext(siteUrl))
    {
        FileStream fileStream = new FileStream(fileName, FileMode.Open);
        string attachmentPath = string.Format("/{0}/Lists/{1}/Attachments/{2}/{3}", webName, listName, itemId, Path.GetFileName(fileName));
        File.SaveBinaryDirect(clientContext, attachmentPath, fileStream, overwrite);
    }
}

 

Download File from ListItem

The following code snippet shows how to download file that is attached with list item.

public void DownloadAttachedFileFromListItem(string siteUrl, string webName, int itemId, string attachmentName, string listName, string downloadLocation)
{
    using (ClientContext clientContext = new ClientContext(siteUrl))
    {
        string attachmentPath = string.Format("/{0}/lists/{1}/Attachments/{2}/{3}", webName, listName, itemId, Path.GetFileName(attachmentName));
        var fileInformation = File.OpenBinaryDirect(clientContext, attachmentPath);
        IList<byte> content = new List<byte>();
        int b;
        while ((b = fileInformation.Stream.ReadByte()) != -1)
        {
            content.Add((byte)b);
        }
        var downloadFileName = Path.Combine(downloadLocation, attachmentName);
        System.IO.File.WriteAllBytes(downloadFileName, content.ToArray());
        fileInformation.Stream.Close();
    }
}

As said before, the attachment location is “http://siteurl/lists/[listname]/Attachments/[itemid]/[filename]”. So I have used Microsoft.SharePoint.Client.File.OpenBinraryDirect to download the file.

 

Delete Attached File From ListItem

The following code shows how to delete a file that is attached with list item.

public void DeleteAttachedFileFromListItem(string siteUrl, string webName, int itemId, string attachmentFileName, string listName)
{
    using (ClientContext clientContext = new ClientContext(siteUrl))
    {
        //http://siteurl/lists/[listname]/attachments/[itemid]/[filename]
        string attachmentPath = string.Format("/{0}/lists/{1}/Attachments/{2}/{3}", webName, listName, itemId, Path.GetFileName(attachmentFileName));
        var file = clientContext.Web.GetFileByServerRelativeUrl(attachmentPath);
        file.DeleteObject();
        clientContext.ExecuteQuery();
    }
}

 

Conclusion

The code snippet above just provide you the gist. One point to notice that the File class used in the code not from System.IO. This is from Microsoft.SharePoint.Client and the full name is “Microsoft.SharePoint.Client.File”.

Saturday, June 26, 2010

SharePoint 2010: Add button to Ribbon with SharePoint designer

Sometimes you may need to add buttons to SharePoint 2010 ribbon. You can take one of two approaches: You can write code to add the button. Another approach is to use SharePoint Designer to add button to Ribbon. Today I’ll show you how to add button to Ribbon using SharePoint Designer. However, when you’ll use SharePoint Designer to add button to Ribbon and if you do  so in Development or Staging server, then think about how to do the same in Production server.

Add Button to List/Library forms (AllItems.aspx, DispForm.aspx, EditForm.aspx, NewForm.aspx)

Let’s say you have a list and you want to add a new Ribbon button in the AllItems.aspx page.

1. First open the site in SharePoint Designer.

2. Then click “List and Libraries” link from the left-hand menu and then click the target list from the details page on the right hand side as shown below:

image

Figure 1: Select List/Libraries in SharePoint Designer

3. Once you click the list name you’ll be landed to the list/library settings page. In this page you can edit four forms (AllItems.aspx, DispForm.aspx, EditForm.aspx and NewForm.aspx) and you can add button to the page’s ribbon. The page is shown below:

image 

Figure 2: Four built-in Forms

 

4. From the ‘List Settings’ tab on the top, select ‘Custom Action’ as shown below:

image

Figure 3: Add custom Action

5. Say you want to add button to ‘NewFom.aspx’. For this click ‘New Form Ribbon’ from the Custom Action. You’ll get the ‘Create Custom Action’ window where you can add custom action to the Ribbon. From the window below you can chose one of three different actions: Navigate to form, Initiate workflow, Navigate to Url. If you need more custom action then SharePoint Designer will not be helpful. You need to write code to extend Ribbon button actions.

image

Figure 4: Custom Action Properties

 

Find Ribbon Location:

In the List/Library form, the button are placed in groups. As shown in the image below, the new form has three sections:

image

Figure 5: Buttons are placed in Groups/Sections in form

Now you may want to place your button in a particular group and for that you need know the location id so that you can put the id in the ‘Ribbon Location’ in the custom action properties in SharePoint Designer. Using Firebug extension of Mozilla Firefox, I have found the ribbon location id as shown below:

image

Figure 6: Ribbon group/section id (detected using Firebug extension of Firefox)

So now you can put the ribbon location id in the custom action properties. For example to put the ‘Show Me’ button in the ‘Commit’ section I have put the ribbon button location to ‘Ribbon.ListForm.Edit.Commit.Controls._children’. Here the ‘.Controls._children’ is added to the ribbon id. The following figure show two windows side by side:

image

Figure 7: Custom Action windows (On Left) and New Item Form (On right) with ‘Show Me’ button

Decide when to show the Ribbon Button

Sometimes you don’t want to show the button for all users. Rather you want to show for users having a set of permissions. The ‘Rights mask’ option allow us to meet this requirements. The available values can be found on MSDN. You can put more than one values in the Rights Mask text box separated by semicolons as shown below:

image

Figure 8: Rights mask

 

 

Define Ribbon button sequence in a group

You can determine the ribbon button location in a group/section by setting the ‘sequence number’. I have not found documentation about the sequence number but I have found by myself that each position holds 10. So giving 10 in that filed will show as the first button. Putting any value more than 10 but less than 20 will show as second button and so on.

Tuesday, June 22, 2010

SharePoint 2010: Backup/Restore with PowerShell Command

If you want to backup/restore your site collection in SharePoint 2010, you can do with PowerShell command. I’ll spilt the post in two sections. One is on how to backup/restore in the same site collection and another is how to backup from one server/site collection to another. For the former (backup/restore in the same site collection), SharePoint provides nice easy GUI page in Central Administration page. However, for second one (backup/restore between different server) you need to run PowerShell scripts.

Backup/Restore in the same server and same site  collection

Sometimes you may want to backup from a site collection and your intention is to restore the backup in the same site collection. One example might be you have a site collection, say “http://myserver” and you are going to run code to test something against the site. However, you fear that running the code may break something and before you run the code, you want to backup the site so that in case running the code breaks something you can restore the backup taken before running the code. In the case, where you need to backup/restore is centered around same server and site collection, you can take SharePoint Central administration UI to do the backup/restore.

Backup Steps

1. Navigate to the Central Administration => Backup and Restore

2. Under “Farm Backup and Restore” section click “Perform a backup”.

3. Now you’ll be landed in the following page where you can select the site or site collection you want to take backup:

image

4. Click next and you’ll be navigated to the following page where you can select backup type (full or differential) and backup location:

image

 

Restoring Steps

To restore follow the steps:

1. Navigate to the Central Administration => Backup and Restore.

2. Under “Farm Backup and Restore” section click “Restore from a backup”. Follow the wizard to restore from a backup.

 

Backup/Restore from one server to another or from one Site Collection to another (with PowerShell command)

In some cases, you have developed a SharePoint site collection in your dev or stg machine and now you want to move the site with data from dev/stg to production. In such cases the process shown in the section “Backup/Restore in the same server and same site  collection” will not work. The recommended way is to use PowerShell command to take backup and restore the backup.

Backup a Site collection with PowerShell command

In SharePoint 2010, PowerShell command Backup-SPSite is used for taking backup. you can get details of the command from the msdn link. The following command will backup the site collection ‘http://myserver’.

Backup-SPSite -Identity http://myserver -Path "c:\backup\file.bak"

Restore a Site Collection with PowerShell command

To restore site collection you’ll use the following command. Use –Force if you want to overwrite the existing site collection

Restore-SPSite -Identity http://myserver -Path "c:\backup\file.bak"

However, once I had restored the backup I could not access the site. The problem was that I needed to deploy the custom SharePoint solution. So in case of site collection migration (with backup/restore) from one server to another or from one site collection to another, the steps should be:

  1. Restore the backup.

  2. If your source site collection (from where you taken backup) uses custom SharePoint solution, then deploy the solution in the destination site collection (where you are restoring the backup). If you try to access the site without deploying solution then you may get the site non-functional.

  3. Now you can try to access the site.

The important point here is that if you take backup from one server to another and restore it, the custom solution related to backup doesn’t go with backup. So after restoring backup you need to manually deploy the solution on the destination server. Then it’ll hopefully work.

Thursday, June 17, 2010

The Ribbon Tab with id: "Ribbon.Read" has not been made available for this page or does not exist. Use Ribbon.MakeTabAvailable()

Last day I was working with few web parts and I had got the error. I had created two pages in ‘site pages’ library. Let’s say one is parentpage.aspx and another is childpage.aspx. There was a web part added in the parentpage.aspx. In that web part I had a button and on the click event of the button I tried to show the childpage.aspx in modal dialog. So I was trying to show a child page inside parent page using SharePoint client modal feature. And I had come up with the error. Let’s dig a bit deeper into the problem.

How to add Web Part page

There are two types of pages (web part page and non-webpart page) you can add in SharePoint. If you just click add page from ‘Site Pages’ library as shown in the image below, the page will be added is non-webpart page.

image

Figure: ‘Add new page’ feature in site pages library

If you want to add web part page then you need to go to ‘Site Actions’ => ‘More Options’ => ‘Page’ and then select ‘web part page’. This is shown in the image below:

image

I had created web part pages and then I tried  to show the page in the modal dialog. And it worked!! So the error was coming up since I was trying to show non-webpart page in the modal dialog.

Solution

So the conclusion is that if you want to show a page in modal dialog taking advantage of SharePoint Modal Dialog API, then you need to make sure that the page you want to show in modal dialog is derived from Microsoft.SharePoint.WebControls.LayoutsPageBase or the page is web part page. So to get rid of the error make sure you have been using WebPart page or your page is inherited from Microsoft.SharePoint.WebControls.LayoutsPageBase .

Tuesday, June 15, 2010

Execadmsvcjobs alternative in SharePoint 2010. Run job immediately

In sharePoint 2007, whenever we needed to ensure that any pending jobs are not there we were used to run the following command. One scenario where we needed this command is when you have deployed a solution to a web or retract solution from web application. In these case, SharePoint doesn’t deploy or retract solution immediately rather creates jobs. So if want to run any command that depends on solution deployment then you need to run the following command to make sure there is no pending jobs.

stsadm –o execadmsvcjobs

However in SharePoint 2010, stsadm is kind of dead. So if you now need to deploy a solution and then activate feature, then after running solution deployment command you need to make sure that the solution deployment job is executed before activating/installing feature. I have found a concept in the link: http://msdn.microsoft.com/en-us/library/ff459292.aspx. I have modified the code a bit to generate the following PowerShell function. Passing a solution name in the function, check if the solution job is finished or not. If not finished then the function waits for the solution deployment job to finish.

function WaitForJobToFinish([string]$SolutionFileName)
{ 
    $JobName = "*solution-deployment*$SolutionFileName*"
    $job = Get-SPTimerJob | ?{ $_.Name -like $JobName }
    if ($job -eq $null) 
    {
        Write-Host 'Timer job not found'
    }
    else
    {
        $JobFullName = $job.Name
        Write-Host -NoNewLine "Waiting to finish job $JobFullName"
        
        while ((Get-SPTimerJob $JobFullName) -ne $null) 
        {
            Write-Host -NoNewLine .
            Start-Sleep -Seconds 2
        }
        Write-Host  "Finished waiting for job.."
    }
}

In my another blog http://ranaictiu-technicalblog.blogspot.com/2010/05/sharepoint-2010-deployment-powershell.html I have described the in details on how to use the function.

Friday, June 4, 2010

Microsoft Desktop Player

Microsoft Desktop Player Beta has been rolled out. User can download the player in desktop or view online. The Desktop Player allow users to view technical contents (like Presentation, Videos, news etc) from a single place. Currently, I move earound Channel 9, MSDN, Codeplex, Fourums to find out what’s new about SharePoint. The information are scattered around  web and it’s cumbersome to move around and find the updates. Though I can use rss feed but the Desktop player is much better way to get resources from a single place. You can download Microsoft Desktop Player from the link below:

http://www.microsoft.com/click/desktopplayer/