Tuesday, August 9, 2011

MOSS - Custom error page

In MOSS 2007, how will you display a custom error page?
By using the simple ASP.net way of setting <custom errors="On" defaultRedirect="errorURL"/> won't work in sharepoint

You basically need to write a custom HTTPModule and add the entries in the web.config of your webapplication.

Here is a sample for your reference.

public class MyError : IHttpModule
        {
            const string errorUrl = "/_layouts/CustomErrorPage/Error.aspx";

            public void Dispose()
            {
                //dispose your objects
            }

            public void Init(HttpApplication context)
            {
                context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);

            }
            void context_PreRequestHandlerExecute(object sender, EventArgs e)
            {
                Page page = HttpContext.Current.CurrentHandler as Page;
                if (page != null)
                {

                    page.Error += new EventHandler(page_Error);
                }
            }


            void page_Error(object sender, EventArgs e)
            {
                Page page = sender as Page;
               
                    string strWebURL = SPContext.Current.Web.Url;
                    HttpContext.Current.Response.Write("<script type=\"text/javascript\">window.location = \"" + errorUrl + "\"</script>");
                    HttpContext.Current.Response.Flush();
               
            }
        }

Hope you all know how to deploy it. If you need steps on how to deploy, please feel free to ask.


Happy Coding :)


Saturday, August 6, 2011

SPFile.Versions.Count & SPListItem.Versions.Count

There is always a confusion between SPFile.Versions.Count & SPListItem.Versions.Count, as to which one to use and what is its purpose.
  

SPListItem.Versions.Count is always 1 more than SPFile.Versions.Count. The reason is SPFile consider the latest version as the current version and therefore does not include this in its version collection.

Thus, SPListItem.Versions.Count[1]  is equal to SPListItem.SPFile.Versions.Count[0].

if you go to  version history of a document , you will get a count equal to SPListItem.Versions.Count

Thus SPListItem.Versions.Count will give you, an exact version count of a document.


Hide the “People and Group” section from breadcrumb in userdisp.aspx

If you want to hide any breadcrumb in sharepoint application pages, Then you are at the right spot. This can be done via javascript.

But Microsoft will not support/recommend modifying the OOTB application pages. 
So we can tweak the application page with a word of caution. It is entirely upto you to decide whether to follow it or not.

Here is the code for reference.

I'm hiding the "People and Group” in userdisp.aspx for those users from a different domain.

<input type="hidden" value="<%=Request.ServerVariables["Auth_User"]%>" id="user_name">

<script language="javascript">
var username=document.getElementById("user_name").value;
     alert(username);
   var domainname =username.substring(0,username.indexOf("\\"));
    alert(domainname.toLowerCase());

if(domainname.toLowerCase()=="Domain_name")//Please give Domain_name in lower case
{
var child=document.getElementById("ctl00_PlaceHolderTitleBreadcrumb_ContentMap").childNodes;

for(i=0;i<child.length;i++)
{
 if(child[i].hasChildNodes);
{
    var spantag=child[i].childNodes;
   
    for(j=0;j<spantag.length;j++)
    {
        if(spantag[j].tagName=="A")
        {
         var anchtag=spantag[j];
         hrefattrb=anchtag.getAttribute("href");
        
         if(hrefattrb.indexOf("people.aspx")!=-1)
         {
         anchtag.style.display="none";
         child[i].style.display="none";
         child[i+1].style.display="none";
         }
        
        }
    }
}
}
}
<script>

How to change rows to columns in SQL


There are two ways(as per my knowledge) to change rows to columns in SQL

1.Pivot statements
2.Case statements

Case statements
 
If the column names are static, it is quite easy to write.i.e For example say months in a year..
we are completely aware of those colmns names it could only be between jan-dec. In this case we can go ahead and use "Case" directly.

In the below snippet I'm changing one column values to column headers depending upon the month and year.

Select [year],
sum(case when [month]='Jan' then 1 else 0) as 'Jan',
sum(case when [month]='Feb' then 1 else 0) as 'Feb',
sum(case when [month]='Mar' then 1 else 0) as 'Mar',
sum(case when [month]='Apr' then 1 else 0) as 'Apr',
sum(case when [month]='May' then 1 else 0) as 'May',
sum(case when [month]='Jun' then 1 else 0) as 'Jun',
sum(case when [month]='Jul' then 1 else 0) as 'Jul',
sum(case when [month]='Aug' then 1 else 0) as 'Aug',
sum(case when [month]='Sep' then 1 else 0) as 'Sep',
sum(case when [month]='Oct' then 1 else 0) as 'Oct',
sum(case when [month]='Nov' then 1 else 0) as 'Nov',
sum(case when [month]='Dec' then 1 else 0) as 'Dec'        
from table_name
group by [year] 


If the column names are dynamic, we can the same logic above using cursors.
But please do not cursors until there is no other way.

declare @strSQL varchar(4000)
set @strSQL='select [Column_To_group
] '
Declare @name varchar(100)
DECLARE descrcursor CURSOR FOR
 
select dept_name from dbo.tblDepartment /* I have the column names in a table*/


OPEN descrcursor

FETCH NEXT FROM descrcursor
INTO @name
WHILE @@FETCH_STATUS = 0
BEGIN
set @strSQL= @strSQL + ' ,sum(case when department='''+replace(@name,'''','''''')+''' then 1 else 0 end ) as ['+@name+']'
FETCH NEXT FROM descrcursor
INTO @name
End
CLOSE descrcursor
DEALLOCATE descrcursor

set @strSQL = @strSQL + ' from table_name
group by [Column_To_group] '
print @strSQL
exec (@strSQL)

PIVOT

The other way is through using PIVOT.
I'm working on this sample, will update it soon.

Sharepoint Document library - Access denied while reading draft

In your custom code, you might have written a logic to read all documents from a document library. Sometimes you might get access denied for some files in library.

The reason behind this is the version setting of the document library, especially when you have "major & minor" versioning setting enabled. When you track major and minor versions, the major versions are whole numbers, and the minor versions are decimals. Minor versions are the draft versions, major versions are the published ones.
For example, 0.1 is the first minor version of a file, 1.4 is the fourth minor version of a file that was published once, and 3.0 is the third major version of a published file.


The version is stored by default is a minor version. When you save a file and close it, the version is tracked as a minor version. Minor versions are considered drafts that are still being developed.
You must first publish the file in order for it to become a major version. You can publish the file by using drop-down commands in a library

In your API, if lock a sitecollection and then try to read your draft document, obviously you will get "access denied", meaning, the content DB is in read only mode when the site collection is locked


The reason behind this was that, documents were never published [they were in draft mode only]. So documents have to be published with at least one major version to prevent this error.

Sharepoint 2010 Claims site - Add FBA users to Sharepoint group programmatically

Adding FBA users to Sharepoint group programmatically is simple. The hunch is the format "i:0#.f|fbamembershipprovider|" + username

In the format, 
i  - stands for Identity provider 
f  - stands for fba authentication
fbamembershipprovider  - is my providername

Here you go, a simple sample that servers the pupose.

SPSite sitename = new SPSite("SiteURL");
        SPWeb spWeb = sitename.OpenWeb();
        
        String strusername= "i:0#.f|fbamembershipprovider|" + UsernameTextbox.Text;
        spWeb.AllUsers.Add[strusername, EmailTextbox.Text, UsernameTextbox.Text, strusername];
       
         
            if (spUser != null)
            {
                SPGroup spGroup = spWeb.Groups["GROUP_NAME"];
            if (spGroup != null)
            spGroup.AddUser(spUser);
            }


Happy Coding :)

Value does not fall within the expected range - Edit aspx in MOSS 2007

When you edit an aspx page in MOSS 2007 you might get "Value does not fall within the expected range". One of the reason for this issue is the incorrect top-level site URL of the publishing page. If you download the page & open it in notepad, you can find the below line consisting of incorrect top level site URL.
Just update it and upload the page again, your issue should be resolved.

<mso:PublishingPageLayout msdt:dt="string">http://incorrect_servername/_catalogs/masterpage/PageLayout.aspx, Article page</mso:PublishingPageLayout>


If you have many pages with the sample problem, manually updating each & every page could be tedious. In such case you can execute the sample, which will save your time.



const string MASTER_PAGE_LIB = "_catalogs/masterpage";

            string SiteCollectionUrl = "http://siteURL/";

            using (SPSite site = new SPSite(SiteCollectionUrl))
            {
                using (SPWeb web = site.OpenWeb())
                {

                    try
                    {

                        if (PublishingWeb.IsPublishingWeb(web))
                        {
                            PublishingWeb pubWeb = PublishingWeb.GetPublishingWeb(web);


                            foreach (PublishingPage page in pubWeb.GetPublishingPages())
                            {

                                string pageLayout = page.ListItem.Properties["PublishingPageLayout"] as string;


                                if (pageLayout != null)
                                    pageLayout = pageLayout.ToLower();

                                if (String.IsNullOrEmpty(pageLayout))
                                {
                                    Console.WriteLine("Error: Page \"{0}\" does not have a Page Layout assigned.\n", page.Uri);
                                }
                                else if (!pageLayout.Contains(MASTER_PAGE_LIB))
                                {
                                    Console.WriteLine("Error: The Page Layout {0} for Page \"{1}\" does not point to the masterpage document library.\n",
                                        pageLayout, page.Uri);
                                }
                                else if (!pageLayout.StartsWith(SiteCollectionUrl) && pageLayout.StartsWith("http"))
                                {
                                    // here we have a page which has a page layout that has a different URL which we have to fix
                                    Console.WriteLine("Page {0} has incorrect PageLayout Url", page.Uri);
                                    Console.WriteLine("Old URL: {0}", pageLayout);
                                    string pageLayoutWithoutPrefix = pageLayout.Substring(pageLayout.IndexOf(MASTER_PAGE_LIB));
                                    string newPageLayout = SiteCollectionUrl + pageLayoutWithoutPrefix;

                                    try
                                    {
                                        int version = page.ListItem.File.MinorVersion;
                                        page.CheckOut();
                                        page.ListItem.Properties["PublishingPageLayout"] = newPageLayout;
                                        page.ListItem.File.Properties["PublishingPageLayout"] = newPageLayout;
                                        page.ListItem.Update();
                                        page.CheckIn("PublishingPageLayout corrected");

                                        //major version means that the item was published. Let's publish it again.
                                       if (version == 0)
                                            page.ListItem.File.Publish("PublishingPageLayout corrected");

                                        Console.WriteLine("Fixed URL: {0}\n", newPageLayout);
                                    }
                                    catch (Exception e)
                                    {
                                        Console.WriteLine("An error occurred while trying to fix the URL to the page layout:\n{0}", e);
                                    }
                                }
                            }
                        }//if Publishing web
                    }
                }
            }


There is a KB available explicitly for this issue. http://support.microsoft.com/kb/953445
The above sample is based on the KB only.

Hope it helps :)

How to unlock the locked tasks in Sharepoint Workflow

Sometimes in Sharepoint Workflow, tasks will get locked and you will not be able to proceed further.

These locks are placed to prevent the task being updated simultaneously. The workflow runtime locks tasks by setting a field (WorkflowVersion)and persisting that to the database.

if workflowversion is 1 then the tasks are "not locked". Any value apart from "1" symbolises that these tasks are "locked".

Ideally to remove the locks, we need to update WorkflowVersion of the task to "1".

Here you go, the sample for it.


public static void UnlockWorkflowTasks(string siteUrl, string webUrl, string listName)
        {
            using (SPSite site = new SPSite(siteUrl))
            {
                using (SPWeb web = site.OpenWeb(webUrl))
                {
                    SPList list = web.Lists[listName];
                    SPListItemCollection items = list.Items;

                    foreach (SPListItem item in items)
                    {
                        SPWorkflowCollection workflows = item.Workflows;
                        foreach (SPWorkflow workflow in workflows)
                        {
                            SPWorkflowTaskCollection tasks = workflow.Tasks;
                            foreach (SPWorkflowTask task in tasks)
                            {
                                if (task[SPBuiltInFieldId.WorkflowVersion].ToString() != "1")
                                {
                                    task[SPBuiltInFieldId.WorkflowVersion] = 1;
                                    task.SystemUpdate();
                                }
                            }
                        }
                    }
                }
            }
        }


Happy Coding :)

How To Update Workflow Task Status

Sometimes in sharepoint, you might have faced an issue, that workflow being completed, but the task status is still shows "In Progress".

In that scenario, a simple workaround would be updating the task programmatically for that particular item.

Here you go,
Make sure you update the task for the required item. the below sample updates the task for item id=3
If the item has more than one task, make sure you update the task status to the appropriate task.


       string SiteCollectionUrl = "http://SiteURL/";

            using (SPSite site = new SPSite(SiteCollectionUrl))
            {
                using (SPWeb oWeb  = site.OpenWeb())
                {
                    SPList oList = oWeb.Lists["LISTNAME"];
                    SPListItem item = oList.Items.GetItemById(3);
                    SPWorkflowTaskCollection taskCol = item.Tasks;
                    SPWorkflowTask taskedit = item.Tasks[0];

                    if (taskedit == null)
                        return;
                    Hashtable ht = new Hashtable(); 
                    ht["TaskStatus"] = "#";    // To make the task "approved"
                    SPWorkflowTask.AlterTask((taskedit as SPListItem), ht, true);
                   
                }
            }





ht["TaskStatus"] = "#";   // To Approve the the task use "#"
ht["TaskStatus"] = "@";   // To Reject the the task use "@"



Happy Coding :)

Forms Based Authentication in Sharepoint

How to configure FBA in MOSS 2007

To configure FBA in MOSS 2007, you basically need to extend your webapplication and add your membership & role provider.

Here you go  http://msdn.microsoft.com/en-us/library/bb975136.aspx

How to configure FBA in Sharepoint 2010

To configure FBA in SP 2010, you need not extend the current webapplication. You can create a brand new webapplication with claims enabled and can have FBA. Additionally you can have Windows Authentication too under claims.

Here you go http://technet.microsoft.com/en-us/library/ee806890.aspx

Programmatically change Workflow Auto Clean up days

Well, if you associate a workflow to any sharepoint list/ library & if the workflow instance is completed, then its association with its tasks and history list details will lasts for only 60 days. This is because we have a sharepoint out of the box "Workflow Auto clean up" timer job running daily for every webapplication.

If you want to change the default 60 days of clean up, then you are the right stop. The below sample will help you out to achieve the behaviour.

static void Main()
        {
            using (SPSite site = new SPSite("http://site_URL"))
            {
              usingSPWeb web = site.OpenWeb())
              {
                SPWorkflowTemplateCollection collection = web.WorkflowTemplates;
                SPList list = web.Lists["LIST_NAME"];//List name
                SPWorkflowAssociation _asso = null;
                foreach (SPWorkflowAssociation asso in list.WorkflowAssociations)
                {
                    if (asso.Name == "WORKFLOW_NAME")//Workflow name
                    {
                        asso.AutoCleanupDays = 7;//clean up days
                        _asso = asso;
                    }
                }
               list.UpdateWorkflowAssociation(_asso);
            }}
        }

Happy Coding !!!!!