netnerds.net

30Jan/120

SharePoint 2010: Stop Mixed Content Prompts on RSS Feeds Viewed on SSL-enabled Sites

I started on a new client site last week and my first task was immediately clear: I had to remove the Mixed Content Security prompt on the encrypted SharePoint intranet's front page. Firebug showed that my browser was making calls to feeds.feedburner.com even though the RSS feed was an entirely different address at cio.com.

My first instinct was to try to see if I could find the feed on HTTPS but https://feeds.cio.com did not respond to my request. Then I thought -- the only reason my browser would be making calls to feedburner would be for tracking. Although it's not a critical security threat or waste of bandwidth (the images were 1x1 gifs), I really didn't want feedburner to collect information about our intranet so I decided it was probably best to just remove all embedded HTML code in the description. After some research, it looked like XSL was likely the best way to do this.

I'm not an XSL pro, but I can get around. My initial search for sample code led me to a promising XSL function (which I can't find again to link :( ), but functions are only supported in XML namespace 2.0 and SharePoint uses 1.0 in the RSS Web Part. After reviewing the XSL provided in the SharePoint RSS Web Part, I decided to embed the strip-tags template within the GetSafeHTML template that is called for all the different types of feeds.

The following code removes all content within HTML tags (img, a, b, etc) so you're left with plain-text for the description. Clicking the title of the article to read more, still works, of course. To use this in your own RSS feed, edit the RSS Web Part, click on XSL Editor ... at the bottom of the web part panel, and replace the GetSafeHtml template with the following code:

        <xsl:template name="GetSafeHtml">
            <xsl:param name="Html"/>
            <xsl:choose>
                <xsl:when test="$rss_IsDesignMode = 'True'">
                    <xsl:call-template name="strip-tags">
                        <xsl:with-param name="text" select="$Html"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:call-template name="strip-tags">
                        <xsl:with-param name="text" select="rssaggwrt:MakeSafe($Html)"/>
                    </xsl:call-template>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>

        <xsl:template name="strip-tags">
            <xsl:param name="text"/>
            <xsl:choose>
                <xsl:when test="contains($text, '&lt;')">
                    <xsl:value-of select="substring-before($text, '&lt;')"/>
                    <xsl:call-template name="strip-tags">
                        <xsl:with-param name="text" select="substring-after($text, '&gt;')"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$text"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>

This code basically runs strip-tags on the final output of the description node, and aside from the strip-tags code itself, only adds 4 extra lines to your original XSL :)

Posted by: Chrissy   Filed under: Security, SharePoint No Comments
18Jan/120

SharePoint: Simple Sample Approach to Autonumber

Below is a formula for a quick and dirty autonumber for lists in SharePoint. There are a variety of ways to generate this field. Some solutions use workflow or external lists, but I prefer this method because of its simplicity.

Note: Because this solution depends on the record's ID column, it generates the autonumber *after* a record has been added, so if you need the autonumber to appear in the "New.." form, this solution is not for you. My project, which required the Request Number to be in "WR-0000" format, was fine with having it generated after the fact.

To create my Request Number, I created a new calculated column, named it Request Number and entered the following formula:

=CONCATENATE("WR-",REPT(0,4-LEN(ID+1)),ID+1)

This formula combines WR- with leading 0's and the ID number that's built into SharePoint lists. The output looks like so:

Thanks to my main SharePoint man, Trevor, for giving me this entire solution. You should start a blog, yo ;)

Posted by: Chrissy   Filed under: SharePoint No Comments
9Jan/120

SharePoint 2010: Use CSS to Auto-expand Calendar Entries

I looked all around the web for a simple solution to auto-expanding events in a monthly Calendar view. Unfortunately, this isn't something that's built-in to SharePoint, so it has to be done using workarounds. There were many solutions suggested on the web, though a majority required file system edits. Hey, no thanks!

Here's what worked for me: I created a new monthly view called Expanded View, set it to default, then fired up SharePoint designer and opened the new view for editing. Per the suggestion found on StackOverflow, I inserted a Content Editor Web Part right above the calendar, then used the split view to find the CEWP and between CDATA[[]], I added

<script type="text/javascript">

_spBodyOnLoadFunctionNames.push('WaitForCalendarToLoad');

function WaitForCalendarToLoad()
{  
    SP.UI.ApplicationPages.SummaryItemRenderer.prototype.$2u = function ()
    {
            ULSvSp: ;
            if (!this.$1A_1) this.$1A_1 = 100;
            return this.$1A_1
        }
}
</script>

 
This works best for calendars that mostly have all-day events. Because hourly events add extra lines to display the hours, this solution may not be the best one for those needing to show calendars packed with hourly events; it's not as streamlined. If anyone has some code for making auto-expanded hourly events look good, I'd love to see it.

Posted by: Chrissy   Filed under: SharePoint No Comments
20Sep/110

SharePoint + PowerShell: Batch Import Models, Create ECT’s, Set Permissions

Here is a script that will batch import Models, create associated ECT's, and set permissions for users and admins. Please modify the variables as needed.

# script from netnerds.net
# Make sure the namespace is the same as the namespace in your exported bdcm files
# My $siteURL happens to be the same as my $namespace. That may not be the case for you.
# Do this only on dev machines
# I'm running this on the SharePoint 2010 server so I made my servicecontext localhost
#
# What this does:
# Scans current directory for BDC Export(.bdcm) files
# Creates the External System/Lob and the External Content Types
# Creates External Lists from those ECT's
# Sets the proper permissions

$adminGroup = "DOMAIN\SharePoint Admins"
$userGroup = "DOMAIN\SharePoint Users"
$serviceContext = "http://localhost/"
$nameSpace = "http://sharepoint/"
$SiteUrl = $nameSpace

$bdc = Get-SPBusinessDataCatalogMetadataObject -BdcObjectType Catalog -ServiceContext $serviceContext
$pathtobdcmfiles = Get-Location
$importFiles = Get-Childitem -path $pathtobdcmfiles | Where {$_.extension -eq ".bdcm" -and $_.basename -ne "catalog"}

foreach ($file in $importFiles) {
Import-SPBusinessDataCatalogModel -Path $file.FullName -Identity $bdc -force -ModelsIncluded -PropertiesIncluded -PermissionsIncluded -Verbose

# THIS WHOLE CHUNK COMES FROM: http://gallery.technet.microsoft.com/scriptcenter/82437789-f294-4bcd-8210-a1ba0e081f82/
# IT MAKES THE EXTERNAL LISTS FROM THE MODELS. AWESOME.
    $Model = Get-SPBusinessDataCatalogMetadataObject -BdcObjectType Model -Name $file.baseName -ServiceContext $ServiceContext
    
    $ListUrl = "Lists/{0}"

        ForEach ($Entity in $Model.AllEntities)
        {
            $ns = $Entity.Namespace
            Write-Host "Entity Namespace : $ns"
            $name = $entity.Name
            Write-Host "Entity Name : $name"

            Write-Host "Looking for MethodInstance specific finder..."
            $sf = $Entity.MethodInstances | Where-Object{ $_.MethodInstanceType -eq [Microsoft.BusinessData.MetadataModel.MethodInstanceType]::SpecificFinder }
            If($sf -eq $null)
            {
                Write-Host "Skipping external list creation as Method Instance of Type SpecificFinder was not found in Entity : $name"
                break
            }
            else
            {
                Write-Host "Creating and Configuring SPListDataSource"
                $ds = New-Object -TypeName Microsoft.SharePoint.SPListDataSource
                $instanceName = [String]::Empty
                ForEach($instance in $entity.LobSystem.LobSystemInstances){$instanceName = $Instance.Name}

                $ds.SetProperty("LobSystemInstance", $instanceName)
                $ds.SetProperty("EntityNamespace", $ns)
                $ds.SetProperty("Entity", $name)
                $ds.SetProperty("SpecificFinder", $sf.Name)

                $site = $null
                Try
                {
                    $site = Get-SPSite -Identity $SiteUrl
                    $web = $site.OpenWeb()
                    $web.Lists.Add($Entity.Name, "", [String]::Format($ListUrl, $Entity.Name), $ds);
                }
                Finally
                {
                    $web.Dispose()
                    $site.Dispose()      
                }
            }
        }
#END CHUNK
}

$claimAdmin = New-SPClaimsPrincipal -Identity $adminGroup -IdentityType WindowsSamAccountName
$claimUsers = New-SPClaimsPrincipal -Identity $userGroup -IdentityType WindowsSamAccountName

Grant-SPBusinessDataCatalogMetadataObject -Identity $bdc -Principal $claimAdmin -Right "Execute,SetPermissions,Edit,SelectableInClients"
Grant-SPBusinessDataCatalogMetadataObject -Identity $bdc -Principal $claimUsers -Right "Execute,Edit,SelectableInClients"

Copy-SPBusinessDataCatalogAclToChildren -MetadataObject $bdc

#Ignore this, its what I wish my namespace was...
#$nameSpace = "http://$env:USERDNSDOMAIN/".toLower()

 
As they say in the SuSE Linux motd: Have a lot of fun!

Posted by: Chrissy   Filed under: PowerShell, SharePoint No Comments
20Sep/110

SharePoint + PowerShell: Import All BDCM Files in a Directory and Set Permissions

In order to invest as little effort as possible creating and recreating and recreating my External Content Types on my development server, I exported the ECT's then deleted External Content Types, External Systems, and BDC Models. I saved them all in a directory on the SharePoint server, then I placed ImportALLbdcms.ps1 in the same directory and ran it.

$adminGroup = "DOMAIN\SharePoint Admins" #for bdc permissions
$userGroup = "Domain\SharePoint Users"

$serviceContext = "http://localhost/"
$nameSpace = "http://$env:USERDNSDOMAIN/".toLower()

$bdc = Get-SPBusinessDataCatalogMetadataObject -BdcObjectType Catalog -ServiceContext $serviceContext
$pathtobdcmfiles = Get-Location
$importFiles = Get-Childitem -path $pathtobdcmfiles | Where {$_.extension -eq ".bdcm" -and $_.basename -ne "catalog"}

foreach ($file in $importFiles) {
Import-SPBusinessDataCatalogModel -Path $file.FullName -Identity $bdc -force -ModelsIncluded -PropertiesIncluded -PermissionsIncluded -Verbose
write-host $file.fullname
}

$claimAdmin = New-SPClaimsPrincipal -Identity $adminGroup -IdentityType WindowsSamAccountName
$claimUsers = New-SPClaimsPrincipal -Identity $userGrup -IdentityType WindowsSamAccountName

Grant-SPBusinessDataCatalogMetadataObject -Identity $bdc -Principal $claimAdmin -Right "Execute,SetPermissions,Edit,SelectableInClients"
Grant-SPBusinessDataCatalogMetadataObject -Identity $bdc -Principal $claimUsers -Right "Execute,Edit,SelectableInClients"

Copy-SPBusinessDataCatalogAclToChildren -MetadataObject $bdc

 
Sorry for a junky intro and no conclusion, I've been working 19 hour straight and I'm about exhausted. Anyway, HTHY!

Posted by: Chrissy   Filed under: PowerShell, SharePoint No Comments
20Sep/110

SharePoint 2010 + PowerShell: Export/Import Calendar Items

Today has been miserable. All I wanted to do was import a SharePoint 2007 calendar into SharePoint 2010 to use as sample data for a prototype site. I was caaarraaazzy enough to believe it would be a simple import/export, but no. There's no such thing. Maybe I could use Outlook calendaring or idq files as a medium? No. So I decided to do the good ol "Save this as a template and include content" workaround. Then I followed Tom's awesome instructions on how to hack the SharePoint 2007 template to get SharePoint 2010 to use it.

That worked! It imported over 3000 calendar items, but then the library itself was broken. When I'd make modifications (such as checking the box to make the calendar a resource), it would throw unknown errors IN MY FACE. So I decided I would just create a fresh SharePoint 2010 Calendar, then import the data using a PowerShell script which would just copy list item data from one calendar to another. This should work for almost any list in SharePoint. Here's how I did it, y'all:

First, create a new Calendar using the regular Calendar template. Then, run this script after changing $siteURL, $sourceListName and $destListName to suit your environment.

$siteURL = "http://sharepoint/"
$sourceListName = "Corporate Calendar"
$destListName= "Prototype Calendar"

$site = Get-SPSite $siteURL
$web = $site.RootWeb
$sourceList = $web.Lists[$sourceListName]
$destList = $web.Lists[$destListName]

$columns = $sourceList.Fields;
$sourceItems = $sourceList.GetItems();

  foreach($sourceItem in $sourceItems) {
    $newItem = $destList.AddItem();
    foreach($column in $columns) {
      if ($column.ReadOnlyField -eq $False -and $column.InternalName -ne "Attachments") {
        $newItem[$($column.InternalName)] = $sourceItem[$($column.InternalName)];
      }
    }
    $newItem.Update();
  }

$web.dispose()
$site.dispose()

 
Note that I didn't include support for attachments at this time; 8 hours after starting on this Copy Calendar Events journey began, I'm just too beat! Perhaps I'll add it later.

Update: My awesome friend Trevor shared this link with me: Create random or demo SharePoint Content with PowerShell Cool.

Posted by: Chrissy   Filed under: PowerShell, SharePoint No Comments
8Sep/110

SharePoint + Powershell: Remove Hold, Record Declaration on All Documents in a Library

In order to delete a Records Library or Records Center, all holds and records in a library must be removed and the Holds and Processing timer job must be run. If this criteria is not met, the "Delete this Document Library" will not be an option in the library settings.

Here is how you can do this programmatically. Note that I did not include the deletion of documents or the document library itself.

$siteURL = $args[0]
$libraryName= $args[1]

$site = Get-SPSite $siteURL
$web = $site.RootWeb
$library = $web.Lists[$libraryName]
$records = $library.Items[0]

# First, remove all holds from all items in the Library
$holdsList = $web.Lists["Holds"]
$holds = $holdsList.Items[0]
[Microsoft.Office.RecordsManagement.Holds.Hold]::RemoveHold($records,$holds,"Holds have been removed.")

# Next, undeclare all items as records. Ps. BulkUndeclareItemsAsRecords is useless.
foreach ($record in $records) {
      [Microsoft.Office.RecordsManagement.RecordsRepository.Records]::UndeclareItemAsRecord($item)
}

Start-SPTimerJob HoldProcessing

$web.Dispose()
$site.Dispose()

 
Thanks to anavijai for the sample Hold removing code.

Posted by: Chrissy   Filed under: PowerShell, SharePoint No Comments
7Sep/110

SharePoint 2010 + PowerShell: Delete Site Content Type

So first, you have to delete all of the content type's children or you'll receive the error "Error: content type in use." Once the dependencies are gone, you can delete the custom site content type itself.

$sitename = $args[0]
$contentType = $args[1]

$web = Get-SPWeb $sitename
$ct = $web.ContentTypes[$contentType]

if ($ct) {
$ctusage = [Microsoft.SharePoint.SPContentTypeUsage]::GetUsages($ct)
      foreach ($ctuse in $ctusage) {
        $list = $web.GetList($ctuse.Url)
        $contentTypeCollection = $list.ContentTypes;
        $contentTypeCollection.Delete($contentTypeCollection[$ContentType].Id);
        Write-host "Deleted $contentType content type from $ctuse.Url"
        }
$ct.Delete()
Write-host "Deleted $contentType from site."

} else { Write-host "Nothing to delete." }

$web.Dispose()

 
Viola.

Posted by: Chrissy   Filed under: PowerShell, SharePoint No Comments
6Sep/110

SharePoint 2010 + PowerShell: View Dependencies of Content Types (aka Content Type Usage)

Earlier today, I created some test Content Types while playing around with the fancy new Content Organizer. I ran into a few errors, though, and subsequently wanted to delete the dummy content types to start over. I was unable to, however, because the content types were still in use. By what? I didn't know.

Too bad SharePoint doesn't have a feature similar to SQL Server Management Studio's "View Dependencies." Till it does.. here is a PowerShell script that does the trick.

$sitename= "http://sharepoint"
$contentType = "My Custom Content Type"

$web = Get-SPWeb $sitename
$ct = $web.AvailableContentTypes[$contentType]
$ctusage = [Microsoft.SharePoint.SPContentTypeUsage]::GetUsages($ct)
     foreach ($ctuse in $ctusage) {
          $ctuse.Url
     }

$web.Dispose()

 
Looking for content type usage within subsites? A more thorough script can be found at StackExchange.

Posted by: Chrissy   Filed under: PowerShell, SharePoint No Comments
6Sep/113

PowerShell Workaround: “Replicate Directory Changes” Permissions in AD Required for SharePoint 2010 Profile Syncs

According to the SharePoint 2010 Communities FAQ, Microsoft is burdening SharePoint 2010 Administrators with new requirements to obtain Active Directory accounts with "Replicate Directory Changes" permissions because...

In order to interrogate AD about “what has changed since time xyz”, we need the replicate-directory-changes permissions on partitions being synchronized, for example the domain partition being synchronized.These permissions are needed in particular to be able to read data within the deleted objects container of the partition. Standard users do not have permissions to read the content of this container, and we cannot simply grant rights over that container to the synchronization account.

While this may be the cleanest way to go about things, it's an unfortunate requirement for SharePoint admins within organizations which understandably frown upon making such concessions for service accounts, especially when the change wasn't required for SharePoint 2007 farms.

Microsoft's recommended architecture is to to use the published information from a centralized farm but some SharePoint administrators are on their own and do not have that luxury. I encountered this issue in a lab environment and was able to circumvent it by writing a quick and dirty script to add new users from AD and sync all SharePoint users with AD. This script only addresses one site collection and does not drill down to its subsites. Run it as often as you would run a regular sync.

# SharePoint <-> AD Group Sync
# netnerds.net
#
# What it does:
# Uses LDAP to get a list of users from a specific group in AD (in this case the "All Finance Users" group)
# Enables "Allow Unsafe Updates" for the given Site Collection then returns it to its previous state.
# Adds AD Users (but ignores nested AD groups) to the specified SharePoint Group
# Syncs all Site Users with Active Directory. This includes name changes, etc.

# 1,2,3 Go
$ldappath = "LDAP://CN=All Finance Users,OU=Finance,DC=icanhas,DC=net"
$spgroupname = "Icanhas Intranet Members"
$sitename= "http://sharepoint"
$domain = $env:USERDOMAIN

$adgroup = [ADSI]($ldappath)
$spsite = SPSite($sitename)
$rootweb = $spsite.rootweb
$spgroup = $rootweb.Groups[$spgroupname]

$allowUnsafeUpdates = $spsite.AllowUnsafeUpdates
$spsite.allowUnsafeUpdates = 1

foreach ($memberDN in $adgroup.member) {

$member = [ADSI]("LDAP://$memberDN")  | where {$_.properties.objectcategory -match "CN=Person"}
     if ($member){
               $username = $domain + "\" + $member.sAMAccountName
               $spsiteuser = $rootweb.EnsureUser($username)

               $spUser = $rootweb.AllUsers[$spsiteuser]
               $spgroup.AddUser($spsiteuser)
               }
}

$spsite.allowUnsafeUpdates = $allowUnsafeUpdates

$rootweb.dispose()
$spsite.dispose()

Get-SPUser –Web $sitename| Set-SPUser –SyncFromAD

 
Note the AD Sync on the last line. Surprisingly enough, PowerShell's SyncFromAD works without explicitly allowing Replicate Directory Changes. To test this, first I tried a domain account with regular privileges then I went a step further and denied Replicate Directory Changes for that account and it still worked.

You may want to make additional changes such as: deleting users that no longer exist in AD, and replicating all changes to subsites.

Ahh, just good enough :)

Posted by: Chrissy   Filed under: Active Directory, PowerShell, SharePoint 3 Comments