Thursday, August 29, 2013

How to remove columns from web part view on a page


The code locates a landing page and search for web part called "Shared Documents". If page is not checked out, it checks out the page and delete "Version" column web part view (_UIVersionString)


private void UpdateViewOfWebpart(SPSite createdSite)
        {
            //// Sets the page url where the webpart is located
            string pageUrl = createdSite.RootWeb.RootFolder.WelcomePage;
            string targetwebpartTitle = "Shared Documents";

            //// Elevating permission
            SPSecurity.RunWithElevatedPrivileges(() =>
            {
                //// Creates a new SPSite instance for the sharepoint website
                using (SPSite spsite = new SPSite(createdSite.Url))
                {
                    //// Opens the SPWeb instance based on which the SPSite was initiated
                    using (SPWeb spweb = spsite.OpenWeb())
                    {
                        /// Gets the page SPFile object
                        SPFile pageSPFile = spweb.GetFile(pageUrl);

                        try
                        {
                            //// If file is not checked out then do a check out
                            if (pageSPFile.Level != SPFileLevel.Checkout)
                            {
                                pageSPFile.CheckOut();
                            }

                            //// Gets the SPLimitedWebpartManager instance from the page
                            SPLimitedWebPartManager limitedWebpartManager = pageSPFile.GetLimitedWebPartManager(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);

                            //// A temp value to use inside the following for loop
                            string currentWebpartTitle = string.Empty;

                            foreach (System.Web.UI.WebControls.WebParts.WebPart webPart in limitedWebpartManager.WebParts)
                            {
                                //// Ignore the closed webpart
                                if (!webPart.IsClosed)
                                {
                                    //// Reads the webpart title in the iterated loop
                                    currentWebpartTitle = webPart == null ? string.Empty : (webPart.Title == null ? string.Empty : webPart.Title);

                                    //// Checks whether current webpart title in iterated loop is the targeted webpart or not
                                    if (currentWebpartTitle == targetwebpartTitle)
                                    {
                                        //// With the help of reflection gets the View field’s info
                                        System.Reflection.FieldInfo viewFieldInfo = webPart.GetType().GetField("view", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

                                        //// Ignore and continue loop if view field is null
                                        if (viewFieldInfo == null)
                                        {
                                            continue;
                                        }

                                        //// Cast the view field’s value which was retrieved using reflection to the SPView
                                        SPView view = viewFieldInfo.GetValue(webPart) as SPView;

                                        if (view.ViewFields.Exists("_UIVersionString"))
                                            view.ViewFields.Delete("_UIVersionString");

                                        /// Updates the view
                                        view.Update();

                                        //// This is optional, need not have to save changes to webpart, but just to confirm
                                        limitedWebpartManager.SaveChanges(webPart);

                                        break;
                                    }
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            throw ex;
                        }
                        finally
                        {
                            if (pageSPFile.Level == SPFileLevel.Checkout)
                            {
                                pageSPFile.CheckIn("Removed Version column", SPCheckinType.MajorCheckIn);
                            }
                        }

                    }
                }
            });

        }

PowerShell script does the same

$deleteField = "_UIVersionString"
$whereToLook = "Shared Documents"

function HasPageWebPartField()
{
      param
      (
      [Microsoft.SharePoint.SPFile]$Page,
      [Microsoft.SharePoint.SPWeb]$Web
      )          
     
      $localWebVariable = Get-SPWeb -Identity $web.Url     
    $checkWpm = $localWebVariable.GetLimitedWebPartManager($page.Url, [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)
      $checkWp = $checkWpm.WebParts | Where-Object { $_.Title -eq $whereToLook }
      $checkList = $localWebVariable.Lists | Where-Object { $_.Title -eq $whereToLook }
      if ($wp.ViewGUID -ne $null -and $wp.ViewGUID -ne "")
      {
            $checkView = $checkList.Views[[Guid]$checkWp.ViewGUID]
            $returnVal = $checkView.ViewFields.Exists($deleteField)
      }
      else
      {
            $returnVal = $false
      }
      $checkWpm.Dispose()
      $localWebVariable.Dispose()
      return $returnVal
}

$web = Get-SPWeb $_.URL
      
       if ([Microsoft.SharePoint.Publishing.PublishingWeb]::IsPublishingWeb($web))
       {
              [Microsoft.SharePoint.Publishing.PublishingWeb]$publishingWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($web)
              $page = $publishingWeb.DefaultPage
              if ($page.CheckedOutDate -eq $null)
              {
                     $hasField = HasPageWebPartField -Page $page -Web $web
                     if ($hasField)
                     {
                     $page.CheckOut()
                     $wpm = $web.GetLimitedWebPartManager($page.Url, [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)
                            $wp = $wpm.WebParts | Where-Object { $_.Title -eq $whereToLook }
                           $list = $web.Lists | Where-Object { $_.Title -eq $whereToLook }
                           $view = $list.Views[[Guid]$wp.ViewGUID]
                           $view.ViewFields.Delete($deleteField)
                           $view.Update()
                           $wpm.SaveChanges($wp)
                           $page.CheckIn("Removed Version column. Update via PowerShell",[Microsoft.SharePoint.SPCheckinType]::MajorCheckIn)
                     }
              }
              else
              {
                     $message = "Web " + $web.Url + " page " + $page.Name + " was checked out - to be processed manually."
                     Write-Host $message -ForegroundColor Red
                     $script:fileContents += ($message)
                     $script:fileContents += ("`r`n")
              }
       }

      $web.Close()


Monday, August 12, 2013

Exception setting „Hidden” Cannot change Hidden attribute for this field”

Requirements:
Requirements was to remove the Taxonomy fieldParent Companyfrom the Sharepoint List  as this field is not used by anyone.
Issue:
Issue with theParent Companyfield deleting with PowerShell script. After the script run the following error message has appeared. Exception settingHiddenCannot change Hidden attribute for this field
Investigation:
This is an internal issue in SharePoint2010.
The common issue description can be found here: http://blogs.msdn.com/b/spblog/archive/2013/04/27/sps2010-field-cannot-be-made-again-visible-if-defined-to-be-hidden-using-visual-studio-2010.aspx .
During development the fieldParent Companywas created usisng xml definition in Visual Studio 2010 and a field attributeHiddenwas set to valueTrue” (true if the field is hidden; otherwisefalse.)
<Field
       ID="{747FC1CF-FCED-44EB-B895-568E9F423174}"
        Name="ALLegalCompanyTaxHTField1"
        StaticName="ALLegalCompanyTaxHTField1"
        Type="Note"
        Overwrite="TRUE"
        RowOrdinal="0"
        DisplayName="$Resources:IntranetGroupDirectory,Fields_ParentCompany_DisplayName;"
        Group="$Resources:IntranetGroupDirectory,GroupDirectory_CT_Group;"
        Description="$Resources:IntranetGroupDirectory,Fields_ParentCompany_Description;"
        Hidden ="TRUE">
  </Field>
An taxonomy field consists of  two fields (example: „Parent Companycan be seen on screenhot)
TheParent Companyis a taxonomy field and consists of two fields definition ALLegalCompany and ALLegalCompanyTaxHTField1
<Field
        ID="{B5FE2B9E-CDF9-4B4A-B34C-85650481A280}"
        Name="ALLegalCompany"
        StaticName="ALLegalCompany"
        Type="TaxonomyFieldType"
        ShowField="Term1033"
        Overwrite="TRUE"
        Mult="TRUE"
        DisplayName="$Resources:IntranetGroupDirectory,Fields_ParentCompany_DisplayName;"
        Group="$Resources:IntranetGroupDirectory,GroupDirectory_CT_Group;"
        Description="$Resources:IntranetGroupDirectory,Fields_ParentCompany_Description;"
        Hidden ="FALSE">
</Field>
The field ALLegalCompany has Hidden proeprty set value to False.
The field ALLegalCompanyTaxHTField1 has propertyHiddenset to True that caused the difficulties to delete this field.
Fields in SharePoint can be added using xml definition via Visual Studio, but cannot be delete that way, therefore the Powershell commands must be used to delete fields from Lists or Content Type. But the fieldParent Comapnycannot be deleted that’s way using scrips, because fields should be visible and property Hidden is set to false. Property Hidden cannot be set to false, because this property depends (relies) on other property called CanToggleHidden and it’s value should be set to True. „CanToggleHiddenproperty is Read-only and  and it’s value cannot be change directly, because there is no setter method (this is a SharePoint property that stored in native assemly (SPField) and was developed that way, the reason of that fact is unknown).
To be possible to change property Hidden to false, the property CanToggleHidden value should be set to True in a field definition, the way like in this example below (but in this case the property is absent at all in the field definition).
<Field
        ID="{747FC1CF-FCED-44EB-B895-568E9F423174}"
        Name="ALLegalCompanyTaxHTField1"
        StaticName="ALLegalCompanyTaxHTField1"
        Type="Note"
        Overwrite="TRUE"
        RowOrdinal="0"
        DisplayName="$Resources:IntranetGroupDirectory,Fields_ParentCompany_DisplayName;"
        Group="$Resources:IntranetGroupDirectory,GroupDirectory_CT_Group;"
        Description="$Resources:IntranetGroupDirectory,Fields_ParentCompany_Description;"
        Hidden ="TRUE"
   CanToggleHidden ="TRUE">
  </Field>
By default CanToggleHidden is return false as it is not presented in the definition. Adding the property and deploy solution don’t change the field defintion.
If we have a look in code at the Hidden property setter in assembly Microsoft.SharePoint.SPField  it also sets „CanToggleHiddenproperty to true, but cannot be reached because it is value by default is false and throw the error message shown in the picture above Exception settingHiddenCannot change Hidden attribute for this field.
this.SetFieldBoolValue("CanToggleHidden", true)
[ClientCallable]
    public bool Hidden
    {
      get
      {
        if (this.HasExternalDataSource)
        {
          return SPExternalList.IsFieldHidden(this);
        }
        return this.GetFieldBoolValue("Hidden", 4);
      }
      [ClientCallableExceptionConstraint(FixedId="1", ErrorCode=-2146232832, Condition="Thrown when attempting to set Hidden property for a field that does not allow this property to be toggled.", ErrorType=typeof(SPException))]
      set
     {
        if (!this.CanToggleHidden)
        {
          throw new SPException(SPResource.GetString("HiddenAttributeUpdateNotAllowed", new object[0]));
        }
        this.SetFieldBoolValue("Hidden", value);
        this.SetFieldBoolValue("CanToggleHidden", true);
      }
    }

Solution:
Solution was found to use reflection to change propertyCanToggleHiddenvalue to True, cahnge Hidden property to false and delete the field from the List via PowerShell

PowerShell example:

$web = Get-SPWeb  "http://sharepoint/
$listLegComp = $web.Lists["YourList"]
$pcFieldTax = $listLegComp.Fields.GetFieldByInternalName("ALLegalCompanyTaxHTField1")
$pcField = $listLegComp.Fields.GetFieldByInternalName("ALLegalCompany")
if(!$pcFieldTax.CanToggleHidden)
{
   $bindingFlags = [Reflection.BindingFlags] "NonPublic,Instance"
   [System.Type] $type = $pcFieldTax.GetType()
   [Reflection.MethodInfo] $mdInfo = $type.GetMethod("SetFieldBoolValue",$bindingFlags)
   $object = [System.Object] @("CanToggleHidden",$true)
   $mdInfo.Invoke($pcFieldTax,$object)
   $pcFieldTax.Hidden = $false
   $pcFieldTax.AllowDeletion = $true
   $pcFieldTax.Update()
     
}
if(!$pcField.CanToggleHidden)
{
   $bindingFlags = [Reflection.BindingFlags] "NonPublic,Instance"
   [System.Type] $type = $pcField.GetType()
   [Reflection.MethodInfo] $mdInfo = $type.GetMethod("SetFieldBoolValue",$bindingFlags)
   $object = [System.Object] @("CanToggleHidden",$true)
   $mdInfo.Invoke($pcField,$object)
   $pcField.Hidden = $false
   $pcField.AllowDeletion = $true
   $pcField.Update() 
}

$pcFieldTax.Delete()
$pcField.Delete()
$listLegComp.Update()
$web.Dispose()