Thursday, February 28, 2013

Image Handling in SharePoint 2013 and IE9/10 - GIF Image in Document Library not Showed

During a migration of a content database from a SharePoint 2010 to a brand new SharePoint 2013, everything seems to work fine except rendering of some GIF image were used in some pages.

Browsing the site with IE10 these images were not rendered. Using another browser these images were rendered correclty.

So I try to browse the "old" SharePoint 2010 with IE10 and images were rendered correctly.

SharePoint 2010 was served by Windows Server 2008R2 while SharePoint 2013 is served by Windows Server 2012 and IIS8.

The point is that when you create a new webapplication in SharePoint 2013 the IIS Virtual Server is created with two specific (local) HTTP Response Header.

One of this header (only IE9 and above versions is able to interprete this) is responsible of the behavior described:

X-Content-Type-Options: nosniff

Removing this custom header from every front-end webserver resolve the issues.

More info about this header can be read on MSDN at:

MIME-Handling Change: X-Content-Type-Options: nosniff

Tuesday, February 19, 2013

SharePoint 2010 - Edit SharePoint Group raise Access Denied Exception

If you experience "Access Denied" exception editing SharePoint Group Property, probably this article can give you some suggestion.

SharePoint page that give you ability to edit group properties in SharePoint 2010 runs with the permissions of the user who invokes it.
The operations that were successful were those related to the Update of the characteristics of the group, and immediately after it was returned "Access Denied".
By reverse eng the assembly from which SharePoint editgrp.aspx inherits (Microsoft.SharePoint.ApplicationPages.dll), what is clear is that, following the method "sPGroup.Update ();" there's a call to a base method called "UpdateAdditionalProperties".
This method essentially updates the description of the group on the hidden list "UserInfo" (properties siteUserInfoList of the Site Collection) that keeps track of all information related to Users and Groups.

This method returns an Access Denied exception (from tracelog: SPRequest Unknown error occurred. More information: 0x80070005).
User is also redirected to AccessDenied.aspx page.

This is the code from SharePoint dll.

// Microsoft.SharePoint.ApplicationPages.EditGroup
protected override int DoOperation()
{
 base.Trace.Write("Editing Group", "");
 if (this.RadAllowRequestToJoinLeaveYes.Checked && !this.RadAutoAcceptRequestYes.Checked && !SPUtility.IsEmailServerSet(base.Web))
 {
  throw new SPException(SPResource.GetString("RequestJoinLeaveGroupEmailServerSettingsInvalid", new object[0]));
 }
 SPGroup sPGroup = base.Web.SiteGroups[this.hdnGrpName.Value];
 if (sPGroup != null)
 {
  if (sPGroup.Name != this.m_strGrpName)
  {
   sPGroup.Name = this.m_strGrpName;
  }
  SPMember memberFromPeoplePicker = base.GetMemberFromPeoplePicker();
  sPGroup.Owner = memberFromPeoplePicker;
  sPGroup.AllowRequestToJoinLeave = this.RadAllowRequestToJoinLeaveYes.Checked;
  sPGroup.AutoAcceptRequestToJoinLeave = this.RadAutoAcceptRequestYes.Checked;
  sPGroup.OnlyAllowMembersViewMembership = this.RadViewMembershipGroupMembers.Checked;
  sPGroup.AllowMembersEditMembership = this.RadEditMembershipGroupMembers.Checked;
  if (this.RadAllowRequestToJoinLeaveYes.Checked && !this.RadAutoAcceptRequestYes.Checked)
  {
   sPGroup.RequestToJoinLeaveEmailSetting = this.TxtRequestEmail.Text.Trim();
  }
  sPGroup.ClearDistributionGroupErrorMessage();
  if (base.DistributionGroupsAvailable)
  {
   ErrorPage.SetupInfoForMessageContainingLink(this.Context, "GroupDistributionGroupErrorMessageContainingLink", "GroupSettingsPage", base.Web.GetServerRelativeUrlFromUrl("_layouts/editgrp.aspx?Group=" + SPHttpUtility.UrlKeyValueEncode(sPGroup.Name)));
   if (this.CreateDLTrue.Checked)
   {
    if (string.IsNullOrEmpty(sPGroup.DistributionGroupAlias))
    {
     sPGroup.CreateDistributionGroup(this.DLAlias.Text);
    }
    else
    {
     if (this.DLAlias.Text != sPGroup.DistributionGroupAlias)
     {
      sPGroup.RenameDistributionGroup(this.DLAlias.Text);
     }
    }
    if (this.ArchiveListExisting.Checked)
    {
     List<SPList> list = new List<SPList>();
     foreach (ListItem listItem in this.ArchiveListExistingLists.Items)
     {
      if (listItem.Selected)
      {
       list.Add(base.Web.Lists[new Guid(listItem.Value)]);
      }
     }
     sPGroup.SetDistributionGroupArchives(new ReadOnlyCollection<SPList>(list), base.Web);
    }
    else
    {
     if (this.ArchiveListNew.Checked)
     {
      base.CreateNewArchiveList(sPGroup, base.Web, this.ArchiveListNewName.Text);
     }
     else
     {
      sPGroup.SetDistributionGroupArchives(new ReadOnlyCollection<SPList>(new List<SPList>()), base.Web);
     }
    }
   }
   else
   {
    if (this.CreateDLFalse.Checked && !string.IsNullOrEmpty(sPGroup.DistributionGroupAlias))
    {
     sPGroup.DeleteDistributionGroup();
    }
   }
   ErrorPage.CleanupInfoForMessageContainingLink(this.Context);
  }
  sPGroup.Update();
 }
 base.UpdateAdditionalProperties(sPGroup.ID);
 return 0;
}


*** This one is the method that raise "Access Denied" exception and the consequent UI redirect to the Access Denied Exception. ***

// Microsoft.SharePoint.ApplicationPages.CBaseNewGroup
protected void UpdateAdditionalProperties(int groupId)
{
 SPList siteUserInfoList = base.Web.SiteUserInfoList;
 SPListItem itemById = siteUserInfoList.GetItemById(groupId);
 string text = this.txtGrpDescription.Text;
 itemById["Notes"] = text;
 int num = 1;
 while (true)
 {
  string text2 = base.Request.QueryString["FieldName" + num.ToString(CultureInfo.InvariantCulture)];
  if (string.IsNullOrEmpty(text2))
  {
   break;
  }
  string value = base.Request.QueryString["FieldValue" + num.ToString(CultureInfo.InvariantCulture)];
  itemById[text2] = value;
  num++;
 }
 itemById.Update();
}


At this point I've checked with powershell the property "EffectiveBasePermissions" over UserInfo list, comparing the result with another Test Farm (which is working fine).
On Test Farm this collection was valued at FullMask. On Production Farm this collection was listing a number of base permissions.

The missing permission is the one related to the Update User Information.
From central admin, at WebApplication level, invoking "User Permissions" dialog, what I've discovered is that there was no flag on the basic permission "Edit Personal User Information".
Settings this flag=true on the webapplication affected will result in no more "Access Denied" redirect .



This bug/behavior has been verified on SharePoint 2010 SP1 + October 2012 CU.

Monday, February 18, 2013

Windows 2102 NLB and Loop Back Adapter

If you need to configure NLB on two or more IIS web server on Windows 2012, what you need is probably install "Microsoft LoopBack Adapter" network driver.
On this version of Windows, the correct name of this adapter is "Microsoft KM-TEST Loopback Adapter". If you need how to install this, you can check this kb article.

kb2777200

After sucessfully setup the adapter, you need to assign to IPv4 Stack on each front-end the same virtual IP Address, using 255.255.255.255 as network mask.

No need to define default gateway. No other protocol should be activated on this adapter. Only IPv4.

After that (no reboot required at all), you need to issue three command on each front-end in order to make everything working (this step was not necessary on Windows Server version prior 2008).

netsh interface ipv4 set interface "Ethernet" weakhostreceive=enabled
netsh interface ipv4 set interface "LoopBack" weakhostreceive=enabled
netsh interface ipv4 set interface "LoopBack" weakhostsend=enabled

Use instead of "Ethernet" and "LoopBack" the right names of your network interface.

Happy load balancing.

Wednesday, February 6, 2013

SharePoint 2010 - Problem in image result thumbnail within search result form a NON default Zone.

There is a problem on the preview of images that are displayed as a search result if this is carried out by an outer zone (therefore using an Alternate Access Mapping). The preview always points to the default AAM and as a result is not displayed.

Basically what needs to be done to solve the problem is to update the property "UseAAMMapping" of the managed property "PictureThumbnailURL."

To do this, simply run the powershell script below.

$searchapp = Get-SPEnterpriseSearchServiceApplication (get-spenterprisesearchserviceapplication)

$property = Get-SPEnterpriseSearchMetadataManagedProperty –SearchApplication $searchapp –Identity "PictureThumbnailURL"

$property.UseAAMMapping = $true

$property.Description = "Some Description" # Need to update this for commit to work properly

$property.Update()

$searchapp.Update()

You do not need to re-crawl content.

The curious thing is that before setting the property to "true", if you check the value you see that is already set correctly.

You can read full story here:
http://social.msdn.microsoft.com/Forums/en-US/sharepoint2010general/thread/173bece0-1fac-43c3-ad2d-ef796cc8371a/

SharePoint 2010 and 2013 convert License type

Often you need to enable the Enterprise feature on a farm originally installed with Standard key (or Trial).

Well, the Central Admin page set up to manage this upgrade will often give you the Text Box to insert the key "disabled" as well as the upgrade button to proceed.

There's another path to get this function. This is called "Enable Enterprise Features" in "Upgrade and Patch Management".

At this point you will prompt for the new key and you can proceed with the upgrade.

Monday, February 4, 2013

Get Query String Parameter with JScript

In order to get client side query string parameter value, you can use this jscript function.

function getQuerystring(key, default_)
{
   if (default_==null) default_="";
   key = key.replace(/[\[]/,"
\\\[").replace(/[\]]/,"\\\]");
   var regex = new RegExp("[\\?&]"+key+"=([^&#]*)");
   var qs = regex.exec(window.location.href);
   if(qs == null)
     return default_;
   else
     return qs[1];
}


Usage:

var paramValue = getQuerystring('keytofindvalue');

You can optionally pass a default value to this function. In this case, if non existing parameter is founded in the query string, the function will return default_ value.

For more detail on this function, please take a look here:
http://www.bloggingdeveloper.com/post/JavaScript-QueryString-ParseGet-QueryString-with-Client-Side-JavaScript.aspx

Friday, February 1, 2013

User Profile Service Provisioning Permissions

In order to successfully provision "User Profile Synchronization Service" on a SharePoint 2010 or a SharePoint 2013 you need specific permission.

1) FIM Account must have "Logon Locally" policy. Grant this using Local Security Policy on the server is receiving User Profile Synchronization Service provisioning;

2) SharePoint Timer Service account need to be local Administrator on target server because Provisioning Timer Job need to create Local Security Group and access with write permission to Windows Registry. After provisioning you can remove this user from local Administrators.

Important NOTE: Be sure that Regional Settings of the Domain User that is running SharePoint Timer Service are the same as Language ID of SharePoint Installation. In my case en-US.
You can check actual regional settings for that user opening PS with "run-as" using the inetrested user and issuing: Get-WinSystemLocale.

The error logged in trace log during service provisioning is:

UserProfileApplication.SynchronizeMIIS: Error updating users with FIM permissions: System.Runtime.Serialization.SerializationException: There was an error deserializing the object . String was not recognized as a valid DateTime. ---> System.FormatException: String was not recognized as a valid DateTime.

Reboot target machine and "try" to provision the service.

SharePoint Trace Logs Files 0kb

If you plan to use a low privilege account to run "SharePoint Tracing Service" (that is raccomanded), probably this will result in 0kb log files. This is due to lack of permission of this account to the LOG folder.
Instead of add the user directly to the folder you should add this account to a specific Windows Local Group named "Performance Log Users".

Once added the account, restart "SharePoint Tracing Service" in order to Apply changes.

From this point, Trace Log files will be correctly populate.