Thursday
Oct112012

Chili-Cheese Butternut Squash

I dig making up new food combinations and some are good, some are decidely not.  I sorta made this up the other night, my lovely wife was swamped down with work and really wanted to 'use the butternut squash' that we had, so I just sort of threw some stuff together that seemed reasonable and I struck gold.  This was some good stuff.  A lack of exact directions are really indicitive of the way I cook.  Fake it, you'll be fine.

What you need:

  • Butternut squash, peeled, seeded, cut into bite sized-hunks.  About 1 to 1.5 lbs.
  • An onion, preferably yellow, medium, diced. If you use a red onion for this you will regret it for 1000 days and somewhere a cute kitty will die because of you.  No red freaking onions.
  • Big old splash of olive oil
  • Dash of chili pepper - the good dark stuff, don't be a sissy with that dash
  • Dash of Chipotle pepper - good n' smoky, maybe 2 dashes
  • Dash o' cumin, use it, but go light
  • Dash of oregano, this is really not about the oregano, just a tiny amount
  • Dash of paprika, I like my paprika like I like my women, hot and Hungarian, use what you have on hand.
  • Garlic, a little.  I used the dry powdered junk and it was great.
  • Salt to taste
  • A mild white cheese.  Montery jack, white american, something like that.  About 2 slices worth.  If you use processed Kraft cheese here you suck and your squash will too.  Don't do it.

The making:

  • Oil in the pan, make it hot.  Start your onion in there.
  • 87 seconds in, add the squash and all of your spices.
  • Knock that crap around with a wooden spoon, cover when needed, cook until the squash is edible, maybe 15 minutes or so depending on how big your chunks are.
  • When they look done throw the cheese on, let it melt, stir to coat.
  • Eat it, sharing optional.
Tuesday
Oct092012

Duty and Solitude

So I'm taking a weekly short story writing workshop at the Virginia Museum of Fine Arts, which was to be a painting class but at the last moment when I saw the opportunity to write a little fiction, I was compelled to run with instead.  I'm sort of loving it now. This week's short story was the result of being told to write a story about loneliness.

Duty and Solitude

   It has been many mortal lifetimes since last I spoke to another, those who knew my voice, my touch, the smell of my hair, have long since returned to dust to begin their great journey again. This is the cost of the oath I have sworn to uphold, for I am the Toth Farah, the chosen one. The Farah is all that stands between the things of beauty and grace in our world and the ancient horrors, writhing with tentacles and bereft of eyes, that will one day rise to reclaim the Earth and return it to the darkness from the time of its birth. Until then, the whole of this world is my prison where I walk among you, neither seen nor heard.

   I was not always as I am today, I too was once mortal before I accepted the burden that I was born to bear and chose to perform the sacred rite of the Farah. The memories of that life, thousands of years gone now, remain ever sharp. I drift in hauntingly clear visions of my longhouse, filled with my children, their laughter the soft sound of raindrops on bells, their small, cool hands held tightly in mine as we walked at dawn in the mist filled meadows.  My sisters and I beckoning the sun to rise with a song, and drinking the moon to sleep each night, all there in perfect clarity, yet gone forever. My beloved husband wore the title and trappings of warlord and chieftain and for our perfect days together he was, of the two of us, the one that knew and embraced battle; I cared nothing for his brutal art, the murals of red and the sadness each stroke bore on black crow wings.  Yet ages later, it is I, not he, who is destined to lead an army and, though the coming battle has always weighed heavily on my heart, I will not waver in what must be done in duty or sacrifice.  None may hear my voice or know that I walk among you, or the rite will be broken and I would fade from this world with no one to carry my mantle.

   I walk freely among all the people of the world and neither door of steel nor wall of stone may bar my passage as I fulfill my duty and gather the discarded teeth of man's young. I have done so for years beyond counting, bearing silent witness to the rise and fall of kingdoms.  My work continues with all the love, passion and drama of the ages denied me as I garner my forces. When that dark and prophesied day comes, I shall draw them forth and sow them as tiny white seeds of hope in the rich black earth where the dazzling light of our sun will bear witness to our gambit.  From the teeth of the innocent our greatest army will spring from the Earth to drive back and imprison despair’s foul forces, securing the hope and prosperity of mankind until the wheel of time spins again and all of these events begin anew, with another Toth Farah chosen to prepare for battle across the ages to come as have I and those that came before me.

   Until that fateful day, my ceaseless work remains fodder for your legends, your name for me ever on your lips but my sacrifice, and depth of solitude, unknown to you.  So leave still the teeth of your youth beneath their headrests and one day I shall stand by your side on the ashes of that vanquished army of the dark where I may once again share your wine and share with you all the full tale of your Tooth Fairy.

 

Thursday
May242012

Making SharePoint look like a million bucks on a shoestring budget - Part II

AKA the sexy, animated, customizable alerts bar you can have for 6 bucks.

In the second installment of our using jQuery, SPS and some inexpensive UI controls we implement a beautiful, customizable alerts/notification bar for SharePoint, driven off of a SharePoint list that'll set you back $6 and take your lunch hour to implement.  Very little configuration is needed to get you up and rolling here and, staying true to a tennant of this series, there is no requirement to install any invasive files (solutions) on your server or farm.  

As shown here it will handle multiple alerts being active at the same time and will default to a generic theme if there is more than one alert.  What you do not see here is the slick behavior of this notification bar and the options it exposes, see a demo of all that the bar does here.

 
First a few Resources needed:
  • The notification bar ($6), officially named 'FooBar - A jQuery Notification Bar'
  • jQuery (consider putting this reference in your master page)
  • SPServices (also consider putting this reference in your master page) 
SharePoint List:
I created a simple custom list (System Alerts) with the following columns, if you use other names just reflect that in lines 36-38, if you don't know the ows_X field name uncomment line 23 to see.
  • IsActive - a yes/no data type
  • Theme - String datatype, required - I made this a choice and provided canned options
  • Message - Multiple lines of text datatype, plaintext or not
The code below:
This I dropped into a text file and just added to a page via a CEWP letting me choose on which pages this is displayed, in our case just the home page.  Note the mention of an image as part of the theme, you can whip your own up or just remove the text in line 85 (and comma in 84 then).  The screenshot above shows how nice this looks with a good image though.
<style> <!-- A few canned styles, go nuts here -->
	.foobar_darktext{ font-weight:bold; color:black; }
	.foobar_lighttext{ font-weight:bold; color:white; }	
</style>
<!-- For jQuery -->
	<script type="text/javascript" src="/_layouts/1033/jquery.SPServices.min.js"></script>
	<script type="text/javascript" src="/_layouts/1033/jquery.min.js"></script>
<!-- For Foobar -->
	<script type='text/javascript' src='/_layouts/1033/jquery.foobar.min.js'></script>
	<link href='/_layouts/1033/styles/jquery.foobar.css' type='text/css' rel='stylesheet' />

<script language = "javascript">
$(document).ready(function() 
{
	$().SPServices(
	{
		operation: "GetListItems",
		webURL: "https://YOUR_SHAREPOINT_SITE",    
		async: false,
		listName: "YOUR_LIST_NAME",
		completefunc: function (xData, Status) 
		{
			//alert("Response from server: " + xData.responseText); // helpful for debug
			var output = "";
			$("#WSOutput").html(output);
			
			var iCounter = 0;
			var arrPayload = [];
			var szBackHexColor = '';
			var szAppliedStyle = '';
			var szPathAlertImage = 'https://SOME_SHAREPOINT_PATH/AlertImages/';
			var szFullPathAlertImage = '';
			
			$(xData.responseXML).find("[nodeName='z:row']").each(function() { 
				
				var szContent = ($(this).attr("ows_Message"));				
				var bIsActive = ($(this).attr("ows_IsActive"));
				var szTheme = ($(this).attr("ows_Theme"));				
				
				if(bIsActive == 1)
				{				
					arrPayload.push(szContent);
							
					switch(szTheme) // set theme options
					{
						case 'Theme A':
							szBackHexColor = '#ffcc00';
							szAppliedStyle = 'foobar_darktext';
							szFullPathAlertImage = szPathAlertImage + 'Achtung.png';							
							break;
						case 'Theme B':
							szBackHexColor = '#CAF87A';
							szAppliedStyle = 'foobar_darktext';
							szFullPathAlertImage = szPathAlertImage + 'Danger Will Robinson.png';	
							break;							
					}					
					szFullPathAlertImage = '<img style=\' padding:0px 0 0 7px; \' src= \'' + szFullPathAlertImage + '\' />';
					iCounter++;  // increment
				}
			});
			
			if(iCounter > 1) // to prevent conflicting themes, make this a generic visual
			{
				szBackHexColor = '#ffcc00';
				szAppliedStyle = 'foobar_darktext';
				szFullPathAlertImage = szPathAlertImage + 'Generic Alert.png';			
			}
			
			if(iCounter > 0) 
			{
				$.foobar({
					"positioning" : "fixed",
					"display" : "delayed",
					"displayDelay" : 2000,
					"messagesDelay" : 5000,
					"messagesScrollSpeed": 50,
					"messagesScrollDelay": 2000,
					"messageClass" : szAppliedStyle,
					"fontColor" : szFontHexColor,
					"backgroundColor" : szBackHexColor,
					"buttonTheme" : "long-arrow",
					"height" : 35,
					"enableShadow" : false,
					"messages" : arrPayload,
					"leftHtml": szFullPathAlertImage
				});	
			}
		}
	});
});	
</script>
<div id="x_container" />
Further fiddling:
Initially I set this up so that the user could specify the background color, the text color, select an image or provide a URL, etc. All of which are done the same way as the theme logic but we decided to simplify the user experince with themes rather than give them so many choices.  You can easily expand on this though.
Consider throwing a few bucks at the jQuery and/or SPServices teams via the donate links on their sites, they do good work and deserve some help. I am not affiliated with any of the groups/sites I steer you toward in these posts...

 

 

Thursday
May102012

Using SPServices, jQuery and an $8 Tab Plugin to Create a Stunning SharePoint List Visualization

AKA: Making SharePoint look like a million bucks on a shoestring budget - Part I

I'd been wanting to spend some time getting familiar with the brilliant open source jQuery library for SharePoint's web services, SPServices, and had the opportunity this past week.  We had a SharePoint site under construction that needed to have a list of links and a little explanation to accompany them  and initially just hosted a standard grouping mechanism on the list view which looks terrible.  I thought that this might be a chance to offer a better way to visualize list data using an inexpensive tabbing control in my library.  The result is shown below, with each 'tab' here linking to 3 fields from a custom list, the title, the sub-title and the content.  This stays in our needed model of users interacting with the standard SP forms that they understand and is leaps and bounds a better user experience.  If this takes you more than a lunch break to implement you might need longer lunch breaks, this is an easy setup and looks great, even responding to mouse-wheel scrolling through the tabs.

 

To do this I used a stunning jQuery plugin called 'Sliding Tabs' that I bought from CodeCanyon.net for a dirt cheap 8 bucks (authored by 360north).  You can see/buy it here and it is one of the many ridiculous bargains on that site that I'll be extending in future posts.  That 8 dollars is all this costs and it looks like $1,000,008 when done.  You'll also need to grab the jQuery file and the SPServices file, we have these hosted on our farm and embedded in the master page for ease of use as seen in the includes in the code.  All includes that are needed but the core jQuery and SPServices files come with the Sliding Tabs package.

With the code in hand from them I created a new list with the aforementioned title, subtitle and content (being a rich content type) list and hooked it up.  Full code follows below.

Notice in the code that you have to specify your site and list names (lines 13 and 15) and if you name your columns something other than I did, you'll need to change the mappings to the 'ows_XXX' moniker that matches your fields (lines 26, 28 and 30).  If you are unsure of what your field's names are you can uncomment line 20 and see for yourself.

You can change the size, shape, orientation and other options with the tab control with the options section starting at line 58.  It does not handle overflow well (um, not at all really, it just gets clipped) but for what we needed it was perfect.

 

<script type="text/javascript" src="_layouts/1033/jquery.min.js"></script>
<script type="text/javascript" src="_layouts/1033/jquery.SPServices.min.js"></script>
<script type="text/javascript" src="_layouts/1033/jquery.mousewheel.js"></script>
<script type="text/javascript" src="_layouts/1033/jquery.slidingtabs.pack.js"></script>
<script type="text/javascript" src="_layouts/1033/jquery.easing.js"></script>

<link href="_layouts/1033/styles/slidingtabs-vertical.css" rel="stylesheet" type="text/css" media="screen"/>

<script language = "javascript">
$(document).ready(function() {
	$().SPServices({
		operation: "GetListItems",
		webURL: "https://MY_SHAREPOINT_ROOT/SOME_SITE",    
		async: false,
		listName: "SIMPLE DEMO LIST",
		completefunc: function (xData, Status) {

			//alert("Status of XML message reaching Sharepoint webservice: " + Status); // handy for debug
			//alert("Response from server: " + xData.responseText); // also handy for debug
			var output = "";
			$("#WSOutput").html(output);
			var TabCounter = 0; // to keep track of tabs/containers

			$(xData.responseXML).find("[nodeName='z:row']").each(function() {

				var title = ($(this).attr("ows_Title"));
					if(title.length == 0) {title = ' ';}
				var subtitle = ($(this).attr("ows_Subtitle"));
					if(subtitle.length == 0) {subtitle = ' ';}
				var content = ($(this).attr("ows_Content"));
					if(content.length == 0) {content = ' ';}
				 
				var TabClass = 'st_tab';
				var ContainerClass = 'st_tab_view';
						
				if(TabCounter==0) // our first items get special 'active' class styling
				{				
					TabClass = 'st_tab st_tab_active';
					ContainerClass = 'st_tab_view st_first_tab_view';
				}
				// Create new tab element as <li>
				var TabHeader = '<li><a href="#stv_content_' + TabCounter + '" rel="v_tab_' + TabCounter + '" class="' + TabClass + '">'
					TabHeader += title + '<span>' + subtitle + '</span></a></li>';
				$("#tablisting").append(TabHeader); // insert into DOM

				// Create new lined content container as <div>
				var stv_content = '<div id="stv_content_' + TabCounter + '" class="' + ContainerClass + '" style="overflow:auto;">';
					stv_content +='<h2 class="sc_title">' + title + '</h2><div class="text"><p>' + content + '</p></div></div>';			
				$("#st_tabs_container").append(stv_content); // insert into DOM

				TabCounter++;  // increment counter
			});
		}
	});
	   
	// Instantiate Vertical Sliding Tabs
	$('div#st_vertical').slideTabs({  			
		// Options
		contentAnim: 'slideH',
		contentAnimTime: 600,
		contentEasing: 'easeInOutExpo',
		orientation: 'vertical',
		tabsAnimTime: 300,
		totalWidth: '660',		
		tabScroll: true				
	});
});	
</script>
       
<!-- Start HTML - Vertical tabs -->
<div id="st_vertical" class="st_vertical">
	<div class="st_tabs_container">   
		<a href="#prev" class="st_prev"></a>
		<a href="#next" class="st_next"></a>                                                                    
		<div class="st_slide_container">            
			<ul class="st_tabs" id="tablisting"></ul><!-- tabs inserted here -->               
		</div> <!-- /.st_slide_container -->
	</div> <!-- /.st_tabs_container -->                               
	<div class="st_view_container">        	            
		<div class="st_view" id="st_tabs_container"></div> <!-- content blocks inserted here -->                        
	</div> <!-- /.st_view_container -->
</div> <!-- /#st_vertical -->
<!-- End HTML - Vertical tabs -->
More jQuery visual enhancements to come...
Wednesday
Apr252012

SharePoint and jQuery - Get the Authenticated User's Name and Modify Content Client-Side

Getting the user's name from a SharePoint page and handing off to some client-side code proved messier than I had hoped for.  Part of the problem is that our farm runs on SSL and IE likes to throw the error message when some resources are secure and others are not and the common solution to this (using jquery.SPServices) was falling prone to that error.  Instead I worked around it so we could serve up a custom control on the intranet for someone's 50th birthday and manage it without audiencing controls.

It requires jQuery and the control that contains the username was not always assigned the same ID though it has the same AccessKey attribute so we made do with what we had.

Since this elegant method did not work:
$(document).ready(function() {
	alert($().SPServices.SPGetCurrentUser());
});

we instead went with the following (note that this includes the resulting action). If you are not in an HTTPS environment go with the SPServices option...

<script type="text/javascript">
	$(document).ready(function() {
		var str = '';
		var strContainer = '';
		
		// find the container that contains the user's name (not always the same...)
		if($('#zz14_Menu').attr("accesskey") == 'W') {strContainer = 'zz14_Menu';}
		if($('#zz15_Menu').attr("accesskey") == 'W') {strContainer = 'zz15_Menu';}
		if($('#zz16_Menu').attr("accesskey") == 'W') {strContainer = 'zz16_Menu';}
		if($('#zz17_Menu').attr("accesskey") == 'W') {strContainer = 'zz17_Menu';}
		
		var str = $('#' + strContainer).find("span").text();

		// date driven
		var d = new Date();
		var n = d.getDate();
		if(n==26) // only show on the 26th...
		{
			if(str == 'Jane User')
			{
				$('#mycontrol').attr('src', 'https://server/Rotator/alternative.htm');
			}
		}
	});
</script>