Saturday, April 3, 2010

30+ SharePoint Custom Actions Location and Sample

Hi all,

It’s been a while. Today I posted a new blog at CustomWare site.

If you are interested in finding a complete reference for custom actions locations, group IDs and sample screenshots, please check out these links:

Tuesday, September 1, 2009

JIRA 4 Beta 3 and SharePoint

Today I installed and configured JIRA 4 Beta 3 in my local PC to check if the product developed by us works as expected with the new version of JIRA 4 Beta 3 API Web Service.

Installation and setting up of JIRA 4 Beta 3 doesn't trouble me much, then I proceed with testing of our product abd it works perfectly without the need for code changes.

So I investigated further the Web Service Definition Language (WSDL) for JIRA 4 Beta 3 and compared to previous version of JIRA 3.13.4 and 3.13.5. It turned out there are total of 8 new methods that have been added with this new version of JIRA 4 Beta 3 API Web Service, these includes:

  1. deleteProjectAvatar ( )
  2. getIssuesFromJqlSearch ( )
  3. getProjectAvatar ( )
  4. getProjectAvatars ( )
  5. getResolutionDateById ( )
  6. getResolutionDateByKey ( )
  7. setNewProjectAvatar ( )
  8. setProjectAvatar ( )

In  JIRA 3.13.4 and 3.13.5, there are total of 100 methods exposed by JIRA API Web Service - based on my initial investigation, none of existing methods are deprecated or updated in JIRA 4 Beta 3 (Don't count on this, let wait until Atlassian release a final documentation on JIRA API Web Service)

Tuesday, August 4, 2009

Implementing Mouse Hover & Click Highlighting in Default List View

Brad has a great post on how to Hover (MouseOver) Highlight and Click to Stay Highlighted in a SharePoint List and the BEST thing he did mentioned my name (“a guy called Sukri”) – Thanks Brad.

Note:

The entire solution step by step with code segment can be found at CustomWare site: SharePoint - Customising List Default View - Implementing mousehover & click highlighting in Default List View

Tuesday, July 21, 2009

June Cumulative Update for Microsoft Office SharePoint Server 2007 and Windows SharePoint Services 3.0

The server-packages of June Cumulative Update for Microsoft Office SharePoint Server 2007 and Windows SharePoint Services 3.0 are ready for download. See below:

Download Information

Windows SharePoint Services 3.0 June cumulative update package
http://support.microsoft.com/hotfix/KBHotfix.aspx?kbnum=971538

Office SharePoint Server 2007 June cumulative update package
http://support.microsoft.com/hotfix/KBHotfix.aspx?kbnum=971537

Detail Description

Description of the Windows SharePoint Services 3.0 June cumulative update package
http://support.microsoft.com/kb/971538

Description of the Office SharePoint Server 2007 June cumulative update package
http://support.microsoft.com/kb/971537

SharePoint Single Sign-On Error: “User DOMAIN\USERID failed to configure the single sign-on server. The error returned was ERROR NUMBER. Verify this account has sufficient permissions and try again”

When configure SharePoint Single Sign-On (SSO), sometimes you’ll get “You do not have the rights to perform this operation.” error message on the “Manage Settings for Single Sign-On” page and subsequently “User DOMAIN\USERID failed to configure the single sign-on server. The error returned was ERROR NUMBER. Verify this account has sufficient permissions and try again.” error message is displayed in the Windows Event Viewer and SharePoint Log.

To fix this issue, you have to make sure “Single Sign-On Administrator Account” account name to start the Microsoft Single Sign-On Service MUST meet all of the following:

  • Must be a domain user account. It cannot be a group account.
    • The user must be a member of Domain Admins and Domain Users

image

  • Must be an Office SharePoint Server farm account
    • Go to Central Administration > Operations, then click on the “Update farm administrator's group” link to add the user to farm administrator group.
  • Must be a member of the local Administrators group on the encryption-key server
    • The encryption-key server is the first server on which you start SSOSrv
  • Must be a member of the Security Administrators role and Database Creators role on the computer running Microsoft SQL Server.
    • Open SQL Management Studio, go to Security > Server Roles folder, add the user to securityadmin and dbcreator server roles

Also most important setting is to MAKE SURE, the same “Single Sign-On Administrator Account” account name is used at both “Manage Server Settings for Single Sign-On” page and “Service Accounts” page as shown below:

image

Saturday, July 18, 2009

Adding Drop-Down Menu Items to SPGridView Using SPMenuField Control

When displaying custom data through SPGridView you won’t get the same look and feel of the default SharePoint List View. SPMenuField provides a data bound drop-down menu to be used in place of BoundField, TemplateField, or similar controls.

The following describe steps required to add drop-down menu items to a SPGridView control in Web Part.

  • Create a Web Part
    • Add project references: Microsoft.SharePoint and System.Web
    • Add the following namespaces to your class:

using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Collections.Generic;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;

    • Inherits your class with Microsoft.SharePoint.WebPartPages.WebPart

public class SampleGrid : WebPart
{
}

  • Override CreateChildControls method, create the ObjectDataSource, SPGridView, SPGridViewPager and SPMenuField controls as shown below:

protected override void CreateChildControls()
{
    // Why UseDefaultStyles = false?
    // See http://sharepointmalaya.blogspot.com/2009/07/how-to-make-spgridview-to-have-same.html for details
    this.UseDefaultStyles = false;

    // ObjectDataSource
    ObjectDataSource dataSource = new ObjectDataSource();
    dataSource.ID = "dataSource";
    dataSource.SelectMethod = "SelectData";
    dataSource.TypeName = this.GetType().AssemblyQualifiedName;
    dataSource.ObjectCreating += new ObjectDataSourceObjectEventHandler(objectCreating);
    this.Controls.Add(dataSource);

    // SPGridView
    spGridView = new SPGridView();
    spGridView.ID = "spGridView";
    spGridView.DataSourceID = dataSource.ID;
    spGridView.AutoGenerateColumns = false;
    spGridView.AllowPaging = true;
    spGridView.PageSize = 10;
    spGridView.AllowSorting = true;
    spGridView.EmptyDataText = string.Empty;
    this.Controls.Add(spGridView);

    // SPGridViewPager
    pager = new SPGridViewPager();
    pager.GridViewId = spGridView.ID;
    this.Controls.Add(pager);

    // SPMenuField - Name field
    SPMenuField nameCol1 = new SPMenuField();
    nameCol1.HeaderText = "Name";
    nameCol1.TextFields = "Name";
    nameCol1.NavigateUrlFields = "Name,Email,WebSite";
    nameCol1.NavigateUrlFormat = "/_layouts/sharepointmalaya/DispDetails.aspx?WebPartID=" + this.ID + "&FilterName={0}&ReturnUrl=" + Page.Request.Url.ToString();
    nameCol1.TokenNameAndValueFields = "KEYNAME=Name,KEYEMAIL=Email,KEYWEBSITE=WebSite";
    nameCol1.SortExpression = "Name";
    nameCol1.MenuTemplateId = "menuTemplate";

    // MenuTemplate
    MenuTemplate menuTemplate = new MenuTemplate();
    menuTemplate.ID = "menuTemplate";
    this.Controls.Add(menuTemplate);
    spGridView.Columns.Add(nameCol1);

    // MenuTemplate - View Item
    MenuItemTemplate menuItemTemplate0 = new MenuItemTemplate("View Item", "~/_layouts/images/LIST.GIF");
    menuItemTemplate0.ClientOnClickNavigateUrl = "/_layouts/sharepointmalaya/DispDetails.aspx?WebPartID=" + this.ID + "&FilterName={%KEYNAME%}&ReturnUrl=" + Page.Request.Url.ToString();
    menuTemplate.Controls.Add(menuItemTemplate0);

    // MenuTemplate - Seperator
    MenuSeparatorTemplate menuSepTemplate = new MenuSeparatorTemplate();
    menuTemplate.Controls.Add(menuSepTemplate);

    // MenuTemplate - Open WebSite
    MenuItemTemplate menuItemTemplate1 = new MenuItemTemplate("Open WebSite", "~/_layouts/images/ASP16.GIF");
    menuItemTemplate1.ClientOnClickNavigateUrl = "javascript:window.open('%KEYWEBSITE%');";
    menuTemplate.Controls.Add(menuItemTemplate1);

    // MenuTemplate - Send Email
    MenuItemTemplate menuItemTemplate2 = new MenuItemTemplate("Send Email", "~/_layouts/images/EML16.GIF");
    menuItemTemplate2.ClientOnClickScript = "javascript:document.location.href='MailTo:%KEYEMAIL%;'";
    menuTemplate.Controls.Add(menuItemTemplate2);

    // BoundField - Email field
    BoundField nameCol2 = new BoundField();
    nameCol2.DataField = "Email";
    nameCol2.SortExpression = "Email";
    nameCol2.HeaderText = "Email";
    spGridView.Columns.Add(nameCol2);

    // BoundField - WebSite field
    BoundField nameCol3 = new BoundField();
    nameCol3.DataField = "WebSite";
    nameCol3.SortExpression = "WebSite";
    nameCol3.HeaderText = "WebSite";
    spGridView.Columns.Add(nameCol3);

    this.ChildControlsCreated = true;
}

  • Code above create three (3) drop-down menu items and a separator menu as shown below:

image

  • Clicking on the “View Item” redirects to custom page in the _layouts folder. When clicking on the “Open WebSite”, it opens web site and “Send Email” menu open client email form.
  • Please note SPMenuField has a property TokenNameAndValueFields which can be used to store token names and values in the following format:

SPMenuField.TokenNameAndValueFields = "KEYNAME=Name,KEYEMAIL=Email,KEYWEBSITE=WebSite";

  • To consume SPMenuField token values, you must specify token name within the % sign as shown below:

“%TOKEN_NAME%” OR “%KEYWEBSITE%”

  • As shown in code example above, ObjectDataSource control require “SelectData” method. You need to create a public method SelectData which returns a System.Data.DataTable – a dummy data which has three (3) columns which are “Name”, “Email” and “WebSite”. See below:

public DataTable SelectData()
{
    DataTable table = new DataTable();
    table.Columns.Add("Name");
    table.Columns.Add("Email");
    table.Columns.Add("WebSite");

    table.Rows.Add("SharePoint Malaya","sharepointmalaya@gmail.com","http://sharepointmalaya.blogspot.com");
    table.Rows.Add("Faris","faris@gmail.com","http://sharepointmalaya.blogspot.com");
    table.Rows.Add("Farhana","farhana@gmail.com","http://sharepointmalaya.blogspot.com");

    return table;
}

  • In addition, ObjectDataSource control implement event handler ObjectCreating – You also need to create instance containing the event data as shown below:

private void objectCreating(object sender, ObjectDataSourceEventArgs e)
{
    e.ObjectInstance = this;
}

  • Next, bind the data with the SPGridView control as shown below:

protected override void Render(HtmlTextWriter writer)
{
    if (ChildControlsCreated)
        spGridView.DataBind();
    base.Render(writer);
}

  • Build and deploy to SharePoint site. That’s it – All Done. See below for screenshot:

image

Get sample code here:

Friday, July 17, 2009

How to Make SPGridView To Have Same Look And Feel Of The Out Of The Box SharePoint List View

When you attempt to develop a Web Part that uses an SPGridView control, you’ll notice the following differences to your SPGridView look and feel compared to the out of the box SharePoint List View:

  • SPGridView font type is Verdana, out of the box SharePoint List View font type is Tahoma
  • Font size and colour different
  • No alternating styles for items
  • Header style, pager styles, etc.

Screenshot below shows the differences:

 image

If you’re good in CSS and HTML, then you’ll find CSS Reference Chart for SharePoint 2007 is helpful and you should be able to change your SPGridView styles to have the same look and feel of of the out of the box SharePoint List View.

Instead of doing above you can assign the WebPart.UseDefaultStyles of your Web Part to false. See code below:

protected override void CreateChildControls()
{
    this.UseDefaultStyles = false;

By setting the UseDefaultStyles to false, your SPGridView styles will have the same look and feel of the out of the box SharePoint List View. See screenshot below:

image

Please note that UseDefaultStyles only applicable for SharePoint Web Part and not ASP.NET Web Part – for ASP.NET Web Part please use SPChromeSettings.UseDefaultStyles.

Thursday, July 16, 2009

Validate User Base Permissions Before Uploading Document to SharePoint Document Library

If you are building a custom Web Part to upload document to SharePoint Document Library, then you need to validate user’s base permission so that unauthorised user can’t perform upload. You can’t validate based on their permission levels since at anytime base permissions of any permission level can be changed by administrator. Plus, in object model there is no specific method to get the permission level or what permission level assigned to a group or a user.

This article describes how to perform document upload to SharePoint and validate user base permissions so that only authorized users are able to perform the upload.

In SharePoint, the out-of-the-box permission level allowed user with "Contribute" permission level or higher (i.e. "Full Control", "Design", "Manage Hierarchy" and "Approve") to upload document to SharePoint. The following code displays the base permissions for each permission level:

SPSite oSite = new SPSite("http://examplesite");
SPWeb oWeb = oSite.OpenWeb();

SPRoleDefinitionBindingCollection usersRoles = oWeb.AllRolesForCurrentUser;
foreach (SPRoleDefinition roleDefinition in usersRoles)
    retVal += roleDefinition.BasePermissions.ToString() + " | ";

System.Diagnostics.Debug.WriteLine(retVal);

“Full Control” permission level:

  • FullMask
  • OR SPWeb.UserIsWebAdmin = TRUE

“Design” permission level:

  • ViewListItems | AddListItems | EditListItems | DeleteListItems | ApproveItems | OpenItems | ViewVersions | DeleteVersions | CancelCheckout | ManagePersonalViews | ManageLists | ViewFormPages | Open | ViewPages | AddAndCustomizePages | ApplyThemeAndBorder | ApplyStyleSheets | CreateSSCSite | BrowseDirectories | BrowseUserInfo | AddDelPrivateWebParts | UpdatePersonalWebParts | UseClientIntegration | UseRemoteAPIs | CreateAlerts | EditMyUserInfo

“Manage Hierarchy” permission level:

  • ViewListItems | AddListItems | EditListItems | DeleteListItems | OpenItems | ViewVersions | DeleteVersions | CancelCheckout | ManagePersonalViews | ManageLists | ViewFormPages | Open | ViewPages | AddAndCustomizePages | ViewUsageData | CreateSSCSite | ManageSubwebs | ManagePermissions | BrowseDirectories | BrowseUserInfo | AddDelPrivateWebParts | UpdatePersonalWebParts | ManageWeb | UseClientIntegration | UseRemoteAPIs | ManageAlerts | CreateAlerts | EditMyUserInfo | EnumeratePermissions
    OR SPWeb.UserIsWebAdmin = TRUE

“Approve” permission level:

  • ViewListItems | AddListItems | EditListItems | DeleteListItems | ApproveItems | OpenItems | ViewVersions | DeleteVersions | CancelCheckout | ManagePersonalViews | ViewFormPages | Open | ViewPages | CreateSSCSite | BrowseDirectories | BrowseUserInfo | AddDelPrivateWebParts | UpdatePersonalWebParts | UseClientIntegration | UseRemoteAPIs | CreateAlerts | EditMyUserInfo

“Contribute” permission level:

  • ViewListItems | AddListItems | EditListItems | DeleteListItems | OpenItems | ViewVersions | DeleteVersions | ManagePersonalViews | ViewFormPages | Open | ViewPages | CreateSSCSite | BrowseDirectories | BrowseUserInfo | AddDelPrivateWebParts | UpdatePersonalWebParts | UseClientIntegration | UseRemoteAPIs | CreateAlerts | EditMyUserInfo

“Read” permission level:

  • ViewListItems | OpenItems | ViewVersions | ViewFormPages | Open | ViewPages | CreateSSCSite | BrowseUserInfo | UseClientIntegration | UseRemoteAPIs | CreateAlerts

and for "Site Collection Administrator" user, base permission as follows:

  • FullMask
  • OR SPWeb.UserIsSiteAdmin= TRUE

To validate whether user access rights to upload document to SharePoint, the following conditions shall be used:

  • SPWeb.UserIsSiteAdmin = TRUE OR
  • SPWeb.UserIsWebAdmin = TRUE OR
  • AddListItems is exist OR
  • EditListItems is exist OR
  • ApproveItems is exist OR

See code below for details:

public static void IsUserBasePermissionValidToUpload(SPWeb oWeb)
{
    try
    {
        if (oWeb.Exists)
        {
            // If user is site collection administrator or admin
            if (oWeb.UserIsWebAdmin || oWeb.UserIsSiteAdmin)
                return;

            // Get roles for current user
            SPRoleDefinitionBindingCollection usersRoles = oWeb.AllRolesForCurrentUser;
            // Validate if user has rights to upload document
            foreach (SPRoleDefinition roleDefinition in usersRoles)
            {
                if (roleDefinition.BasePermissions.ToString().Contains(SPBasePermissions.FullMask.ToString())
                    || roleDefinition.BasePermissions.ToString().Contains(SPBasePermissions.AddListItems.ToString())
                    || roleDefinition.BasePermissions.ToString().Contains(SPBasePermissions.EditListItems.ToString())
                    || roleDefinition.BasePermissions.ToString().Contains(SPBasePermissions.ApproveItems.ToString()))
                    return;
            }

            // If user has invalid rights, then throw exceptions
            throw new Exception("Unauthorised to upload document to SharePoint Document Library. " +
                "You are currently signed in as: " + oWeb.CurrentUser.LoginName);
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

SPUtility.Redirect – Redirect Pages at _layouts folder, the Safest Way

When you need to redirect to pages located at _layouts folder, the safest way is to use SPUtility.Redirect method. The following are sample to redirect to settings.aspx page:

SPUtility.Redirect("settings.aspx", SPRedirectFlags.UseSource | SPRedirectFlags.RelativeToLayoutsPage, HttpContext.Context);

If you want to redirect to custom pages at _layout folder, see below:

SPUtility.Redirect("sharepointmalaya/customadminsettings.aspx", SPRedirectFlags.Static | SPRedirectFlags.RelativeToLayoutsPage | SPRedirectFlags.RelativeToLocalizedLayoutsPage, HttpContext.Current);

Details on the SPRedirectFlags enumerations can be found here.

Wednesday, July 15, 2009

How To Check If MOSS Or WSS Is Installed

There are many approaches to check if MOSS or WSS is installed at specific installation site. One of the approach is to check the Windows Registry key.

If MOSS is installed, there will be registry entries in the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office Server\12.0

image

If WSS is installed, there will be registry entries in the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\12.0

image

Please note that if MOSS is installed both registry entries as shown above will be exist at installation sites.

I’ve created a helper class SharePointConfig.cs which exposes method and properties which are useful to those who are interested use this approach. See code below:

public class SharePointConfig
{
    private const string MOSS_REGISTRY_PATH = @"SOFTWARE\Microsoft\Office Server\12.0";
    private const string MOSS_BUILD_VERSION = "BuildVersion";
    private const string MOSS_TEMPLATE_PATH = "TemplatePath";

    private const string WSS_REGISTRY_PATH = @"SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\12.0";
    private const string WSS_BUILD_VERSION = "BuildVersion";
    private const string WSS_TEMPLATE_PATH = "TemplatePath";
    private bool _ismossinstalled;
    private bool _iswssinstalled;
    private string _buildversion;
    private string _templatepath;

    /// <summary>
    /// Initializes a new instance of the <see cref="SharePointConfig"/> class.
    /// </summary>
    public SharePointConfig()
    {
        GetSharePointConfig();
    }

    /// <summary>
    /// Gets a value indicating whether this instance is MOSS installed.
    /// </summary>
    /// <value>
    ///     <c>true</c> if this instance is MOSS installed; otherwise, <c>false</c>.
    /// </value>
    public bool IsMOSSInstalled
    {
        get { return _ismossinstalled; }
    }

    /// <summary>
    /// Gets a value indicating whether this instance is WSS installed.
    /// </summary>
    /// <value>
    ///     <c>true</c> if this instance is WSS installed; otherwise, <c>false</c>.
    /// </value>
    public bool IsWSSInstalled
    {
        get { return _iswssinstalled; }
    }

    /// <summary>
    /// Gets the build version.
    /// </summary>
    /// <value>The build version.</value>
    public string BuildVersion
    {
        get { return _buildversion; }
    }

    /// <summary>
    /// Gets the template path.
    /// </summary>
    /// <value>The template path.</value>
    public string TemplatePath
    {
        get { return _templatepath; }
    }

    /// <summary>
    /// Loads the share point config.
    /// </summary>
    public void GetSharePointConfig()
    {
        try
        {
            // Assume current user has full rights to registry settings
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                // Get MOSS registry key
                RegistryKey theRegistry = Registry.LocalMachine.OpenSubKey(MOSS_REGISTRY_PATH);

                if (theRegistry != null)
                {
                    // Get registry values
                    _buildversion = theRegistry.GetValue(MOSS_BUILD_VERSION).ToString();
                    _templatepath = theRegistry.GetValue(MOSS_TEMPLATE_PATH).ToString();
                    _ismossinstalled = MossOrWSSIsFound();
                }
                else
                {
                    // Get WSS registry key
                    theRegistry = Registry.LocalMachine.OpenSubKey(WSS_REGISTRY_PATH);

                    if (theRegistry != null)
                    {
                        // Get registry values
                        _buildversion = theRegistry.GetValue(WSS_BUILD_VERSION).ToString();
                        _templatepath = theRegistry.GetValue(WSS_TEMPLATE_PATH).ToString();
                        _iswssinstalled = MossOrWSSIsFound();
                    }
                }

            });
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    /// <summary>
    /// Mosses the or WSS is found.
    /// </summary>
    /// <returns></returns>
    private bool MossOrWSSIsFound()
    {
        bool retVal = false;
        try
        {
            if (!string.IsNullOrEmpty(_buildversion) && !string.IsNullOrEmpty(_templatepath))
            {
                Version buildVersion = new Version(_buildversion);
                if (buildVersion.Major == 12)
                    retVal = true;
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
        return retVal;
    }
}

The following tables list the members exposed by the SharePointMalaya.SSOHelper.SSOConfiguration type.

Public method SharePointConfig - Initializes a new instance of the SharePointConfig class, get registry key for either MOSS or WSS.

Public method IsMOSSInstalled - Gets a value indicating whether MOSS instance is installed.

Public method IsWSSInstalled - Gets a value indicating whether WSS instance is installed.

Public method BuildVersion - Gets either MOSS or WSS build version.

Public method TemplatePath - Gets either MOSS or WSS template path.

Get source code here:

Sample Web Part

image

Tuesday, July 14, 2009

“Open tool pane” link in Web Part for SharePoint

If you want to display “Open tool pane” link in Web Part for SharePoint, just like the one displayed in the Content Editor Web Part and several others out-of-the-box Web Parts. Then add the following JavaScript code to your Web Part class:

lblErrorMessage.text = "<a href=\"javascript:MSOTlPn_ShowToolPane2Wrapper('Edit', this,'" + this.ID + "')\">Open tool pane</a>"

Get source code here:

Screenshots

  • Figure 1: Display “Open tool pane” link in the Web Part content

image

  • Figure 2: User clicked on the “Open tool pane” link

image

Monday, June 22, 2009

SharePoint Designer 2007 is NOW a FREE Download

A great tool and very happy now it is free, heaps of THANKS Microsoft.

Check out this video :)

Sunday, June 7, 2009

Retrieve Username and Password from SharePoint Single Sign-on (SSO) Provider Application

I’ve created a helper component SharePointMalaya.SSOHelper.SSOConfiguration that helped to provide the specific methods to retrieve username and password from the given SharePoint SSO provider application name.

The SharePointMalaya.SSOHelper.SSOConfiguration source code details are shown below:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Web;

using Microsoft.SharePoint;
using MSSO = Microsoft.SharePoint.Portal.SingleSignon;

namespace SharePointMalaya.SSOHelper
{
    /// <summary>
    ///
    /// </summary>
    public class SSOConfiguration
    {
        #region Member Variables

        private string _username;
        private string _password;
        private bool _isexistssoprovider;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="SSOConfiguration"/> class.
        /// </summary>
        /// <param name="ssoApplicationName">Name of the sso application.</param>
        public SSOConfiguration(string ssoApplicationName)
        {
            try
            {
                ConnectToSSO(ssoApplicationName);
            }
            catch (Exception ex)
            {
                throw ex;
            }           
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the username.
        /// </summary>
        /// <value>The username.</value>
        public string Username
        {
            get { return _username; }
        }

        /// <summary>
        /// Gets the password.
        /// </summary>
        /// <value>The password.</value>
        public string Password
        {
            get { return _password; }
        }

        /// <summary>
        /// Gets a value indicating whether SSO provider is exist.
        /// </summary>
        /// <value>
        ///     <c>true</c> if this instance is exist SSO provider; otherwise, <c>false</c>.
        /// </value>
        public bool IsExistSSOProvider
        {
            get { return _isexistssoprovider; }
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Connects to SSO.
        /// </summary>
        /// <param name="ssoApplicationName">Name of the sso application.</param>
        private void ConnectToSSO(string ssoApplicationName)
        {
            try
            {
                MSSO.ISsoProvider provider = MSSO.SsoProviderFactory.GetSsoProvider();

                if (provider != null)
                {
                    MSSO.SsoCredentials creds = provider.GetCredentials(ssoApplicationName);

                    IntPtr pUserName = IntPtr.Zero;
                    IntPtr pPassword = IntPtr.Zero;

                    try
                    {
                        // Get the non-secure string version of the credentials 
                        pUserName = Marshal.SecureStringToBSTR(creds.UserName);
                        _username = Marshal.PtrToStringBSTR(pUserName);

                        pPassword = Marshal.SecureStringToBSTR(creds.Evidence[1]);
                        _password = Marshal.PtrToStringBSTR(pPassword);

                        _isexistssoprovider = true;
                    }
                    finally
                    {
                        //Zero out and free the BSTR pointers
                        if (IntPtr.Zero != pUserName)
                        {
                            Marshal.ZeroFreeBSTR(pUserName);
                        }

                        if (IntPtr.Zero != pPassword)
                        {
                            Marshal.ZeroFreeBSTR(pPassword);
                        }
                    }
                }
            }
            catch (MSSO.SingleSignonException ssoEx)
            {
                throw ssoEx;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        #endregion
    }
}

The following tables list the members exposed by the SharePointMalaya.SSOHelper.SSOConfiguration type.

Public method SSOConfiguration - Initializes a new instance of the SSOConfiguration class, connects to SSO provider and get the username and password.

Public method Username - Gets the username from SSO provider.

Public method Password - Gets the password from SSO provider.

Public method IsExistSSOProvider - Gets a value indicating whether SSO provider is exist.

Example

The following is code sample on how to connect to the SSO provider and retrieve stored SSO username and password:

if (!string.IsNullOrEmpty(txtSSOApplicationName.Text))
{
    // Instantiate SSOConfiguration and connect to SharePoint SSO provider by passing SSO Application Name
    SSOConfiguration objSSOConfiguration = new SSOConfiguration(txtSSOApplicationName.Text);

    // Check if SSO provider is found
    if (objSSOConfiguration.IsExistSSOProvider)
    {
        // Assign stored SSO Username and Password to controls
        lblSSOUsername.Text = objSSOConfiguration.Username;
        lblSSOPassword.Text = objSSOConfiguration.Password;
    }
}

The following are screenshot showing a user logon to SharePoint site and retrieve his/her username and password from “SSO_Provider1” SSO Provider application name.

SSO1

Get source code here:

Sunday, May 3, 2009

Installing Custom Database as a SharePoint Feature

In any SharePoint project, sometimes there is a need to create an external custom RDBMS database to host non-related SharePoint information outside SharePoint's Content Database for example user credentials for form-based authentication, look up data, etc. To do this, the obvious solution is to use either one of following techniques:

  • provide customer with .SQL file and installation steps
  • provide customer with .MSI file and installation steps

None of the above technique is wrong!

However, I found technique no. 1 (install custom RDBMS database using .SQL file) is time-consuming and more prone to error - especially if customer who doing installation is not technical at all or the .SQL file doesn't obey the rules. For technique no. 2 - you need strong knowledge in Microsoft Windows Installer packaging tools and you need to download/configure MSI Redistributable package if you don't have one. So, the best solution is to utilize the SharePoint Feature Framework.

Features are the backbone of SharePoint development because every custom development project can and really should be deployed as a feature. Features give tremendous control over SharePoint configurations and capabilities at the administrator level. This means that developers can create features and then turn them over to SharePoint administrators without having to get involved repeatedly in small configuration changes.

I have developed a REUSEABLE FEATURE(SharePointMalaya.Feature.CustomDBInstaller.dll) that able to automate the installation of custom RDBMS database to any SharePoint’s database server. All you need to do is to generate SQL scripts using MS Server Management Studio, save the SQL scripts to SharePointMalaya.Feature.CustomDBInstaller feature folder, build WSP, install and activate the feature at SharePoint site.

SharePointMalaya.Feature.CustomDBInstaller feature comprises of SharePoint Event Handler which inherits from base abstract class SPFeatureReceiver to trap the activation, deactivation, installation, or uninstallation of a Feature. See codes below:

private const string FEATURE_FOLDER_PATH = "TEMPLATE\\FEATURES\\SharePointMalaya.Feature.CustomDBInstaller\\";
private const string TSQL_SCRIPT_INSTALLDB_FOLDER_PATH = FEATURE_FOLDER_PATH + "T-SQL\\Install\\";
private const string TSQL_SCRIPT_UNINSTALLDB_FOLDER_PATH = FEATURE_FOLDER_PATH + "T-SQL\\UnInstall\\";

/// <summary>
/// Occurs after a Feature is activated.
/// </summary>
/// <param name="properties">An <see cref="T:Microsoft.SharePoint.SPFeatureReceiverProperties"></see> object that represents the properties of the event.</param>
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    try
    {
        // Run with an account with higher privileges than the current user
        SPSecurity.RunWithElevatedPrivileges(delegate()
        {
            using (SPSite oSite = properties.Feature.Parent as SPSite)
            {
                SPWeb oWeb = null;

                if (oSite != null)
                    oWeb = oSite.RootWeb;
                else
                    oWeb = properties.Feature.Parent as SPWeb;

                if (oWeb.Exists)
                {
                    // Turn off security validation
                    oWeb.AllowUnsafeUpdates = true;

                    // Execute T-SQL scripts from <<FEATUREFOLDER>>\T_SQL\Install folder
                    ExecuteSQLScripts(oSite, TSQL_SCRIPT_INSTALLDB_FOLDER_PATH);

                    // Turn on security validation
                    oWeb.AllowUnsafeUpdates = false;
                }
                else
                    throw new Exception("Unable to open site.");
            }
        });
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

/// <summary>
/// Occurs when a Feature is deactivated.
/// </summary>
/// <param name="properties">An <see cref="T:Microsoft.SharePoint.SPFeatureReceiverProperties"></see> object that represents the properties of the event.</param>
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
    try
    {
        // Run with an account with higher privileges than the current user
        SPSecurity.RunWithElevatedPrivileges(delegate()
        {
            using (SPSite oSite = properties.Feature.Parent as SPSite)
            {
                SPWeb oWeb = null;

                if (oSite != null)
                    oWeb = oSite.RootWeb;
                else
                    oWeb = properties.Feature.Parent as SPWeb;

                if (oWeb.Exists)
                {
                    // Turn off security validation
                    oWeb.AllowUnsafeUpdates = true;

                    // Execute T-SQL scripts to uninstall from <<FEATUREFOLDER>>\T_SQL\UnInstall folder
                    ExecuteSQLScripts(oSite, TSQL_SCRIPT_UNINSTALLDB_FOLDER_PATH);

                    // Turn on security validation
                    oWeb.AllowUnsafeUpdates = false;
                }
                else
                    throw new Exception("Unable to open site.");
            }
        });
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

/// <summary>
/// Executes the SQL scripts.
/// </summary>
/// <param name="oSite">The o site.</param>
/// <param name="scriptsPath">The scripts path.</param>
private void ExecuteSQLScripts(SPSite oSite, string scriptsPath)
{
    SqlConnection objCon = null;
    SqlCommand objCmd = null;

    try
    {
        // Get SharePoint content database connection string
        string connectionString = oSite.ContentDatabase.DatabaseConnectionString;

        // Get T-SQL script files for installing database from Feature folder
        string[] fileEntries = Directory.GetFiles(SPUtility.GetGenericSetupPath(scriptsPath));

        // files exist then open connection to database and sort file by name
        if (fileEntries.Length > 0)
        {
            // Create and open connection object
            objCon = new SqlConnection(connectionString);
            objCon.Open();

            // Sort file name
            Array.Sort(fileEntries);

            // Loop for each files in directory
            foreach (string fileName in fileEntries)
            {
                // Proceed if file name is not empty and file extension is .SQL
                if (!string.IsNullOrEmpty(fileName) && fileName.EndsWith(".sql"))
                {
                    string tsqlScript = string.Empty;
                    using (StreamReader reader = new StreamReader(fileName))
                    {
                        // Reading all lines in file and parse T-SQL
                        tsqlScript = PrepareTSQL(reader.ReadToEnd());

                        // Proceed if content of the file is not empty
                        if (!string.IsNullOrEmpty(tsqlScript))
                        {
                            // Create command object
                            objCmd = new SqlCommand(tsqlScript, objCon);
                            objCmd.CommandType = CommandType.Text;

                            // Execute non query
                            objCmd.ExecuteNonQuery();
                        }
                    }
                }
            }
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
    finally
    {
        if (objCmd != null) objCmd.Dispose();
        if (objCon.State == ConnectionState.Open) objCon.Close();
        if (objCon != null) objCon.Dispose();
    }
}

/// <summary>
/// Prepares the TSQL.
/// </summary>
/// <param name="tsqlScript">The TSQL script.</param>
/// <returns></returns>
private string PrepareTSQL(string tsqlScript)
{
    string retVal = string.Empty;
    try
    {
        // Replace "GO" statement with empty string since GO is not a valid command in ADO.NET
        retVal = tsqlScript.Replace("GO", string.Empty);
    }
    catch (Exception) { }

    return retVal;
}

Get source code here:

The following are four (4) simple steps to update the SQL scripts to SharePointMalaya.Feature.CustomDBInstaller feature folder, build WSP, install and activate the feature:

Step 1: Generate SQL Scripts using MS Server Management Studio

Step 2: Drop the SQL Scripts to either 'Install' or 'UnInstall' folder

  • For SQL Scripts that install database objects, add script files to \12\TEMPLATE\FEATURES\ Customware.CustomDBInstaller\T-SQL\Install feature folder
  • For SQL Scripts that uninstall database objects, add script files \12\TEMPLATE\FEATURES\ Customware.CustomDBInstaller\T-SQL\UnInstall feature folder
  • Use <<SEQUENCENUMBER>>-<<FILENAME>>.sql as script filename to control the execution of SQL Scripts, see below:

Feature2

Step 3: Build WSP

  • Right- click on the Project, select WSPBuilder > Build WSP from the menu (if you don't have WSPBuilder, download and install from CodePlex)

Step 4: Install and Activate SharePoint Feature

  • Install the .WSP file to SharePoint site, at command prompt execute the following commands:
    • stsadm -o addsolution -filename SharePointMalaya.Feature.CustomDBInstaller.wsp
    • stsadm -o deploysolution -name SharePointMalaya.Feature.CustomDBInstaller.wsp -url http://<<SHAREPOINTURL>> -local –allowgacdeployment
  • Go to SharePoint Site Collection Features page (i.e. http://<<SHAREPOINTURL>>:<<PORTNUMBER/_layouts/ManageFeatures.aspx?Scope=Site), activate the SharePointMalaya.Feature.CustomDBInstaller feature:

Feature1

Friday, April 24, 2009

Trigger JavaScript Events When a Choice (Radio Button) SharePoint Field Type is Clicked

Brad has provided a workaround on how to use a JavaScript technique to manipulate SharePoint Form Field using SharePoint Designer 2007. The code in his blog supported the following SharePoint Field Types, but NOT Choice (Radio Buttons) field type:

  • Single Line of Text
  • Multiple Lines of Text
  • Number
  • Currency
  • Choice (dropdown)
  • Lookup (single)
  • Lookup (multiple)
  • Yes/No

Let say you have a requirement from a customer to develop a SharePoint list for storing user credentials which has the following UI mock-up:

UserCredential1

The requirement stated that when ‘Anonymous’ authentication mode radio button is selected, hide Username and Password textbox else display Username and Password textbox.

The following are steps required to add JavaScript to the SharePoint Form List page (NewForm.aspx and EditForm.aspx):

  • Open NewForm.aspx using Microsoft SharePoint Designer 2007 (i.e. http://<<sharepoint_servername:port_number/Lists/User Credential/NewForm.aspx)
  • Locate the <asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server"> tag and paste the following JavaScript just below the tag:

<script language="javascript" type="text/javascript">

    _spBodyOnLoadFunctionNames.push("selectedAuthenticationMode");

    function selectedAuthenticationMode() {
        // to get the array for radio buttons
        var myRadioButtonsArray = getTagFromIdentifierAndTitle("input", "RadioButtons", "Authentication_x0020_Mode");
        for (var x = 0; x < myRadioButtonsArray.length; x++) {
            if (myRadioButtonsArray[x].value == 'ctl00') // per-user static authentication mode
            {
                // create client event handler - onclick
                myRadioButtonsArray[x].parentElement.onclick = function() {
                    // display username and password textbox                   
                    var controlUserName = findacontrol("Username");
                    controlUserName.parentNode.parentNode.style.display = "";
                    var controlPassword = findacontrol("Password");
                    controlPassword.parentNode.parentNode.style.display = "";
                };
            }
            else // anonymous authentication mode
            {
                // create client event handler - onclick
                myRadioButtonsArray[x].parentElement.onclick = function() {
                    // unhide username and password field                   
                    var controlUserName = findacontrol("Username");
                    controlUserName.parentNode.parentNode.style.display = "none";
                    var controlPassword = findacontrol("Password");
                    controlPassword.parentNode.parentNode.style.display = "none";
                };
            }
        }
    }

    function getTagFromIdentifierAndTitle(tagName, identifier, title, option) {
        var len = identifier.length;
        var tags = document.getElementsByTagName(tagName);
        for (var i = 0; i < tags.length; i++) {
            var idString = tags[i].id;
            var nameString = tags[i].name;
            // get selected radio button value only
            if (option == "value" && tags[i].type == "radio" && (identifier == "RadioButtons" && nameString.indexOf(identifier) == nameString.length - len)) {
                var tagParentHTML = tags[i].parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.innerHTML;
                if (tagParentHTML.indexOf('FieldInternalName="' + title + '"') > -1) {
                    var radioButtons = document.getElementsByName(nameString);
                    var radioValue = "";
                    for (var x = 0; x < radioButtons.length; x++) {
                        if (radioButtons[x].checked) {
                            radioValue = radioButtons[x].parentElement.title;
                            break;
                        }
                    }
                    var o = document.createElement("INPUT");
                    o.type = "hidden";
                    o.value = radioValue;
                    return o;
                }
            }
            // get radio buttons group
            if (tags[i].type == "radio" && (identifier == "RadioButtons" && nameString.indexOf(identifier) == nameString.length - len)) {
                var tagParentHTML = tags[i].parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.innerHTML;
                if (tagParentHTML.indexOf('FieldInternalName="' + title + '"') > -1) {
                    return document.getElementsByName(nameString);
                }
            }
            // all other input or select type
            else if (tags[i].title == title && (identifier == "" || idString.indexOf(identifier) == idString.length - len)) {
                return tags[i];
            }
        }
        return null;
    }

    function findacontrol(FieldName) {
        var arr = document.getElementsByTagName("!");
        // get all comments
        for (var i = 0; i < arr.length; i++) {
            // now match the field name
            if (arr[i].innerHTML.indexOf('FieldInternalName=\"' + FieldName + '\"') > -1) {
                return arr[i];
            }
        }
    }
</script>

  • Save NewForm.aspx and preview in the Browser
  • Repeat the steps for EditForm.aspx

Instead of having to create a new custom Web Part or a XSLT List Form page for a SharePoint List, this approach provides uncomplicated customization and the best thing is the out-of-the-box functionalities of the List Form View for a SharePoint List are still intact.

Sample Screenshot

  • User chooses ‘Anonymous’ authentication mode radio button:

UserCredential2

  • User chooses ‘Per-User Static’ authentication mode radio button:

UserCredential1