Category Archives: SharePoint 2010

Styling the Welcome menu

Sometimes I wonder why MS makes some things so hard to do. During the styling of our project, we wanted to move the Welcome menu, also known as the user menu or Personal Action menu to another location on the page.

The control is easy to find and is basically a user control located in the ControlTemplates folder in the 14 hyve. However, that control only defines the menu structure of the control, not the appearance. I started to worry a little, as that probably means that the appearance of the control is defined in code.

Posts by Erik Svenson and Shehan Peruma showed how to change the link color. But I hope that is not the only stuff you can change in the control. How would you ever integrate this into a new publication design?

Hence, I decided to call my favourite friend called Reflector again and dive into the code of SharePoint. Turns out the behaviour of the control is determined by two classes in the CSS, called ms-SPLink ms-SpLinkButtonActive ms-welcomeMenu and ms-SPLink ms-SpLinkButtonInActive ms-welcomeMenu, controlling the active and inactive state respectively.

Basically, the most important part to look for in the Corev4.css (in the Themable folder for publishing sites) is the ms-welcomeMenu class and the classes derived from that. Controlling the font color, family, background and other options is quite easily done through these classes:

.ms-welcomeMenu {
   padding:2px 5px 3px;
   margin:0px 3px;
   font-size:1em;
   font-family:Verdana,sans-serif;
   border:0px solid transparent;
   display:inline-block;
}
.ms-welcomeMenu a:link {
   color:#00259b;
}
.ms-welcomeMenu a:hover {
   text-decoration:none !important;
}
.ms-welcomeMenu.ms-SpLinkButtonActive {
/* [ReplaceColor(themeColor:”Dark2-Lighter”)] */
/* [RecolorImage(themeColor:”Dark2-Lightest”,includeRectangle:{x:0,y:489,width:1,height:11})] */
   background: url(../img/backgrounds/bg-sprite-tabbar.png) 100% 0 no-repeat;
/* [ReplaceColor(themeColor:”Dark2″,themeShade:”0.8″)] */
  background-color:#21374c;
}
.ms-welcomeMenu.ms-SpLinkButtonActive a:link {
/* [ReplaceColor(themeColor:”Light1″,themeTint:”0.9″)] */
   color:#00259b;
}

What I did in above CSS override is changing .ms-welcome a:link and .ms-welcome a:hover to change the font color of the inactive state.
To change to color of the font as soon as the mouse is over it, add the style ms-welcomeMenu.ms-SpLinkButtonActive a:link.

Secondly, I have overridden the background in .ms-welcomeMenu.ms-SpLinkButtonActive to show a custom made image. If IU left it, it would show the dark background of the top bar, which is something I do not want on a white background ;-).

By default, the ms-welcomeMenu has a transparent background in the inactive state, as it should be and therefore blends in into its container. Remember though that you should override these classes in a separate CSS file and be sure it is loaded last (or at least after Corev4.css) in the master page. Also, you cannot remove any styles, just override the settings already made.

Finally, you can do the same for the SiteActions menu. You can do that by looking for the classes called .ms-siteactionsmenu.


Advertisements

Branding SharePoint Sites, how to get started

Many of us out there have encountered it before; How do I make SharePoint look the way I want it to, without destroying any of the functionality that comes out of the box? In my current project, we face the same challenges that leave us sometimes puzzled on why Microsoft has chosen certain design approaches.

One of those things is the use of table style design, especially in the WebPartZone and WebPartChrome controls. It really frustrates designing based on DIV’s, which is the standard nowadays.

The second thing is the complete overload on CSS that SharePoint applies to pages to handle the styling of the various elements. In one way, this allows for very detailed control over UI elements, but in the other hand makes it almost impossible to apply a generic style across all pages and controls.

Fortunately, I was not the only one facing this issue. So here are some of the links I found very useful when starting to design Intranet en Internet web sites based on SharePoint 2010.

To start off, Andrew Connell and Randy Drisgill provided a quite extensive article on how to approach Branding with SharePoint 2010. This article is excellent and will provide you a flying start in any design and helps to understand the various elements contained in a SharePoint master page and the styles that are applied to them.

When web designers start developing their front-end design their first course of action is to reset all the css styles that are applied by default. Doing that on SharePoint will, well, confuse SharePoint to say it mildly. Kyle Schaeffer has developed a reset css specific for SharePoint 2010 that gives front-end designers a head start in applying their custom css on SharePoint.

CSS is basically the keyword in styling SharePoint. Not by introducing your own classes, but understanding the 200+ classes Microsoft introduced themselves and which does what. One of the absolute guru’s on this front is Heather Solomon that also provided a complete reference to all the styles that are applied to certain UI elements and what they said. A must read for any developer that needs to style SharePoint. Unfortunately, only for SharePoint 2007 now, but I am sure she will also provide one for SharePoint 2010.

Master pages and Page Layout form the basics on any SharePoint site and customizing can be challenging. Andrew and Randy provide a good starter.master that has all the essential elements in place and also contains good comments on which section does what. Couple of things I encountered that are crucial:

  • Do not remove the s4-workspace, s4-bodyContainer and MSO_ContentDIV divs.
    Doing so will really mess up your user experience with SharePoint, especially when entering the edit modes.
  • When going for a fixed width site, which is quite common these days, apply the s4-nosetwidth class to your workspace div. This will instruct the javascript behind the scenes not to touch the widths set in the CSS definitions.
  • Use the s4-notdlg class for any element you do not want to show in the modal dialogs of SharePoint. Failing to do so will show your beautiful header in full power within small dialog boxes.
  • Accept that SharePoint controls output their HTML in a certain way. Yes, some use tables to structure it, deal with it. If have played around with ControlAdapters and basically found that they do more damage than they solve. For one thing, because a lot of the html rendering methods of controls are a) internal and b) sealed, there is no way of reaching it, only to completely override it. Thing is, they introduce the object DOM in the HTML that you will then omit…and shake up SharePoint.

In short, styling SharePoint can be a tedious task. With these links, I hope you guys get in the right direction quick. I wished I knew them before finding out the hard way 😉

CU

Using Social Tagging to create a Favorites web part

On my journey in the new features of SharePoint 2010, my attention was caught by the so-called ‘Social’ features of SharePoint 2010. In particular, the use of the tagging and rating functionality had my interest.

A client wanted to give end-users a quick way to register favourites on their Intranet. I immediately thought of the ‘I Like It’ button in the Ribbon, where end-users can quickly tag a page that they like. In their profile, they could see all the tags and the url’s where they placed the tags.

Unfortunately, I could not find a web part that could display the tags for a given user for a specific url, or vice versa. The latter is what I needed. And so I decided to write some small code to do that for me.

In fact, the basic principles are simple. We connect to the Users profile, retrieve their tags, get the tag we want to filter on, retrieve the urls for that tag and display those in a list. Easy right?

Social Tagging

SharePoint 2010 stores tags entered by users in their user profile. You can view the tags you entered by going to your profile page and review the Tags and Notes section. That sections shows all the notes and tags, as well as a history (activities) trail for that particular tag or note. Simply put, the information is there.

I tried to find the controls SharePoint is actually using to generate these views, but was not able to make those available through a web part. Secondly, I want to be able to control the output and settings, so I will go with my own web part anyway.

Setting up the solution

I would like to keep this post simple, so I will not go into the specific details of formatting and making things configurable. I will just show the principle.

So, start Visual Studio 2010 and create a new Empty SharePoint project. Add a new item of the type Web Part. Give it all some reasonable names and your solution explorer should resemble the following picture:

Open the Favourites.cs file and we will start adding the code. Add the following references to your project:

  • Microsoft.Office.Server (needed for the UserProfile servercontext)
  • Microsoft.Office.Server.UserProfiles (needed for the UserProfiles)
  • Microsoft.SharePoint.Taxonomy (needed for the term store)
  • Microsoft.SharePoint (needed for the normal SharePoint objects)

 GetUserUrlsByTag method

Below code shows the GetUserUrlsByTag method that will retrieve all urls from the current user for a specific tag.

private Uri[] GetUserUrlsByTag(string tag)
{
   // first get the service context
   SPContext ctx = SPContext.Current;
   SPServiceContext serviceContext = SPServiceContext.GetContext(ctx.Site);

   // Then load the UserProfileManager

   UserProfileManager mngr = new
   UserProfileManager(serviceContext);

   // Get the user’s profile

   UserProfile currentProfile = mngr.GetUserProfile(false);

   // If the user has a profile, get the SocialTagManager
   if (currentProfile != null)
   {
      SocialTagManager smngr = new
      SocialTagManager(serviceContext);

      // Get the terms for the user

      SocialTerm[] terms = smngr.GetTerms(currentProfile);
      SocialTerm favTerm = null;

      //Iterate through the terms and search for the passed tag
      foreach (SocialTerm t in terms)
      {
         if (t.Term.Name.ToLower() == tag.ToLower())
         {
            favTerm = t;
            break;
         }
      }

      // If found, get the Urls and return
      if (favTerm != null)
         return smngr.GetUrls(favTerm.Term, currentProfile);
      else
         return null;
   }
   return null;
}

The method needs to return an array of urls that have a certain tag applied to it. We first set up the context and service context for this site. We then create a UserProfileManager based on the service context and retrieve the UserProfile of the current user.

If the current user has a user profile, we create a SocialTagManager that will perform the bulk of the functionalities we need. We then get all the SocialTerms for this user. It would be better if we could request a specific Term to be retrieved, but unfortunately I have not found a method to do so. So instead, we will need to iterate through the returned terms until we find the one we want to filter on. I would be much obliged if someone know a better method of doing this. For now, it works.

Once we found the term we are interested in, we can request all the url’s for that tag by calling the SocialTagManager.GetUrls method, passing the term and the profile.

And we are done! The web part I coded contains the method call and will output all the url’s in a list. However, you can style it in the any way you want obviously. Now when users click the ‘I like it’ button in the ribbon, it will show up in their favorites web part.

To be complete, the code in the CreateChildControls method of the web part.

protected override void CreateChildControls()
{
   try
   {
      Uri[] urls = GetUserUrlsByTag(“I Like It”);
      HtmlGenericControl table = new
      HtmlGenericControl(“table”);
      Controls.Add(table);

      if (urls != null)
      {
         foreach (Uri url in urls)
         {
            HtmlGenericControl row = new
            HtmlGenericControl(“tr”);
            HtmlGenericControl cell = new
            HtmlGenericControl(“td”);

            HyperLink link = new HyperLink();
            link.NavigateUrl = url.AbsoluteUri;
            link.Text = url.AbsoluteUri;
            table.Controls.Add(row);
            row.Controls.Add(cell);
            cell.Controls.Add(link);
         }
      }
      else
      {
         Label lbl = new Label();
         lbl.Text = “No favourites found.”;
         Controls.Add(lbl);
      }
   }
   catch (SPException ex)
   {
      Label lbl = new Label();
      lbl.Text = ex.ToString();
      Controls.Add(lbl);
   }
}

And finally the output:

Improvements on this code

So, a couple of things can obviously be improved in this little piece of code.

  1. Make the input and output configurable. So selecting the term to filter on and exporting the output through XML, which allows us to use XSL for the rendering.
  2. Get details about the url’s in the profile. For example, we could retrieve the page title rather than the url by obtaining that from the page instance in the pages library.
  3. Add a button or control to the ribbon or expose that as a separate web part that does the favourite tagging. That way we can control the term we call favourite (for example, MyFavourite instead of I like it)
  4. Improve the filtering on the terms. As soon as a user gets a lot of terms, the filtering might slow down.

 Conclusion

The new Social framework of SharePoint 2010 can be used for very neat things. To bad some of the features are not exposed more through web parts, but I am sure the community will provide their own sets of controls that expose the power behind Social tagging and rating. You can find the VS project for this example here.

Till next time!

Using a Timer Job to ensure diagnostic areas of SPGuidance

In one of my previous posts, I described a PowerShell script that could be run on each front-end to ensure that for each diagnostic area defined in the logging framework, a corresponding event source was created. That script should be run with sufficient privileges on the target box, but does the job.

I then promised to post a method to do that from a timer job next, but work and private life has kept me busy lately 😉 My apologies. However, here it is.

In this post, I would like to expand on that a little, but instead of using the PowerShell route, I use a SharePoint Timer Job to accomplish the same thing.

You can download the example Visual Studio 2010 project from this location. What we will do in this post:

  1. Create a timer job that should be run on each front-end that ensures that for each diagnostic area, an event source is registered.
  2. Create two features:
    1. Diagnostics areas and categories
    2. Timer Job

In a real-world situation, I would publish the SPGuidance assemblies in a separate WSP, however, for simplicity; they are packaged with this solution.

I will create one diagnostic area called Boom.CustomLogging and two categories, called Connections and Events. From within the web part, I will log to both categories. In my next post, I will also show how to create a custom logging component. For this post, we use the default one (which logs to the event log and ULS logs).

First, open Visual Studio and create an empty SharePoint project. We will create three features:

  1. DiagnosticAreas à Sets up the diagnostic areas and categories
  2. JobInstaller à Will install our job to ensure the event sources
  3. WebParts à Will contain our test web part

Also create a Web Part element called LoggingTester and add it to the solution. Add Event receivers to the DiagnosticAreas and JobInstaller features. Finally, add a class to your project called EnsureEventSourcesJob.cs that will hold your timer job to ensure event sources. Your solution explorer should look something like this:

Configure Custom Diagnostic Areas

We will start with the event receiver for the Diagnostic areas. This feature receiver will set up our areas and categories that we will use in our sample. I want to configure one custom diagnostic area that is specific to my application and two categories beneath it.

  • Diagnostic Area: Boom.CustomLogging
    • Category: Connections (default event severity Error, default trace severity Medium)
    • Category: Events (default event severity Information, default trace severity Medium)

On the side, it is also possible to add categories to the default SharePoint 2010 diagnostic areas, such as Access Services, Excel Services Application and SharePoint Foundation Search, however, this is considered bad practice. In short, do not do it. Whenever you wish to introduce custom categories, do so in your own diagnostic area.

The following piece of code was taken from the SPGuidance chm. It defines a property in our event receiver class that returns a DiagnosticsAreaCollection with my custom areas and categories.

// This helper property builds a collection of areas and categories.
DiagnosticsAreaCollection _myAreas = null;
DiagnosticsAreaCollection MyAreas
{
   get {
      if (_myAreas == null)
     
{
         _myAreas = new DiagnosticsAreaCollection();
        
DiagnosticsArea boomArea = new DiagnosticsArea(“Boom.CustomLogging”);
         boomArea.DiagnosticsCategories.Add(new DiagnosticsCategory(“Events”, EventSeverity.Information, TraceSeverity.Medium));
         boomArea.DiagnosticsCategories.Add(new DiagnosticsCategory(“Connections”, EventSeverity.Error, TraceSeverity.Medium));
      
         _
myAreas.Add(boomArea);
     }

     return
_myAreas;
   }
}

In this code snippet, we define a property that creates a new Microsoft.Practices.SharePoint.Common.Logging.DiagnosticsAreaCollection, creates a new Microsoft.Practices.SharePoint.Common.Logging.DiagnosticsArea called Boom.CustomLogging and finally adds two Microsoft.Practices.SharePoint.Common.Logging.DiagnosticsCategory objects that defines my two categories and adds it to the custom area. Finally, I add the area to the collection and return it.

No real magic here. We will use this property in both the feature activated and feature deactivating events. So, let us go in the feature activated event.

public override void FeatureActivated(SPFeatureReceiverProperties properties) {

   IConfigManager
configMgr = SharePointServiceLocator.GetCurrent().GetInstance<IConfigManager>();
   DiagnosticsAreaCollection configuredAreas = new DiagnosticsAreaCollection(configMgr);

   foreach
(DiagnosticsArea newArea in MyAreas)
{
     var existingArea = configuredAreas[newArea.Name];
     if (existingArea == null) {
        configuredAreas.Add(newArea);
    
else {
        throw new SPException(“Diagnostic area already exists”);
     }
   }

   configuredAreas.SaveConfiguration();
}

In this method, we use the SharePointServiceLocator, which is also part of the Guidance framework, to return an instance of IConfigManager. The IConfigManager can be used to store configuration data on any level in SharePoint, so at Web, Site, WebApp and Farm level. Again, this is part of the Guidance framework and provides a single consistent framework to store configuration data that is not to be located in the web.config. Using the config manager, we request the current custom configured diagnostic areas. Then, for each area from our property (our new set), we check whether or not it already exists. If not, we add it. Finally, save the configuration again.

The feature deactivating method basically does the reverse, shown below.

public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {
   // Then remove the areas
   IConfigManager configMgr = SharePointServiceLocator.GetCurrent().GetInstance<IConfigManager>();
   DiagnosticsAreaCollection configuredAreas = new DiagnosticsAreaCollection(configMgr);
  
  
foreach (DiagnosticsArea area in MyAreas) {
      DiagnosticsArea areaToRemove = configuredAreas[area.Name];
      if (areaToRemove != null) {
         foreach (DiagnosticsCategory c in area.DiagnosticsCategories) {
            var existingCat = areaToRemove.DiagnosticsCategories[c.Name];
            if (existingCat != null) {
               areaToRemove.DiagnosticsCategories.Remove(existingCat);
            }
         }
     
        
if (areaToRemove.DiagnosticsCategories.Count == 0) {
            configuredAreas.Remove(areaToRemove);
         }
      }
   }

   configuredAreas.SaveConfiguration();
}

The only difference here is that we take a safe approach as suggested in the guidance, because other applications (depending on company guidelines) could have added categories to the diagnostic area. The safe way therefore is to remove the own configured categories and if none remain, remove the area.

Create the timer job to ensure event sources

In this section we will create a simple timer job that needs to be executed on every server in the farm. The job will call a method from the Guidance framework that will ensure that for each custom diagnostic area, a corresponding event source is created. This will have the same function as my PowerShell script in my previous post, just implemented through a timer job. The advantage here however, is that I can configure the timer job to run on specific intervals on all servers within the farm. The PowerShell script can also be scheduled and run remotely, but needs more management.

We define the timer job in the EnsureEventSourcesJob class.

public class EnsureSourcesJob : SPJobDefinition {
   public EnsureSourcesJob() : base() {}
  
   public
EnsureSourcesJob(string name, SPWebApplication webApplication) : base(name, webApplication, null, SPJobLockType.None) {
      this.Title = name;
   }

   public o
verride void Execute(Guid targetInstanceId)
{
      ILogger logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
      logger.TraceToDeveloper(“Ensure Configured Diagnostic Areas registered timer job executing.”, 103, Microsoft.SharePoint.Administration.TraceSeverity.Monitorable, “Events”);
      // Important! The JobLockType should be set to None to run on each server!
      if (this.LockType == SPJobLockType.None) {
         // our job has only one very simple task.
         // call the ensure configured areas registered method of the DiagnosticsAreaEventSource class.
         DiagnosticsAreaEventSource.EnsureConfiguredAreasRegistered();
      }
   }
}

Two things are important in this job, which is quite simple. First, you need to provide a public parameter less constructor for the serialization. Second, the type of job specified should be of type SPJobLockType.None. This will ensure that my timer job will be run on each server within the farm.

The call to the DiagnosticsAreaEventSource.EnsureConfiguredAreasRegistered method will make sure that an event source for each of the configured diagnostic areas exists on the specific server.


Once the timer job has run, you can go to Central Admin à Diagnostic logging to see your own categories and events. They will be treated the same as any out of the box configured sources and categories.

The Visual Studio solution can be downloaded from here.

CU.

Using PowerShell to register SPGuidance diagnostic areas

The SPGuidance package delivers a set of components to ease certain administration tasks with SP 2010, for example logging.

It hooks in to the built-in SharePoint logging functionality and exposes a set of classes to easily log your own events in the event log, ULS logs or any custom log sources, like a database.

If one would look into this model, you will find that a diagnostic area corresponds to an event source in the event logs. For instance, if I register a diagnostic area called Boom.MyCustomApplication, the event source mentioned in the logs would read the same. This allows support engineers to easily track the source of the event and act accordingly.

However, although the diagnostic area corresponds to the event source, they are not the same. You can register diagnostic areas using the logging component of the SPGuidance set, but this does not mean a corresponding event source is also created. In fact, event sources need to be registered on each front-end separately with sufficient permissions, before the logging component can write to the event log using the diagnostic area as the event source. So how would you create the event sources on each front-end? Well, there are a couple of possible approaches:

  1. Use PowerShell to create the sources on each front-end remotely
  2. Use a Timer Job to run custom code on each front-end that registers the event source.

In this post, I will show how to do that from PowerShell. These are only two lines of code and the script can be run from a remote location on each front-end.

# First load object models to use
[void][System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.Practices.SharePoint.Common”)

# call the registration method to ensure all registered diagnostic areas are also registered as event sources
[Microsoft.Practices.SharePoint.Common.Logging.DiagnosticsAreaEventSource]::EnsureConfiguredAreasRegistered()

In this script, we first load the assembly Microsoft.Practices.SharePoint.Common.dll. We assume here that the assembly is available from the GAC. In my next post, the solution provided will ensure these assemblies are deployed to the GAC for use. Once we have done that, we can call the static EnsureConfiguredAreasRegistered method of the DiagnosticsAreaEventSource class that will iterate through all the configured and registered diagnostic areas and ensures that an event source is registered for that. The event sources will be registered on the Application event log. During the next couple of days, I will also investigate the possibility of registering the sources to different logs.

In the next post, I will show a custom solution on how to create additional diagnostic areas and use a timer job to ensure the event sources are created at the front end. Till next time!