Tuesday
Jan152013

A Fresh Visualization for SharePoint Calendars with SPServices

This is a set of scripts that can be dropped into a SharePoint 2010 deployment to allow for an alternate view of calendar lists.

There was also a request to color cancelled items that the user had on the calendar so I've left that code in there. The widget display start and end times as well as alter the behavior for those events that span days. I also added CAML support so you can provide your own query to it if you want or just you the built-in default. There is a separate CSS file to do your own styling and a few images that I use. One is optional and controlled by the arguments to the script.

I chose to set this up to live in our farm's common repository and be called relatively easily from any page so you see 2 files here, one being the LoadCalendarWidget function and the other being the code we drop into a page to create the calendar control. Options/args you pass to the function are:

  1. Site's URL, no end '/' character
  2. List name
  3. Title to appear for the webpart
  4. GUID of your calendar list
  5. # of results to show
  6. Optional CAML query to use
  7. Indicator to show icon or use 'Add to Outlook' text instead

If you want to keep it simple and not place file directly on your farm's servers, the zipped package below has all of the files needed and is just about ready-to-use for a dirt-simple deployment.  There is a PDF included that contains instructions on use that are a little more detailed than in the posting that follows.

Download all code and images used here.

This is the code we call to create the control. I house this in a text file in a library and add to the page in a CEWP as the 'Content Link'.

<!-- jQuery -->
<script type="text/javascript" src="https://myfarm/_layouts/1033/jquery.SPServices-0.7.2.min.js"></script>
<script type="text/javascript" src="https://myfarm/_layouts/1033/jquery.dateFormat.js"></script>
<!-- Calendar Widget -->
<script type="text/javascript" src="https://myfarm/_layouts/1033/jquery.SPS.Calendar_Widget-1.0.js"></script>
<link href="/_layouts/1033/styles/Calendar_Widget.css" type="text/css" rel="stylesheet" />
<script language = "javascript">
	$(document).ready(function() {		
		LoadCalendarWidget("https://myfarm/hr/", "Upcoming HR Events Calendar", "Event Calendar", "{B7FE9372-9724-4504-9699-DEDD445B1BA3}", 8, ,true);	
	});	
</script>
<div id="cw_TopContainer" style="margin-bottom:15px;"><div id="cw_HeaderContainer"><table style="width:100%;"><tr><td><span id="cw_HeaderTitle"></span></td><td style="text-align:right;"><a id="cw_HeaderLink" href="#" >Calendar View</a></td></tr></table></div><div id="cw_ItemsContainer" /></div>

And this is the function. I put it in our 14 hive for access across the farm, you put it where you want, just make sure the script source in the code above points to where you dropped this.

// REQUIRES:
// - jquery-1.8.0.js or higher
// - jquery.SPServices-0.7.2.min.js or higher
// - jquery.dateFormat.js

function LoadCalendarWidget(cw_SiteUrl, cw_ListName, cw_ListNameForHeader, cw_EncodedListGuid, cw_ReturnRowCount, cw_CAML , cw_ShowIcon)
{
	// USAGE: --------------
	// cw_SiteUrl = "https://myfarm/hr/"; // Site's URL, no end '/' character
	// cw_ListName = "HR Events Calendar"; // List name
	// cw_ListNameForHeader = "Upcoming HR Events";  // what will appear in webpart
	// cw_EncodedListGuid = "{B7FE9372-9724-4504-9699-DEDD445B1BA3}";  // GUID of your calendar list
	// cw_ReturnRowCount = 15;  // # of results 
	// cw_CAML = Your optional CAML query
	// cw_ShowIcon = true;  // false will use 'Add to Outlook'

	var cw_ListUrlForHeader = cw_SiteUrl + '/lists/' + cw_ListName;
	var cw_IcsEventPath = cw_SiteUrl + "/_vti_bin/owssvr.dll?CS=109&Cmd=Display&List=" + cw_EncodedListGuid + "&CacheControl=1&Using=event.ics&ID=";
	var cw_ListItemDetailPath = cw_SiteUrl + "/Lists/" + cw_ListName + "/DispForm.aspx?ID=";
	var cw_CamlQuery = "";
	if (cw_CAML.length < 2) {
		cw_CamlQuery = "<Query><Where><Geq><FieldRef Name='EventDate' /><Value IncludeTimeValue='TRUE' Type='DateTime'><Today/></Value></Geq></Where></Query>";}
	else { cw_CamlQuery = cw_CAML; }

	// format header info
	$('#cw_HeaderTitle').html(cw_ListNameForHeader);  // title
	$('#cw_HeaderLink').attr('href',cw_ListUrlForHeader); // link
	
   $().SPServices({
		operation: "GetListItems",
		webURL: cw_SiteUrl, 
		listName: cw_ListName,
		async: false,
		CAMLRowLimit: cw_ReturnRowCount,
		CAMLQuery: cw_CamlQuery,	 
	 
		completefunc: function (xData, Status) {							
		   $(xData.responseXML).SPFilterNode("z:row").each(function() {				
				var cw_id = ($(this).attr("ows_ID"));
				var cw_title = ($(this).attr("ows_Title"));
					if(cw_title.length == 0) {cw_title = 'No event title.';}
					
				// By request, if an item is canceled, make that fact stand out.
				cw_title = cw_title.replace("Deleted:","<span class='cw_canceled'>DELETED:</span> ");
				cw_title = cw_title.replace("DELETED:","<span class='cw_canceled'>DELETED:</span> ");
				cw_title = cw_title.replace("Cancelled:","<span class='cw_canceled'>CANCELLED:</span> ");
				cw_title = cw_title.replace("CANCELLED:","<span class='cw_canceled'>CANCELLED:</span> ");
				
				// Clean up date formats				
				var cw_startdateday = 		$.format.date(($(this).attr("ows_EventDate")), "M/d/yyyy");
				var cw_startdatetime = 		$.format.date(($(this).attr("ows_EventDate")), "h:mm a");
				var cw_startdate = 			cw_startdateday + ' - ' + cw_startdatetime;
				var cw_enddateday = 		$.format.date(($(this).attr("ows_EndDate")), "M/d/yyyy");
				var cw_enddatedaytime = 	$.format.date(($(this).attr("ows_EndDate")), "h:mm a");
				var cw_enddate = 			cw_enddateday + ' - ' + cw_enddatedaytime
				
				var cw_CX = '<div class="cw_item">';
				if(cw_startdateday == cw_enddateday)  // single day event
				{
					cw_CX += '<table width="100%" cellpadding="0">';
					cw_CX += '<tr><td colspan="2"><a href="' + cw_ListItemDetailPath + cw_id + '" target="_new" class="cw_title">' + cw_title + '</a></td></tr>';					
					cw_CX += '<tr><td><span class="cw_time">' + cw_startdateday + ' ' + cw_startdatetime + ' to ' + cw_enddatedaytime +'</span></td>';
					cw_CX += '<td align="right" valign="top"><a href="' + cw_IcsEventPath + cw_id + '" target="_new">';					
					if(cw_ShowIcon) {
						cw_CX += '<img src="FARM/Images/add_item.png" class="cw_addIcon" title="Add to Outlook Calendar" /></a></td></tr>'; 
					}
					else { cw_CX += '[Add to Outlook]</a></td></tr>'; }						
					cw_CX += '</table>';
				}
				else // multi day event
				{
					cw_CX += '<table width="100%" cellpadding="0">';
					cw_CX += '<tr><td colspan="2"><a href="' + cw_ListItemDetailPath + cw_id + '" target="_new" class="cw_title">' + cw_title + '</a></td></tr>';					
					cw_CX += '<tr><td><span class="cw_time">Start:' + cw_startdate + '</span></td><td align="right" rowspan="2" valign="top"><a href="' + cw_IcsEventPath + cw_id + '" target="_new">';
					
					if(cw_ShowIcon) {
						cw_CX += '<img src="https://cbhq.cbh.com/_layouts/images/myelite2/cal/add_item.png" class="cw_addIcon" title="Add to Outlook Calendar" /></a></td></tr>';
					}
					else { cw_CX += '[<a href="' + cw_IcsEventPath + cw_id + '" target="_new">Add to Outlook]</a></td></tr>'; }						
					cw_CX += '<tr><td><span class="cw_time">End:' + cw_enddate + '</span></td></tr>';
					cw_CX += '</table>';				
				}				
				cw_CX += '</div>';
				$("#cw_ItemsContainer").append(cw_CX); // insert into DOM						
		   }); // -- $(xData.responseXML).find   
		} // -- completefunc
   }); // -- $().SPServices({
} // -- LoadCalendarWidget()
Friday
Dec212012

Add a Little Holiday Wonder to SharePoint: Cost $3, Time 10 Minutes

I've been wanting to see some snow here for the holiday season and there has been a sad lack of even cold weather, so I took a short break from an ocean of CSS and re-branding this morning to add a snow-effect web part to our SharePoint farm for site admins to use. It's cheap, easy and people are pretty excited about it so I thought I might throw the instructions out for you to implement in just a few minutes.

 

Step 1: Cheat, spend $3 and grab the excellent javascript snow effect package from codecanyon from here (I have no affiliation with that site or the snow effect author) http://codecanyon.net/item/jsized-snow-effect/75580. I did the math and writing my own script was not economically viable with this at 3 bucks. That and, well this is a little frivolous, so we gotta make it all pretty brief.

Step 2: Pop the files on your farm. We'll assume that you do not have access to add files to ye olde 14 hive so we'll keep this simple, though I added to our 14 hive for easier cross site collection availability. Create a folder in a doc library and drop the files from that snow-effect package there. You really just have one .js file and 5 .gif files (the flakes).

Step 3: Create the file to kick the effect off. Create a text file in that same folder, I'll name mine s.txt to save on typing. Put the following text in that text file, replacing the url in caps there to your library file's urls.

<script src="http://YOUR_SP_URL/FOLDERS/jsized.snow/jsized.snow.min.js" type="text/javascript"></script>        
<script>createSnow('http://YOUR_SP_URL/FOLDERS/jsized.snow/', 30);</script>

Here was mine:

Step 4: On some page on your SP site, add a Content Editor web part, setting the url to your freshly minted text file as the Content Link, giving it a name and setting the Chrome Type to none.

Optional: Export your new web part from that page as a .dwp file and import it into your site collection's web-part library for easy re-use. This may depend on your admin privileges and may not be an option. If not just repeating step 4 above will also work to add wherever you want.

Tuesday
Dec112012

New American Idols

Well my writing workshop has sadly ended and I will miss spending time with the others, and their writing, as well as the pressure that it created for me to actually write, which was pretty great (and effective). This was my last submission, inspired in no small manner by one of my all time favorite books, Neil Gaiman's American Gods. The story was inspired by an actual conversation I had in a coffee shop recently, though that conversation was not nearly as interesting as the one in the story.


New American Idols - K.Guyer 2012

The waiter, a heavily tattooed 20-something with an oversized nose ring and shock of dyed-black hair, gazed at his paper pad as if seeing for the first time what he had just written seconds before, 'Uh, alright, I got a coffee and a black tea. Is that all gent-?' He glanced up as he spoke, scanning the greying, older men occupying the booth, his eyes suddenly widening. 'Whoa, that is a sweet t-shirt! What is that thing on the front?'

The smaller of the two gentlemen in the booth, sporting a dark t-shirt, smiled a wide grin at his companion and turned to face the boy. His voice seemed an ill fit, too rich and strong for his aging frame. 'This is Dumuzi, the Mesopotamian god of the Underworld. Heard of him?'

'Hmm, no man, I've never heard of him before.' The waiter's glance drifted away; distracted by a ringtone having burst into a Dick Dale guitar riff a few booths distant.

The older man raised his voice slightly to lure the waiter back to the conversation. 'Yes, Dumuzi. He was a real badass, vicious lord of the underworld, and a fertility god on the side.' He sized the young man up. 'I tell you what son; this Dumuzi shirt has got me laid plenty. Here.' He reached into his jacket pocket and pulled out a shiny business card, handing it to the young man who had finally cut himself loose of the ringtone's spell to face the old speaker. 'There's a website on the card, go there, use the code I wrote on the back and you can get yourself one of these shirts for free, no shipping either. Trust me friend, the ladies can't get enough of it.'

'Yeah, uh, thanks guy.' He turned the card over slowly, and as the words he had been buffering finally processed into comprehension, he smiled and nodded. 'Yeah. Yeah. Fuckin-A, thanks man. I'll go get you young men your drinks. Yeah.' He meandered his way back toward the coffee bar, still fingering the card.

The old man watched him go and turned back to his companion, a huge bear of a man with a bushy mane of silver streaked red, now framing a distinct look of distaste. The large man's voice was even deeper than his friend's, hovering just north of a rumble, 'What the fuck was that Dumuzi? You giving out swag with your face plastered on it now? That's the most ridiculous, debasing thing I've ever seen from a god.'

Dumuzi leaned forward conspiratorially, grinning with his ancient, coffee stained teeth and dropping the volume. 'This is why I asked you to meet me for a drink Tyr. The mortals no longer have to worship, or really even believe in the old gods for us to exist. All it takes now for sustenance is for them to knowingly bear our likeness, like the t-shirt. I've given up on trying to get new worshippers and hired a marketing consultant.'

A microscopic twitch of the lip and Tyr cocked his head, leaning in himself. 'Wait, what?'

'I take it you haven't talked to Loki since you fell out of favor with the other Norse gods.' He glanced around as if genuinely expecting someone to be listening, looking a little disappointed to see that nobody was. 'Ever since he and your old Asgardian crew up and went Hollywood with that big Thor movie a few years back, Loki began noticing something. See, ever since that movie, his image is slapped on all kinds of shit. He's on Slurpee cups, lunch boxes, sweatshirts, comics. Mountains of branded crap. He says that it's not creating the same high as actually being worshipped does, but he says that he's got more energy flowing into him now, just from that junk, than he's seen from mortal worshippers at any time in the last, like, 900 years. So I've been testing it out myself. I hired a marketing consultant, I'm giving away shirts on my website and I've hired a college student to write me into an iPad game. Seriously. Kids love these underworld fighting games, especially if we can get the 'mature' rating. Tyr, I can already feel the incoming energy. It sounds nuts, I realize that, but this is real, this changes everything.'

The drinks arrived with the waiter's eyes more on Dumuzi's shirt than on the delivery but he managed to get the mugs and check on the table without incident. Tyr shook his wooly head as the boy retreated and locked eyes again with his companion. 'Are you telling me that we can gain enough sustenance to continue living just from the mortals having our picture printed on their things? There is no longer a need for prayer, offerings or blood sacrifices? Dumuzi, don't take my blood sacrifices.'

'So far it seems that your image has got to be on things that are important to them, things they use and ideally cherish, but, yes. How many of us old gods have withered and died in the last millennium? Well with a little creative product placement we might make it to see another 100 or even 1000 years. Humans worship their possessions now Tyr, and if those things that they love bear our likeness then those things become an idol of us and we siphon off some of that adoration. Love of idols didn't used to mean squat in the old days but today's mortals love to worship their shit. The days of prayer and blood sacrifices may be over my friend.'

Tyr drew back, looking more than a little stricken. 'No. I don't want that. I need mortals to bludgeon their enemies, then raise the bloody steel to the sky and roar my name. This is what I am, what I have always been. Anything less is, is beneath an old god.' He paused, thinking. 'Isn't it?'

'Look, I know how great that kind of devotion is Tyr, but you don't really need blood sacrifice on the field of battle just as I don't really need the blood bowls and mounds of grain. We just prefer the old ways. Those days are gone. Look, I'm not thrilled about it, I'm gonna miss the hell out of those blood bowls and charred goats. Make no mistake, this is the tofu of deity worship, but we can still live off of it. If I can remain here on Earth and retain a grasp on godhood this way, even a tenuous grasp, then I'm gonna do it. We both know that gaining new worshipers is damned near impossible anymore. People just Google our shit on the Internet, see how crazy it all looks now and then wander off to watch You-Tube videos of cats or porn. We've got to adapt or we'll die.'

Tyr drained his cup as the two men sat wordlessly studying their mugs. Minutes passed in a heavy silence. With a slow exhalation, bordering on a sigh, Tyr reached his huge, weathered hand to gently hold Dumuzi's. He turned his head slightly, averting his eyes from the other's kind, piercing, gaze. 'I don't want to die, not by fading away, forgotten, like so many of the others have. I have earned the right to fall in battle with my axe in hand and take my place in Valhalla. For so long now that dream has seemed beyond my grasp.'

Dumuzi smiled and placed his smaller, smoother hand over that of Tyr, 'I don't think that we have to die, not just yet, and not by fading into history. Take the name of my marketing guy; I know he can do something for you. You are the Norse god of war for fuck's sake. That just screams Xbox game or a line of snowboards. You hire a few good artists to put Tyr out there in the market and guys like our waiter here will be lining up to get a tattoo of you. You're gonna sell, I mean it, you are gonna sell. With a shift in strategy and a few concessions we can survive just fine in this age. Immortality doesn't have to end for us yet.'

Tyr took a first, good, hard look at the stylized artwork on his companion's shirt and with an almost imperceptible shrug and hint of a grin, threw a Visa card on the check tray. 'This round's on me.'

Friday
Nov162012

Easy Scroll-Aware Hovering HTML Container with jQuery

I needed a quick and easy way to have a container hover on the side of a page and update with details as the user selected options. This is a simple, jQuery-based approach to doing so. Note that the portion at the end is included if you are needing to fire the positioning from a repost in asp.net, in this instance I needed to do so as the floating container was only displayed and filled via an async event when the user selected their first item.

<!DOCTYPE HTML>
<style>
	#oHeader {height:200px;width:100%;background:lightyellow;margin-bottom:20px;} /* Not needed */
	#oAnchorObject {height:2000px;width:450px;background:lightgreen;} /* Not needed */
	#oSidebar {position:fixed;left:500px;border:1px solid #ff0000;padding:40px} /* Position and left or right value needed */
</style>

<script src="jquery.js" type="text/javascript"></script>
<script type="text/javascript">
	$(document).ready(function () {
		RepositionCard();
	});	
	
	$(window).scroll(function () {
		RepositionCard();
	});
	
	function RepositionCard() {
		try{
			var eTop = $('#oAnchorObject').offset().top;
			if (eTop - $(window).scrollTop() <= 1) {
				$("#oSidebar").css("top", 0);
			}
			else {
				$("#oSidebar").css("top", eTop - $(window).scrollTop());
			}
		}
		catch(err) {}
	}

</script>

<div id="oHeader">
	Here is some header content to show how the locking works
</div>

<div id="oAnchorObject">
	Here is your large content block to which the sidebar will orient
</div>

<div id="oSidebar">
	This will hover/stick
</div>

Optional for firing the client event after ajax postback from C#, use in page load where needed:

ScriptManager.RegisterClientScriptBlock(Page, typeof(Page), "MyScript", "RepositionCard();", true);
Tuesday
Oct232012

Refugees from the Days of the Dying Sun

Another writing prompt from my Tuesday group.  I was supposed to write something in the past tense, that left a lot of room to manuver.  I figured I'd write a narration about guy from the future, living in our past where our past is his fuzzily remembered future.  It is really just a look into what I see now as a larger story or series of stories about these people that have been along-side us all along.  3 pages was not nearly enough to tackle how they may have fixed 'the problem' either, that'll have to come at some other time.

Refugees from the Days of the Dying Sun

Back in my younger days life on Earth was pretty good, incredible advances in social, environmental and technological solutions made living in the prior few centuries look downright medieval by comparison.  But all good things must come to an end and in late July of 2175, a week after my 44th birthday, it all came crashing down.

It was then that we noticed that something had begun happening to the sun, something very bad.  Our local star was not quite halfway through its natural life cycle and should have burned on for another 2 billion years before its natural, and expected, expansion would make it too hot for life on our planet. It should have taken some 4 billion years beyond that for the sun to expand and actually engulf the Earth, but something was happening that accelerated that process, something artificial and unbound by the natural laws that govern a star's lifecycle. And it was all happening with a dizzying speed. So it was, in the sweltering days of late July that year, that we realized that the Earth, and most of our solar system, would be reduced to cinder in less than a decade and that mankind was on a collision course with certain extinction.

Back then our technology was impressive, really impressive, but could we have evacuated everyone to another inhabitable planet in 10 years?  No way.  Our ships were too slow, too small and the nearest candidate planet was too far away. So what do you do, where do 6 billion people run when there’s no safe place to run to?  Well, we realized that ‘where do we run?’ was not the right question to be asking, and that 'to when do we run?', now that one had some merit.

My team and I at the Ministry of Applied Theoretics had worked out the fundamentals of time travel about 15 years prior to the sun entering its death throes though, to be honest, we hadn't considered it for widespread use.  It destroyed electronics, but organics seemed to travel unharmed.  When it came down to it, we still didn't know much about how it worked or how to control it, only that tests showed that we could send people forward or backward in the time stream, however, going forward no longer seemed a desirable option.  The problem was that we didn't have any control over how far a traveller would go, 10 minutes, 10 years, 10,000 years or longer, it was a crap shoot.  And if the place you were standing on is lava at the time you travel to, well, oh well.  But desperate times called for desperate measures so we started the ball rolling for mankind to abandon the present and flee to the past.

Qualification for travel was simple, anyone aged 16 and older, willing to undergo some, I think we called it, slight protein editing, could travel backward. The ‘protein editing’ was a brilliantly advanced neurological modification aimed at identifying and suppressing some specific thought patterns connected to specific contemporary knowledge that might completely alter history, such as your giving gunpowder to the Roman Empire or revealing the nature of bacteria to a plague-ridden Europe.  Mankind had gotten exceptionally good with biochemical manipulation and so it was not a big deal to obfuscate some memories, all aimed at not sending us back to terrorize the past.  The modifications to our bodies apparently prevented our bones from fossilizing and blocked your ability to coherently communicate that you had come from the future as well.  I stumbled on that little mental block years later and made a fool of myself in the process.

Ideally, we’d hoped to get a few people back into a reasonable timeframe before the sun got broken, to find out what had been the cause and try to prevent it from starting.  Otherwise we were doomed to face extinction again.  In the end there were 4.2 billion of us that volunteered and qualified, ready to be blasted like buckshot at the past, in the desperate hope that a handful of us would land when we were needed. For anyone that did wind up in our recent past and had a real shot of discovering, and fixing, whatever had happened to the sun, they would have the full library of future human knowledge just unlock itself in their heads where it lay in wait.  All hope really rested with them.  The remainder of us, statistically most of the 4.2 billion that chose to go back, were free to live out our lives as best we could, whenever we wound up.  About 90% of us would not last a week, and another 9% not much longer than that.  The odds of survival were staggeringly slim; they were pretty up-front with the fact that this was not likely to end well for most of us.  If you conservatively looked at, say, the last 1 million years that you might find yourself sent to, anything but the last .05% of that timeframe did not bode well for longevity, and the further back, the worse your chances were. If you wound up pre-dating civilization you were pretty much screwed. Dinosaurs, ancient plagues we had no immunities for, the list of likely deaths was as terrifying as it was long. But you did have a chance if you went back, albeit a slim one, and you might just save the world.  So back we went.

I wound up being one of the very, very lucky players of time roulette, finding myself in 1908, just outside of what was Cleveland back then. I experienced the 20's, depression, war, and chose to be a farmer where I was able to eke out a few innovations from my neutered memories and see the land flourish.  I had a simple, fulfilling second chance at life that convinced me that mankind really was worth saving, hoping each of my days that cold-hearted bitch, Our Lady of Statistical Probability would give mankind a fair shot and that a few good minds made it to the sweet spot of the 22nd century to save us all.  The years passed and I found that scraps of my memories began returning courtesy of a terminal brain tumor.  I found that I could talk about things that were suppressed but, well, nobody really listens to the ravings of a crazy old dying man. There was no point in my having held out hope for a miracle cure either, the Chinese wouldn’t discover a general cure for cancer for another 95 years or so. 

So, we were here among you in some capacity all along; from well before the days that mankind walked upright, refugees from the days of the dying sun.  All of us that have walked the Earth to date were the statistical collateral, that spray of buckshot that missed the intended target.  We have always sought the same life of simple, timeless joys under the magnificent sun that you have ever chased.  May we all bask in it until the end of our days.