Pages

Showing posts with label SharePoint. Show all posts
Showing posts with label SharePoint. Show all posts

Tuesday, May 13, 2014

SharePoint: Safe Control Entry using elements file

Recently I’ve discovered that it’s possible to add safe control entries through elements.xml file. To do this, add an elements.xml file (or use an existing xml file). Then select the elements.xml file and open the properties window as shown below:

image

Then click the ... button next to (Safe Control Entries) as shown in the above image. Then in the safe control entries dialog click ‘Add’ and edit the namespace as shown below:

image

Finally, add the element xml file to a web/site scoped feature. As you will deploy the WSP, the safe control entry will be added in web.config file.

Sunday, October 14, 2012

SharePoint: Manipulate Audience Programmatically

Audience is a nice feature in SharePoint but only available in MOSS/SharePoint Server. Generally speaking, Audience is group of users that can be used to target contents. The class ‘Microsoft.Office.Server.Audience.AudienceManager’ is the main entry point for accessing Audience feature in SharePoint. Audience values are of three different types which are separated by ‘;;’:

  • Global audience represented by GUIDs. Multiple global audience values are separated by commas.
  • Distinguished names represented by Fully Qualified Domain Name. Multiple distinguished names are separated by ‘\r\n’.
  • SharePoint Group IDs. Multiple sharepoint groups are separated by commas.

An example of audience field value is given below:

A88B9DCB-5B82-41E4-8A19-17672F307B95, B88B9DCB-5B82-41E4-8A19-17672F307B95 ;; cn=all developers,ou=distribution lists,dc=redmond,dc=corp,dc=microsoft,dc=com \r\n cn=all testers,ou=distribution lists,dc=redmond,dc=corp,dc=microsoft,dc=com \r\n ;; 1,12,21,37

I’ve found many times that people are manipulating these audience field values manually by parsing the text. However there’s SharePoint server object model support to manipulate Audience field programmatically.

Find Audience Field in List/Library

Audience field is of type ‘Microsoft.Office.Server.WebControls.FieldTypes.SPFieldTargetTo’. You can find out the field types from list fields’ as shown below (using any of the two described):

var audienceFields = list.Fields.OfType<Microsoft.Office.Server.WebControls.FieldTypes.SPFieldTargetTo>();

//Alternatively you can use the following code snippet also:
foreach (SPField field in list.Fields)
{
if (field is SPFieldTargetTo)
{
//found audience field
}
}

Code 1: Get Audience fields

 

Read/Parse Audience Values

You can manipulate the audience values as shown below. To read value from a list/library audience field, pass the value in the AudienceManager.GetAudienceIDsFromText method. The method will parse the audience field value and populate three out variables: Global Audience, Distinguished Name and SharePoint Group.

string[] globalAudienceIds;
string[] distinguisedNames;
string[] sharepointGroupNames;

var audienceFieldValue = listItem["AudienceField"] == null ? string.Empty : listItem["AudienceField"].ToString();
AudienceManager.GetAudienceIDsFromText(audienceFieldValue, out globalAudienceIds, out distinguisedNames, out sharepointGroupNames);

Code 2: Read Audience Field values

 

Once you have the global audience values you can get three different types of audience-details as shown below:

var audienceManager = new AudienceManager(ServerContext.GetContext(SPContext.Current.Site));
//get global audience
Audience globalaudience = audienceManager.GetAudience(globalAudienceGuid);
//get distinguished name audience
Audience distinguisedNameAudience = audienceManager.GetAudience(distinguisedName);
//get sharepoint group audience
SPGroup sharepointGroupAudience = web.SiteGroups.GetByID(sharepointGroupId);

Code 3: Get audience based on audience id or name

If you have global audience id (which is valid guid), please convert the guid string to GUID type variable and then pass in the GetAudience method.

 

Update Audience Field Value

To update audience field value programmatically you need to use following code snippet. You need pass the global audience IDs, distinguished names and sharepoint group names to Audience Manager’s GetAudienceIDsAsText method which will combine the values as text to save in audience field.

listItem["AudienceField"] = AudienceManager.GetAudienceIDsAsText(globalAudienceIds, distinguisedNames, sharepointGroupNames);

Useful links

The following links might be useful for you to read.

Sunday, August 12, 2012

SharePoint: Too much customization is not good for health

SharePoint allows developer/architect to customize it’s power in numerous ways. You can write event receivers, timer jobs, webparts, application pages and so on. But what I’ve found with my years of experience in working with SharePoint is that sometimes developers/architects make too much customization which is not good from maintenance point of view.

If you are senior SharePoint developer or architect or you have the responsibility to design a solution for SharePoint, first focus on out-of-the-box way to solve the problems. If possible, try to compromise features talking to clients.

Real world Example

Let’s consider a simple example. You have a database which is used by other LOB systems. SharePoint reads and writes data to the database and then other LOB systems (like SAP or some custom applications) use (only read) the data in the database for it’s own use.

image

Now client requirement is to show the data in the database as SharePoint list so that clients get the feelings that the tables in the database are kind of List in the SharePoint. Now what options do you have? I think we have two options:

  • Option 1: Use Business Connectivity Service to show Database tables as SharePoint list
  • Option 2: Create custom SharePoint list and then write event receivers to synchronize data between database and List.

Different architect/developer under different circumstances, may use first option or second. But the first option is more out-of-the-box way of implementing the requirements. However some situations demand the second option where you need to  write custom code and need to sync data between SharePoint list and table in database manually. So as an architect/developer I would prefer the first option using Business Connectivity Service. If I need to choose second option then I should have explanation why I need to use second option. The explanation might be Business Connectivity Service has some limitations that we need to overcome with second option, or the requirements are too complex to implement with Business Connectivity Service.

 

But Why?

Now you may ask why it’s matter if I do more customization (I would prefer to say unnecessary customization). I bet it’s matter. The more customization you do, the more difficult to manage codebase, more time to develop, more possible to generate more bugs, more difficult to upgrade to newer version of SharePoint and many more. SharePoint is a rich application framework and we need to utilize it in best possible ways.

 

conclusion

The idea I wanted to share here not to say that you should not do any customization rather make sure your customization really needed. If you do unnecessary customization then you might paving the way for more customizations (maybe for error fixing or features related to customizations). As I read somewhere, “A single bad developer creates job for more developers”. Let’s not create more customizations by an unnecessary customizations.

Saturday, June 16, 2012

SharePoint: Package your external dependencies to another solution

Sometimes we use some third party tools that create external dependencies to our SharePoint projects. For example if you use Pattern and Practice SharePoint Guidance Library in your SharePoint project in visual studio and build WSP, then  WSP package will include these SharePoint Guidance libraries also. However, this might create problems related to deployment. To explain the issue, let’s consider how SharePoint solution retraction/removal might affects other WSP package deployed in the same SharePoint environment.

 

Scenario: Two WSPs sharing Common Dependency

Let’s consider you have two Visual studio SharePoint project: SharePoint Project 1 and SharePoint Project 2. Both projects are using (i.e., referencing) a third party dll (i.e., thirdparty.dll). Now if you generate WSP from two different Visual Studio projects, both WSP will include the thirdparty.dll in their WSP package.

image

Figure 1: A single thirdparty.dll is shared by two WSP solutions

 

As shown in the image above, a single third party dll is referenced by two Visual studio SharePoint projects. Now when you 'will build two different WSP solutions the same third party dll will be included to two different WSPs. If these two WSPs are deployed in a single SharePoint server/farm with GAC deployment, then both WSP will deploy the same ThirdParty.dll in the GAC. Everything will work fine till now.

 

Problem: retracting/removing one solution will fail other WSP to work

Now consider a scenario. After deploying the two WSPs in the production with GAC deployment, you have found a problem with Project 2 WSP. So you want to remove the Project 2 WSP while you will keep Project 1 WSP. So you will remove the Project 2 WSP. Removing the Project 2 WSP will remove all the assemblies from GAC/BIN related to the WSP. So removing the Project 2 WSP will remove thirdparty.dll from GAC. As soon as you remove the Project 2 WSP from SharePoint server/farm, Project 1 WSP will fail to work as it’ll not find the referenced thirdparty.dll in GAC. So removing Project 1 WSP (or project 2 WSP) will remove the thirdparty.dll from GAC and other WSPs which are using the same dll will fail to work.

 

Solution: Package your external dependency to new WSP

The solution to this problem is to exclude thirdparty.dll from both WSPs and create a new WSP dependency/prerequisite solution for thirdparty.dll. You can do it easily with SharePoint 2010 Package editor. As a result the thridparty.dll will not be included in any of the two WSPs.To package a new WSP for the thirdparty.dll you need to create another new Visual Studio SharePoint projects which will just include the thirdparty.dll (or any other prerequisites). The new architecture is shown below:

image

Figure 2: Shared thirdparty.dll is included in separate WSP

 

As shown in the figure 2, now the shared dll (thirdparty.dll) is excluded from Project 1 and Project 2 WSP. Whether you use WSPBuilder or SharePoint 2010 project template in Visual studio, you can exclude one more dlls from the WSP. Then create a new Visual Studio project (WSPBuilder or SharePoint) which will include the thirdparty.dll and the output WSP package will include the shared/external dll. Now you will have three WSPs and the new WSP will be prerequisite WSP for other two WSPs.

Now with this model, you can remove Project 1 WSP or Project 2 WSP from your server without affecting each other. Even if you remove Project 1 WSP from the Farm/Server, the thirdparty.dll will not be removed from GAC as it’s not deployed as part of Project 1 WSP. Later if you want to redeploy the Project 1 WSP again then you don’t need to redeploy the prerequisite WSP as it’s already deployed in the server. If needed you can package your external dependency in more than one WSP packages.

 

Conclusion

If you are developing a SharePoint custom product that will be deployed in some client’s environment, then you should care about creating one or more dependency WSP packages. If your SharePoint solution is developed for a specific client or as an in-house product then you might not face the problem even if you don’t create a prerequisite WSP package. However if you are developing a SharePoint solution as a custom product and you don’t know the client yet, you should create prerequisite WSP package. And then when you will deploy your product in client server, you can deploy the perquisite or not depending on if the client has the prerequisite dlls install or not. If the client has the dlls/prerequisites installed in the farm, you don’t need to deploy the prerequisite WSP. This will make your SharePoint product more independent and will have less impact on other SharePoint solution deployed in the farm.

Thursday, May 17, 2012

SharePoint Tips: Iterating through All the webs in the site

Sometimes we need to process all webs in a site collection, as you want to do some quick fixes in the web. Few weeks back my manager asked me to do some fixes in the list items exists in all the webs in the site collection. There were about 30,000 webs in the site collection and I was looking for some kind of script that will be efficient. The usual way of looping through all webs might be using some recursive way, as shown below.

//Starting point
public void ProcessAllWeb(SPSite site)
{
    using (var web = site.RootWeb)
    {
        ProcessWebRecursive(web);
    }

}

//Recursive method
private static void ProcessWebRecursive(SPWeb web)
{
    //do some processing
    //web.Lists["listName"].ItemCount

    foreach (SPWeb subWeb in web.Webs)
    {
        using (subWeb)
        {
            ProcessWebRecursive(subWeb);            
        }
    }

}

Code Snippet 1: Recursive way of processing all webs in the site collection (Not optimized)

In the recursive way of processing all webs, there will be more than one SPWeb instance alive in memory. In the above code snippet, when the method ProcessAllWeb is invoked it’ll call the recursive method ProcessWebRecursive. The recursive method will keep calling the subwebs while keeping the parent web alive.

 

While I was writing the code, I was wonder if there’s any way of processing only one web non-recursively. So my intention was to open only one web in memory at once. And then I found it. You can get all web Url(including all subwebs at all level) using SPSite.AllWebs.Names. The following code snippet shows the efficient way of processing all webs in the site collection:

public void ProcessAllWeb(SPSite site)
{
    string[] allWebUrls = site.AllWebs.Names;
    foreach (string webUrl in allWebUrls)
    {
        using (SPWeb web = site.OpenWeb(webUrl))
        {
            //process web
        }
    }
}

Code Snippet 2: Process all webs one by one (Optimized for large number of webs)

Using the code snippet shown in figure 2, you just open one web at a time in memory for processing. The trick here is ‘SPSite.AllWebs.Names’ which will return all the (I mean it!) subwebs (including children and their children and so on) as a result. If you have thousands of webs under a site collection (and if it’s production), you should care about performance issue.

Thursday, May 3, 2012

SharePoint Tips: List.ItemCount vs List.Items.Count

If you need to know the total items in the list, how do you write code? The usual way to write code is shown below:
var itemCount = list.Items.Count;
Code Snippet 1: Usual (not suggested) way to get items count
However this will fetch all the records from database and apply the count in memory.

SharePoint object model provides an easiest way to find the items count without fetching all records and you can use the following code snippet to do so:
var itemCount = list.ItemCount;
Code Snippet 2: Suggested way to get items count

Saturday, April 21, 2012

SharePoint Tips: Object Mode provides Built-in Field and Content type Ids

Sometimes we need to access SharePoint built-in fields from list. You may find code to access list item value as shown below:

listItem["Title"] = "value";

Code Snippet 1: Usual approach

 

The title field is SharePoint built-in field and you don’t need to hard-code the field name . SharePoint Object Model provides a class ‘SPBuiltInFieldId'’ where you can find most of the SharePoint built-in field’s IDs. So you can write the above code snippet as shown below:

listItem[SPBuiltInFieldId.Title] = "value";

Code snippet 2: Suggested approach to access built-in fields

 

The class ‘SPBuiltInFieldId’ also provides many built-in fields Ids like ‘created by’ (known as owner), modified by etc.

Similarly you can get built-in content type Ids using the class ‘SPBuiltInContentTypeId’. I would recommend you to use these classes to access built-in fields and content types.

Friday, March 9, 2012

Managing a single codebase for both SharePoint 2007 and 2010

Sometimes we need to work on source code that needs to be maintained for both SharePoint 2007 and 2010. There’s no built-in support from Microsoft to manage two versions of same Visual Studio solution for multiple versions of SharePoint. The problem is more complicated as the SharePoint assembly details are hard-coded in markup (ascx, aspx).

Anyway, I worked on a project where my team was developing a SharePoint custom product and some of our clients are still using SharePoint 2007 while some others are already moved to SharePoint 2010. So we had a challenge to support all clients (both SharePoint 2007 and 2010) but maintaining a single codebase. I am not demanding that the solution I’ll provide in this post is the best but the approach might be helpful to minimize code duplication. The sample Visual studio project can be download from skydrive.

 

Introduction

I’ve used Model View Presenter (MVP) to separate the presenter logic from view. Also, code that not dependent on SharePoint assembly directly are put in presenter layer. If for any reason, presenter is dependent on SharePoint assembly (for example SPGridView is used in code-behind) then the SharePoint assembly dependent code is implemented in individual (2007 or 2010) SharePoint project where common non-SharePoint presenter logic is kept in Presenter layer. For explaining the solution, I would like to introduce you to the Visual Studio projects (can be downloaded from here)I’ve been used.

  • Sohel.Blogging.Common: This is common project where interfaces, data contracts, common utilities etc are declared.
  • Sohel.Blogging.Presenter: This is the presenter layer where all non-SharePoint related presenter logic is kept. Since there’s no SharePoint-dependent logic is places, no reference to SharePoint dll is needed. As a result this project (or more specifically, presenter logic) can be reused by both SharePoint 2007 and 2010.
  • Sohel.Blogging.Wss: This is SharePoint 2007 (wss) project from where we’ll generate WSP file. Only SharePoint 2007 project structure, Views (aspx, ascx etc.) and Site artifacts (site definition, columns etc.) are placed in this project. As code-behind is replaced by presenter (which resides in Presenter layer), only views are need to duplicated between SharePoint 2007 and 2010.
  • Sohel.Blogging.Foundation: This is SharePoint 2010 project from which we’ll generate WSP solution file. Only views (aspx, ascx), site structure and site artifacts (site definitions, site columns etc.) are maintained in this project

To plug presenter/view to each other, Dependency Injection is used and for that purpose, Microsoft Patterns & Practices SharePoint Guidance is used. The Guidance Library for both SharePoint 2007 and 2010 can be downloaded from CodePlex (http://spg.codeplex.com/). For more information on how to use the Guidance Library for Dependency Injection please follow another post SharePoint Service Locator in my blog.

The overall architecture of the Visual Studio solution is presented below:

image

Figure 1: Visual Studio Project Dependency

The overall goal of the architecture is to move aspx/ascx code behind to presenter layer. As a result the code-behind logic is reused in two different views (SharePoint 2007 and 2010).

 

Views

The view (aspx, ascx) files are slimmed by removing code to presenter. View files (aspx,ascx etc) are placed in Sohel.Blogging.Foundation or Sohel.BLogging.Wss project. An interface is extracted from view to make Controls (like TextBox, Dropdown etc) available to Presenter. This is done by exposing View’s controls as the interface properties of the view. Though this is not the real Model View Presenter (MVP) but it works and faster to develop. The only problem is that the view interface/presenter is tightly coupled to asp.net controls.

public interface IProductListview
{
    Panel CreateListPanel { get; }
    Panel AddItemListPanel { get; }
    HtmlGenericControl MessageDiv { get; }
    HyperLink ViewAllProductLink { get; }
    Product GetProduct();
    string GetProductListUrl();
}

However if you use SharePoint controls (like SPGridView), then instead of exposing them in View interface, keep the logic related to the control in View and use a method in view. For example if you use SharePoint DateTime control, a method in view/code-behind can be used to put the logic related to SharePoint control. The following view interface declares SetDate method that set the date value of SharePoint DateTime control.

public interface IProductListview
{
    //Panel CreateListPanel { get; }
    //Panel AddItemListPanel { get; }
    //HtmlGenericControl MessageDiv { get; }
    //HyperLink ViewAllProductLink { get; }
    //Product GetProduct();
    //string GetProductListUrl();
    void SetDate(DateTime dateTime);
}

The view (say UserControl) below implement the SetDate method.

public partial class ProductList : UserControl, IProductListview
{
    public void SetDate(DateTime dateTime)
    {
        uiSpDateTimePicker.SelectedDate = dateTime;
    }
..............
..............
}

 

Presenter

All Presenters are kept in Sohel.Blogging.Prensenter project. Presenter access View controls through view interface. As I’ve mentioned already, this is not the right for MVP pattern to use web control directly inside view interface. SharePoint Service Locator is used as shown below:

public class CurrentSharePointServiceLocator : IBloggingServiceLocator
{
    public T ResolveInstance<T>()
    {
        return SharePointServiceLocator.Current.GetInstance<T>();
    }
}

The interface to the service locator (IBloggingServiceLocator) is passed to the Presenter constructor inside view, as shown below:

public partial class ProductList : UserControl, IProductListview
{
    private ProductListPresenter _presenter;
    public ProductListPresenter Presenter
    {
        get
        {
            if (_presenter == null)
            {
                _presenter = new ProductListPresenter(this, CurrentSharePointServiceLocator.GetInstance<IBloggingServiceLocator>());
            }
            return _presenter;
        }
    }
.....
.....
}

As presenter get access to the IBloggingServiceLocator, presenter can access any object that is registered through service locator. The presenter gets access to the view and service locator through constructor as shown below:

public class ProductListPresenter:APresenter
{
    public IProductListview View { get; set; }

    public ProductListPresenter(IProductListview view,IBloggingServiceLocator serviceLocator):base(serviceLocator)
    {
        View = view;
    }
.....
.....
}

 

New development Consideration

Once the project architecture is in place and if you need to develop new control, application pages etc, you should develop new control for any version of SharePoint first. For example if I need to develop a new application page, I would go and develop the page for SharePoint 2010. Then I would refactor the code to move the code-behind logic to Presenter class. Then I’ll create a new page in SharePoint 2007 copying the similar file from SharePoint 2010 (but surely with modifications). Now while you’ll create new page for SharePoint 2007, you may fine more refactoring needed to generalize code more for both SharePoint 2007 and 2010. The only duplication needs to be made in this approach is resources like markup/xml/css/jss etc. need to be duplication. But I think its possible to minimize the duplication by putting most of the resources in separate web project and on post-build event, you can copy the resources in individual SharePoint projects.

 

Conclusion

As more and more clients are adopting SharePoint and more and more Independent Software Vendor (ISV) are developing custom SharePoint products, ISVs need to support more than one version of their product. Since Microsoft doesn’t provide any out-of-the box solution for this multiple-version support for Visual Studio Solution, it takes extra efforts for developers to use the same codebase for different version of SharePoint and increase the possible of code duplication which in turn make maintenance difficult. You can download the sample code from skydrive.

Disclaimer: The solution provided in this post is not the best solution but a workaround. If anybody has any better idea, you are welcome to share.

Saturday, September 17, 2011

SharePoint 2010: Access WCF Service with jQuery

In one of my earlier post I described how to develop a custom WCF service. Today I’ll cover how you can invoke the SharePoint WCF Service from jQuery. In my last post I described to develop a SOAP web service but for using WCF service from jQuery I’m going to use REST web service. For the list of service types and factories supported in SharePoint you can visit the link in MSDN. You can download source code from the link given at the end of the post.

Prepare the service to call from jQuery

Consider developing a service as described in my earlier post with the following changes:

  • For  using json I’ve used REST service factory ‘Microsoft.SharePoint.Client.Services.MultipleBaseAddressWebServiceHostFactory’ as shown below. You can use SOAP factory but you then need to parse data in different way.
    <%@ ServiceHost Language="C#" Debug="true"
    Service="AccessSPServiceFromJQuery.MyService, $SharePoint.Project.AssemblyFullName$"
    CodeBehind="MyService.svc.cs"
    Factory="Microsoft.SharePoint.Client.Services.MultipleBaseAddressWebServiceHostFactory, Microsoft.SharePoint.Client.ServerRuntime, Version=14.0.0.0,
    Culture=neutral, PublicKeyToken=71e9bce111e9429c"
    %>


  • Next you need to specify the return type to json in the service interface as shown below. I’ve specified both request and response type to json in WebInvoke attribute:

    [ServiceContract]
    public interface IMyService
    {
    [OperationContract]
    [WebInvoke(Method = "GET", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
    List<Product> SearchProduct(string productName);


    [OperationContract]
    [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
    bool Save(Product product);
    }

One thing to notice here is that you can’t access the service in browser with mex endpoint. For example if you service is http://myserver/myservice.svc, then the url http://myserver/myservice.svc/mex will not work for service created with MultipleBaseAddressWebServiceHostFactory.
 

Call Service with jQuery

The next step is to call the service with jQuery. The url of the service to be used in jQuery will be service url and method name. For example if you service url is ‘/_vti_bin/AccessSPServiceFromJQuery/MyService.svc’ and the method name you want to invoke is ‘Search’ then the full url will be ‘/_vti_bin/AccessSPServiceFromJQuery/MyService.svc/Search’. As shown in the code below, you can invoke the service Search by passing the parameter in data field of ajax call of jquery

function getProductFromService(searchText) {
try {
$.ajax({
type: "GET",
url: '/_vti_bin/AccessSPServiceFromJQuery/MyService.svc/SearchProduct',
contentType: "application/json; charset=utf-8",
data: { "productName": searchText },
dataType: 'json',
success: function (msg) {
WCFServiceGetSucceeded(msg);
},
error: WCFServiceGetFailed
});
}
catch (e) {

alert('error invoking service.get()' + e);
}
}
function WCFServiceGetSucceeded(result) {
alert('success');
}
function WCFServiceGetFailed(error) {
alert('Service Failed.');
}

Download and use code

I’ve uploaded the code for this post in my skydrive. You can download the code from the link below. To use the code please ensure you have internet connection as I’ve used jqery from Microsoft CDN. The search functionality get all products matching name. You can try to search just by typing a single character. You can debug and test the code. In save I’ve just shown you can pass value from browser to service using POST method.

Wednesday, August 24, 2011

SharePoint 2010: Postback to the same page from Ribbon

Most of the examples related to ribbon button actions involve opening dialog. However, if you need to send post back request to the same page/dialog where the ribbon exists, you need to trick a bit. Let’s explain how you can do so.

Define Ribbon XML

You can define the postback action in the command action in Ribbon xml file. To do so, you can use javascript function _doPostBack(‘token’,’’). This will send a postback request to the same page.

<CommandUIHandler
Command="MyButton.Command"
CommandAction="javascript:__doPostBack('MyButtonPostback','');"
EnabledScript="true" />
Figure 1: PostBack action defined in Ribbon xml

 

Identify the PostBack request in the page

The second step is to identity from code behind file, if the postback occurred from ribbon button’s action. From the page load, you can check if the postback occurred due to the command “MyButton.Command”. To do so we can have a check shown below in the page load method.

if (Page.Request["__EVENTTARGET"] == "MyButtonPostback")
{
//The ribbon button initiated the postback
}

 

 

Though this is very simple trick but might be helpful for someone.

Tuesday, August 23, 2011

SharePoint 2010: Modifying and Deploying Ribbon doesn’t update the ribbon

Recently I’ve been working extensively with Ribbon. So what I did, I developed a ribbon and attached the ribbon to a feature. Then after modifying the ribbon in Visual Studio I deployed the ribbon again to see the changes. But interestingly the modification was not working. The ribbon was deployed successfully for the first time. But later I updated the ribbon xml in Visual studio and redeployed and the update to the ribbon was not showing in the site though the deployment was successful. Event I tried to deactivate/activate the ribbon feature but no luck. So the problem was ribbon update was not showing in effect in the site event after successful deployment.

 

Solution

Finally I found the solution from Sandrino Di Mattia’s post here. Basically, You need to modify the feature version every time you modify the ribbon and redeploy. First find which feature the ribbon is associated with. Then open the feature properties window and modify the feature version as shown below:

image

Figure 1: Change Feature Version

Another solution might be to clear your browser cache.

 

Conclusion

So changing the feature version or clearing the browser cache or private browsing might be solution for getting ribbon update in browser.

Monday, April 25, 2011

SharePoint Development Environment: Virtual Machine Or Virtual Hard Disk (VHD) with Dual boot

It’s true that SharePoint team has made the SharePoint rich enough to be useful in any organization. But they have taken less care of developers (my personal opinion). Developing with SharePoint was really a nightmare in old days (in SharePoint 2007) and the situation has not been changed that much today. I reminisce the days back in 2008 when four members of our SharePoint team worked in a single SharePoint server by logging in remotely. When one developer needed to reset IIS he needed to to inform others either he may interrupt some others’ debugging session.

From that nightmare development experience of my first SharePoint project, I’ve come to a better place. Now I’ve Core I7, 8GB ram laptop. But I used to work in Virtual Machine till few months ago and I had got slower disk access in Virtual Machine. So compiling, debugging and deploying was relatively slow. I don’t want to install SharePoint in my laptops’ Windows 7 and I hope fewer (possibly fewest)  people want to do so.

I’ve personally found developers who are coming to SharePoint Development from Asp.net, has got it difficult to get used to this environmental complexity. Anyway I’ve come to know about Virtual Hard Disk (VHD) solution few months ago and I’ve found it really a healthy approach. Let’s dig it deeper.

SharePoint Development in Virtual Machine

If you use Virtual Machine or any other virtual solution, you will have to realize that you will not the full processing power of your computer in SharePoint development. Even if in my Core I7 with 8GB ram, I get the development in Virtual Machine slower. If you do a lot of debugging and deployment you may loose your patience soon. However the real benefits include you can save virtual machine state. You can backup your virtual machine and restore.

SharePoint Development with Virtual Hard Disk (VHD)

Though I knew the concept of  Virtual Hard Disk about a year about ago, I didn’t get interested about it till few months ago. Basically the concept if you will have a Hard Disk (virtual obviously) and you will mount that virtual disk as a drive in your computer. Then you will install OS in that drive and add that OS in boot menu in your computer. So you will have more than one operating system in your computer but the OS will be in Virtual Hard Disk (which is a .vhd file). I’m going to describe it in details:

Create and Initialize a Virtual Hard Disk

For your information, the following option for creating VHD file from “Computer Management” is only available in Windows Server 2008 R2 and Windows 7. I’ve not explored if the option shown below is available for other operating systems.

  1. Open the “Computer Management” from Administrator tools and right click on the “Disk Management” and click “Create VHD” as shown below:

    image
    Figure 1: Create VHD option in disk management

  2. Then in the New VHD window, enter appropriate value as shown below:

    image

    Figure 2: Create new VHD settings page

  3. After that you will find a new disk is added as shown below:

    image
    Figure 3 New disk created for VHD

  4. So you have found you new VHD as new disk but the disk need to initialized. To do right click on the new disk and click initialize as shown below:

    image
    Figure 4: Initialize Virtual Hard Disk

  5. However when you’ll try to initialize the disk you will prompted for partition style. Its recommended MBR as shown below:

    image
    Figure 5: Create MBR partition in new VHD

  6. After that you will find the disk (disk 1 in above figure) online. After that create a new volume in that virtual disk as shown below:

    image
    Figure 6: Create new sample volume in Virtual (but online) disk.

  7. In the “New Sample Volume Wizard”, you can go with default settings and finally you will get a new drive in your computer which is virtual.

 

Mount a VHD and Install OS

Once you have your Virtual Hard Disk (VHD) ready you can try to install an OS in the VHD. To do so insert a Bootable CD/DVD in the CD/DVD-ROM and reboot your computer. To boot from CD/DVD make sure boot from CD/DVD enabled. I had tried to install Windows Server 2008 R2 in VHD. The process is described below:

  1. First, let the OS to start from bootable disk. Then when the following window comes up, press Shift + F10 to bring command prompt.
    image

    Figure 7: Windows Server 2008 R2 installation window.

  2. When the command prompt comes up (after press Shift + F10), type diskpart and press enter. And then type list volume and press enter to see all volumes available.
    image
    Figure 8: Use diskpart command to list volumes.

  3. You will find from “list volume” command that your drive letter is shifted by a letter (so, C becomes D, D becomes E and so on). Now type Select vdisk file=”VhdFilePath” as shown in the image below:
    image
    Figure 9: Select VHD file command

  4. Now you have selected the vhd file and you need to attach the file. To do so, run the command “attach vdisk” as shown below
    image
    Figure 10: Attach Vdisk command

  5. You have selected vdisk and attached it. Now close the diskpart by typing Exit and then close the command prompt by typing exit again.
  6. Now you can click Next in the “Install Windows” wizard as shown in figure 7. As you have attached the vdisk in the system, Installer will show the vhd as an disk and you can then choose to install OS in that vhd disk.
  7. When the Installation options come to choose the drive where to install OS, you can choose the VHD mounted disk (as shown in the image below). However you may get an warning saying “Windows Cannot be installed…” if you select the VHD for installing OS. Ignore the warning and install the OS in the vhd mounted disk.
    image
    Figure 11: Select VHD disk to install OS

  8. And when the installation will be finished, you will find your another OS is added in your boot menu.

So the final output is a file (VHD file) which is used for dual boot. You can copy the vhd file and attach it to another pc.

 

Attach an Existing VHD to boot option

Now say you have a vhd file and you need to attach the vhd file in boot option in your pc. You can do so with command. Fortunately, there’s GUI tools available to edit boot loader options. One such tool I’ve used is EasyBCD. I’ve shown below how to add a VHD file in boot menu.

  1. Run EasyBCD and click “Add New Entry”.
  2. From the bottom click “Virtual Disk” tab and then select the vhd file from disk.
  3. Finally click “Add Entry” to add the entry in boot option.

The following image shows process in a glance:

image

Figure 12: Add VHD file in boot option with EasyBCD

 

Now when you will restart your pc, you will find the new newly added entry is in boot option. Be cautious while you edit your boot menu. Improper editing of boot menu may fail your booting system.

 

Conclusion

If you are working with SharePoint frequently (as I do), you need to care about every minutes you are loosing for working in Virtual Environment. Since few months ago I had used Virtual Environment in my powerful laptop (Core-i7, 8GB RAM). However, disk access in VM environment is slow and as Visual Studio performance is somehow related to disk access (as during build a lot of disk read/write access operations are performed) so finally performance was a major problem.

Now I’ve moved my work to VHD and I’ve two VHD files (one for SharePoint Foundation and another is for SharePoint Server). I’ve three OS in my boot options: Windows 7 (installed with my laptop), SharePoint Foundation and SharePoint Server. I can remove any boot option from my boot menu any time with EasyBCD. And most of all, with VHD option, I get the full power of my laptop. If you guys have doubt about VHD option, I’ll ask you to give VHD option a try and I do believe you will like it. You will have to like it. FYI, I’m not a marketing guy anyway so maybe it’s hard for me to convenience you.

Please post your feedback.

Saturday, November 20, 2010

SharePoint: Event Receiver and Assembly Versions

I had encountered an interesting problem with event receiver few days back. By default we are used to attach an event receiver class to a list by declaring the event receiver in an xml file as shown below:

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Receivers ListTemplateId="101">
    <Receiver>
      <Name>MyEverReceiverOnAdd</Name>
      <Type>ItemAdded</Type>
      <SequenceNumber>10000</SequenceNumber>
      <Assembly>MyProject.MyEventReceiver, Version=2.1.0.0, Culture=neutral, PublicKeyToken=</Assembly>
      <Class>PermissionsEventHandler.PermissionsHandler</Class>
      <Data></Data>
      <Filter></Filter>
    </Receiver>
  </Receivers>
</Elements>

Figure 1: Event Receiver for version 2.1.0.0

As you can see in the xml file above we need to specify the version of the assembly.

 

Let’s image a scenario:

1. You have deployed your SharePoint solution (which has event receivers) while the assembly version is 2.1.0.0. So when you deployed the SharePoint solution, the list event receivers  in the SharePoint webs are registered with that 2.1.0.0 version of dll.

2. After few months, another version of your product is ready and you have changed the dll version (as this is practice to change your dll version as the development goes on). Now your dll version is 2.2.0.0. So you have updated the event receiver’s definition in xml file to reflect the new dll version as shown below:

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Receivers ListTemplateId="101">
    <Receiver>
      <Name>MyEverReceiverOnAdd</Name>
      <Type>ItemAdded</Type>
      <SequenceNumber>10000</SequenceNumber>
      <Assembly>MyProject.MyEventReceiver, Version=2.2.0.0, Culture=neutral, PublicKeyToken=</Assembly>
      <Class>PermissionsEventHandler.PermissionsHandler</Class>
      <Data></Data>
      <Filter></Filter>
    </Receiver>
  </Receivers>
</Elements>

Figure 2:Event Receiver for version 2.2.0.0

3. Now if you deploy the latest version of dll in server, the old assembly will be removed from GAC.Now you can get two different behaviors:

  • New site: If you create a new site after version 2.2.0.0 is deployed, there’ll be no problem as the new site will register it’s event receiver with the updated xml (which is using 2.2.0.0.0 as shown in image 2).
  • Old site: Here’s your are in trouble. You attached the event receiver after the version 2.1.0.0 is deployed. So the lists will look for that version of dll in GAC. 

 

Solutions:

So how can you handle the situation? After googling and analyzing I have found three solutions:

  • Fixed Version: One solution might be to keep the event receiver classes in a project whose version will not be changed. So you’ll always refer to the an assembly with the same dll version.
  • Deactivate/Activate Old Features: Another solution might be to uninstall/Deactive and then install/activate the event receiver features for all old sites. But this will add extra overhead in deployment. Additionally, if the number of webs are larger then it’s not viable solution.
  • DotNet Publisher Policy: .net publisher policy can be another option to redirect the reference of old assembly to new one. More information on publisher policy can be found in the links: http://msdn.microsoft.com/en-us/library/dz32563a.aspx, http://support.microsoft.com/kb/891030

Sunday, October 24, 2010

SharePoint: StaticName, InternalName, DisplayName. Which one to use in case of Multilingual supported site?

SharePoint provides three different names for referring a field: DisplayName, Intername and StaticName. You can get more details on these three properties in the MSDN post. In short, Display name is what you see in the site. Internal name is the readonly name which is created when the filed in created and can’t be modified i.e., readonly and the name should be unique. Static name doesn’t need to be unique and can be changed. If you don’t change the static name, internal name and static name are same.

 

Ok, let’s come to point. We used to access list item’s value by using display name as shown below:

var value = ListItem[DisplayName]

The problem in this approach is that if display name can be different for different language. So using display name can work in a language but may not work in different language.

 

Problem I experienced in using Display name

I was working on content approval related stuffs. And I had found the approval status is kept in a field whose display name is ‘Approval Status’. So I had used code like shown below to get the approval status field value.

ListItem[‘Approval Status’]

Unfortunately, my client was using German version and when I deployed I found my code is not working. After spending some time I had figured out the column ‘Approval Status’ is missing in the list. And I finally figured out the column name is not ‘approval status’ but it’s German equivalent.

 

So, which one to use from InternalName, StaticName, DisplayName?

So which field to use when? If you need to access custom field created by you or your team, you can use display name (considering you are using different display name for different language) . If you are using any SharePoint provided field then better to use StaticName or Intername, since you are not sure if the display name will be changed or not.

Saturday, October 23, 2010

SharePoint: Active/Deactivate Feature programmatically

Sometimes you may need to activate/deactivate features programmatically. In my case, I needed to do some synchronization from a Synchronization feature. As part of the synchronization, I needed to move data from one list to another. However the destination list had event receiver attached. The event receiver was attached using another feature (say ListItemEventReceiverAttacher). So I needed to deactivate the feature (i.e., ListItemEventReceiverAttacher) to make sure that event receiver associated to the list is not active. So let’s come to the point rather than beating around the bush.

First of all, your feature can have scope of web, site or webapplication or Farm. so you need to get the feature first. You can do so by accessing Features properties of Site or web as shown below:

1. Get the activated Features available in the WebApplication/Site/Web level

var webApplicationFeatures = webapplication.Features
var webFeatures = web.Features;
var siteFeatures = site.Features;

2. once you have got the features you can find your feature by feature id or name, if the feature is activated, as shown below:

var feature = site.Features.SingleOrDefault(sf => sf.DefinitionId == myFeatureDefinitionId);

var feature = site.Features.SingleOrDefault(sf => sf.Definition.DisplayName.Equals("Feature Title"));

If the feature is null then the feature is not activated. Only activated features will be available through site.Features/web.Features property.

3. If feature is not activated, you can activate the feature as shown below:

if(feature==null)//if feature is not activated
{
site.Features.Add(featureId);//activate feature
}

4. If the feature is activated, you can deactivate the feature as shown below:

if(feature!=null)//if feature is activated
{
site.Features.Remove(feature.DefinitionId);//deactivate feature
}

Important: One important thing to notice that once you activate/deactivate a feature against an object (SPWebApplication/SPSite/SPWeb), you need to open a new SPWebApplication/SPSite/SPWeb object to get the changes you made. The scenario shown below:

using (SPSite site=new SPSite("http://myserver"))
{
    //Activate or deactivate feature
    //the changes will not be available in this scope.
}
using (SPSite site=new SPSite("http://myserver"))
{
    //Changes made to the feature in upper using block will be effeftive here
}

Wednesday, October 13, 2010

SharePoint 2010: Delete webpart Title (remove untitled title)

When you add a webpart in a page, the webpart title is shown. But sometimes we want to delete/hide the webpart. To remove the webpart title, we can edit the webpart and remove the title from webpart edit toolbox. Unfortunately, if you remove the webpart title, you’ll find a default webpart title ‘Untitled’ is added.

To remove ‘untitled’ title, edit the title of the webpart and put an empty string (or or more spaces) in the tile. This will show the title but as empty string is shown, it can’t be seen.

image
Figure 1: Deleting Title
image
Figure 2: empty title (space)

 

As shown in the image above, if you put empty space in title the title can’t be seen and it seems there’s no title.

Tuesday, October 12, 2010

SharePoint Long running tasks/Operations: Timer Job or SPLongOperation

When you need to perform some operations in a webpart and the operation may take few minutes, you have two options to choose from. One might be Timer job. Clicking on a button in the webpart will start the timer job and you can check the job status periodically and show the status on the page. Timer job is perfect for long running operation but showing status in UI while the job is running might be a bit hard.

Another option might be to use SPLongOperation class of SharePoint to run long operation while to show user meaningful message. SPLongOperation is a good choice when you want to run an operation that takes few minutes only and when the user is trusted ( I mean the user is trusted to wait while the operation in progress by seeing the wait screen rather than refreshing the page). As shown in the following code snippet, you can create a SPLongOperation instance and put the long running code between SPLogOperation.Begin() and SPLongOperaiton.End() method invocation. In the end operation you need to specify the url the page will be redirected to once the operation is done. This might be the same page url or different page’s url.

var longOperation=new SPLongOperation(this.Page);
longOperation.LeadingHTML = "Please wait while the operation is running";
longOperation.TrailingHTML = "Once the operation is finished you will be redirected to result page";
longOperation.Begin();

//Do long operation here
Thread.Sleep(10000);

longOperation.End("Result.aspx");

As as result of the above code you can see the following screen during the operation is progress.

image

 

Even when you are not sure how much time a page takes to load you can use the SPLongOperation interface which may provide better user experience. So if your operation needs several minutes or more your choice might be Timer job. But the operation takes few minutes and the operation needs user interaction then you can opt to use SPLongOperation.

Friday, February 12, 2010

SharePoint 2007: Create web part page library, add a webpart page with webpart without using publishing feature

You can create a document library with several document templates for example, excel, word, webpart page etc. If you need to create a document library with ‘web part page’ document template programmatically then you can do it without enabling publishing feature. Most of the examples on the web uses publishing feature.

Create a new document library with ‘web part page’ document template

The following code snippet shows how to Create the web part page document library.

private void CreateWebPartPageLibrary(SPWeb web)
{
//get the document library list template
var documentListTemplate = web.ListTemplates["Document Library"];
//get document template (word file,excel file, web part pages etc) that will be used in the documentlibrary
var webPartPageDocTempalte = GetWebPartPageDocTemplate(web);
web.Lists.Add(PAGE_LIBRARY_NAME, "Programmatically added library", documentListTemplate, webPartPageDocTempalte);

}
private SPDocTemplate GetWebPartPageDocTemplate(SPWeb web)
{
SPDocTemplate docTemplate = null;
foreach (SPDocTemplate template in web.DocTemplates)
{
if (template.Name.Equals("Web Part Page", StringComparison.OrdinalIgnoreCase))
{
docTemplate = template;
break;
}
}
return docTemplate;
}

In the CreateWebPartPageLibrary method, the first line get the template for document library. Then the GetWebPartPageDocTemplate return the document template for “web part page”. Finally I have called web.lists.add method to add our new list to the list collection.

 

Add a web part page in Web Part page library

Once you have web part page library, you need to add web part page in the library. You can do so programmatically. To add the page you need to prepare an xml command and execute with with ProcessBatchData method.

SPList list = web.Lists["MyPagesLibrary"];
string postInformation =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<Method>" +
"<SetList Scope=\"Request\">" + list.ID + "</SetList>" +
"<SetVar Name=\"ID\">New</SetVar>" +
"<SetVar Name=\"Cmd\">NewWebPage</SetVar>" +
"<SetVar Name=\"Type\">WebPartPage</SetVar>" +
"<SetVar Name=\"WebPartPageTemplate\">1</SetVar>" +
"<SetVar Name=\"Title\">{0}</SetVar>"+
"<SetVar Name=\"Overwrite\">true</SetVar>" +
"</Method>";

web.ProcessBatchData(postInformation);

In the above code snippet a command is prepared to generate a web part page. In the command the Type is WebPartPage. WebPartPageTemplate is for different page layout that we can find in the sharepoint UI. The page title is only title without aspx.

Add web part to webpart page

You have created a ‘web part page’ library and added a webpart page. Now you need to add your web part in that page. First of all you need to create a web part instance. The following code Snippet shows how to create a web part programmatically.

private System.Web.UI.WebControls.WebParts.WebPart GetWebPart(SPWeb web, string webPartName)
{
var query = new SPQuery();
query.Query = String.Format(
"<Where><Eq><FieldRef Name='FileLeafRef'/><Value Type='File'>{0}</Value></Eq></Where>",
webPartName);

SPList webPartGallery;
if (web.IsRootWeb)
{
webPartGallery = web.GetCatalog(
SPListTemplateType.WebPartCatalog);
}
else
{
webPartGallery = web.ParentWeb.GetCatalog(
SPListTemplateType.WebPartCatalog);
}
var webParts = webPartGallery.GetItems(query);
var typeName = webParts[0].GetFormattedValue("WebPartTypeName");
var assemblyName = webParts[0].GetFormattedValue("WebPartAssembly");
var webPartHandle = Activator.CreateInstance(
assemblyName, typeName);

System.Web.UI.WebControls.WebParts.WebPart webPart =
(System.Web.UI.WebControls.WebParts.WebPart)webPartHandle.Unwrap();
return webPart;
}

The web part name in the above method GetWebPart() is like “employeeviewer.webpart”. The webpart collection exists in the rootweb. So I have check the IsRootWeb to make sure I run the caml query against the root web.

Once you have got the web part instance created by Activator.CreateInstance you can add it in the page.

const string WEBPART_TITLE="test web part"
//get the web part with GetWebPart method that I have posted in this blog
System.Web.UI.WebControls.WebParts.WebPart webPart = GetWebPart(web, "wpCopyFile.webpart");
using (webPart)
{
using (SPLimitedWebPartManager manager = web.GetLimitedWebPartManager(string.Format("Pages/{0}.aspx",WEBPART_PAGE_NAME), PersonalizationScope.Shared))
{
//if web part already exists then return
foreach (System.Web.UI.WebControls.WebParts.WebPart oldWebPart in manager.WebParts)
{
if (oldWebPart.Title.Equals(WEBPART_TITLE))
return;
}
webPart.Title = WEBPART_TITLE;
manager.AddWebPart(webPart, "", 0);
manager.SaveChanges(webPart);
}
}


The code snippet above call the GetWebPart() method to get the instance of the web part. Then using LimitedWebPartManager it get the page’s web part manager. Then it check if the web par already exists in the page by checking title. And finally add the web part if it doesn’t exists. FYI, be careful to dispose webpart and SPLimtiedWebPartManager.