Basic Page Document Libraries upgraded to SharePoint 2010 and an error message: “Cannot save your changes”

This one was quite an anomally so I thought I’d share…

Scenario:

I have a SharePoint sub-site with multiple Document Libraries created with the Basic Page Document Template. These libraries were upgraded from MOSS 2007 to SharePoint 2010. Users with Full Control were able to modify/edit the pages just fine but users with Contributor rights were not able to save changes made to existing content pages. When Contributors tried to save changes made with the WYSIWYG editor, which seems to be a variation of the Content Editor Web Part, they get an browser popup error message that says “Cannot save your changes“.

Troubleshooting Steps:

I went up, down and all around all of the permission configurations on the site, library, parents, etc. but couldn’t see why a user with Contributor rights was not able to save their changes. Also of note: the content rendered ok in IE7 but didn’t render at all for the user in Firefox. I finally ended up deleting the page and tried to recreate it with the affected user’s session using Firefox. That’s when I was given a much better clue. The Content Editor Web Part on the page returned: “A Web Part or Web Form Control on this Page cannot be displayed or imported. You don’t have Add and Customize Pages permissions required to perform this action.”

Solution:

With that clue, I trekked up to the root site and modified the permission levels for Contributors. Site Actions > Site Permissions > Permission Levels (in the ribbon) > Clicked on Contribute and then checked the “Add and Customize Pages” option. And that did it!

SharePoint 2010 Converting From Classic to Claims Authentication Lessons Learned

Environment

2 x SharePoint Server 2010 Web Front Ends
2 x SharePoint Server 2010 Application Servers
1 x SQL Server 2008 R2 DB Server
SSL and load-balanced enabled web application

What we’re trying to do

After upgrading a SharePoint 2007 content database to a SharePoint 2010 farm, we wanted to convert to Claims Authentication. Following the TechNet documentation on this, we came across an error message after trying to set the authentication provider as shown below:

Set-SPwebApplication $wa -AuthenticationProvider (New-SPAuthenticationProvider) -Zone Default

Error Message

Set-SPWebApplication: An update conflict has occurred, and you must re-try this action. The object SPWebApplication Name=SharePoint Sites was updated by domain\serviceAccount, in the OWSTIMER process, on machine SPServer. View the tracing log for more information about the conflict.

After spending over 20 hours going through a multitude of workarounds and scenarios including those that are commonly known like deleting the file cache, I think I’ve come up with a series of steps that is repeatable and reliable (at least in my environment).

Steps

1. Perform all actions below using the farm account.

2. Create your web app in classic authentication mode, attach content DB, etc. as appropriate. In my environment the base line web application included host headers, SSL and load-balancing already configured. We also had the appropriate content database attached and upgraded. One lesson learned here was that you’ll want to do a backup of your content database after a successful mount-spcontentdatabase and upgrade (if you’re upgrading from SharePoint 2007) prior to performing any more actions on it. Depending on how large your content database is, it will behoove you to back it up since the upgrade process could be very time consuming on large content databases.

3. Log on to each non-database server in the farm and stop the “SharePoint 2010 Timer” service. You can get to this by going to Start > Run > Services.msc

4. Pre-emptively clear the file cache on each of the non-database servers. The file cache can typically be found on each server here: C:\ProgramData\Microsoft\SharePoint\Config. If you don’t see some files/folders, you may have to unhide file extensions and hidden files/folders. Now you’ll notice that there may be more than 1 folder with GUIDs. You’ll want to open them and find the one that contains nothing but XML files and one “cache.ini” file. Go ahead and delete all the XML files. With the lonely cache.ini file that’s left over, open it up, delete all the contents and replace it with “1”. Make sure you do this on all non-database servers. You’ll know that you correctly stopped the SharePoint Timer Service if the XML files don’t come back.

5. Now let’s move forward with the claims authentication conversion. You can follow all of the instructions on TechNet here. To summarize, you’ll want to perform the following commands in Powershell via the SharePoint 2010 Management Shell:

$WebAppName = "http://yourWebAppUrl"
$account = "yourDomain\farmaccount"
$wa = get-SPWebApplication $WebAppName
Set-SPwebApplication $wa -AuthenticationProvider (New-SPAuthenticationProvider) -Zone Default

Press 'Y' for Yes when prompted

$wa = get-SPWebApplication $WebAppName
$account = (New-SPClaimsPrincipal -identity $account -identitytype 1).ToEncodedString()

$zp = $wa.ZonePolicies("Default")
$p = $zp.Add($account,"PSPolicy")
$fc=$wa.PolicyRoles.GetSpecialRole("FullControl")
$p.PolicyRoleBindings.Add($fc)
$wa.Update()

$wa = get-SPWebApplication $WebAppName
$wa.MigrateUsers($true)

6.  What the above commands will do is convert the authentication mechanism for the web application to claims-based as well as convert all the user objects in your content database to the claims format (“i:0#.w|domain\account”). Now a thing to note here is that through experimentation, I found that even when/if I get the failure/error message (in red above) after the “Set-SPwebApplication $wa -AuthenticationProvider (New-SPAuthenticationProvider) -Zone Default” command, my content database is not corrupted. So no need to restore/re-upgrade. It seems to have performed the proper claims updates to the content database and then tries to update the web application. If the error occurs, you’ll want to dismount the content database, delete the web application, re-create the web application in classic authentication mode and then start over with Step 1 above. If you don’t get any error messages, then continue below.

7. Time to update the Portal Super User and Super Reader Accounts using the same Powershell session as above. If you had closed it already, you’ll need to grab the web application object again before performing the commands below:

$wa.Properties["portalsuperuseraccount"] = "i:0#.w|domain\yourSuperuserAccount"
$wa.Properties["portalsuperreaderaccount"] = "i:0#.w|domain\yourSuperReaderAccount"
$wa.Update()

8. Time to start all of the SharePoint Timer Jobs (services.msc). When you perform this action, you’ll notice that all of the cache files you had deleted previously will be recreated and the number in the cache.ini file will show a number other than 1. After a few minutes, the timer service will propagate all of the changes that we’ve been performing to all of the web front end servers. You can monitor this by checking on the status of the timer jobs @ http://centraladmin/_admin/Timer.aspx.

9. Now you should be able to login to your site and everything should work! You should not get any Access Denied messages. If you do, you probably didn’t do Step 7 properly. If you look at the permissions and users in your claims-based SharePoint site, you’ll notice that almost all users will be in the claims format (“i:0#.w|domain\account”). If they are not, they are most likely users that no longer exist in Active Directory. If they are valid users and for some reason didn’t have their login name changed then you have some other problems that I haven’t come across yet. Another place to review to make sure that claims is working properly is to look at the user policy for your web app. There’s a “User Policy” button in the ribbon of this Central Admin page: http://centraladmin/_admin/WebApplicationList.aspx.

Happy claiming! 🙂


Reporting on All Sites for a SharePoint Web Application

Hey everyone! I’ve been knee deep in a migration project the last month and just now got a breather to post some of the goodies I’ve come up with. So in analyzing our current SharePoint farm we wanted to generate a report of what kind of SharePoint sites exist in our environment. We were looking for these attributes:

– When was the site created?
– When was the site last modified?
– Who owns the site?
– Site name
– Site URL
– The name of the template being used
– The template ID
– Whether or not it’s a Fab40 site
– How many items are in the site?
– Whether or not the site is empty

How did we use this script?

I originally wrote this script to detect all instances of Fab40 sites so that we could deal with them (get rid of them prior to upgrading to SharePoint 2010), but then extended it to report on all site templates. This gave me the information I needed to reach out to all the site owners to clean up empty and/or low usage sites (we don’t have automatic site deletion turned on).

The Script

# This script will generate a report of all sites in the web app
# along with how many items are within the site.
#
# Author: Henry Ong

######################## Start Variables ########################
$siteURL = “http://siteurl” #URL to any site in the web application.
$filePath = “D:\PowerShellScripts\AllSites.csv”
######################## End Variables ########################

if(Test-Path $filePath)
{
Remove-Item $filePath
}
Clear-Host

[System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.SharePoint”)

# Creates an object that represents a SharePoint site.
function CreateNewObject
{
$customObject = New-Object system.Object
$customObject | Add-Member -type NoteProperty -Name WebCreated -Value $web.Created.ToShortDateString()
$customObject | Add-Member -type NoteProperty -Name WebModified -Value $web.LastItemModifiedDate.ToShortDateString()
$customObject | Add-Member -type NoteProperty -Name RequestAccessEmail -Value $web.RequestAccessEmail
$customObject | Add-Member -type NoteProperty -Name WebTitle -Value $web.Title
$customObject | Add-Member -type NoteProperty -Name WebURL -Value $web.URL
$customObject | Add-Member -type NoteProperty -Name WebTemplateName -Value $web.WebTemplate
$customObject | Add-Member -type NoteProperty -Name WebTemplateID -Value $web.WebTemplateId
$customObject | Add-Member -type NoteProperty -Name Fab40Site -Value $false
$customObject | Add-Member -type NoteProperty -Name ItemCount -Value “”
$customObject | Add-Member -type NoteProperty -Name SiteAdmins -Value “”
$customObject | Add-Member -type NoteProperty -Name GreaterThanZeroItems -Value $false

return $customObject
}

$site = new-object microsoft.sharepoint.spsite($siteURL)
$webApp = $site.webapplication
$allSites = $webApp.sites

$customObjectsList =@()

foreach

($site in $allSites)
{
$allWebs = $site.AllWebs

foreach ($web in $allWebs)
{
$itemCount = 0;

$customWebObject = CreateNewObject

$templateID = $web.WebTemplateID.ToString()

if($templateID.startswith(“758”))
{
$customWebObject.Fab40Site = $true
}

foreach ($list in $web.Lists)
{
if(($list.ItemCount -gt 0) -and ($list.Hidden -ne $true))
{
$customWebObject.GreaterThanZeroItems = $true

$itemCount += $list.ItemCount;
}
}

$customWebObject.ItemCount = $itemCount.ToString()
$customObjectsList += $customWebObject

foreach($user in $web.SiteAdministrators)
{
$customWebObject.SiteAdmins += “$user ”
}

Write-Host $web.title
$web.Dispose()
}
$site.dispose()
}

# Exporting the data to a CSV file
$customObjectsList | Select-Object WebCreated,WebModified,WebTitle,WebURL,WebTemplateName,WebTemplateID,Fab40Site,ItemCount,GreaterThanZeroItems,SiteAdmins,RequestAccessEmail | Export-Csv $filePath
write-host “Done”

 Don’t forget to use PowerGUI! PowerGUI is your friend – http://powergui.org/index.jspa

SharePoint Link Reporter using PowerShell

Hey gang! So here’s what I’ve been cooking up… I was trying to figure out a way to generate a report of all (almost all) links within a SharePoint web application so that I could analyze them for potential broken links come a migration project. Details and the PowerShell script are below. Or you can download the script here.

Scenario:
In preps for a SharePoint 2007 to SharePoint 2010 migration project, I wanted to do a prescan of all the links in the:

– Quick Launch
– Top/Global Navigation
– Links Lists

Why? Well, since we have an environment with a bajillion collaborative Site Collections, we all know it’s rare for an end-user to use relative links when manually creating links to things within the SharePoint sites. More often than not, absolute links will be used which could be quite a quandary in a migration situation in which URLs can change for any number of reasons. With this report, I could scan through it very quickly and notify site owners about links that may need to be updated in the future.

Solution Synopsis:
Loop through every SharePoint Site and Site Collection in a web application, every link in the Quick Launch menus of each site, every Top/Global Nav link of each site, and every Links list of each site. Take all that and dump into a spreadsheet. Visually scan spreadsheet for suspect links. This script will work with both publishing and non-publishing sites. *Have not tested on SharePoint 2010 environments.

*Modified script 5/23/2011 to also report all links starting with “http://” contained within the body of the default page for each SPWeb object. This is useful in case end-users manually put links into Content Editor Web Parts or Page Content Controls that link back to site content using absolute links. Thanks to the Hey, Scripting Guy! Blog for the inspiration.

Sample Output:

Click here for a sample output CSV.

PowerShell Script (feel free to leave constructive feedback if you see something that could be done differently/better!):

# This script will print out a list of all links in the Quick Launch, Links Lists, and default
# page for each SPWeb object to a text file located in the directory specified in the variables section.
#
# Author: Henry Ong
######################## Start Variables ########################
$siteURL = "http://alvhong2" #URL to any site in the web application.
$filePath = "C:\PowerShellScripts\Links.csv"
$PublishingFeatureGUID = "94c94ca6-b32f-4da9-a9e3-1f3d343d7ecb"
######################## End Variables ########################
if(Test-Path $filePath)
{
 Remove-Item $filePath
}
Clear-Host
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Publishing")
[System.Reflection.Assembly]::LoadWithPartialName("System.Net.WebClient")

# Creates an object that represents an SPWeb's Title and URL
function CreateNewWebObject
{
 $linkObject = New-Object system.Object
 $linkObject | Add-Member -type NoteProperty -Name WebTitle -Value $web.Title
 $linkObject | Add-Member -type NoteProperty -Name WebURL -Value $web.URL

 return $linkObject
}
# Creates an object that represents the header links of the Quick Launch
function CreateNewLinkHeaderObject
{
 $linkObject = New-Object system.Object
 $linkObject | Add-Member -type NoteProperty -Name WebTitle -Value $prevWebTitle
 $linkObject | Add-Member -type NoteProperty -Name WebURL -Value $prevWebURL  
 $linkObject | Add-Member -type NoteProperty -Name QLHeaderTitle -Value $node.Title
 $linkObject | Add-Member -type NoteProperty -Name QLHeaderLink -Value $node.Url
 return $linkObject
}
# Creates an object that represents to the links in the Top Link bar
function CreateNewTopLinkObject
{
 $linkObject = New-Object system.Object
 $linkObject | Add-Member -type NoteProperty -Name WebTitle -Value $prevWebTitle
 $linkObject | Add-Member -type NoteProperty -Name WebURL -Value $prevWebURL  
 $linkObject | Add-Member -type NoteProperty -Name TopLinkTitle -Value $node.Title
 $linkObject | Add-Member -type NoteProperty -Name TopLinkURL -Value $node.Url
 $linkObject | Add-Member -type NoteProperty -Name TopNavLink -Value $true
 return $linkObject
}
# Creates an object that represents the links of in the Quick Launch (underneath the headers)
function CreateNewLinkChildObject
{
 $linkObject = New-Object system.Object
 $linkObject | Add-Member -type NoteProperty -Name WebTitle -Value $prevWebTitle
 $linkObject | Add-Member -type NoteProperty -Name WebURL -Value $prevWebURL
 $linkObject | Add-Member -type NoteProperty -Name QLHeaderTitle -Value $prevHeaderTitle
 $linkObject | Add-Member -type NoteProperty -Name QLHeaderLink -Value $prevHeaderLink
 $linkObject | Add-Member -type NoteProperty -Name QLChildLinkTitle -Value $childNode.Title
 $linkObject | Add-Member -type NoteProperty -Name QLChildLink -Value $childNode.URL
 return $linkObject
}
## Creates an object that represents items in a Links list.
function CreateNewLinkItemObject
{
 $linkObject = New-Object system.Object
 $linkObject | Add-Member -type NoteProperty -Name WebTitle -Value $prevWebTitle
 $linkObject | Add-Member -type NoteProperty -Name WebURL -Value $prevWebURL
 $linkObject | Add-Member -type NoteProperty -Name ListName -Value $list.Title

 $spFieldURLValue = New-Object microsoft.sharepoint.spfieldurlvalue($item["URL"])

 $linkObject | Add-Member -type NoteProperty -Name ItemTitle -Value $spFieldURLValue.Description
 $linkObject | Add-Member -type NoteProperty -Name ItemURL -Value $spFieldURLValue.Url
 return $linkObject
}
# Determines whether or not the passed in Feature is activated on the site or not.
function FeatureIsActivated
{param($FeatureID, $Web)
 return $web.Features[$FeatureID] -ne $null
}
# Creates an object that represents a link within the body of a content page.
function CreateNewPageContentLinkObject
{
 $linkObject = New-Object system.Object
 $linkObject | Add-Member -type NoteProperty -Name WebTitle -Value $prevWebTitle
 $linkObject | Add-Member -type NoteProperty -Name WebURL -Value $prevWebURL
 $linkObject | Add-Member -type NoteProperty -Name PageContentLink -Value $link

 return $linkObject
}
$wc = New-Object System.Net.WebClient
$wc.UseDefaultCredentials = $true
$pattern = "(((f|ht){1}tp://)[-a-zA-Z0-9@:%_\+.~#?&//=]+)"
$site = new-object microsoft.sharepoint.spsite($siteURL)
$webApp = $site.webapplication
$allSites = $webApp.sites
$customLinkObjects =@()foreach
($site in $allSites)
{
 $allWebs = $site.AllWebs

 foreach ($web in $allWebs)
 {
  ## If the web has the publishing feature turned OFF, use this method
  if((FeatureIsActivated $PublishingFeatureGUID $web) -ne $true)
  {
   $quickLaunch = $web.Navigation.QuickLaunch
   $customLinkObject = CreateNewWebObject
   $customLinkObjects += $customLinkObject

   $prevWebTitle = $customLinkObject.WebTitle
   $prevWebURL = $customLinkObject.WebURL

   # First level of the Quick Launch (Headers)
   foreach ($node in $quickLaunch)
   {
    $customLinkObject = CreateNewLinkHeaderObject

    $customLinkObjects += $customLinkObject

    $prevHeaderTitle = $node.Title
    $prevHeaderLink = $node.Url

    # Second level of the Quick Launch (Links)
    foreach ($childNode in $node.Children)
    {
     $customLinkObject = CreateNewLinkChildObject

     $customLinkObjects += $customLinkObject
    }
   }

   # Get all the links in the Top Link bar
   $topLinks = $web.Navigation.TopNavigationBar
   foreach ($node in $topLinks)
   {
    $customLinkObject = CreateNewTopLinkObject

    $customLinkObjects += $customLinkObject

    $prevHeaderTitle = $node.Title
    $prevHeaderLink = $node.Url    
   }
  }

  ## If the web has the publishing feature turned ON, use this method
  else
  {
   $publishingWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($web)
   $quickLaunch = $publishingWeb.CurrentNavigationNodes
   $customLinkObject = CreateNewWebObject
   $customLinkObjects += $customLinkObject

   $prevWebTitle = $customLinkObject.WebTitle
   $prevWebURL = $customLinkObject.WebURL

   # First level of the Quick Launch (Headers)
   foreach ($node in $quickLaunch)
   {
    $customLinkObject = CreateNewLinkHeaderObject

    $customLinkObjects += $customLinkObject

    $prevHeaderTitle = $node.Title
    $prevHeaderLink = $node.Url

    # Second level of the Quick Launch (Links)
    foreach ($childNode in $node.Children)
    {
     $customLinkObject = CreateNewLinkChildObject

     $customLinkObjects += $customLinkObject
    }
   }

   # Get all the links in the Top Link bar
   $topLinks = $web.Navigation.TopNavigationBar
   foreach ($node in $topLinks)
   {
    $customLinkObject = CreateNewTopLinkObject

    $customLinkObjects += $customLinkObject

    $prevHeaderTitle = $node.Title
    $prevHeaderLink = $node.Url    
   }   

  }

  #Looking for lists of type Links
  $lists = $web.Lists
  foreach ($list in $lists)
  {
   if($list.BaseTemplate -eq "Links")
   {
    $prevWebTitle = $customLinkObject.WebTitle
    $prevWebURL = $customLinkObject.WebURL

    # Going through all the links in a Links List
    foreach ($item in $list.Items)
    {
     $customLinkObject = CreateNewLinkItemObject

     $customLinkObjects += $customLinkObject     
    }

Write-Host $list.Title
   }  
  }

  #Looking at the default page for each web for links embedded within the content areas
  $htmlContent = $wc.DownloadString($web.URL)
  $result = $htmlContent | Select-String -Pattern $pattern -AllMatches
  $links = $result.Matches | ForEach-Object {$_.Groups[1].Value}
  foreach ($link in $links)
  {
   $customLinkObject = CreateNewPageContentLinkObject
   $customLinkObjects += $customLinkObject
  }

Write-Host $web.Title
  $web.Dispose()
 }
$site.dispose()
}
# Exporting the data to a CSV file
$customLinkObjects | Select-Object WebTitle,WebURL,TopNavLink,TopLinkTitle,TopLinkURL,QLHeaderTitle,QLHeaderLink,QLChildLinkTitle,QLChildLink,ListName,ItemTitle,ItemURL,PageContentLink | Export-Csv $filePath
write-host "Done"

The workflow failed to start due to an internal error.

Inheriting a new SharePoint environment is so fun (not) because I get to come across wacky errors like this. Luckily it only gave me bad dreams for a couple of days.

The workflow failed to start due to an internal error.

Trobleshooting Steps Taken:

1. Reproduced using a fresh Document Library in the same site.
2. Reproduced on another site in the same Site Collection.
3. Did not reproduce in a different Site Collection in Farm.
4. Performed a STSADM Export/Import to a dev farm: could not reproduce error.
5. Looked at diagnostic logs, Event Viewer: Not much going on there.

Solution:

Now if you go out and do some googling, there’s quite a bit of chatter about this error with all kinds reported solutions ranging from uninstalling/reinstalling the workflow features to modifying feature XML files and all kinds of other stuff. The one that got me to a resolution was this one.

But wait, there’s more!

I thought since the approval workflow that I was working with was the suspect one that deactivating/reactivating the feature for the “ReviewWorkflows” feature would fix the issue. It didn’t. What I had to do was deactivate/reactivate all of the workflows like so:

stsadm -o Deactivatefeature -name TranslationWorkflow -url http://SiteCollectionURL
stsadm -o Deactivatefeature -name ReviewWorkflows -url http://SiteCollectionURL
stsadm -o Deactivatefeature -name SignaturesWorkflow -url http://SiteCollectionURL
stsadm -o Deactivatefeature -name ExpirationWorkflow -url http://SiteCollectionURL
stsadm -o Deactivatefeature -name IssueTrackingWorkflow -url http://SiteCollectionURL
stsadm -o Activatefeature -name TranslationWorkflow -url http://SiteCollectionURL
stsadm -o Activatefeature -name ReviewWorkflows -url http://SiteCollectionURL
stsadm -o Activatefeature -name SignaturesWorkflow -url http://SiteCollectionURL
stsadm -o Activatefeature -name ExpirationWorkflow -url http://SiteCollectionURL
stsadm -o Activatefeature -name IssueTrackingWorkflow -url http://SiteCollectionURL

So after deactivating/reactivating all the workflow features listed above, the Site Collection was happy once again. 🙂

Adding or removing a Site Collection Administrator from all Site Collections

So let’s say you have an environment with a bajillion Site Collections and some new hire comes along and wants to be a Site Collection Administrator to every Site Collection in your farm. And because your boss said so. What would you do?

A. Tell him no way jose!

B. Add him to each Site Collection as a Site Collection Administrator manually a bajillion times. And then file for worker’s comp.

C. Tell him to manually request access to each SharePoint site as he goes along.

D. It’s PowerShell time!

If you picked D, I applaud you. Now here’s a PowerShell script that’ll carry this out for you:

# This script will add or remove a named Site Collection Administrator
# to all Site Collections within a Web Application.
#
# Author: Henry Ong
######################## Start Variables ########################
$newSiteCollectionAdminLoginName = "domain\login"
$newSiteCollectionAdminEmail = "your.email@address.com"
$newSiteCollectionAdminName = "Your Display Name"
$newSiteCollectionAdminNotes = ""
$siteURL = "http://SharePointSiteURL" #URL to any site in the web application.
$add = 1 # 1 for adding this user, 0 to remove this user
######################## End Variables ########################
Clear-Host
$siteCount = 0
[system.reflection.assembly]::loadwithpartialname("Microsoft.SharePoint")
$site = new-object microsoft.sharepoint.spsite($siteURL)
$webApp = $site.webapplication
$allSites = $webApp.sites
foreach ($site in $allSites)
{
    
    $web = $site.openweb()
    $web.allusers.add($newSiteCollectionAdminLoginName, $newSiteCollectionAdminEmail, $newSiteCollectionAdminName, $newSiteCollectionAdminNotes)
    
    $user = $web.allUsers[$newSiteCollectionAdminLoginName]
    $user.IsSiteAdmin = $add
    $user.Update()
    $web.Dispose()
    $siteCount++
}
$site.dispose()
write-host "Updated" $siteCount "Site Collections."

Notes: Thanks to Google and Keith Richie for the assist.

SharePoint Survey Lists Can’t Export More Than First 100 Items

Long time no post eh? Last 6 months or so have been pretty humdrum but I’m glad to be back in an environment where I can flex my SharePoint muscles. :mrgreen: As of last week, I’m now working as one of two internal SharePoint keepers of the farm at Quest Software and couldn’t be happier! First interesting thing I’ve come across so far:

Scenario
If you have a SharePoint Survey List with over 100 list items/submissions, an export to Excel from the Actions menu will only yield the first 100 items of the list. This is a SharePoint 2007 farm by the way. Not sure if this is still applicable for 2010.

Why?
For some reason, the SPView.Paged property is set to false by default and thus only the default first 100 items of the query will be returned to Excel when the list is exported.

Solution
Now I know there are a bunch of solutions to this issue already posted out there but they all involved SharePoint Designer. I don’t like SharePoint Designer… especially when having to modify markup… Instead, I used SPManager 2007 to simply flip the property for the default view from false to true and that was it. Simple, clean and didn’t involve having to open up SharePoint Designer. 😉

Changing the Paged property for the default view of the Survey List.

SharePoint 2010 My Content Sites with User Domain\Network Login Site Titles

This one isn’t exactly a critical bug or issue but it’s definitely one that will get on the nerves of some of your more keen end-users. Often times, when you have a user navigate to their My Site prior to having the User Profile Import fully configured and run, you may notice that the users’ display names are nowhere to be found in the user interface. Instead you see that all mentions of their name are in the form of their network logins, aka domain\username.

You’ll notice that the image above shows that the display name does actually present itself properly (by modifying the user profile in Central Admin’s User Profile Service) next to the presence bubble but what’s up with the link under SharePoint Documents? Upon clicking on the link, we are taken to the user’s My Content site…

Aha! Isn’t that where the Site Title goes? That’s an easy fix. Now you can go to Site Actions > Site Settings > Title, description, and icon and that’s where you can update the Site Title. After you do that, the link in the first image above will properly show the user’s display name…or whatever you decide to save as the Site Title for the user’s My Content Site. Have fun!

A Strategy for Migrating Documents Out of Files Shares and Into SharePoint

A little while back I was working with a customer that had a seemingly simple question… “How do I help my users transition from saving their collaborative documents in unstructured file shares to a more structured environment in SharePoint?”

I thought to myself… “Well, before we let the masses start saving stuff to SharePoint, we should go over information architecture, taxonomy, governance, retention policies, quotas…. that’s quite a bit to chew… and will probably confuse the heck out of this guy I’m talking to…”

So I took a few seconds to mentally flip through some of the possible options:

1. Leave the file shares as is and trust that the users will slowly adopt SharePoint over time through evangelism. “Of course my users will start using SharePoint. They love adopting new technologies!”

2. Physically migrate/relocate all the file shares into SharePoint sites. “Of course IT would gladly help migrate files and our users wouldn’t even know the difference. We’ll just set their homepages to SharePoint!”

3. Make the file share read-only and force the users to save files to SharePoint. “Of course my users are troopers and wouldn’t mind going back and forth between 2 systems to get to their files!”

Did you sense the sarcasm?

Then I had an aha! moment and thought of something that I think is quite a feasible strategy for most organizations. This would also not put too much burden on any one part of the organization in particular. I kind of surprised myself for not thinking of this before. Enter the Page Viewer Web Part.

So here’s the game plan (Works with SharePoint 2010 as well by the way):

1. Start with a team or department that uses the file shares heavily and wants to believe that SharePoint will be good for them.

2. Identify their branch of the file share and make it read only.

3. Create one or more SharePoint sites for this department or team to collaborate on.

4. Embed a Page Viewer Web Part to the page and point it to the appropriately defined file share branch using the “Folder” destination option.

5. Now your users can use the SharePoint site to collaborate on all new stuff while at the same time be able to easily reference the “Archived” read-only documents very easily from the same exact site!

6. If anyone needs to modify a document that was previously stored on the file share, then they can open the document through the Page Viewer Web Part (works just like an Explorer Window) and then “Save As…” to the SharePoint site that they are a member of. Pretty cool huh?

Summary

So this kind of game plan will not only help give your users a smooth transition from file shares to SharePoint but it also gives them the ability to work with both technologies side-by-side to be able to more easily see the benefits of using SharePoint. It also helps IT by not requiring them to manage a file migration project that could potentially turn into mega-sized content databases of purgatory from day 1. Once your users become acclimated with SharePoint, you can then introduce features like required metadata, content types and the like, slowly warming your users up to a more formal information architecture plan.

How’s that sound? Have you tried a similar plan to any success?

How to Create Custom SharePoint 2010 Page Layouts using SharePoint Designer 2010

Becky Bertram has a nice post on how to create custom SharePoint 2010 Page Layouts via Visual Studio but my googling didn’t yield any walkthroughs on how to do this via SharePoint Designer. So let’s take a crack at this…

Scenario:

You’re working with the Enterprise Wiki Site Template and you don’t really like where the “Last modified…” information is located (above the content). You want to move that information to the bottom of the page.

Option 1: Modify the “EnterpriseWiki.aspx” Page Layout directly.

Option 2: Create a new Page Layout based on the original one and then modify that one.

We’ll go ahead and go with Option 2 since we don’t want to modify the out of the box template just in case we need it later on.

How To:

Step 1

Navigate to the top level site of the Site Collection > Site Actions > Site Settings > Master pages (Under the Galleries section). Then switch over to the Documents tab in the Ribbon and then click New > Page Layout.

Step 2

Select the Enterprise Wiki Page Content Type to associate with, give it a URL and Title. Note that there’s also a link on this page to create a new Content Type. You might be interested in doing this if you wanted to say, add more editing fields or metadata properties to the layout. For example if you wanted to add another Managed Metadata column to capture folksonomy aside from the already included “Wiki Categories” Managed Metadata column.

Step 3

SharePoint Designer time! Hover over your newly created Page Layout and “Edit in Microsoft SharePoint Designer.”

Step 4

Now you can choose to build your page manually by dragging your SharePoint Controls onto the page and laying them out as you’d like…

… Or you can copy and paste the OOB Enterprise Wiki Page Layout. I think I’ll do that. 🙂

Step 5

Alright, so you’ve copied the contents of the EnterpriseWiki.aspx Page Layout and now it’s time for some customizing. I found the control I want to move, so I’ll simply do a copy or cut/paste to the new spot.

Step 6

Check-in, publish, and approve the new Page Layout. Side note: I like to add the Check-In/Check-Out/Discard or Undo-Checkout buttons to all of my Office Applications’ Quick Access Toolbars for convenience.

Step 7

Almost there! Navigate to your publishing site, in this case the Enterprise Wiki Site, then go to Site Actions > Site Settings > Page layouts and site templates (Under Look and Feel). Here you’ll be able to make the new Page Layout available for use within the site.

Step 8

Go back to your site and edit the page that you’d like to change the layout for. On the Page tab of the Ribbon, click on Page Layout and select your custom Page Layout.

Et voila! You just created a custom Page Layout using SharePoint Designer 2010, re-arranged a SharePoint control and managed to plan for the future by not modifying the out of the box template. That was a really simple example but I hope it helped to give you some ideas on how else you can customize Page Layouts within SharePoint 2010!