Monday, November 21, 2011

SharePoint basics

I thought it would be a good idea to put together my thought on how SharePoint request navigates through the server and how it works. That explains lot of things in SharePoint world.

Since SharePoint web application is a .NET web application, the fundamentals remains same. Though there are some differences.
When SharePoint is installed on a web server, the installation process updates the IIS metabase to introduce it to the new mechanism for getting pages from content database rather then getting it from file system as it does naively.
So after SharePoint installation, IIS is armed to understand the SharePoint requests. We all know that SharePoint stores everything in database. However IIS still needs to be told whether the request that it has received it for SharePoint site or classic ASP.NET web site. Therefore a basic web application existence is required on each web server for each web application. And that web application has the web.config file that has the mapping of HTTP handler and SPRequest (HTTPModule) that worker process needs to use to get pages out of content database.
So when any browser sends the request for a SharePoint site, IIS either has the application instance in application domain (memory assigned for .NET application and owned by .NET framework) or it creates a new instance and loads the web.config settings. And if it is a SharePoint web app, the conifig settings would instruct worker process to get data from content database using SharePoint HTTP Handler.
Now since the SharePoint pages are stored in database, SharePoint provides safety mechanism as what would come from where and it will also enforce the safety rules.
The SPModule is a special HTTPModule that filters through the requests that comes to it. It also registers the SPVirtualPathProvider on Init method. SPVirtualPathProvider helps identify if the page should come from content database or SharePoint root as there are application pages, core list definition and other things that would come from SharePoint root folder.
SPPageParserFilter would help handler decide on the basis of the Web.Config settings if the code blocks are allowed from the location that request is trying to get page from.This is when the page contains server side code.The Safe Mode Processing allows various rules to enforce what would come and what not.
Finally when the request has been filtered through SPModule, it reaches to SPHTTPHandler and it gets page from content database, processes it and send the processed HTML back to the requester.



Sunday, April 3, 2011

SharePoint 2010: Custom List form and Lookup Field rendering displacement

If you have below scenario:
1. Custom List Form
2. Lookup Field 
3. Lookup list more  having more than 19 items
4. You are using some custom CSS that has position:relative

Then in IE, the lookup field starts rendering like this:
See the displacement of the lookup list values from the drop down. This happens because if the number of items crosses beyond 19, it starts rendering it as Input HTML control and if items are below 19, it renders them as Select. It assumes that if there are more then 19 items, users will type the starting characters to get smaller number of items to select from. And if you custom CSS has position:relative for DIV (if you are using JQueryUI, you may have it), you get this. 
So the solution is to override the custom CSS setting. simpleste solution would be to put this in style section
Note: Style section should go in the PlaceHolderAdditionalPageHead placeholder.

<style>
#_Select
{
position:static!important;
}

</style>

And then you get it right.

Saturday, April 2, 2011

SharePoint 2010: Custom Validation in List Form for US Phone Number

Recently I had hard time in validating the a filed for a phone number in a custom list form. The OOB ASP.NET RegEx validator can be used but somehow in SharePoint 2010 even if the RegEx expression is correct, it does not validate the correctly formated number. Therefore I decided to use the custom validator.

1. Before you go ahead and use the validator, you should have the ID of the control you wish to validate. The best way I have figured out so far is:
    a. Browse to the custom new form in browser and go to view source
    b. Locate the control and copy the portion of the control name (not the ID) aftre the ff value of control. For example if the name of the control is ctl00$m$g_7864a235_2177_4ad9_bb8e_c9c86a8b6b4f$ff61$ctl00$ctl00$TextField then you should copy only the text aftre the ff (form field) text including ff. so the form name that you will use for this example will be ff61$ctl00$ctl00$TextField

2. You can simply insert the custom ASP.NET validator from ASP.NET tools sectionwhile you are in design view of the custom form:

3. Put correct statement in error message property of the validator.
4. Paste this name (ff61$ctl00$ctl00$TextField in our example) in the ControlToValidate property of the custom validator.
5. Put the name of the javascript function (where you will be performing the validation.) in the ClientValidationFunction property of custom validator.
6. Go to the Code of your form and put the java script function in the PlaceHolderAdditionalPageHead area.
Sample Code:

        function ValidateCallerPhoneNumber(source, arguments)
{
var phoneNumberPattern = /^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/;
arguments.IsValid = phoneNumberPattern.test(arguments.Value);
return;
}


To understand the RegEx, please read this.

And you are all set.On run time, the custom validator will fire this java script function and supply the required parameters and get the IsValid back from function and show the error message in case validation fails.

Friday, April 1, 2011

Logged-In/Current user's email address populated from list in custom list form

Scenario: Recently I had a requirement wherein the business wanted to maintain the email addresses of users in a list. And in other forms where users are suppose to enter data, the email address should automatically populated from this list. The Site was going to be hosted with hosting company and they wanted the email address in a list so that every user can easily maintain the email address and other information.

Solution: One custom list for user where at least email address, User ID and the person's NT ID (filed type-person) is stored. Then in the record list where users are making entries (for example: Sales Order Main) there will be a field for email address of the user (for example: Sales Executive) and in the new form, it would automatically populated form the other user list using SPServices GetListItems method supplying the currentUser as parameter.

Example execution:

1. Create a list for users:

Note: The title column has been renamed to NTUserID. You may as well customize the new list form for this lsit so that users dont have to enter the NTUserID manually and by validating the NTUserName, it inserts the NTUserID in the text field. and you can make the NTUserID hidden. 

2. In SPD, create a custom list form for new items and and edit that form in advance mode and locate the placeholder PlaceHolderAdditionalPageHead

3. Put reference to the Script Library (I assume that you would have a DocLib for script libraries that will have all scripting libraries uploaded).


<script src="/ScriptLib/jquery-1.5.2.js"></script>
<script language="javascript" type="text/javascript" src="/ScriptLib/jquery.SPServices-0.6.0/jquery.SPServices-0.6.0.min.js"></script>

4. Now you have to get the email address from the Users list for currently logged in user.

$(document).ready(function() {
var thisUserAccount = $().SPServices.SPGetCurrentUser({
fieldName: "Name",
debug: false
});

Note: You should alway use the Name field as working with Title is easy but then there could be more than one person with same Title person in AD however there cannot be two person with same account name.

5. Before you can use the thisUserAccount , you should replace the single slash with double slash so that it works with CAML.

var userID=String(thisUserAccount);
userID.replace("\\","\\\\")

5. Now get the element ID for the email address text box. (If the element ID in your form is ff4 then it will be rendered with a big ID with _ff41_ as part of the ID text. You can just use this rather then using complete ID to get hold of the text box and assign value of the email address you got from previous JQuery call.


   $().SPServices({
   operation: "GetListItems",
   async: false,
   listName: "Users",
   CAMLViewFields: "<ViewFields><FieldRef Name='EMailAddress' /></ViewFields>",
   CAMLQuery: "<Query><Where><Eq><FieldRef Name='Title' /><Value Type='Text'>" + userID + "</Value></Eq></Where></Query>",
   completefunc: function (xData, Status) {
     $(xData.responseXML).find("[nodeName='z:row']").each(function() {
       $("#[id*='_ff41_']").val($(this).attr("ows_EMailAddress"));
     });
   }
 });
});

Note: The expression $("#[id*='_ff41_']").val() will locate any element that has _ff41_ in the ID. Since we are using both underscores, it would surely get that text box and another good thing (may be) about this is that even if you move this list to other web apps/site collection on any different level, or upgrade SharePoint  in future, the full ID may change but (most probably) it will have the _ff41_ text in it.

So now if the list has the email address for logged in user, the new form will have that in the email address text box on form load. 


Wednesday, March 23, 2011

SharePoint 2010 Custom List Form: Issues with MultiTab UI

If you want not to use InfoPath Form for some reason and want to customize the List Form using DVWP, which is still a good option in case you want more control on the UI of the form, you will come accross a few issues specially if you are using the JQuery tabs (MultiTab form). To get the JQuery tabs to work, you have to download JQuery UI and then upload in a Script Library (A doc library), as you were doing on SharePoint 2007. Then you have to refer them on the custom form like this:

[This should go in the PlaceHolderAdditionalPageHead]

<link rel="stylesheet" href="/ScriptLib/jquery-ui-1.8.11.custom/development-bundle/themes/base/jquery.ui.all.css">
<script src="/ScriptLib/jquery-1.5.1.js"></script>
<script src="/ScriptLib/jquery-ui-1.8.11.custom/development-bundle/ui/jquery.ui.core.js"></script>
<script src="/ScriptLib/jquery-ui-1.8.11.custom/development-bundle/ui/jquery.ui.widget.js"></script>
<script src="/ScriptLib/jquery-ui-1.8.11.custom/development-bundle/ui/jquery.ui.tabs.js"></script>
<link rel="stylesheet" href="/ScriptLib/jquery-ui-1.8.11.custom/development-bundle/demos/demos.css">

Once done, you code the form HTML to have tabs as explained here.

But you are not done. If you have any DateTime control on your form, they will look very small and if you look at the view source, you would find that the font size is 0 for the Date Picker control.


Therefore, you have to override the settings of CSS like this:


         <style>

.ms-dtinput{
   font-size:11px!important;
}

</style>

Note: I recently learned that the issue was with one of the CSS and better way of handling would be to correct that CSS. I was using JQuery UI (tabs) and one of the CSS I was using was jquery.ui.theme.css. In this CSS, here is the code snippet that should be changed:


/* Component containers
----------------------------------*/
.ui-widget { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1.1em/*{fsDefault}*/; }
.ui-widget .ui-widget { font-size: 1em; }
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1em; }

The font-size part should be removed and this wil fis the issue and the consumer component will inherit the font size from main CSS (in my case core.css).



Then there is another issue if you many any HTML change in the form and save it, assuming that you made your custom form default, aftre changing the HTML, though it still shows as default form, it does not work and you will see the OOB form when aftre you change any HTML in the form and save it. SO you have to set OOB form default and set the custom form default again. I am assuming that either I am missing some setting some where (and the chances for that is remote) or it is a bug.

Saturday, March 19, 2011

SharePoint 2010- Content Types, Lists and UI for the List

In my recent project on SharePoint 2010, I came across with various options to create list, content type and UI for the list. Few of them that I played with were:
VS Code Approach:
SharePoint Guidance Approach
1. Create your content type 
2. Create your List Instances
3. Create the feature to assign Content Type to List Instances
4. Put the required code like removing other or default content type from list in feature activation and deleting the list in feature deactivation.
5. Create the Entity classes using SPMetal
6. Create your UI in the Visual web parts.


The approach is good but it all depend on your requirement. If your requirement is such that you want business to give the flexibility to be able to add new field, or to modify the view, then this approach is not the correct one for you. In most of the cases, since SharePoint is a system for business, you would like those features to be available for business.


Another issue with this approach is that it takes TOO much time to do a small thing. 


However it would be a perfect solution for a project where the requirements are pretty much stable and business should NOT be able to change things.


Then there is a middle approach, though with the coding approach only, to create list definition with custom list form in the code and deploying them. List definition approach can also utilize site content types and list content types or site columns and list columns. And you may put a custom list form within the project rather then creating a web part or visual web part. 


And then there is No Code approach where you use SPD. You can create your site content type. Though using site content type is probably not a good idea is the requirements keep changing or to be more specific, you see business coming up with new fields or change in field type frequently. So you can create your list columns and either customize the list form or use InfoPath custom forms. If you customize the list forms, you can get the benefits of all JQuery power that you used in MOSS and can do most of the things in client side and have more control on how things will be rendered. However InfoPath form is better if you don't want to go in SPD each time you have a little change or you have a tech-savvy business user who needs to power to be able to change things in UI. However most of the things can be done in both places like validation or RegEx validation and client side rules.



Custom Content Type with Lookup Column and SPMetal in SharePoint 2010

If you have custom content type in your Visual Studio project that you are going to use to create a List and if that Content Type has a Lookup field that points to another custom content type AND if you try to use SPMetal to create entity classes, you get this error:


Error: The given key was not present in the dictionary.


Now Nowhere in Microsoft documentation it even gives you any clue of this. rather, as per the documentation, it should work flawlessly. 

I came across this article from where I got some clues. So, you have to create a custom parameter XML in order to get it to work. I in any case liked this as if you don't use any parameter file, SPMetal creates a very big, complex entity class and most of the thing in there you wont use. Therefore, using parameterize SPMetal is a good idea to keep code simple.

So here is what I did:


<?xml version="1.0" encoding="utf-8"?>
<Web AccessModifier="Internal" xmlns="http://schemas.microsoft.com/SharePoint/2009/spmetal">
<ContentType Name="Facility"></ContentType>
<ContentType Name="IntakeQuestionaire"></ContentType>
<ExcludeOtherContentTypes></ExcludeOtherContentTypes>
</Web>

Check that you have to use the ExcludeOtherContentTypes element in order to get rid of other content type and you get s simpler code.

Though it has bee a few weeks that I did but I also remember that if you dont use this, and if you in same scenerio, you will get entity class with two entity classes, one for content type and one for list that uses same content type and it names like contenttype1 and list1contenttype1. 

So, as best practice you should always use the Exclude elements and use parameter files for SPMetal.