Monthly Archives: October 2008

Adding FAVICONS to SharePoint

Ok .. I admit it .. this isn’t really just a SharePoint thing, it’s a "web" thing, but quite frankly I am astounded that this isn’t something provided out of the box.

So what are these favicon things anyway?

Favicons are the icons that appear in your web browser when you are surfing. They usually appear in the address bar (depending on your browser) and were originally created for older IE environments to help identify web sites in the Favourites folder (hence the name "favicon" = "favourites icon").

example favicons

Ok, thats great, so how do I get them in SharePoint?

Well, it’s really quite simple. Favicons are represented by small "link" tags in your HTML. In SharePoint the most sensible place to put them is in the Master Page. The reason for this is that (generally) your Master Page is rendered for pretty much every page .. therefore you don’t have to add favicon links all over the place.

All you need to do is put a "link" tag into your Master Page, within the "<head>" tags (i.e. it needs to be in the page header!)

<link rel="shortcut icon" href="/Portal Images/favicon.jpg" />

How to "SharePoint’ise" it!

I know, that’s not a real word, but the best approach you can take is to place your icon image (either an icon or image file) into an Image Library. You can then place a relative URL to a specific image in that image library, and it allows the content editors to upload a new favicon whenever they want 🙂 (with the same version control and content approval you would normally have).

Thats exactly what I did with the sample line of code above, with a file called "favicon.jpg" which is located in an Image Library called "Portal Images" at the top level of the site collection.

Now, if you want to get REALLY fancy, then you could change it to be relative to the Site Collection (so each site collection could potentially have a different favicon). Do you that, just follow my earlier post on creating "Site Collection Relative URLs in MOSS 2007".

Neat eh?

Word, Excel and PowerPoint running from the browser .. it must be Office 14 for Web

This is a great "first look" video from Channel9 (https://channel9.msdn.com/posts/PDCNews/First-Look-Office-14-for-Web/) showing off the new browser based features of the Office 14 for web (still in beta, still in development!) at this weeks PDC!

It’s hot off the press, and it’s going to blow your socks off!

According to the video they have made heavy use of Silverlight and AJAX to create rich office clients that run through a web browser. Due to the user of Silverlight the rendering is crisp and detailed, and to be honest I couldn’t tell the difference between the web version and the desktop client!

Multi-User editing (including from the browser!) allowing multiple users to edit the same document at the same time is demo’d, including talk about integrating better collaboration tools!

To get you even more excited, the web browser based application is demo’d from FireFox! That’s right, the new Office 14 apps are being designed with complete and total cross-browser compatibility.

Excited about the next version of Office?? (i.e. SharePoint!!!) I am!

Blank “Web File Properties” when saving document from Word 2003

This is a problem that I encountered with a MOSS 2007 system that was upgraded from SPS 2003.

Specifically, we were developing a custom document library, and found that if we had libraries with multiple content types, when we tried to save documents from Microsoft Word 2003 the "Web File Properties" dialog (the pop-up window that asks you for metadata before the file hits the document library) was completely empty.

None of the fields were visible, not even the Content Type drop-down box!

Well, It seems that this problem is documented and known about, and there is a Microsoft Hotfix available (upon request).

(KB934253) The Web File Properties dialog box displays incorrect properties for a document that is saved in a Windows SharePoint Services 3.0 document library – https://support.microsoft.com/default.aspx?scid=kb;EN-US;934253

The solution to the issue above has been included in to a hotfix, which you can get the information from the following KB article:

(KB934790) Description of the Windows SharePoint Services 3.0 hotfix package: April 12, 2007 – https://support.microsoft.com/default.aspx?scid=kb;EN-US;934790

*files this one away for later*

Medals in the MSDN Forums .. what do they mean?

If you are a regular user of the MSDN Forums then you may (or may not?) that each user is granted a number of "medals" (they used to be stars, for the old timers).
Well, I’ve taken a sneak peak through the members list, and deduced that medals = points.
 
Exactly how many points you have depend on a lot of things, including posts, answers, "helpful" tags and how "popular" your threads are.
 
I have got the following list though:
  • 0 Medals – 0 points (1 or 2 posts)
  • 1 Medal – 10 points (usually 1-5 posts)
  • 2 Medals – 750 points (roughly 30 "answers")
  • 3 Medals – 2,000 points (roughly 150 "answers")
  • 4 Medals – 7,500 points (roughly 300 – 500 answers)
  • 5 Medals – 50,000 points (roughly 1000 answers)

To find your own account (and how many "points" you have) you can use the search page.

Number of certified SharePoint Developers increase by 500% in 12 months …

So demand slowly catches up with the market. Despite the credit crunch, and supposed "global economic recession", the SharePoint markets (and IT in general) continue to buck the trend.
 
 
There are now 2,360 MCTS WSS 3.0 Application Developers in the world. Last time I checked (about this time last year) there was less than 450! Not bad growth, I think.

The onet.xml order of execution (shown in 8 easy steps)

This post was prompted by a post in the MSDN Forums. Simply put … what order do things happen in the onet.xml?

This may seem like a trivial question .. but if you are writing custom features, it is important (to say vital) that you know whether or not your feature will activate before a list is created or after, or whether or not your files in modules will have been provisioned or not.

The order that I discovered is as follows:

  1. *<SiteFeatures> in onet.xml
  2. *Stapled Site Features (stapled using FeatureSiteTemplateAssociation)
  3. <WebFeature> in onet.xml
  4. Stapled Web Features (using FeatureSiteTemplateAssociation)
  5. <Lists> in onet.xml
  6. <Modules> in onet.xml

* note – obviously Site Features will only activate if you are creating a Site Collection, as opposed to a normal web site

How I worked this out

Well, you can copy the steps yourself, in 8 easy steps:

First off, create a new Custom Site Definition (a simple copy of STS). In my example, I called it MHSTS with a single configuration (#0) and created a WEBTEMP file to register it.

Second, create a new SPFeatureEventReceiver class (in an assembly from a class library). The code in the "FeatureActivated" should be something like this:

 
// this will let you check which feature is being activated .. so you can work out the order
string strFeature = properties.Definition.DisplayName;
 
// Add more code to check the SPWeb.Features, SPSite.Features, SPWeb.Lists and SPWeb.Files …
// this will tell you what has been created in the site, and what other features have been activated

Third, create 5 features (the scope is in [ ])

  • mhOnetSiteFeature [Site]
  • mhStapledSiteFeature [Site]
  • mhOnetWebFeature [WEB]
  • mhStapledWebFeature [WEB
  • mhManualWebFeature [WEB]

Each of these needs to be attached to the same SPFeatureEventReceiver class that we referenced earlier. This allows your debugger to work out which feature is activating … so each time your code steps into breakpoints, you can check the "Feature.Definition" properties and work out which feature is being activated.

Fourth step, create 2 more features which will act as Feature Staplers (using FeatureSiteTemplateAssociation elements).

  • mhWebStapler [Site]
    • Staples the mhStapledWebFeature to the MHSTS#0 template (when used to create a new Site or Site Collection).
  • mhSiteStapler [WebApplication]
    • Staples the mhStapledSiteFeature to the MHSTS#0 template (when used to create a Site Collection)

Fifth.. in your custom site definition (MHSTS) add the following features into the onet.xml:

  • <SiteFeatures>
    • mhOnetSiteFeature
    • mhWebStapler
  • <WebFeatures>
    • mhOnetWebFeature

Sixth; Install all of the features using STSADM, and (in Central Administration) activate the "mhSiteStapler" feature within a test Web Application.

Seventh; Within that Web Application, create a new Site Collection using your new site definition (MHSTS). This should fire off all of your features in the following order;

  1. mhOnetSiteFeature
  2. mhStapledSiteFeature
  3. mhOnetWebFeature
  4. mhStapledWebFeature

(you can check this by debugging your event handler, and setting a watch on the properties.Definition.DisplayName value)

Now .. if you are keeping a close eye on your debugging window, then you can also start to look at the state of the SPWeb itself. The main thing is that None of the Lists or pages will have been created … at ANY point in this process. This means that <Lists> and <Modules> will not be executed until ALL of your features have activated.

Eighth; Now you can double-check the theory … navigate to your new Site Collection .. and activate your remaining feature manually (mhManualWebFeature).

You should still have your debugging window open .. which should let you know that now (after the site is provisioned) all of the Lists and web pages are now accessible from Code.

Ok .. that’s great … errrr … so What ??

Well, this has quite a lot of importance for writing custom features. If you want to write features which automatically manipulate pages and lists then you are going to be a bit stuck, as the lists and pages won’t have been created until after your feature activates! (bummer eh? But what you gonna do?)

To get around this problem you really have 2 options:

  1. Make your own custom definition. Strip the lists out of the onet.xml, and create ListInstance Features. These can then be created during the feature activation stage, allowing them to be modified by other features that are being activated / stapled.
  2. Put some While Loops with Thread.Sleep() statements in your code, basically waiting until the lists have been created. This is not a very elegant solution but you don’t have much choice if you are trying to use staplers to modify out of the box definitions (or if you don’t want to touch the onet.xml).

Thats it … comments welcome as always 🙂

Hiding Menu Items and Site Settings in SharePoint using Features

I have used the example of hiding the "Theme" button in Site Settings (as for corporate intranets with strict branding, this is quite a comment request. I would like to show you though how to not only hide specific menus, but how to hide whole sections of the Site Settings menu and other SharePoint menus too!

This all hinges on the HideCustomAction element which you can use in Features.

There are actually some pretty good (MSDN) references:

HideCustomAction – element

Default Custom Action Locations & IDs

How to: Hide a Menu Item in the ECB from SharePoint List Items

John Holliday – SharePoint Custom Action Identifiers

And a cross post, for completion:

How to: Add Actions to the User Interface

So What about this HideCustomAction thing then?

Ahhh yes, got carried away with MSDN references …

First off, you will need a feature (scoped at the Site Level)

Feature File (Feature.xml, don’t forget to put your own GUID value in)

<?xml version="1.0" encoding="utf-8" ?>

<Feature Id="GUID" 
    Title="Hide UI Custom Actions"

    Description="This example shows how you can hide menus inside Windows SharePoint Services."

    Version="1.0.0.0"

    Scope="Site"

    xmlns="https://schemas.microsoft.com/sharepoint/">

  <ElementManifests>

    <ElementManifest Location="HideUICustomActions.xml" />

  </ElementManifests>

</Feature>

Elements File (HideUICustomActions.xml)

<?xml version="1.0" encoding="utf-8" ?>

<Elements xmlns="https://schemas.microsoft.com/sharepoint/">

<HideCustomAction

Id="HideThemeLink"

GroupId="Customization"

HideActionId="Theme"

Location="Microsoft.SharePoint.SiteSettings"

</HideCustomAction>

</Elements>

That will give you a feature which will remove the "Theme" links from the Site Settings menu. Now, let me quickly walk you through what the element file is composed of (hopefully the Feature file should be familiar enough).

HideCustomAction – This element defines a new custom action which we want to hide.

Id – This is optional (but recommended), used to identify this specific "hide" action from others.

GroupId – This is the GroupId value for the Custom Action that you want to hide.

HideActionId – This is the Id value in the Custom Action that you want to hide.

Location – This is the location value from the Custom Action that you want to hide.

If you want to find custom actions (and therefore access all of these values) then simply open up the Features folder (..\12\Templates\Features) and do a search for the phrase "CustomAction" in the files. You should find all of the CustomActions that are provided out of the box, and subsequently the ability to hide them if you want to! Exactly the same thing applies to Site Settings, so if you wanted to hide (for example) the "Themes" menu then you can find the Custom Action in the standard "SiteSettings" feature.

Enjoy, and let me know how you get on!

[Code Update] SDK and Microsoft Press Both Wrong?? Custom Fields in the schema.xml

UPDATE
For those who missed my original article (SDK and Microsoft Press Both Wrong?? Custom Fields in the schema.xml) , you can find it here!
 
I finally got round to posting the code that goes with this article ..
 
Please find below the details on code for an SPFeatureReceiver class, that will automatically push down changes from your Content Type Features
(you will need to attach the code to your feature using the ReceiverAssembly and ReceiverClass attributes in your feature.
 
The code needs to be created as a Class in a Class Library, with Microsoft.SharePoint.dll added as a reference, and then added as a Using statement in the class itself.
 
Code Sample
 
EDIT – Thanks to a quick spot from Nick’s SharePoint blog, I have updated the code to match! Thanks Nick!
 

class

ContentTypeInstaller : SPFeatureReceiver

{

// CODE DESCRIPTION

// —————-

/*

* This is a feature handler which should be paired
* with a content type feature.
*
* We have identified a problem with Content Types,
* where the content type site columns are not pushed
* down to custom list definitions, until they are
* "modified" first.
*
* This feature will interrogate all of the associated
* xml files that are attached to the feature.
* Once found, it will "modify" each of the custom fields
* that are referenced.
*
* This should allow list definitions to use the content
* type columns, without declaring them in the schema.xml
*/

public override void FeatureActivated(SPFeatureReceiverProperties properties)

{

using(SPSite site = (SPSite)properties.Feature.Parent)

{
SPWeb web = site.OpenWeb(

"");

// loop through each of the elements in the feature

foreach (SPElementDefinition element in properties.Definition.GetElementDefinitions(CultureInfo.CurrentCulture))

{

#region

Loop through feature elements

 

// retrieve the xml content for the feature element

XmlNode content = element.XmlDefinition;

// only continue if the element is a content type definition

if (content.Name == "ContentType")

{

// grab a new Content Type object

string strCTypeID = content.Attributes["ID"].Value;

SPContentType cType = web.ContentTypes[

new SPContentTypeId(strCTypeID)];

#region

Get FieldRef Order from Content type

// grab the original order, we will need this later

string[] fieldOrder = new string[cType.FieldLinks.Count];

int x = 0;

foreach (SPFieldLink fieldlink in cType.FieldLinks)

{
fieldOrder[x] = fieldlink.Name;
x++;
}

#endregion
#region

Add new columns to the content type

// loop through each sub-node in the Content Type file

foreach (XmlNode node in content.ChildNodes)

{

#region

loop through sub nodes

// only continue for

// the FieldRefs collection

if (node.Name == "FieldRefs")

{

foreach (XmlNode fieldRef in node.ChildNodes)

{

#region

Loop through FieldRefs

// only apply for FieldRef tags

if (fieldRef.Name == "FieldRef")

{

// get the FieldID and use it to

// retrieve the SPField object

string fieldID = fieldRef.Attributes["ID"].Value;

//SPFieldLink fieldLink = cType.FieldLinks[new Guid(fieldID)];

SPField field = cType.Fields[

new Guid(fieldID)];

// first we need to remove the fieldref

cType.FieldLinks.Delete(

new Guid(fieldID));

// and save, pushing this change down

cType.Update(

true);

// NOTE – this will NOT delete any content

// in existing lists!

// now add the field back in again

cType.FieldLinks.Add(

new SPFieldLink(field));

// and call an update, pushing down all changes

cType.Update(

true);

// NOTE – this is what adds the column to those

// lists who don’t already have it.

}

#endregion

}
 
}

#endregion

}

#endregion
#region

Apply changes

// reset the field order

// it is possible that adding and

// removing fields would have

// affected this

cType.FieldLinks.Reorder(fieldOrder);

// force update of the content type,

// pushing down to children

cType.Update(

true);

#endregion

}

#endregion

}
}
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)

{
}

public override void FeatureInstalled(SPFeatureReceiverProperties properties)

{
}

public override void FeatureUninstalling(SPFeatureReceiverProperties properties)

{
}
}
Enjoy, and happy coding!

Quick Tip – Site Collection Relative URLs in MOSS 2007

This is a nice quick one…

 

If you try to use normal relative URL paths, then you may run into a spot of bother when trying to call a URL path that is relative to the Site Collection, especially when you have multiple Site Collections in your web application:

 

https://RootSite/sites/ThisSite/

 

How do you make an URL that is relative to the "ThisSite" site collection?

 

Well, you could hard-code the URL prefix, but that doesn’t really sit very well for most implementations, especially when using multiple-access mappings, you might have multiple URLs for the same site collection!

 

Alternatively, you can use the SPUrl function:

 

Code Sample:

This example code will take you to the Site Settings page for the current Site Collection.

 

<a runat="server" href="<% SPUrl:~SiteCollection\_layouts\settings.aspx %>">Go to Top Level Site Settings</a>

 

Note – You must use the runat="server" attribute, otherwise it will send the literal text of <% SPUrl …%> instead of the desired relative URL!

Also, you will need to add a reference to the Microsoft Publishing Assembly in the header tags; for that, see this blog

 

Publishing Header Tags

Thanks go to Hannah Scott (and her subsequent source) for this blog post.

 

<%@ Register Tagprefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="PublishingNavigation" Namespace="Microsoft.SharePoint.Publishing.Navigation" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="PublishingVariations" TagName="VariationsLabelMenu" src="~/_controltemplates/VariationsLabelMenu.ascx" %>
<%@ Register Tagprefix="PublishingConsole" TagName="Console" src="~/_controltemplates/PublishingConsole.ascx" %>
<%@ Register TagPrefix="PublishingSiteAction" TagName="SiteActionMenu" src="~/_controltemplates/PublishingActionMenu.ascx" %>

« Older Entries