NetCommunity Lab Notes

NetCommunity Lab Notes http://labs.blackbaud.com
RSS Feed
Embedding in NetCommunity - Disqus Comments
Wednesday, May 19, 2010
Before we go any further with this series on embedding, I wanted to make sure that we here at Blackbaud could hear from you NetCommunity users out there. Killing two birds with one stone, I'm going to embed comments in Labs and write this first article on how you can do it too.
 
For Labs, I've decided to go with a very cool commenting system from a company called Disqus. Disqus does nothing but comments, and they do it very well. They give you, amont other things, export capabilities, advanced moderation tools, and integration with Twitter, Facebook, and OpenID. It's extremely feature-rich and completely free. You can see the Disqus comment thread for this article down below.
 
It couldn't be easier to get all this functionality onto your NetCommunity site and start soliciting valuable feedback and generating discussion among your online community.
 
Step 1: Sign up for a (free) account at Disqus.
Step 2: Choose the "Universal Code" option when viewing the install instructions.
Step 3: Create a new Formatted Text & Images part in NetCommunity, switch to HTML view and paste in the "embed code" from Disqus. Save.
 
Now, any page you place that FT&I part on will have a Disqus comment thread!
 
Disqus associates comments with a URL, so you can even put your FT&I comment part on a NetCommunity template and all pages derived from that template will have their own set of comments.
 
The only problem here is that your logged-in NetCommunity users have to re-authenticate against another service (Facebook, Twitter, OpenId, or Disqus itself) to post comments (unless you have enabled anonymous commenting). This means that this flavor of Disqus integration is best suited to sites that have lots of discussion-worthy content freely and easily available - the bulk of your comments are not coming from logged-in users.
 
If you'd like to try and integrate the logins, then you can investigate Disqus' VIP program which provides a single sign on API. I have not actually worked with their API, so I can't vouch for the ease of integration, but it appears to be a standard SSO pattern.
 
Even without integrated logins, you can still do quite a bit with the Disqus data. Disqus supports xml exporting, so you can harvest the email addresses of everyone who has commented and even import that data back to RE7 or BBEC to identify users who have commented.
 
I'm excited to hear your thoughts in the comments below!
Embedding in NetCommunity
Monday, May 10, 2010
This article is going to kick off a series of posts that will show you how to embed all kinds of social media, interactive content, and 3rd-party web applications into NetCommunity. It feels like every week or two I see a new, cool web application that I know Blackbaud customers would love to use and very often it's dead simple to bring that functionality into NetCommunity sites. Do you like Vimeo's media player? No problem - it's trivial to embed your video content in NetCommunity. Do you use the Flickr community and photo management tools? Then dive in, knowing you will be able to seamlessly surface slick slideshows back on your home page.

The mashups and extensions are going to start coming fast, so get ready to push your website in exciting new directions!
Secrets of Custom Confirmation Screens: Twitter & Google eCommerce
Monday, September 28, 2009

Motivation

A common customization scenario we hear about is performing an action after a user donates. When a donation comes in you may want to alert someone, update a report, or log the donation to a 3rd party system. The most obvious way to do this is by writing a custom donation form with the NetCommunity API, but that’s an awful lot of work just to tack something onto the end of the donation process. In this article I’ll show you an easy way to take action after a constituent donates online. We’ll be relying on the custom confirmation screen feature of the donation form, but all the techniques will work with the event and membership forms as well.

Technique

To perform an action after a donation, we need to answer two questions:

1.       How do we know a user has just donated? The URL of the donation form doesn’t change after a completed donation.

2.       How do we access the transaction data? To do anything interesting after a donation, we will need some information about the transaction. The amount donated, for instance.

The answers are to be found in custom confirmation screens. Custom confirmation screens let you design the page (using the NetCommunity WYSIWYG editor and merge fields) that shows after a donation. You have complete control over the markup. We’re going to take advantage of that to introduce some special markup that will make it easy for JavaScript to read values from the confirmation screen. Then we can determine if we are on a confirmation screen and get any relevant transaction values.

Enough talk. Let’s code.

Simple Example

(note: This code has only been tested in BBNC version 6.10)

I’m going to use jQuery to pull values off of the confirmation screen. You can use whatever JavaScript technique you like, but I recommend jQuery because it’s easy to write, it’s fast, and it’s already included on every NetCommunity page.

First, I’ll create a donation form with a really simple custom confirmation screen. Here’s the HTML for my screen (I’ve abbreviated the merge field markup for readability):

<div>

 <img src=”...” (Individual.Begin Section)>

</div>

<div>

 Name:<span class="TSName"><img src="..." (First Name)></span>

</div>

<div>

 <img src="..." (Individual.End Section)>

</div>

<div>

 Amount:<span class="TSAmount"><img src="..." (Gift Amount)></span>

</div>

This just displays the first name of the donor and the amount of the donation. If you look carefully though, I’ve added a “class” attribute to the span tags that hold the merge fields. jQuery loves class attributes, and makes it super easy to get data from elements with unique classes. If you feel more comfortable using document.GetElementById, then by all means give the span tags an Id attribute.

Now that we have our custom confirmation screen all set to go, we have to write some script to parse it. Create a new formatted text and images part, switch to HTML mode and enter this script:

<script type=”text/javascript”>

function AfterDonation() {

       if ($(".TSName").length > 0) {

              fname = $(".TSName).children(":first").text();

              alert("Thanks " + fname + "!");

       }

}

$(document).ready(

    function() {

        Sys.WebForms.PageRequestManager.getInstance().add_endRequest(AfterDonation);

    }

);

</script>

This script defines a function (AfterDonation) that will pop a “Thank you” alert after a donation. The second part of the script registers that function to fire after the donation form updates (See Footnote #1 for a technical explanation). The first line of AfterDonation verifies that our custom confirmation screen is loaded by looking for an element with the class we defined, and then grabs the first name field value from the confirmation screen and pops the alert. Cool!
 
Technical NoteThe NetCommunity donation form uses an asp.net UpdatePanel when processing a donation. The sys.webforms… line of code tells asp.net to fire the AfterDonation function whenever the updatepanel completes an update. The AfterDonation function checks to make sure it is actually on a confirmation screen by verifying that our custom confirmation screen markup is present, and then fires off the alert. We need to verify that it is actually a confirmation screen because the update panel could update with a validation message instead of a successful donation, which would cause the AfterDonation function to fire, but in that case we are not actually on a confirmation screen. Finally, to get the Name value out of the confirmation screen, we select the span element containing the merge field, and then get the value of the next child span tag. This is because merge fields render as span tags, so we wind up with another span tag inside the original span tag that we decorated with the class name.
 
 
Disclaimer: This script is a bit "fragile". If we decided at some point to change how merge fields are rendered, then this will stop working. The way merge fields render has remained the same for a long time and, while we don't anticipate changing them, this is not a 100% future-proof solution. As always, test all mission critical site functionality before upgrading.
 
Now that we have the technique down, we can use it for all kind of cool things. Here are two examples.
 

Example 1 – Google eCommerce Integration

(note: This code has only been tested in BBNC version 6.10)

About Google Analytics & eCommerceGoogle eCommerce is an advanced set of tools inside Google Analytics that gives deep insight into the behavior of your visitors and some really handy reports and metrics about your monetization efforts. Digging into all the features of Google eCommerce tracking and how to use them is beyond the scope of this footnote, but for those of you who have a handle on the basic functionality in GA, I definitely encourage you to explore the eCommerce features. Here’s a link to get you started: http://www.epikone.com/blog/2008/06/25/google-analytics-e-commerce-tracking-pt-3-why-everyone-should-use-it/
 
I use this script in my site tracking box to send data over to Google eCommerce (See Footnote #2 for information about Google Analytics' eCommerce features) after a user has donated. The new parts are in red, the rest is the default Google Analytics code that you probably have in your site tracking box already:

<script type="text/javascript">

var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");

document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));

</script>

<script type="text/javascript">

function GetValue(className) {

       val = $("." + className).children(":first").text();

      

       //if there are any currency symbols, strip them out. GA doesn't like them.

       m = /^"$|"£|"€/g;

       return val.replace(m, "");

}

try {

       var pageTracker = _gat._getTracker("UA-4094862-3");

       pageTracker._trackPageview(BBNCAnalyticsURL);

       if ($(".TSConfirmation").length > 0) {

              //Generate a fake order ID, thanks to http://www.epikone.com

              var d = new Date;

              var unixTime = parseInt(d.getTime / 1000);

              var orderID = pageTracker._visitCode() + '-' + unixTime;

             

              //Only Order ID and Total are required

              //Feel free to track more information if you want

              pageTracker._addTrans(

                  orderID,                       // Order ID

                  "",                            // Affiliation

                  GetValue("TSAmount"),          // Total

                  "",                            // Tax

                  "",                            // Shipping

                  "",                            // City

                  "",                         // State

                  ""                             // Country

               );

               

               pageTracker._addItem(

                  orderID,                        // Order ID

                  "",                             // SKU

                  "",                             // Product Name

                  "",                             // Category

                  GetValue("TSAmount"),           // Price

                  "1"                             // Quantity

               );

               pageTracker._trackTrans();

       }

} catch(err) {}</script>

This is really just the code that Google provides for eCommerce integration (See: http://www.google.com/support/analytics/bin/answer.py?hl=en&answer=55528). All I did was rip some values out of the confirmation screen. I also wrote a bit of code to generate a unique order ID and a function to get a merge field value and remove any currency symbols. NetCommunity already takes care of firing the tracking code after a donation form update, so this is all I had to do. You can easily pull some other data points off the confirmation screen to fill out other values you find useful in your reports. Easy!

 

Example 2 – Twitter Updates

(note: This code has only been tested in BBNC version 6.10)

What would a sample be without Twitter integration? Here’s a custom part that I’ve named TweetStream that sends an alert to Twitter whenever someone donates, using the 3rd party TweetSharp library. You can use this part to generate a live feed of donations as they come in. Here’s a screenshot of an integrated Twitter account after a few donations (you can see this live at http://www.twitter.com/DemoOrg):
 
 

And here’s the editor for the part:
 

Once you’ve download and installed the part (don’t forget to put TweetStream.js in your Client"Scripts folder!), create a new TweetStream part, fill out the settings, and place it on a page with a donation form. The part is invisible, but will detect a completed donation and update the Twitter account you specified in the editor. The code is heavily commented and should be easy to understand and expand. This is a little different than previous examples because it actually makes an Ajax request (via the asp.net ICallBackHandler, though you could use any Ajax technique you like) back to the server after the donation, and the Twitter update is performed server side. From the server you could update a database, call out to a 3rd party system (as I did here) or perform analysis on transactions as they come in. Now that’s really powerful!

That’s it for today! I hope you’ll find some awesome new uses for this technique!

Google Analytics in NetCommunity Demystified
Thursday, August 27, 2009

Google Analytics in NetCommunity Demystified

A Little Background

Why do I need to web analytics?

Trying to run a successful web site without analytics is like writing a novel by putting alphabet soup in a blender. You know what’s going in, but have no way to control what’s coming out. I believe that good, actionable web analytics are as important as your marketing copy and site design, and represent the biggest opportunity to make money and generate leads online. Web analytics help you turn an opaque and faceless stream of online visitors into a fascinating source of insight and action.

Why Google Analytics (GA)?

GA is powerful, free, constantly adding new features, and supported by a large, stable company. It integrates well with Google AdWords for organizations who are interested in running a online ad campaign. NetCommunity’s analytics integration has been designed with GA in mind. It will work with most any analytics solution, but going forward NetCommunity will include more and more first-class hooks into Google Analytics.

            How does GA track my site?

After signing up for GA, you are given a piece of Javascript code and told to put it on every page of your site. The script runs every time someone views a page, and sends Google information about that visitor like:
                - How did this person get to your site?
                - Where is the visitor, geographically?
                - What URL is the visitor looking at? (n.b. this makes the URL very important)
Based on just that information you can learn an incredible amount about how your site is performing and what your visitors are doing.

Setting up Google Analytics in NetCommunity

Signing up

Go to http://www.google.com/analytics and sign up for an account (it’s painless, I promise). When you finish the sign-up you will be presented with your tracking script. Save it somewhere (though you can always find it again on the GA site).

Configuring GA

Go to the home page of Google Analytics and edit the profile settings for the account you just created. Hit “Edit” on the top left of the “Main Website Profile Information” box. You can leave “Default page” blank because NetCommunity always serves up a page URL. I suggest putting “bm, erid, frsid” into the “Exclude URL Query Parameters” box.  Finally, use the settings below to track site search:
 
           

This will let you see what your users are searching for inside NetCommunity. It will pick up queries from both the Search Part and the Quicksearch Part.

Configuring NetCommunity

Go the Administration->System Options, and scroll down to “Enable Site Tracking”. Check the box and copy-paste the code GA gave you into the site tracking script box. Now, we have some slightly different instructions depending on which version of NetCommunity you are using:

                 5.6 – You’re done!

                6.10 – Near the end of the code Google provided there is this line:
                                                pageTracker._trackPageview();
                                Change it to:
                                                pageTracker._trackPageview(BBNCAnalyticsURL);
Update! Google has changed their tracking code. The correct insertion point (as noted in the comments below) is now:

 
  _gaq.push(['_trackPageview', BBNCAnalyticsURL]);

Save.

Whoa, whoa, whoa, what was that? Why are things different between the two versions? Good question! Allow me to digress into a…

Soul-Crushing Technical Discussion (TM)

GA tracks visitors by examining the URLs a person visits while on your site. This can cause a bit of confusion is complex web applications. Here’s an example - let’s say you have a donation form at URL http://MyOrg.com/page.aspx?pid=100. When a visitor makes a donation and sees the confirmation screen, the URL doesn’t change. GA has no way to distinguish between users who actually donated and users who just viewed the donation form and then did something else. That’s a pretty big problem! How can you measure the effectiveness of your site if you can’t even track how many people donated? Similar problems exist for the Event Registration Form and the Membership Form.

NetCommunity’s solution is to build up a special URL for consumption by GA. This URL has extra analytics goodness on it to help you make sense of your data. For example when someone views that donation form, the URL that gets sent to GA looks like:

Page.aspx?DonationStep_51=DonationStep_Checkout&pagename=Donate&pid=100

And when a visitor completes a donation the URL looks like this:

Page.aspx?DonationStep_51=DonationStep_Acknowledgement&pagename=Donate&pid=100

Voilà! You can distinguish between people who did and did not donate. There is some extra information in there as well, like the part ID # of the donation form (51) and a human-readable page name. Any information that was originally present on the URL is also sent over.

What changed between version 5.6 and version 6.10 is how NetCommunity sends this special URL to the analytics service. In 5.6 NetCommunity built an invisible iFrame on each page containing your tracking code and pointed the iFrame to the special URL. The tracking code would see the URL of the iFrame and send it over to GA. This worked ok, except for one major problem – GA lost all referrer information. If someone clicked through to your site from a blog, there was no way to know because the tracking script got loaded in an iFrame which always had a URL referrer of the host NetCommunity page. Referrer information is extremely important when analyzing traffic because it allows you to place value on you marketing, PR, and advertising efforts.

In version 6.10, NetCommunity eliminated the iFrame and put the tracking code into the main page. NetCommunity then writes a Javascript variable called “BBNCAnalyticsURL” to every page. You can tell the GA code to use a URL other than the one in the address bar by passing the new URL into the trackPageView() method as instructed for 6.10 users above. Now we have the best of both worlds. The tracking code is running in the correct page, we get extra information on the URL, and we have our referrers intact.

Looking at the Data

Setting up Goals in GA

Tracking conversions in GA is done by setting up Goals. Goals are triggered when someone completes an action on your site. I’ll quickly show you how to set up a goal for a completed donation. You can set up different goals of your own, and with some clever Javascript you can even track things that NetCommunity doesn’t build into its special URL by modifying the BBNCAnalyticsURL variable.

To set up a simple donation goal, first edit your analytics profile and edit the first goal in the “Conversion Goals and Funnel” box. Set up the goal like this:
 
            


All BBNC donation confirmation pages will contain the value “DonationStep_Acknowledgement” and by using the Regular Expression Match Type, any URLs that contain that value will count as a completed goal. You can look through the NetCommunity documentation to see how the URLs are built for different part types.

Where to go from here

Digging deep into GA and generating actionable metrics is well beyond the scope of this document, but here are a few resources to get you started:

1.       Avinash Kaushik’s Blog – Avinash is one of the real analytics gurus out there and has a great archive of blog posts explaining the ins and outs of web analysis.

2.       CopyBlogger – Fantastic advice for converting traffic, optimizing landing pages, and writing copy that generates results.

3.       Conversion Room – Tips for conversion written by the Google Analytics team.

4.       Conversion University – A thorough nuts & bolts guide to GA written by the Google team.

5.       Practical eCommerce – Blogs and articles to help get value from your visitors. Many articles are applicable to nonprofits as well as ecommerce sites.

Up Next

          eCommerce in GA

I’m going to write another article showing you how to set up the eCommerce features of GA and integrate them into NetCommunity. Even if you aren’t selling anything or soliciting donations the ecommerce reports in GA can be powerful tools.

Testing Your Content

I’m going to dig into the Content Comparison part and show you how simple tests can make a big difference in your online revenue. I’ll also look at the testing functionality built into GA.

NetCommunity Developer Challenge Entries Released
Friday, December 12, 2008
We are excited to announce the release of the full source code for the top entries submitted in the NetCommunity Open Platform Developer Challenge.  There is some really cool stuff here that makes creative use of the NetCommunity Open API.  We would like to thank all the folks that submitted entries.  It was a blast to check them out!

You can view the entries, download the code, and try these parts out on your NetCommunity today. 
 
Congrats to the contest winner, Garrett Keating, Senior Web Developer at the U.S. Naval Academy Alumni Association & Foundation for his Events on a Google Map entry.  Garrett also maintains a great blog covering all things NetCommunity Open Platform.
 
We'd also like to announce our internal contest winner Matt Bell, a software engineer at Blackbaud.  Matt built a super cool application that provides a Facebook-like chat experience to NetCommunity.  Word on the street is that Matt got help from fellow Blackbaudians Jon Bell and JD Livingstone but it appears he is trying to take all the credit (we had a little internal cash reward).  We are still investigating this ;)
 
Thanks again to all the participants!
 
Create Custom Donation and Payment Forms
Thursday, November 20, 2008
In this sample we explore the possibilities of creating donations and other payments via the NetCommunity API, by building our own custom giving form. Donations collected through the API become standard donation transactions that get delivered to The Raiser's Edge or Blackbaud eCRM just as if they were captured with the built in BBNC donation form part. This means you can focus on presenting a unique giving experience, yet get all of the built in back office donation integration for free. 
 
Our simple sample demonstrates some key features of the API including:
  • How to record a donation
  • Using gift attributes, including how to get all gift attribute types from RE7
  • Selecting a Raiser's Edge Appeal using the CFAPicker Server Control
  • Clearing a Credit Card
  • Designing and sending a custom email acknowledgement that uses custom merge fields, using the EmailEditor Server Control
  • Using jQuery scripts and images from embedded resources
The use of custom donation forms isn't limited to gifts, of course. You can also use the principles presented here to build an online shopping,  gift giving,  or tribute experience. What's more, the API examples here are not limited to Custom Parts either, using Custom Handlers, you can create donations and generate gift records from any external source that simply requests your custom handler's URL. Wicked. 
 
We hope you find this code a helpful reference as you explore the possibilities.
 
Integrated Facebook Application: Open Platform Toolkit
Monday, November 17, 2008
 
This article introduces the Facebook Application Open Platform Toolkit, a free NetCommunity toolkit of open source code for your consumption. 
 
Start by downloading the presentation here, to read the full overview of what's possible. Then check out the sample code project. There is a lot of really interesting code here, and we're very excited to see how this project develops. The code and concepts presented build on the Open Platform technologies covered in this prior article on building Custom Request Handlers in NetCommunity. It is stongly recommended that you start there.
 
So check it out!
 
Rock Your NetCommunity with jQuery
Monday, November 10, 2008
There's been a lot of buzz lately about Microsoft's decision to adopt the open jQuery javascript library into it's Visual Studio development platform and it's getting .Net web developers very excited, including the Blackbaud NetCommunity development and Interactive design teams. In this article I'm going to show you how you can begin using jQuery with NetCommunity today to add some zing to your web site. 

"Microsoft is looking to make jQuery part of their official development platform. Their JavaScript offering today includes the ASP.NET Ajax Framework and they’re looking to expand it with the use of jQuery. This means that jQuery will be distributed with Visual Studio (which will include jQuery intellisense, snippets, examples, and documentation).

[Microsoft isn't] looking to make any modifications to jQuery (both in the form of code or licensing) - they simply wish to promote its use as-is. They’ve recognized its position as the most popular JavaScript library and wish to see its growth and popularity continue to flourish."


Excerpt from http://jquery.com/blog/2008/09/28/jquery-microsoft-nokia/

The announcement came in a blog post by Scott Guthrie, a vice president in Microsoft's developer division, who described the library's attraction:

"A big part of the appeal of jQuery is that it allows you to elegantly (and efficiently) find and manipulate HTML elements with minimum lines of code. jQuery supports this via a nice "selector" API that allows developers to query for HTML elements, and then apply "commands" to them. One of the characteristics of jQuery commands is that they can be "chained" together - so that the result of one command can feed into another. jQuery also includes a built-in set of animation APIs that can be used as commands. The combination allows you to do some really cool things with only a few keystrokes."


The jQueryScript Custom Part

The download for this article is a cool custom part that makes adding jQuery scripting to any NetCommunity page a snap. When placed on a page or template the part automatically attaches links to the jQuery and jQuery UI script libraries to the NetCommunity page. In addition, the part editor provides a textarea for creating and editing the jQuery script you want spit onto the page. It also provides a slick way to attach jQuery themes (CSS and images that you can create using the jQuery ThemeRoller) to your page, so that you have complete control over the style of the jQuery UI elements you chose to use, such as popup dialogs, tabs, and accordians.
 

What would I do with jQuery? 

This is the fun part.
 
jQuery has a very elegant syntax for locating elements on a web page and allowing you to manipulate them in countless ways. You can add new elements, remove, move, hide, show, and restyle existing ones. You can also add interesting effects, like sliding or fading in and out. jQuery also provides a very clean model for hooking into the events of any page element, such as the click, hover, resize, and focus events.
 
That's great Mike, can we see an example?

Example: A Better Weblog Display

One practical use I found for it was to reskin the NetCommunity Weblog part. The Labs website uses weblog parts throughout as a means to store, display, and RSS feed most of its content. We have a weblog part for each major area of the site, for now that's NetCommunity, Infinty, and Other Stuff. The weblogs are used primarily as a table of contents for each of the articles on the site (which are actually Article Custom Parts - we'll cover those in another post). Ideally, the weblog stories are short abstracts of the full article, but even so the weblog part can get very long on a page very quickly as it renders the content of every story one after the other. It is very difficult to tell where one story ends and another begins.
 
There are two approaches to solving this basic problem with the built-in weblog part.
 
The first solution you can see on the Labs home page, where we've built a new custom part that consumes each feed and renders just the titles as links to the actual articles. This is cool in itself and we'll share that code and blog about that another day, but it doesn't use jQuery so that brings us to the second approach.
 
With the second approach, using a little jQuery script we can completely manipulate the default layout and behavior of the weblog part to render something totally different. Check out these two very different version of the same page:
See how the first version renders all of the blog stories in a continuous page as long as your leg?
 
 
Now, see how the second version reduces the blog content down to just the story titles and as you click on each one it opens the story in-place in a pretty window, automatically closing the previous open story, providing a close button, and injecting a read more link into the bottom of the reading pane. 
 
 
 
That's cool.
 
Once you have the jQueryScript custom part installed you can use techiniques like this all over your site to change the default look, content, and behavior of any parts and pages using simple jQuery, without having to build specific custom parts. So let's take a look at how its done.
 

Using the jQueryScript Custom Part

The part is actually pretty simple. It automatically attaches the jQuery script files to what ever page it is placed on and provides a place to create and edit your own jQuery inline page script to execute when the page loads.

Click here to learn all about jQuery and quicky get started using it
.
 
To have your script execute when the page loads you start with something like this:
 
$(document).ready(function(){
    // your script here
});
 
This function will execute on each page load and is where you hook in your script to monkey with the page.
 
At the heart of jQuery is the idea of selecting something on the page and messing with it. You can select an element by its tagname, its DOM id, its class name, its location, and several other ways. To create the blog script on Labs, I used Firefox and Firebug to inspect the classnames that are slapped on the various elements of the weblog part.  I then selected the various elements of the blog output and rearranged a few things, and inserted others.
 
Here is my entire script (the download has a text version of it for your consumption
 

var lastOpenStory=null;
var lastOpenStoryContainer=null;

$(document).ready(function(){
    ToggleNews();
});


function ToggleNews (){

    $(".NewsChannelItemDesc").hide();
    $(".NewsChannelItemDate").hide();
    $(".NewsChannelItemTitle").prepend("<img src='document.doc?id=193' style='margin-right:5px;' />");

    $(".ChannelStoryEditButtonWAI").parent().parent().toggle();
    $(".NewsChannelItemPending").removeClass("taLeft").css("text-align","right");
   
    $(".NewsChannelItem").css("margin-top","3px").attr("open", "0");
   
    $(".NewsChannelItem").click(function(e){

        if ($(this).attr("open") == "0") {
            e.preventDefault();
        }

        CloseStory();

        //mark clicked story as open
        $(this).attr("open", "1");

        lastOpenStoryContainer = $(this);
        lastOpenStory = $(this).find(".NewsChannelItemDesc");

        //show story
        $(lastOpenStory).toggle();

        // hilite the title row
       $(this).find(".NewsChannelItemTitle").parent().css("background-color", "#e5f6d1");

        // style the story
       $(this).css("border", "1pt solid #e5f6d1").css("padding", "8px");

        //add close button
        $(this).find(".NewsChannelItemPending").append("<img src='document.doc?id=192' id='btnStoryClose' />");
        $("#btnStoryClose").click(function(e){CloseStory();});

        //show edit/delete buttons
        $(this).find(".ChannelStoryEditButtonWAI").parent().parent().toggle();

        //add more link
        var href= $(this).find(".NewsChannelItemTitle").find("a").attr("href");
        if (href){
            $(this).append("<tbody id='divStoryMoreLink'><tr><td colspan='2' style='text-align:left;'><a id='anchorStoryMoreLink' href='about:blank'>Read the full story</a></td></tr></tbody>")
            $("#anchorStoryMoreLink").attr("href", href);
            $("#divStoryMoreLink").find("td").css("border-bottom","3px solid #e5f6d1");
        }

    });

    function CloseStory(){
        if (lastOpenStory){

            //close last story and clean it up

            $(lastOpenStory).toggle();

            $(lastOpenStoryContainer).attr("open", "0");
            $(lastOpenStoryContainer).css("border", "0").css("padding", "0px");
            $(lastOpenStoryContainer).find(".NewsChannelItemTitle").parent().css("background-color", "#fff");
            $(lastOpenStoryContainer).find(".NewsChannelItemPending").find("#btnStoryClose").remove();

            $(lastOpenStoryContainer).find(".ChannelStoryEditButtonWAI").parent().parent().toggle();

            $("#divStoryMoreLink").remove();

        }
       
        lastOpenStory=null;
        lastOpenStoryContainer=null;

    }


    //add mouse over effects to stories
    $(".NewsChannelItem").hover(
        function(){
           $(this).css("cursor", "pointer");},
        function(){
           $(this).css("cursor", "default");
        }
    );
}

 
As the page loads I'm hiding every story's content by selecting and hiding every element with the NewsChannelItemDesc classname:
 
    $(".NewsChannelItemDesc").hide();

since this is the class assigned to each story's content.  I then assign a click handler to each story which will toggle the display of the story content. This sample also shows some other cool things you can do, like adding addtional elements to the page. See how each story title gets a little orange document image placed in front of it:
 
    $(".NewsChannelItemTitle").prepend("<img src='document.doc?id=193' style='margin-right:5px;' />");

and maybe this is a nice seque into how the part also helps you theme your jQuery content, in this case the little document image you see is from a jQuery theme I built with the jQuery Themeroller.
 

Themes and the NetCommunity Document API

Since jQuery is very much about document partying it makes sense that it has a rich set of methods that allow you to go nuts with CSS. jQuery also has a very nice set of UI components such as Tabs, Accordians, and Dialogs, all of which can be themed using predefined CSS classes. Typical with these types of UI elements is the need to reference images in the CSS.  If you play with the ThemeRoller site you'll see that it lets you build your own themes then generates images for you with the colors you chose and creates a CSS style sheet that uses the images. These images are typcially used for styling the jQuery UI controls for button backgrounds, resize handles, toggle glyphs, and so on. 
 
When you've built your theme, it then lets you download it as a zip file. The zip contains the CSS file and all the associated images. So how can we easily use that on our NetCommunity site?  Why the Document Library API of course!
 
The doument libarary APIs expose access to the document storage and retrieval engine within NetCommunity, the same model used by the Document Parts. Documents of any kind may be stored in NetCommunity and retrieved by a URL (e.g. http://labs.blackbaud.com/netcommunity/document.doc?id=14) or in code, using the API. The API supports adding, replacing, deleting, listing, and retrieving documents in a given library.
 
You can have multiple libraries on your site, and libraries are created by creating a Document Part. Each Document Part has it's own library, and each libary has its own role-based security rules - who can upload, access, etc.
 
The Document Part is the default presentation of a libary on your site, and it is rather basic and visually inflexible, but through the API you can get creative and present document libaries and even use documents in new and more interesting ways.
 
Documents are stored in the database and their access and retrieval is very well optimized, they employ caching on the web server to save trips to the database and they play very nicely in the browser caching model to save on uneeded downloads, just as the image library does. Documents can be of practically any mime type, meaning they can be just about any kind of file, such as Microsoft Offie docs, CSS files, jScript files, PDF, XML files, image files, video, or just plain text files, etc.   The NetCommunity document model, is essentially a virtual file system.
 

So How Does this Help Our jQuery Story?

To add support for using jQuery themes we need the ability to store and reference the theme's CSS file and the images it references. One approach would be to copy and paste the theme CSS into a standard NetCommunity stylesheet and upload the images into the image library. We would then need to modify the CSS so that the image url() references it contains would point to the uploaded image library files:
 
background: url(images/272626_40x100_textures_03_highlight_soft_20.png)
 
would need to become something like:
 
background: url(http://labs.blackbaud.com/NetCommunity/view.image?id=272)
 
Needless to say it would take a while to do this with the 50 or so images that exist in a jQuery theme. So here's a more convenient approach that the jQuery Custom part takes by using a document library to hold the CSS file and related images:
  1. Upload the theme zip file created on jQuery ThemeRoller site
  2. Crack the zip file using the Zip routines in ICSharpCode.SharpZipLib which ships with NetCommunity
  3. Add each image file into the Document Library of the users chosing
  4. Update the CSS file to use image Urls that reference the document.doc?id=x Urls for each added image
  5. Add the modified CSS file to the selected Document Libary
In the jQuery Custom Part's display control, we attach the CSS file to the page using:
 
Dim Url As String = Me.ResolveUrl(Me.API.Navigation.GetDocumentURL(id))
Me.API.Pages.CurrentPage.AddStylesheet(Url)
 
For the user, who in this case is the jQuery author/site designer, they just need to create a Document Part to hold the CSS file and images, and have at it. The custom part supports adding as many themes/images as you like, and chosing which one to use at display time. So one library could hold all your themes and each unique instance of the jQuery part on different pages could reference its theme of choice.
 
And of course once you've uploaded theme images into a document library your free to use them yourself in other content, which is what I did in my sample jQuery script above, to add the little orange document image  (which I'm also using here!).
 
You could make the case that if the image library had similar methods in the API for add, edit, delete it would be a better place to keep the images for consistency sake. Maybe one day it will, but for now document libraries work very well and under the hood are implemented identically to the image library so there is no performance penalty. You could also make the case that its nice to keep the images and CSS together and not overload the image gallery. I need a good rationalization every day.
 

Embedded Resources

So the jQuery part allows you to create page and template level jQuery script that executes on page load, tap into jQuery themes, and monkey at will with the page containing the part and all of the parts on it. But what it you wanted to use jQuery in your custom part's editor control? Our model so far has been to put our themes into a document library, but I do that step in the editor, so I have chicken-egg problem if I want to theme my editor.
 
For this I use ASP.Net embedded resources. I used ThemeRoller to build the theme I want to use in my editor control, then added all the generated images to my project, marking each as an embedded resource. I then took the CSS file from themeRoller and modified all of the image url references to use WebResource urls to get the images as embedded resources, so:
 
background: url(images/272626_40x100_textures_03_highlight_soft_20.png)
 
now becomes:

background: url(<%=WebResource("272626_40x100_textures_03_highlight_soft_20.png")%>)
 
I'm not going to get into the details of using embedded resources, since there is plenty of help on that subject available online.
 
Since converting 50 plus image references in to WebResource links is as tedious as adding them to the image library, I created a WinForm utility ThemeRollerEmbedder available in the solution in the download that will take a ThemeRoller zip file, crack out the images, and create a new CSS file with the WebResource urls. It also creates the <assembly: webresource > attribute needed for each image in a seperate file you can add to your project.  Run the utility on your themeRoller file, put the images, css file, and assembly attribute file in your project and be sure to mark the css and image files as embedded resources and your good to go.
 
Take a look at the code-behind for how the embedded resources for the CSS, and jQuery script files are used. Pretty cool way to deploy dependant files in your custom parts.
 
That's if for now. Have fun with jQuery, I think maybe now I'll use it to build a nicer Document Part display.
 
 
 
Single Sign On with NetCommunity
Monday, November 10, 2008
NetCommunity 5.5 introduced a built-in Single Sign On model that you can use to authenticate requests coming in from other systems against a NetCommunity user account. If you know the user's NetCommunity username you can fly the user into the site, authenticating a new session for that user.
 
The model uses the concept of a secret key known only to the NetCommunity web site and the calling system. The key is encrypted onto the url with some additional information, including a timer value that sets the number of seconds for which the url will be accepted from the time it was created.
 
To setup a calling system, you create a new secret key in the System Options page:
 
 
  • Description: Anything you like that helps you keep track of this entry
  • Shared Key: A secret key that only you and the incoming system know. This is any string value. A long combination of letters and numbers is a good bet.
  • Querystring Parameter names. These are the variable names of the 3 parameters that the other system will be passing in on the URL for the following values
    • Username - plain text username
    • Time - epochtime when the Url was created (the time in number of seconds since Jan 1, 1970)
    • MD5 Hash - MD5 hash of the concatention of Secret key, Username, IP address, and epochseconds
  • Expiration - number of seconds after the Time value to expire the Url
  • Include IP - check this box if the hash contains the known caller's IP address
Once a calling system is defined as above,  it is up to the caller to build a URL that matches this definition. If a secured page or login page in NetCommunity is hit with a valid Url the session will be created with the provided username.
The accompanying sample code, demonstrates how to create a valid SSO url for a calling system you've setup this way, in your System Options. To test this, define a system as above, create a page not viewable to the Everyone role.
 
Run the sample app which is a single web form:
 
 
Fill in the Url of your secured page, use a username that is in a role that does have view rights to the page, and specify your secret key. Click the Create Url Button to generate a Url. Click the Url to test it. The Url will expire based on the number of seconds you defined for this calling system.
 
 
 
I Really Need Some REST - Custom Handlers in Action
Wednesday, August 27, 2008
 
After digging in one recent evening to figure out how to build a Facebook Canvas application using NetCommunity, it became clear to me that we needed a way to write REST based custom code in BBNC. One NetCommunity 5.5 patch later and now we can and so can you!
 
Introducing Custom Handlers to the NetCommunity API, enabling full control over the request/response model of a NetCommunity web site.

Add to this the ability to access any Blackbaud API (e.g. RE, BBEC, FE, EE and BBNC)  from a custom handler and watch the Faces turn...  

Creating An Accessible UL Based Menu
Tuesday, August 26, 2008
This article is a follow up to the article posted on July 18th,  Exploring the API: Building a Custom Menu in which Mike Andrews presented a customization that leverages the new API in order to build a light weight, unordered list based menu while still maintaining the built-in page security model of NetCommunity.  In this article I am going to highlight a few more of the new API methods while I take the menu a step further and allow for 'skinning' and the ability for the menu to drop down and fly out!  I also add accessibility functionality to the menu.  The complete source for this project (C#) can be downloaded at the end of this article.  
  
Mike did a great job in the first article of explaining the API as it relates to menu content, I'm not going to rehash that article.  I'm going to mostly touch base on the additional functionality and the API functions that were required.  I'll also briefly touching on the .js required to support cross browser drop downs and keyboard accessibility.
 
At this point I would suggest you download the attached source code and compile the project. Don't forget to update the reference path in the project to point to your NetCommunity\bin folder so that it can find BBNCExtensions.dll.  You may also need to edit the DefaultDeploy.cmd file to point to your NetCommunity installation. If you've never built and deployed a custom part, I strongly recommend reading this first.
 

Skinning the menu 

 
One of my goals for this project was to allow for the menu to be 'skinable'.  For an HTML based project skinning is basically the application of different images and CSS to the HTML.   I have done this in a manner that allows the end user to simply select the name of the provided 'skin' (css file) from a drop down list on the edit part.  In order to accomplish this I needed to be able to dynamically add stylesheet links to the page that the part resides on.  Fortunately the API now allows for that, the code snippet below will demonstrate this. 
 
 

//The API now allows you to add custom stylesheets to a page.  This CSS path resolves to a css file that we create

//and using our deploy script deploy to the our custom folder structure that we create under the NetCommunity/Custom folder

//The edit part for this sample allows you to select a CSSClass name for the menu's root.  Here we use the same name

//to associate to a CSS file.  This allows different menus, perhaps on the same page, to be styled differently

this.API.Pages.CurrentPage.AddStylesheet(this.Page.ResolveUrl("~/Custom/NCC.Menu/css/" + mContent.RootMenuClass + ".css"));

 
 
As you may infer from the API call above, the API determines the current page and dynamically inserts the resolved stylesheet path into the page header.  In the example above, mContent.RootMenuClass is the name of the skin as stored in my part's content object,  and refers to the user selected skin name.  I use this to dynamically change the class names in the menu HTML.  I have provided 14 'skins' to get you started, the css and images for the skins are deployed by the included .cmd file and are located in the img and css directories of the project.
 
 

Javascript for the menu

 
I don't want to get too much into the SuckerFish methodology in this article since that is aptly described here however I do need to touch briefly on the core idea behind suckerfish in order to illustrate a point.  In order to provide cross browser compatibility for a UL based menu the suckerfish method relies on tricking Internet Explorer by adding a  pseudo hover class to all of the LI elements in the menu.  The :hover pseudo class should work with any element, but in Internet Explorer it only works with links thus the need to add it using DOM based scripting. 
 
The .JS file included with this example is a standard suckerfish .js function adapted so that we can support multiple menus, each with it's own style if necessary (a horizontal and vertical menu on the same page perhaps).  As you can see from the code snippets below, the RegisterClientScriptInclude call adds the link to the custom .js file that we provide with the customization and the RegisterStartupScript passes the RootMenuClass name of the current custom menu to the function in order to limit the LI nodes that we attach the psuedo hover class to as the partial .js snippet below shows:
 
 
ScriptManager.RegisterClientScriptInclude(this.Page, mULRoot.GetType(), "nccUlMenu", this.Page.ResolveUrl("~/Custom/NCC.Menu/js/UlMenu.js"));
 

Page.ClientScript.RegisterStartupScript(mULRoot.GetType(), "OnMenuLoaded" + mULRoot.ClientID, String.Concat("OnMenuLoaded('", mContent.RootMenuClass, "');"), true);

 
One of our design philosophies here in the product development group is that we do not want to wrap calls into the API that can be achieved in a more standard way.  An illustration of this is that we do not have any script registration calls exposed via the API.  This example uses the standard ScriptManager or Page.ClientScript calls to register the necessary .js file and startup scripts:
 
 
 
 This is a partial snippet of the standard suckerfish .js function, this function searches for all <li> elements within the menu's HTML structure and adds a class "sfhover" which can then be addressed in the CSS to provide for pseudo hover capabilities.  On line 1 below, className is the 'skin name' (RootMenuClass) that allows the js function to only iterate the nodes that belong to this particular menu instance (we don't want to add "sfhover" to every <li> on the page!
 
 

function OnMenuLoaded(className) {

 

        var mcAllUls = document.getElementsByTagName("UL");

        for (var iA=0; iA<mcAllUls.length; iA++) {

            if(mcAllUls[iA].className == className ){

 

                //attach sfhover class to elements

                var sfEls = mcAllUls[iA].getElementsByTagName("LI");

 

                for (var i=0; i<sfEls.length; i++)

                {

                    sfEls[i].onmouseover=function()

                    {

                        this.className+= (this.className.length>0? " ": "") + "sfhover";

                    }

                    sfEls[i].onmouseout=function()

                    {

                        this.className=this.className.replace(new RegExp(" sfhover\\b"), "");

                    }

                }

 
 
The complete Javascript file is included with the project in the /js folder.  The file is small (3kb) and not shown above is a section that adds functionality for cross browser keyboard navigation, a requirement for an accessible menu.
 

 Changing presentation based on a User's role

 
Another use of the API in my enhanced menu is to allow an edit button to surface on the menu if the currently logged in use is an administrator.  This is admittedly not all that important as you could simply click on the NetCommunity part edit button to essentially end up at the same place however it does add a certain 'skinnability' to the part, allows you to change the skin without clicking on 'edit this page', and also illustrates a potential use for another API function.  Using the code below:
 
 

if (this.API.Users.CurrentUser.IsAdmin)

 
 
I check to see if the current user is an admin.  If the user is an admin I then use the new PartTypeInfo class to query NetCommunity for the part type ID which is needed in order to spawn a PartEditButton.
 
 

///<summary>

/// This uses the API's Parts class to get the PartTypeID by name, this is needed by the PartEditButton server control

/// this in effect wraps up something like "SELECT ID FROM dbo.ContentTypes WHERE (Name = '?')

///</summary>

publicstaticint GetPartTypeId(string partName)

{

    BBNCExtensions.API.Parts.PartTypeInfo oPartTypeInfo = BBNCExtensions.API.NetCommunity.Current().Parts.GetPartTypeInfoByName(partName);

    if (oPartTypeInfo != null)

        return oPartTypeInfo.Id;

    else

        return 0;

}

 
 
Once I have the part type ID the following code creates one of the newly exposed PartEditButton server controls and adds it to my menu.
 
 

//dynamically create a PartEditButton API servercontrol

BBNCExtensions.ServerControls.PartEditButton peb = new BBNCExtensions.ServerControls.PartEditButton();

peb.CallbackFunctionName = "myToolBarEditCallback";

peb.CallbackContext = "editskin";

peb.Text = "Edit";

peb.Visible = true;

peb.PartId = this.Content.ContentID;

peb.PartTypeId = iPartTypeID;

peb.Attributes.Add("style", "font-size:9px; font-family:verdana;color:#333;border:dotted 1px #000;background:#eee;vertical-align:baseline;text-align:center;");

 

//add inside an <li> to make it sit on toolbar nicely at the end

HtmlGenericControl li = newHtmlGenericControl("li");

li.Controls.Add(peb);

li.Attributes.Add("style", "vertical-align:baseline;text-align:center;");

 

mULRoot.Controls.Add(li);

 
on the last line I add the <li> that contains the PartEditButton server control to the menu's controls collection.  This results in the button being displayed as the last item in the menu (at the root level).
 
 

Accessibility

I touched on keyboard accessibility in the section about the Javascript.   In order to be fully compliant we need to do more than simply add the ability to navigate the menu using the keyboard.  We also need to allow for the ability to SKIP the menu if desired.   The keyboard navigation tabs through every item, sub item, sub sub item, etc. in the menu.  As you can imagine it would be quite annoying to have to tab through a large number of menu items to get to, for instance, a simple login form.  In order to avoid this we add a skip link before the menu and a skip anchor after the menu.  This allows someone to select the skip link which will in effect hop them over the menu to the skip anchor where they can continue tabbing to the screen element they desire.
 
Look for this region in the source code to see where I add the skip link and anchor:
 
#region accessibility

 
 

ScreenShots

 

5 menus with different skins on the same page:

 
 
 

Expanded Menu:


 
 
 

Vertical Menu:

 
 
 
 
Please feel free to get back to me with any feedback or suggestions for improvements or other articles / topics you would like to see covered in the future.  tom.demille@blackbaud.com
 
 
 
 
Exploring the API: Building a Custom Menu
Friday, July 18, 2008
If you've started reading up on the ton of new stuff that's been added to the open API in NetCommunity version 5.5, you may have noticed a number of new methods that expose programmatic access to the menu parts of your web site. One of the product areas we get frequent requests for enhancing is the menu part. Menus are like noses, everybody has one, and they all do the same thing, yet everyone of them looks just a little bit different. In this article, we are going to explore a custom part for creating a simple menu that taps the built-in page security model of NetCommunity, yet gives you absolute complete control over it's look and behavior.
 

Menu Challenges

Making a menu part built into the system that can look and behave in so many different ways has always been a challenge. In earlier versions of the product, we used a menu that, while offering the dynamic popup and styling flexibility clients demanded, lacked the required accessibility features to gracefully downgrade and still work, without javascript or CSS enabled in the browser. In version 5.5 a new menu part type has been added, Menu 2.0, that is based on the Microsoft ASP.net menu control that ships with .net. While it solves most of the accessibility issues, and adds keyboard support, it still leaves some features to be desired, particularly around controlling it's down-level behavior and appearance in less capable browsers, such as those needed for accessibility and those found on mobile devices like Blackberries, Windows Smartphones, and iPhones.
 
If a menu is really just a list of links to other pages, why don't you just build menus in NetCommunity with a Formatted Text part and call it a day? Then you could use what ever personal HTML markup preference you may have (tables vs. unordered lists vs. divs  vs. flash and so on).  You can even sprinkle in some Javascript and CSS, and Bob's your uncle. You certainly can do this, but what you would miss is what NetCommunity really adds to the equation: Page Security and Targeted Content.
 

How NetCommunity Helps

NetCommunity has this wicked concept that you can assign viewing rights to pages based on what roles the person looking at the site is a member of. These roles can be based on practically anything you know about them in your CRM. The roles you define can be based on your CRM Queries and can be as dynamic as you desire. So if you're a Gold Member you can access the Gold Member pages, or if you are a Big Donor the Donate Now menu item takes you to the Big Donor donation page vs. the little donor donation page and so on. With so many potential combinations of secured and targeted pages building a menu system manually would become very complicated, if not impossible.
 
So, NetCommunity's menu part handles the tricky bit. You can define one menu with all possible links in it, and based on who's looking, the menu only renders the items that link to pages the user has the right to see. A logged in Gold Member would see more links in the menu than a casual anonymous visitor to your site, but you only had to define one menu and one part.
 
But there's the rub. We want to build our own menu but we need NetCommunity to help us manage its contents dynamically. Enter the new Navigation methods of the API.
 

The Menu API

The way it works is that you still use the built-in menu part to define your menu, since it has a nice UI for building the hierarchical menu tree, grouping and nesting items, selecting links to internal pages, external URLs, documents, and special pages like your login page. The Menu part also handles the database storage of your menu and an optimized and cached retrieval of your menu from the database, with security in mind. Another cool thing the menu does is automatically exclude a link to your login page, if the user is already logged in. So we want to keep the built in menu.
 
Once you have a built-in menu created, you can use the API from within a custom part to retrieve it's definition as an XML document. This document will only contain the menu items that the user requesting the document has the rights to see. Once you have this document, your custom part can build and render your own menu in any fashion it sees fit.
 
There are two ways (methods) to get the XML document from the API.
 
  1. API.Navigation.GetMenuXML(MenuPartId) - this version simply returns the XML document as a string, passing in the id of the menu part whose data you want. You would then typically load the document with something like:

    Dim xmlDoc AsNew XmlDocument

    xmlDoc.LoadXml(Me.API.Navigation.GetMenuXml(MyContent.MenuPartId))


  2. API.Navigation.GetMenuXMLUrl(MenuPartId) - this versions returns a fully qualified URL from your NetCommunity server that will return the XML document. This is very handy if you want to get your menu data from within a Flash or Silverlight menu implementation for example.


Menu XML

So what does the menu XML look like?
 
Using the second method above, I got this url to retrieve the menu XML for the main left navigation menu on this website, so that you can check it out:
 
 
Go ahead...click on it. You should get back an XML document that contains a recursive list of <menuitem> tags.
 
Notice that each menu item and the hierarchy is represented and that only items you have rights to see are included.  Also note that none of the styling you may have specified in the built-in menu is represented in the document. The idea of course is that we don't care about that, since we will be doing a custom rendering.
 
What you do get for each item is:
  • active - meaning the page this item is linked to is the page you are currently on
  • target - the name of any target window you specified for this link
  • caption - the menu item text
  • tooltip - the tooltip you specified
  • pageid - the id of the NetCommunity page if this item is linked to an internal page, otherwise 0
  • tabid - some internal pages have "tabs" (e.g. login, fundraiser) so you can link directly to the different stages of the certain parts that are on them, like straight to the new user sign up form of the login page.
  • url - the fully qualified url to the linked page built for you

That's all there is to it.

Building a Different Menu 

The menu we are building in our sample is actually the one you see on the left side of this page. For our demo we decided we wanted to build a very simple menu with the following characteristics:
  • <ul><li> based - using strictly an html unordered list of anchor tags, creating sub lists where appropriate to keep the hierarchy represented.
  • CSS based - the elements are all styled with some crafty CSS.
  • No Javascript required
  • No dynamic pop-out sub menus
  • Graceful down-level rendering for accessible and mobile browsers
As a point of reference we decided to follow much of the advise on building UL menus from a common implementation called Suckerfish. This website has some excellent articles on HTML, CSS, and is well worth perusing, so definitely check it out.
 
At this point I would suggest you download the attached source code and compile the project. Don't forget to update the reference path in the project to point to your NetCommunity\bin folder so that it can find BBNCExtensions.dll.  You may also need to tweak the BuildEvents commands in the Compile properties of the project to point to your NetCommunity installation. If you've never built and deployed a custom part, I strongly recommend reading this first. 
 
As you can see the implementation is very simple. Essentially, in the part editor of our Custom Menu, the user selects a built-in menu part that they want to render. In the display control for our part, we load the xml for the selected menu, walking its <menuitem> nodes recursively, spitting out <ul>, <li>, and <a> tags as appropriate.
 
The entire menu is rendered within a <div> tag that gets a CSS class assigned to it whose name you decide, in the part's editor. This is important because if you were to place multiple custom menu parts on the same page, you would want some way to apply different CSS to each. Having a containing <div> with a unique classname allows us to handle this in the CSS.
 
So, if you look at the emitted HTML source for our Labs menu you will see it essentially looks like this:
 
<div class="LeftMenu1">
   <ul>
      <li>
         <a href="...">
         <ul>
            <li>
               <a href="...">
            </li>
         </ul>
      </li>
   </ul>    
</div>
 
Pretty simple. The fun part is styling it. Which we do in the custom section of our labs stylesheet. Note that if CSS is not enabled in the visitor's browser, the default rendering of this HTML is an easy to read indented hierarchal list of links.
 

Style It

Let's take a look at the styles we added to our labs stylesheet to format our menu.
 
For our containing <div> tag, we gave it a class name (LeftMenu1) in the part editor so we can style it: 

.LeftMenu1  {
        border: 1pt solid #ccc;
        margin: .5em;
        width: 12em;
        padding: 5px;
    }
 
Note how every subsequent class selector is preceded with the class name we gave our <div> in the Custom Menu part editor.
 
Here are the styes for the outer most <ul> list:

.LeftMenu1 UL {
            color:#90ba33;
            padding-left:1em;
            margin-left:0;
            list-style:none;
}
 
See how list-style:none removes all bullets from our lists. Again, check out the articles found here for some great ideas on styling lists. 
 
Here are the defaults for our main menu links:
 
.LeftMenu1 A:link,
.LeftMenu1 A:active, 
.LeftMenu1 A:visited{
            text-decoration:none;
            color:#90ba33;
}

.LeftMenu1 A:hover {
            text-decoration:none;
            color:#90ba33;
            border-bottom:1pt dotted #14b1d0;
}
For styling the second level of links we use some fancy CSS selectors like this:
 
.LeftMenu1 li ul a:link {
   color:#888;
}
 
This isolates "all A tags, under a UL tag that is under an LI tag that is inside our DIV" meaning: the second level of anchors in our menu. Which is why you see them all gray.  You could use this approach to style uniquely to any depth in the menu.
 

The Current Active Item

Finally worth mentioning is how we style the "current item", in other words the menu item that represents the page we are currently on. See how the Home item is highlighted when we are on the home page?  This takes a couple of steps.
 
First, in the source code we need to determine as we walk the menu XML nodes, when we've hit an item that is a link to the current page. Check out the GetCurrentPageNode function in the CustomMenu.ascx.vb class. It executes an XPATH query on the menu XML to locate the first node that has a pageid attribute equal to the current page id.  We can get the current page id from the API using me.API.Pages.CurrentPage.Id.
 
 

    PrivateFunction GetCurrentPageNode() As XmlNode

        'find node in menu tree that has a link to the page we

        'are currently on. There may not be one - since current page may

        'not be represented in the menu. If there is one, we want

        'to give the menu item its own class - so user can

        'highlight it if they want to.

        Dim ns As XmlNodeList = mXmlDoc.SelectNodes( _

            String.Format("/menu//menuitem[@pageid={0}]", _

                          Me.API.Pages.CurrentPage.Id))

        If ns.Count > 0 Then

            Return ns(0)

        Else

            ReturnNothing

        EndIf

    EndFunction

 
As the nodes are walked and the <LI> tags are built, we look out for this node and add the MenuItemActive class to it. So in the CSS we can isolate these items with something like:
 
.LeftMenu1 .MenuItemActive {
   border:1pt solid #14b1d0;
   color:#fff;
   background-color:#14b1d0;
}
 

.LeftMenu1 .MenuItemActive:link,
.LeftMenu1 .MenuItemActive:visited,
.LeftMenu1 .MenuItemActive:hover,
.LeftMenu1 .MenuItemActive:active{
    color:#fff;
}

Since we've been so specific on the sub level items already, we have to do the same thing for the active sub level item too:

.LeftMenu1 li ul .MenuItemActive:link,
.LeftMenu1 li ul .MenuItemActive:visited,
.LeftMenu1 li ul .MenuItemActive:hover,
.LeftMenu1 li ul .MenuItemActive:active
{
    color:#fff;
}

That's basically it for how the menu is built and styled. I would encourage you to play with the hundreds of interesting style tricks you can apply to lists. In a subsequent post we may take a stab at adding some popout capabilities, using interesting combinations of CSS and Javascript.

PartSelect and PartEdit Server Controls 

Before we part, we've used two new powerful Server Controls that can be found in the NetCommunity v5.5 API that are worth explaining, The PartSelectButton and the PartEditButton.
 
If you take a look at the Custom Menu part editor you'll see these two buttons:
 
 
 
These handy controls can be found in BBNCExtensions.dll. Take a look at the CustomMenuEditor.ascx file to see how we reference the dll with a  <@Register> tag. So that we can use them in the markup. 
 
The PartSelect button renders a button that when clicked summons the modal NetCommunity dialog for searching for and selecting a part. The PartEdit button summons the NetCommunity modal dialog for editing a specific part. Since, any part's editor may be summoned this way it offers some interesting ideas for building parts that party on other parts.  Each control follows a pattern of calling a javascript function of your choosing, when the dialog is closed. You specify the function name in the CallbackFunctionName property of each control.  Each callback will be called with two arguments, a context string that you provide on the button (via CallbackContext property) and a returnValue object.
 
The CallbackContext allows you to have a common callback function but know which button called it. The returnValue object differs based on the button.
 
For a PartSelect button the returnValue object has two properties:
  • partId
  • partName

For the PartEdit button the retValue is a string which is either "SAVE" or "CANCEL", indicating the user's action to close the part editor's dialog.

The PartSelect button has FilterByPartType property that takes an integer that is the NetCommunity id of a part type, to only allow selecting parts of a specific type. Look here to find the ids for all of the build in part types.
 
The PartEdit button has a PartId property that is the id of the part instance you want to edit. Any part can be edited from this button, but you will also have to specify the PartTypeId for this button as well.  For convenience, all Part Property dialogs in the system now show you the part's id.
 
 
It is up to you to handle a part being selected and passing that info back to your custom part. In the Custom Menu sample, we used hidden elements to store the selected part id and name, for passing it back to the server on the postback.  We then stash the menu id, into our Custom Menu's content object so we'll have it at display time.
 

Summary

That's probably good for this installment. We've covered how to get access to the menu data for any built-in menu, how to use it to render a simple yet useful and highly accessible menu, how most noses menus are different, we touched on some interesting CSS, and finally covered how to use two new server controls available in the NetCommunity Open Platform Release shipping with version 5.5.
 
This is a certainly a simple example, and there is much room for enhancing it, so stay tuned for more samples as we discover what's new in NetCommunity v.5.5.
 
NetCommunity Open Platform Release
Wednesday, July 09, 2008
As NetCommunity version 5.5 is getting wrapped up by the Products team for release, it's a good time to get an introduction to the new open platform API features that will be released with it. Lot's of effort has been put into expanding the API into new areas of the platform, primarily around giving the Custom Part developer much more access to core platform features like RSS feeds, Document Libraries, built-in dialogs, donations, user accounts, and programmatic part creation, to name just a few.
 
To coincide with this expansion, we've added lots of new content on the labs web site.
 
This link is where you'll want to start: NetCommunity Open Platform. But here are some of the highlights:
 
Learn how to extend your Blackbaud NetCommunity system using the NetCommunity Open Platform APIs for creating custom parts, integrated web services, and custom transactions.
 
See what's new in version 5.5 of the NetCommunity Open Platform. This is a good place to get an overview of what's changed, what's new, and what's possible, whether you are new to NetCommunity API programming or a seasoned veteran.
 
Check out these samples to help you get started with building NetCommunity custom parts. Start with "Hello World" and progress upwards into advanced topics like Custom Web Methods and Custom Transaction Processing. We've updated the samples to include Visual Studio 2008 versions that target NetCommunity v5.5. The 5.1 versions are still available here, also.
 
We're very excited about the growth of NetCommunity as an open platform and several of us here can't wait to post some pretty cool samples that exercise the powerful stuff added in NetCommunity v5.5. So stay tuned!
 
Drinking Our Own Champagne
Wednesday, May 07, 2008
Some call it eating your own dog food, but that's a little rough (sorry). We prefer to call it, Drinking our own champagne. Blackbaud Labs has been ported over to run entirely on the soon to be released Blackbaud NetCommunity 5.5.

We are very excited about the numerous benefits we're going to gain from what turned out to be a pretty seamless and enjoyable move.
 
 
So why did we do it?
 
Lots of reasons really, but here are a few of the big ones:
  • Labs was getting bigger. We needed a better way to organize and manage all the articles and documents that we were posting. Hmmm, where could we get a powerful CMS from...?
  • More people were wanting to post stuff, so we needed an easier way to support contributors, both inside and outside of Blackbaud Product Dev. and even outside the company.
  • The BBNC team has been working double time to get v5.5 ready and a big part of that release is a massive expansion to the BBNC API. And since Labs is all about things that interest the developer, we needed a way to demonstrate the slew of these new features we've added to the open API. What better way to do that than to see it in action as you read about it?
We have a ton of new articles and sample custom parts to post in the comming weeks so stay tuned to see some cools stuff including:
  • How we built the Labs site itself. It has some interesting custom parts that are worth a look-see. We're going to share all the code, both as a demonstration of the new API(s) and in case there's something here you'd like to download and use on your site.
  • Demonstrations of the various new areas of the API object model
  • Building custom donation forms that create built-in donation transactions
  • Email Acknowledgments with custom merge data
  • Document Management
  • Taping into the RSS feeds
  • Building your own menus (css based)
  • Div/Css based site layouts (labs is layed out this way)
  • Creating user accounts and custom login forms
  • A cool bread crumb control
  • New Server controls in the API for access to selecting pages, parts, and images in your custom part editors
  • API Server controls for WYSIWYG html editing and displaying
  • and much more
One thing to note for returning visitors:

The one RSS feed we had (http://labs.blackbaud.com/rss.xml) has being replaced with multiple new feeds, and since they are BBNC feeds they have different URLs. The old feed will get one more post with links to the new feeds. Info on the new feeds can be found here.

By the way, one cool new feature in 5.5 is that feeds/posts can contain categories so the sorting and filtering of feeds in IE 7 and other browsers kicks in nicely.

That's probably it for now. Have a look around and stay tuned.

Going Mobile with NetCommunity
Monday, April 14, 2008

“I can pull up by the curb, I can make it on the road goin’ mobile. I can stop in any street, invitin’ people that we meet, goin’ mobile…”  - Pete Townshend

Look around the airport, the restaurant, the movie theatre, or the Laundromat, and it’s hard not to find someone surfing the web on their cell phone. With the arrival of the iPhone, the ubiquitous Blackberry, and the slew of phones now running Windows Mobile (aka Smartphones) this trend will likely continue. So much so that the web’s major content providers are tapping into it. Google, Facebook, ESPN, MSN, Yahoo, and everyone else it seems are now offering their content and online tools to the mobile consumer. In this article we’re going to explore just how easy it is for you to target this growing base of web users with your Blackbaud NetCommunity web site.     Read more...

Social Bookmarking Custom Part Sample
Sunday, April 06, 2008

 
This is a basic sample part that lets you add social bookmarking functionality to your site. Make it easy for folks to use bookmarking services like Digg, Facebook, Yahoo, del.icio.us and more.

Watch the Social Bookmarking Screen Cast
NetCommunity Open API Sample Code
Friday, April 04, 2008
As promised in the NetCommunity Open API screen cast, we have posted a number of samples that illustrate how to use the APIs in the NetCommunity arsenal. From "Hello World" to a full blown transaction round trip, this stuff should serve as a good starting point for those looking to dig in. For those of you that have already been using the API over the past few years, these are just updated, scrubbed samples in Visual Studio 2005 format. We'll post some new stuff soon! Thanks to TomO on the NetCommunity team for helping me pull this together.

Read more...
A Look at the NetCommunity Open API
Thursday, April 03, 2008
In this screen cast we'll take a quick look at the NetCommunity Open API. Available since the original release of NetCommunity, this robust, mature API is being employed in production environments by many NetCommunity customers. In this screen cast we'll give a high level introduction to the API and since it's Blackbaud Labs, we'll attach the debugger and step through some code baby! Lastly, by popular demand, we'll reveal road map information on our plans for the NetCommunity API moving forward.
 
Note: the Wiki we were using for documenting the open platform has been replaced with content on the Labs NetCommunity site. So visit here for all of the juicy details.
Watch the NetCommunity API Screen Cast  (Flash format, 20 minutes)
Want to work for a company that loves building stuff with bleeding edge technology? Feel like doing your part in helping to change the world? If you have a passion for technology and you are interested in building applications that power the world's largest nonprofit organizations, you need to come work for us.

 
© 2010 Blackbaud, Inc.