Wednesday 14 December 2011

Custom Login Page Sharepoint 2010

The implementation above actually works as expected -- well, up to a point.

The problem is that when you click the out-of-the-box Sign Out link anytime after authenticating via the custom Web Part, a rather nasty unhandled exception occurs:

[ArgumentException: Exception of type 'System.ArgumentException' was thrown. Parameter name: encodedValue] Microsoft.SharePoint.Administration.Claims.SPClaimEncodingManager.DecodeClaimFromFormsSuffix(String encodedValue) +25829214 Microsoft.SharePoint.Administration.Claims.SPClaimProviderManager.GetProviderUserKey(String encodedSuffix) +73 Microsoft.SharePoint.ApplicationRuntime.SPHeaderManager.AddIsapiHeaders(HttpContext context, String encodedUrl, NameValueCollection headers) +845 Microsoft.SharePoint.ApplicationRuntime.SPRequestModule.PreRequestExecuteAppHandler(Object oSender, EventArgs ea) +352 System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +80 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +171

Obviously this exception doesn't occur when authenticating using the OOTB login pages. I also verified that it doesn't occur when using a custom application page for FBA and claims. This really had me stumped for a little while since my Web Part was calling the exact same code as the custom application page in order to perform the claims authentication.

Using Fiddler, I observed the following cookie when authenticating with the OOTB login page:

  • Cookies / Login
    • Cookie
      • FedAuth
        • 77u/PD94...

I noticed a similar cookie when authenticating with the custom application page (e.g. /_layouts/Fabrikam/SignIn.aspx).

However, when I logged in using the Claims Login Form Web Part, I found that there were two cookies:

  • Cookies / Login
    • Cookie
      • .ASPXAUTH=1D0DDE35...
      • FedAuth
        • 77u/PD94...

When I then clicked Sign Out, I noticed that while the "FedAuth" cookie was removed from the subsequent request, the ".ASPXAUTH" cookie was not. In other words, the presence of an ".ASPXAUTH" cookie (without a "FedAuth" cookie) causes SharePoint Server 2010 to "blow chunks."

To remedy the issue, I moved the code in the LoginForm_LoggedIn event handler into the LoginForm_Authenticate event handler. Since the user is redirected upon successful login, this prevents the ASP.NET Login control from generating the ".ASPXAUTH" cookie. With this change, the OOTB Sign Out link started working as expected.

COMPLETE WEBPART CODE.... can comment out SPLogger.Log functionality..  using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IdentityModel.Tokens;
using System.Web;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Fabrikam.Demo.CoreServices.SharePoint.Diagnostics;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.IdentityModel;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
using System.Web.Security;
using System.Web.UI;

namespace Fabrikam.Demo.Web.UI.WebControls
{
///
/// A Web Part that displays a login form for claims-based authentication.
///

[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Microsoft.Naming",
"CA1726:UsePreferredTerms",
MessageId = "Login")]
[ToolboxItemAttribute(false)]
public class ClaimsLoginFormWebPart : SslRequiredWebPart
{
private Login loginForm;
private HyperLink windowsLoginLink;
private const string defaultWindowsLoginLinkText =
"Fabrikam Employee Sign In";

///
/// Gets or sets a value that indicates whether to display a check box
/// to enable the user to control whether a persistent cookie is sent
/// to their browser.
///

[Category("Presentation")]
[DefaultValue(true)]
[Personalizable(PersonalizationScope.Shared, true)]
[WebBrowsable(true)]
[WebDescription("Indicates whether to display a check box to enable the"
+ "user to control whether a persistent cookie is sent to their"
+ "browser.")]
[WebDisplayName("Display Remember Me")]
public bool DisplayRememberMe
{
get
{
if (HttpContext.Current == null)
{
// Avoid NullReferenceException when programmatically
// configuring pages outside the context of an HTTP request
// (e.g. when adding the Web Part to a page during feature
// activation through PowerShell)
return true;
}

this.EnsureChildControls();
return this.loginForm.DisplayRememberMe;
}
set
{
this.EnsureChildControls();
this.loginForm.DisplayRememberMe = value;
}
}

private static SPIisSettings IisSettings
{
get
{
SPWebApplication webApp = SPWebApplication.Lookup(
new Uri(SPContext.Current.Web.Url));

SPIisSettings settings = null;

if (webApp.IisSettings.ContainsKey(
SPContext.Current.Site.Zone) == true)
{
settings = webApp.IisSettings[
SPContext.Current.Site.Zone];
}
else
{
Debug.Assert(webApp.IisSettings.ContainsKey(
SPUrlZone.Default) == true);

settings = webApp.IisSettings[SPUrlZone.Default];
}

return settings;
}
}

///
/// Gets or sets the text for the link used for Windows authentication.
///

[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Microsoft.Naming",
"CA1726:UsePreferredTerms",
MessageId = "Login")]
[Category("Presentation")]
[DefaultValue(defaultWindowsLoginLinkText)]
[Personalizable(PersonalizationScope.Shared, true)]
[WebBrowsable(true)]
[WebDescription("Gets or sets the text for the link used for Windows"
+ " authentication.")]
[WebDisplayName("Windows Login Link Text")]
public string WindowsLoginLinkText
{
get
{
if (HttpContext.Current == null)
{
// Avoid NullReferenceException when programmatically
// configuring pages outside the context of an HTTP request
// (e.g. when adding the Web Part to a page during feature
// activation through PowerShell)
return defaultWindowsLoginLinkText;
}

this.EnsureChildControls();
return this.windowsLoginLink.Text;
}
set
{
this.EnsureChildControls();
this.windowsLoginLink.Text = value;
}
}

///
/// Called by the ASP.NET page framework to notify server controls
/// that use composition-based implementation to create any child
/// controls they contain in preparation for posting back or rendering.
///

protected override void CreateChildControls()
{
base.CreateChildControls();

this.CssClass = "loginForm";

if (this.Page.Request.IsSecureConnection == false)
{
this.Controls.Add(new LiteralControl(
"

Warning: This page is not encrypted"
+ " for secure communication. The user name and"
+ " password entered in the form below will be sent in"
+ " clear text.

"));
}

this.loginForm = new Login();
this.loginForm.TextBoxStyle.CssClass = "text";

// HACK: ASP.NET hard-codes align="center" if not specified
this.loginForm.FailureTextStyle.HorizontalAlign =
HorizontalAlign.Left;

this.loginForm.FailureTextStyle.CssClass = "errorMessage";

this.loginForm.Authenticate += new AuthenticateEventHandler(
LoginForm_Authenticate);

this.loginForm.LoginError += new EventHandler(LoginForm_LoginError);

this.Controls.Add(loginForm);

windowsLoginLink = new HyperLink();
windowsLoginLink.Text = defaultWindowsLoginLinkText;

string returnUrl = "/";

if (string.IsNullOrEmpty(
this.Context.Request.QueryString["ReturnUrl"]) == false)
{
returnUrl = this.Context.Request.QueryString["ReturnUrl"];
}

windowsLoginLink.NavigateUrl =
"/_windows/default.aspx?ReturnUrl="
+ returnUrl;

this.Controls.Add(windowsLoginLink);
}

void LoginForm_LoginError(
object sender,
EventArgs e)
{
string membershipProviderName = null;
string roleProviderName = null;

GetClaimsProviderNames(
out membershipProviderName,
out roleProviderName);

MembershipUser user =
Membership.Providers[membershipProviderName].GetUser(
this.loginForm.UserName, false);

if (user == null)
{
return;
}

if (user.IsLockedOut == true)
{
loginForm.FailureText = "Your account has been locked out"
+ " because of too many invalid login attempts. Please"
+ " contact a site administrator to have your account"
+ " unlocked.";
}
else if (user.IsApproved == false)
{
loginForm.FailureText = "Your account has not yet been"
+ " approved. You cannot login until a site"
+ " administrator approves your account.";
}
}

private void DisableLoginForm()
{
this.EnsureChildControls();

this.loginForm.Enabled = false;

DisableLoginValidator(this.loginForm, "UserNameRequired");
DisableLoginValidator(this.loginForm, "PasswordRequired");

windowsLoginLink.Enabled = false;
}

private static void DisableLoginValidator(
Login loginControl,
string validatorID)
{
Debug.Assert(loginControl != null);
Debug.Assert(string.IsNullOrEmpty(validatorID) == false);

RequiredFieldValidator validator = loginControl.FindControl(
validatorID) as RequiredFieldValidator;

if (validator != null)
{
validator.Enabled = false;
}
}

private static void GetClaimsProviderNames(
out string membershipProviderName,
out string roleProviderName)
{
SPIisSettings iisSettings = IisSettings;

SPFormsAuthenticationProvider authProvider =
iisSettings.FormsClaimsAuthenticationProvider;

membershipProviderName = authProvider.MembershipProvider;
roleProviderName = authProvider.RoleProvider;
}

[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Microsoft.Naming",
"CA1726:UsePreferredTerms",
MessageId = "Login")]
private void LoginForm_Authenticate(
object sender,
AuthenticateEventArgs e)
{
if (e == null)
{
throw new ArgumentNullException("e");
}

SPLogger.Log(
LogCategory.WebParts,
TraceSeverity.Verbose,
"Authenticating forms user ({0}) on site ({1})...",
this.loginForm.UserName,
SPContext.Current.Web.Url);

e.Authenticated = SPClaimsUtility.AuthenticateFormsUser(
new Uri(SPContext.Current.Web.Url),
this.loginForm.UserName,
this.loginForm.Password);

if (e.Authenticated == false)
{
SPLogger.Log(
LogCategory.WebParts,
TraceSeverity.Verbose,
"Failed to authenticate forms user ({0}) on site ({1}).",
this.loginForm.UserName,
SPContext.Current.Web.Url);

return;
}

string membershipProviderName = null;
string roleProviderName = null;

GetClaimsProviderNames(
out membershipProviderName,
out roleProviderName);

SPLogger.Log(
LogCategory.WebParts,
TraceSeverity.Verbose,
"Getting security token for user ({0})...",
this.loginForm.UserName);

SecurityToken token =
SPSecurityContext.SecurityTokenForFormsAuthentication(
new Uri(SPContext.Current.Web.Url),
membershipProviderName,
roleProviderName,
this.loginForm.UserName,
this.loginForm.Password);

SPLogger.Log(
LogCategory.WebParts,
TraceSeverity.Verbose,
"Setting principal claim and writing session token for user"
+ " ({0})...",
this.loginForm.UserName);

SPFederationAuthenticationModule.Current.SetPrincipalAndWriteSessionToken(
token);

SPLogger.LogEvent(
LogCategory.WebParts,
EventSeverity.Verbose,
"Successfully authenticated forms user ({0}) on site ({1}).",
this.loginForm.UserName,
SPContext.Current.Web.Url);

RedirectToSuccessUrl();
}

///
/// Raises the event.
///

///
/// This method is overridden in order to disable the validators when
/// editing the page containing the Web Part. Note that the validators
/// must be disabled before the PreRender phase of the page life cycle
/// in order for this to work.
///

/// An object
/// that contains the event data.
protected override void OnLoad(
EventArgs e)
{
base.OnLoad(e);

EnsureChildControls();

SPControlMode formMode = SPContext.Current.FormContext.FormMode;

if (formMode == SPControlMode.Edit
|| formMode == SPControlMode.New)
{
DisableLoginForm();
}
else if (this.Page.User.Identity.IsAuthenticated == true)
{
DisableLoginForm();
}
}

///
/// Raises the event.
///

///
/// This method is overridden in order to hide the Web Part for
/// authenticated users.
///

/// An object
/// that contains the event data.
protected override void OnPreRender(
EventArgs e)
{
base.OnPreRender(e);

SPControlMode formMode = SPContext.Current.FormContext.FormMode;

if (formMode == SPControlMode.Edit
|| formMode == SPControlMode.New)
{
// Always show the Web Part when editing a page
}
else
{
if (this.Page.User.Identity.IsAuthenticated == true)
{
// Hide the login form for authenticated users

// HACK: Attempting to set the Visible property on a Web
// Part can result in an InvalidOperationException:
//
// "The Visible property cannot be set on Web Part
// 'LoginForm'. It can only be set on a standalone Web
// Part."
//
//this.Visible = false;
this.Controls.Clear();
}
}
}

///
/// The following method was originally snarfed from
/// Microsoft.SharePoint.IdentityModel.Pages.IdentityModelSignInPageBase.
///

private void RedirectToSuccessUrl()
{
string uriString = null;
if (
this.Context == null
|| this.Context.Request == null
|| this.Context.Request.QueryString == null)
{
uriString = null;
}
else if (
string.IsNullOrEmpty(
this.Context.Request.QueryString["loginasanotheruser"]) == false
&& string.Equals(
this.Context.Request.QueryString["loginasanotheruser"],
"true",
StringComparison.OrdinalIgnoreCase)
&& string.IsNullOrEmpty(
this.Context.Request.QueryString["Source"]) == false)
{
uriString = this.Context.Request.QueryString["Source"];
}
else if (string.IsNullOrEmpty(
this.Context.Request.QueryString["ReturnUrl"]) == false)
{
uriString = this.Context.Request.QueryString["ReturnUrl"];
}

if (uriString == null)
{
uriString = "/";
}
else if (uriString.StartsWith(
"/_layouts/Authenticate.aspx",
StringComparison.OrdinalIgnoreCase) == true)
{
SPLogger.Log(
LogCategory.WebParts,
TraceSeverity.Medium,
"The user clicked the \"Sign In\" link on the custom"
+ " sign-in page (instead of immediately entering"
+ " credentials). Redirecting to '/' in order to"
+ " avoid \"Access Denied\" error.",
null);

uriString = "/";
}

SPLogger.Log(
LogCategory.WebParts,
TraceSeverity.Verbose,
"Redirecting authenticated user to '{0}'...",
uriString);

SPRedirectFlags trusted = SPRedirectFlags.Default;
if (((SPControl.GetContextWeb(this.Context) == null)
&& Uri.IsWellFormedUriString(uriString, UriKind.Absolute))
&& (SPWebApplication.Lookup(new Uri(uriString)) != null))
{
trusted = SPRedirectFlags.Trusted;
}

SPUtility.Redirect(uriString, trusted, this.Context);
}
}
}

Friday 30 September 2011

sharepoint inputformtextbox not working updatepanel

side updatepanel






Recently I faced a weird issue while using Richtext or InputFormTextBox control of SharePoint. Actually there were many fields in the custom form, so to avoid postback I decided to use UpdatePanel for partial page update. Unfortunately my richtext contol was inside UpdatePanel and started showing weird behavior after asynchronous postback.

Before postback richtext control
Before Postback Richtext Control


After postback richtext control

After Postback Richtext Control

Actually SharePoint calls few JavaScript methods to maintain the toolbar and look of richtext control on page load. But while partial page update browser don't trigger window onload event. That is why, JavaScript methods associated with richtext control don't get triggered and control looks completely different after asynchronous postback.

It means, somehow we have to call these method after asynchronous postback complete. To solve this problem you can use client side endRequest event of asynchronous postback to call required javaScript methods richtext control.
Sample Code:

<script type="text/javascript">

Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);

function EndRequestHandler()

{

var richtextContolId = '<%= richtextControlId.ClientID %>';

if (browseris.ie5up && browseris.win32 && !IsAccessibilityFeatureEnabled()){

g_aToolBarButtons = null;

g_fRTEFirstTimeGenerateCalled=true;

RTE_ConvertTextAreaToRichEdit(richtextContolId, true, true, "", "1033",null, null, null, null, null,"FullHtml", "",null,null,null,null);

RTE_TextAreaWindow_OnLoad(richtextContolId);

}

else{

document.write("
Click for help about adding basic HTML formatting.
"
);

};

}

script>


That's all you need to do to fix the above issue. After each asynchronous postback "EndRequestHandler" method will get called and it will maintain the state of richtext control. Go through the following blog to get good idea about client side events:http://www.a2zmenu.com/Blogs/Ajax/Call-JavaScript-method-after-Ajax-call-complete.aspx .


Working with multiple Inputformtexboxes :

Problem with sharepoint inputformtextbox control with updatepanel

When using SharePoint:InputFormTextBox control in UpdatePanel I faced one issue. On my webpart part page I had one SharePoint:InputFormTextBox control with one asp:DropDown control with autopostback property true. On SelectedIndexChanged event of that dropdown, InputformTextBox control appears without toolbar. This behaviour was coming because the InputformTextBox is a TextArea control. It needs script to achieve the rich text box feature when loading the page and in update panel due to partial postback the script was not loading. To fix this issue we are required to load that script. I am giving you the steps to fix this issue:
Step 1: Put this script function in design code.
<script language="javascript" type="text/javascript">
function CreateRichEdit(elementid)
{
if (browseris.ie5up && browseris.win32 && !IsAccessibilityFeatureEnabled()){
g_aToolBarButtons = null;
g_fRTEFirstTimeGenerateCalled=true;
RTE_ConvertTextAreaToRichEdit(elementid, true, true, "", "1033", null, null, null, null, null,"FullHtml","\u002f",null,null,null,null);
RTE_TextAreaWindow_OnLoad(elementid);
RTE_DocEditor_AdjustHeight(elementid);
RTE_DocEditor_AdjustWidth(elementid);
}
else{
};
}
script>


Step 2: Put this code in code behind file

protected void Page_PreRender(object sender, EventArgs e)
{
ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "@@CreateRichEdit", "", false);
}

Tuesday 13 September 2011

Custom new form validation on Save Ok button

Another undocumented piece of SharePoint.

I want to validate two fields on a new list item form by invoking JavaScript custom function. They are two date fields and I want to ensure that the end date can't happen before the start date. My first idea was to attach a validation function on the onclick event of the Submit button.

I started by inspecting the generated HTML of the form. The Submit button already has a onclick() code which is:

if (!PreSaveItem()_) return false;WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("ctl00$ctl13$g_740c1963_b0da_4b45_9b71_0dcca5d082b0$ctl00$toolBarTbl$RightRptControls$ctl00$ctl00$diidIOSaveItem", "", true, "", "", false, true))

Searching in the SharePoint JavaScript files in the LAYOUT folder, I found the definition of PreSaveItem function in FORMS.JS file. It simply invokes PreSaveAction function, if defined.

Finally, it was just a matter of inserting a custom function named PreSaveAction in a

Monday 12 September 2011

Classic ASP Read Excel with Microsoft.Jet.OLEDB.4.0

Reading Excel 2003 file using  Microsoft.Jet.OLEDB.4.0

In connection string
HDR=No - returns column headers  as row
HDR=Yes -  returns only rows without column headers
IMEX=1 - entire first row cells are converted as text



M-1
Dim conn,rs
set conn=Server.CreateObject("ADODB.Connection")
dim sFileConnectionString,sFileSQL, curValue
sFileConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;"&_
"Data Source=c:\mdalldownload.xls;Extended Properties=""Excel 8.0;HDR=No;IMEX=1;"";"
    sFileSQL =  "SELECT * FROM [mdalldownload$]"
            dim oFileCN, objRS, aSourceData
            set oFileCN = server.createobject("ADODB.Connection")
            oFileCN.Open sFileConnectionString
            set objRS = oFileCN.Execute(sFileSQL)
            Response.Write("<table border=""1"">")
Response.Write("<tr>")
For x=0 To objRS.Fields.Count-1
   'Response.Write("<th>" & objRS.Fields(x).Name & "</th>")
Next
Response.Write("</tr>")
o Until objRS.EOF
Response.Write("<tr>")
For x=0 To objRS.Fields.Count-1
curValue = objRS.Fields(x).Value
If IsNull(curValue) Then
curValue="N/A"
End If
curValue = CStr(curValue)
Response.Write("<td>" & curValue & "</td>")
Next
Response.Write("</tr>")
objRS.MoveNext
Loop
objRS.Close
Response.Write("</table>")

M-2
Function ReadExcel( myXlsFile, mySheet, my1stCell, myLastCell, blnHeader )
' Function :  ReadExcel
' Version  :  2.00
' This function reads data from an Excel sheet without using MS-Office
'
' Arguments:
' myXlsFile   [string]   The path and file name of the Excel file
' mySheet     [string]   The name of the worksheet used (e.g. "Sheet1")
' my1stCell   [string]   The index of the first cell to be read (e.g. "A1")
' myLastCell  [string]   The index of the last cell to be read (e.g. "D100")
' blnHeader   [boolean]  True if the first row in the sheet is a header
'
' Returns:
' The values read from the Excel sheet are returned in a two-dimensional
' array; the first dimension holds the columns, the second dimension holds
' the rows read from the Excel sheet.
'
' Written by Rob van der Woude
' http://www.robvanderwoude.com
    Dim arrData( ), i, j
    Dim objExcel, objRS
    Dim strHeader, strRange

    Const adOpenForwardOnly = 0
    Const adOpenKeyset      = 1
    Const adOpenDynamic     = 2
    Const adOpenStatic      = 3

    ' Define header parameter string for Excel object
    If blnHeader Then
        strHeader = "HDR=YES;"
    Else
        strHeader = "HDR=NO;"
    End If

    ' Open the object for the Excel file
    Set objExcel = CreateObject( "ADODB.Connection" )
    ' IMEX=1 includes cell content of any format; tip by Thomas Willig
    objExcel.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & _
                  myXlsFile & ";Extended Properties=""Excel 8.0;IMEX=1;" & _
                  strHeader & """"

    ' Open a recordset object for the sheet and range
    Set objRS = CreateObject( "ADODB.Recordset" )
    strRange = mySheet & "$" & my1stCell & ":" & myLastCell
    objRS.Open "Select * from [" & strRange & "]", objExcel, adOpenStatic

    ' Read the data from the Excel sheet
    i = 0
    Do Until objRS.EOF
        ' Stop reading when an empty row is encountered in the Excel sheet
        If IsNull( objRS.Fields(0).Value ) Or Trim( objRS.Fields(0).Value ) = "" Then Exit Do
        ' Add a new row to the output array
        ReDim Preserve arrData( objRS.Fields.Count - 1, i )
        ' Copy the Excel sheet's row values to the array "row"
        ' IsNull test credits: Adriaan Westra
        For j = 0 To objRS.Fields.Count - 1
            If IsNull( objRS.Fields(j).Value ) Then
                arrData( j, i ) = ""
            Else
                arrData( j, i ) = Trim( objRS.Fields(j).Value )
            End If
        Next
        ' Move to the next row
        objRS.MoveNext
        ' Increment the array "row" number
        i = i + 1
    Loop

    ' Close the file and release the objects
    objRS.Close
    objExcel.Close
    Set objRS    = Nothing
    Set objExcel = Nothing

    ' Return the results
    ReadExcel = arrData
End Function