09/10/2013

Modify your Master Page to display the top level web description

1) Create a new Web Control

The web control shall retrieve the description from your top level site.

In your Visual Studio project, right click and choose Add > New Item... In the New Item dialogue select Module. Give the new module a name (SiteDescription) and click OK.
Remove the txt file.
Add a class (Add > Class...), give it a name (SiteDescription) and click Add.
The source code of your class will look something like this:

namespace NameSpace.SiteDescription
{
    /// <summary>
    /// SiteDescription class is used to displayes the description of the top level web.
    /// </summary>
    [ToolboxData("<{0}:SiteDescription runat=server></{0}:SiteDescription>")]
    public class SiteDescription : WebControl
    {
        private string _alt = "- default description of site";

        /// <summary>
        /// override so that the surrounding tag is a div insteadof a span
        /// </summary>
        protected override HtmlTextWriterTag TagKey
        {
            get { return HtmlTextWriterTag.Div; }
        }

        /// <summary>
        /// Add the description as an em tag
        /// </summary>
        /// <param name="writer"></param>
        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust"
            )]
        protected override void AddAttributesToRender(HtmlTextWriter writer)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.Em);
            writer.WriteEncodedText(_alt);
            writer.RenderEndTag();
        }

        private void InitiateDescription()
        {
            try
            {
                var webApp = SPContext.Current.Site.WebApplication;
                SPWeb siteToUse = SPContext.Current.Web;
                foreach (SPSite site in webApp.Sites)
                {
                    if (site.RootWeb.Title.Equals("Title")) /* your title...*/
                    {
                        siteToUse = site.RootWeb;
                        break;
                    }
                }
                if (!String.IsNullOrEmpty(siteToUse.Description))
                {
                    _alt = siteToUse.Description;
                }
              
            }
            catch (Exception e)
            {
                /*error handling*/
            }
        }

        /// <summary>
        /// Create new web control
        /// </summary>
        public BannerDescription()
        {
            InitiateDescription();
        }
    }
}


Edit the Elements.xml file of your new Web Control:

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Control
   Id="SiteDescription"
   Sequence="24"
   ControlClass="Namespace.SiteDescription.SiteDescription"
    ControlAssembly="Namespace, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxx">
  </Control>
</Elements>


2) Use your description web control in your master page

<NameSpace:SiteDescription ID="SiteDescriptionWebControl" runat="server"/>

Visual Studio will prompt you with the right import statements.

07/10/2013

Unable to change Navigation Settings

Error message from ULS log when trying to modify Navigation Settings (Site Settings > Navigation):

Unable to retrieve TopNavigationBar SPNavigationNodeCollection from Web at: <url>. The SPNavigation store is likely corrupt.

Run the following db script:

INSERT INTO [Cotent_db].[dbo].[NavNodes]
 ([SiteId], [WebId], [Eid], [EidParent], [NumChildren], [RankChild],[ElementType], [Url],
 [DocId], [Name], [DateLastModified], [NodeMetainfo], [NonNavPage], [NavSequence], [ChildOfSequence])
SELECT DISTINCT SiteId, WebId ,1002 ,0 ,0 ,1 ,1 ,'', NULL, 'SharePoint Top Navbar',getdate() ,NULL ,0 ,1 ,0
 FROM [Cotent_db].[dbo].[NavNodes] WHERE WebId NOT IN (SELECT WebId FROM [Cotent_db].[dbo].[NavNodes] WHERE Eid = 1002)
 

Thanks to:
http://bimoss.wordpress.com/2009/09/29/unable-to-modify-global-navigation-add-headinglink/

02/10/2013

Add Site Column to existing Content Type in code

How to add a new Site Column to an existing Content Type from code


This post describes how to create a new column, add it to an existing Content Type in an existing feature and last run an upgrade in PowerShell of the feature.

Feature.xml file:
<?xml version="1.0" encoding="utf-8" ?>
<Feature xmlns="
http://schemas.microsoft.com/sharepoint/" Version="2.0.1.0">
  <ElementManifests>
    <ElementManifest Location="NewColName\Elements.xml" />
  </ElementManifests> 
  <UpgradeActions>
    <VersionRange EndVersion="3.0.0.0">
      <ApplyElementManifests>
        <ElementManifest Location="NewColName\Elements.xml" />
      </ApplyElementManifests>
      <CustomUpgradeAction Name="AddFieldToContentType">
        <Parameters>
          <Parameter Name="FieldId">x-x-x-x-x</Parameter>
          <Parameter Name="ContentTypeId">xxxx</Parameter>
          <Parameter Name="PushDown">TRUE</Parameter>
        </Parameters>
      </CustomUpgradeAction>
    </VersionRange>
  </UpgradeActions>
</Feature>


The NewColName\Elements.xml contains the definition of the new column and shall be added in both the ElementManifest section and the UpgradeActions-ApplyElementManifest section. The ElementManifest section is used when deploying the feature the first time, and the UpgradeAction section is used when upgrading the feature. The new version of the feature is 2.0.1.0, and the upgrade action shall be active until version 3.0.0.0 (EndVersion attribute).
The CustomUpgradeAction refers to an upgrade action named "AddFieldToContentType". The action takes three parameters the new field ID the existing Content type ID and a parameter called PushDown, witch configures of the Content Type changes shall be pushed down to child Content Types.

The new column is defined in NewColName\Elements.xml:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="
http://schemas.microsoft.com/sharepoint/"> 
  <Field

    Name="NewColName"
    StaticName="NewColName"
    Type="Text"
    Required="FALSE"
    DisplayName="NewColName"
    Description="The new column"
    Group="NewGroup"
    ID="{x-x-x-x-x}"
    Overwrite="TRUE" OverwriteInChildScopes="FALSE" 
    SourceID="http://schemas.microsoft.com/sharepoint/v3"
    xmlns="http://schemas.microsoft.com/sharepoint/" />
</Elements>


Create an event receiver and override the FeatureUpgrading method, this method is invoked when the feature is upgraded (surprise!) and the upgrade action name is taken as a parameter:
public override void FeatureUpgrading(
  SPFeatureReceiverProperties properties,
  string upgradeActionName,
  System.Collections.Generic.IDictionary<string, string> parameters)
{
  if (properties.Feature.Parent is SPSite)
  {
    SPWeb web = ((SPSite) properties.Feature.Parent).RootWeb;
    switch (upgradeActionName)
    {
      case "AddFieldToContentType":
        string fieldId = parameters["FieldId"];
        string contentTypeId = parameters["ContentTypeId"];
        bool updateChilds = true;
        bool flag;
        updateChilds=bool.TryParse(parameters["PushDown"],out flag);
        AddFieldToContentType(web,contentTypeId,fieldId,updateChilds);
        break;
      default:
        break;
    }
  }
}


AddFieldToContentType method:
private void AddFieldToContentType(SPWeb web, string contentTypeId, string fieldId, bool updateChilds)
{
  SPContentType type = web.ContentTypes[new SPContentTypeId(contentTypeId)];
  type.FieldLinks.Add(new SPFieldLink(web.Fields[new Guid(fieldId)]));
  type.Update(updateChilds, updateChilds);
}


The last thing to do is to override the FeatureActivated method, this method is invoked when the feature is activated and it will make sure that the new Field also is added to the Content Type the first time the feature is activated:
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
  if (properties.Feature.Parent is SPSite)
  {
    SPWeb site = ((SPSite) properties.Feature.Parent).RootWeb;
    const string fieldId = "x-x-x-x-x";
    const string contentTypeId = "xxx";
    AddFieldToContentType(site, contentTypeId, fieldId, true);
  }
}


So to upgrade the feature run this script in PowerShell :
$versionFolder = "C:\temp\"
$solutionName = “x.wsp”
$solutionPath = $versionFolder + $solutionName
$siteUrl = "
http://.../"
$featureId = "{x-x-x-x-x}"

Update-SPSolution –Identity $solutionName –LiteralPath $solutionPath –GacDeployment
# wait for it to finish
$site = get-spsite $siteUrl
$feature = $site.Features | where {$_.Definition.Id -eq $featureId}

if($feature)
{
    $ex = $feature.Upgrade($true)
    Write-Host $ex #if anything went wrong this is printed
}