In my previous post on how to design and deploy a custom SharePoint master page I explained how to create an own branded master page and how to deploy that master page to your SharePoint site. Now I first stumbled upon the problem that the master page doesn’t get automatically applied to newly created (sub)sites, and secondly I received a comment from a reader describing the exact same problem. So I decided to write my findings and solution to the open world. Here it is.

The problem is very simple: You create your own master page, you apply your master page to all sites and it does that, nice, but when you create a new site you just get the old v4.master on that site and not your custom master page. Everyone with this problem, if you followed my previous post or not, can follow this solution. It doesn’t even matter what SharePoint version you’re using.

When I Googled for a bit, I found out that there is no out-of-the-box-solution for this and a lot of guys use Visual Studio to solve this. You could program an event receiver that sets your master page to every new site. Or you could program a Feature that holds your master page and apply that feature to a site definition. But I find that a bit heavy and not very flexible solution. So I played around a bit and found a solution myself without the use of Visual Studio or programming at all.

Here we go:

You should have your master page deployed to the main sites master pages gallery. You should see your masterpage when going to http:///_catalogs/masterpage/Forms/AllItems.aspx

(Yes I’m using Chrome)

Now create a new blank Team Site using whatever name you like (you won’t see it in the end).

Now go to Site Settings and click “Save site as template” under Site Actions and go save the site as a template.

On the next screen click on the link to go to the solution gallery. In the solution gallery just click on the name of your just created template to download it. Save it somewhere where you can easily find it.

Did you know that as WSP is in fact just a CAB file? That’s why we need a tool that can extract and recreate our wsp. I used the tool ACDZip, which has a free x-days trial. You can get it here http://acdzip.findmysoft.com/. A reader has also mentioned the tool IZArc, which is free. A great alternative.

Now find your file and rename it from TemplateWithoutMyMasterPage.wsp to TemplateWithoutMyMasterPage.cab, you should see your icon change.

Open the file with ACDZip and extract the ONet.xml file from theWebTemplateONet.xml to a location where you can edit it easily.

 

Now edit the following line of code

masterpage/v4.master" ThemedCssFolderUrl="">

to

<Configuration ID="0" Name="Default" MasterUrl="/_catalogs/masterpage/mymaster.master" ThemedCssFolderUrl="">

Don’t forget the slash before _catalogs. This will ensure that the mymaster.master from the master page gallery on the root of the site gets used.

Save the file and put it back in the archive (drag and drop), it will automatically rebuild the archive. Rename the file back to a wsp file, I also used another file name.

Upload the file into the Solution Gallery by using the Upload Solution button on the ribbon of Solutions.

 

In the next screen you must upgrade the solution.

You can now use the new template to create a site with your masterpage. Go ahead and try. Site Actions, New Site, scroll down and select the template, fill in a title and url and create the site.

You should now have your master page automatically applied.

Ok, another day, another problem!

I did create a few usercontrols which work together quite nicely. And after I wrapped an updatepanel around them, the ajax seems to work just fine.

However, once a validator inside the panel gets triggered the whole page freezes and it looks like javascript isn’t working anymore. I googled (cause bing just sucks) the problem and found very less about this problem, so when I finally found a solution I decided to share it with all of you having the same problem.

This is the problem:

When a validator is loaded on the page it creates a bit of javascript to support the clientside validation. When you place a validator inside an usercontrol that isn´t visible by default, and this usercontrol is in an updatepanel, it does not create that javascript properly.

This is the solution:

Outside the updatepanel, I did above, create a dummy validator with a dummy textbox using a dummy validationgroup like so:

<%--dummy validator to make ajax validation possible--%>
<asp:RequiredFieldValidator runat="server" CssClass="hidden" ControlToValidate="dummyTextBox" ValidationGroup="dummy"></asp:RequiredFieldValidator>
<asp:TextBox runat="server" ID="dummyTextBox" CssClass="hidden"></asp:TextBox>

The CssClass “hidden” corresponds with the following CSS:

<style>
.hidden {
  display: none;
}
</style>

As you already might understand, the dummy validator now creates the proper javascript that the other ‘real’ validators can use.

Hope this also works for you. Also if you found another, maybe neater solution, please inform me…

This post explains how to create a new master page with a custom stylesheet and some custom images. We do this with the following steps:

  • Install  master page picker
  • Create a new master page
  • Create a stylesheet
  • Create an images folder
  • Prepare master page
  • Edit master page
  • Deploy master page

Install master page picker
First we need a feature that is going to deploy our new master page to the sitecollections. Because the Foundation version doesn’t have a master page selector, I used this fantastic very-easy-to-install feature:

http://blog.thedesigndrifter.com/?p=1112

It also enables us to change the master page from within the sitecollection properties.

Create new master page
The next thing to do is to copy the existing master page.

From within Sharepoint Designer you can go to the Master Pages section and copy the v4.master in the same folder and rename it to whatever you want. I generally choose the clients name.

Tip! In the folder you also find a default.master and a minimal.master file. The default.master is the old 2007 master page. The minimal.master is a master page with only the base controls, no layout whatsoever. I always use a copy of the v4 master page and not the minimalistic one, because it’s more efficient to tweak the existing master page instead of creating a new one based on the minimal.master.

Create stylesheet
We also want to apply our own custom stylesheet to the masterpage so let’s create a stylesheet.

Go to the folder Common FilesMicrosoft SharedWeb Server Extensions14TEMPLATELAYOUTS1033STYLESThemable and create an empty mystyle.css file.

Create an images folder
Now create a new folder inside the Common FilesMicrosoft SharedWeb Server Extensions14TEMPLATEIMAGES named myimages.

Tip! We create a new folder to keep our images separated so they won´t be mixed up with the ones that come with Sharepoint. Another reason is that future editing would be easier because our images are easier to find, naturally.


Prepare master page
We are going to prepare our master page to use our custom stylesheet.

Go to Sharepoint Designer and open the edit view for the mymaster.master and click the code view. Now insert the line

<sharepoint:CSSRegistration Name="/_layouts/1033/Styles/Themable/mystyle.css" After="corev4.css" runat="server"/>

just before the </head> tag. Then save the file and accept the warning for the customized page. The After=”corev4.css” ensures that the style elements in our custom stylesheet override the default css.

Edit master page
In this example I’m going to change the color of the selected ribbon header from it’s dark grey to a solid blue color. Now browse to the sharepoint page and check the html source to find out which class is being used by the selected ribbon header. In my example the class for the header of the selected ribbon is .ms-cui-tts > .ms-cui-tt-s > .ms-cui-tt-a > .ms-cui-tt-span (do you wanna know how to find these style classes easily? Check the next tip). Now we go to our mystyle.css and create this class here and give it our blue color style.

.ms-cui-tts > .ms-cui-tt-s > .ms-cui-tt-a > .ms-cui-tt-span {
    color: #0069b0;
}

Save the CSS file.

Tip! I highly recommend the usage of developer tools for your browser. I always use Google Chrome with the built-in developer tools. They are very accurate and easy to use. In Chrome, rightclick the item you wish to know the style from and click Inspect element. At the right side of the window you find matched CSS Rules. You can copy a CSS rule and paste it in your mystyle.css and tweak it the way you want it.

Deploy master page
Now we wan’t to test our new master page. Before we can do that we need to select it with the master page picker we installed in the first step.

Go to your sitecollection  settings and go to the Master Page Picker which can be found under the Look and Feel category. Our new master page will be displayed in the list, select it and click ok to apply our new master page to the sitecollection.

Now go to the site and check our result. The selected ribbon header text color is now blue, thumbs up!

Images
When you use images in your stylesheet or master page then put the image in the myimages folder and reference it with the following url

/_layouts/images/myimages/.png

Get classnames for hover items
Tip! It may be difficult to find the hover style of some elements. In Google Chrome you can do this by rightclicking the item and leave your cursor where it is and browse the menu with up and down keys on the keyboard, then select Inspect element and press enter. At the rightside of the window you now find the classnames for the hover of the item.

UPDATE:

Some readers ran into  an error when using their new master page. The error reads The expression prefix ‘SPUrl’ was not recognized. Please correct the prefix or register the prefix in the section of configuration. This is because SharePoint Foundation doesn’t support the SPUrl prefix out of the box.

There problem is with this line of code in the master page:
<sharepoint:CSSRegistration
Name="<% $SPUrl:~sitecollection/_layouts/1033/Styles/Themable/mystyle.css %>"
After="corev4.css" runat="server"/>

A simple solution is to just simplify the code to this
<sharepoint:CSSRegistration
Name="/_layouts/1033/Styles/Themable/mystyle.css"
After="corev4.css" runat="server"/>

A more time-consuming but maybe better way is this: http://buyevich.blogspot.com/2010/10/spurl-for-wss-30-or-sharepoint.html

ANOTHER UPDATE:

If you want your master page to automatically get applied with every new site you create, please read this post:

http://jeffreypaarhuis.wordpress.com/2011/10/21/your-master-page-automatically-on-newly-created-subsites/

I have done a few years of sharepoint developing now. When I began to write small webparts in Sharepoint I was very enthusiastic about the features of the Sharepoint framework and thought it was as easy and solid as writing ASP.NET. Now I know better, developing Sharepoint masterpages, webparts, workflows, etc. is not all beer and skitties. Also when you haven’t encountered any troubles yet, you will get to it! So don’t waste your effort on developing the wrong way.

Here are some points of what I consider “Sharepoint 2007 developing and designing: Best practices”

Developing

  • Sharepoint Designer is crap. It´s instable and unfinished. So, try to avoid it where possible. Try to use Visual Studio instead.
  • Use Microsofts Visual Studio extensions for WSS. Always use the latest version: CTP, Beta or RC? It all did work for me, so just use it. Here’s a link to the latest version: http://www.microsoft.com/downloads/details.aspx?familyid=FB9D4B85-DA2A-432E-91FB-D505199C49F6&displaylang=en
  • Always use embedded resources, never copy your images, css or javascripts to a folder in the 12 hive. An article on how to use embedded resources can be found here: http://aspalliance.com/726
  • Important! Before developing get you’re Functional Requirements straightened out. It is very important to know if you are going to create an application that is going to be updated (frequently) AND/OR needs to work in other environments, for example an ASP.NET environment AND/OR if it needs to be compatible with other/future versions of sharepoint. If it does, and the most cases will be, then mind the following points
    • Try to use the Sharepoint controls in the Microsoft.SharePoint.WebControls namespace as less as possible. Some controls contain some bugs and off course this way you can easily use the same application in an ASP.NET webpage or implement it in another version of Sharepoint.
    • Create a tiered application. Create for every tier a new project. Create a SharepointData project which handles all data between your project and sharepoint, like users, groups, properties, lists, libraries
    • Try to avoid the use of the IWebEditable interface and the EditorPart class. Handle configurations in a second webpart where only an admin can touch it.

Designing (Master pages)

  • Again. SharePoint Designer is crap, but with designing a master page we cannot evade the piece of beta-software Microsoft has shipped with SharePoint. So, good luck with the frustrations.
  • A disadvantage of Sharepoint is that you cannot deploy a masterpage centralized, you have to do a copy-paste job to create the same look on all sites. Fortunately I have a feature for globally deploying masterpages. In a few steps you can build your own feature, it’s very easy. Here is the article: http://www.dontpapanic.com/blog/?p=36
  • WARNING! NEVER EVER overwrite the default masterpage located at 12TEMPLATEGLOBAL but use it only to copy, it would be wise to make a backup of it.
  • I discourage the use of a minimalistic masterpage.  Use the default sharepoint masterpage located at <%System Drive%>Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TEMPLATEGLOBAL as you’re base masterpage. Generally implementing a new design will take less effort when tweaking the default masterpage, than creating a new one based on a minimalistic base masterpage.

These are the important points that have been in my head for a while, finally they got stored in HTML and are they readable for others. Did I miss something or do you have any tips yourself? I ‘ll be glad to know.

I thought there was a simple method like MoveTo or CopyTo to move one listitem to another sharepoint list. MoveTo doesn’t exist and CopyTo didn’t behave like expected.

So I started looking on the internet and what I found was that the SPImport and SPExport classes did the job. There is a 6-part writeup at http://blogs.technet.com/stefan_gossner/archive/2007/08/29/deep-dive-into-the-sharepoint-content-deployment-and-migration-api-part-2.aspx. This however is an overkill for what I tried to accomplish.

Finally I made a function that copies all the fields and attachments one by one to another listitem.

Here is the code:

private void moveListItem(String destinationListName)

        {

            //creating new listitem at the destination list

            SPWeb web = workflowProperties.Web;

            SPList destList = web.Lists[destinationListName];

            SPListItem destListItem = destList.Items.Add();

            SPListItem sourceListItem = workflowProperties.Item;

            //transfer fields from sourceListItem to destListItem

            foreach (SPField field in sourceListItem.Fields)

            {

                if (!field.ReadOnlyField && !field.Hidden && field.InternalName != "Attachments")

                {

                    if (destListItem.Fields.ContainsField(field.InternalName))

                    {

                        destListItem[field.InternalName] = sourceListItem[field.InternalName];

                    }

                }

            }

            //move all attachments one by one to the new listitem

            SPAttachmentCollection attColl = sourceListItem.Attachments;

            foreach (string fileName in attColl)

            {

                SPFile attFile = sourceListItem.ParentList.ParentWeb.GetFile(sourceListItem.Attachments.UrlPrefix + fileName);

                StreamReader fsReader = new StreamReader(attFile.OpenBinaryStream());

                Stream fStream = fsReader.BaseStream;

                byte[] contents = new byte[fStream.Length];

                fStream.Read(contents, 0, (int)fStream.Length);

                fStream.Close();

                fStream.Dispose();

                destListItem.Attachments.Add(fileName, contents);

            }

            destListItem.Update();

            sourceListItem.Delete();

        }
private void moveListItem(String destinationListName)
{
//creating new listitem at the destination list
SPWeb web = workflowProperties.Web;
SPList destList = web.Lists[destinationListName];
SPListItem destListItem = destList.Items.Add();
SPListItem sourceListItem = workflowProperties.Item;
//transfer fields from sourceListItem to destListItem
foreach (SPField field in sourceListItem.Fields)
{
if (!field.ReadOnlyField && !field.Hidden && field.InternalName != “Attachments”)
{
if (destListItem.Fields.ContainsField(field.InternalName))
{
destListItem[field.InternalName] = sourceListItem[field.InternalName];
}
}
}
//move all attachments one by one to the new listitem
SPAttachmentCollection attColl = sourceListItem.Attachments;
foreach (string fileName in attColl)
{
SPFile attFile = sourceListItem.ParentList.ParentWeb.GetFile(sourceListItem.Attachments.UrlPrefix + fileName);
StreamReader fsReader = new StreamReader(attFile.OpenBinaryStream());
Stream fStream = fsReader.BaseStream;
byte[] contents = new byte[fStream.Length];
fStream.Read(contents, 0, (int)fStream.Length);
fStream.Close();
fStream.Dispose();
destListItem.Attachments.Add(fileName, contents);
}
destListItem.Update();
sourceListItem.Delete();
}