February 20, 2010

Silly me…

I’ve started this blog a few months ago, and just realized 3 days ago why I was getting no comment on my posts: the comments section was not working! Duh! I’ve fixed that, apologies if you’ve been frustrated in the past attending the leave a comment. Try again now.

February 11, 2010

Visualize your SharePoint Calendar with a Dynamic Timeline

[Update Feb 2010: see new blog post for  more advanced version]
I’m a lazy developer. I hate reinventing the wheel. Worse, I like stealing other people’s wheels! I guess that’s why I love JavaScript so much: no need to have access to any server, I can embed my script into this very blog (or into a Content Editor Web Part if I’m using SharePoint). I can point to a JavaScript library other geniuses have invented, and use their fancy functions against some of the enterprise data accessible through XML (RSS, Web services, REST, etc.), and voila! I can impress my boss in just an hour of work! In future articles, I’m going to show a few examples of amazing things you can do in JavaScript and enterprise data.
Starting with this example. Here I’m going to show how to visualize a SharePoint calendar with the SIMILE Timeline widget.
 Timeline
This is what we’re going to use:
  • SIMILE Timeline widget for the visualization.
  • Darren’s SharePoint JavaScript API to access any SharePoint data. Any SharePoint list or document library data can be accessed through a web service, so Darren’s API makes accessing SharePoint data really easy (you can also use JQuery as I will show in future articles, although I like this one better). You will soon find yourself using this API in many scripts, so to improve its availability I suggest you copy Darren’s JavaScript files on your production server.
  • And finally SharePoint calendar data itself. As I said, the events details are accessible in JavaScript through the calendar web service. To make life easier, all you need to provide is the RSS feed associated to your calendar (see bottom of the script). The feed URL contains both the path of your calendar and its list ID, which is all we need to access the web service.
So there you have it. Modify just the few lines where you see “myserver” (i.e. line 7, 8 9 and bottom of the script), insert the script into a Content Editor Web Part, and enjoy!

<script language="javascript" type="text/javascript">
var Timeline_urlPrefix = "http://simile.mit.edu/timeline/api/";
// Include these javascript files only if not loaded already by another web part
// In portals like a SharePoint page, you never know what script might already be loaded by the master page or other web parts
includeJSScript("http://simile.mit.edu/timeline/api/timeline-api.js");
includeJSScript("http://myserver/js/spapi/spapi_core.js");
includeJSScript("http://myserver/js/spapi/spapi_types.js");
includeJSScript("http://myserver/js/spapi/spapi_lists.js");
function includeJSScript(p_file) {
// before we insert this script, we need to check if it already exists
var bAlreadyExists = false;
var scripts = document.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src == p_file) {
//scripts[i] is the one
bAlreadyExists = true;
break;
}
}
if (!bAlreadyExists) {
var v_script = document.createElement('script');
v_script.type = 'text/javascript';
v_script.src = p_file;
document.getElementsByTagName('head')[0].appendChild(v_script);
}
}
// Call the web service associated to the calendar to extract its items
function getCalendarListItems() 
{ 
var lists = new SPAPI_Lists(ExtractWebSiteURL(MyList_RSS_Url)); 
var items = lists.getListItems(ExtractListID(MyList_RSS_Url),'',"<Query><OrderBy><FieldRef Name='EventDate' /></OrderBy></Query>",'',100); 
if (items.status == 200) 
{ 
var rows = items.responseXML.getElementsByTagName("z:row");        
return rows; 
} 
else 
{ 
return null; 
} 
} 
var resizeTimerID = null; 
function formatDateString(strDate) 
{ 
var yearStr = strDate.substr(0, 4); 
var monthStr = strDate.substr(5, 2); 
var dayStr = strDate.substr(8, 2);        
return monthStr + "/" + dayStr + "/" + yearStr + " " + strDate.substr(11); 
} 
// This is one of the most important functions. Once the calendar events are retrieved, 
// we loop through them and add them to the timeline
function main() 
{ 
var items = getCalendarListItems(); 
if (items == null) 
{ 
document.getElementById("my-timeline").innerHTML = "Cannot get items from list <i>" + ExtractWebSiteURL(MyList_RSS_Url) + "/" + ExtractListID(MyList_RSS_Url) + "<i>"; 
return; 
} 
var eventSource = new Timeline.DefaultEventSource(); 
// for each event returned by the calednar web service, we create a timeline event
for (var i = 0; i < items.length; ++i) 
{ 
var ows_EventDate = formatDateString(items[i].getAttribute("ows_EventDate")); 
var ows_EndDate = formatDateString(items[i].getAttribute("ows_EndDate")); 
var ows_Title = items[i].getAttribute("ows_Title"); 
var ows_Location = items[i].getAttribute("ows_Location");
var eventDate = new Date(ows_EventDate); 
var endDate = new Date(ows_EndDate); 
var event = new Timeline.DefaultEventSource.Event( 
eventDate, //start 
endDate, //end 
eventDate, //latestStart 
endDate , //earliestEnd 
true, //instant (use FALSE if events are longer than a few hours of duration
ows_Title, //text 
"<strong>Where? </strong>" + ows_Location + "<br><strong>When? </strong>"  //description that appears in a bubble when user clicks on the event
); 
eventSource.add(event); 
} 
// This is where we define 3 timelines. Advanced users can play with these parameters to use different timeline or timeline behaviors
// See http://code.google.com/p/simile-widgets/wiki/Timeline for more information
var theme = Timeline.ClassicTheme.create(); // create the theme    
theme.event.bubble.width = 300;   // modify this bubble size to fit your needs   
theme.event.bubble.height = 170;    
var bandInfos = [ 
Timeline.createBandInfo({ 
trackGap:       0.5, 
width:          "60%", 
intervalUnit:   Timeline.DateTime.WEEK, 
intervalPixels: 100, 
timeZone : 8, 
eventSource: eventSource, theme:theme 
}), 
Timeline.createBandInfo({ 
showEventText:  false, 
trackHeight:    0.5, 
trackGap:       0.2, 
width:          "25%", 
intervalUnit:   Timeline.DateTime.MONTH, 
intervalPixels: 150, 
timeZone : 8, 
eventSource: eventSource 
}), 
Timeline.createBandInfo({ 
showEventText:  false, 
trackHeight:    0.5, 
trackGap:       0.2, 
width:          "15%", 
intervalUnit:   Timeline.DateTime.YEAR, 
intervalPixels: 400, 
timeZone : 8, 
eventSource: eventSource 
}) 
]; 
bandInfos[1].syncWith = 0; 
bandInfos[2].highlight = true; 
bandInfos[2].syncWith = 1; 
var timeLine = Timeline.create(document.getElementById("my-timeline"), bandInfos); 
} 
function ExtractWebSiteURL(sUrl) {
var index = sUrl.toLowerCase().indexOf("_layouts");
var MyCurrentPath = "";
if (index != -1) {
MyCurrentPath = sUrl.substring(0, index);
MyCurrentPath = MyCurrentPath.substring(0,MyCurrentPath.lastIndexOf('/'));
}
else { return null;}
return MyCurrentPath 
}
function ExtractListID(sUrl) {
var index = sUrl.toLowerCase().indexOf("_layouts");
var DestinationListID = "";
if (index != -1) {
DestinationListID = unescape(sUrl.substring(index + 28, sUrl.length));
}
else { return null;}
return DestinationListID 
}
// _spBodyOnLoadFunctionNames.push is a SharePoint function that insures that the script will be run only AFTER the page has been loaded
_spBodyOnLoadFunctionNames.push("main"); 
// ******************** Settings ******************** 
// URl of the RSS associated to your calendarvar
MyList_RSS_Url = "http://myserver/mysite/_layouts/listfeed.aspx?List=%7B3186664F%2D626C%2D4925%2D896B%2D53517E1D0244%7D"; 
</script>
<!-- Feel free to modify the following parameters: height, border, font -->
<div id="my-timeline" style="height: 120px; border: 1px solid #aaa; font-size: 9pt"></div>  

Visualize What’s New on a SharePoint Site with SIMILE Timeline

 It’s often frustrating to connect to a SharePoint site and spend a few minutes looking around to see if anything new has been added or modified on this site. “But that’s why lists and documents libraries have RSS feeds and email notifications”, you’ll reply. True, if you’re an IT person (which you are, if you’re reading this blog), or one of the few “power users”. But I’m talking about the majority of users, who don’t use RSS feeds and email notifications. Don’t blame them; blame Microsoft who’s done a poor job exposing RSS and email notification features (and don’t expect SharePoint 2010 to fix this issue). The net result is that most of the content you add on your web site will probably go unnoticed. 

Enters SharePoint Activities Timeline (SAT, for short)

When dropped on any web site, this JavaScript code (i.e. client side, i.e. nothing to deploy on the server, i.e. not need to negotiate with IT…) will enable site owners to select what lists and document libraries of any site of their choosing will be visualized (in other words: you can visualize on site A, on which you need read/write access, the activity of site B, on which you only need read access). Visitors can then interact with a timeline that shows all activities on the entire site. The timeline is based on MIT’s SIMILE Timeline widget. Here’s an example of the result:
SAT1
The immediate value of this visualization tool is that it displays all new items of selected lists and document libraries of a given site. Users can drag one of the 3 timelines (by day, by month, by year) to quickly browse past and future items.
But wait, there’s more!
  • Clicking on an item displays more details. As you’ll see later in this article, you even have the control of what is being displayed in the callout. A few handy links are available: link to the item itself; subscribe via RSS or email to the related list.
SAT2
  • Making RSS and email subscription features more visible will lead to better dissemination and usage of your content. Site Activity Timeline offers a collapsible panel, which gives access to the selected lists and document libraries RSS and email subscription links. Clicking on “Stay Connected” expends and collapses the panel.
SAT3a
SAT3b
  • The person who adds the JavaScript to the SharePoint site will see another collapsible menu, which gives access to the options of the Site Activity Timeline:
SAT4
This person will always see that menu on the site. Other users will not. Notice that the name of the person who can modify the settings can be manually changed in the settings list (see settings list creation in “step 1” below).
  • When opened, the settings panel shows all the existing lists and document libraries of the target site.
SAT5
The first column is for selecting which of the targeted site lists/document libraries should appear in the Site Activity Timeline.
The start/end date columns set what fields should be used for positioning the item on the timeline. Be careful to only select date fields that always have a value. For example, if you selected an Announcement list, remember that the “expiration date” is not a mandatory field so it doesn’t always have a value. An error message will appear below the timeline if such a problem occurs.
The “Select body of bubble” column configures what fields will be displayed when an item is clicked (see screen shot #2 above).
Finally, the last column defines whether then item will be represented by the proposed icon, or by a blue bar as shown below. Notice that document libraries will always try to use the document type related icons (Word, Excel, PowerPoint, PDF, etc.). The bar is typically more useful for calendar and task lists.
SAT7

How Can I add SAT to a SharePoint Site?


Create a SharePoint list to host your settings
1) Create a new custom list on SharePoint, and make sure everyone has read/write access to it. This list is used by JavaScript to save the Timeline settings. Here’s how the list should be configured (click for larger version).
SATsetting1 SATsetting2
SATsetting3 SATsetting4

2) Copy the related RSS link somewhere, we’ll need it in step 4.
Modify JavaScript to fit your needs
3) Open the JavaScript source in your favorite text editor:
4) Replace any reference to “http://MyServer” with your own urls (double check you have replaced all of them, it’s important) 
5) Replace last variable of the script with the url of the site you want to monitor.
Add the modified script to your site
6) Edit your SharePoint page and add a Content Editor Web Part
7) Click “Source Code” to open an empty text box
8) Copy and paste your modified script.
What should I do if nothing appears?
In case you run into some issues, there are a few things you can do:
  • Verify your settings list has read/write rights for everyone
  • Verify all the urls you’re using in the script(links to timeline-api.js; spapi_core.js; spapi_types.js; spapi_lists.js; SPAPI_dspsts.js; SPAPI_UserProfile.js; your targeted site and settings RSS link)
  • Refresh your page (SIMILE Timeline can sometime time out, that’s why we recommend you save the timeline-api.js script on your own server)
  • Look inside the script, a few lines containing a “Trace” function call have been commented. Comment them out to display more information.
  • Verify that _spBodyOnLoadFunctionNames.push does what it’s supposed to do, by adding a simple “Alert” function at the very beginning of the “getAllLists” function.
  • Throw me a comment to this post to see if I can help out.