Tuesday, November 23, 2010

How To: Add a Report to the Project Web Database in SharePoint 2010

This is relatively simple, but I couldn’t find a step-by-step for it, so I thought I’d post it.  SharePoint 2010 has some new default templates based on Access Data Services.  These templates- Project Web Database,  Assets Web Database, and all the other * Web Database templates – all create a SharePoint site that is essentially a handful of lists and a published Access Database containing reports and forms for showing the lists in a nice tabbed view.

One common request may be to tweak the reports that come with the site, or to add a new report altogether.  This isn’t too difficult, but my initial assumption was that all that was necessary was to create the report in Access.  There’s a little more to it than that.  To add a new report, follow these steps:

· Browse to the site

· Open the site in Access (Options ->  Open in Access) from a machine that has Access 2010 installed.

· Once the database is open, click ‘Enable editing’

· Create a new report by clicking Create -> Blank Report and dragging the desired fields, etc.  See online support for Access to learn more about the tool.

· Save the report

· Rt Click Forms -> Report Center and choose ‘Layout’

· Add a command button to the ‘Select a Report’ region.

· Click ‘Property Sheet’

· Give it a caption

· Click the ‘Event’ tab

· Click the elipses next to  ‘OnClick’

· In ‘Add New Action’, choose ‘BrowseTo’

· Choose ObjectType =Report, Object Name = Name of your report

· Set ‘Path to Subform Control’ = Main.NavigationSubform>ReportCenter.sfrReportPage

· Set Data Mode = Read Only

· Save

· Click ‘File’ -> Sync All

Once synced, give it a minute and refresh the page in your browser.  The custom report should appear in the ‘report center’ section in your site. 


Note: If these templates are not working at all in your farm, be sure Access Data Services is enabled and configured correctly.  If the default reports are not working, be sure Reporting Services is configured, including the steps to enable Access Data Services.

Friday, November 19, 2010

How To: Create a Kanban Board in SharePoint 2010 with Zero Code

imageUpdate:  If you're looking for a quick and easy Kanban board you should also check out my book Enter, Trello Dojo.  In it, I show you how to use the awesome kanban service Trello and even give you templates for getting started right away.

Update 2:  This guy has taken the ideas below to the next level with "MyBoard". His solution is not exactly "out of the box", but it is a fairly lightweight solution.

For those unfamiliar with it, Kanban is a project management technique developed by Toyota and popularized in IT circles by its application in Agile software development methodologies.  In software development, it’s simplest form is a whiteboard with columns for each status, in which post-it notes are affixed for each task.  As tasks migrate through development, the post-it note is moved from column to column.   Several web-based services and downloadable project management applications exist for implementing something similar sans the whiteboard (one of the best being AgileZen).  However, in some cases a lightweight version based on SharePoint might be handy.  SharePoint provides out-of-the-box functionality for creating project task lists, and, with a little pointing-and-clicking, it’s possible to display these out-of-the-box project task lists Kanban-style.  Here’s how:
Create the Project List
The standard Team Site has one of these, called ‘Tasks’, but you may choose to create your own.  To do this, click Site Settings –> More Options –> Project Tasks.  Give the list a name and url, and click OK.  Once you have a task list, to create a few tasks- one for each status.  When you’re done, you should have something like this:
image
Next, create views for each column that will be on the Kanban board.   You may want to give some thought as to what the columns will be, but for this demo, I chose views named ‘Not Started’, ‘In Progress’, and ‘Recently Completed’.  Tasks with a Deferred or Waiting status will be considered ‘In Progress’.  To do this, follow these steps:
  • Click ‘List –> Create View –> Standard View
  • Enter ‘Not Started’ as a view name
  • Uncheck all columns except for title
  • For Sort, choose Priority and then Due Date
  • For Filter, Check ‘Show items only when the following is true’
  • Set ‘Show items when column’ Status is equal to Not Started
  • Repeat for each view, changing the name and filters appropriately
  • For the ‘Recently Completed’ view, sort by Modified instead of ‘Due Date’
Create the Kanban Board
To visualize all of these in columns, create a new page and then add each view in a table.  To do this, follow these steps:
  • Click ‘Site Actions –> New Page’
  • Enter the page name and click OK
  • Click ‘Insert’ and drop down on ‘Table’ to create a 3 column table.
image
  • Click inside the first column and click Insert –> Existing List
  • Choose the task list you created (or the default one) and click ‘Add’
  • Drop down on the web part menu and choose ‘Edit Web Part’
  • Choose the ‘Not Started’ view and click ‘OK’ for the warning.
  • Enter ‘Not Started’ for the Title.
  • Repeat for each column, choosing the appropriate view.
  • For In Progress and Completed, also choose ‘No Toolbar’
In the end, you should have something that looks like this:
image
And there you have it!  A simple Kanban board, built in minutes, with out-of-the-box SharePoint functionality!
If you’re feeling adventurous, you can spice it up a little bit by customizing the page in SharePoint Designer.  I’ll leave that as an exercise for the reader for now, but here are some ideas on how to improve on this:
  • Remove those ‘Title’ column headers
  • Use formatting to highlight items that are deferred, waiting, or past due
  • Use Content Query Web Parts instead to roll up tasks from multiple sites into a single Kanban board.

Thursday, November 11, 2010

One Password To Rule Them All

About a month ago, Elizabeth and I had our email and Facebook accounts hacked.   A little sleuthing traced it to a Russian group who likely were trying to use our accounts to run a scam, and had gotten our accounts either through a hacked router or through GMail’s normal password reset feature.  We’d been using the same few passwords everywhere (a cardinal sin in online security), and finally got bit.  I caught it pretty quickly, and was able to change our passwords before anything bad happened, but the hassle that ensued, changing all of our passwords for all of our online services had both of us about ready to give up the internet and head for the hills.   As tempting as that was, I decided to look into some alternatives to better manage our personal online security.  I had the following requirements:

  • It had to be secure.  Fort Knox secure.  I was mad at these hackers, and didn’t want it to happen again.
  • The Wife Acceptance Factor (WAF) had to be high.  Poor Elizabeth spent hours resetting passwords and was pretty frazzled.  A solution that was not user-friendly was not going to work.
  • I wanted it to work on my PC, Elizabeth’s Mac, our iPhones, and my iPad.  They all needed to sync up like magic.

After evaluating KeePass, and a few others I landed on LastPass.  For $1 a month, it does everything we could want, on every device, and syncs up seamlessly.  It works by storing all of your passwords in one file that is encrypted using the same algorithms as the military.  You have to remember a single password to get to your other passwords, but this single password can be long, easy to remember, and hard to guess.  The decryption only happens on your devices, so you never actually share your passwords with LastPass.   Once logged in to LastPass, you can see your passwords, but you don’t really have to, because it also supports automatically logging in to websites. The idea is that by only having to remember one password and making it easy to use,  you can afford to change your passwords frequently, use more secure passwords, and not use the same one everywhere. 

If all of this sounds complicated, it’s really not.  In the end, LastPass makes passwords and other sensitive data more secure AND easier to use.  If you’re at all worried about the security of your online accounts, go watch their video, then download and install the free version (only pay if you need to sync between multiple devices).  It’ll step you through the process and soon you’ll wonder how you lived without it.

Friday, November 5, 2010

How to Become a SharePoint PowerShell Ninja in 4 Easy Steps

If you’re at all interested in PowerShell, here’s 4 easy steps to becoming a powershell ninja:

  1. Learn .NET.  PowerShell is .NET in the commandline.  The more .NET you know, the more you’ll know PowerShell.  If you’re stuck trying to figure out how some PowerShell thing works, run $mything.GetType() and look it up in MSDN.  In SharePoint for example, $mything = Get-SPWeb http://somesite; $mything.GetType() returns a SPWeb instance, which you can learn all about on MSDN.
  2. Use the ‘intellisense’ in PowerShell.  You can use <tab> to step through members on a variable, like this: $mything. <tab>  OR you can use get-member, like this : $mything | Get-Member
  3. Use ‘gcm *Something*’ alot.  Do you want to know commands related to SharePoint Search? gcm *Search*.  The naming is usually consistent, so you can do things like “gcm Get-SP*”, if you know you want to get some SP thing.
  4. Use get-help <command> alot.  Read up the help.  Sometimes it’s more helpful than others, but usually –examples will at least put you on the right track and teach you about pipes, where, and foreach.

That’s it.  Lather, Rinse, Repeat, and before long, you’ll be slinging scripts instead of poking around in tedious CA or Site Settings pages.

Thursday, October 14, 2010

Grant Search Account Access to all Web Applications via PowerShell

Here’s a handy script I just whipped up.  When administering SharePoint Search, you typically have a ‘crawl account’ that is given access to all SharePoint sites via user policy.  This isn’t a terribly hard thing to do in Central Admin, but it can be a hard thing to remember =)  In CA, you go to each web app –> User Policy –> Add –> All Zones –> Type Username, full read –> OK.  Depending on how many web apps you have, this can be a little tedious. Also, if you’re trying to be sure you can provision your farm from script as much as possible, a little powershell can be handy here. This script does just that:

$accountName = "DOMAIN\CrawlAccount"

$webApps = Get-SPWebApplication
$webApps | %{
$webApp = $_
    $searchPolicy = $webApp.Policies | ?{$_.UserName -eq $accountName}
    if ($searchPolicy -eq $null){
        Write-Host "$($webApp.Url) does not have a $accountName policy, so it will be added."
        $searchPolicy = $webApp.Policies.Add($accountName, $accountName)
        $searchPolicy.PolicyRoleBindings.Add($webApp.PolicyRoles.GetSpecialRole([Microsoft.SharePoint.Administration.SPPolicyRoleType]::FullRead))
        $webApp.Update()
    }else{
        Write-Host "$($webApp.Url) already has a policy for $accountName, so it will be skipped."
    }

}

Friday, August 6, 2010

SharePoint 2010 Odd Error and Quick Tip Mega-Post #1

One reason for my recent lack of blog posts is that I’ve been working pretty heavily with a customer as part of a SharePoint 2010 Rapid Deployment engagement, and then on their post-RDP SharePoint environment.  As part of this, I’ve had early access to the SharePoint 2010 bits, and worked closely with some top-notch Microsoft SharePoint consultants.  During this time, I’ve tried to jot down in Evernote a few tips, gotchas and oddball errors that I thought the blogosphere could benefit from.  I’m trying to keep this concise, so with no furthur ado:

SharePoint 2010 Admin

Quick Tip: During an install, don’t use the Service App Setup Wizard in Central Admin.  Set up the service apps manually, and pay attention to the app pools and user accounts you use.

Quick Tip: A good admin practice is to keep a ‘SharePoint Admin Log’ to track all admin changes on your farm.  Use a custom list with Title, ChangedBy, Details, ChangeDate, Servers (multiselect), and whatever else you want to log.

Issue: Error in event log “There are no addresses for this application”
Fix: Typically means there is no service enabled for a given service application.  Go to ‘Services on Server’ in Central Admin and start the service that corresponds to the error.

Issue: Error "The form cannot be rendered. This may be due to a misconfiguration of the Microsoft SharePoint Server State Service" when adding workflow (or presumably other infopath forms)
Fix: Create a State Service Application using Powershell.

$s = New-SPStateServiceApplication -Name "State Service Application"
$d = New-SPStateServiceDatabase -Name "State ServiceDatabase" -ServiceApplication $s
$p = New-SPStateServiceApplicationProxy -Name "State Service Application Proxy" -ServiceApplication $s –DefaultProxyGroup

Issue: User Profile Service stuck on ‘Starting’
Fix: Did you hold your nose correctly when entering the account name for the user profile service app? :) Delete the thing and go through one of the online guides- this must be installed exactly as the guides say, including the service account you’re logged in as during provisioning.  If you’ve done that, one thing to try is, in powershell: start-sptimerjob job-timer-recycle

Issue: Error in event log “No SharePoint Usage Service application”
Fix: Check box for 'Usage logging' in CA:  DO NOT create SharePoint Usage SA in powershell.

Issue: 80070003 error trying to remove/add/restore sites after removing a site or site collection?
Fix: Use stsadm -o databaserepair command to remove orphans.  Then, you MUST remove and re-add content database in Web Application Management -> Manage Content Databases

Issue: Service 503 error from IIS when browsing to SP site on one front end?
Fix: Check app pools.  If one (besides web services one) has stopped, restart it.

Issue: Access denied error crawling profiles (sps3://sitename).
Fix: Ensure crawl account has access to site in policy, and to User Profile Service Application (service app -> Administrators -> Add).

Issue: PerformancePoint not displaying in public, anonymous internet site?
Fix: If you've extended the web app from a non-anonymous default zone, be sure the default zone also allows anonymous access.

Issue: Browsing to http://server/site/_vti_bin/listdata.svc returns an error.
Fix: Be sure to install WCF Data Services (Win7/2008 R2  or Vista/2008) on all WFEs and then do an IISReset

SharePoint 2010 Developer

Quick Tip: Use a virtual machine to develop in, instead of installing on your ‘real’ machine.  I suggest VMWare WorkStation.  Use one of the online setup guides to go through the process. 

Issue: Visual Studio error deploying SharePoint project.  Output window complains about the path.
Fix: Move project to shorter path.  I suggest [D]:\Code\SolutionName

Issue: Gap at bottom of page in a custom SharePoint masterpage.
Fix: No content outside of scrollable section should have sizing.  If there's a 'visible:hidden' div there, move it up.

Issue: JavaScript or runtime errors on certain pages when using a custom masterpage
Fix: Make sure ALL ContentPlaceHolders, SharePoint Controls and Delegate Controls that SharePoint needs are available.  Consider using SharePoint Starter Master Page.  If you absolutely don’t want to show a block, then use CSS to hide visibility- don’t remove it from the master.

Issue: Reporting services connected to a SharePoint list doesn’t traverse folders.
Fix: Use the XML data source type and point it to http://server/site/_vti_bin/listdata.svc instead. Your ‘query’ in the dataset will need to be modified to get ‘properties’.

SharePoint 2010 End User

Issue: Word 2007 documents aren’t routed correctly by Content Organizer when saved directly to the drop-off library.  They stay in drop-off library, even though a rule should move them, and does for 2010 docs or uploaded docs.
Fix:  Microsoft confirmed this one for us, but won’t fix.  The core bug is that Office 07’s file lock prevents moving the item while it’s open, while 2010 is smart enough to handle the move.  One solution is to have the admin up the Content Organizer Timer Job frequency to 15 minutes.  This will make it try to route every 15 minutes, so that once the user closes the document, it gets routed.  Solution B is to upgrade to 2010 :)

Issue: Unable to Connect To Explorer or save from Word to SP from Windows 2008?
Fix: Server Manager-> Add Features -> Desktop Experience to enable WebDAV

Office 2010\2007 Client

Issue: Word 2010 refuses to open Word Document when opening a document from SharePoint 2010, but users can download the document.
Fix:  Check your “Trusted Locations” and “Protected View” in Word.

Friday, July 30, 2010

Obscure SharePoint 2010 Workflow Status Link Error

Here’s an obscure error I ran into today, and a nasty workaround.  In SharePoint 2010, if you add a Library or List to a page, and the view you show has an associated workflow status column, then clicking the link to show the workflow status will result in an error.  If you dig in the ULS logs, the error is ‘Index Out of Bounds’.  Looking closely at the URL, you can see that the QueryString is not properly formed – it contains an empty List parameter: WrkStat.aspx?List=&WorkflowInstanceId=<guid>

Here’s repro steps:

  • Create a new blank site
  • Create a new Document library named ‘Test Docs’
  • Associate any workflow with the library, set to start when item created
  • Edit the blank site’s home page
  • Add ‘Test Docs’ as a web part to the page
  • Change the view to ‘All Documents’
  • Upload a document
  • Click the workflow status link
  • Observe an error occurs.
  • Observe the URL QueryString contains ‘List=&’ (ie a blank List)

The cause of the error is apparently that WrkStat.aspx attempts to use the “List” parameter passed in to the page.  (ie, currentWeb.Lists[listId]).  This is Bad Design (tm) – your page should be resilient enough to handle junk input in the querystring.  Fortunately, that parameter is not strictly required. Removing the List parameter altogether from the querystring results in the page being displayed correctly.  Unfortunately, that’s not exactly trivial to do.

When you add a List to a page in SharePoint 2010, behind the scenes you are really adding an XSLTListViewWebPart.  This web part can be customized in SharePoint Designer using XSLT.  If you’re not familiar with XSLT, you’re very fortunate.  It’s a language used to transform XML into something else – in this case HTML to render the list.   It’s powerful, but can be a beast to work with.  For example, it doesn’t have a built-in “string replace” function, which is just the thing you’ll need to fix up that bum url.  Luckily, I found a string replace template online, which gave me what I needed to fix the link.

So, here’s the workaround until this is patched:

  • Open the site in sharepoint designer, edit homepage (or, whatever page has the issue)
  • place the text cursor in the Workflow Status cell that has the bug
  • type ‘test’ – this is to break into custom xslt, and to help you mark where to place the snippets.
  • switch to split view
  • find the <td></td> that contains some xslt and the word ‘test’.  Your cursor _should_ be there already. Replace everything between the <td></td> with Snippet 2. 
  • Add Snippet 1 to the bottom of the stylesheet, immediately before  </xsl:stylesheet>
  • Remove the word ‘test’ that you typed, if you didn’t replace it above
  • Save

Snippet 1

   <xsl:template name="string-replace-all">

    <xsl:param name="text" />

    <xsl:param name="replace" />

    <xsl:param name="by" />

    <xsl:choose>

      <xsl:when test="contains($text, $replace)">

        <xsl:value-of select="substring-before($text,$replace)" disable-output-escaping="yes" />

        <xsl:value-of select="$by" disable-output-escaping="yes" />

        <xsl:call-template name="string-replace-all">

          <xsl:with-param name="text"

          select="substring-after($text,$replace)" />

          <xsl:with-param name="replace" select="$replace" />

          <xsl:with-param name="by" select="$by" />

        </xsl:call-template>

      </xsl:when>

      <xsl:otherwise>

        <xsl:value-of select="$text" disable-output-escaping="yes" />

      </xsl:otherwise>

    </xsl:choose>

  </xsl:template>   

Snippet 2

  <xsl:if test="@ClassInfo='Menu' or @ListItemMenu='TRUE'">

                <xsl:attribute name="height">100%</xsl:attribute>

                <xsl:attribute name="onmouseover">OnChildItem(this)</xsl:attribute>

      </xsl:if>

                <xsl:attribute name="class">

                <xsl:call-template name="getTDClassValue">

                                <xsl:with-param name="class" select="$class" />

                                <xsl:with-param name="Type" select="@Type"/>

                                <xsl:with-param name="ClassInfo" select="@ClassInfo"/>

                </xsl:call-template>

      </xsl:attribute>

      <xsl:variable name="workflowStatusLink">

      <xsl:apply-templates select="." mode="PrintFieldWithECB">

        <xsl:with-param name="thisNode" select="$thisNode"/>

      </xsl:apply-templates>

      </xsl:variable>

      <xsl:variable name="workflowStatusLinkFixedUp">

    <xsl:call-template name="string-replace-all">

      <xsl:with-param name="text" select="$workflowStatusLink" />

      <xsl:with-param name="replace" select="'List='" />

      <xsl:with-param name="by" select="''" />

    </xsl:call-template>

  </xsl:variable>

      <xsl:value-of select="$workflowStatusLinkFixedUp" disable-output-escaping="yes"/>

Saturday, July 10, 2010

How A Microsofty Writes iPad Apps

I’ve just spent a few hours writing my first iPad app, without a Mac, and without Objective-C.  The app itself is nothing special, but the process let me play with a couple new toys.

Sencha Touch is an HTML 5 Application Framework just for developing HTML 5 applications that look and feel like native iPhone, iPad, and Android apps.  It’s based on ExtJS, a jQuery-like JavaScript library, so if you’re familiar with JavaScript and HTML, you can pick this up fairly easily.

WebMatrix was recently announced, and is a great, lightweight website editor that’s perfect for this sort of thing.  I use Visual Studio 2010 for most development I do, but WebMatrix offers a simple, get-out-of-the-way-and-let-me-play UI that’s perfect for hacking away at new code.  At some point, I probably will migrate to Visual Studio – WebMatrix offers a handy ‘Visual Studio’ button for just this occasion.  It ships with IIS Express, a lightweight version of IIS that allows you to quickly serve up folders with many of the bells and whistles of IIS. 

So, with those in hand, I was able to fairly quickly get a proof-of-concept iPad app running. Here’s how to get started yourself:

  • Install the new WebMatrix and IIS Express
  • Download Sencha Touch and copied all of the files into a WebMatrix site.
  • Modify IISExpress’s applicationHost.config to enable off-box requests.
    • Open C:\Users\{YOU}\Documents\IISExpress8\config\applicationHost.config
    • Comment out the binding element for your site
    • Add a new binding element using your machine’s name and a port of your choosing.  Here’s what mine looks like:
            <site name="Empty Site" id="386908880">
                     <application path="/">
                         <virtualDirectory path="/" physicalPath="%IIS_SITES_HOME%\Empty Site" />
                     </application>
                     <bindings>
                         <binding protocol="http" bindingInformation="*:8181:daniellaptop" />
                     </bindings>
                 </site>
  • Modify IISExpress’s applicationHost.config to enable the cache manifest mime type
    • Comment out the .manifest mime type (sorry ClickOnce!)
    • Add the following line: <mimeMap fileExtension=".manifest" mimeType="text/cache-manifest" />
  • Restart WebMatrix, run as admin (This is required if you allow off-box access)
  • Turn off your firewall or open up the port you set above.
  • Add a cache.manifest file to one of the examples
    • Follow the instructions here to build the file
    • Here’s what mine, for the tabs example looks like:
      CACHE MANIFEST
      icon.png
      index.html
      index.js
      phone_startup.png
      tablet_startup.png
      ../../resources/css/ext-touch.css
      ../../ext-touch-debug.js
  • Run the app to get IIS Express cranked up
  • Browse to the site on your iPad
  • Click the  “Plus button” and add to homepage
  • Stop the server or turn off the iPad wifi
  • Run the app from the homepage offline!

That’s it!  I have a few ideas for how I want to put this to use, so stay tuned for some real-world examples.

Monday, May 10, 2010

Blogging at 31000ft: an iPad after 90 days

I started writing this from somewhere above North Carolina, where I have in flight wifi and get a few strange looks as I download a book, read, check news, email and even remote desktop into my SharePoint farm. All from a device I've cleverly concealed in what formerly held a paper notepad. In my last post, I wondered if my purchase was worth sacrificing my next birthday, fathers day, and maybe even on into Christmas. It was. Both the Courier and Hp slate device I had my eyes on got nixed - leaving the Windows tablet market empty for a while. A few me-to Linux and Android offerings grace engadget, but none with the functionality or battery life of the iPad. To be sure, this gadget has made itself at home in the Root house. It turns out there is a whole class of use case for which a phone is too small and a laptop too big and slow. We regularly watch tv on it - only needing a laptop for our weekly Jack Bauer fix, since Hulu doesn't have an app out yet. I paint in Brushes, check email and news, and read books on it. I hardly ever open my laptop at home, except to code. There are a few drawbacks. This thing needs a stylus and handwriting support. One or two apps support it, but my fave, Evernote, is slow to the gate with support for natural note taking. The keyboard still lists as an awkward point for me, though I've gotten better at it. And, yes, it is a "walled garden": Apple and ATT have a choke-hold on the entire experience, occasionally reminding customers with data plan changes or app store rejections. So, my recommendation: if you read, check email, social networks and news, and/or watch netflix every day, then get one of these.

Saturday, April 10, 2010

Mmmm Kool Aid, or Daniel Reviews the iPad

Last Saturday, like a apple fanboy, except dressed like a pc, I went to the local apple store and bought an iPad. I had decided not to- they're just oversized iPods. I was going to wait for the HP slate or MS Courier to drop. But news started to trickle out - Netflix was available day one, and Hulu was 'working on it'. And I remembered waiting on the iPhone because "Windows Mobile 7 is coming out." Now, 4 cycles of iPhone later with WiMo7 still "about to come out", I don't regret my switch. So, following spousal approval, I went to the Apple store and picked one up, thinking I'd sell it on ebay if i didn't like it. Will I regret jumping in the iPad frenzy? Time will tell, but here's my two bits on a week with the gadget.

First, the bad. Hardware teardowns reveal that the iPad is, in fact, pretty much a large iPod touch. The UI is not much different from the phone, and $500 for an oversized iPod with only 16 gb storage is steep. The keyboard is also a little clumsy. It's too small to two hand type, but too large to thumb. It's growing on me, and I'm typing this on it, but natural it's not. Finally, iBooks scares me. I'm reading a Michael Chriton book now, and it works great, but I worry that I will always need an Apple device to read it. I like the idea of chipping away at the cluttered bookshelves in my house, but it bugs me that to read it again in 10 years, I'll need Sr. Jobs latest creation. I suppose it's the same with music, but drm just seems even worse against the written word.

With that out of the way, I love this thing. I haven't opened my laptop once at home since I got it. For checking email, weather, news, reading books, watching netflix, or the limited number of available tv shows (Lost and V), it's the bees knees. It's instant on, light, easy, and fun. You quickly realize that there are so many everyday things that an oversized iPod is really well suited for. And, I should mention, it's an oversized iPod with a beautiful right sized screen, a fast processor, and a nice 10 hour battery. Painting with Brushes on this device is much fun, and I hope to be picking up that again soon.

In the end, the iPad is a netbook killer. Light, and fast, its suited for most common home uses. For work, a few things like more support for stylus note-taking (looking at you Evernote) may make it more handy. This one's not going on eBay any time soon...unless that Courier drops tomorrow.

Sunday, March 21, 2010

SharePoint PowerShell Kung-Fu: Compare Folders to Fix a Bum Web Front-End

I’ve been working on a large SharePoint 2010 rollout and learning quite a bit about managing the farm using PowerShell.  I’ve never been much of a console junkie – we have _Graphical_ User Interfaces for a reason, right?  But, it’s always good to have an extra tool in the belt, so when one of my Web Front Ends was refusing to serve up a site, and the SharePoint log was being less than helpful, I came up with some PS to compare it to a working server and was able to fix my problem.

The error I was seeing was that a certain site returned a ‘404 file not found’ error browsing to the root of one of the sites, but only from one web front-end.  By putting an entry in my host file and trying the 3 web front-ends, I isolated the server.  Looking in the logs, even with verbose logging, only revealed an ‘Unknown SPRequest error occurred’.

The next step, then, was to compare the failing server with a working one.  If you aren’t familiar with SharePoint’s architecture – it manages copying supporting files between multiple servers for failover, scale, and redundancy.  If you aren’t familiar with SharePoint’s headaches, that process is apparently harder than it sounds, and occasionally, files will be missing from servers.  The end result is that requests that hit one server may fail, while requests to another succeed.

I’m sure there are utilities I could have used to do this, but after a little sleuthing, I came up with the following PowerShell to compare the default sharepoint folders on the two servers:

$prod01 = dir 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14' -recurse | ?{$_.mode -match "d"}
$prod02 = dir '\\spprod02\c$\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14' -recurse | ?{$_.mode -match "d"}
Compare-Object $prod01 $prod02

This stores the directory listing, containing only folders, of one server in one variable, and of the second server in another variable.  Compare-Object then compares the two, and spits out the results.  In my case- a feature that had failed to deploy to the second server.  Redeploying fixed the issue, and immediately the ‘404 file not found’ error went away.

Saturday, January 9, 2010

How to Write a .NET Windows Service with Console Mode

Windows Services are applications that run in the background and provide some functionality to the system. They have virtually no user interface – except Windows’ built in “start” and “stop” functionality and a few settings to control security, etc.  This makes them great for standalone processes that ‘just work’ and require no user intervention, but it also makes them notoriously difficult to debug and develop.  Because of this, I typically create two projects when creating windows services – the windows service for production, and a console app for development.  However, recently, I came up with a way to combine the two, so that I can have both the windows service AND the console app in the same project.  Here’s what I did:

First, in general, it’s good to separate your actual logic into a separate class library that contains the bulk of your service logic.  I name this something like “MyCompany.App”, and generally have one class that the Windows Service can use fairly easily– that is, it has a Start and Stop method.

   1: public class MyService{
   2:    public void Start(){//Tip:BackgroundWorkers are great here!...}
   3:    public void Stop(){...}
   4: }

Next, add a Windows Service project and in the ServiceBase code that is generated, new up the above class and use it:

   1: public partial class MyServiceWindowsService : ServiceBase
   2: {
   3:    private MyService service;
   4:  
   5:    public AlertFmMonitoringStationService()
   6:    {
   7:        InitializeComponent();
   8:    }
   9:  
  10:    protected override void OnStart(string[] args)
  11:    {
  12:        this.service = new MyService();
  13:        service.Start();           
  14:    }
  15:  
  16:    protected override void OnStop()
  17:    {
  18:        service.Stop();
  19:    }
  20: }

Add a ProjectInstaller (open the service in design mode, view properties, and click ‘Add Installer’), and you have the windows service, ready for installation with installutil.exe.   To wire up the console app, a few more steps are required.  See, Windows Services are .exes, just like any other app – they just happen to follow a certain convention that makes them Windows Services (namely, they have a ProjectInstaller and one or more instances of ServiceBase).   As long as we follow that convention, Windows is happy, and we can do whatever else we want.  So:

  • Right click the Windows Service project and click ‘properties’
  • Set Output type to ‘Console Application’, and Startup Object to the ‘Program’ class.
  • Under ‘Debug’ enter ‘-console’ in the ‘Startup Options’ ‘Command Line Arguments’ section
  • Inside the ‘Program’ class, add a switch to enable console mode
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main(string[] args)
{           
  if (args.Contains("-console"))
  {
      System.Console.WriteLine("press 'q' to quit.");
      var app = new MyService();
      app.Start();
      while (Console.ReadKey().KeyChar != 'q')
      {
      }
      app.Stop();
  }
  else
  {                
      ServiceBase[] ServicesToRun;
      ServicesToRun = new ServiceBase[]
                          {
                              new MyServiceWindowsService()
                          };
      ServiceBase.Run(ServicesToRun);
  }
}

And that’s it!  The end result is a command line app that can be run using the ‘-console’ switch, or installed as a Windows Service.  When debugging locally, it’s run as a console app.  Ideally, you would build a Setup package to install it, but to install manually, you would use the installutil tool that ships with .NET.