tag:blogger.com,1999:blog-27061708785525575202024-03-08T10:45:28.293-06:00Programmer RamblingsRamblings on Sharepoint (WSS3.0), .NET Technologies (C#, ASP.NET, AJAX), SQL Server, Javascript, etc.Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.comBlogger57125tag:blogger.com,1999:blog-2706170878552557520.post-30452290158164418052011-11-03T23:37:00.002-05:002011-11-03T23:41:19.632-05:00New LookAs you may have noticed, the site (finally) has a new theme applied. The blue-and-white circa 2007 theme is history! <br /><br />I also took the opportunity to upgrade to the latest 3.x (hosted) version of Syntax Highlighter for code snippets. I went back through all the old posts and updated everything accordingly - hopefully I didn't miss/break anything.Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com0tag:blogger.com,1999:blog-2706170878552557520.post-57586193036121396972011-07-28T22:58:00.010-05:002011-11-03T23:59:35.173-05:00Clientside API for ASP.NET User ControlsHave you ever had an ASP.NET User Control that performed some simple function that you needed available via javascript on the containing page? Perhaps it's simply a combination of controls like an address/city/state/zip block, or maybe it's a custom control like a fancy combobox you pieced together. Whatever it is, it's generally risky and/or a pain to interact with it via javascript on the containing page.
<br />
<br />The reason this is difficult is because all the element id's are generated dynamically, and you have no way of knowing what the User Control's element ids will be at runtime - therefore you don't know how to reference them back in your code on the containing Page. Now this is where many people will advise you to simply use the jQuery "ends-with" selector here and just hard-code the element ids you're looking for, but I have another option for you.
<br />
<br />Although the jQuery "ends-with" approach does work, you are still hard-coding element ids on a page they don't belong on. I think a more appropriate solution is to expose the javascript methods and properties by extending the User Control itself client side. The problem though is that a User Control by itself doesn't actually render any DOM, so there's nothing to extend. Only a User Control's contents are actually rendered. This solution actually exploits that fact by adding a single wrapper div inside the User Control and giving it the same ID as the actual User Control like so:
<br />
<br /><pre class="brush: xml"><%@ Control Language="C#" AutoEventWireup="true" CodeBehind="UcDemo.ascx.cs" ...etc... %>
<br /> <!-- We use this.ID so users can reference our usercontrol's ID on their page with $get and it will have our methods/properties available off of it -->
<br /> <div id="<%= this.ID %>">
<br /> .... actual content....
<br /> </div>
<br /></pre>
<br />Now we'll have a known element id we can reference on the containing Page, because it'll actually be the ID that we assign the User Control!
<br />
<br />Now all we have to do is expose our methods and properties by using jQuery's extend method:
<br /><pre class="brush: xml"><%@ Control Language="C#" AutoEventWireup="true" CodeBehind="UcDemo.ascx.cs" ...etc... %>
<br /> <!-- We use this.ID so wecan reference our usercontrol's ID on the containing page and it will have our methods/properties exposed -->
<br /> <div id="<%= this.ID %>">
<br /> <asp:textbox id="TextBox1" runat="server"></asp:textbox>
<br />
<br /> <script type="text/javascript">
<br /> Sys.Application.add_init(function () {
<br /> // dummy/temp object we can used to extend our wrapper div
<br /> myObj = {
<br /> SayHello: function () {
<br /> alert('Hello World!');
<br /> },
<br /> get_textbox1: function () {
<br /> return $("#" + "<%= TextBox1.ClientID %>").val();
<br /> },
<br /> set_textbox1: function (value) {
<br /> return $("#" + "<%= TextBox1.ClientID %>").val(value);
<br /> }
<br /> }
<br />
<br /> // extend this usercontrol's wrapper div with our methods
<br /> $.extend($get("<%= this.ID %>"), myObj);
<br /> });
<br /> </script>
<br /> </div>
<br /></pre>
<br />So now we've just added 3 methods to our little container div that will be available once we select the element.
<br />To do that, our containing page would look something like this:
<br />
<br /><pre class="brush: xml">
<br /><html>
<br /> <body>
<br /> ...
<br /> <a href="#" onclick="$get('demo1').SayHello();">Say Hello</a>
<br /> <a href="#" onclick="$get('demo1').set_textbox1('TESTING');">Set "TESTING" to the Textbox Value</a>
<br /> <a href="#" onclick="alert($get('demo1').get_textbox1());">Alert the Textbox Value</a>
<br /> ...
<br /> <uc:demo id="demo1" runat="server"></uc:demo>
<br /> ...
<br /> </body>
<br /></html>
<br /></pre>
<br />Now the only hard-coded element id you have on the page is actually to the user control. Another added bonus is that all your javascript methods on the user control are all neatly scoped in the add_init function (or document ready or whatever you're using) so nothing is in the global namespace.
<br />
<br />Full demo project available below:
<br /><iframe title ="Preview" scrolling="no" marginheight="0" marginwidth="0" frameborder="0" style="width:98px;height:115px;padding:0;background-color:#fcfcfc;" src="https://skydrive.live.com/embedicon.aspx/UserControlClientAPI?cid=f7c93b143c55f787&sc=documents"></iframe>Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com10tag:blogger.com,1999:blog-2706170878552557520.post-61682360158264544142011-07-24T23:19:00.004-05:002011-10-03T23:08:49.808-05:00ASP.NET Session Timeout Control with jQuery UI DialogAs you may or may not be aware, back in 2009 I <a href="http://programmerramblings.blogspot.com/2009/03/aspnet-session-timeout-control-jquery.html">posted</a> code where I had extended an ASP.NET session timeout control originally created by <a href="http://weblogs.asp.net/traviscollins/archive/2008/02/22/ajax-timeout-server-control.aspx">Travis Collins</a>. That control then later evolved into a second <a href="http://programmerramblings.blogspot.com/2009/08/aspnet-session-timeout-control-jquery.html">version</a> that included a countdown timer, and later still I made it <a href="http://programmerramblings.blogspot.com/2010/08/aspnet-40-session-timeout-control.html">.NET 4.0 compatible</a>. All along the way I attempted to retain as much of the original code as possible, supporting the original shown/hidden div approach Travis created - as well as my jQuery UI dialog approach. While this seemed like a good idea at the time, it ultimately led to some ugly code as well as a bit of code bloat for no good reason - and I've finally gotten around to doing something about it.<div><br /></div><div>I've rewritten most of the code now to such a degree that it looks nothing like the original control from Travis. The javascript class especially has been completely reworked, notably forgoing prototypes for simple closures. I did this largely for two reasons. The first is that there's no reason to have more than one Timeout class instantiated on a page. The second is that I hope the closure style is simpler for everyone to follow. </div><div><br /></div><div><div>Please note that due to all these changes, I have renamed the project, namespace, etc. - dropping all references to the original TSC name - as well as reset the version number to 1.0.</div><div><br /></div><div>This new version functionally is very similar to the last, but jQuery is now a requirement. Also partial postbacks are no longer optional - they will simply always reset the control just as a full postback would. The countdown timer is also no longer optional. It is simply required. If you don't want to show it, simply place a display hidden style on the span.</div></div><div><br /></div><div>The demo project has been updated to use the latest version of jQuery and the jQuery UI (installed as NuGet packages).</div><div><br /></div><div>There is one new bit of functionality to take note of. You can now reset the timeout from outside the control (something requested a few times over the years). Simply grab a reference and call the reset() method using $find like this:</div><div>$find('myTimeout1').reset();</div><div><br /></div><div>Also to foster social development, I have moved the code to a github <a href="https://github.com/kennethscott/ASP.NET-Session-Timeout-Control">repository</a> so fork away!</div><div><br /></div>Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com55tag:blogger.com,1999:blog-2706170878552557520.post-77018780716193861482010-08-12T18:31:00.004-05:002010-08-12T23:34:41.353-05:00VisualSVN Server SearchI've always been a huge fan of the <a href="http://www.visualsvn.com/server/">VisualSVN Server</a> product and how easy it makes installation and setup of Subversion on Windows. You can be up and running with a single install in a matter of minutes. It's a very nice and neat package for source control. Best of all, it's FREE.<br /><br />The one drawback with the product (and Subversion in general) is that there's no easy way to Search the contents. Sure, there's products like <a href="http://www.atlassian.com/software/fisheye/">FishEye</a> and <a href="http://svnquery.tigris.org/">SvnQuery</a> - but FishEye costs $$$ and SvnQuery can't easily search across repositories.<br /><br />Does it really have to be so hard? Well, maybe not. If you happen to have some sort of web crawler/spider software (i.e. <a href="http://www.copernic.com/">Copernic</a>, <a href="http://www.dtsearch.com/">dtSearch</a>, <a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=cea31a4f-a8b4-4864-b520-be612becdcfa&displaylang=en">MS Search Server 2010 Express</a>, etc etc) that's capable of crawling sites and indexing the contents - you might be thinking, "Why don't I just point the spider at the SVN root and let it crawl?". What'd you quickly learn is that VisualSVN's repository browser doesn't actually serve HTML. It serves XML and XSLT, and your browser actually transforms it into HTML as you browse. Now I don't have a lot of experience with web crawlers, but I couldn't find one smart enough to do that.<br /><br />Then it hit me - why not just write a simple HTTP Handler and let it transform the XML/XSLT into HTML server-side. That way the crawler would actually have access to real HTML it could crawl.<br /><br />That night I threw together a little C#/ASP.NET project doing just that - and guess what? It works great! The crawler was able to crawl the generated HTML and index the content just fine. The coding was a little tricky until I got my head around how everything basically points to the handler aspx page, but includes a querystring parameter with the true URL to render.<br /><br />After I got it going and started playing with the indexed content, I realized my crawler's search page actually provides links to the cached document it received during the crawl, as well as live links to the document. I thought it'd be kind of cool if the live links could serve up Syntax-Highlighted source files, so I implemented Alex Gorbatchev's <a href="http://alexgorbatchev.com/SyntaxHighlighter/">SyntaxHighlighter</a>. Basically the Handler checks the UserAgent string and if you're a spider, it returns the plain text content. If you're not a spider, it dynamically loads the needed javascript and css files to syntax highlight the document.<br /><br />I did wind up making one additional change so that when the handler serves content to a spider, it prepends the contents of the source file with the Repository location and full filename. I found this increased the hit accuracy.<br /><br />The entire app is completely config file driven. Most settings are in the standard web.config file - things like the URL to SVN, UserId & Password (if needed), Paths to your SyntaxHighlighter files, etc. There's also an additional config file named BrushConfig.xml that contains mappings for all the enabled file extensions and their brush aliases. Simply add/remove file extensions as needed to enable/disable SyntaxHighlighting of a particular file type.<br /><br />To try it out, just open the solution in Visual Studio, edit the web.config AppSettings to point it to your SVN root, and hit run. The solution is configured to run using the integrated development webserver so it should fire right up and you'll be browsing your SVN root. When you come to a source file, it should SyntaxHighlight as soon as you view it. The generated repository browsing pages are very bare-bones, but remember they're just for the spider. You'll actually be interacting with your Search product after it's crawled this generated content.<br /><br />So after you set it up in IIS somewhere (I actually installed it on the same server with VisualSVN Server) - point your Search crawler at the Handler and let 'er rip! It'll index across multiple repositories and everything-<br /><br />Comments & Critiques welcome-<br /><br /><iframe title="Preview" marginheight="0" marginwidth="0" style="width: 98px; height: 115px; padding: 0pt; background-color: rgb(252, 252, 252);" src="http://cid-f7c93b143c55f787.office.live.com/embedicon.aspx/SvnBrowser/SvnBrowserSolution.zip" scrolling="no" frameborder="0"></iframe>Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com7tag:blogger.com,1999:blog-2706170878552557520.post-27493413884010690662010-08-07T16:07:00.007-05:002011-07-25T00:10:59.394-05:00ASP.NET 4.0 Session Timeout Control + jQuery Dialog + Countdown Timer<div><span style="color: red;">UPDATE!!</span> - 7/25/2011 - New version + moved to github - see this <a href="http://programmerramblings.blogspot.com/2011/07/aspnet-session-timeout-control-with.html">post</a> for details.</div><div><br /></div>As part of my TSC Timeout Control modifications series, I'd like to present an updated version of the control that is built using the current .NET 4.0 Framework as well as current versions of jQuery (1.4.2) and the jQuery UI (1.8.3).<br /><br /><iframe title="Preview" scrolling="no" marginheight="0" marginwidth="0" frameborder="0" style="width:320px;height:250px;padding:0;background-color:#fcfcfc;" src="https://skydrive.live.com/embedphoto.aspx/Session%20Timeout%20Control%20.NET%204.0/Timeout4.0.png?cid=f7c93b143c55f787&sc=documents"></iframe><br /><br />There were a couple of minor tweaks, but it's basically just an update to bring the control and demo app current.<br /><br />The attached zip contains a Visual Studio 2010 solution with 2 projects. One for the Timeout control itself, and another for a demo web app using a Master page setup.<br /><br />If you're not familiar with the control, please read through the previous two posts that detail its usage <a href="http://programmerramblings.blogspot.com/2009/08/aspnet-session-timeout-control-jquery.html">here (v2)</a> and <a href="http://programmerramblings.blogspot.com/2009/03/aspnet-session-timeout-control-jquery.html">here (v1)</a>.<br /><br /><iframe title="Preview" scrolling="no" marginheight="0" marginwidth="0" frameborder="0" style="width:98px;height:115px;padding:0;background-color:#fcfcfc;" src="https://skydrive.live.com/embedicon.aspx/Session%20Timeout%20Control%20.NET%204.0?cid=f7c93b143c55f787&sc=documents"></iframe>Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com40tag:blogger.com,1999:blog-2706170878552557520.post-75227367514934510442009-08-03T22:58:00.013-05:002011-11-03T23:04:35.295-05:00ASP.NET Session Timeout Control + jQuery Dialog + Countdown Timer<span style="color: rgb(255, 0, 0);">UPDATE: 8/7/2010 4:38PM<br /></span>New .NET 4.0 version available <a href="http://programmerramblings.blogspot.com/2010/08/aspnet-40-session-timeout-control.html">here</a>.<br /><span style="color: rgb(255, 0, 0);"><br />UPDATE: 9/27/2009 10:16PM</span><br />I originally posted this control using the jCountr jquery plugin to handle the countdown timer for me. Since then it has been brought to my attention that there were issues with this approach. Not to say the problems were from the jCountr plugin itself, but most likely my implementation of it. Regardless, I have since abandoned the jCountr plugin approach and reverted to plain javascript for handling the countdown timer. This seems to work better and give me more control over the process as well. Both attached zip files have been updated accordingly.<br /><span style="color: rgb(255, 0, 0);">END UPDATE</span><br /><br />This is my second iteration of what was originally Travis Collins' <a href="http://weblogs.asp.net/traviscollins/archive/2008/02/22/ajax-timeout-server-control.aspx">ASP.NET Session Timeout Control</a>.<br /><br />Note: You may want to read through my original <a href="http://programmerramblings.blogspot.com/2009/03/aspnet-session-timeout-control-jquery.html">post</a> about version1 of this control as I will only be covering changes here in this post.<br /><br />Essentially, v1 expanded upon Travis' original design and replaced a simple timeout warning div with a jQuery UI Dialog popup.<br /><br />Based upon feedback from several users (and my own continued use of the control), I now present to you v2 of this control.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://q5k4ua.bay.livefilestore.com/y1pq0SOrMDcxrZESMjpTRch4f42kgEbif09tmnNBwF6ZH_pWKU4A-_-Y3zFnewrRsSWM3OoJhbhaTPpZBjQ-Nggcx_mngf2AP6r/warning_dialog.PNG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 387px; height: 280px;" src="http://q5k4ua.bay.livefilestore.com/y1pq0SOrMDcxrZESMjpTRch4f42kgEbif09tmnNBwF6ZH_pWKU4A-_-Y3zFnewrRsSWM3OoJhbhaTPpZBjQ-Nggcx_mngf2AP6r/warning_dialog.PNG" alt="" border="0" /></a><br />The first and most notable change is a countdown timer. If enabled, when the warning popup appears - it will countdown the time remaining until session timeout so that users can clearly see exactly how long they have left. Of course the timer calculates its countdown time based on the Timeout control's settings (which actually comes from the normal Session tag in your web.config).<br /><br />The countdown timer is implemented using a new property on the control called <span style="font-weight: bold;">CountDownSpanID</span>. This controls the span element used to display the countdown timer. *Note this setting is completely optional.<br /><br />The next change is the ability to control whether async/partial postbacks reset session.<br />This is also implemented via a new property on the control - <span style="font-weight: bold;">ResetSessionOnAsyncPostback</span>.<br /><br />ResetSessionOnAsyncPostback is a boolean value that defaults to TRUE. This means even if you don't set it - the control will work just as it did in version1.<br /><br />The last change is a very simple property called <span style="font-weight: bold;">DirtyFormSpanID</span>. When set, the specified span will be given a value of "False" just prior to the timeout redirect. I use this option in conjunction with other special purpose code to implement dirty form warnings if a user attempts to leave a page with unsaved changes. Please understand this control <span style="font-weight: bold;">does not implement any type of dirty form warning</span>. <span style="font-weight: bold;">ALL </span>this option does is set a span value to "False" upon timeout redirect. Obviously, this setting is completely optional.<br /><br />Here's an example of how to set all three of the new properties, as well as how to easily add the span for the countdown timer:<br /><pre class="brush: html"><br /><tsc:Timeout ID="Timeout1" runat="server" title="Session Expiring" Enabled="true" TimeoutURL="~/TimeOut.aspx" ResetSessionOnAsyncPostback="true" CountDownSpanID="tscCounter" DirtyFormSpanID="dirtyForm"><br /><Template> <br /> <p><br /> <span class="ui-icon ui-icon-alert" style="float:left; margin: 1px 10px 20px 0;"></span><br /> Your session is about to Expire.<br /> </p><br /> <span id="tscCounter"></span><br /> <p>Click <b>OK</b> to continue your session.</p><br /></Template><br /></tsc:Timeout><br /></pre>Just remember to add references to your script files (jquery, jqueryUI, bgiframe) and you should be good to go.<br /><br />Oh one more thing, if you're upgrading from the first version of my control, be aware I bumped the version number so you'll need to redo the reference to the dll.<br /><br />That's it.<br /><br />Full Timeout Control project (source, dll's, etc):<br /><iframe marginheight="0" marginwidth="0" style="border: 1px solid rgb(221, 229, 233); margin: 3px; padding: 0pt; width: 240px; height: 66px; background-color: rgb(255, 255, 255);" src="http://cid-f7c93b143c55f787.skydrive.live.com/embedrowdetail.aspx/ASPNET%7C_jQueryUI%7C_Dialog%7C_Timeout%7C_With%7C_Counter/TSC.Timeout%7C_With%7C_Counter.zip" scrolling="no" frameborder="0"></iframe><br /><br />Full working Web App solution demonstrating usage:<br /><iframe marginheight="0" marginwidth="0" style="border: 1px solid rgb(221, 229, 233); margin: 3px; padding: 0pt; width: 240px; height: 66px; background-color: rgb(255, 255, 255);" src="http://cid-f7c93b143c55f787.skydrive.live.com/embedrowdetail.aspx/ASPNET%7C_jQueryUI%7C_Dialog%7C_Timeout%7C_With%7C_Counter/ASPNET%7C_jQueryUI%7C_Dialog%7C_Timeout%7C_With%7C_Counter.zip" scrolling="no" frameborder="0"></iframe>Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com63tag:blogger.com,1999:blog-2706170878552557520.post-15257187590883237642009-03-13T00:05:00.015-05:002011-11-03T23:07:19.591-05:00ASP.NET Session Timeout Control + jQuery Dialog<span style="color: rgb(255, 0, 0);">UPDATE!</span> 8/7/2010 - click <a href="http://programmerramblings.blogspot.com/2010/08/aspnet-40-session-timeout-control.html">HERE</a> for a .NET 4.0 version of control (with Countdown timer)<br /><br /><span style="color: rgb(255, 0, 0);">UPDATE!</span> 8/4/2009 - click <a href="http://programmerramblings.blogspot.com/2009/08/aspnet-session-timeout-control-jquery.html">HERE</a> for new version of control (now with Countdown timer)<br /><br />For quite awhile now I've been a big fan of the <a href="http://weblogs.asp.net/traviscollins/archive/2008/02/22/ajax-timeout-server-control.aspx">ASP.NET Session Timeout Control</a> by Travis Collins.<br /><br />I really like the way he designed it - and by also implementing partial postback capability (see his post's comments) - it really takes care of everything I need in a timeout control... except for one thing.. visibility. That's the only complaint I've ever heard is that when the template div is made visible - it's not always the most eye-catching element.<br /><br />'Enter jQuery UI's Dialog (modal) <a href="http://jqueryui.com/demos/dialog/#modal">plugin</a>. I'd thought it'd be really nice to easily have a ui-themed dialog popup upon session timeout instead of simply showing a div. That'd definitely grab their attention instead of just a scrollTo + div.show() kind of approach.<br /><br />This is my (work-in-process) solution.<br /><br />We edit Travis's javascript file with the following changes:<br /><br />At the bottom of the <span style="font-weight: bold;">initialize: function() {</span>.... section, we add the following code (simply so we can override it if desired):<br /><pre class="brush: javascript"><br /> this.initDialog();<br /></pre><br />That will allow the initialize routine to call our new initDialog function we're about to add (that we can easily override from the using application).<br /><br />So now we add our new initDialog function (I added mine just after the _resetTimeout function):<br /><pre class="brush: javascript"><br /> // this will be the default Dialog if not overriden<br /> initDialog: function(e) {<br /> if (typeof jQuery.ui != 'undefined') {<br /> var tsc = this;<br /> $("#" + this._clientId).dialog({<br /> autoOpen: false,<br /> bgiframe: true,<br /> modal: true,<br /> buttons: {<br /> Ok: function() {<br /> $(this).dialog('close');<br /> CallServer();<br /> tsc._resetTimeout();<br /> }<br /> }<br /> });<br /> }<br /> },<br /></pre><br />Now Initialize will call this function and setup our default dialog if we don't bother to override it. (Notice we're not going to do anything if the jQuery.ui isn't loaded.)<br /><br />Next we change the existing showAboutToTimeout function to look like this:<br /><pre class="brush: javascript"><br /> showAboutToTimeout: function(e) {<br /> if (typeof jQuery.ui != 'undefined')<br /> $("#" + this._clientId).dialog('open');<br /> else {<br /> $get(this._clientId).style.display = 'block';<br /> ScrollToElement($get(this._clientId));<br /> }<br /> window.focus();<br /> },<br /></pre><br />So basically if jQuery.ui is loaded, we'll open our dialog (it should be initialized by now). If no jQuery.ui - we revert to Travis's original design.<br /><br />One more slight change to the _resetTimeout function and we're done:<br /><pre class="brush: javascript"><br /> if (typeof jQuery.ui == 'undefined')<br /> $get(this._clientId).style.display = 'none';<br /></pre><br />This just takes care of fully reverting to Travis's original design in case the jQuery UI isn't loaded. (Because our UI Dialog is going to close on it's own using the OK button function.<br /><br />So that's it! Now the timeout control is capable of detecting if you have the jQuery IU loaded - and if so - display a dialog upon timeout. If no UI detected - it reverts back to Travis's original show/hide div design.<br /><br />Here's an HTML example to use the new version of the Timeout control. Notice it still uses the original Template design for the dialog contents. *You have to add the <span style="font-weight: bold;">title</span> property to the Timeout control to set the Dialog window title:<br /><br /><pre class="brush: html"><br /> <tsc:Timeout ID="Timeout1" runat="server" title="Session Expiring" Enabled="true" TimeoutURL="~/TimeOut.aspx" DisplayButton="false"><br /> <Template> <br /> <p><br /> <span class="ui-icon ui-icon-alert" style="float:left; margin: 1px 10px 20px 0;"></span><br /> Your session is about to Expire.<br /> </p><br /> <br style="font-size:x-small;" /><br /> <p>Click <b>OK</b> to continue your session.</p> <br /> </Template><br /> </tsc:Timeout><br /></pre><br />* Notice the dialog will be theme using whatever jQuery UI theme you are using.<br /><br />And lastly, an example of how you'd override the default Dialog from the using application's HTML (if you wanted to):<br /><pre class="brush: html"><br /> <script type="text/javascript"><br /> TSC.Timeout.Timeout.prototype.initDialog() {<br /> if (typeof jQuery.ui != 'undefined') {<br /> var tsc = this;<br /> $("#" + this._clientId).dialog({<br /> autoOpen: false,<br /> bgiframe: true,<br /> draggable: true,<br /> modal: true,<br /> buttons: {<br /> Ok: function() {<br /> $(this).dialog('close');<br /> CallServer();<br /> tsc._resetTimeout();<br /> }<br /> }<br /> });<br /> }<br /> };<br /> </script><br /></pre><br />So there you have it - a fully jQuery UI Dialog enabled ASP.NET Session Timeout control! Thanks again to Travis Collins for his original design.<br /><br />Click below for full Timeout Control project (source, dll's, etc).<br /><iframe style="border: 1px solid rgb(221, 229, 233); margin: 3px; padding: 0px; width: 240px; height: 66px; background-color: rgb(255, 255, 255);" marginwidth="0" marginheight="0" src="http://cid-f7c93b143c55f787.skydrive.live.com/embedrowdetail.aspx/TSC%20Timeout%20Control/TSC.zip" scrolling="no" frameborder="0"></iframe><br /><br />NEW! Full working Web App solution demonstrating usage:<br /><iframe style="border: 1px solid rgb(221, 229, 233); margin: 3px; padding: 0px; width: 240px; height: 66px; background-color: rgb(255, 255, 255);" marginwidth="0" marginheight="0" src="http://cid-f7c93b143c55f787.skydrive.live.com/embedrowdetail.aspx/ASPNET%20jQueryUI%20Dialog%20Timeout/ASPNET%7C_jQueryUI%7C_Dialog%7C_Timeout.zip" scrolling="no" frameborder="0"></iframe>Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com69tag:blogger.com,1999:blog-2706170878552557520.post-15843398586166169552009-03-06T23:24:00.004-06:002009-03-07T00:05:29.239-06:00Async Error Handling Changes in .NET 3.5I was recently surprised to learn about a very subtle, yet important change in asynchronous error handling that Microsoft implemented in ASP.NET AJAX 3.5.<br /><br />I'd noticed that async errors in a new 3.5 app I'm currently working on no longer resulted in the 2.0-style alert popup - but I just assumed it was some change in IE8RC1 causing the different behavior. It finally bugged me enough that I spent a few minutes researching the topic and ran across a very interesting <a href="http://weblogs.asp.net/davidbarkol/archive/2008/09/25/asynchronous-error-handling-change-in-asp-net-ajax-3-5.aspx">post</a> on David Barkol's blog explaining the situation.<br /><br />It seems Microsoft decided to let us handle the error ourselves so we can do whatever we want with it (maybe use a cool jquery modal or something) instead of forcing us to use the vanilla alert box.<br /><br />David's blog also provides a great code example on how to catch the exception and reinstate the alert box behavior from .NET 2.0.<br /><br />Tip: For a more descriptive error message, I will sometimes concatenate the usual e.Message text with the InnerException.Message text.Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com0tag:blogger.com,1999:blog-2706170878552557520.post-13167354805944222912009-01-25T16:57:00.010-06:002011-11-03T23:08:12.535-05:00Sorting Textbox contents with jQuery TableSorter<p>I recently needed to add client-side sorting to a table having one column of textboxes. I wanted the client to have the same sorting capabilities for this column as they did for the rest of the columns in the table.</p> <p>Although I love the jQuery <a href="http://tablesorter.com/">TableSorter</a> plugin – the built-in parsers don’t really accommodate this type of thing.</p> <p>After a bit of javascript debugging, I realized as long as there wasn’t any other markup in the cell besides the textbox itself – it’d be pretty simple using a bit of <a href="http://jquery.com/">jQuery</a> to extract the value.</p> <p>This is the custom parser I came up with:</p> <pre class="brush: javascript">$.tablesorter.addParser({<br /> id: "textbox_text",<br /> is: function(s) {<br /> return false;<br /> },<br /> format: function(s) {<br /> return $($.trim(s)).val().toLowerCase();<br /> },<br /> type: "text"<br />});</pre>The <strong>format</strong> function takes the raw html of the <strong>input</strong> element passed in via the <strong>s</strong> argument and basically just uses jQuery to extract the <strong>val()</strong>.<br /><br />Just remember the <strong>is</strong> function always returns false so you have to explicitly set the parser on a particular column like this:<pre class="brush: javascript">$('#table1').tablesorter({<br /> headers: {<br /> 0: { sorter: "textbox_text" }<br /> }<br />});</pre>The cool thing is that although this illustrates the concept of extracting text from an input element, you could use this technique to obtain a value/attribute/childnode/etc from any type of element.Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com7tag:blogger.com,1999:blog-2706170878552557520.post-74503550040433548102008-11-20T23:24:00.017-06:002011-11-03T23:28:23.126-05:00Basic jQuery Examples for ASP.NET Controls<p>Well it’s official - I’m the newest member of the <a href="http://jquery.com/">jQuery</a> fan club. Since Microsoft <a href="http://weblogs.asp.net/scottgu/archive/2008/09/28/jquery-and-microsoft.aspx">announced</a> their partnership with the jQuery team and it’s integration with Visual Studio – I’ve been all over it.</p><p>I am amazed at how fast it’s taken off in the short time it’s been around. People are writing and publishing <a href="http://plugins.jquery.com/">plugins</a> like crazy.</p><p>As my first jQuery post, I thought I’d show a few basic examples of interacting with some of the common ASP.NET server controls.</p><p>DropDownList SelectedValue:</p><pre class="brush: csharp">GET: $('#<%=DropDownList1.ClientID%>').val()<br />SET: $('#<%=DropDownList1.ClientID%>').val("value_to_set")</pre><p>DropDownList SelectedIndex (2 options):</p><p></p><pre class="brush: csharp">GET: $('#<%=DropDownList1.ClientID%>').attr("selectedIndex")<br />GET: $('#<%=DropDownList1.ClientID%>')[0].selectedIndex<br />SET: $('#<%=DropDownList1.ClientID%>').attr("selectedIndex", "0")<br />SET: $('#<%=DropDownList1.ClientID%>')[0].selectedIndex = 0</pre><p></p><p>RadioButtonList SelectedValue:</p><pre class="brush: csharp">GET: $("input[name='<%=RadioButtonList1.UniqueID%>']:checked").val()<br />SET: $("input[name='<%=RadioButtonList1.UniqueID%>'][value='value_to_set']").attr("checked", true)</pre><p>Button Enable/Disable</p><pre class="brush: csharp">$('#<%=Button1.ClientID%>').attr("disabled", true/false);</pre><p>Label Text</p><pre class="brush: csharp">GET: $('#<%=Label1.ClientID%>').text()<br />SET: $('#<%=Label1.ClientID%>').text("value_to_set")</pre><p>TextBox Text (also HiddenField Value)<br /></p><pre class="brush: csharp">GET: $('#<%=TextBox1.ClientID%>').val()<br />SET: $('#<%=TextBox1.ClientID%>').val("value_to_set")</pre><p>Check Visibility</p><pre class="brush: csharp">if ($('#<%=Button1.ClientID%>').is(':visible'))</pre><br /><p>That’s it for now. More to come-</p>Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com5tag:blogger.com,1999:blog-2706170878552557520.post-31643609848074581212008-10-17T23:45:00.003-05:002009-03-07T00:15:38.812-06:00Visual Studio 2008 PowerCommands Add-In<p>UPDATE: 3/7/2009 - Make sure you follow the instructions in this forum <a href="http://code.msdn.microsoft.com/PowerCommands/Thread/View.aspx?ThreadId=759">thread</a> to prevent some very bizarre disappearing (AKA crashing) studio issues after installing this addin.<br /></p><p><br /></p><p>I’m not sure how I missed this great, free add-in for Visual Studio 2008 called <a href="http://code.msdn.microsoft.com/PowerCommands">PowerCommands</a>.</p><p>It adds several handy features that I’ve grown quite fond of such as Collapse Projects, Open Containing Folder, Email CodeSnippet, etc.</p><p>‘Several truly useful functions.</p><p>See the <a href="http://code.msdn.microsoft.com/PowerCommands">website</a> for full details.</p>Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com0tag:blogger.com,1999:blog-2706170878552557520.post-10273131303153344062008-09-09T00:31:00.002-05:002008-09-09T00:37:21.735-05:00SQL 2005 Management Studio Color SchemesIf you're into <a href="http://programmerramblings.blogspot.com/2008/02/visual-studio-2008-color-schemes-aka.html">Visual Studio Color Schemes</a>, you'll want to check out Tomas Restrepo's <a href="http://www.winterdom.com/weblog/CommentView,guid,506c6302-b9af-43fc-908e-728f309dcf96.aspx#commentstart">utility</a> to port your current color scheme from Visual Studio into SQL Management Studio. <br /><br />It basically just copies all the applicable registry settings from VS to SMS - but it turned out well for me.<br /><br />Check it out-Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com0tag:blogger.com,1999:blog-2706170878552557520.post-37416308421288729372008-09-06T21:13:00.013-05:002009-05-15T14:43:28.998-05:00Full (Bulk) Edit Dynamic ASP.NET ListView (AKA Spreadsheet in a Web Page)<p>Inspired from <a href="http://www.mattberseth.com/">Matt Berseth's</a> awesome <a href="http://mattberseth.com/blog/2008/05/bulk_inserting_data_with_the_l.html">Bulk Insert ListView</a>, I present a slightly modified version that is in essence an auto-saving, dynamic row, viewstate-based listview.</p><p><img src="http://exsgqg.bay.livefilestore.com/y1pPUHrweqv-ZeihjD4JK15nJYvUrSv86f-lzmJbp-yI1TE0Y0zyjmU1Fezmoz512MZdjuSqusvxcU/viewstatelistview.JPG" /><br />It functions basically just like a blank spreadsheet. You start out with one blank row. Need more rows? Click Add. Add too many? Click Delete.<br /><br />The nice thing is that anytime you click anything, the contents are saved to viewstate via an ObjectDataSource.<br /><br />After you've entered everything you need, click Submit and do whatever you need to do with the data (send it to a database, etc).<br /><br />The ListView's ItemTemplate is made up of input controls (TextBoxes, DropDownLists, etc) - so it's basically always in edit mode.. there's just no Edit/Update/Cancel clicking required.<br /><br />I also demonstrate the use of special-case bindings like the DropDownList that you can't simply %# Bind % like normal TextBoxes.<br /><br />Saving state is accomplished somewhat similar to Matt's, only mine saves via an overriden OnItemCommand event in an extended ListView class. This makes saving state *almost* completely automatic.<br /><br />The elegance is in the simplicity. There's nothing quirky or magic about it. It's just a combination of simple concepts that result in an intuitive bulk input grid. And obviously you could also use it to Edit existing data in a dynamic way - just load the ListView on Page_Load.<br /><br />Again, a huge thanks to <a href="http://www.mattberseth.com/">Matt Berseth</a> for the original concept/stylings/etc.<br /><br />Full source is available below:<br /><iframe style="BORDER-RIGHT: rgb(221,229,233) 1px solid; PADDING-RIGHT: 0pt; BORDER-TOP: rgb(221,229,233) 1px solid; PADDING-LEFT: 0pt; PADDING-BOTTOM: 0pt; MARGIN: 3px; BORDER-LEFT: rgb(221,229,233) 1px solid; WIDTH: 240px; PADDING-TOP: 0pt; BORDER-BOTTOM: rgb(221,229,233) 1px solid; HEIGHT: 66px; BACKGROUND-COLOR: rgb(255,255,255)" marginwidth="0" marginheight="0" src="http://cid-f7c93b143c55f787.skydrive.live.com/embedrowdetail.aspx/ViewStateListView/ViewStateListView.zip" frameborder="0" scrolling="no"></iframe><br /></p>Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com21tag:blogger.com,1999:blog-2706170878552557520.post-52292913386908763202008-09-04T23:04:00.008-05:002011-12-08T22:05:51.515-06:00ASP.NET Site Warmup<span style="color: Red;">UPDATE - 12/8/2011</span> - Project updated and moved to <a href="http://github.com/kennethscott/ASP.NET-Site-Warmup">github</a><br /><br />ASP.NET applications by nature exhibit some degree of delay upon initial access to the site. This is due to the nature of the JIT compilation, caching, etc - and most all ASP.NET websites of any size are going to exhibit some amount of delay on that first hit.<br /><br />Now if you've ever done any admin work with Sharepoint, you've most likely used the Sharepoint-specific <a href="http://blogs.msdn.com/joelo/archive/2006/08/13/697044.aspx">warmup script</a> to eliminate Sharepoint's extremely annoying first-hit delay.<br /><br />It was actually that script that got me thinking.. why not write a little app I can schedule to programmatically perform that first hit to any site(s) I want?<br /><br />Sure, you could just plug an individual URL into a Scheduled Task and get the same effect - but this way just seemed a little cleaner to me.<br /><br />Now there is nothing magic about the app. All it does is read in a list of sites from an xml file and perform an HTTP GET against them using an HttpWebRequest. The only thing even remotely out of the ordinary is that I included code to use the DefaultCredentials and also set the UserAgent. <br /><br />I actually didn't realize it before this, but if you don't set the UserAgent and attempt to do a GET to a page that contains an AJAX .NET UpdatePanel - the page doesn't recognize the UserAgent and you'll get the wonderfully generic 500 System Error.<br /><br />The heart of the code is this:<br /><br /><pre class="brush: csharp"><br />foreach (XmlNode node in xmlDoc.SelectNodes("Sites/Site"))<br />{<br /> XmlElement url = (XmlElement)node;<br /> HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url.SelectSingleNode("URL").InnerText);<br /> request.Credentials = CredentialCache.DefaultCredentials;<br /> request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)";<br /> WebResponse response = request.GetResponse();<br /> response.Close();<br /> Console.WriteLine(String.Format("{0} : Successful", url.SelectSingleNode("URL").InnerText));<br />}<br /></pre><br /><br />To use, simply plug your sites into the sites.xml file and schedule the app to run as a Scheduled Task.<br /><br />You may also run the exe with any normal help trigger (/h or /? or -? or etc..) for more information.Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com12tag:blogger.com,1999:blog-2706170878552557520.post-48517755246000012492008-08-11T23:09:00.006-05:002011-11-03T23:31:05.832-05:00Elegant Date Validation in C#Simple example of a clean, elegant way to validate a date of any format without throwing a single exception.<br /><br /><pre class="brush: csharp"><br />System.IFormatProvider format = new System.Globalization.CultureInfo("en-US", true);<br /><br />DateTime outDate;<br /><br />bool isDate = DateTime.TryParseExact("08112008", "MMddyyyy", format, <br /> System.Globalization.DateTimeStyles.AllowWhiteSpaces, out outDate);<br /></pre><br />The TryParseExact method allows you specify the exact formatting of the input string. If the Parse is successful, the boolean returned will be True - and the output DateTime variable will contain a valid date.Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com0tag:blogger.com,1999:blog-2706170878552557520.post-14044327661319914052008-07-25T20:52:00.003-05:002008-07-25T21:06:23.715-05:00LCSMessengerHistory Helper AppIf you're unlucky enough to still be running MS Office Communicator 2005, you know the pain of not having any kind of automatic conversation logging ability. Sure, you have the manual option of saving an individual conversation to an RTF file or emailing it to yourself, but who wants to remember to do that every time? <br /><br />Luckily there's a free open-source helper app from Darrin M. Gorski called <a href="http://www.gorski.net/darrin/lcsmh/">LCSMessengerHistory</a> that fills this void. The great part is that even if you don't think it works exactly like it should - just grab the source and tailor it to your liking.<br /><br />While it's not exactly "integrated" with Communicator - meaning it basically just runs along-side Communicator - it still works great.<br /><br />Couple this with Windows Desktop Search 4.0 to index all those saved conversations and you'll have instant searchable access to all your past conversations.<br /><br />* It should be noted that MS Office Communicator 2007 has a built-in Conversation History logging feature that creates a folder within Outlook and stores your conversations there.Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com2tag:blogger.com,1999:blog-2706170878552557520.post-54844918312345827012008-07-03T00:14:00.002-05:002008-07-03T00:26:05.977-05:00DebugBar & IETesterJust a quick reference to a couple of really cool utilities I ran across earlier - <a href="http://www.my-debugbar.com/wiki/IETester/HomePage">IETester</a> and <a href="http://www.debugbar.com/">DebugBar</a>.<br /><br />They're both available on the DebugBar <a href="http://www.debugbar.com/">homepage</a>.<br /><br />IETester is an incredibly handy little tool to test your app in multiple versions of IE at the same time. It features a tabbed interface and you simply pick the version of IE you want when you add a new tab. With Microsoft making it (nearly) impossible to run multiple versions of IE at the same time, this FREE app fills a huge void.<br /><br />DebugBar is also an extremely handy debugger's toolbar (think Firebug for Firefox). It's FREE as well, but unfortunately only for personal use.<br /><br />I still haven't used DebugBar enough to give it a fair comparison against the standard <a href="http://www.microsoft.com/downloads/details.aspx?familyid=e59c3964-672d-4511-bb3e-2d5e1db91038&displaylang=en">Microsoft IE Developer Toolbar</a> - but I really like what I see. It seems to have quite a bit more functionality than MS's version.<br /><br />With them all being free, how can you go wrong?Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com0tag:blogger.com,1999:blog-2706170878552557520.post-22649195391450031712008-04-24T23:13:00.003-05:002008-04-24T23:29:23.151-05:00Color Coded Calendar for SharepointIf you've ever spent time screwing around with javascript and custom columns trying to get color coding functionality out of a Sharepoint calendar, you will LOVE <a href="http://planetwilson.blogspot.com">Mark Wilson's</a> <a href="http://www.codeplex.com/planetwilson">Colour Calendar v2.3</a>.<br /><br />It's a nicely packaged solution that works on both WSS3.0 and MOSS2007 and seems to work quite well. I've been playing around with it on a test server for a couple of days now and I'm really impressed with how easily it just works. You basically deploy the feature on the site, Activate it, and enable it on whatever Calendar you want - and it takes it from there. It creates custom lists and content types and ties everything together so you basically don't have to do anything manually if you don't want to. After activation, adding new calendar entries will give you a new dropdown/choice field for Event Category that controls what color is shown. There are several defaults included, or you can modify/create your own. <br /><br />I do have one question I need to ask Mark though about using custom color lists instead of inheriting the defaults from the root site. Most of our sites are full Site Collections off the root/sites/.. setup - and whenever I Activate the feature on a Site Collection, it sets everything up and works just fine.. but it strangly creates a Color Mapping List that it simply doesn't use. By default it's pointed back to the root site's List - and I've tried changing the one at the Site Collection level and then pointing it to this modified List - but it doesn't seem to like it... but you can create a brand new Color Mapping List in your Site Collection - and it seems to take that one just fine.<br /><br />So anyway, I'm sure there's some method to the madness. I probably just haven't spent enough time with it to get my head around it. <br /><br />It could probably use a little more detail in the documentation concerning setting up/modifying these Custom Color Mapping Lists - but aside from that, the thing still works beautifully. Especially if you don't want custom colors - then it's a total no-brainer. I'm sure my situation is simply "Operator Error".Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com0tag:blogger.com,1999:blog-2706170878552557520.post-44632572314415299582008-04-24T22:54:00.007-05:002011-11-03T23:31:44.382-05:00LINQ to XMLI had the opportunity to play around with LINQ to XML today for a real-world project. <br /><br />I have to admit I am LOVING how easy it is to whip out an XDocument with some XElements in only a couple of lines of code. <br /><br /><pre class="brush: csharp"><br />var xmlData = new XElement("Transactions", <br /> from trans in dc.Transactions<br /> select new XElement("Transaction",<br /> new XElement("Date", trans.Date),<br /> new XElement("Amt", trans.Amt)));<br /></pre><br />It's pretty flexible in that if you dont want the generic var, you could actually create a strongly typed XElement instead.<br /><br />This makes it a snap to do things like create a single XDocument - and then piece together the document from different data sources or database servers.Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com0tag:blogger.com,1999:blog-2706170878552557520.post-43811345462580095732008-04-08T23:10:00.006-05:002011-11-03T23:32:05.581-05:00LINQ Grouping ExampleI recently had the need to do some grouping on a SQL db table via LINQ. Records were grouped based on a single identifier field - but then to make them unique they had a version field that started at 0 and incremented whenever the record was modified (each modification created a new copy of the record with the incremented version#). So of course I needed to only pull the most recent versions of these records.<br /><br />Example Records:<br /><br />Identifier Version Data<br />1234ABC 000 ................ <- dont select this one<br />12BCD34 000 ................ <- select this one<br />1234ABC 001 ................ <- select this one<br />etc..<br /><br />I've done this type of thing before with raw sql, but this was my first attempt at it via LINQ.<br /><br /><pre class="brush: csharp"><br />var recs = from trans in dc.Transactions<br /> group trans by new <br /> { <br /> Identifier = trans.Identifier<br /> } into grp<br /> select new <br /> { <br /> Identifier = grp.Key.Identifier,<br /> Version = grp.Max(t => t.Version)<br /> };<br /><br /></pre><br />Pretty simple once you get your head around the syntax. It simply groups on the Identifier column and only selects the Version with the highest number.Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com0tag:blogger.com,1999:blog-2706170878552557520.post-4235399010744085282008-04-01T22:27:00.003-05:002008-04-01T22:50:56.429-05:00ClipX Clipboard ManagerFor years I've tried (and abandoned) several different clipboard manager apps. They all sound great in theory, but in practice - it's a different story. <br /><br />Enter <a href="http://bluemars.org/clipx">ClipX </a>from Francis Gastellu. ClipX has totally changed the way I think about clipboard management. I've been using the latest beta (1.0.3.9c) - and have been very impressed with what I've seen. Not only is this thing extremely lean and quick (generally uses only about 600kb of RAM) - it's completely extendable with it's own SDK. A few truly useful plugins are also available for instant extension.<br /><br />I have to admit, I didn't care much for the default hotkey configuration - but it's completely customizable so without much trouble I had it working just like I wanted.<br /><br />Some of the best features include the ability to save clips between sessions (or permanently), view copied images while in the clipboard, etc. I especially love the Smart Navigation plugin that you can setup with regular expressions to launch apps when certain regex patterns are matched. It even includes a couple of builtin regex patterns to get you started (URL Extractor, Mailto, etc). You simply copy a url (or string of text that contains a URL or maybe an email address) - and hit your designated HotKey - and poof - the app you have setup with launch and navigate to the url, setup the email, etc.<br /><br />* Note: There are actually options within the base app itself to perform browser navigation and launch application functionality - but they are disabled in the current beta. I emailed the author and he promises to have them functional by the next release.<br /><br />Oh, did I mention it's FREE?<br /><br />This will definitely go on my <a href="http://programmerramblings.blogspot.com/2007/10/my-favorite-mostly-free-applications.html">Favorite Freebies</a> list.Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com1tag:blogger.com,1999:blog-2706170878552557520.post-2160441005634804022008-03-31T22:40:00.002-05:002008-03-31T22:44:18.248-05:00STSADM Extensions from Gary LapointeI just wanted to reference an incredibly handy STSADM Extension that you simply install like a Feature.<br /><br />It adds dozens of handy options to the Sharepoint command-line utility STSADM.EXE.<br /><br />The Extensions are courtesy of Gary Lapointe and you can find it on his blog <a href="http://stsadm.blogspot.com/2007/08/stsadm-commands_09.html">here</a>.<br /><br />It seems to work fine in WSS3.0 (as well as MOSS2007).Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com0tag:blogger.com,1999:blog-2706170878552557520.post-86355488460352664902008-03-31T14:30:00.009-05:002008-04-08T17:50:09.917-05:00Adventures with WSS3.0 Room and Equipment Reservation Application Template<strong>UPDATE 4/7/2008 - 11:06PM:</strong><br />I spent a few more hours digging through the template and doing some testing. In the end, I simply cannot recommend the RER template for critical production use. Specifically, the email Alerts just don't work consistently. *I also could never figure out how to keep the Status field from being displayed in the email Alert body text (without making more changes than it was worth anyway).<br /><br />But the use of Alerts was a core requirement for my needs - and without that... well, it's pretty much useless to me.<br /><br />I probably created 50 sites during testing, and I simply never got the same results with Alerts. Sometimes email Alerts would only trigger on the real "<strong>Requested</strong>" status'es - while other times they'd be triggered by the "<strong>Available</strong>" status'es... and *sigh* - sometimes they simply wouldn't be triggered at all.<br /><br />I do know there are a lot of people complaining about Alerts in general. Specifically, how reliable they are and to what extent they should be counted on. But I've never had much of a problem with them on the servers in question. They're anything but 100% - but still - they've always been pretty consistent for me. Sadly, such was simply not the case with my adventures with the RER template.<br /><br /><br /><strong>ORIGINAL POST:</strong><br /><br />After I installed Sharepoint’s RER app template, I quickly realized just how limited its use was.<br />Upon installation, I quickly realized that by default – you can NOT SEE other people’s reservations?!? What the heck is up with that?<br /><br />For working with the actual reservations, you get 2 pages: “<strong>Reserve a Resource</strong>” and “<strong>My Reservations</strong>”. Reserve a Resource allows you to see (ONE-DAY-AT-A-TIME) other people’s reservations – but you can NOT tell anything about the reservation itself (i.e. who made it). And of course on the My Reservations page – you guessed it – you only get your own reservations listed. They actually give you a third page that they don’t bother including a link to – you just kind of have to stumble upon it – “<strong>Reservations Calendar</strong>” – by changing the view on the “<strong>My Reservations</strong>” page. But guess what? IT ONLY SHOWS YOUR OWN RESERVATIONS TOO – just in a calendar view. Why so secretive Microsoft?!? Well, actually there is one view that shows everything – “<strong>Allitems</strong>” view – but it’s a LIST view… come on Microsoft…<br /><br />So it’s about this time that you start going – well surely I can just change those view filters and do what I want. HAH! So so wrong.. You see, to make this Reservation process work in a way that doesn’t allow overlapping reservations – Microsoft devised a method consisting of a hidden Status field called “<strong>RERStatus</strong>”. It is a choice field with values of <strong>Available</strong> and <strong>Requested</strong> (there’s also <strong>Approved</strong> I believe, but I’ve never seen it used). Now like I said, this field is completely hidden by definition and you cannot get your hands on it. Which is actually a good thing by default because you can totally hose the reservations by screwing around with it (and make your resources appear booked forever).<br /><br />The methodology is nothing magic.. they simply use the status to indicate when you can make the reservation. For a day with let’s say a 9:00AM-11:00AM reservation, the list table will have entries like the following:<br /><br />Conf Room 1 12:00AM 9:00AM Available<br />Conf Room 1 9:00AM 11:00AM Requested<br />Conf Room 1 11:00AM Available<br /><br />If you pop open the <strong>RoomEquipmentReservations.wsp</strong> (aka .cab file) – you can open the <strong>ReservationEventHandler.dll</strong> with Reflector and see the code used to check the Status and Date Range and either make the reservation or error out with the appropriate message.<br /><br />Now moving on… If you do get all brave and decide you want the ability to create/manipulate your own views and do whatever you want with this magic “<strong>Status</strong>” field – you’ll want to modify the List Field attributes in the schema file.<br /><br />If you’ve already installed the template – you’ll find the schema.xml file here:<br /><strong>C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\ReservationsList\Reservation\schema.xml</strong><br /><br />If you haven’t installed it – you can crack open the <strong>ApplicationTemplateCore.wsp</strong> (aka .cab file) – and edit this guy:<br /><strong>ReservationsList\Reservation\schema.xml</strong> (just make a backup first)<br /><br />The part you’re looking for is:<br /><Field ID="{6f99f38c-e91f-46be-a4f8-255f793967c9}" Name="<strong>RERStatus</strong>" Group="RER Columns" Type="Choice" DisplayName="$Resources:rer,RERStatus_DispName;" Hidden="<span style="color:#ff0000;">TRUE</span>"><br /><br />CHANGE TO:<br /><Field ID="{6f99f38c-e91f-46be-a4f8-255f793967c9}" Name="<strong>RERStatus</strong>" Group="RER Columns" Type="Choice" DisplayName="$Resources:rer,RERStatus_DispName;" <span style="color:#ff0000;">ShowInNewForm="FALSE" ShowInEditForm="FALSE" ShowInDisplayForm="FALSE"</span> Hidden="<span style="color:#ff0000;">FALSE</span>"><br /><br />Notice I added the attributes <strong>ShowInNew/Edit/ViewForm</strong> because if you don’t – after you set that <strong>Hidden=TRUE</strong> property - the field will be visible and changeable everywhere (which would be really, really bad).<br /><br />*Note1: I couldn’t figure out how to keep the Status field from displaying in email <strong>Alert</strong> body’s.<br />**Note2: If you edited the installed <strong>schema.xml</strong> – you’ll have to restart IIS to pickup your change.<br /><br />Anyway, you now have a site with the magic <strong>Status</strong> field visible in the right places so you can use it however you like.<br /><br />You should now be able to edit any of the existing Views and see the real Filter parameters and how they’re using “<strong>NOT EQ Status=Available</strong>” everywhere to keep out the placeholder records... So you can now create your own views or modify the existing ones to your liking.<br /><br />BUT BE WARNED, if your permissions allow your members to CREATE VIEWS (which they do by default) – I HIGHLY SUGGEST you remove those permissions, else users could possibly create their own views and NOT take into account the Status field (thereby pulling records they shouldn’t). Because once those placeholder (Status=Available) records are visible – people will see them and get confused/delete them and trash the site/etc.<br /><br />You can set these permissions here:<br /><strong>People and Groups - Site Permissions - Settings/Permission Levels.<br /></strong>You'll normally modify <strong>Contribute</strong> access (because that's what <strong>Members</strong> have by default - and <strong>Members</strong> are probably the ones you have designated as having the ability to make reservations).<br />* Note: You will have to disable inheriting permissions from parent site to make this change:<br /><br />So after clicking <strong>Contribute</strong> - scroll down to <strong>Personal Permissions</strong> and MAKE SURE "<strong>Manage Personal Views - Create, change, and delete personal views of lists.</strong>" is UNCHECKED.<br /><br />*Another tip – if you’ll notice, the “<strong>Reservations Calendar</strong>” view doesn’t have the <strong>toolbar</strong> on by default. If you try and edit the Web Part to put it back – you’ll get some screwy results (mine would often disappear entirely). The alternative is to set it at the source (back in the same schema.xml file as the List Field definitions):<br /><br />To set it, find this:<br /><View BaseViewID="3" Type="<strong>CALENDAR</strong>" WebPartZoneID="Main" DisplayName="$Resources:rer,ReservationsCalendar_ViewName;" SetupPath="pages\viewpage.aspx" ImageUrl="/_layouts/images/announce.png" Url="ReservationCalendar.aspx" Scope="Recursive"><br /><Toolbar Type="<span style="color:#ff0000;">None</span>"/><br /><br />And change it to “<strong>Standard</strong>” for the full normal toolbar:<br /><View BaseViewID="3" Type="<strong>CALENDAR</strong>" WebPartZoneID="Main" DisplayName="$Resources:rer,ReservationsCalendar_ViewName;" SetupPath="pages\viewpage.aspx" ImageUrl="/_layouts/images/announce.png" Url="ReservationCalendar.aspx" Scope="Recursive"><br /><Toolbar Type="<span style="color:#ff0000;">Standard</span>"/><br /><br />*** Notes about RER and Email Alerts:<br /><br />My new custom views consist basically of clones of the originals – only filtered to show a single resource. For example, I created individual Calendar Views for each individual Resource. This just makes it a little nicer for users wanting a calendar view to only have to look through entries for the particular resource they’re interested in. I also took the opportunity on these custom Calendar views to change it so that the Reserved By field is displayed on the calendar date instead of the Resource name.<br /><br />Everything was rolling along fine until I realized you can’t create alerts from Calendar views. So I thought – OK – I’ll just create List views for each calendar.. basically the same filter and everything – just a List instead of a Calendar. You can then select THOSE List views in the Alert setup.. but for some reason I cannot get these to work. The existing default views (MyReservations and Allitems) trigger Alerts perfectly – but I cannot seem to get my custom List views to trigger them. I see them setup in the <strong>ImmedSchedule</strong> table in the <strong>WSS_Content</strong> database – but they don’t seem to be triggered for some reason.<br /><br />I hope to have a resolution soon and I will update my findings.<br /><br />If you have any ideas, I’d love to hear them.Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com16tag:blogger.com,1999:blog-2706170878552557520.post-62812631139134832482008-03-24T22:52:00.005-05:002011-11-03T23:33:30.036-05:00Add Messenger Presence to Web PagesDid you know it's extremely easy to add Messenger presence to web pages?<br /><br />I recently ran across the ActiveX control "Name.NameCtrl" that you can call via client-side script and display the Messenger status icon (which also provides calendar availability, link to email, link to instant message, etc).<br /><br />The control has two main methods - ShowOOUI() and HideOOUI().<br /><br />The concept is simple:<br />1) instantiate the control on page load<br />2) add callable functions to Show/Hide the presence info<br />3) use an event such as onmouseover/onmouseout to call the functions<br /><br />If you're using Microsoft's AJAX .net framework, they make it really easy providing the perfect place to instantiate the object (the built-in pageLoad() event) - and there's even a Sys.UI method for getting the X and Y coordinates of an element.<br /><br />If you're not using the framework, you can still do it the old fashioned way.<br /><br />For example:<br /><br /><br /><pre class="brush: javascript"><br /> // create Messenger activexobj (using pageLoad(), body onLoad, etc)<br /> NameObj = new ActiveXObject("Name.NameCtrl");<br /><br /> // show Messenger icons<br /> function ShowOOUI(name, element)<br /> {<br /> var offsetX = 0;<br /> var offsetY = 0;<br /> var parent;<br /> // this X/Y coordinate code was taken from the AJAX .NET framework<br /> for (parent = element; parent; parent = parent.offsetParent) {<br /> if (parent.offsetLeft) {<br /> offsetX += parent.offsetLeft;<br /> }<br /> if (parent.offsetTop) {<br /> offsetY += parent.offsetTop;<br /> }<br /> }<br /> NameObj.ShowOOUI(name, 1, offsetX, offsetY);<br /> }<br /><br /> // hide Messenger icons<br /> function HideOOUI()<br /> {<br /> NameObj.HideOOUI();<br /> }</pre><br />Then just call it from an element's onmouseover/onmouseout events like:<br />onmouseover="ShowOOUI('somebody@nowhere.com', this);"<br />onmouseout="HideOOUI();"<br /><br />FYI - My usage has been in an intranet environment using Windows Messenger 5.1 (SIP/Live Comm Server backend). It's also my understanding that Office is what actually installs the prerequisite DLLs.<br /><br />Additional info <a href="http://msdn2.microsoft.com/en-us/library/bb862236.aspx">here</a>.Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com3tag:blogger.com,1999:blog-2706170878552557520.post-43387482714397402012008-03-05T23:05:00.013-06:002011-11-03T23:33:53.243-05:00Convert Hex String to Byte Array and Vice-VersaIf you've dealt with legacy encryption schemes across multiple platforms, you know what a chore it can be to deal with the various encoding options, the character sets, etc. Sure, Microsoft provides various short-cuts for converting hex strings to/from byte arrays (BitConverter, Encoding.ASCII.GetBytes, etc) that seem to sometimes work depending on the details of what you're doing and the systems involved.. but time and again I wind up going back to an old tried-and-true method shown below.<br /><br /><pre class="brush: csharp"><br /> /// <summary><br /> /// Convert string to byte_array<br /> /// </summary><br /> /// <param name="strInput"><br /> private byte[] String_To_Bytes(string strInput)<br /> {<br /> // i variable used to hold position in string<br /> int i = 0;<br /> // x variable used to hold byte array element position<br /> int x = 0;<br /> // allocate byte array based on half of string length<br /> byte[] bytes = new byte[(strInput.Length) / 2];<br /> // loop through the string - 2 bytes at a time converting<br /> // it to decimal equivalent and store in byte array<br /> while (strInput.Length > i + 1)<br /> {<br /> long lngDecimal = Convert.ToInt32(strInput.Substring(i, 2), 16);<br /> bytes[x] = Convert.ToByte(lngDecimal);<br /> i = i + 2;<br /> ++x;<br /> }<br /> // return the finished byte array of decimal values<br /> return bytes;<br /> }<br /><br /> /// <summary><br /> /// Convert byte_array to string<br /> /// </summary><br /> /// <param name="bytes_Input"><br /> private string Bytes_To_String(byte[] bytes_Input)<br /> {<br /> // convert the byte array back to a true string<br /> string strTemp = "";<br /> for (int x = 0; x <= bytes_Input.GetUpperBound(0); x++)<br /> {<br /> int number = int.Parse(bytes_Input[x].ToString());<br /> strTemp += number.ToString("X").PadLeft(2, '0');<br /> }<br /> // return the finished string of hex values<br /> return strTemp;<br /> }</pre><br />This allows you, for example, to go from a string value of say "A5B2FFD0" back and forth between the equivalent byte array { 0xA5, 0xB2, 0xFF, 0xD0 }.Kenneth Scotthttp://www.blogger.com/profile/08960674005656172607noreply@blogger.com14