Home > Blogs > Eric Shupps
​The SharePoint Cowboy


April 03
International SharePoint Conference 2012

In just a few weeks, from April 23rd through the 25th, many of the best and brightest SharePoint people from around the world will be gathering in London, England for what is widely considered to be one of the best events in the world – the International SharePoint Conference. This time around, assuming no natural disasters get in the way, attendees will experience something entirely new. Unlike most conference formats, which provide an array of disconnected material to pick and choose from, at this event we will be delivering complete, end-to-end solutions in the core IT Pro, Information Worker and Developer tracks. This will be a true "deep dive" experience, allowing attendees to immerse themselves in their chosen topic for the entire conference.

For IT Pros and Developers, it all starts on day one with an empty set of servers and a blank Visual Studio project. Over the course of three days, both teams will define requirements, architect a solution, build and configure all the components, and deliver the final product. The audience in each core track will become part of the team by participating in discussions, troubleshooting issues, and advocating solutions. The teams will communicate with each other in real-time throughout the event, coordinating on solution delivery and monitoring progress. Each session will build on the previous one, following a structured, step-by-step approach with consistent demonstrations and walk-throughs. This format allows for deeper discussion of core best practices and insight into real-world scenarios that can only be exposed when building actual solutions.

For those attendees whose interests lie in other areas, such as power users, project managers, business analysts, and designers, there will be three additional tracks – more than 40 sessions – covering every technical aspect of SharePoint from design and branding to records management and My Sites. But that's not all. Executives, stakeholders, managers, and CxO's have a Business track all to themselves to discuss topics related to user adoption, information architecture, social networking, and governance. And that still doesn't cover all the available content, as a separate track exists just for case studies and customer solutions.

If you can only attend one event this year, no matter where in the world you live and work, you need to be at the International SharePoint Conference. Registration is still open but I promise you it will sell out so don't wait – make your plans now to be in London April 23 – 25, 2012.

See you there!

March 20
How to Manually Disable Claims Authentication in SharePoint 2010

In a previous post I shared some thoughts regarding changes to authentication providers in SharePoint 2010. As I worked through the issue of removing Claims/FBA and reverting to NTLM I discovered a number of issues that manifested themselves in strange ways. The first problem I encountered was the inability for a Farm account to make changes to the Authentication Providers settings in Central Administration. The System Account couldn't even view the dialog – each attempt resulted in a 403 error. This was bad news as a lot of things happen behind the scenes when changing authentication settings in this dialog – not the least of which is propagation of changes to all the web servers. This meant I would have to undo all of the Claims settings manually and repeat them on each server. Not my idea of a fun afternoon.

 

I began by changing the authentication provider for my content web applications back to Classic mode using Powershell:

 

$webApp = Get-SPWebApplication http://site
$webApp.UseClaimsAuthentication = 0;
$webApp.Update()
$webApp.ProvisionGlobally()

 

Next, I removed the highlighted membership provider from inclusion in the people picker in all web applications, including Central Administration:

 

<PeoplePickerWildcards>

<clear />

<add key="AspNetSqlMembershipProvider" value="%" />

<add key="MyCustomMembershipProvider" value="%" />

</PeoplePickerWildcards>

 

The next part of the process involves the removal of role and membership provider information, along with any supporting connection strings, from the various web.config files. This has to be done for the Central Administration web app, the Security Token Service web app, and all content web apps that use Claims on each web server. The process is a bit different for each type of web application, so it's not just a simple matter of find and delete.


Starting with the Security Token Service, locate the web.config file in the C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\WebServices\SecurityToken directory and remove all the entries added to enable Claims in the first place. Typically, these are located after the <system.net> entries in a new <system.web> node:

 

<system.web>

<membership defaultProvider="i">

<providers>

<add name="i" type="Microsoft.SharePoint.Administration.Claims.SPClaimsAuthMembershipProvider, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add name="MyCustomMembershipProvider" connectionStringName="MyDBConnection" passwordAttemptWindow="2" maxInvalidPasswordAttempts="100" enablePasswordRetrieval="true" enablePasswordReset="true" requiresQuestionAndAnswer="false" applicationName="/" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="8" requiresUniqueEmail="true" passwordFormat="Clear" description="Stores and Retrieves membership data from SQL Server" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

</providers>

</membership>

<roleManager defaultProvider="c" enabled="true" cacheRolesInCookie="false">

<providers>

<add name="c" type="Microsoft.SharePoint.Administration.Claims.SPClaimsAuthRoleProvider, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add name="MyCustomRoleProvider" connectionStringName="MyDBConnection" applicationName="/" description="Stores and retrieves roles from SQL Server" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

</providers>

</roleManager>

</system.web>

 

Be sure to also remove any connection strings:

 

<connectionStrings>

<add name="MyDBConnection" connectionString="user id=[User/Password];Persist Security Info=False;Initial Catalog=[Database Name];Data Source=[Server]" providerName="System.Data.SqlClient" />

</connectionStrings>

 

In the Central Administration web application config, the membership and role provider nodes have a slightly different syntax from the Security Token and content web applications but this doesn't have any bearing on the removal of Claims from the environment. Replace them with the out of the box configuration settings (basically an empty set of nodes), so this:

 

<roleManager defaultProvider="AspNetWindowsTokenRoleProvider" enabled="true" cacheRolesInCookie="false">

<providers>

<add name="MyCustomRoleProvider" connectionStringName="MyDBConnection" applicationName="/" description="Stores and retrieves roles from SQL Server" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

</providers>

</roleManager>

<membership defaultProvider=" MyCustomMembershipProvider ">

<providers>

<add name="MyCustomMembershipProvider" connectionStringName="MyDBConnection" passwordAttemptWindow="2" maxInvalidPasswordAttempts="100" enablePasswordRetrieval="true" enablePasswordReset="true" requiresQuestionAndAnswer="false" applicationName="/" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="8" requiresUniqueEmail="true" passwordFormat="Clear" description="Stores and Retrieves membership data from SQL Server" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

</providers>

</membership>

 

Becomes this:

 

<roleManager>

<providers>

</providers>

</roleManager>

<membership>

<providers>

</providers>

</membership>

 

We can now move on to the content web applications and remove the membership and role information, along with any connection strings, from those web.config files. Remember that this has to be done on every web server in the farm (including any application servers which have the Microsoft SharePoint Foundation Web Application role). Remove the following nodes in their entirety (if you wish, you may replace the <roleManager> and <membership> sections with empty nodes as was done with Central Administration):

 

<membership defaultProvider="i">

<providers>

<add name="i" type="Microsoft.SharePoint.Administration.Claims.SPClaimsAuthMembershipProvider, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add name="MyCustomMembershipProvider" connectionStringName="MyDBConnection" passwordAttemptWindow="2" maxInvalidPasswordAttempts="100" enablePasswordRetrieval="true" enablePasswordReset="true" requiresQuestionAndAnswer="false" applicationName="/" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="8" requiresUniqueEmail="true" passwordFormat="Clear" description="Stores and Retrieves membership data from SQL Server" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

</providers>

</membership>

<roleManager defaultProvider="c" enabled="true" cacheRolesInCookie="false">

<providers>

<add name="c" type="Microsoft.SharePoint.Administration.Claims.SPClaimsAuthRoleProvider, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add name="MyCustomRoleProvider" connectionStringName="MyDBConnection" applicationName="/" description="Stores and retrieves roles from SQL Server" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

</providers>

</roleManager>

 

<connectionStrings>

<add name="MyDBConnection" connectionString="user id=[User/Password];Persist Security Info=False;Initial Catalog=LearningCounts;Data Source=[Server]" providerName="System.Data.SqlClient" />

</connectionStrings>

 

After the membership and role settings have been removed, the manner in which users are prompted for authentication must be changed. In Claims mode, a login form is presented to the user but in Classic mode we want the NTLM prompt instead. This setting is controlled by the following lines in the content web application web.config files:

 

<authentication mode="Forms">

<forms loginUrl="/_login/default.aspx" />

</authentication>

 

Changing it to the following will re-enable the NTLM prompt:

 

<authentication mode="Windows" />

 

The next step is to remove all Claims accounts from the web application in User Policy settings and replace them with NTLM accounts:

 

User Policy

 

At this point, Classic authentication should be working on all web applications. As mentioned previously, it will be necessary to remove and replace user accounts in SharePoint groups on each site collection and it may also be necessary to change entries in the Site Owners group. After getting this far, it would appear that Claims has been disabled – users should get an NTLM prompt when attempting to navigate to site pages and be able to login successfully. Alas, our work is not yet done. While the Claims authentication settings have been changed, there are a number of artifacts left behind in the web.config files which enable the system to process Claims identities. These artifacts won't prevent users from logging in using NTLM but they will cause various other issues, one of which is the strange behavior that occurs when attempting to login as a different user in the same browser session.

 

SharePoint provides a link in the Personal Actions menu entitled "Sign in as a different user". This link points to "/_layouts/closeconnection.aspx" with a query string argument of "?loginasanotheruser=true" and a second argument which points back to the referring page. If everything is working properly, the current user will be logged out and an NTLM prompt displayed for new user credentials. If things aren't in such good shape, the user will be redirected to the "/_layouts/accessdenied.aspx" page and receive a generic SharePoint error, which is exactly what happened to me. Digging through the ULS log files, I found the matching correlation ID which revealed the underlying error:

 

System.ArgumentException: Exception of type 'System.ArgumentException' was thrown. Parameter name: identity

at Microsoft.SharePoint.Administration.Claims.SPClaimProviderManager.GetUserIdentifierEncodedClaim(IIdentity identity)

at Microsoft.SharePoint.Administration.Claims.SPClaimExtensionMethods.GetDisplayName(IClaimsIdentity claimsIdentity)

at Microsoft.SharePoint.ApplicationPages.AccessDeniedPage.OnLoad(EventArgs e)

at System.Web.UI.Control.LoadRecursive()

at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)    26b85aba-6b2a-4b96-93de-91aa18db98d3

 

The error indicates that the system is still trying to process user identities in Claims mode instead of Classic mode. To rectify this situation, it is necessary to remove all the Claims-related entries in the content web application web.config files. Your mileage may vary, but a compare on a clean non-Claims config file and the ones I had been working with revealed the following entries which needed to be cleaned up:

 

<httpModules>

<add name="FederatedAuthentication" type="Microsoft.SharePoint.IdentityModel.SPFederationAuthenticationModule, Microsoft.SharePoint.IdentityModel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add name="SessionAuthentication" type="Microsoft.SharePoint.IdentityModel.SPSessionAuthenticationModule, Microsoft.SharePoint.IdentityModel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add name="SPWindowsClaimsAuthentication" type="Microsoft.SharePoint.IdentityModel.SPWindowsClaimsAuthenticationHttpModule, Microsoft.SharePoint.IdentityModel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

</httpModules>

 

<assemblies>

<add assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

<add assembly="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add assembly="Microsoft.SharePoint.Search, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add assembly="Microsoft.Office.Access.Server.UI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add assembly="Microsoft.Office.Server.Search, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add assembly="Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

<add assembly="Microsoft.SharePoint.IdentityModel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

</assemblies>

 

<modules runAllManagedModulesForAllRequests="true">

<remove name="AnonymousIdentification" />

<remove name="FileAuthorization" />

<remove name="Profile" />

<remove name="WebDAVModule" />

<remove name="Session" />

<add name="SPRequestModule" preCondition="integratedMode" type="Microsoft.SharePoint.ApplicationRuntime.SPRequestModule, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

<add name="SharePoint14Module" preCondition="integratedMode" />

<add name="StateServiceModule" type="Microsoft.Office.Server.Administration.StateModule, Microsoft.Office.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add name="RSRedirectModule" type="Microsoft.ReportingServices.SharePoint.Soap.RSRedirectModule, RSSharePointSoapProxy, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" />

<add name="PublishingHttpModule" type="Microsoft.SharePoint.Publishing.PublishingHttpModule, Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add name="FederatedAuthentication" type="Microsoft.SharePoint.IdentityModel.SPFederationAuthenticationModule, Microsoft.SharePoint.IdentityModel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add name="SessionAuthentication" type="Microsoft.SharePoint.IdentityModel.SPSessionAuthenticationModule, Microsoft.SharePoint.IdentityModel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<add name="SPWindowsClaimsAuthentication" type="Microsoft.SharePoint.IdentityModel.SPWindowsClaimsAuthenticationHttpModule, Microsoft.SharePoint.IdentityModel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

</modules>

 

<microsoft.identityModel>

<service saveBootstrapTokens="true">

<audienceUris />

<issuerNameRegistry type="Microsoft.SharePoint.IdentityModel.SPPassiveIssuerNameRegistry, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

<securityTokenHandlers>

<clear />

<add type="Microsoft.IdentityModel.Tokens.X509SecurityTokenHandler, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

<add type="Microsoft.SharePoint.IdentityModel.SPSaml11SecurityTokenHandler, Microsoft.SharePoint.IdentityModel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">

<samlSecurityTokenRequirement>

<nameClaimType value="http://schemas.microsoft.com/sharepoint/2009/08/claims/userid" />

</samlSecurityTokenRequirement>

</add>

<add type="Microsoft.SharePoint.IdentityModel.SPTokenCache, Microsoft.SharePoint.IdentityModel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

</securityTokenHandlers>

<federatedAuthentication>

<wsFederation passiveRedirectEnabled="false" issuer="https://none" realm="https://none" />

<cookieHandler mode="Custom" path="/">

<customCookieHandler type="Microsoft.SharePoint.IdentityModel.SPChunkedCookieHandler, Microsoft.SharePoint.IdentityModel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

</cookieHandler>

</federatedAuthentication>

</service>

</microsoft.identityModel>

 

Once these are removed, all Claims settings should be gone and Classic mode fully restored. Note that the above samples are all based on the AspNetMembershipProvider using SQL as the data source. Use of LDAP or other identity providers may result in a different set of configuration entries. Alter the procedure as necessary for your environment, remembering to compare a pre-Claims config file (if you have one) with a post-Claims file to identify all of the changes. Don't forget to check the Authorization settings in the IIS manager as well just to be sure everything is working properly.

 

 

 

 

 

 

 

March 10
The SharePoint Shepherd’s Nine Keys to SharePoint Success

Friend and fellow MVP Rob Bogue has just released a new DVD entitled "Nine Keys to SharePoint Success". It's part of his ongoing series of engagement and adoption materials which complement his SharePoint Shepherd books and tutorials for end users. Rob and I work together on a number of projects, including our upcoming Introduction and Professional SharePoint development seminars at the SharePoint Connections conference in Las Vegas at the end of March, 2012, and I've seen the high level of dedication he has for producing quality educational material (such as the SharePoint 2010 ECM Implementers course we produced for Microsoft).

If your organization is planning an enterprise deployment in the near future, don't miss out on the SharePoint Shepherd resources. They'll save you a ton of time and money and help ensure your project is successful. On the other hand, if you already have SharePoint in place but are struggling with user adoption, the tips and tricks Rob provides can help turn things around and get users excited about the potential of SharePoint in your organization. This is one set of tools you don't want missing from your SharePoint toolbox.

 

 

March 10
Access Denied Errors After Changing Authentication Provider Settings

Sometimes, we SharePointers take things for granted because we work with the platform on a daily basis and intuitively understand how things are supposed to function. Accidental site administrators, power users, and the poor folks in IT who've had SharePoint dumped in their lap through no fault of their own, can easily get frustrated by all the ins and outs when something they think should work just doesn't.

Case in point. Some time back I implemented a small 2010 environment for a client. At the time, they chose to go with a mixture of NTLM and Claims authentication due to the nature of their workforce, many of whom were seasonal employees without AD accounts. So we did the FBA dance and they went on about their business. After using the portal for some time it became apparent that none of their FBA users were ever accessing SharePoint and the NTLM users were complaining loudly about the out of the box login page (which forces users to select their authentication type in an extended, multiple zone web app configuration). So they made what seemed to be a perfectly normal request – turn off Claims and revert to NTLM for all users.

In simple scenarios, this isn't difficult – a little powershell to change the auth provider for the default zone and some web.config modifications to pull out all the role and membership settings (along with a couple of extra IISRESET operations 'cause you always forget about that pesky web.config in the SecurityToken directory and the people picker wildcard settings). This is not a good time to disconnect from the VPN and go catch up on all those CSI episodes you've missed because it's right about then that all hell breaks loose – nobody can log into the portal anymore. It's a big helping of accessdenied.aspx for everyone except the system account.

This is where the occasional site admins get frustrated. They can login as the farm account or system user but regular users are out in the cold. They spent a bunch of time adding all those people into SharePoint groups so they shouldn't have to do all that again, right? Eh, not quite. If the web application was originally created in Claims mode it's very likely that a bunch of user accounts were added from a membership database (usually an ASP.NET membership provider but it could be any other type of Claims-compliant provider). A quick glance into the site permission reveals the culprit – the system accounts are in a domain format (DOMAIN\Username) while the user accounts are in that oh-so-lovely Claims format (something like "i:0#.f|members|joesmith"). Even if they were domain accounts to begin with, you'll still see them listed in claims format, such as "i:0#.w|domain\joesmith". In other words, your users don't exist anymore as far as SharePoint is concerned because "domain\joesmith" is NOT the same person as "i:0#.w|domain\joesmith".

The fix is simple (if tedious) and is immediately apparent to any seasoned SharePoint admin – recreate group membership with the proper domain accounts after the claims provider has been changed. But that kind of thinking doesn't come naturally to people who don't eat, breathe and live SharePoint. To them, Joe Smith is Joe Smith and he certainly appeared as such when they selected his user account in the people picker. A bunch of mumbo-jumbo in front of the name (which sometimes isn't apparent until you look at the user's profile page) doesn't mean anything to them. It's easy enough to explain but that's the point – we have to explain it. Just like the list = library conversation and the site collection/site/web relationship, these sort of things take a little extra mental processing to conceptualize – it's not at all intuitive.

The moral of the story is obvious – validate changes before you implement them. But it's also a cautionary tale on trying to think like a user and not like an expert. A few extra minutes spent explaining the wily ways of SharePoint can go a long way to empowering users to look after themselves. And that's always a good thing (at least, I think it is, right?).

 

 

December 05
Hiding Toolbars in the SharePoint 2010 Chart Web Part

SharePoint Server 2010 ships with a nifty Chart web part that displays visual data from a number of sources – SharePoint lists, BDC, Excel services, etc. It's a handy control and one that was sorely missing from the 2007 version. It provides a number of chart options, including pies, lines, bars, cones, scatters, etc. in both 2D and 3D. Neat…but (and there's always a 'but')…it has one very annoying characteristic that drives site administrators crazy. When you drop it onto the page, it displays a toolbar with links for "Data & Appearance" and "Advanced Properties" to everyone with more than basic read permissions, like so:



We certainly don't want everyone to see that – too much temptation to click on those links and blow up our pretty little graphs. Well, ok, should be easy enough to turn that off, right? Wrong.

Somebody, somewhere, forget to include the ubiquitous hide toolbar switch that's on most other out of the box web parts. While trying to figure out a workaround for this nice little undocumented feature, I came across a lot of links to this blog post by Nick Grattan in which he suggests editing the page in SharePoint Designer and changing the web part properties manually in the markup. That's all well and good but anyone who has ever heard me speak at a conference knows that I am not exactly the world's biggest fan of using SPD to edit pages (that may be understating it a bit, sort of like saying the Pope is a little bit Catholic or Texas gets a bit warm in the summertime). So what to do?

Instead of hobbling performance by saving my page markup in the content db via SharePoint Destroyer, I instead opted for a little bit of JavaScript trickery to solve the problem. Turns out the chart web parts renders the toolbar content in a very predictable pattern:



 

The < span > tag in which the toolbar resides is followed by an < img > tag that has a link to a specific page used to render the chart preview image. The < span > also follows an < input > control that is the first child of the parent < div >. That should be easy enough to find in the DOM and just unique enough to insure we don't turn off any other web part toolbars we might want. So, the following script, when added to the page in a hidden Content Editor web part, will hide those pesky toolbars by doing just a little bit of DOM walking:

< script type="text/javascript" >
var arr = document.documentElement.getElementsByTagName("img");
for (var i = 0; i < arr.length; i++)
{
    var imgSrc = arr[i].src;
    if (imgSrc.indexOf("ChartPreviewImage") != -1)
    {
        var parent = arr[i].parentNode;
        parent.childNodes[1].setAttribute("style","display:none");
    }
}
< / script >

If you prefer to use JQuery for messing around with the page DOM that works just fine as well (and probably with half as much code). Unfortunately, it means we have to add the content editor and script to every page our chart web parts are on, but it's better than saving the entire page back to the content DB in Designer.

Problem solved.

 

October 26
SharePoint Global Navigation Sometimes Ignores Target Audience Settings

I recently came across a curious scenario when using target audiences with SharePoint 2010 global navigation. I began with a common enough requirement – create a static menu structure in which certain links would be visible only to members of specific SharePoint security groups (no Active Directory in this particular environment). The basic hierarchy looked something like the following:

Root Node [Members]
-- Publishing Page 1 [Custom SP Group 1]
-- Publishing Page 2 [Custom SP Group 1]
-- Layouts Page [Custom SP Group 2]
-- External Link [Custom SP Group 3]

In reality it was a bit more complex than that but not by much. At first, it all worked as expected. But then I noticed that the Layouts Page and External Link were showing up no matter what group a user was in. That seemed odd but not catastrophic – anonymous users still couldn't see the root node so all it needed was a little tweaking to get the groups right. That's where the fun began. The audience setting for each node mapped to only one SP group and there was no cross-membership in any of the groups. In theory, members of Group 3 shouldn't see anything but the last link; in practice, they were presented with the Layouts Page node and the External Link node but NOT the Publishing Page nodes. No matter what I did, the Layouts and External nodes always showed up.

About this time I realized that the Root node would have to changed as it wouldn't work for users on IOS (iPhone, iPad) – Safari on the touch platform won't allow the drop-down menu to expand like clicking on the arrow with a mouse would. It always navigates the user to the root node link. No problem, I had already dealt with this on the other root nodes, so I simply removed the link from the Root node, left the target audience set to Members, and inserted a child node (thus, when the root node was touched or clicked, it would simply expand the child nodes instead of navigating anywhere). The menu structure then became:

Root Node [Members] (empty link field)
-- Home Page [Members]
-- Publishing Page 1 [Custom SP Group 1]
-- Publishing Page 2 [Custom SP Group 1]
-- Layouts Page [Custom SP Group 2]
-- External Link [Custom SP Group 3]

Oops, now I really had a problem. As soon as I removed the hyperlink value from the root node, the root element, layouts node and external node all became visible to anonymous users! Mucho bad mojo, there – no way would the customer allow non-authenticated users to see a root node that should only be available to authenticated users. I put a random value into the hyperlink field to try and hide to root menu item but nothing worked until I put in a reference to an actual SharePoint object (web, page, list item, etc.) - so long as that object wasn't an application page in the /_layouts directory. Ok, serious head-scratching time. This didn't make any sense at all whatsoever. Why should SharePoint care whether or not the node links to an SP object or www.anyoldwebsite.com?

My immediate suspicion was that somehow the external and layouts links were falling through the security trimming logic in some way. However, Audiences don't work the same way as actual security groups. The only check that's performed on an audience is whether or not the user is a member of the specified group not whether that group actually has permissions on the target object – that's a completely different process. Had it been a security trimming issue, anonymous users wouldn't have been able to see the Layouts Page link as the lockdown feature was enabled on the site. Something else was going on behind the scenes but what could it be.

So, being the curious developer type that I am, I fired up ILSpy, turned on the Developer Dashboard and went to work tracking down this little gem of undocumented functionality. After stumbling along several blind alleys, following umpteen dead-end stub methods (really, do we need a bunch of classes that do nothing but return false every time they're called?), and twisting my brain around convoluted inline nested IF statements, I finally discovered the answer. Turns out that there's a nice little method in the Publishing namespace called CreateObjectFromID that is responsible for taking a reference to a cached node object in a site map data source and returning an object of a specific node type. Unfortunately, there are only three allowable node types – Web, Page or ListItem (with the default in the switch statement being ListItem). This means that a link must point to something that can be converted to one of these three types. Nice – not even a catch to allow for generic or non-SP objects.

As if that little coding blunder wasn't enough, the convoluted logic in the GetNavigationChildren method of PortalSiteMapNode allows a null object (which is the result of not matching one of the three allowable types) to be displayed – the exact opposite of how I would think it should work. This may not be security trimming but it's awfully darn close, at least in spirit – I would have fallen back to hiding anything I didn't know how to deal with (null) instead of allowing it to pass through. So, the net result is this brilliant bit of logic:

  1. If the node includes a link to something that cannot be converted to a Web, Page or ListItem, audience targeting will be ignored.
  2. If the root node does not satisfy #1 then the root and the child nodes will be hidden.
  3. If a child node satisfies #1 it will be displayed only IF the root node also satisfies #1.

The only way to circumvent this functionality is to make sure that all static links in the navigation settings point to valid SharePoint objects that are either a Web (site collection root or subsite), Page (publishing, web part, wiki, etc.) or a list item if you intend to use target audiences to restrict the display of individual nodes.

And there you have it. Another afternoon wasted in the dark back alleys of SharePoint. Aren't you glad I beat my head against the wall so you don't have to?

 

 

October 17
SharePoint Saturday UK 2011 and SharePoint Exchange Forum 2011

SharePoint Conference has come and gone but the conference season isn't over quite yet – there are still plenty of opportunities for great SharePoint content and education available across the pond. First up is SharePoint Saturday UK, being held in Nottingham on November 12th. Nottingham is our new base of operations in the UK and BinaryWave is a Gold sponsor of the event, which is being held right around the corner from our offices at Nottingham Science Park. There will be plenty of good speakers and informative sessions – if you were unable to travel to Anaheim be sure not to miss this one. I've got one session on the developer track covering the Visual Studio 2010 SharePoint tools (with plenty of plugs for the Developer Community Kit – if you want to know how the team built some of the cool integration pieces in CKS:DEV be sure not to miss it):

Customizing the SharePoint Packaging and Deployment Process in Visual Studio 2010

One of the most exciting new features in Visual Studio 2010 is the built-in support for building custom SharePoint 2010 solutions. In this presentation we will explore the various methods available for automated solution packaging and deployment in a development environment. We will also examine multiple ways to package solutions to meet different design and deployment objectives. Finally, we will take a deep dive into the process of extending the out of the box features of Visual Studio to create customized deployment configurations, including SharePoint commands, deployment steps, and Visual Studio Extensibility (VSIX) packages.

The following day (after recovering from the requisite SharePint) I will be jetting off to Stockholm, Sweden for SharePoint Exchange Forum 2011. This annual event is one of my favorites. Stockholm is a beautiful city and Goran Hussman, Office 365 MVP, puts on a great conference. Held at the Clarion Hotel Sodermalm November 14th – 15th, it's two days of intense SharePoint knowledge and networking. Having spent a great deal of time working on the ECM Implementers Course for Microsoft with Rob Bogue, Darrin Bishop, Steve Curran, Ben Robb, and Spence Harbar (who will also be speaking at SEF), I'll be breaking away from my normal Visual Studio and performance optimization topics to deliver a couple of sessions covering Enterprise Content Management in SharePoint 2010:

Taming Information Chaos: Metadata, Taxonomy and Information Architecture Fundamentals for Enterprise Content Management

Implementation of a proper information architecture based on well-defined taxonomies and structured metadata can make information easier to find and dramatically increase productivity; however, getting it right the first time is a challenging task. In this session you will learn the fundamentals of structured information management in SharePoint 2010 and receive guidance from real-world scenarios that can be applied immediately within your organization. Topics include information architecture, taxonomy design, managed metadata, creation and publication of enterprise content types, use of document sets, and much more.

Building Enterprise Records Management Solutions for SharePoint 2010

SharePoint 2010 introduces many new content management features that can be applied to build both document and records management solutions. In this session, we'll examine these features in detail and explore ways to apply them to solve traditional records management problems, such as creating hierarchical file plans, using metadata to drive content routing and making e-Discovery more accessible for records managers and end users.

[Not to worry, I'll still have some cool code demos and useful developer tidbits J]

So put 'em on your calendar and be sure to stop by and say "Howdy"!

 

 

October 06
SharePoint Conference 2011 Wrap Up

Another SharePoint Conference has come and gone. With more than 7,000 attendees, we pushed the facility and surrounding entertainment venues to their limits (let's just say Disney wasn't quite prepared for our kind of crowd). There was a ton of great content, with lots of it being real-world advice from the trenches – and even a few sessions that didn't mention the words "cloud", "on prem" or "365" (which no doubt violated some rule or other).

For those who attended either of my sessions, the decks and related materials can be found here (development) and here (performance). Please note that you'll need the Test or Ultimate versions of Visual Studio 2010 to run the load tests from the performance session (and a very beefy machine if you plan to do host > guest stress testing like I did). If you were at the conference but didn't make it into the sessions, the video is available from your MySPC sessions page (session numbers SPC214 and SPC373). Thanks to everyone who attended and posted all the great messages on Twitter. If you have any questions I was unable to answer on either topic, please add a comment to this post and I'll get back to you as soon as I've recovered a bit from the whole experience.

Next year we head back to Las Vegas to do it all over again (I may need a liver transplant to survive that one). See you there!

 

September 29
Custom Field Elements and the SourceID Attribute

Programmatically creating site columns and content types in SharePoint 2010 is a relatively straightforward process, especially with the new project item framework in Visual Studio 2010 and enhancements like CKS:DEV. Create an element file to define the columns, create another to define the content type and specify which columns to include, deploy it and whistle Dixie 'til the cows come home (or at least until you have to update it, which is an entirely different herd of cows). For a basic text field the declarative markup is pretty simple:

<Field ID="{BF2D02C3-8EF3-44A7-8950-13DE6541AF01}" Name="M_ProjectName" DisplayName="Project Name" Description="Project Name is case sensitive" Group="Custom Site Columns" Type="Text" DisplaceOnUpgrade="TRUE" Required="FALSE" MaxLength="255" />

And including it in the content type is also an easy bank shot (just don't forget to include the brackets in the ID value):

<FieldRef ID="{BF2D02C3-8EF3-44A7-8950-13DE6541AF01}" Name="M_ProjectName" Description="" DisplayName="Project Name" Required="FALSE" howInDisplayForm="TRUE" ShowInEditForm="TRUE" ShowInNewForm="TRUE" />

But wait. There's something missing here that's not completely obvious at first. While (or whilst, depending upon which side of the pond you're from) the site column definition contains a value for the Description attribute, if you deploy the code as is the description text will never show up when the field is rendered on the form. This is due, in whole or in part, to the fact that the Field element is missing the SourceID attribute. The documentation states that this is an optional field and I suppose it is – the field will work without it. But omit it and the description text will not be displayed. Changing the markup to the following will fix the issue:

<Field ID="{BF2D02C3-8EF3-44A7-8950-13DE6541AF01}" Name="M_ProjectName" DisplayName="Project Name" Description="Project Name is case sensitive" Group="Custom Site Columns" Type="Text" DisplaceOnUpgrade="TRUE" Required="FALSE" MaxLength="255" SourceID=" http://schemas.microsoft.com/sharepoint/v3" />

Bingo! The description text shows up right where it's supposed to. This might also affect other functionality that I haven't stumbled across yet. As a general rule, I always include the SourceID attribute when creating site columns declaratively. I have not tried the same thing using a programmatic approach – it may be inferred under that model without the need to specifically declare it. If anyone has tested it that way I'd like to hear about it.

Happy SharePointing!

 

 

September 21
SharePoint Conference 2011

SharePoint Conference 2011 is almost upon us. Anaheim, California is *the* place to be the first week of October for all things SharePoint. There will be more than 200 sessions covering every aspect of the technology stack from top to bottom delivered by top SharePoint experts from around the world.

I'll be presenting two sessions at the conference, one for developers and one for administrators and architects:

SPC214 – Introduction to SharePoint Development
10/3/2011 10:30 AM
You want to start building solutions on SharePoint that require some code but you dont know where to start? Come and learn how to leverage your existing .NET skills to begin creating scalable, secure, and high-performance SharePoint 2010 applications. Take a quick tour through the creation of web parts, discover how to extend SharePoint to remote applications using the client object model, get an overview of user managed solutions using the sandbox and receive a wealth of tips and tricks from real-world scenarios. In addition, you will learn how Visual Studio 2010 makes SharePoint developoment faster, easier, and more efficient than ever before.

SPC373 – Performance Tuning SharePoint 2010
10/5/2011 9:00 AM
There are many facets of SharePoint that can be tuned to dramatically increase the overall performance of your farm. In this session, you will learn real-world performance optimization techniques for SharePoint 2010, including planning and sizing for anticipated usage, handling large numbers of concurrent users, implementing caching and compression, configuring IIS, tuning SQL server, managing large lists and resource throttling, optimizing custom code, reducing page payload and performing load and scalability testing.

Be sure to drop by and say hello – I'll have some groovy giveaways during both sessions. As usual, there will be plenty of extra-curricular activities happening throughout the week. A few are listed below (follow the hashtag #spc11 throughout the week for breaking info):

SharePint
AvePoint RED Party
Idera ESPN Zone
Yammer
Axceler OctoberFiesta

We'll see you in Anaheim!

 


 

1 - 10Next

 
 



 
Twitter Counter for @eshupps
 
 

Copyright © 2011 BinaryWave, Inc. All rights reserved.