Tuesday, May 20, 2008

PowerShell for Flexible Iterative SharePoint-based Development


This post by David Mann is a good resource for 3rd-party and MS tools to help with SharePoint-based software development:

Using Tools to make SharePoint Development Easier

While we here at the SharePoint Solutions Software Division certainly use some of the tools mentioned in David’s post, I guess maybe our development requirements are a bit "heavier" than the typical IT project since we're developing packaged products for commercial redistribution. We need the fined-grained control of a flexible, highly re-usable tool-chain.

Our developers use PowerShell extensively for various iterative "build and deploy" scenarios. For example, here's the usage menu from a PowerShell script called Deploy-ExCM which we use while working on our ExCM product:

----------

PS C:\usr\src\devteam\ExtranetCollaborationManager2009\Mainline\Dependencies> .\deploy-excm -u

Deploy-ExCM
Build and deploy utility script for Extranet Collaboration Manager

Deploy-ExCM usage information.
Arguments:
-i Recycle IIS application pool
-a Deploy artifacts (xcopy)
-g Deploy assemblies to GAC
-b Build solution (devenv)
-fui Uninstall / Install Features
-fda Deactivate / Activate Features
-bcl Update BCL assemblies to GAC
-testweb Delete and recreate test web
-wsp Build WSP package (makecab)
-light Light deployment -a
-heavy Heavy deployment -b -a -g -bcl -i

Examples:
deploy-excm -a -b -i -g
deploy-excm -b -g
deploy-excm -a -i -g -testweb -fui

----------


In our development environments, each developer using this script has a few environmental variables set in his/her Microsoft.PowerShell_profile.ps1 profile:

----------

#
# User-specific settings
#
write-output "`nSetting environment for SharePoint Solutions development.`n"
$WorkspaceRootDir = "C:\usr\src\devteam"
$PowerShellLibDir = "${WorkspaceRootDir}\DeveloperCenter\PowerShell"

# ExCM
$VBL_ExCM = "Mainline"
$BuildConfig_ExCM = "Debug"
$AppPool_ExCM = "SharePoint - 8081"
$AppRoot_ExCM = "C:\inetpub\wwwroot\wss\VirtualDirectories\8082"
$SiteUrl_ExCM = "http://devapp08:8082"
$WebUrl_ExCM = "http://devapp08:8082/downloadstest"

---------


The wide range of flags and parameters for Deploy-Excm make it a flexible and convenient tool for use while we work on the various types of artifacts involved in a SharePoint solution package. For the most common iterative build scenarios, I call the utility directly from the Visual Studio Tools menu as an External Tool. The three permutations I've added there are:

Deploy-ExCM (iterative) - this calls deploy-excm -a -i -g
Deploy-ExCM (heavy) - this calls deploy-excm -a -i -g -testweb -fui
Deploy-ExCM (makecab) - this calls deploy-excm -wsp

For your reference, here are the PowerShell scripts we're using.

1. Profile - contains developer-specific variables, pathing modifications, and PowerShell translation of vsvars32.bat:
http://www.codekeep.net/snippets/9ba5c3c9-87ea-44f2-81cd-7466f982caaf.aspx

2. Shared Functions - re-usable functions
http://www.codekeep.net/snippets/c3069a2f-3027-496c-8d0a-94e1a0894cfb.aspx

3. Deploy-* - product/project specific calls, command-line parsing:
http://www.codekeep.net/snippets/8071237c-fb1f-4426-96bb-1b4b75f96631.aspx

We try to have a "Deploy-*" script for each major product/project we work on. By sourcing PowerShell include files, much of the internal functions are re-used across scripts. If anyone has questions, feedback, suggestions or would just like to discuss further, please leave your comments and we'll start a dialog.

Monday, May 19, 2008

Does the !New tag annoy you?


Are you a bit sick of that !New tag that appears beside a new item uploaded to a library? Well, you are not alone. I am repeatedly asked this question in our Applying SharePoint 2007 Core class: "How do I get rid of that annoying !New tag?" or "How long does an item really stays new?" J. Usually students ask these questions because they have uploaded/migrated a bunch of documents from their file share which are not really "new", but they get the !New tag assigned to them anyway.






Ok, so here are the answers:
First of all, this feature applies to not only Document Libraries, but also to other Lists such as Tasks, Issues, Contacts, Links etc. In the default installation of SharePoint, the "!New" tag gets appended to every new item in SharePoint lists and remains there for one calendar day. An administrator who has access to the SharePoint server has two options when changing the behavior of this feature:
  1. Increase the number of days the icon should appear beside a new item in the list
  2. Eliminate the appearance of the icon altogether
The stsadm.exe utility (located in the SharePoint server) is used to accomplish this task. Navigate to stsadm's location using a command prompt:
..\program files\common files\microsoft shared\web server extensions\60\bin
The "setproperty" command will be used for this task as follows:
stsadm.exe –o setproperty –pn days-to-show-new-icon –pv (number of days) –url (Virtual server address)

For example, use the following syntax to prevent the new tag from appearing:
stsadm.exe –o setproperty –pn days-to-show-new-icon –pv 0 –url http://(your server name)

Hope this helps!

Tuesday, May 06, 2008

Looping Through Items in a SharePoint List with SharePoint Designer Workflows


One of the things that most frustrates me about SharePoint Designer workflows is that there is no convenient way to write loops. Not only can we not create For…Next, For…Each, Do…While, and Do…Until loops within our workflows, but there is no obvious way to loop through items in a list.
The Scenario
For example, assume you have a document library with 1,000 documents in it. You have just written a workflow with SharePoint Designer to e-mail the document creator and have him or her enter certain pieces of metadata. This will work fine for new documents going forward, but what about the 1,000 documents that already exist? You certainly don't want to have to start all those workflows manually!
The Solution
You can accomplish this automatically by setting the workflow on the library to run whenever an item is changed. Then write another workflow on another list to cycle through all the documents in this Document Library and set a processed flag to both indicate which ones have been processed.
Getting the Library Ready
First, you need some way in the document library to identify which items have been processed—that is, on which items your workflow has already initiated the workflow. You can do this by adding a custom column named "Processed", of type Yes/No (checkbox), with the default value set to "No".
Second, you'll need to modify your workflow to run when an item in the library changes. If you don't want the workflow to run every time an item changes, but only when a new item is created, you may want to check your new Processed flag at the beginning of your workflow and have your workflow set it to "Yes" after it runs.
Writing the Loop Controller
You will need another list on which the Loop Control workflow will run. I prefer to use a custom list named "Loop Controller". I also add a field to track the ID of the last processed item. This column serves two purposes: we can change this value to re-run the workflow and it also serves as a gauge to show how far along the loop controller is in its process. This field is just a number field and you can name it "Last Updated Item" with a default value of "0".
Now you are ready to write a workflow on the Loop Controller. It should be set to run manually and when an item changes.
The workflow will not have any conditions, but will have several actions together:
  1. Look at the Document Library and get the ID for the first item that does not have its Processed value set to Yes. Store the ID for this item in a workflow variable named CurrentItemID.
  2. Change the value of the Processed field for the item in Document Library with the ID equal to the CurrentItemID. This change will cause the workflow on the Document Library to run for that item because you wrote the workflow to run when an item changed.
  3. Update the value in the Last Updated Item column on the Loop Controller list item. The workflow will stop after this action. However, because the item changed, and the workflow is set to start when an item changes, it will kick-off a new instance of the workflow. Through this stopping, restarting, and setting the Processed flags, you have created a workflow that will loop through the items in the Document Library.
Your completed workflow on the Loop Controller will look something like this.




Once you have finished writing the workflow on the Loop Controller list, you just need to add a new item and manually start the workflow.
If everything worked properly, then you can refresh the view of the custom list and watch the value in the Last Updated Column increase as it loops through the items.


You will notice that once the workflow can no longer locate an item in the Document Library with the "Processed" field equal to "No", the workflow will error out and stop. That's OK, because you want it to stop, otherwise you would have an infinite loop.
If you look at the history of the Loop Controller workflow, you will see where it ran once for each document in the Document Library.




If you look at the Document Library, you can see the workflow for each item was initiated when each item's "Processed" column was updated to "Yes".


When you are finished using the Loop Controller you can just delete the list and workflow.
Looping Inside a Workflow
Using this same technique of changing a value on an item, you can create workflows that run multiple times. You can even set a value in a column to act as a counter to control the number of times it runs. In some cases, by using conditions, you can simulate simple loops within a SharePoint Designer workflow.
Enjoy!

Monday, April 14, 2008

SharePoint Alerts—Activesync Synchronization Issues?


I got a new toy about three weeks ago...an AT&T Tilt. A.K.A. the HTC 8925, the Tilt runs Windows Mobile 6 and is my first portable device on which I will receive email. Okay, so I'm a bit late jumping into the whole mobile email thing. I've never thought of email (or me) as being important enough to have to have in front of me 24x7. So now I have it. I'll let you know later if that's good…or bad.


Learning curve engaged. I had to figure out this Activesync thing. You know, like Active X and Active desktop and Active Directory and Active bladder…but I digress (too many of those commercials on TV). Back to Activesync. Got the Activesync thing figured out pretty easily. My biggest problem was entering my "secure" password on the keyboard to set up my OWA Exchange account correctly. Things were rockin' and rollin' and mail was downloadin' until…


…about a week into it. Suddenly, my device stopped syncing. Well, partially, anyway. Seemed like it developed a distaste for my email. However, it was still syncing my contacts, calendar and tasks without a problem. Kinda' like Powerpoint, it started sucking the life right out of me as I began my troubleshooting journey. I did all the standard Windows Mobile things…delete the account and start over, soft reset, hard reset and finally, upgrading the ROM (I originally thought the upgrade had fixed the problem).


I found several articles that talked about a problem with corrupt emails causing Activesync to stop synchronizing emails. The question for me was how to determine which of my gazillion emails might be causing the problem. I removed all my emails from my Inbox (it needed a good housecleaning, anyway). I even tried moving entire folders into my PST to no avail. Still, no sync. It was all very interesting, as the error I had originally received suddenly went away and I was left with an errorless, non-syncing mailbox.


Well, someone else in the company (thanks, Eric) started having problems and determined quickly that it might be caused by a SharePoint alert! Must be a better Googler than I. Okay, now THAT makes sense. I've seen those SharePoint alerts in the past that won't open correctly in Outlook, so I thought it sounded plausible. Sure enough, I deleted my daily alerts from my Inbox and volia, I was syncing again.


I went back and tested the scenario by forcing a SharePoint immediate alert. Sure enough, as soon as I got the alert in my Inbox, my email stopped syncing with Activesync. Follow-up provided me with article 937788 from Microsoft which seems to shed more light on the issue. I've requested the hotfix to see if it really fixes the issue.


We all love SharePoint alerts, but they appear to have some undesirable side effects. I'm pretty sure I'm not the only one who has had (or is having) this issue. I've seen other posts with similar issues. I found out quickly that losing your portable email can lead to withdrawl!

Saturday, April 12, 2008

When Good Web Parts Go Bad


Here's a quick tip for a Saturday morning.

At some point it's bound to happen. You'll mess up a perfectly good SharePoint page by incorrectly configuring a web part. You may add some incorrect custom HTML, JavaScript, or style sheet code to a Content Editor Web Part, and then not be able to edit or close the web part. Or you may add a page viewer web part to frame an external web page and the webmaster controlling that external page writes some code to break it out of frames.

When this happens, you need some way to close the offending web part. To do this, go to the Edit Properties page for the page that contains the bad web part. If you scroll to the bottom of Edit Properties page, you will see a link to Open WebPart Page in maintenance view. Click on this link.

From the Web Part Page Maintenance page, you can select the web part you messed up and then either Close it, Reset it, or Delete it.

Ah! You're back in business!

Wednesday, March 26, 2008

Close Individual SharePoint Blog Posts for Comments


In a recent Extreme Makeover class, a student asked how they could close a Post in a SharePoint blog site for comments. They still wanted users to be able to read the blog post and read existing comments, but they didn't want them to be able to post comments on it. Kevin Pine and I worked together to create the following solution using some JavaScript in a Content Editor Web Part on the Posts.aspx page.

Add a Custom Column to the Posts List to Allow Comments

We needed some way for the blog owners to indicate when a post had been closed. On the Posts list, we added a new column named Allow Comments. We set the Type as Yes/No (check box) and the Default Value to Yes.

Update the Posts.aspx Page to Show the Value of the Allow Comments Column

Users can only submit comments while reading an individual post. This view of a blog post is generated by the Posts/Posts.aspx page. I clicked on the title of one of my blog posts to view the post in this page.

The Post Footer is displayed at the bottom of the post and above the comments section.

We needed the value of our new Allow Comments field to show up in the Post Footer. To accomplish this, we edited the current view by doing the following:

  1. Click on Site Actions, Edit Page.
  2. On the Posts web part, click on edit, Modify Shared Web Part.
  3. In the task pane, click on Edit the current view.

  4. Check the box beside Allow Comments, and click OK.

  5. View the blog post again and notice that the Allowed Comments value is now visible in the Post Footer.

Add the JavaScript to Hide the New Comment Fields If the Post is Closed for Comments

The form to submit new comments only appears on the Posts/Posts.aspx page. This means that we can add some JavaScript to this page to hide it if the phrase Allow Comments: No appears in that bottom line. Here is what we did to get the JavaScript on the page.

  1. While viewing a post in the Posts/Posts.aspx page, click on Site Actions, Edit Page.
  2. In the Right Web Part Zone, click on Add a Web Part.
  3. Select the Content Editor Web Part.
  4. Click the link to open the tool pane.
  5. Click on the Source Editor button.
  6. Paste in the following JavaScript code and click the Save button. Note that the code is fairly well commented if you are interested in what it is actually doing.

    <script language="JavaScript">
    //add an entry to the _spBodyOnLoadFunctionNames array

    //so that our function will run on the pageLoad event

    _spBodyOnLoadFunctionNames.push("hideComments");


    function hideComments() {

    //Create an array to store all elements to which

    //the ms-PstFooter class have been applied

    var PostFooterArray = getElementsByClass('ms-PostFooter');


    //Check to see if the first element of the array contains

    //the text "Allow Comments: No"

    if(PostFooterArray[0].innerHTML.indexOf('Allow Comments: No')!= -1){

    //create an array to store the SPAN tag with the ID of part1

    var commentSPAN = document.getElementById("part1");

    //Replace the comment form controls with my own HTML statement

    commentSPAN.innerHTML = '<b>Comments have been closed on this post.</b>';

    }

    }


    function getElementsByClass(theClass) {

    //From: http://www.seandempsey.com/code/getElementsByClass.php

    var allPageTags = new Array();

    //Populate the array with all the page tags

    allPageTags=document.getElementsByTagName("*");

    var Elements = new Array();

    //Cycle through the tags

    var n = 0;

    for (i=0; i<allPageTags.length; i++) {

    //Pick out the tags with our class name

    if (allPageTags[i].className==theClass) {

    Elements[n] = allPageTags[i];

    n++;

    }

    }

    return Elements;

    }

    </script>

  7. In the tool pane, expand the Appearance section.
  8. Set the Chrome Type to None.
  9. Click OK to close the tool pane.
  10. Click Exit Edit Mode.

Testing the Solution

My first blog post had the check box for the Allow Comments field checked. I can see the fields to post a comment.

If I uncheck the Allow Comments field and view the post, I can see that the comments have been closed.


Sunday, March 16, 2008

Changing Host Header for the MySite Host


I was playing around with SharePoint on my virtual machine and had set up a new installation of MOSS when I noticed I had entered a host header incorrectly (at least incorrectly from the standpoint of being consistent with my previous installations). I entered "MySite" instead of "MySites" as the first part of the host header. I know, I know, it's probably more correct to use the singular, but I was trying to be consistent with what others had done.
All I wanted to do was to change the host header and have SharePoint recognize the change. I performed the standard internet search and didn't get instant gratification, so I started playing around. I found that making the changes in three places and an IISRESET seemed to take care of my problem.
First, in Central Administration under Operations, Global Configuration, Alternate access mappings, edit the internal URL.


Next, open up IIS Manager and select the properties of the MySites web app. Change the host header by clicking on the Advanced… button.


Again in Central Administration, navigate to the Shared Services provider and click on My Site settings in the User Profiles and My Sites section. Change the personal site provider URL.


Perform an IISRESET. All should be well.

Monday, March 10, 2008

Get a Virtual PC image with SharePoint Solutions software pre-installed


Now you can download a Virtual PC hard drive image (VHD) with a forms-based SharePoint Extranet AND our entire software product line already installed and pre-configured for your evaluation. This downloadable virtual hard-drive image is pre-installed with:


  • SHAREPOINT SOLUTIONS EXTRANET COLLABORATION MANAGER
  • SHAREPOINT SOLUTIONS SITE PROVISIONING ASSISTANT
  • SHAREPOINT SOLUTIONS ALERT MANAGER 2007
  • MICROSOFT WINDOWS SERVER 2003 R2 ENTERPRISE EDITION
  • MICROSOFT SQL SERVER 2005 ENTERPRISE EDITION
  • SERVICE PACK 1 FOR MICROSOFT SQL SERVER 2005
  • MICROSOFT OFFICE PROFESSIONAL 2007
  • MICROSOFT OFFICE SHAREPOINT DESIGNER 2007
  • MICROSOFT WINDOWS SHAREPOINT SERVICES 3.0
  • MICROSOFT OFFICE SHAREPOINT SERVER 2007
  • MICROSOFT .NET FRAMEWORK 2.0
  • MICROSOFT .NET FRAMEWORK 3.0

The VHD evaluation images are designed to last for 30 days from the date the virtual machine is first run. After 30 days the image and all the software on the image will expire. Free registration is required for download.

Tuesday, March 04, 2008

Update from Microsoft SharePoint Conference 2008


By all accounts, Microsoft SharePoint Conference 2008 is a resounding success. There seems to be a lot of enthusiasm from both attendees and exhibitors alike.

Here's a photo of our SharePoint Solutions' booth:




Our session yesterday on SharePoint Extranets and FBA was standing-room-only. Here's a photo of myself presenting:

Monday, March 03, 2008

SharePoint and Office 14: Looks like I may have guessed right for once


For once, it looks like I may have guessed correctly about some enhancements we can expect in the next version of Microsoft SharePoint and Office: Office 14.

Three weeks ago, I was the keynote speaker at the SharePoint Information Worker Conference 2008. In my keynote, I gave my best guess at some of the enhancements we might see in the next version of SharePoint (expected sometime in late 2009).

Today, while listening to Bill Gates' Keynote address at the Microsoft SharePoint Conference in Seattle, I was pleasantly surprised to hear that I may have guessed right on my #1 prediction.

Gates said that a top priority for the next version of SharePoint is to beef up SharePoint Lists. SharePoint's List features don't sound as sexy and appealing as some of its other features, such as BI and the Business Data Catalog, but Lists are really the bedrock of the product and have proven to be a breath of fresh air to information workers who previously had to rely on Excel spreadsheets and Access databases for their seemingly endless need for tracking applications.

Unfortunately, so far SharePoint's List features have not been robust enough to develop true database applications. Fundamental features that application developers have come to expect in a relational database management system have been absent so far in SharePoint Lists due to a layer of architecture between SharePoint Lists and the underlying DBMS, SQL Server.

As I understood Gates today, that is all going to change in the next version of SharePoint. In the next version, SQL Server will "understand" SharePoint Lists and it will be possible to create a List that will, in effect, create a native table in the SQL Server database. Furthermore, because it is native, this will allow developers to make use of the more robust features of the DBMS and will eliminate the current performance issues with SharePoint Lists that have over 2000 rows.

In my mind, this improvement will also open the door for thousands of ISVs to port their vertical market applications to SharePoint as a platform, if they choose to do so.

I think this is great news for SharePoint as a development platform.