I needed to output a custom Hyperlink/Image site column in the CQWP the other day.
Remembering Heather Solomon’s wonderful blog (http://www.heathersolomon.com/blog/articles/CustomItemStyle.aspx), I found that the column type for this was URL.
I exported the web part definition
Updating the CommonViewFields node to have my custom column
LinkedImage, URL
Imported the updated definition
I kept getting a query error in the UI, and error executing making the cross list query in the log
After a number of attempts, I reuploaded the definition without the space between the comma and the field type and it worked from:
Caused Error:
LinkedImage, URL
Worked Fine:
LinkedImage,URL
Here’s a quick hit, more of a reminder to myself.
When trying to connect a BDC LOB to a WCF Web Service you need to ensure that the endpoint using basicHttpBinding.
<system.serviceModel>
<services>
<service name=”Service1″>
<endpoint address=”" binding=”basicHttpBinding” contract=”IService1″/>
…
An error occurs on import of the application definition file:
- Event ID: 5191
- Runtime Error in method PackageImporter::Import of type System.ArgumentException. The exception was System.ArgumentException: Could not load Type described by TypeDescriptor’s TypeName …
Recently I had a client ask me how to notify a Discussion’s originator when a reply is made to their discussion using a Workflow created in SharePoint Designer.
They had posted this question on a few forums and had been told that it could not be done using a workflow created in SharePoint Designer.
Not really be a big fan of designer, I noted to him that it could be done by using either a List Event Handler or a workflow created in Visual Studio. When told this his optimistic demeanor faded (he’s a not a developer). Seeing this I told him I’d look into designer … even though it would end up taking food off of my kid’s plate in the long run.
I’ve never really looked into how the Discussion List was created before so I had to start by investigating that.
First I looked at the advanced settings of the list and determined that there are two content types that are used create items in the list. The first is the Discussion, and the second is the Message (this is the reply).
I then created a simple console application it turns out that each Discussion is created as a Folder the replies are created as items in the folder.
Great now I know I just need to determine who created the parent folder associated to a newly created reply.
As it turns out it’s actually quite easy to do in designer. I won’t go into the details of creating a workflow in designer just the steps to accomplish this.
- Create a workflow on the discussion that starts on item created
- Add a condition that checks if the ContentType of the current item is “Message” (there are two content types used in the discussion: Discussion and Message (the reply) )
- Add an action to “Set a Workflow Variable”
- Create a new variable called “DiscussionID” which is the “List Item ID” type
- Set the DiscussionID to the Current Item’s “Parent Folder ID”
- Add a second action to “Set a Workflow Variable”
- Create a new variable called “DiscussionCreatedBy” of type “string”
- Set the DiscussionCreatedBy variable to a value from a look up
- The look up should be set to Query the discussion list, and return the “Created By” field
- The filter should be set to filter [Discussion List]:ID equal to DiscussionID
- Create an Email action and set the To property to be the DiscussionCreatedBy variable
And that’s it … I’ll try and add some pics later
Today in my dev environment I ran into this interesting little issue:
- Event ID: 4138
- Description: An index corruption was detected in component IndexDirectory3 in catalog AnchorProject. Stack trace is .Component: f7815373-e2a4-48de-ad74-5dc85d31cc4a
No SharePoint site (including the Central Admin) would work.
To get around it you need to uninstall/reinstall the Office SharePoint Search Service like so:
- Run: stsadm -o osearch -action stop
- Confirm that you want to delete all data in the index for all SSPs.
- Run: stsadm -o search -action start -role [Index|Query|IndexQuery]
After that everything worked like a charm. Luckily it wasn’t a production environment.
I just ran into this problem, today. When sending an email with SPUtility.SendMail it was truncating my email body.
After a long search a ran into this posting:
http://social.msdn.microsoft.com/Forums/en-US/sharepointdevelopment/thread/3b7627e4-886d-48db-8c54-13fdbaa6dcb6/
The answer is that SPUtility.SendEmail puts a cap on the length of single line.
I was generating the body of my by using a xml/xsl transformation. To get around this I made use of the XmlWriterSettings object as follows:
…
System.Xml.XmlWriterSettings writerSettings = new System.Xml.XmlWriterSettings();
writerSettings.OmitXmlDeclaration = true;
writerSettings.NewLineHandling = System.Xml.NewLineHandling.Replace;
writerSettings.NewLineOnAttributes = true;
writerSettings.Indent = true;
using (System.Xml.XmlWriter resultsWriter = System.Xml.XmlTextWriter.Create(transformationResults, writerSettings))
{
xslTransform.Transform(itemXml.CreateNavigator(), argumentsList, resultsWriter);
}
…
The key lines are the ones for NewLineHandling and NewLineOnAttributes (to try and ensure it doesn’t get cut off).
Corey Roth has a great solution for extending the out of the box search results to include linking directly to a document’s site, document library, folder, properties, and
version history. Here is the link to his blog explaining this:
http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2008/10/31/introducing-document-link-handler-for-moss-2007-enterprise-search.aspx
and can be downloaded at:
http://www.codeplex.com/MOSSSearchLinks
But what if you are searching external SharePoint sources, for example separate MOSS, WSS 3.0 or SPS 2003 farms? I recently came off a project where they wanted that. I will out line the high-level details of how I implemented it below:
- I started with a solution using Document Link Handler as a starting point.
- As the farms I wanted to connect to were all over the world I used web services to pull web and list information to generate the links.
- On the request I interrogate the URL to determine the actual site (web) the item is from and use that to dynamically generate the URL to the proper web service
- I then call the web services on that server, by impersonating a service account (that has read access to the sites … I used the same account(s) I use to crawl them). I stored the credentials in the SharePoint SSO.
- After getting the required properties, I generate the required links and return the requested link.
- One caveat in generating the links was that I am also returning results from SPS 2003 which has a slightly different URL format. To get around this I created an XML file where I define the base URLs for SPS 2003, WSS 3.0 and MOSS sites. When generating the links I use that file to check if the result is a “SPS 2003″ result and format it accordingly.
This is the third post in this series, in this post we’ll review how to update the sptheme.xml file using a solution. We will also be picking up from the first and second posts in the series, please review that posting before continuing with this one.
I found this originally on Keith Dahlby’s blog at: Theme-amajig: Updating SPTHEMES.XML Through a Feature
Please refer to his blog for specific details. In this blog I’ll discuss how I extended that solution to allow for updating the SPTheme.xml file across many languages.
I extended the solution as a fair number of my clients use SharePoint in multiple languages.
The first step I made in extending the solution was to update XML structure of the custom SPTheme.xml file (step 2 in Keith’s blog).
I wrap each SPTheme node with a “Language” node that exposes an attribute “code” which coresponds to a deployed language. I then wrap all of that in a “Languages” node.
<?xml version="1.0" encoding="utf-8"?>
<Languages>
<Language code="1033">
<SPThemes xmlns="http://tempuri.org/SPThemes.xsd">
<Templates>
<TemplateID>SPHOLS</TemplateID>
<DisplayName>SharePoint Hands-On Labs</DisplayName>
<Description>A glorious theme created for the SharePoint Hands-On Labs.</Description>
<Thumbnail>images/SPHOLS/thSPHOLS.gif</Thumbnail>
<Preview>images/SPHOLS/thSPHOLS.gif</Preview>
</Templates>
<Templates>
<TemplateID>SPHOLSX</TemplateID>
<DisplayName>SharePoint Hands-On Labs X</DisplayName>
<Description>An XTRA glorious theme created for the SharePoint Hands-On Labs.</Description>
<Thumbnail>images/SPHOLS/thSPHOLS.gif</Thumbnail>
<Preview>images/SPHOLS/thSPHOLS.gif</Preview>
</Templates>
</SPThemes>
</Language>
<Language code="1036">
<SPThemes xmlns="http://tempuri.org/SPThemes.xsd">
<Templates>
<TemplateID>SPHOLS</TemplateID>
<DisplayName>Laboratoires sur le tas de SharePoint</DisplayName>
<Description>Un thème glorieux créé pour les laboratoires sur le tas de SharePoint.</Description>
<Thumbnail>images/SPHOLS/thSPHOLS_fr.gif</Thumbnail>
<Preview>images/SPHOLS/thSPHOLS_fr.gif</Preview>
</Templates>
<Templates>
<TemplateID>SPHOLSX</TemplateID>
<DisplayName>Laboratoires sur le tas de SharePoint X</DisplayName>
<Description>Un thème glorieux de XTRA créé pour les laboratoires sur le tas de SharePoint.</Description>
<Thumbnail>images/SPHOLS/thSPHOLS_fr.gif</Thumbnail>
<Preview>images/SPHOLS/thSPHOLS_fr.gif</Preview>
</Templates>
</SPThemes>
</Language>
</Languages>
The next step is to update the “FeatureThemeJobs” class to support multiple languages.
Update the const SPTHEMES_PATH to allow for the language code to inserted:
private const string SPTHEMES_PATH = @"TEMPLATE\LAYOUTS\{0}\SPTHEMES.XML";
Next we need to update the DoMerge method to take in the list of nodes to update.
public void DoMerge(string pathToSPThemes, XmlNodeList mergeNodes)
{
if (string.IsNullOrEmpty(pathToSPThemes) || mergeNodes == null)
return;
try
{
XmlDocument docThemes = new XmlDocument();
docThemes.Load(pathToSPThemes);
string nsThemes = docThemes.DocumentElement.NamespaceURI;
XPathNavigator navThemes = docThemes.CreateNavigator();
XmlNamespaceManager mgrThemes = new XmlNamespaceManager(navThemes.NameTable);
mgrThemes.AddNamespace("t", nsThemes);
bool shouldSave = false;
foreach (XmlNode mergeNode in mergeNodes)
{
// And use XPath again to find an existing theme with the same TemplateID.
try
{
string xpath = string.Format("/t:SPThemes/t:Templates[t:TemplateID = '{0}']“, mergeNode.InnerText);
XmlNode node = docThemes.SelectSingleNode(xpath, mgrThemes);
// If we’re deleting and we find the theme node, get rid of it.
if (_delete)
{
if (node != null)
node.ParentNode.RemoveChild(node);
}
// Otherwise create a Templates element for the new theme. I’m assuming this part could be cleaned up with a deeper understanding of the XML object model — help! For now I just copy the source XML into a new node in the destination document. I iterate through the new children and RemoveAllAttributes() to eliminate extra xmlns attributes that I can’t figure out how to prevent.
else
{
XmlNode newNodeParent = mergeNode.ParentNode;
XmlElement toInsert = docThemes.CreateElement(newNodeParent.Name, nsThemes);
toInsert.InnerXml = newNodeParent.InnerXml;
foreach (XmlElement xe in toInsert.ChildNodes)
xe.RemoveAllAttributes();
if (node == null)
docThemes.DocumentElement.AppendChild(toInsert);
else
node.ParentNode.ReplaceChild(toInsert, node);
}
// If we get to this point, everything went fine and we should save our results.
shouldSave = true;
}
catch (Exception ex)
{
Trace.WriteLine(”Error merging theme ” + mergeNode.InnerText);
Trace.WriteLine(ex);
}
} // foreach mergeNode
if (shouldSave)
docThemes.Save(pathToSPThemes);
}
catch (Exception ex)
{
Trace.WriteLine(”Failed to merge themes”);
Trace.WriteLine(ex);
throw;
}
}
Next we need to create a method that parses our SPThemes.xml file to get the Definitions per language.
public void DoMerge( string pathToMerge)
{
try
{
XmlDocument docMerge = new XmlDocument();
docMerge.Load(pathToMerge);
XPathNavigator navMerge = docMerge.CreateNavigator();
XmlNamespaceManager mgrMerge = new XmlNamespaceManager(navMerge.NameTable);
mgrMerge.AddNamespace("t", docMerge.DocumentElement.NamespaceURI);
// Now we use XPath to retrieve the Language nodes to iterate over
XmlNodeList languageNodes = docMerge.SelectNodes("/t:Languages/t:Language", mgrMerge);
if (languageNodes == null || languageNodes.Count == 0)
return;
foreach (XmlNode language in languageNodes)
{
// Get the language code
string languageCode = language.Attributes["code"].Value;
// Build the path to the SPThemes.xml file to update
string pathToSPThemes = SPUtility.GetGenericSetupPath( string.Format(SPTHEMES_PATH, languageCode) );
// Now we use XPath to retrieve and iterate over a list of the themes in our feature.
XmlNodeList mergeNodes = language.SelectNodes(”/t:SPThemes/t:Templates/t:TemplateID”, mgrMerge);
DoMerge(pathToSPThemes, mergeNodes);
}
}
catch (Exception ex)
{
Trace.WriteLine(”Failed to merge themes”);
Trace.WriteLine(ex);
throw;
}
}
And Finally we’ll need to update the Execute method to call our new DoMerge function.
public override void Execute(Guid targetInstanceId)
{
SPFeatureDefinition fDef = Farm.FeatureDefinitions[_featureID];
if (fDef != null)
DoMerge(Path.Combine(fDef.RootDirectory, _themesFile));
}
Well that’s that.
This is the second post in this series, in this post we’ll review how to deploy the theme using a solution. We will also be picking up from the first post in the series on How to make Theme Updates Easier, please review that posting before continuing with this one.
The first thing to do when creating a new theme, is to copy an existing one, that closely resembles the goal of your end state.
- Copy an existing theme folder from C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\THEMES, for my purposes the classic theme was close to what I wanted so I used it.
- I then rename the folder to “Custom.Theme”, and the theme CSS file to “CustomTheme.CSS”.
This will serve as the folder that we’ll deploy inside the layouts directory.
- Next I create a second folder, “Custom” and move the theme’s INF file from the “Custom.Theme” folder to this folder.
- Rename the INF file to “Custom.INF”
- Edit the INF file, replace all instances of the existing title with the new title
- Create a new file theme.css in the “Custom” folder, and add the import sytax (as describe in the first posting in this series, see above)
- The next thing I do is deploy it and apply it to a site (see below). From there I can make changes to my theme, copy the updates into the theme folder and instantly see my changes in affect.
Here are the steps I use to manually deploy my custom theme
- Define our feature file
Below is our feature file xml, in the next posting we’ll be updating this to have a feature receiver to update SPThemes.xml file automatically.
<Feature xmlns="http://schemas.microsoft.com/sharepoint/"
Id="{992B54B6-83EE-4b89-82FD-AD54E2AD7642}"
Title="My Custom Theme"
Version="1.0.0.0"
Scope="Farm"
Hidden="FALSE"
DefaultResourceFile="core"
>
</Feature>
- Define the Manifest file>
Below is an an example manifest.
<Solution xmlns="http://schemas.microsoft.com/sharepoint/"
SolutionId="{5AD3D704-5565-42c7-809E-A217B2DED2E7}"
ResetWebServer="True"
>
<FeatureManifests>
<FeatureManifest Location="Custom.Theme\feature.xml"/>
</FeatureManifests>
<TemplateFiles>
<!-- Begin Custom.Theme Files-->
<TemplateFile Location="THEMES\CustomTheme\CustomTheme.INF"/>
<TemplateFile Location="THEMES\CustomTheme\Customtheme.css"/>
<TemplateFile Location="Images\thCustomTheme.jpg"/>
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\alldayOver_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\allday_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\Background-Gradient.jpg" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\calnumbttnfocus_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\calnumbttntoday_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\calnumbttn_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\cnspgrdn_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\cnsugrdn_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\Corporate-Intranet.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\CreateGraphic_classic.jpg" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\filedialogselected_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\formtitlegrad_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\Konnected-Logo.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\CustomTheme.css" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\linksectiongrad_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\listheadergrad_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\menubuttonhover_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\mgradlargertl_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\mgradlarge_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\mgradrtl_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\mgrad_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\navBullet_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\Navshape_classic.jpg" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\pagebackgrad_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\pageTitleBKGD_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\partgrad_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\quickLaunchHeader_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\rtebnhov_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\rtebnsel_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\selectednavbullet_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\selectednav_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\settingsbkg_classic.jpg" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\SettingsGraphic_classic.jpg" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\siteactionsmenugrad_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\siteactionsmenuhovergrad_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\siteTitleBKGD_classic.jpg" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\templatepickerselected_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\templatepickerunselected_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\toolgrad_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\Top-Banner.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\topnavhover_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\topnavselected_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\topnavunselected_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\Topshape_classic.jpg" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\viewheadergrad_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\viewselectorgrad_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\weekbox_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\wpqamh_classic.gif" />
<TemplateFile Location="LAYOUTS\1033\STYLES\Custom.Theme\wpqam_classic.gif" />
<!-- End Custom.Theme Files-->
</TemplateFiles>
</Solution>
- Define the Solution Package ddf
Below is an excert from my ddf file.
.OPTION EXPLICIT
.Set CabinetNameTemplate=Custom.Theme.wsp
.set DiskDirectoryTemplate=CDROM
.Set CompressionType=MSZIP
.Set UniqueFiles=off
.Set Cabinet=on
.Set DiskDirectory1=.
;*** Solution manifest
manifest.xml
;*** add files for Custom.Theme feature
.Set DestinationDir=Custom.Theme
..\RootFiles\TEMPLATE\FEATURES\Custom.Theme\feature.xml
..\RootFiles\TEMPLATE\FEATURES\Custom.Theme\SPThemes.xml
.Set DestinationDir=THEMES\CustomTheme
..\RootFiles\TEMPLATE\THEMES\CustomTheme\CustomTheme.INF
..\RootFiles\TEMPLATE\THEMES\CustomTheme\CustomTheme.css
.Set DestinationDir=IMAGES
..\RootFiles\TEMPLATE\Images\thCustomTheme.jpg
.Set DestinationDir=LAYOUTS\1033\STYLES\Custom.Theme
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\alldayOver_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\allday_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\Background-Gradient.jpg
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\calnumbttnfocus_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\calnumbttntoday_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\calnumbttn_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\cnspgrdn_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\cnsugrdn_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\Corporate-Intranet.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\CreateGraphic_classic.jpg
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\filedialogselected_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\formtitlegrad_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\Konnected-Logo.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\CustomTheme.css
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\linksectiongrad_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\listheadergrad_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\menubuttonhover_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\mgradlargertl_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\mgradlarge_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\mgradrtl_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\mgrad_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\navBullet_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\Navshape_classic.jpg
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\pagebackgrad_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\pageTitleBKGD_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\partgrad_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\quickLaunchHeader_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\rtebnhov_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\rtebnsel_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\selectednavbullet_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\selectednav_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\settingsbkg_classic.jpg
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\SettingsGraphic_classic.jpg
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\siteactionsmenugrad_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\siteactionsmenuhovergrad_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\siteTitleBKGD_classic.jpg
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\templatepickerselected_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\templatepickerunselected_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\toolgrad_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\Top-Banner.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\topnavhover_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\topnavselected_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\topnavunselected_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\Topshape_classic.jpg
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\viewheadergrad_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\viewselectorgrad_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\weekbox_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\wpqamh_classic.gif
..\RootFiles\TEMPLATE\Layouts\1033\Styles\Custom.Theme\wpqam_classic.gif
- Deploy the Solution
- Create the deployment CAB file by executing: c:\windows\system32\makecab /F Custom.Theme.ddf
- Add the solution to SharePoint by executing: stsadm -o addsolution -filename Custom.Theme.wsp
- Deploy the solution to SharePoint by executing: stsadm -o deploysolution -name Custom.Theme.wsp -immediate -force
- Force the timer job to execute immediately by running: stsadm -o execadmsvcjobs
- Next we need to tell SharePoint that our theme is there
To do that we need to add the following SPThemes node to the SPThemes.xml file. This file is located at: (in the next posting we’ll create a feature receiver and custom job to update this file automatically)
<SPThemes xmlns="http://tempuri.org/SPThemes.xsd">
<Templates>
<TemplateID>CustomTheme</TemplateID>
<DisplayName>My Custom Theme</DisplayName>
<Description></Description>
<Thumbnail>images/thCustomTheme.jpg</Thumbnail>
<Preview>images/thCustomTheme.jpg</Preview>
</Templates>
</SPThemes>
- Surf to a site, apply the new theme and begin customizing
Other things to note:
- The image thCustomTheme.jpg, is the theme preview image; I create the thCustomTheme.jpg after the fact by resizing a screen shot of the finished product.
In the next posting in this series we’ll be looking at how to automate the update the for the Theme List file.
This is the first posting in this series that will deal with the following:
- Making Theme Updates Easier (Trick from Heather Solomon’s blog, click here)
- Deploying the theme as a solution
- Automatically updating the theme list file.
If you’ve ever deployed a custom theme to multiple sites you must of experienced the pain of trying update the theme after the fact. This is because even though themes are stored on the file system SharePoint makes a copy of theme and stores it in the content database for each site that the theme is applied too.
Inorder to apply the changes to existing sites you must either: manually surf to each site the theme is applied to and reapply the theme; or write some custom code to crawl the site structure and reapply the theme. Both of these options are a bit of a pain.
To get around this, we need to change our custom theme definition to the absolute minimal:
- The INF definition file and
- a CSS file that points to the actual implementation stored on the file system.
This is an awesome trick that I picked up from Heather Solomon’s blog
http://www.heathersolomon.com/blog/archive/2008/01/30/SharePoint-2007-Design-Tip-Import-your-CSS-for-SharePoint-Themes.aspx.
I use it on every project I’ve been on since picking it up.
In the next step I’ll detail the process of deploying both the minimal theme definition and the actual custom theme files via a solution.