Archives

Archives / 2009 / November
  • Cloning The SharePoint Toolbar for Custom Web Parts

    Trying to match the “SharePoint look” is always a challenge for developers. Recently I had to build a web part that would live along side other “stock” SharePoint web parts. I also needed a functional toolbar for the web part and having it on the same page I wanted a seamless look to the toolbar so it looked just like any other part of the solution.

    In the past I would just create a lot of HTML helpers to spit out the right markup with my values dropped in. I wanted something a little more elegant so I turned to my best programming buddy, the Internet. Doing a little sleuthing I found a path that I would take. First I found this MSDN article on creating a custom field control. Snuggled inside the code sample was a snippet of code to create a custom toolbar inherited from the RepeatedControls class which would mimic the look and feel of a SharePoint toolbar. Buried in the comments for the RepeatedControls class I found a link to Reza Alirezaei’s blog entry where he implemented this (his previous attempt used the ITemplate interface but had issues with bin deployments and FBA based websites).

    His code and the code snippet on the MSDN article gave me enough ammo to pursue my attempt. When I first implemented what Reza had done, I was a little disappointed. It wasn’t *quite* a match to the SharePoint one and didn’t support adding separators or buttons without images. The problem is that Reza’s code uses DIV elements and while I prefer this over the TABLE approach, SharePoint 2007 is full of TABLEs (and tables inside of tables inside of tables…). So I set off to do a few modifications.

    Here’s the original SharePoint toolbar for a typical list item that you see on the DispForm.aspx page:

    SharePointToolbar

    The code modifications I did were really minor so kudos to Reza for digging out this nugget in the first place as you can’t easily find out how to do this on MSDN. The changes were mostly around using TABLE tags instead of DIVs, aligning images to the middle of the table row, and adding the appropriate classes and styles. Also this is somewhat improved over the stock SharePoint output in that the IMG tags are closed correctly. I’m not sure if it validates against W3C (what SharePoint code does?) but it gives me a bit of a warm and fuzzy.

    public class FileToolbar : RepeatedControls
    {
        // {0} = Button text
        // {1} = Button link
        // {2} = Tooltip text
        // {3} = Button image source
        private const string ButtonImageHtmlFormat = @"                                        
        <td id=""_spFocusHere"" class=""ms-toolbar"" noWrap=""nowrap"">
            <table cellSpacing=""0"" cellPadding=""1"">
                <tr>
                    <td class=""ms-toolbar"" noWrap=""nowrap"">
                        <a class=""ms-toolbar"" title=""{2}"" href=""{1}"">
                            <img style=""border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; 
                                border-left-width: 0px"" align=""absMiddle"" alt=""{2}"" src=""{3}"" 
                                border=""0"" width=""16"" height=""16"" /img>
                        </a>
                    </td>
                    <td class=""ms-toolbar"" noWrap=""nowrap"">
                        <a class=""ms-toolbar"" href=\""{1}"" title=""{2}"">{0}</a>
                    </td>
                </tr>
            </table>
        </td>";
    
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> {0} = Button text
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> {1} = Button link
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> {2} = Tooltip text</span><span style="color: #008000;">
    

    private const string ButtonHtmlFormat = @" <td id=""_spFocusHere"" class=""ms-toolbar"" noWrap=""nowrap""> <table cellSpacing=""0"" cellPadding=""1""> <tr> <td class=""ms-toolbar"" noWrap=""nowrap""> <a class=""ms-toolbar"" href=""{1}"" title=""{2}"">{0}</a> </td> </tr> </table> </td>";

    </span><span style="color: #008000;">//</span><span style="color: #008000;"> {0} = Separator text/image source</span><span style="color: #008000;">
    

    private const string SeparatorHtmlFormat = @" <td class=""ms-separator""> {0} </td>";

    </span><span style="color: #0000FF;">public</span><span style="color: #000000;"> FileToolbar()
    {
        HeaderHtml </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800000;">@"</span><span style="color: #800000;">
            &lt;table class=""ms-toolbar"" width=""100%"" 
            border=""0"" cellSpacing=""0"" cellPadding=""2""&gt;&lt;tr&gt;</span><span style="color: #800000;">"</span><span style="color: #000000;">;
        FooterHtml </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800000;">@"</span><span style="color: #800000;">
            &lt;td class=""ms-toolbar"" width=""99%"" nowrap&gt;
                &lt;img alt="""" src=""/_layouts/images/blank.gif"" 
                    width=""1"" height=""18""/&gt;
            &lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</span><span style="color: #800000;">"</span><span style="color: #000000;">;
    }
    
    </span><span style="color: #0000FF;">public</span><span style="color: #000000;"> </span><span style="color: #0000FF;">void</span><span style="color: #000000;"> AddButton(</span><span style="color: #0000FF;">string</span><span style="color: #000000;"> buttonId, </span><span style="color: #0000FF;">string</span><span style="color: #000000;"> buttonText, </span><span style="color: #0000FF;">string</span><span style="color: #000000;"> clientOnClick, </span><span style="color: #0000FF;">string</span><span style="color: #000000;"> tooltipText,
                          </span><span style="color: #0000FF;">string</span><span style="color: #000000;"> buttonImageSrc)
    {
        var buttonMarkupLiteral </span><span style="color: #000000;">=</span><span style="color: #000000;">
            </span><span style="color: #0000FF;">new</span><span style="color: #000000;"> Literal
                {
                    Text </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000FF;">string</span><span style="color: #000000;">.Format(
                        </span><span style="color: #0000FF;">string</span><span style="color: #000000;">.IsNullOrEmpty(buttonImageSrc) </span><span style="color: #000000;">?</span><span style="color: #000000;"> ButtonHtmlFormat : ButtonImageHtmlFormat,
                        SPHttpUtility.HtmlEncode(buttonText),
                        SPHttpUtility.HtmlEncode(clientOnClick),
                        SPHttpUtility.HtmlEncode(tooltipText),
                        SPHttpUtility.HtmlUrlAttributeEncode(buttonImageSrc)),
                    ID </span><span style="color: #000000;">=</span><span style="color: #000000;"> buttonId
                };
        Controls.Add(buttonMarkupLiteral);
    }
    
    </span><span style="color: #0000FF;">public</span><span style="color: #000000;"> </span><span style="color: #0000FF;">void</span><span style="color: #000000;"> AddSeparator()
    {
        var literal </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000FF;">new</span><span style="color: #000000;"> Literal {Text </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000FF;">string</span><span style="color: #000000;">.Format(SeparatorHtmlFormat, </span><span style="color: #800000;">"</span><span style="color: #800000;">|</span><span style="color: #800000;">"</span><span style="color: #000000;">)};
        Controls.Add(literal);
    }
    

    }

    ButtonHtmlFormat is a formatting constant for buttons without images (the “Alert Me” button at the end), ButtonImageHtmlFormat is for regular buttons with images. The images must be 16x16 pixels in size. Try to re-use the stock SharePoint ones if you can so users are familiar with them. The worst thing is seeing a Save icon that’s different depending on what page you’re on.

    You also might notice on the FooterHtml there’s an extra TD added with a blank image file set to 1px wide using up 99% of the space. This is to force all of the toolbar items to scrunch up (technical term) to the left. Without this, the elements will be spread out across the entire toolbar.

    To use this, just create the toolbar class in your web part and add whatever buttons/separators you need:

    private void CreateToolbar()
    {
        var fileToolbar = new FileToolbar {ID = "ToolBar"};
        Controls.Add(fileToolbar);
        
        fileToolbar.AddButton("NewItem", "New Item", "NewForm.aspx", 
            "Click here to create a new item in this list.", "/_layouts/IMAGES/newitem.gif"); 
        fileToolbar.AddSeparator();
        fileToolbar.AddButton("EditItem", "Edit Item", "EditForm.aspx", 
            "Click here to edit this item.", "/_layouts/IMAGES/edititem.gif");
        fileToolbar.AddSeparator();
        fileToolbar.AddButton("DelItem", "Delete Item", "DeleteForm.aspx", 
            "Click here to delete this item.", "/_layout/IMAGES/delitem.gif");
        fileToolbar.AddSeparator();
        fileToolbar.AddButton("ManagePerm", "Manage Permissions", "ManagePermissions.aspx", 
            "Click here to manage permissions for this item.", "/_layouts/IMAGES/manageperm.gif");
        fileToolbar.AddSeparator();
        fileToolbar.AddButton("AlertMe", "Alert Me", "FormAlerts.aspx", 
            "Click here to create an alert for this item.", string.Empty);
    }
    

    Here’s the output from the code above.

    CustomToolbar

    The spacing is *slightly* off. I think this might be because there’s an additional (empty) A link added to the SharePoint output but for me this is good enough.

    Enjoy!

  • Name that WSP - SharePoint Developer Tricks with WSPBuilder

    One of my favorite tools that I highly recommend next to breathing is WSPBuilder. With this tool in your arsenal, no longer do you have to worry about hand crafting manifest files or running cryptic makecab.exe command line tools. Building solution packages for SharePoint can be a super big problem, and for super big problems we turn to WSPBuilder to save the day!

    WSPBuilder comes in the form of a command line tool that you can run against a specific directory structure on your hard drive where your solution files live. It scans the folder and out pops a WSP file you can easily install onto SharePoint (make your life even easier by using the SharePoint Solution Installer so you can automatically deploy and activate your features!). While the command line tool is easy, the Visual Studio Plugin is even easier and useful. This is an integrated tool to Visual Studio that will allow you to build and deploy your solution from inside your IDE.

    The default settings are fine but for example the WPS filename is the same as your project name. Personally I like using the CompanyName.Department.Project nomenclature when naming my artifacts (Features, WSP Files, Assemblies, etc.). This way I can easily identify them in the GAC and folder trees. So the default name just won't do for me and I don't care to rename my solutions or projects into fully qualified names, I just want my output files this way.

    To override the default settings in WSPBuilder from inside the IDE is easy. Just create an xml file (Project > Add New Item > XML File) in your project named WSPBuilder.exe.config. Drop a copy of the contents from where WSPBuilder is installed (by default the IDE uses C:\Program Files (x86)\WSPTools\WSPBuilderExtensions) into this file and make the changes you want. For example to change the output name just add the key name WSPName and set the value to whatever you want:

    <add key="WSPName" value="MyCompany.MyProject.wsp"/>

    Now when you right click on your project and select WSPBuilder > Build WSP, it uses the local config file. You can set any other values you want to override from the default here as well. Hope that helps!

    If you don't have WSPBuilder in your toolbelt, check it out today!

  • Resources for my SharePoint Services Talk

    As promised here are some resources for my recent talk on creating and consuming services in SharePoint that I delivered at TechDays. You’ll be able to get the full slide deck, video, and code later at http://my.techdays.ca but here are the links from the slide deck and things I mentioned during the talk.

    Links

    Books

    Tools

  • SharePoint 2010 Setup - Hurry up and Wait Edition

    By now, everyone and his brother are downloading and installing the SharePoint 2010 beta onto their Windows 7 and Vista boxes. Why not? I mean, for the longest time we were stuck with having to spin up a Windows 2003 or 2008 server so now accessibility is here and we can genuinely put it on our machines without hackish workarounds. Of course why you would install a server application on a desktop OS is beyond this blog post and for a discussion over beers at SharePint.

    In any case there all kinds of guidance coming out on how to set things up. One of them is the MSDN article called Setting Up the Development Environment for SharePoint Server. This is a pretty good article but two gotchas I want to point out if anyone is following it and trying to slam SharePoint onto their desktop machine.

    Step 8 of installing the prerequisites requires that you have certain Windows Features turned on. These are things like the web server role, ASP.NET, authentication, etc. If you use the UI and manually select the features to turn on, just ignore me for a few minutes. If you copy the command line code they provide to do it all in one fell swoop then read on. Do not copy the command then paste it directly into a command window. It'll fail with a cryptic message and the feature enablement will only be half baked. Instead copy it but paste it into a text editor like Notepad, remove the line breaks, then paste it into a command prompt. I will give Microsoft credit as they state "The following text contains line breaks. You must remove the line breaks to run this script from a command prompt." however its all very Python-esk like pulling the pin on a grenade *then* reading the next instruction that says "Do not hold the grenade for very long after pulling the pin".

    The other trip is the SQL Server KB update (step 5 of installing SharePoint 2010). The instructions read as if you're going to click on a link, download a small file and run it, and move on quickly. The reality is a little more involved.

    When you click on the SQL Server KB 970315 link, you're taken to a page to *request* the hotfix. There's no file there, just a link to request one (two in fact). So first enter your email address (twice) and the captcha and select both items from the list of hotfixes. In a few minutes you'll receive an email with a link to both items. BTW if you view this email in a web-based mail system (GMail, etc.) the link is incorrect and when you click on it you'll see a 404 error in your browser window. Click on the url in the addextress bar, move the cursor to the end of the address and remove the extra ")" character then press enter. The download should startup correctly (clicking on the link in a mail client like Outlook works fine).

    Start the downloads and go grab a coffee, play a game or two of Modern Warfare 2 and maybe read a short novella. The two downloads are 400mb in size so it'll be a few minutes before you can extract them. Once you have files downloaded, click on them. You'll be asked for a location to extract them to and then be asked for a password. The password is in the email they sent you (you did keep it right?). Extract the two files to separate directories. Finally, launch the hotfixes by running the first one (381569 which is actually KB970315) then the second one (398850 which is KB976761). Once this is done, go back to the SharePoint installer that's beeen hanging out and waiting this whole time and launch the configuration wizard.

    Clear as mud? Great. Enjoy!

  • First Calgary SharePoint User Group Meeting

    I have the pleasure of speaking at the first Calgary SharePoint User Group meeting next Monday, November 23rd. The planning for a Calgary based SharePoint group has been a long time coming (I’ve been engaged in talks about one at least 4 times over the last few years). Finally it’s a reality.

    image

    Purpose:

    • The Calgary SharePoint User Group will provide an opportunity to discuss and grow new ideas and innovations by allowing consultants and IT knowledge workers to share their common experiences, issues, and solutions. 
    • It will create a forum for individuals to discuss SharePoint and related technologies, such as .NET/development, customization/branding, Business Intelligence (BI), content/document & records management, etc, with other experienced individuals in the field.

    Who can attend?

    • Anyone!
    • SharePoint is a broad product where users range from having no technical experience to hardcore programmers who can make SharePoint rollover and do tricks.  If necessary we may even have separate meetings depending on the number of attendees and their areas of interest.

     

                 

    Date Monday, November 23rd

    Doors open at 5:00pm, meeting starts at 5:30pm

    Topic SharePoint 2010 Overview
    Location TransCanada Building, +15 level conference room
    450 1 St SW
    Website http://www.calspug.org/

    See you there!

  • Resources for my SharePoint Versioning Talk

    Just finished up my talk on SharePoint Solution Versioning at TechDays 2009. I hope it went well for everyone (got a tweet from someone thanking me for answering their question before they asked it, now I just need to go out and buy a LottoMAX ticket). Here’s some quick resources on the talk. You’ll be able to get the full slide deck and video later at my.techdays.ca but here’s some links that may be useful if you enjoyed my talk.

    Planning SharePoint Solution Packages
    http://weblogs.asp.net/erobillard/archive/2009/06/19/planning-sharepoint-solution-packages-wsps.aspx

    Building and Packaging SharePoint Solution Packages
    http://weblogs.asp.net/erobillard/archive/2009/07/30/Building-and-Packaging-SharePoint-Solutions-and-the-WSPSolution-Project.aspx

    CodePlex: WSPSolution Project
    http://www.codeplex.com/wspsolution/

    CodePlex: WSPBuilder Project
    http://wspbuilder.codeplex.com/

    See you guys tommorrow as we go over Consuming and Building SharePoint Services!

  • Hiding the New Toolbar Button in SharePoint with jQuery

    Another quick little fun thing today. Many times you might want (need) to hide the “New” button on a list toolbar. You know the one I mean?

    image

    Why would you want to do such a thing? For example on a project I’m building I actually call the NewForm.aspx page with a querystring because I want to pre-populate my form with some vales. As such, I don’t want users to create new items in a list without these references and since they have to come from another list I’m left with the problem of trying to restrict them from creating new items but still offer them the ability to use the features of the list like alerts, exporting to spreadsheets, etc. Yes, the “New” button isn’t available for readers of a list but for contributors it is and for admins you can’t just turn some of this stuff off easily.

    If you do some Googling you’ll find some ways to do it. Some want you to modify the list schema, others have C# code to hide it, an others even want you to crack open SharePoint designer and butcher your AllItems.aspx page. Bazooka to kill a mosquito solution IMHO.

    Here’s another simple way to do it with your Swiss Army knife, jQuery.

    Ingredients

    • jQuery (either installed on your server or remotely and referenced in a master page or via a Content Editor Web Part)
    • 1 Content Editor Web Part
    • 1 lines of jQuery/JavaScript

    First you’ll need to have a list to modify. In this case I’ll use a Task list, but any list or library will do. Next go to the view page for the list that you want to do this on.

    Click on Edit Page in the Actions menu and you’ll be allowed to add and edit web parts on the view page. This was a feature Microsoft smartly added and is fully supported. Now we can start adding our jQuery love.

    Click on Add Web Part, browse for the CEWP and drop it on the page. Make sure you place it below the list form and also mark it as “Hidden” in the Layout options. This keeps the page looking as clean as it was originally.

    If you inspect the HTML of any toolbar, it’s basically composed of something like this:

    <table class=”ms-menutoolbar”>
    <tr>
    <td class=”ms-toolbar”>[toolbar item]</td>
    <td class=”ms-separator”>[separator image]</td>
    <td class=”ms-toolbar”>[toolbar item]</td>
    <td class=”ms-separator”>[separator image]</td>
    <td class=”ms-toolbar”>[toolbar item]</td>
    <td class=”ms-separator”>[separator image]</td>
    [etc.]
    </tr>
    </table>

    We want to hide the first couple of <TD> elements which contain the “New” button as well as the separator. We can do this easily with this little nugget of jQuery:

    <script src="/Javascript/jquery/jquery.js"></script>
    <script>
        $(document).ready(function(){
            $('.ms-menutoolbar td:lt(4)').hide();
        });
    </script>

    Add that to your CEWP you added to the NewForm.aspx page and you get this:

    image

    fooP! The “New” button disappears.

    Sidenotes

    The jQuery is super simple here (I try to write as little code as possible). When the document loads, find the toolbar class (‘.ms-menutoolbar’) then find the first 3 <TD> tags and hide them. One thing to note, when I wrote this today at work I was on IE7 and there were only 2 <TD> tags to hide (thus my jQuery selector was “td:lt(3)”). When I wrote this post I did so hitting my site using FireFox and lo and behold there seems to be an additional <TD> tag. In any case, you might have to experiment with the selectors to get the right number depending on your setup.

    There are *always* many ways to do things in SharePoint. This is just one of them. I suppose you could also find the “id” of the buttons and remove/hide them but SharePoint IDs are always cryptic and not guaranteed to be the same from list to list, page to page, and site to site. I just find this method easy and low impact. YMMV.