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

Tuesday, March 27, 2012

Add List Items with users identity (Created By) when using RunWithElevatedPriviledges delegate

We faced a situation when we have to elevate the read only users permission to add/update the list items in SharePoint list. To accomplish this we usually go for the RunWithElevatedPriviledges delegate, but the only disadvantage of this approach is if we add/update any item within this delegate, it will save that record with 'System Account' identity.

Following code can be used to overwrite the 'System Account' identity with current user's identity, So list item will display 'Created By' or 'Modified By' columns value as current user.

SPUser user = SPContext.Current.Web.CurrentUser;

SPSecurity.RunWithElevatedPrivileges(delegate()
{
    using (SPSite site = new SPSite(SPContext.Current.Web.Url))
    {
        using (SPWeb web = site.OpenWeb())
        {
            web.AllowUnsafeUpdates = true;
            SPList list = web.Lists["TempList"];
            SPListItem listItem = list.AddItem();
            listItem["Title"] = string.Format("Hey {0}", user.LoginName);
            listItem["Editor"] = user;  // Modified By                    
            listItem["Author"] = user;  // Created By
            listItem.Update();
            web.AllowUnsafeUpdates = false;
        }
    }
});
 

Friday, March 9, 2012

Simplest way to get the strong name of an assembly

I found very interesting post which describes how to get strong name of the assembly without even opening the properties window of that assembly. I saw many fellow-developers are still not aware of this simple option that may make their life at least a bit easier, hence sharing this post.

Old Method:

Open the assembly in the GAC (c:\Windows\Assembly) and view its properties. Then form the strong name with assembly name, version and public key token.


Simplest method:

Open the assembly, select the assembly you need the strong name for, then from the Edit menu choose Copy Display Name menu item


Monday, June 20, 2011

Configurable multi-level treeview left navigation in SharePoint

It’s a very common requirement in SharePoint to implement a multilevel Left or Top navigation. And another requirement is that it should be configurable i.e. user able to add or remove the navigation links.

I have used treeview control and SharePoint list to implement Left navigation of the site.


Step 1:
    Create a SharePoint list (LeftNavigation) with following columns
    1. Title – Single Line of Text (Name to be displayed in navigation) 
    2. URL – Single Line of Text (Relative URL)
    3. Hierarchy – Lookup column (Parent node)

Step 2:
Add new Class Library project or WSPBuilder project. Then add a User control inside the CNTROLTEMPLATES folder (as shown below).


Give proper name to the user control, here my user controls name is ‘SPNavUserControl’

Step 3: Copy this code in SPNavUserControl.ascx file
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="SPNavUserControl.ascx.cs"
    Inherits="SPNavControl.SPNavUserControl,SPNavControl,Version=1.0.0.0,Culture=neutral,PublicKeyToken=d3f00d350c249841" %>
<div id="navPanel">
    <asp:XmlDataSource ID="xmlDS" runat="server" />
    <asp:TreeView BackColor="White" ID="treeViewLeftNav" runat="server" DataSourceID="xmlDS"
        ShowLines="true" ShowExpandCollapse="true" SelectedNodeStyle-Font-Bold="true"
        HoverNodeStyle-Font-Bold="true" SelectedNodeStyle-ForeColor="Red" NodeStyle-Width="100%"
        NodeStyle-Height="100%" NodeStyle-Font-Size="11px" NodeStyle-Font-Names="verdana"
        NodeIndent="15" RootNodeStyle-BackColor="#d6e8ff" RootNodeStyle-Width="100%"
        RootNodeStyle-BorderColor="#add1ff" RootNodeStyle-BorderStyle="Solid" RootNodeStyle-BorderWidth="1px"
        LeafNodeStyle-BorderStyle="None">
        <DataBindings>
            <asp:TreeNodeBinding DataMember="menu" TextField="name" NavigateUrlField="url" />
        </DataBindings>
    </asp:TreeView>
</div>

Step 4: Write following code in code-behind file (.cs) to generate XML string (data source) from the SharePoint List and bind the Data source to the treeview control.

namespace SPNavControl
{
    public partial class SPNavUserControl : System.Web.UI.UserControl
    {
        #region Variable Declaration
        XPathExpression expr = null;
        StringBuilder xmlDataSource = null;
        string tempString = string.Empty;
        XmlDocument xmlDoc = null;
        XPathDocument doc = null;
        #endregion

        protected override void OnInit(EventArgs e)
        {
            treeViewLeftNav.PreRender += new EventHandler(treeViewLeftNav_PreRender);
            treeViewLeftNav.DataBinding += new EventHandler(treeViewLeftNav_DataBinding);
            treeViewLeftNav.TreeNodeDataBound += new TreeNodeEventHandler(treeViewLeftNav_TreeNodeDataBound);
        }
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                //Generate XML file from the SharePoint list
                xmlDataSource = new StringBuilder();
                xmlDataSource.Append("<?xml version='1.0' encoding='iso-8859-1' ?>");

                using (SPSite spSite = new SPSite(SPContext.Current.Site.ID))
                {
                    using (SPWeb spWeb = spSite.OpenWeb())
                    {
                        SPList list = spWeb.GetList(spWeb.Url + "/Lists/LeftNavigation");
                        DataTable dtList = list.Items.GetDataTable();
                        DataRow[] dtListItems = dtList.Select("Hierarchy = ''");
                        foreach (DataRow dtRowItem in dtListItems)
                        {
                            tempString = dtRowItem["Title"].ToString();
                            //Recursive method to create a XML file from list
                            GenerateXMLDS(xmlDataSource, dtRowItem, dtList);
                        }
                    }
                }
            }
        }

        void treeViewLeftNav_TreeNodeDataBound(object sender, TreeNodeEventArgs e)
        {
            if (string.IsNullOrEmpty(e.Node.NavigateUrl))
            {
                e.Node.SelectAction = TreeNodeSelectAction.Expand;
            }
        }

        private void GenerateXMLDS(StringBuilder xmlDataSource, DataRow dtRowItem, DataTable dtList)
        {
            try
            {
                xmlDataSource.Append("<menu name='" + dtRowItem["Title"] + "' url='" + (String.IsNullOrEmpty(dtRowItem["URL"].ToString().Trim('/')) ? String.Empty : (SPContext.Current.Site.Url + "/" + dtRowItem["URL"].ToString().Trim('/'))) + "' level='" + tempString + "'>");
                DataRow[] dtListItems = dtList.Select("Hierarchy = '" + dtRowItem["Title"] + "'");
                if (dtListItems.Length != 0)
                {
                    foreach (DataRow dtRowItemNew in dtListItems)
                    {
                        tempString += "/" + dtRowItemNew["Title"];
                        GenerateXMLDS(xmlDataSource, dtRowItemNew, dtList);
                    }
                }
                if (-1 != tempString.LastIndexOf('/'))
                    tempString = tempString.Remove(tempString.LastIndexOf('/'));

                xmlDataSource.Append("</menu>");
            }
            catch (Exception)
            {
                // Handle and Log your exception here.
            }
        }

        void treeViewLeftNav_DataBinding(object sender, EventArgs e)
        {
            try
            {
                //Bind XML data source to the Treeview control
                xmlDS.Data = xmlDataSource.ToString();
            }
            catch (Exception)
            {
                // Handle and Log your exception here.
            }

        }

        void treeViewLeftNav_PreRender(object sender, EventArgs e)
        {
            try
            {
                //Code block to maintain the previous state of treeview control after redirection
                treeViewLeftNav.CollapseAll();

                xmlDoc = new XmlDocument();
                xmlDoc.LoadXml(xmlDataSource.ToString());
                doc = new XPathDocument(new XmlNodeReader(xmlDoc.DocumentElement));
                XPathNavigator nav = doc.CreateNavigator();

                string currentUrl = Request.Url.AbsoluteUri.Replace("%20", " ");
                string pageTitle = Request.QueryString["title"];

                expr = nav.Compile("//menu[@name='" + pageTitle + "' and @url='" + currentUrl.Trim('/') + "']");
                XPathNodeIterator iterator = nav.Select(expr);

                while (iterator.MoveNext())
                {
                    string currentNodeToSelect = iterator.Current.GetAttribute("level", nav.NamespaceURI);
                    TreeNode treeNodeToExpand = treeViewLeftNav.FindNode(currentNodeToSelect.Trim('/'));
                    if (null != treeNodeToExpand)
                    {
                        treeNodeToExpand.Selected = true;

                        while (null != treeNodeToExpand)
                        {
                            treeNodeToExpand.Expand();
                            treeNodeToExpand = treeNodeToExpand.Parent;
                        }
                    }
                }
            }
            catch (Exception)
            {
                // Handle and Log your exception here.
            }
        }
    }
}

Step 5: Add this User Control in master page.

Note: There is limitation of the treeview control. It will not maintain a state after redirection. To overcome this limitation, I have introduced ‘Hierarchy’ column which always point’s to parent node.
When user clicks on any of the links from navigation it is redirected to the URL specified in NavigationUrl property of Treeview. As the user control is placed in master page, in treeView_PreRender event with the help of the current URL I am finding the level of the clicked node and expanding it based on that. 

Friday, March 11, 2011

The Web application at ... could not be found. Verify that you have typed the URL correctly.

Yesterday my colleague showed me this error. He was trying to open a SPSite object using a URL and hitting the following error: 

The Web application at ... could not be found. Verify that you have typed the URL correctly. If the URL should be serving existing content, the system administrator may need to add a new request URL mapping to the intended application.

This is common, and can have several causes.

1. The obvious: The URL does not exist on the farm.  

Solution: Change the URL you are loading to the correct one.  

2. The not so obvious: the code is in a console or windows application which you are now running with an account that does not have permissions to connect to the SQL server. 

Solution: either run the application as a different user or grant the user permissions on the SQL database.

3. The last but most important: the code is in a console or windows application that is compiled with platform target x86, while your SharePoint server is x64. 

Solution: set the platform target to "Any CPU".

Friday, March 4, 2011

Add event receiver to a SharePoint list

Recently I have created a generic function to add event receiver to specific SharePoint list.
Explaination is not need as code is very simple to understand.

private void AddEventReceiverToAList(string siteUrl)
{
    using (SPSite site = new SPSite(siteUrl))
    {
        using (SPWeb web = site.OpenWeb())
        {
            try
            {
                SPList list = web.Lists["myList"];
                if (list != null)
                {
                    int receivers = list.EventReceivers.Count;
                    string className = "EventReceiverClass";
                    string asmName = "EventReceiverAssemblyName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a865f0ecc234ea51";
                    web.AllowUnsafeUpdates = true;
                    bool isAddedReceiverExist = false;
                    for (int i = 0; i < receivers; i++)
                    {
                        SPEventReceiverDefinition eventReceiver = list.EventReceivers[i];
                        if (eventReceiver.Class == className && eventReceiver.Type == SPEventReceiverType.ItemAdded)
                        {
                            isAddedReceiverExist = true;
                            break;
                        }
                    }
                    if (!isAddedReceiverExist)
                        list.EventReceivers.Add(SPEventReceiverType.ItemAdded, asmName, className);
                }
            }
            catch { }
            finally
            {
                web.AllowUnsafeUpdates = false;
            }
        }
    }

Tuesday, February 22, 2011

Execute a JavaScript onload() function in SharePoint page

In SharePoint, most of the time master page contains the BODY tag, now if you want to add your JavaScript function on body onload() then there is a workaround for it. SharePoint provides the "_spBodyOnLoadFunctionNames" array. When the body is loaded, the onload event handler executes each function whose name is contained in this array.

<script type="text/javascript">
    _spBodyOnLoadFunctionNames.push("FunctionName");
    function FunctionName()
    {
      // Code goes here...
    }
script>

This array is basically a part of init. If you open the JavaScript file, you will notice the page onLoad event is handled by function _spBodyOnLoadWrapper, which further calls ProcessDefaultOnLoad, and this function executes all the function names added in array _spBodyOnLoadFunctionNames.

Monday, February 14, 2011

Out of box SharePoint Development

DO NOT write code if you don't have to.

I am a developer and I love coding. However, I find that not to write code, when the requirements can be fulfilled using OOB, is the way to make a SharePoint development project successful.
Yes, go codeless in a SharePoint development project.

Here are pros for going OOB:
  1. No need to write code
  2. No need to debug code
  3. No need to test code
  4. No need to optimize code, such as cache, log, exception handling, etc.
  5. Easier to understand (not always)
  6. Easier to deploy (not always)
  7. Shorter development cycle
  8. Save time and $
And of course there are cons:
  1. Harder to debug and test
  2. Limited options for optimization
  3. May not be feasible
  4. May be harder to deploy

Future of the SharePoint is OOB only, Sandbox solutions in SharePoint 2010 is the first step towards it.

Tuesday, January 25, 2011

Some useful methods in SPUtility class

SPUtility is a very rich class, which contains a number of useful functions; it also contains a number of obsolete functions. I found following methods very useful in our daily programming. 

1. System DateTime to ISO8601 DateTime
Converts a system DateTime value to ISO8601 DateTime format (yyyy-mm-ddThh:mm:ssZ)
string isoDateTime = SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Now());
//Output: 2010-03-30T15:30:33Z

2. Redirect
Signature of Redirect method:
public static bool Redirect(string url, SPRedirectFlags flags, HttpContext context)
The below link will provide you detailed information about the SPRedirectFlags.
3. Transfer to Success or Error Page
These methods are useful when you are
    SPUtility.TransferToErrorPage("Hi this is an Error Page", "this is link","{your_url}");

    SPUtility.TransferToErrorPage("Hi this is an Error Page {0}, {1}", "click here to Go to Top site", "{your_url}");
 
    SPUtility.TransferToSuccessPage("Hi this is an Sucess Page");
    
    SPUtility.TransferToSuccessPage("Hi this is an Sucess Page {0},{1}", "{url}", "This is Link", "{url}");
Useful link:
4. Email
Send an email from the context of the given SPWeb.
SPWeb web = SPContext.Current.Site.OpenWeb();
string subject = "Email from the " + web.Title + " web";
string body = "The body of the email";
SPUtility.SendEmail(web, false, false, "someone@somewhere.com", subject, body);

5. 12-hive Path:
Returns the filesystem path for the 12-Hive, or any of the folders beneath it.
(C:\Program Files\Common Files\Microsoft Shared\web server extensions\12)
string featuresPath = SPUtility.GetGenericSetupPath("Template\\Features");

6. Full URL:
Return absolute url from relative url
string webUrl = "/sub/default.aspx";
SPUtility.GetFullUrl(SPContext.Current.Site, webUrl);
//Output: "http://localhost/sub/default.aspx"

There are methods that I did not describe; I will publish them as soon as I find out how they can be used.

Reference Links:
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.utilities.sputility_members.aspx

Monday, January 24, 2011

SPWeb.ProcessBatchData method to improve SharePoint list operations performance

The common way to add or update multiple items in SharePoint list is SPListItem.Update. You can an existing item or add a new item using SplistItemCollection .Add(). 

Sample Code Snippet:

for (int itemCount = 0; itemCount < 100;s itemCount++)
{
SPListItem newItem = listItemCollection.Add();
newItem.Update();
}

Using Batch Update:
ProcessBatchData method takes XML format as input and it will directly communicate with SharePoint content database.

StringBuilder query = new StringBuilder();
query.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Batch>");
for (int itemCount = 0; itemCount < 100; itemCount++)
{
query.AppendFormat("<Method ID=\"{0}\">" +
"<SetList>{1}</SetList>" +
"<SetVar Name=\"ID\">New</SetVar>" +
"<SetVar Name=\"Cmd\">Save</SetVar>" +
"<SetVar Name=\"{3}Title\">{2}</SetVar>" +
"</Method>", i, listGuid, someValue, "urn:schemas-microsoft-com:office:office#");
}
query.Append("</Batch>");
spWeb.ProcessBatchData(query.ToString());


Conclusion:
If you have to update a larger number of items its highly recommended to not use the Update method on every item. Instead – use the batch update function ProcessBatchData provided by SPWeb.


Reference Links: