Tuesday, December 8, 2015

Cannot create SharePoint 2010 site collections

You heard that right... Fixing the ability to create site collections within SharePoint.  Recently, I was tasked to get 3 environments in sync (DEV/TEST/PROD).  Part of this task was to set up User Profiles and My Sites with Self-Service Site Creation enabled.

Setting up DEV and TEST went rather smoothly.  At that point, I should have known there was a hidden whammie that would ruin a whole weekend of mine. I'm so naive sometimes...

Cannot Create Site Collections Error

This madness came to the forefront after setting up My Sites.  One of the last steps is to create a site collection with a template of My Sites Host.  While trying to create this site collection, I get a stack trace in my face:

Failed to load receiver assembly "Microsoft.Office.Server.Powerpoint.Web.MOSSHost, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" for feature "PowerPointServer" (ID: 5709298b-1876-4686-b257-f101a923f58d).: System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.Office.Server.Powerpoint.Web.MOSSHost, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' or one of its dependencies. The system cannot find the file specified. File name: 'Microsoft.Office.Server.Powerpoint.Web.MOSSHost, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) at System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) at System.Reflection.Assembly.Load(String assemblyString) at Microsoft.SharePoint.Administration.SPFeatureDefinition.get_ReceiverObject() WRN: Assembly binding logging is turned OFF. To enable assembly bind failure logging, set the registry value [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) to 1. Note: There is some performance penalty associated with assembly bind failure logging. To turn this feature off, remove the registry value [HKLM\Software\Microsoft\Fusion!EnableLog]. &ErrorCorrelationId=c01e4e3a-1a39-401d-9b67-5a38961a7bd9

Clearly there is errant code running that isn't in the other two environments.  Reading the error, it's easy to discern there's a problem with that assembly.  Looking into the GAC, I noticed that this .dll didn't even exist.


I said this to myself... 

It's early in the outage window, so I jumped into triage mode.  Googling that error, there is not much out there at all.  That was disheartening and a punch in the gut.  There's nothing like fixing an error that didn't come up in DEV or TEST and nothing of value on the net to fix it.

Looking more into the error, it's easy to see there's a feature that is running this code:

Failed to load receiver assembly "Microsoft.Office.Server.Powerpoint.Web.MOSSHost, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" for feature "PowerPointServer"

Based on that feature being available to the farm, I was able to discern that Office Web Apps has been installed.  But why isn't this working and why is the .dll missing from the GAC?  Reading over the uninstallation steps for Office Web Apps, it became very apparent that this component was previously installed and then removed.  For some reason, the features are still activated within the farm. GREAT!!!  There goes my weekend...

Find and Remove the Office Web Apps Feature

Before last weekend, I hadn't much knowledge of installing or configuring Office Web Apps, so needless to say, I treaded lightly.  First up was to find the feature that I thought was the culprit.  Using this PowerShell command, I was able to peruse the features within the farm:

Get-SPFeature -Limit All | Out-File -FilePath "$pwd\spfeatures.log" 

Armed with this, it's easy to find the OfficeWebApps feature.  But that feature is scoped to a Site!

OfficeWebApps    0c504a5c-bcea-4376-b05e-cbca5ced7b4f     Site   

That cannot be the problem since I don't even have a site collection! So, I tried to create a site collection in a different web app, thinking maybe it was something with the My Site stuff.  NO BUENO.  Same stack trace in my face...  Directly below this feature in the log is another feature that looked very promising:

OfficeWebAppsStapling  fb67f269-fd1d-4f9a-af0b-50f5755e19d7  Farm   

The name of this feature and since this is scoped to the farm, I got the warm and fuzzies.  Since Office Web Apps wasn't in Programs and Features and the .dll was missing from the GAC, I decided that I could safely uninstall this feature from the farm altogether.

Uninstall-SPFeature -Identity "fb67f269-fd1d-4f9a-af0b-50f5755e19d7"

Once this was uninstalled, I hit paydirt.  I was able to create site collections again!  

As an aside, this farm hadn't created a new site collection in well over 2 years.  Check the created dates on your own farm sometime.

Get-SPSite -Limit All | % { $_.RootWeb.Created }

Beware of using -Limit All, it may cause a degradation in performance.

Another Office Web Apps Error

It was only a small victory in the lengthy weekend battle.  Now that the site collection is created, it's only natural to enjoy the fruits of you labor.  Upon visiting the site, I'm thrown and error with a Correlation ID.  TO THE ULS!!!

Error initializing Safe control - Assembly:Microsoft.Office.SharePoint.ClientExtensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c TypeName: Microsoft.Office.SharePoint.ClientExtensions.Publishing.TakeListOfflineRibbonControl Error: Could not load type 'Microsoft.Office.SharePoint.ClientExtensions.Publishing.TakeListOfflineRibbonControl' from assembly 'Microsoft.Office.SharePoint.ClientExtensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c'.

What we have here is ANOTHER .dll missing from the GAC.  If you've been following along, you're probably yelling the answer out.  Uninstall the OTHER Office Web Apps feature.  I originally chose not too, since I was treading lightly. :)

Uninstall-SPFeature -Identity "0c504a5c-bcea-4376-b05e-cbca5ced7b4f"

I'm now able to visit the My Sites site collection!  Now it's time to click on My Content and create my own personal site collection. No whammies, no whammies, no whammies!!!

UGH!!! I can't get that lucky... Another stack trace in my face.


My Sites Error

After clicking My Content, the site collection was being provisioned and started the best Mr. Burns impression I can.  Not so fast...










There has been an error creating the personal site. Contact your site administrator for more information.

Unfortunately, this doesn't come with a Correlation ID, but the ULS will tell all.  Cracking open ULS Viewer and watching the ULS log live, I found........  You guessed it ANOTHER missing .dll that's related to Office Web Apps.  Are you starting to find a theme within this post?

The following exception occurred: Failed to load receiver assembly "Microsoft.Office.Excel.Server.HostExtensionWac, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" for feature "ExcelServerEdit" (ID: b3da33d0-5e51-4694-99ce-705a3ac80dc5).: System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.Office.Excel.Server.HostExtensionWac, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' or one of its dependencies. The system cannot find the file specified.  File name: 'Microsoft.Office.Excel.Server.HostExtensionWac, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c'    
 at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)    
 at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)    
 at System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)    
 at System.Reflection.Assembly.Load(String assemblyString)    
 at Microsoft.SharePoint.Administration.SPFeatureDefinition.get_ReceiverObject()    WRN: Assembly binding logging is turned OFF.  To enable assembly bind failure logging, set the registry value [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) to 1.  Note: There is some performance penalty associated with assembly bind failure logging.  To turn this feature off, remove the registry value [HKLM\Software\Microsoft\Fusion!EnableLog].

This one made me pause a little bit more, because it's not clear if this feature is a default feature of the farm.  Treading lightly, I opted to simply disable this one and not uninstall completely, just in case.

Disable-SPFeature –identity "ExcelServerEdit" -URL http://webapp

Wrap up

I'm not sure how or why Office Web Apps was removed, but the features that come with this component were still activated and made for an interesting weekend, nonetheless.  I was able to find some great resources out there for My Sites troubleshooting.  If you ever run into issues with this, I'd definitely take a look at these two links:
As for Office Web Apps, I found an update that lists what I would assume to be most of the .dlls that can be found within.  It helped me along the way:

I hope this helps someone save some precious weekend hours...

Tuesday, April 15, 2014

Large Lists, BCS, Excel REST, JSOM, CSVs and Office 365 OH MY!

An interesting challenge came across my desk last week:
"How can we import a CSV into Office 365 and use that data to tag other items within the site.  The CSV currently has ~7600 rows and is expected to grow.  We'll also need to re-import the CSV on an ad-hoc basis when the data changes."
The last part of that was the real issue.  There was simply no easy way of doing that.  After trying a few things, falling flat on my face, I'm prepared to share my exploration into the different options.

Each have their own merits and pitfalls.  This post will examine each and try to shed some light on the pitfalls I've found using each of these.

BCS

Wiring up an ECT on Office 365 can be a little finicky. I initially had some issues due to 2 BDC Models that were created for the same ECT.  After calling in the eagle-eyed BCS guru, +Fabian Williams, I got squared away.

Immediately after that, I could tell that I was not going to be able to use BCS for what I needed.  In Office 365, there is a hard limit of 2000 items that can be retrieved.  Ironically, the error message that is displayed is not a supported cmdlet for Office 365.

Office 365 BCS Throttle Error
Adding insult to injury, I decided to run a simple test using JSOM.  I wanted to clarify if JSOM would provide me PagingInfo with a BCS List.  Using the code below, you'd expect line 68 to produce a value instead of nothing at all.


Since BCS will not work and due to the limitation of the API, I had to look for another solution.

Custom List

Using a custom list initially worked great.  I'm able to use JSOM, query the list for 5000 items per trip to the server, AND get PagingInfo.  Using the code below works great for this scenario.


Importing the Excel into Office 365 is relatively straightforward and will work for most needs.  The file I used had about ~7600 rows of data.  After importing the file, I noticed the Server Resource Quota was tapped.

Office 365 Server Utilization
So using this approach has 2 problems.  I will not be able to do a mass import again of my data (the list already exists) and the Server Resource Quota points are exhausted.

Excel REST

This seemed like a cool way of getting around the limitations above, so I dove in to find out if this will work for my needs.  After all, I'm allowed to have a *lot* of rows in Excel and I'll be able to easily update the file, since it's in a document library.  Using the code below, I ran into a showstopper though.


There is a hard limit in the API set at 500 rows.  That would be painfully slow to get all of the items or even worse; a user may try to use the form control while this is still querying for data.

Excel REST API - 500 row limit
So that leaves us with our raw data that was exported from SQL and given to us to use.

CSV

Updating the CSV will be easy, since it will be stored in a document library.  Now all we need to be able to do is make sense of it.  Using the code below, I'm able to parse the CSV and create an array of objects that I need to pass off to another library.  Also note the use of localStorage.  This is a nice way to cache the data and prevent the retrieval/processing of the data client-side on every page load.  If the CSV is updated, simply clear the browser cache and you'll get the latest and greatest.

Conclusion

All approaches have their merits and pitfalls... BCS and PagingInfo, I'm looking at you!  If the ad-hoc mass-import wasn't needed, then using list driven data would have been my choice.  If I used that approach, I would have still used localStorage though.  It makes sense to cache the processed data since it'll not change very much.  Since my solution works client-side, I'll have to take into consideration the amount of time this takes to render.  I'm getting good performance out of the CSV approach, so I'm going to stick with it for the time being.

Friday, January 10, 2014

Fire Workflows with Initiation Parameters using #SPServices

Firing Workflows using Javascript, I've never had to pass in Initiation Parameters.  This post takes a look at how to do just that and provides some code that will allow easy use of Workflows in Javascript.

Setup Workflow

The workflow has to be set for Manual starting, otherwise this will not work.  Also, to pass in parameters to the workflow, you'll need to have Initiation Variable(s) within the workflow. I've only fiddled with Number and Single Line of Text fields, so if you use other column types, feel free to share your experience.

Workflow Initiation Parameters

This workflow is simply logging the variables out to the Workflow History.  Easy peazy...

Workflow Parameters

Looking over the documentation for SPServices and StartWorkflow, I found some examples that were a great starting point.  After fiddling a bit with 1 field, I decided to test this a little more.  I created a column with spaces in the Name field *gasp*.  I only did this to see how to handle this programmatically, so a word to the wise: Friends don't let friends create columns with spaces...

Reading over the examples, if you have multiple parameters, it says you have to change from passing the column name to this weird pattern: 

<Data><Parameter1>" + parameter1 + "</Parameter1><Parameter2>" + parameter2 + "</Parameter2></Data>

I've found this to not work at all for me at all [sad_panda]... Back to the drawing board, I guess. Then an idea came to me.  Since I'm targeting a column with a space in it, I tried what normally happens to spaces in Static Names: _x0020_.  So, I tried this next:

<Data><TextField>Will it blend?</TextField><With_x0020_Spaces>42</With_x0020_Spaces></Data>

However, this didn't work either!  Very curious to find a resolution, I set out to find out why this didn't work...  Using SPD (SharePoint Designer), you are able to view the files generated by the workflow. Opening up the XML file as text, you can clearly see that SPD removed the space in the Static Name.

Workflow wfconfig.xml
Within this file, all of the Initiation Parameters are visible and it's now easy to tell what's exactly going on.

Workflow Parameter Names
For all of this to work while using multiple parameters, you have to use the exact Static Name as defined in the XML.  The workflow parameters below work just fine for me now.

<Data><TextField>Will it blend?</TextField><WithSpaces>42</WithSpaces></Data>

Code to Fire the Workflow

This function will handle the pain of getting a workflow to fire.  All you need to know is the correct URL, the workflow name, and the workflow parameters ( if any ).

*** Update *** I took my original idea and made it more or less a plug-in for SPServices. Add this function to the SPServices source and it'll work without any issues. Original function:

Friday, November 22, 2013

Use Server Variables In XSLT To Find The Root Site

There’s no way to tell what the site’s Root Web URL will be, nor the protocol used on any given site, so here’s a quick rundown of how to get the Root Web URL using XSLT:

Variable Setup

This example requires these two server variables.
  • SERVER_NAME
  • HTTPS

ServerVariable - SERVER_NAME
Here’s a screenshot of what it looks like in SPD.  With these in place we'll be able to determine the Root Web URL dynamically, so this could be used on any site and the code will know exactly what the URL of the root web is.





XSLT Code

I've commented the key parts to this simple example, so if you want to just take those pieces you can.  Doing this through the GUI, it adds two <ParameterBinding />’s to your code.  Later in the code, you’ll see two variables that are used:
  • httpProtocol
  • sitePath
These variables are put together to form the URL of the Root Web.

<WebPartPages:DataFormWebPart runat="server" Description="" PartOrder="2" HelpLink="" AllowRemove="True" IsVisible="True" AllowHide="True" UseSQLDataSourcePaging="True" ExportControlledProperties="True" DataSourceID="" Title="" ViewFlag="8" NoDefaultStyle="TRUE" AllowConnect="True" FrameState="Normal" PageSize="-1" PartImageLarge="" AsyncRefresh="True" ExportMode="All" Dir="Default" DetailLink="" ShowWithSampleData="True" ListId="" ListName="" FrameType="None" PartImageSmall="" IsIncluded="True" SuppressWebPartChrome="False" AllowEdit="True" ManualRefresh="False" ChromeType="None" AutoRefresh="False" AutoRefreshInterval="60" AllowMinimize="True" ViewContentTypeId="" InitialAsyncDataFetch="False" MissingAssembly="Cannot import this Web Part." HelpMode="Modeless" ID="" ConnectionID="00000000-0000-0000-0000-000000000000" AllowZoneChange="True" IsIncludedFilter="" __MarkupType="vsattributemarkup" __WebPartId="" __AllowXSLTEditing="true" WebPart="true" Height="" Width=""><ParameterBindings>
                        <ParameterBinding Name="dvt_apos" Location="Postback;Connection"/>
                        <ParameterBinding Name="ManualRefresh" Location="WPProperty[ManualRefresh]"/>
                        <ParameterBinding Name="UserID" Location="CAMLVariable" DefaultValue="CurrentUserName"/>
                        <ParameterBinding Name="Today" Location="CAMLVariable" DefaultValue="CurrentDate"/>
                       
                        <!-- Set these two variables up within your DVWP/XLV Web Part -->
                        <ParameterBinding Name="HTTPS" Location="ServerVariable(HTTPS)" DefaultValue=""/>
                        <ParameterBinding Name="SERVER_NAME" Location="ServerVariable(SERVER_NAME)" DefaultValue=""/>
                    </ParameterBindings>
<DataFields>
@myField,myField;</DataFields>
<Xsl>
    <xsl:stylesheet xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" version="1.0" exclude-result-prefixes="xsl msxsl ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal">
        <xsl:output method="html" indent="no"/>
        <xsl:decimal-format NaN=""/>
        <xsl:param name="dvt_apos">&apos;</xsl:param>
        <xsl:param name="ManualRefresh"></xsl:param>
        <xsl:param name="webUrl" />
        <xsl:param name="HTTPS" />
        <xsl:param name="SERVER_NAME" />
        <xsl:variable name="dvt_1_automode">0</xsl:variable>
        <xsl:template match="/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:SharePoint="Microsoft.SharePoint.WebControls">
            <xsl:call-template name="dvt_1"/>
        </xsl:template>
        <xsl:template name="dvt_1">                       <xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row"/>
            <xsl:variable name="dvt_RowCount" select="count($Rows)"/>
            <xsl:variable name="IsEmpty" select="$dvt_RowCount = 0" />
            <xsl:variable name="dvt_IsEmpty" select="$dvt_RowCount = 0"/>
            <!-- Use this variable to determine the protocol used by the site -->
            <xsl:variable name="httpProtocol">
                <xsl:choose>
                    <xsl:when test="normalize-space($HTTPS) = 'on'">https://</xsl:when>
                    <xsl:otherwise>http://</xsl:otherwise>
                </xsl:choose>
            </xsl:variable>
            <!-- This variable concats all of these variables together -->
            <!-- Example sitePath output: http://iOnline247.me/ -->
            <xsl:variable name="sitePath">
                <xsl:value-of select="string(normalize-space(concat(concat(normalize-space($httpProtocol), normalize-space($SERVER_NAME)), '/')))" />
            </xsl:variable>   
                   
                    <!-- {...SNIP...} -->
        </xsl:template>
    </xsl:stylesheet>
</Xsl>

The end result is a nice URL.  Here’s a sample output: http://iOnline247.me/
I'm not sure why the protocol isn't straightforward to get.  I may have missed something obvious, but I didn't see any other way of determining the protocol.  If there's an easier way, feel free to let me know.

Monday, November 18, 2013

Add SublimeText to Windows Context Menu

Since I've made the switch, I've loved SublimeText.  It is by far superior to Notepad++.  There are some small things that I'm still working out, but I'm sure with time they'll go away.  One of those things was the context menu was missing.  I'm sure this was due to me using the portable installer, but it's a must for me to have!  Doing a simple search on the web, I came across this gist that does exactly what I needed:

Here's the code in its entirety... You just never know with the web. ;)

@echo off
SET st2Path=C:\Program Files\Sublime Text 2\sublime_text.exe
 
rem add it for all file types
@reg add "HKEY_CLASSES_ROOT\*\shell\Open with Sublime Text 2"         /t REG_SZ /v "" /d "Open with Sublime Text 2"   /f
@reg add "HKEY_CLASSES_ROOT\*\shell\Open with Sublime Text 2"         /t REG_EXPAND_SZ /v "Icon" /d "%st2Path%,0" /f
@reg add "HKEY_CLASSES_ROOT\*\shell\Open with Sublime Text 2\command" /t REG_SZ /v "" /d "%st2Path% \"%%1\"" /f
 
rem add it for folders
@reg add "HKEY_CLASSES_ROOT\Folder\shell\Open with Sublime Text 2"         /t REG_SZ /v "" /d "Open with Sublime Text 2"   /f
@reg add "HKEY_CLASSES_ROOT\Folder\shell\Open with Sublime Text 2"         /t REG_EXPAND_SZ /v "Icon" /d "%st2Path%,0" /f
@reg add "HKEY_CLASSES_ROOT\Folder\shell\Open with Sublime Text 2\command" /t REG_SZ /v "" /d "%st2Path% \"%%1\"" /f
pause