Thursday, November 29, 2012

Binding Event Handlers to SharePoint Content Types (Sandbox)

Fiddling around in SharePoint's Sandbox land can and will test your sanity.  There are things that are the way they are and you just have to deal with it.  All in all, a learning experience indeed...

Here's some key takeaways from my current project:
I opted not to bind this to the Content Type because I didn't want the Event Handler's behavior to persist on any other list.  I doubt the CT will be reused, but if it is, I know my code will not run erroneously.

Once that is added to your list, then you can dive into the properties and detect the Content Type GUID for the item that was just added.  A simple example:

       public override void ItemAdded(SPItemEventProperties properties)
           if (properties.ListItem.ContentTypeId.Parent.ToString().ToUpper() == Constants.ctGuid.ToUpper())
            // Snipped
This is only a small part of what I'm in the middle of building.  Hopefully, I can abstract more out and show off some of the innards.


Shortly after posting this @SharePointAlex and I started to discuss binding Event Handlers to lists.  Based on my tests, I've confirmed that using a Site-scoped solution does in fact fire the Event Handler on all lists in your web.  Using the same technique but having the solution Web-scoped will only bind the Event Handler to the list you declare.  Thanks Alex for making dig a bit deeper to further get better documentation.

Monday, November 26, 2012

CEWP Usage in SharePoint

While I absolutely loathe the term "Best Practices", it's time to dive into a topic that really hasn't been talked about but should have by now.  I'm *not* going to label this post as a "Best Practice", although I'd like to, but label it more like: this is what I do and why...

Adding Script to Pages

It's well known that most of the front-end developers favor using CEWP's to inject script into our pages.  Many well known webparts use this technique.  This is great!  We can add functionality to my pages with a small amount of work and without using SharePoint Designer.

Page Weight

What happens when you add scripts to your page using a CEWP?  This is a very much a misnomer because you really aren't adding scripts and instead are adding HTML.  CEWP's only interpret HTML and as such, they DO NOT cache any of the HTML added to the page.  To take advantage of caching these resources, you must use write the HTML correctly.  Adding <script> tags with a src attribute will tell the browser to cache your JavaScript files.  If you avoid doing this, your HTML will have to be sent to your browser each time, causing your page to load slower than it really has to.  Using a script tag correctly to call your .js file, your script will be cached by the browser and will not have to be downloaded for each page load.


Reading over an excellent write up about page weight got me to thinking about what I've seen blogged in the SharePoint space.  Most solutions have you connect a pseudo .js file to a CEWP.  In actuality, this isn't a JavaScript file at all.  It's a file that contains HTML.  So that's quite confusing, not to mention, adds to your page weight.  The post linked above says to keep your inlined CSS or JavaScript to < 1 ~ 4 KB.  

Here's my list of considerations when adding a new CEWP to my page.
  • What will this script potentially do over the life of this application?
  • Do I think this could grow over 1 KB?
  • Does the page weight even matter for this application?
  • Will I gain anything by caching these resources?

What I Do

Mostly, I create a .html file wherever I store my assets.  I write HTML in that file.  Generally speaking it's <link> tags and <script> tags that reference my resources.  Doing so, will allow these files to be cached by the browser.  Page weight and load times are very important to me, so this is generally what I do.  However, I will write CSS and JavaScript in my .html file if I know there will be a very small amount of HTML going into that file.

Using this technique, I feel less confused overall.  When I open a .js file, I'm actually looking at JavaScript and not CSS or HTML or both.  Maybe it'll do the same for you, who knows?!?  

This is what I do though.

Thursday, September 13, 2012

Query Strings Found on SharePoint's List Forms

I love looking for an easier way and this time I've found one that previously worked just a bit weird before...

The issue I've had is when creating items in a particular folder within a list, you have to be very conscious of the BaseName. It MUST be unique within your whole list structure or else the save fails. Look at the comments in the documentation for more details.

I've been building a tree navigation and I've been developing a way to insert folders/items dynamically.  I just found an easier way to create folders wherever I need them to appear in the list. I no longer have to worry about the BaseName as SharePoint takes care of that for me. As I learn of more query strings, I'll append them to this post

Query Strings 

RootFolder=%2FSPDev%2FLists%2FLinkNav%2FmahFolder - Used to control the destination of where your new item will be saved.

Type=0 - Used to tell SharePoint whether you want a folder or an item. 0 = folder; 1 = item.

ContentTypeId=0x01040004B13BF0D61E0D44A7D0327C51C01D8A - Create the content type of your choice.

IsDlg=1 - This query string can be set to anything and will behave as if the form is in a modal. Sometimes this can cause issues because saves/cancels rely on window.frameElement and if the <iframe> isn't present, the form will not work properly.

That's all for now but I'm sure I'll find some more...

Tuesday, August 28, 2012

From the depths of SharePoint's sp.js Pt. 2

So I lied... :)  The function found below is a part of core.js.

A good ol' fashioned
console.dir( GetCurrentCtx().clvp );
generates this bit of awesome:


 ctx : [object Object],

 tab : [object],

 pagingTab : null,

 tBody : null,

 wpq : "WebPartWPQ4",

 inplUrl : "http://portal/SPDev/_layouts/inplview.aspx?List={49AFC299-FC30-4EE9-9571-50B3B366969E}&View={876230CC-BE1A-4845-8998-652BB77C891C}&ViewCount=7&ListViewPageUrl=http://portal/SPDev/pages/spUtils.aspx&IsXslView=TRUE",

 trEmpty : null,

 rootFolder : null,

 rootFolderGuid : null,

 rgpag : null,

 wpid : null,

 isEditing : false,

 dsrc : null,

 isInserting : false,

 strGroupName : null,

 strGroupCache : null,

 bRequestOutstanding : false,

 isEcbInfo : false,

 queueEcbInfo : ,

 fnEcbCallback : null,

 rgpaging : ,

 focusInfo : null,

 CUIItemInfo : [object Object],

 Init : function CLVPInit(){ULSiIp:;var f=this.ctx.listBaseType==1,d;if(this.ctx.listTemplate=="109"){this.ctx.clvp=null;return}d=this.ctx.listName+"-"+this.ctx.view;var b=GetElementsByName(d);if(b.length==0&&f){d="onetidDoclibViewTbl0";b=GetElementsByName(d)}va,

 RestoreNavigation : function CLVPRestoreNavigation(){ULSiIp:;var a=window.location.hash,c=this.InplViewUrl(),b=this.RefreshCurrent(true);b=b.substr(c.length+1);if(a.indexOf("InplviewHash=")==1){a=DecodeHashAsQueryString(a);var d=a.substr(14,38);if(d!=this.ctx.view)return;a=,

 FindWebPartDiv : function CLVPFindWebPartDiv(b){ULSiIp:;var a=b;while(a!=null&&a.tagName!="DIV")a=a.parentNode;return a},

 SyncPagingTables : function CLVPSyncPagingTables(){ULSiIp:;var b=this.pagingTab,a=document.getElementById("topPagingCell"+this.wpq.substr(7));if(b==null){if(a!=null)"none";return}if(a!=null){"";if(GetInnerText(a)!=GetInnerText(b))a.innerHTML,

 RehookPaging : function CLVPRehookPaging(){ULSiIp:;if(typeof this.ctx.noAJAX!="undefined"&&this.ctx.noAJAX)return;var tab=this.pagingTab;if(tab==null){this.SyncPagingTables();return}for(var lnks=tab.getElementsByTagName("A"),i=0;i<lnks.length;i++){var lnk=lnks[i],str=",

 FilterString : function CLVPFilterString(){ULSiIp:;if(this.rgpaging==null)return null;var a,d,b=[],c=true;for(a in this.rgpaging)if(a.indexOf("Filter")==0||a.indexOf("Sort")==0){if(c)c=false;else b.push("&");b.push(a);b.push("=");b.push(this.rgpaging[a])}return b.join(,

 RefreshPaging : function CLVPRefreshPaging(c,a,b){ULSiIp:;if(a!=null)this.tBody=document.getElementById(a);var d=this.isEditing?"JsGrid":null;return this.RefreshPagingEx(c,true,d,b)},

 RefreshPagingEx : function CLVPRefreshPagingEx(e,q,p,t){ULSiIp:;var s=GetUrlKeyValue("PageFirstRow",false,e)=="1";if(s)e=RemoveOnlyPagingArgs(e);var v=new CUrl(e),h=v.query;if(h.length>0)h=h.substr(1);var i=h.split("&"),f,a=[];if(p!=null)a.push("&Cmd="+p);else this.isEdit,

 ResetSelection : function CLVPResetSelection(){ULSiIp:;var a=this.ctx,f=true;this.CUIItemInfo=null;if(CountSelectedItems(a)>0){var h=a.dictSel;a.dictSel=[];a.CurrentSelectedItems=0;var g=window.location.href;if(g.indexOf("Filter")==-1&&g.indexOf("Sort")==-1){var d=this.t,

 WebPartId : function CLVPWebPartId(){ULSiIp:;if(this.wpid==null){if(!=null){var;a=GetAncestor(a,"DIV");if(a!=null){if(a.getAttribute("WebPartID2")!=null){this.wpid=a.getAttribute("WebPartID2");return this.wpid}if(a.getAttribute("WebPartID")!=null),

 RefreshCore : function CLVPRefreshCore(strUrl){ULSiIp:;MenuHtc_hide();var req;if(window.XMLHttpRequest)req=new XMLHttpRequest;else req=new ActiveXObject("Microsoft.XMLHTTP");var additionalPostData="";if(typeof this.ctx.overrideSelectCommand!="undefined"){additionalPos,

 RefreshCurrent : function CLVPRefreshCurrent(){ULSiIp:;var a=document.URL;STSNavigate(a)},

 GetQueryString : function CLVPGetQueryString(){ULSiIp:;if(!=null&&"FilterLink")!=null)return"FilterLink");else return document.URL},

 RefreshEcbInfo : function CLVPRefreshEcbInfo(b){ULSiIp:;var a=this.GetQueryString();a=FixUrlFromClvp2(this,a,false);this.isEcbInfo=true;this.strGroupName=b;var c=this;this.RefreshPagingEx(a,false,"EcbView")},

 CacheEcbInfo : function CLVPCacheEcbInfo(b){ULSiIp:;if(this.ctx.HasRelatedCascadeLists==1&&this.ctx.CascadeDeleteWarningMessage==null){var j="<CascadeDeleteWarningMessage>",k="</CascadeDeleteWarningMessage>";if(b.startsWith(j)){var g=b.indexOf(k);if(g!==-1){this.ctx.Ca,

 EnsureEcbInfo : function CLVPEnsureEcbInfo(c,f,a){ULSiIp:;var g="ecbtab_ctx"+this.ctx.ctxId,b=document.getElementById(g);if(b==null&&!=null&& d=0;d<;d++){var[d];if(}if(b&,

 InvalidateEcbInfo : function CLVPInvalidateEcbInfo(){ULSiIp:;var b="ecbtab_ctx"+this.ctx.ctxId,a=document.getElementById(b);if(a!=null){;this.DeleteGroupNameCache()}},

 GetEcbInfo : function CLVPGetEcbInfo(b){ULSiIp:;var a=this.EnsureEcbInfo();if(a!=null)if(a.dict!=null)return a.dict[b];return null},

 SwitchToEdit : function CLVPSwitchToEdit(){ULSiIp:;var a=document.URL;a=FixUrlFromClvp2(this,a,false);this.RefreshPagingEx(a,false,"JsGrid")},

 EnsureChangeContext : function CLVPEnsureChangeContext(){ULSiIp:;if(this.cctx==null)this.cctx=new SP.ClientContext(null)},

 DeleteItemCore : function CLVPDeleteItemCore(c,f){ULSiIp:;var h=this.ctx.listBaseType==1;this.EnsureChangeContext();var a=null;if(typeof this.rgehs=="undefined")this.rgehs=[];var b,e;b=new SP.ExceptionHandlingScope(this.cctx);this.rgehs.push(b);e=b.startScope();this.cctx,

 CheckoutItem : function CLVPCheckoutItem(f,d){ULSiIp:;this.EnsureChangeContext();if(typeof this.rgehs=="undefined")this.rgehs=[];var a,c,b=null;if(d!=0)return;a=new SP.ExceptionHandlingScope(this.cctx);this.rgehs.push(a);c=a.startScope();b=this.cctx.get_web().get_lists,

 DiscardCheckoutItem : function CLVPDiscardCheckoutItem(e,g){ULSiIp:;this.EnsureChangeContext();if(typeof this.rgehs=="undefined")this.rgehs=[];var b=null,a,f;if(g!=0)return;a=new SP.ExceptionHandlingScope(this.cctx);this.rgehs.push(a);f=a.startScope();b=this.cctx.get_web().ge,

 CheckinItem : function CLVPCheckinItem(g,j,a){ULSiIp:;this.EnsureChangeContext();if(typeof this.rgehs=="undefined")this.rgehs=[];var c=null,b,h;if(j!=0)return;b=new SP.ExceptionHandlingScope(this.cctx);this.rgehs.push(b);h=b.startScope();c=this.cctx.get_web().get_list,

 ManageCopies : function CLVPManageCopies(f,e){ULSiIp:;if(e!=0)return;var b=this.GetEcbInfo(f);if(b!=null&&b.getAttribute("Url")!=null){var d=this.ctx.HttpRoot+"/_layouts/managecopies.aspx?ItemUrl="+b.getAttribute("Url")+"&Source="+GetSource(),a=window["ctx"+this.ctx.ct,

 ShowErrorDialog : function CLVPShowErrorDialog(g){ULSiIp:;var i,a=[],d=null,c=false;for(i in this.rgehs){var b=this.rgehs[i];if(b.get_hasException()){var f="s4-dlg-err-itm";if(!c){f="s4-dlg-err-firstitm";c=true;a.push(SP.Res.dlgTitleError)}var k=b.get_errorMessage();a.pus,

 InplViewUrl : function CLVPInplViewUrl(){ULSiIp:;if(this.inplUrl!=null)return this.inplUrl;var a=[],b=escapeUrlForCallback(this.ctx.HttpRoot);a.push(b);b[b.length-1]!="/"&&a.push("/");a.push("_layouts/inplview.aspx?List=");a.push(this.ctx.listName);if(this.ctx.view!=n,

 InplViewUrlTrim : function CLVPInplViewUrlTrim(a){ULSiIp:;var b="";if(a)if(typeof this.ctx.overrideFilterQstring!="undefined")b=a.substr(this.InplViewUrl().length-this.ctx.overrideFilterQstring.length);else b=a.substr(this.InplViewUrl().length+1);return b},

 ShowPopup : function CLVPShowPopup(a){ULSiIp:;MenuHtc_hide();if(typeof this.fnOnCommitPopup!="undefined"&&this.fnOnCommitPopup!=null)OpenPopUpPage(a,this.fnOnCommitPopup);else OpenPopUpPage(a,RefreshPage);return false},

 ValidateField : function CLVPValidateField(e,d,a,b){ULSiIp:;var c=this.FieldCommand("FldValidate",e,d,a,b),f={callback:a,newval:b,strCmd:c};if(this.qvalidate==null)this.qvalidate=[];this.qvalidate.push(f);if(!this.isValidating){this.isValidating=true;this.RefreshCore(c),

 FieldCommand : function CLVPFieldCommand(j,h,d,m,f){ULSiIp:;var a=[];a.push(this.InplViewUrl());a.push("&Cmd=");a.push(j);a.push("&Field=");a.push(d);a.push("&FieldValue=");var b=h.GetProp(d),c,n=this.gridParam.tableViewParams.columns.GetColumnByKey(d),k=jsGridParams.t,

 IsInGroupCache : function CLVPIsInGroupCache(a){ULSiIp:;if(this.strGroupCache==null)return false;return this.strGroupCache.indexOf(a+"$")!=-1},

 CacheGroupName : function CLVPCacheGroupName(a){ULSiIp:;if(this.strGroupCache==null)this.strGroupCache=a+"$";else this.strGroupCache=this.strGroupCache+a+"$"},

 DeleteGroupNameCache : function CLVPDeleteGroupNameCache(){ULSiIp:;if(this.strGroupCache==null)return;var b=0,a;a=this.strGroupCache.indexOf("$",b);while(a!=-1){var d=this.strGroupCache.substring(b,a),e="ecbtab_ctx"+this.ctx.ctxId+"_"+d,c=document.getElementById(e);c&&,

 EnqueueEcbInfoRequest : function CLVPEnqueueEcbInfoRequest(b){ULSiIp:;for(var a=0;a<this.queueEcbInfo.length;a++)if(this.queueEcbInfo[a]==b)return;this.queueEcbInfo.push(b)},

 NoOutstandingECBRequests : function CLVPNoOutstandingECBRequests(){ULSiIp:;if(this.isEcbInfo)return false;if(this.queueEcbInfo.length>0)return false;return true}


That makes a bunch of noise, however, you'll see an interesting URL at the property named: inplUrl. 
Upon visiting this URL, you'll see all of your data for that particular view.  This seems very attractive for AJAX applications in SharePoint.  Look for more to come in regards to this...


Tuesday, July 24, 2012

From the depths of SharePoint's sp.js Pt. 1

<Part 1>

Crack open your favorite browser tools and run:

ExecuteOrDelayUntilScriptLoaded( function() { console.dir( SP.Res ); }, 'sp.js');

The results will be:

LOG: {

 lcid : "1033",

 autocompleteAccessibleMenuName : "Autocomplete Menu",

 autocompleteLoading : "Loading...",

 autocompleteListNotFound : "List Not Found.",

 autocompleteListTooLarge : "List is too large to support Autocomplete.",

 autocompleteNoResults : "Item does not exist.",

 autocompleteTooManyResults : "Enter more of the name to see suggestions.",

 autocompleteList : "List:",

 autocompleteView : "View:",

 noObjectPathAssociatedWithObject : "The object is not associated with an object identity or the object identity is invalid.",

 propertyHasNotBeenInitialized : "The property or field has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested.",

 collectionHasNotBeenInitialized : "The collection has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested.",

 requestEmptyQueryName : "The property or query name is empty.",

 requestHasBeenExecuted : "Request has been executed.",

 requestAbortedOrTimedOut : "The request was aborted or timed out.",

 requestUnexpectedResponseWithStatus : "Unexpected response from server. The status code of response is '{0}'. The status text of response is '{1}'.",

 requestUnexpectedResponse : "Unexpected response from server.",

 invalidUsageOfExceptionHandlingScope : "Incorrect usage of exception handling scope.",

 unknownError : "Unknown Error",

 unknownResponseData : "Unexpected response data from server.",

 notSupportedQueryExpressionWithExpressionDetail : "The query expression '{0}' is not supported.",

 collectionModified : "The collection was modified. Enumeration operation may not execute.",

 cui_AddPerson : "Add Person or Group",

 calendarPrevMonth : "Previous Month",

 calendarNextMonth : "Next Month",

 calendarPrevWeek : "Previous Week",

 calendarNextWeek : "Next Week",

 calendarPrevDay : "Previous Day",

 calendarNextDay : "Next Day",

 oneMore : "1 more item",

 twoMore : "{0} more items",

 collapse : "collapse",

 calendarItemNew : "Add",

 calendarMenuLoading : "Loading items...",

 calendarRecurrenceException : "Exception to Recurring Event",

 calendarRecurrence : "Recurring Event",

 calendarNotifyLoading : "Loading calendar...",

 calendarNotifyUpdating : "Updating...",

 calendarErrorHeader : "Error",

 calendarClientDateOutOfSupportedRangeError : "Specified calendar is out of supported date range.",

 calendarDisabledWhileEditing : "This page may have been modified. You may need to reload this page to render the calendar view.",

 calendarDeleteConfirm : "Are you sure you want to delete this item?",

 calendarDateTimeSeparator : " - ",

 calendarViewEvent : "View Properties",

 calendarOpenItem : "Open",

 calendarResolveError : "No exact match was found for {0}.",

 invalidMerge : "The cells you are trying to merge do not have the same height or width. Change the size of the adjacent cells to match the selected cell before merging.",

 fontSizeUnitPt : "pt",

 fontSizeUnitPx : "px",

 fontSizeUnitEm : "em",

 fontSizeUnitCm : "cm",

 fontSizeUnitIn : "in",

 fontSizeUnitMm : "mm",

 fonts : "Fonts",

 themeFonts : "Theme Fonts",

 errorClipboard : "Your browser is not configured to allow access to your computer's clipboard. Use Control+X for Cut, Control+C for Copy, and Control+V for Paste.",

 clipboardNoAccess : "Your browser is not configured to allow access to your computer's clipboard. ",

 colorPickerStandardHeaderText : "Standard Colors",

 colorPickerThemeHeaderText : "Theme Colors",

 colorPickerMoreColorsText : "More Colors...",

 customBackgroundColorTitle : "Background Color",

 customColorTitle : "Color",

 editHtmlSourceTitle : "HTML Source",

 menuArrangeFloatTitle : "Float",

 menuArrangeInlineTitle : "Inline",

 selectMenuItemWithNumber : "{0} ({1})",

 selectMenuCENTER : "Centered text",

 selectMenuDIR : "Directory list",

 selectMenuFONT : "Font",

 selectMenuPLAINTEXT : "Plain text",

 selectMenuS : "Strikethrough",

 selectMenuSTRIKE : "Strikethrough",

 selectMenuU : "Underline",

 selectMenuXMP : "Plain text block",

 selectMenuRT : "Ruby text",

 selectMenuRUBY : "Ruby",

 selectMenuA : "Anchor",

 selectMenuABBR : "Abbreviation",

 selectMenuACRONYM : "Acronym",

 selectMenuADDRESS : "Address",

 selectMenuB : "Bold",

 selectMenuBDO : "Bi-directional text",

 selectMenuBIG : "Big text",

 selectMenuBLOCKQUOTE : "Quotation",

 selectMenuCAPTION : "Caption",

 selectMenuCITE : "Citation",

 selectMenuCODE : "Code",

 selectMenuDD : "Definition description",

 selectMenuDEL : "Deleted text",

 selectMenuDFN : "Definition",

 selectMenuDIV : "Text division",

 selectMenuDL : "Definition list",

 selectMenuDT : "Definition term",

 selectMenuEM : "Emphasized text",

 selectMenuFIELDSET : "Fieldset",

 selectMenuH1 : "Heading 1",

 selectMenuH2 : "Heading 2",

 selectMenuH3 : "Heading 3",

 selectMenuH4 : "Heading 4",

 selectMenuH5 : "Heading 5",

 selectMenuH6 : "Heading 6",

 selectMenuHR : "Horizontal rule",

 selectMenuI : "Italic",

 selectMenuIMG : "Image",

 selectMenuINS : "Inserted Text",

 selectMenuLABEL : "Label",

 selectMenuLEGEND : "Fieldset legend",

 selectMenuLI : "List Item",

 selectMenuMARQUEE : "Marquee",

 selectMenuMENU : "Menu list",

 selectMenuNOBR : "No line breaks",

 selectMenuOL : "Ordered list",

 selectMenuP : "Paragraph",

 selectMenuPRE : "Pre-formatted text",

 selectMenuQ : "Short quotation",

 selectMenuSAMP : "Sample",

 selectMenuSMALL : "Small text",

 selectMenuSPAN : "Span",

 selectMenuSTRONG : "Strong text",

 selectMenuSUB : "Subscript",

 selectMenuSUP : "Superscript",

 selectMenuTABLE : "Table",

 selectMenuTBODY : "Table body",

 selectMenuTD : "Table cell",

 selectMenuTFOOT : "Table foot",

 selectMenuTH : "Table header cell",

 selectMenuTHEAD : "Table head",

 selectMenuTR : "Table row",

 selectMenuTT : "Teletype text",

 selectMenuUL : "Unordered list",

 onPasteTooltip : "Paste Options",

 pasteTextLabel : "Keep Text Only",

 pasteStripLabel : "Remove Inline Styles",

 pasteHtmlLabel : "Keep Inline Styles",

 onCellTooltip : "Cell Menu",

 deleteRowLabel : "Delete Row",

 deleteColumnLabel : "Delete Column",

 splitCellLabel : "Split Cell",

 insertRowAboveLabel : "Insert Above",

 insertRowBelowLabel : "Insert Below",

 insertColumnLeftLabel : "Insert Left",

 insertColumnRightLabel : "Insert Right",

 pasteWaitScreenTitle : "Paste",

 pasteWaitScreenText : "Pasting...",

 xhtmlWaitScreenTitle : "XHTML",

 xhtmlWaitScreenText : "Converting to XHTML...",

 xhtmlDone : "XHTML conversion completed",

 htmlSourceChangedTitle : "Warning",

 htmlSourceChangedText : "The HTML source you entered might have been modified.",

 keyboardShortcutUndo_AccessKey : "Z",

 keyboardShortcutUndo_Ctrl : "t",

 keyboardShortcutUndo_Shift : "f",

 keyboardShortcutUndo_Alt : "f",

 keyboardShortcutRedo_AccessKey : "Y",

 keyboardShortcutRedo_Ctrl : "t",

 keyboardShortcutRedo_Shift : "f",

 keyboardShortcutRedo_Alt : "f",

 keyboardShortcutSelectAll_AccessKey : "A",

 keyboardShortcutSelectAll_Ctrl : "t",

 keyboardShortcutSelectAll_Shift : "f",

 keyboardShortcutSelectAll_Alt : "f",

 keyboardShortcutBold_AccessKey : "B",

 keyboardShortcutBold_Ctrl : "t",

 keyboardShortcutBold_Shift : "f",

 keyboardShortcutBold_Alt : "f",

 keyboardShortcutItalic_AccessKey : "I",

 keyboardShortcutItalic_Ctrl : "t",

 keyboardShortcutItalic_Shift : "f",

 keyboardShortcutItalic_Alt : "f",

 keyboardShortcutUnderline_AccessKey : "U",

 keyboardShortcutUnderline_Ctrl : "t",

 keyboardShortcutUnderline_Shift : "f",

 keyboardShortcutUnderline_Alt : "f",

 keyboardShortcutClearFormatting_AccessKey : " ",

 keyboardShortcutClearFormatting_Ctrl : "t",

 keyboardShortcutClearFormatting_Shift : "f",

 keyboardShortcutClearFormatting_Alt : "f",

 keyboardShortcutIndent_AccessKey : "M",

 keyboardShortcutIndent_Ctrl : "t",

 keyboardShortcutIndent_Shift : "f",

 keyboardShortcutIndent_Alt : "f",

 keyboardShortcutOutdent_AccessKey : "M",

 keyboardShortcutOutdent_Ctrl : "t",

 keyboardShortcutOutdent_Shift : "t",

 keyboardShortcutOutdent_Alt : "f",

 keyboardShortcutLeftAlign_AccessKey : "L",

 keyboardShortcutLeftAlign_Ctrl : "t",

 keyboardShortcutLeftAlign_Shift : "f",

 keyboardShortcutLeftAlign_Alt : "f",

 keyboardShortcutCenterAlign_AccessKey : "E",

 keyboardShortcutCenterAlign_Ctrl : "t",

 keyboardShortcutCenterAlign_Shift : "f",

 keyboardShortcutCenterAlign_Alt : "f",

 keyboardShortcutRightAlign_AccessKey : "R",

 keyboardShortcutRightAlign_Ctrl : "t",

 keyboardShortcutRightAlign_Shift : "f",

 keyboardShortcutRightAlign_Alt : "f",

 keyboardShortcutMakeHyperlink_AccessKey : "K",

 keyboardShortcutMakeHyperlink_Ctrl : "t",

 keyboardShortcutMakeHyperlink_Shift : "f",

 keyboardShortcutMakeHyperlink_Alt : "f",

 tablePreviewChar : "_",

 textStyleExampleText : "AaBbCcDdEeFfGgHhIiJjKkLlMm",

 reverting : "Reverting...",

 sending : "Sending...",

 emailDefaultBody : "Sample",

 saving : "Saving...",

 saved : "Saved",

 processingRequest : "Processing request...",

 deleting : "Deleting...",

 pageIsSiteHomePage : "This page is now the site homepage.",

 saveChangeDialogTitle : "Save Changes",

 saveChangeDialogDesc : "Do you want to save the changes you made to this page?",

 saveConflictStatusBarTitle : "Save Conflict:",

 saveFailed : "Cannot save the page.",

 buttonYes : "Yes",

 buttonNo : "No",

 buttonCancel : "Cancel",

 layoutsNotSupported : "This feature does not support the Layouts Editor.",

 dropdownImageAltText : "Dropdown",

 checkAltText : "Check",

 uncheckAltText : "Uncheck",

 editListItems : "Edit List Items",

 moreItems : "Not all items are shown...",

 dropdownLoading : "Loading...",

 getDataError : "This service isn't available right now.",

 getDataErrorRetry : "Click here to try again.",

 defaultViewDescription : "Default View",

 personalViewDescription : "Personal View",

 publicViewDescription : "Public View",

 moderationViewDescription : "Moderation View",

 close : "Close",

 restore : "Restore",

 maximize : "Maximize",

 dialogLoading : "Loading...",

 dialogLoadingText : "Please wait while the content loads.",

 hiddenButtonValueBeforeDialog : "Wrap focus to the end of the dialog",

 hiddenButtonValueAfterDialog : "Wrap focus to the beginning of the dialog",

 defaultDialogTitle : "Dialog",

 defaultDialogWidth : "768",

 defaultDialogHeight : "576",

 modelessDialogsNotImplemented : "Modeless dialogs are not implemented.",

 dialogCancelAK : "C",

 confirmWelcomePage : "If you make this page the home page for this site, users will no longer be able to see the old site home page. Do you want to set this page as this site's home page?",

 relationships_UnknownError : "Unknown Error.",

 relationships_RelatedFieldsFetchFailed : "Failed to retrieve related lists for this list.",

 relationships_DocLibHasNoRelatedLists : "The {0} document library does not have any related lists.",

 relationships_ListHasNoRelatedLists : "The {0} list does not have any related lists.",

 relationships_RelatedFetchFieldsTookTooLong : "A timeout occurred while retrieving related lists for this list.",

 relationships_ListFormsFetchFailed : "Failed to retrieve forms for list.",

 relationships_NoNewEditDisplayListForms : "This list does not contain any forms.",

 relationships_DefaultDisplayForm : "Default Display Form",

 relationships_DefaultNewForm : "Default New Form",

 relationships_DefaultEditForm : "Default Edit Form",

 relationships_CTForms : "Content Type Forms",

 relationships_CTDisplayForm : "({0}) Display Form",

 relationships_CTNewForm : "({0}) New Form",

 relationships_CTEditForm : "({0}) Edit Form",

 relationships_ToolTipTitle : "Insert Related Items Web Part",

 dlgTitleDel : "Delete Items",

 dlgTitleCheckin : "Check In Files",

 dlgTitleCheckout : "Check Out Files",

 dlgTitleError : "The server has encountered the following error(s):",

 errorDialogTitleText : "Error",

 genericLoading : "Loading...",

 webPartNotDisplay : "This web part cannot be displayed. Save and stop editing the page to see the web part.",

 viewGroupDefault : "Default",

 viewGroupPersonal : "Personal",

 viewGroupPublic : "Public",

 viewGroupModerated : "Moderated",

 viewGroupOther : "Other",

 modifyViewLabel : "Modify View",

 modifyInDesignerLabel : "Modify in SharePoint Designer (Advanced)",

 workflowSettingsLabel : "Workflow Settings",

 associateWorkflowLabel : "Add a Workflow",

 createWorkflowInDesignerLabel : "Create a Workflow in SharePoint Designer",

 createReUsableWorkflowInDesignerLabel : "Create a Reusable Workflow in SharePoint Designer",

 sendToOtherLocation : "Other Location",

 okButtonCaption : "OK",

 detailsView : "Details",

 thumbnailsView : "Thumbnails",

 filmstripView : "Filmstrip",

 modifyThisView : "Modify this view",

 createView : "Create view",

 editColEditable : "Editable",

 editColReadOnly : "Read Only",

 deleteText : "Delete",

 deleteFailed : "This item could not be deleted",

 requiredField : "This field requires a value.",

 serverCommError : "Unable to communicate with the server.",

 savingToServer : "Changes are currently being saved back to the server.  If you leave now, some of those changes may be lost.",

 changesHaveNotBeenSaved : "Changes have been made that have not yet been saved back to the server.  If you leave now, all of those changes will be lost.",

 docAlreadyExists : "The specified name is already in use.  A summary task cannot have the same name as another summary task in the current list.",

 ganttListName : "Project Tasks",

 createWaitScreenCancel : "Cancel",

 checkInComments : "Comments:",

 okButtonText : "OK",

 continueButtonText : "Continue",

 cancelButtonText : "Cancel",

 okCancelButtonWidth : "6em",

 alignRight : "right",

 pageStateCancelApprovalLabel : "Cancel Approval",

 pageStateDiscardCheckoutLabel : "Discard Check Out",

 pageStateDontSaveLabel : "Exit Without Saving",

 pageStateEditLabel : "Edit",

 pageStateSaveLabel : "Save",

 pageStateCheckinLabel : "Check In",

 pageStateCheckoutLabel : "Check Out",

 pageStateOverrideCheckoutLabel : "Override Check Out",

 pageStatePublishLabel : "Publish",

 pageStateSaveAndStopEditingLabel : "Save and Stop Editing",

 pageStateSubmitForApprovalLabel : "Submit for Approval",

 pageStateUnpublishLabel : "Unpublish",

 pageStateApproveLabel : "Approve",

 pageStateRejectLabel : "Reject",

 pageStateSaveBeforeNavigateUnknownError : "The page took too long to save. You can click "Cancel", and then try to save the page again. If you click "OK", you might lose unsaved data.",

 pageStateSaveBeforeNavigateNotCheckedOutWarning : "To save your changes before continuing, click "OK". To continue without saving changes, click "Cancel".",

 pageStateSaveBeforeNavigateLastChanceWarning : "You have made changes to your page that have not yet been saved. Are you sure you want to discard those changes?",

 tenantAdmin_SiteCollectionNewDialogTitle : "Create Site Collection",

 tenantAdmin_SiteCollectionDeleteDialogTitle : "Delete Site Collections",

 tenantAdmin_SiteCollectionPropertiesDialogTitle : "Site Collection Properties",

 tenantAdmin_SiteCollectionOwnersDialogTitle : "Site Collection Owners",

 tenantAdmin_SiteCollectionDiskQuotaDialogTitle : "Site Collection Disk Quota",

 rteDialog_Height : "175",

 rteDialog_Width : "400",

 renamePageDialog_Title : "Rename Page",

 webPartPageRecycleConfirmation : "Are you sure you want to send this page to the site Recycle Bin?",

 webPartPageDeleteConfirmation : "Are you sure you want to permanently delete this page?"


Some mighty fine messages ready for reuse...  I'll continue to post some more obscure things I'm finding as I dig deeper into the depths of sp.js.  

Monday, April 2, 2012

Modifying the default language in InfoPath

This will be a quick exercise on how to change the language you can write within InfoPath 2007...

Click on Form Options
Crack open your favorite form builder and then click on Tools.  From that menu, click on Programming on the left...  You should already know where I'm going with this.  Let's change that default (C#) to something nonsensical, unstructured and putrid; yet everyone seems to be jumping on the bandwagon: JavaScript or in this case JScript. Whatev...

(¸¸.♥➷♥•*¨)¸.•´¸.•*¨) ¸.•*¨)
(¸.•´(¸. ¸.•´¸.•*¨) ¸.♥➷•*¨)
──██─▀██▄██▀─▀█▄█▀─██▀█──all ♥➷♥
¸.•´¸.•*¨) ¸♥.•*¨) (¸.•´¸♥➷♥¸.•´♥¸.•´♥¸.•*¨)♥.•*¨)¸.•*♥¸

Form Options

(¸¸.♥➷♥•*¨)¸.•´¸.•*¨) ¸.•*¨)
(¸.•´(¸. ¸.•´¸.•*¨) ¸.♥➷•*¨)
──██─▀██▄██▀─▀█▄█▀─██▀█──all ♥➷♥
¸.•´¸.•*¨) ¸♥.•*¨) (¸.•´¸♥➷♥¸.•´♥¸.•´♥¸.•*¨)♥.•*¨)¸.•*♥¸

I've decided from now on any InfoPath post will need something extra for me, so I can start to



Want to see what damage I can do?  Stay tuned...

Monday, March 26, 2012

Get the ID of any SharePoint Wiki page

Hello _spPageContextInfo!!!
I found this little gem by viewing the source of my site  Simply running a console.dir() on this object reveals these properties:

  1. alertsEnabledtrue
  2. allowSilverlightPrompt"True"
  3. currentLanguage1033
  4. pageItemId1
  5. pageListId"{15d98081-a722-48ad-a30c-c25d4c31a228}"
  6. siteServerRelativeUrl"/"
  7. userId1
  8. webLanguage1033
  9. webServerRelativeUrl"/"
  10. webUIVersion4

I've noticed running this same inquiry on a different site, say, the properties are different:

  1. alertsEnabledtrue
  2. allowSilverlightPrompt"True"
  3. currentLanguage1033
  4. pageListId"{a4668dd2-4dac-4cce-baf0-ddb1ea4f2d5e}"
  5. siteServerRelativeUrl"/"
  6. webLanguage1033
  7. webServerRelativeUrl"/blog"
  8. webUIVersion4

It's safe to say, this is because the latter is a blog and not a wiki.

What to make of all of this?  I'm able to display pertinent data accordingly and no longer required to rely on the text of the page name.

var pageID = _spPageContextInfo.pageItemId;

It's a much cleaner solution that just feels good.

I've been working on a commenting system for a SharePoint 2010 system.  Knowing some of these internal variables that are floating around is invaluable.  Hope you find this useful as well.

Yes Josh, I'm going to finish this finally. ;-)

Wednesday, March 21, 2012

Add Notepad++ to the context menu

This little regedit keeps coming in handy.  Thought I’d add it here so it’s easy for me to find and the other 2 of you that read this blog.

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\*\shell\Open with Notepad++\command]
@="\"C:\\Program Files (x86)\\Notepad++\\notepad++.exe\" \"%1\""

ContextMenuSave that into a file with the extension .reg and double click it to add to the registry.  You’ll now see Open with Notepad++ in the context menu.

Pretty awesome, eh?  You can add whatever else you like to your context menu as well.  As always though be cautious when modifying the registry.

I’ve seen people wreck, destroy, obliterate their machine fiddling with things they shouldn’t have.  With that said, have fun doing it!

This post was inspired by:

Friday, March 2, 2012

roboCAML v0.4 has been released!

RoboCAMLYou may have seen me tweet about roboCAML the last few days.  If you were scratching your head about what it actually is, don’t feel bad.  No one knew…  It’s a jQuery module, *not* a plugin, I’ve built specifically to handle the tedious task of manually building CAML or worse hard coding CAML within your scripts.  An added benefit, is the ability to create dynamic CAML queries on the fly.

roboCAML has a depends on jQuery, but very well may become a  pure JavaScript module without any external dependencies.  It's hard to beat the $.ajax function within jQuery, but I'm willing to change based on feedback.  It’s all about the community, you know?

What does it do?

roboCAML takes away the pain of scripts that look like this:
     // #CAMLToggle doesn't exist, but this is here in case we want to give the user the ability to AND
     // or OR the PartNum
  var camlToggle = $("#CAMLToggle").val() ? $("#CAMLToggle").val() : "Or", 
   ddlSelected = false, 
   PartCat = [], 
   PartNum = [], 
   thisFieldRef = "", 
   camlQuery = "";
  $( "select.PartCat" ).each(function() {
   // Each select on the page has the PartCat class
   // the title attribute is also the name of the field for the CAML
   ctlTitle = $(this).attr("title");
   PartCat[ctlTitle] = ctlTitle;
   if ( ctlTitle ) != 0 ) {
    ddlSelected = true;
//First example of CAML engine
  i = 0;
  for (index in PartCat) {
   //console.log("Select title: " + i);
   //console.log("Select Val: " + PartCat[i]);
   if (PartCat[index] > 0) {
    thisFieldRef = "<Eq><FieldRef Name='" + index + "' LookupId='True' /><Value Type='Number'>" + PartCat[index] + "</Value></Eq>";
    if (i <= 1) {
     camlQuery += thisFieldRef;
    if (i == 1) {
     camlQuery = "<And>" + camlQuery + "</And>";
    if (i > 1) {
     camlQuery = "<And>" + camlQuery + thisFieldRef + "</And>";
//Show example of other CAML Engine
  $("input.PartNum:checked").each(function(index) {
   // If this isn't the first PartNum, we'll "wrap" the array with the camlToggle
   if(index > 0) {
    PartNum.unshift("<" + camlToggle + ">");
   PartNum.push("<Eq><FieldRef Name='PartNum' LookupId='True' /><Value Type='Number'>" + $(this).attr("alt") + "</Value></Eq>");
   // If this isn't the first PartNum, we'll "wrap" the array with the camlToggle
   if(index > 0) {
    PartNum.push("</" + camlToggle + ">");
  // .join() defaults to commas, .join("") does the same thing. We'll join with a space, then replace
  // the spaces that fall between tags
  camlQuery += PartNum.join(" ").replace(/> </gi,"><");
  if ( $("input.PartNum:checked").length > 0 && ddlSelected ) {
   // If we have DDLs and PartNums, we'll <And> the two groups together, otherwise we won't
   camlQuery = "<And>" + camlQuery + "</And>";
  camlQuery = "<Query><Where>" + camlQuery + "</Where><OrderBy><FieldRef Name='Title' Ascending='True' /></OrderBy></Query>";

Within this script are two different ways to dynamically build CAML.  Each have their merits, but why do I have to think about setting up my CAML correctly and debugging it if I am having issues?  After being tasked to build a few of the complex scripts, sometimes several in a week, I had enough...  Time to roll up the sleeves and code a solution.  This is why I’ve built roboCAML, so now lets see what it does.

roboCAML In Action

roboCAML will assist in generating a string of useful CAML for use when making your Web Service or Client Object Model calls.  There are currently 5 methods available in roboCAML.


So, now let’s look at roboCAML.BatchCMD method.  This method is a little tricky because there are three distinct actions you can take when using a batch.  Each requiring a different set of parameters.  In example 2.1, we’ll look at Deleting:
Example 2.1:
   batchCMD: 'Delete',
   IDs: [1,2,3]

This call accepts an array of ID’s and the “Delete” command.  The output will be:
<Batch OnError='Continue'><Method ID='3' Cmd='Delete'><Field Name='ID'>3</Field></Method><Method ID='2' Cmd='Delete'><Field Name='ID'>2</Field></Method><Method ID='1' Cmd='Delete'><Field Name='ID'>1</Field></Method></Batch> 
Now onto the New operation.  This example accepts the command for the batch.  What is different here from the Delete operation is, we can now pass in a valuePairs parameter.  This parameter accepts an array of arrays.  You’ll notice each array follows a certain pattern.  First the Static Name is provided and then the value.
Example: 2.2:
 batchCMD: "New",  
 valuePairs: [["PersonnelLookup", 1]]  //Static Column Name, Value

A more complex array would look like this:
 batchCMD: "New",  
 valuePairs: [["PersonnelLookup", 1, "ModuleNotes", "ModuleNotes", "Description", "Googly Glop"], ["ListUID", 3]]  //Static Column Name, Value

Which in turn produces:
<Batch OnError='Continue'><Method ID='1' Cmd='New'><Field Name='PersonnelLookup'>1</Field><Field Name='ModuleNotes'>ModuleNotes</Field><Field Name='Description'>Googly Glop</Field></Method><Method ID='2' Cmd='New'><Field Name='ListUID'>3</Field></Method></Batch> 
The last operation within roboCAML.BatchCMD is Update:

Example 2.3
 updates: [
   //Notice batchCMD isn't present...
   //Static Column Name, Value
   valuePairs: ["Title", "Numero Tres", "PercentComplete", 1, "Boolean", 0, "ID", 3]
   //Defaults to Update anyway. No need to pass it.
   batchCMD: "Update",
   valuePairs: ["ID", 4, "Title", "Item4", "Boolean", 0]
   batchCMD: "New",
   valuePairs: ["Title", "Something New", "PercentComplete", 1]
   batchCMD: "Delete",
   ID: 6

The output of the call above will generate:
<Batch OnError='Continue'>
 <Method ID='1' Cmd='Update'>
  <Field Name='Title'>Numero Tres</Field>
  <Field Name='PercentComplete'>1</Field>
  <Field Name='Boolean'>0</Field>
  <Field Name='ID'>3</Field>
 <Method ID='2' Cmd='Update'>
  <Field Name='ID'>4</Field>
  <Field Name='Title'>Item4</Field>
  <Field Name='Boolean'>0</Field>
 <Method ID='3' Cmd='New'>
  <Field Name='Title'>Something New</Field>
  <Field Name='PercentComplete'>1</Field>
 <Method ID='4' Cmd='Delete'>
  <Field Name='ID'>6</Field>

What’s very interesting with using the updates property of roboCAML.BatchCMD is the batch that is generated can be chocked full of all your different operations. Delete, New and Update all within one Web Service call. That’s #bada55.


And if you weren’t impressed by any of the above, maybe this will persuade you… It’s another snazzy way to build CAML on the fly. Here’s how to use roboCAML.OrderBy:

 MyColumn: false,
 ID: true
Note: The value of each staticName can be a boolean or a string... The output of the call above will look like this:
<OrderBy><FieldRef Name='MyColumn' Ascending='False' /><FieldRef Name='ID' Ascending='True' /></OrderBy>


Probably the most interesting method roboCAML has to offer.  There are a plethora of options within this method.  The best way to learn how to use this would be to read the docs or use the live demo.  Let’s look over a sample query you can use with roboCAML.
 listName: 'Calendar', 
 closeCaml: "Clientom",
 ViewFields: ["ID", "Created", "Title"],
 OrderBy: {
  ID: true
 QueryOptions: {
  IncludeMandatoryColumns: false
 config: [
   filter: "&&",
   op: "*",
   staticName: "Title",
   value: "Daily"
   filter: "&&",
   op: "^",
   staticName: "Title",
   value: "Deleted"
   op: "!=",
   staticName: "ID",
   value: 3

Since the CAML is not hardcoded, you can now easily generate whatever options needed to retrieve information from SharePoint.  Just like above in the “CAML Engines”, stuff an array full of info and do something with it...  I’m thinking of building a demo soon that will serve as a real world example of why this is useful for front-end development.

In case you were wondering, this is the output from heavy lifting done from roboCAML:

  <FieldRef Name='Title' />
  <FieldRef Name='Created' />
  <FieldRef Name='ID' />
     <FieldRef Name='Title' />
     <Value Type='Text'>Daily</Value>
      <FieldRef Name='Title' />
      <Value Type='Text'>Deleted</Value>
      <FieldRef Name='ID' />
      <Value Type='Counter'>3</Value>
   <FieldRef Name='ID' Ascending='True' />


This method will assist you in building queries and other various CAML fragments that you may need.  I haven’t found a comprehensive list that details all of the options available within this node.  The closest I’ve come to full documentation was on the Lists.GetListItems Method page within MSDN.  As I find/test/evaluate each new option I find, I’ll piecemeal them into the project. For now, the documentation can be found on the roboCAML project page.


As you could guess (if you are familiar with CAML), this does exactly what you would expect. Let’s take a look at roboCAML.ViewFields:

roboCAML.ViewFields(["Title", "Description", "ProjectName", "RelatedID"]);

This method accept an array of Static Names. The output will be:
<ViewFields><FieldRef Name='RelatedID' /><FieldRef Name='ProjectName' /><FieldRef Name='Description' /><FieldRef Name='Title' /></ViewFields> 
It’s just that simple… Really!

There you have it, roboCAML in a nutshell.  I’m missing some key parts that you *should* be able to do when creating CAML queries for SharePoint. I plan on adding them very soon!  Support for <Membership />, <Joins> (if it’s possible, haven’t tried yet...), and <ProjectedFields> are on top of my list as well as nested CAML fragments (Thanks Jim Bob!).  If you can think of anything you’d like to see added, feel free to ask