SharePoint Designer: Useful Tool or Spawn of the Devil?

OK, so you know how I’m going to come down on this, but I got your attention, right? In fact, at almost every large client I work with, the latter seems to be the opinion.  The rules are usually: No SharePoint Designer use in staging and production.  Period.

This stems from a fundamental lack of understanding of what SharePoint Designer is and what it allows you to do.  It’s unfortunate, because as the middle tier option to development in SharePoint, it facilitates the development of everything from page layouts to full-fledged applications with custom forms, navigation, and reporting.

A little history is probably useful here. SharePoint Designer is indeed FrontPage all grown up.  When Microsoft released SharePoint Designer, it was the newest version of FrontPage and a part of the Office 2007 suite of productivity tools.  At the same time (or very recently afterward), Microsoft forked FrontPage into Expression.  Initially SharePoint Designer and Expression looked like they were identical products, but Expression didn’t have support for SharePoint and it was targeted at designers more than developers.  (It has since been enhanced to be even more valuable in that space.)

So the fact that SharePoint Designer comes from FrontPage is probably a part of the prejudice. It was certainly true that letting a random user loose on older server platforms could cause utter havoc for the IT folks.  Those platforms usually didn’t have the permissions layers that SharePoint provides, however, and FrontPage was probably too liberally given out.  Enter the guard rail mentality. (I just hit the right one, let me pull hard left and — oops, I just hit the left one, so let me pull hard right, and…  You get the idea.)  FrontPage was bad, so SharePoint Designer, as its older sibling, must be even worse.

Another contributing fact to this whole picture is the experiences with SharePoint 2003 (or even earlier versions like 2001, Tahoe, TPU, etc.)..  In SharePoint 2003, there were serious issues with pages becoming "unghosted" and being "moved into the database".  This *sometimes* caused performance issues.  If you edited a page in FrontPage, it became unghosted.  Again, not in itself a bad thing, but FrontPage was the "cause".  This is no longer the case with SharePoint 2007.  While pages can be customized away from the site definition, there is not performance hit and they can be reverted back to the site definition easily if needed.

So what’s the case for using SharePoint Designer on SharePoint 2007 (whether it be MOSS or WSS)?  Well, you can do a heck of a lot without a "real" developer with faster turnaround cycles.  Here are some examples:

  • Customizing the master page and CSS — The master page drives the overall structure of a site’s pages. (It can be used at a single site level or at the Site Collection level.  Your choice.)  The master page is simply an HTML structure with a lot of SharePoint controls embedded in it.  The default master page is what exposes the tabbed navigation the welcome message, the search bar, etc. in the default site.  Because we’re in the 21st century, it, like most things, is all text.  (You can edit it with Notepad.)  However, SharePoint Designer "understands" all of the controls and gives you contextual menus and help to configure them.
  • Creating layout pages — Page layouts define the structure of pages that are set to use them.  Wait, this sounds like the master page, but it isn’t.  Page layouts inherit the master page and then can provide additional internal structure and Web Parts.  (If there’s even a chance that you will use variations, get in the habit of using page layouts — you’ll need them.)
  • Adding Data View Web Parts to pages — Data View Web Parts (DVWPs or Data Form Web Parts) are, as I’ve said many times, the Swiss Army Knife of Web Parts.  They give you total freedom over how you display content and can connect to many types of Data Sources.  You can (though you wouldn’t want to) edit the XSL for DVWPs through the UI, but again, SharePoint Designer is fully DVWP and XSL aware, so you really want to use it.  You can only add DVWPs to pages using SharePoint Designer.
  • Workflows — There’s a middle tier of workflow development (between the UI and Visual Studio) where I would argue you can do 80% of what you’ll need.  And SharePoint Designer is the tool that you need to do it.  There is a nice If-Then-Else editor in SharePoint Designer that lets you do this very well.

Now all of these things (except workflows — they are in a class by themselves in many ways) can be edited through the UI if you are a Site Collection Administrator.  You simply navigate to the containing location (Master Page Gallery, Pages folder, or whatever), download the file to your hard drive, open it in Notepad, and edit away.  You can then upload the file, check it in, and approve it (where required) and you have a new version in place.  Odds are very good that it won’t work because there’s far too much room for error, but you’ve just edited a file without using SharePoint Designer.  If you are a masochist, maybe this is the way to go for you.

Keep in mind that in SharePoint, EVERYTHING — let me repeat — EVERYTHING is security trimmed.  If you don’t have permission to access something through the UI, you won’t be able to access it through SharePoint Designer, either.  So you can’t just open the root site and delete everything.  If you are a Site Collection Administrator, you have the choice to be this destructive through the UI or through SharePoint Designer, but the average user can’t even open anything if they have SharePoint Designer.

So what does prohibiting SharePoint Designer protect?  Not much, really.  A malicious user with permissions can wreak havoc.  An untrained user with permissions can wreak havoc.  And both of these statements are true without SharePoint Designer.  With SharePoint Designer and a bit of training, your users (and I wouldn’t suggest that you need more than a few people with SharePoint Designer and the permissions to use it) can build wonderful functionality.  They can’t touch anything on the server.  Nothing in the file system.  No server settings.

When you add these things up, does SharePoint Designer sound like a useful tool or the spawn of the devil?

Sorting Displays in Data View Web Parts

I figured out a great little trick for sorting a DVWP-based display based on a column name passed in the Query String.  You can see this in action in the default All Items or All Documents views in lists or libraries.  When you click on the column headers that allow sorting, the browser is sent to the same page with the Query String looking something like:

http://[servername]/Lists/[listname]Forms/AllItems.aspx?SortField=ContentType&SortDir=Asc

By passing the column name that you want to sort on in the Query String, the List View Web Part “knows” what to sort the display on and in what order.

If you want to build analogous functionality in a DVWP, you can easily rig the column headers to act the same way, but the trick is to get the items to then sort correctly.  In your body template, you need some XSL like the following:

<xsl:variable name="AscDesc">
 <xsl:choose>
  <xsl:when test="string-length($SortDir) = 0 or $SortDir = 'Asc'">ascending</xsl:when>
  <xsl:otherwise>descending</xsl:otherwise>
 </xsl:choose>  
</xsl:variable>
<xsl:for-each select="$Rows">
 <xsl:sort select="@*[name()=$SortField]" order="{$AscDesc}" />  
 <xsl:call-template name="Community_ContentDetailed.rowview">
  <xsl:with-param name="SiteType" select="$SiteType" /> 
  <xsl:with-param name="CommunityContentFilterValue" select="$CommunityContentFilterValue" /> 
 </xsl:call-template>
</xsl:for-each>

The cool line is #8.  It allows you to take the Query String value for SortField and use it as the column name by which to sort.  The select parameter in the xsl:sort evaluates to @ContentType (from the URL above).

I wanted to be able to easily add the sorting option to header columns in my DVWPs, so I created the following utility template to create the links.  It decides based on the Query String values whether this is the first or second click of the column header (ascending or descending) and shows the appropriate image indicator for the result.

 <xsl:template name="SortHeader">
  <xsl:param name="LinkText"/>
  <xsl:param name="ColumnName"/>
  <xsl:param name="SortField"/>
  <xsl:param name="SortDir"/>
  <xsl:variable name="ASortIMG" select="'/_layouts/images/sortaz.gif'"/>
  <xsl:variable name="DSortIMG" select="'/_layouts/images/sortza.gif'"/>
  <xsl:variable name="SortableIMG" select="'/SiteCollectionImages/sortable.gif'"/>
  <xsl:variable name="AscDesc">
   <xsl:choose>
    <xsl:when test="$ColumnName = $SortField and (string-length($SortDir) = 0 or $SortDir = 'Asc')">Desc</xsl:when>
    <xsl:otherwise>Asc</xsl:otherwise>
   </xsl:choose>
  </xsl:variable>
  <img alt="Sortable" border="0" src="{$SortableIMG}"/>
  <a href="#">
   <xsl:attribute name="onclick">
    document.location = '<xsl:value-of select="$URL"/>' +
     '?SortField=' + '<xsl:value-of select="$ColumnName"/>' +
     '&amp;SortDir=' + '<xsl:value-of select="$AscDesc"/>';
   </xsl:attribute>
   <xsl:value-of select="$LinkText"/>
   <span style="padding-left:2px;">
   <xsl:choose>
    <xsl:when test="$ColumnName = $SortField and ($SortDir = '' or $SortDir = 'Asc')">
     <img alt="Ascending - Click to Reverse" border="0" src="{$ASortIMG}"/>
    </xsl:when>
    <xsl:when test="$ColumnName = $SortField and $SortDir = 'Desc'">
     <img alt="Descending - Click to Reverse" border="0" src="{$DSortIMG}"/>
    </xsl:when>
    <xsl:otherwise>
    </xsl:otherwise>
   </xsl:choose>
   </span>
  </a>
 </xsl:template>

Replacing ListIDs with ListNames in Data View Web Parts

This is another idea that I was certain I’d posted about long ago, but I can’t seem to find.  It’s especially useful if you are developing Data View Web Parts (DVWPs) in one environment and then porting them to another on any sort of frequent basis.

When you create a Data Source for a SharePoint list, you end up with some code that looks roughly like this by default (This is a code snippet from a DVWP in WSS and I’ve added spacing to make it more readable.):

<DataSources>
    <SharePoint:SPDataSource runat="server" DataSourceMode="List" UseInternalName="true" selectcommand="<View></View>" id="dataformwebpart1">
        <SelectParameters>
            <asp:Parameter Name="ListID" DefaultValue="6335D0C8-3089-435F-80C4-12850D5CB7D3"/>
        </SelectParameters>
        <DeleteParameters>
            <asp:Parameter Name="ListID" DefaultValue="6335D0C8-3089-435F-80C4-12850D5CB7D3"/>
        </DeleteParameters>
        <UpdateParameters>
            <asp:Parameter Name="ListID" DefaultValue="6335D0C8-3089-435F-80C4-12850D5CB7D3"/>
        </UpdateParameters>
        <InsertParameters>
            <asp:Parameter Name="ListID" DefaultValue="6335D0C8-3089-435F-80C4-12850D5CB7D3"/>
        </InsertParameters>
    </SharePoint:SPDataSource>
</DataSources>

Alternatively, you may see a slightly different structure in MOSS and/or if you have chosen a list in a different site (smaller snippet):

<SelectParameters>
    <WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="BCD0B397-2550-4937-892A-64370C87913E" />
    <WebPartPages:DataFormParameter Name="WebURL" ParameterKey="WebURL" PropertyName="ParameterValues" DefaultValue="/" />
</SelectParameters>

As you can see, in both cases the list is referred to by its GUID.  This is great on one level because the reference stays intact if you rename the list.  However, if you want to move your DVWP code to another environment which refers to the same list, it will throw an error because the list GUID is different.

So rather than changing the ListID each time, replace the occurrences of ListID with ListName.  This lets you refer to the list by its obvious name rather than the more obscure GUID:

<DataSources>
    <SharePoint:SPDataSource runat="server" DataSourceMode="List" UseInternalName="true" selectcommand="<View></View>" id="dataformwebpart1">
        <SelectParameters>
            <asp:Parameter Name="ListName" DefaultValue="My List Name"/>
        </SelectParameters>
    </SharePoint:SPDataSource>
</DataSources>

or

<SelectParameters>
    <WebPartPages:DataFormParameter Name="ListName" ParameterKey="ListName" PropertyName="ParameterValues" DefaultValue="My Other List Name" />
    <WebPartPages:DataFormParameter Name="WebURL" ParameterKey="WebURL" PropertyName="ParameterValues" DefaultValue="/" />
</SelectParameters>

This makes your DVWP code portable to other environments where there is a list in the same location with the same name, like a DEV/Staging/PROD construct.

Note that I’ve also removed the Delete, Update, and Insert sections above.  If your DVWP is just displaying the contents of a list (which is probably the most common use of a DVWP), you don’t need the Delete, Update, and Insert sections.

Technorati tags: , , ,

Backing Up SharePoint

I think I’ve posted about this before, but it seems that every client I have asks me about how best to do backups with SharePoint.  If you are just backing up the server, you have almost zero (repeat: ZERO) chance of restoring after a problem.

Keep in mind that a backup strategy only works if you regularly test things by restoring your backup into a new environment. If your backups are no good, then you might as well not do them!

Here’s a backup script that you can schedule.  Many people also run a SQL backup, but the STSADM backup of the Site Collection is the fastest way I know of back from disaster.  This is simply a Windows batch file which you can schedule and expand upon, as needed.  Change the ‘Set variables here’ section to reflect your environment.

@echo off
echo ===========================================
echo Backup Script For Microsoft Office SharePoint Server 2007 (MOSS)
echo   or Windows SharePoint Services 2007 (WSS)
echo ===========================================

REM Set variables here
set SharePointServer=http://spvm
set BackupLocation=c:backupsharepointbackupsharepoint.dat
set CopyLocation=Z:temp
REM Done setting variables

echo Backing up %SharePointServer% to %BackupLocation%
"C:program filescommon filesmicrosoft sharedweb server extensions12binstsadm.exe" -o backup -url %SharePointServer% -filename %BackupLocation% -overwrite

echo Copying backup file from %BackupLocation% to %CopyLocation%
copy %BackupLocation% %CopyLocation%

echo Backup operation completed

Technorati tags: ,

“Content Type is still in use” Error

Sometimes when you try to remove a Content Type’s association from a list, you will get the error "Content Type is still in use", even if you don’t seem to have any items in the list!

Here are a few things to check:

  • Make sure that the Content Type isn’t the default – Go to the list settings and under the Content Types section, click on the ‘Change new button order and default content type’ link.  Be sure that the Content Type that you want to delete isn’t in position one, which makes it the default Content Type for the list.
  • Empty the Recycling Bin – If you have deleted items that are based upon the Content Type you are trying to delete, you probably won’t be able to delete it.  At least clean out the items that used it, and don’t forget that there are two levels of Recycling Bins (Site and Site Collection).
  • Make sure that others don’t have items associated with the Content Type that you can’t see – This usually will happen in Document Libraries.  Go to the list settings and click on the ‘Manage checked out files’.  You may see some items listed that belong to other users which they haven’t checked in.  Assuming that it is appropriate, take ownership of those items and then you can delete them.

All this assumes that you are a Site Collection Administrator, since you probably wouldn’t be doing this if you weren’t.  If you aren’t a Site Collection Administrator, then send these pointers to someone who is and have them check things for you.

Technorati tags: ,