Customizing Sharepoint 2007: Quick Post

by James Shaw 17. July 2008 09:29

In my 1st technical post I'd like to share how I implemented a delicious-like feature in a Sharepoint site (called "Quick Post") that lets users post a file or Url to a Sharepoint document library with one click of a button on any page of the site; the user can tag each post just like with delicious, and the site has tag cloud and searching-by-tags features for users to find info later; the user will get suggestions on existing tags when he tags. 

The following is a screen shot of the pop-up window after the user clicks on the Quick Post button in the site:  



The type just gives the user a choice on one of the two different document libraries.  The location is a toggle between the ASP.NET FileUpload control for file and TextBox control for Url, using Javascript.  

With this custom UI, the user is able to post a file or Url to the document library without leaving the page he's on, and in one screen (unlike the built-in multiple Sharepoint pages for adding a file or Url, and any tags in the tag field of the document library); the custom UI also has tag suggestion to maximize tag reuse :).

The Pop-up Window

The pop-up window you see in the screen shot is a DHTML window (which is better than a browser window because of pop-up blocker).  I'm all for re-using others' code as much as possible :), so for the pop-up window I used and would recommend this 3rd party widget.  Just include the 3rd party widget in your page that pops up the window (in our case the master page since the QuickPost button needs to show up everywhere):

    <link rel="stylesheet" href="http://devcentral/_vti_bin/windowfiles/dhtmlwindow.css" type="text/css" />   
    <script type="text/javascript" src="http://devcentral/_vti_bin/windowfiles/dhtmlwindow.js">
   
    </script>

and have the button click call the following javascript function:

        var quickPostWin = null;
       
        function openQuickPostWindow ()
        {
            quickPostWin = dhtmlwindow.open("quickPostBox",
                                            "iframe",
                                            "http://devcentral/Pages/QuickPost.aspx",
                                            "Quick Post",
                                            "width=650px,height=400px,resize=1,scrolling=1,center=1",
                                            "recal");                       

            if (quickPostWin.onclose == null)
            {
                quickPostWin.onclose=function(){ //Run custom code when window is being closed (return false to cancel action):
                    return true;
                }
            }               
           
        }

The content inside the window comes from a ASP.NET user control (.ascx).  The easiest way I found to develop custom pages in Sharepoint is to develop it in Visual Studio (so you take advantage of the designer support, code-behind, etc), and host it in a Sharepoint web part: a page viewer web part to host .aspx, or like in this case, a 3rd party smart part web part to host .ascx; the page shown in the DHTML window (http://devcentral/Pages/QuickPost.aspx) is just a Sharepoint page with one smart part web part that hosts a .ascx.  Sharepoint still has some way to go in terms of its integration with Visual Studio for development, and this is kind of a stopgap until MSFT builds the functionality of developing custom pages for Sharepoint right into Visual Studio.

AJAX functionality

As seen in the screen shot, Quick Post provides suggestions as the user starts typing in tags, just like delicious.  To add AJAX functionality like this, I used the AJAX Control Toolkit from codeplex; it contains quite a few extenders to ASP.NET controls to add AJAX to your web page, and in this case I used the auto-complete extender to the tags text box.

To use AJAX Control Toolkit in Sharepoint, make sure to reference it and include the ScriptManager in the page.  Since in our case we want AJAX functionality in every page (as in most cases I think), I just put the following in the master page:

<%@ Register Tagprefix="ajaxToolkit"
             Namespace="AjaxControlToolkit"
             Assembly="AjaxControlToolkit, Version=3.0.11119.26320,
             Culture=neutral, PublicKeyToken=28f01b0e84b6d53e" %>

...

    <ajaxToolkit:ToolkitScriptManager ID="ScriptManager1" runat="server" />

Here I'm using AJAX Control Toolkit's implementation of the ScriptManager rather than the one in ASP.NET AJAX, as the former is supposed to be more efficient at sending the javascript down to the client.

The auto-complete extender itself is pretty straight-forward as documented: in my implementation, the web-service responding to the user key strokes in the tags text box just iterates over all records in the document library, extracts and parses the tags from the comma-separated tag field (using Sharepoint API's), and return the tags matching the given prefix.

There's perhaps one tricky thing in the web service.  In our Sharepoint site, the user is free to create sub-sites, and those sub-sites can contain library items with tags as well, so to make sure we capture all existing tags from all sites in this site collection, you need to make sure either every user has the permission to iterate over all sites, or elevate the privilege in the web service code somehow, .e.g, by impersonating a user that for sure has permission to do it:

            SPUser wssAccount = null;
            using (SPSite siteCollection = new SPSite("http://devcentral"))
            {
                // need to impersonate to iterate over all sub-sites
                wssAccount = siteCollection.OpenWeb().AllUsers["corp\\dcentral_wss_svc"];
            }
           
            using (SPSite impersonatedSiteCollection = new SPSite("http://devcentral", wssAccount.UserToken))
            {
               // iterate over all sites, get all the tags, and return matching tags

               ...

            }

The above will also do the trick any time your custom code using the Sharepoint API's needs to run in elevated privilege mode.

Storing Url in a Document Library

Sharepoint document libraries can have multiple content types, and when it does, the user can choose which content type it is when adding a record to a document library.  There's a "Link to a Document" content type that's well suited for storing Url's in a document library; in fact it can store link to another document as well.  For our Sharepoint site, however, we just need to support storing Url's.  

It's however not well documented how to add a document link programmatically, as I had to do for our custom UI.  When a user adds a document link to a document library, what happens behind the scenes is that Sharepoint actually creates an aspx file on the fly that does nothing more than redirecting to the document link Url you give, and it's the aspx file that's stored in the document library, just like a document stored in the document library.  A rather convoluted way to store links in my opinion, but that's the way the document link content type works.

After bugging him about it, Cliff Green from MSFT wrote up how to programmatically add a document link to a document library in his blog :).  Essentially you have a aspx template file that you read into memory each time you add a document link to a document library, after filling in the Url in the template file in-memory.

Conclusion

In this blog post, I've hopefully covered how to have custom pages in Sharepoint (leveraging Visual Studio designer), adding AJAX to your page, and how to have Url's in a document library.  In future posts I will share more customizations I've done in Sharepoint.  Hope this has been a useful read :).

My First Blog!

by James Shaw 24. October 2007 12:05

Hi, everyone!

This is my first blog!  A lot of people have been blogging for a while, but I just started.  It's kind of ironic considering that I'm in hi-tech.  Why?  Well I'm not that big of a talker in real life; digitally I'm not that big of a writer either.  My emails have always been succinct and to the point; just ask my sister, who used to write these long emails to my family only to get short responses from me Smile.

So why start now?  What really changed my mind was seeing the benefit of blogging, on the receiving/listening end that is.  Often when I have a question about something (technical or not), I can find my answers in somebody's blog after Googling it.  I've also found a lot of useful content in those aggregator sites on the web (digg, delicious, etc) coming from people's blogs.  In a way blogging has become the under-pinning of free information in a society.  Think about it; a lot of info made available to us today (from periodicals, TV, etc) have been filtered or even deliberately distorted by a few individuals; only on the internet, through blogging and mediums like digg.com do we have access to all info, from all sides of opinions.

So after benefiting from other bloggers for a while, it's time for me to also blog and join fully this free intellectual exchange.  Who am I and what will I blog about?  Well I'm a Software Engineer working for Intuit (the maker of Quicken, QuickBooks and Turbo Tax); I've been with the company since after college (see my linkedin profile).  I love the ocean and live in the beautiful San Diego.  In my spare time I like to (you guessed it) read others' blogs, play volleyball and salsa dance (doesn't mean I'm good at it).  

I will blog a lot about techie stuff (latest technologies, gadgets, etc); you can probably already tell from my geeky writing style.  I'm also into real-estate, finance/economics, history and travelling.  Of course once in a while I throw in some fun stuff to make you laugh Laughing.

So keep in touch and look forward to hear from you.  Feel free to check out my website (soon to be merged with this site) to see what I look like, and if you already know me, let's keep in touch in Facebook.

 

Powered by BlogEngine.NET 1.4.5.0
Theme by Extensive SEO