Wednesday, July 16, 2008

Changing a master page during runtime

Since SharePoint is built on the ASP.Net 2.0 framework, you can take advantage of the same code that you would use in an ASP.Net application. One of these that is useful in SharePoint is the ability to change the master page programmatically at run time.



This is easily enough accomplished through the use of the Page.MasterPageFile property. It is important to note that you need to use this property in the Page_PreInit method as this is the earliest point that you can access the Page lifecycle. It is also the only point that you can affect both the master and content page before they are combined into a single instance.



You can use this property to change the master page for a single page inside a site since with SharePoint you can only change the master pages for an entire site through the SharePoint UI.

Labels: ,

Monday, May 19, 2008

Programmatically Build a Word 2007 Document Part 1

Today I was assigned the task of building a Word 2007 document programmatically and then uploading that document to a SharePoint document library. The second part was not that hard as I had already written a block of code that would upload a word document to a SharePoint library. Building the word document shouldn't be that hard as Office 2007 documents are simply XML files. I was able to build a simple document with text in it. The problem came when I tried to add a header to the document.

The new XML format for Office files makes it interesting to build. Each element lies in its own separate XML file and there are relationship files that determine how all of the files relate to each other within the package. If you rename a typical .docx or .pptx or .xlsx file to a .zip extension, you can open the file up in WinZip or as a Windows Compressed folder. The first think you will notice in a .docx folder is that there is a rels folder and a word folder. The rels folder contains all of the package level relationships and determines where the rest of the data files lie. The word folder contains these data files. Within the word folder is a relationship file and at least a document.xml. There could also be a style.xml, header.xml, or footer.xml among others. The folder structure will look similar to the following drawing.

The relationship file that resides in the word folder relates the various pieces that make up a word document: the main document, the header, the footer, and any styles related to the content in the document.

To access this object model in a more efficient way, you can download the Microsoft SDK for Open XML Formats. The Microsoft.Office.DocumentFormat.OpenXml.Packaging class will expose some objects to make it easier to construct or modify a Word 2007 document.

The first class you will want to access is the WordprocessingDocument class. You create an object in code similar to this:

WordprocessingDocument myDocument = WordprocessingDocument.Open(myPackage);

where myPackage is a System.Xml.Packaging Package. You can also create a WordprocessingDocument from a Stream or a file.

Once you have the WordprocessingDocument, you can get back the main part of the document through the MainDocumentPart:

MainDocumentPart mainDoc = myDocument.MainDocumentPart;

The MainDocumentPart represents the main part of the document which resides in the document.xml file.

You can also gain access to headers and footers:

IEnumerable<headerpart> headerParts = mainDoc.HeaderParts;

You have to create an IEnumerable interface of the type of part you want to enumerate. You can then enumerate the headerParts collection. The reason that headers and footers are collections is that you can have multiple headers and footers in a document.

In the next part I will get back the XML and modify it.

Labels: ,

Tuesday, March 18, 2008

HRESULT 0x80040E14

Today I was trying to debug an annoying error that we were getting in one of our projects. We created a custom list with about 25 custom content types that all inherit from the built-in event type. These content types contain many columns on each one so there are probably a couple hundred columns on the list. When we tried running our application that worked with this list and a couple of other lists, there is a place where a user can enter a Task owner. When we would add an owner and then click the button to update the list item we would get the Unexpected error. HRESULT 0x80040E14. I did some research on the topic and found a couple of posts and blogs and even a KB article. It appears that this is a generic error for many issues. Below are the blogs or articles and I will explain which case will be fixed with each link.

http://support.microsoft.com/kb/841216

This Knowledge Base article describes the error message as being an issue with installing Service Pack 1 and then uninstalling it. The article recommends running the stsadm -o upgrade command. This did not apply to our issue but it could be a possibility if you receive this error message.

http://www.sharepointblogs.com/ajp/archive/2007/11/21/hresult-0x80040e14-when-added-items-to-sharepoint.aspx

This link indicates that there may be an issue when the SQL server data drive is full or the mdf and ldf database files may have reached their full allocation size. This did not apply to our issue but it is another option.

http://www.themssforum.com/WindowsServices/HRESULT-WHEN/


This post appeared to be the answer for our issue. When I turned versioning off on the list I did not receive the HRESULT error. The minute that I added the versioning back to the list, the error appeared again. It only happened when we modified an item in the list using the ItemAdded event receiver. Although this appears to be an answer, it does not help if you want to turn versioning on the list that is having the problem. I will be keeping my eyes out for any word from Microsoft about fixes for this issue.

UPDATE:

Today I had another client that was receiving the HRESULT error when they tried to add an item to a calendar. I began poking around in the the SharePoint install and tried adding items to other existing lists and got the error. So then I tried creating a new list and still got the error. The next logical thing for me to do was to poke into the SharePoint logs and there is where I found my answer. The logs had a database entry that had the following error message. "The log file for database 'SharePoint_Config' is full. Back up the transaction log for the database to free up some log space."

Apparently the HRESULT error is a generic one for Database errors that could include many different things. If you find that you can't add any items to a list or even create a new list, then check the SharePoint logs to see if there is a meaningful error that could lead you in the right direction.

Labels: , ,

Monday, March 10, 2008

Adding Video to a SharePoint Site

Adding video to your SharePoint site will probably come up sooner or later. There is a very easy way to do this. First upload your video to a document library. Then create a Javascript file and place it in the document library. Your Javascript file should build the embed and object code to insert the video.

function DisplayMovie() {

document.write('<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=5,0,0,0" height="211" width="210">');

document.write('<param name="movie" value="/Movie.swf">');

document.write('<param name="quality" value="high">');

document.write('<param name="wmode" value="transparent">');

document.write('<embed src="/Movie.swf" wmode="opaque" quality="high" pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash" type="application/x-shockwave-flash" height="211" width="210"></embed>');

document.write('</object>');

}


The reason for the separate Javascript file is to work around the fact that you have to click on embedded media to activate it.


Once you have the Javascript file you can add a Content Editor Web Part to whatever page you want the video to appear in. If you add the following lines to the Editor Web Part it will call your Javascript file and display the video.

<script type="text/javascript" language="javascript" src="/en-US/FlashMovie.js"></script>

<script type="text/javascript" language="javascript">DisplayMovie();</script>

Labels: , ,

Wednesday, March 5, 2008

Planning a Multi-lingual Site Structure

When building a multi-lingual site there are many things to take into consideration such as location of files and documents, number of servers in the server farm, and amount of resources allocated to SharePoint and SQL Server just to name a few.

When you build out the variations you will get sub-sites that represent each language, i.e. fr-ca, en-us, fr-fr. You will also still have the top level site. In this top level site is a Publishing Images folder and a Site Collection Images folder. Either of these photo libraries works well for placing images that will need to be used throughout the site and do not contain any text on them that would need to be translated.

Each language sub-site also contains a Publishing Images folder. These folders work well for images that contain text on them that need to be translated into each language.

Inside each language sub-site we created document libraries to hold Brochures, other word documents, and PDF files.

In the language sites may be other sub-sites, and in each one of these is a Documents folder and an Images folder. If you want to break your content down to that level you can place images in these folders. We used the Documents folder to place XML files that we used for XML web parts in the sub-sites.

In the language sites we also placed Javascript files and Flash movies that were language specific.

For much of your site you can also use ASP.Net Global Resources for text that will automatically use the right translated version in each language site.

With the variation landing page in place, you can allow the user to be directed to the correct language site based on their default browser language settings. If you want you can also put another way to direct users to the different language sites, such as a drop down menu or little flags that are links to the different languages.

Once you plan out the location of your files in a multi-lingual site, it will make it that much easier to maintain the site, especially when you add more languages.

Labels: ,

Monday, January 7, 2008

The Greatest Christmas Gift Ever

This post has been a long time coming but I have been busy lately with some project work due to the fact that I finally received my hotfix from Microsoft to allow me to build a French variation for my client.

This long ordeal began last September when we were getting ready to start building the French language version of a site for our client. I wanted to see which items would be copied over with the variation mechanism, so I ran it on our development environment. About three pages into the creation of the variation hierarchies, the variation deployment failed with a primary key violation. So I tried to delete the partially done French version and start over. Same thing happened.

So the next most logical thing to do was to call Microsoft and submit a ticket. After waiting for the call back and explaining the problem to the tech, as well as showing him through a net meeting, the tech finally came back and said there was a hotfix available for the problem. He sent me the hotfix and I installed it on the development server and ran the SharePoint Technologies Configuration wizard.

I thought I was off and running again so I created the French variation label and clicked to build the hierarchies. This time it ran for 4 hours and then died with an error message that read "Server out of memory." I didn't think this was possible, so I poked into the SharePoint log files and found that sure enough the server had run out of memory. I looked further back in the logs and soon found out why it ran out of memory. There were hundreds of entries that read "Excessive number of SPRequests objects are not being disposed of. Check to make sure that SPWeb and SPSite objects are disposed." The number of request objects was around 964 when the server ran out of memory.

Once again I called Microsoft and submitted a ticket. I sat on the phone with the tech and explained the problem and what I did to get the error. Then I ran the typical SPSReports tool to gather information for them to look over. After a few weeks I received an email that said my case was being reassigned to a new tech. So the new tech called me and asked the same questions. He wanted to watch the server to see what happened so I reran the variations and he watched and I pointed out that SPRequests were not being cleaned up (this was something we realized we had to do earlier in this project in our own code). After 4 hours of being on the phone while it ran, the variations died with the server out of memory error. We reran the SPSReports tool and the tech said he would get back to me with the results.

About a week later I heard back from the tech that there wan one other person having this issue and that the SharePoint team was working on a hotfix for the issue. He said it should be out in the next two weeks. So I waited and waited and after two and a half weeks emailed the tech only to find out that my case was being passed onto another tech because the previous was going to training.

Now the third tech said that the hotfix should be out shortly and that they would let me know when. About a week later I received another email saying that this hotfix was not going to be included in SP1 and that I needed to hold off on installing SP1 until the post-SP1 hotfix was out. That was fine with me, it was more important to build this variation anyway. So I was optimistic that the hotfix would be out soon.

After about 6 more weeks of emailing back and forth with the techs about the status of the case, I finally received an email saying that the case had been escalated and that more resources were being put on the hotfix.

Another 2 weeks later, now almost Christmas and I finally received a call from someone at Microsoft saying that the hotfix was ready.

We installed the fix between Christmas and New Year's and it worked wonderfully. The 2,300 page site was built out in the French version. It appears that the hotfix calls the garbage collector more frequently and this causes the garbage collector to clean up those SPRequests that were lying around.

So now I am working on creating the French version of a very large site. Look for more posts soon about the issues that come up when building a multi-lingual site.

Labels: , , , , ,

Thursday, December 6, 2007

PerformancePoint Server Training

Currently I am taking the PerformancePoint Server - Applications training in preparation for retaking the beta exam for PPS. The training has been very comprehensive and was able to answer some of my questions as to how PerformancePoint and SharePoint are integrated.

As for SharePoint stuff, the next few posts will include more detail about Features and building out Content Types as I am back into the mire that can be Features. I have found some new tools that also help in the creation of Features and Solutions that make the development of them much easier.

Additional upcoming topics include creating a custom field type and rendering it, using Feature Receivers, and some clarification of how content type inheritance really works.

Labels: , , , ,

Tuesday, November 6, 2007

Controlling the BLOB

The third part of the caching trilogy covers the BLOB cache, or binary large object cache. In a typical site there are many references to CSS files and images. MOSS caches these objects to prevent multiple trips to the database for these resources. MOSS caches these resources on the Web-front end servers in the file system. This type of caching is not enabled by default.

To enable BLOB caching change the line in the Web.config file for the blobcache to true. You can set the maximum size for the cache in this line as well. The default is 10 GB. In the path attribute you can determine the file extensions that will be cached in this type of caching. You may want to add such file extensions as .swf for Flash movies, if your site utilizes them. NOTE: this caching only works on items that are in document libraries and lists. If you place files in the site but not in a library or list, the caching will not cache the file.

<BlobCache location="C:\blobCache" path="\.(gif|jpg|png|css|js)$" maxSize="10" max-age="86400" enabled="True"/>


You can also set the maxAge to the number of seconds that you want the client to retain the object in its cache. This allows the client to use its copy of the resource and not re-request the URL for the resource until the timeout has expired.

With these three types of caching your Internet facing MOSS site can efficiently serve up files to many users.

Additional resources for caching:

Microsoft Enterprise Content Management (ECM) Team Blog
Custom Caching Overview


Labels: , , , ,

Monday, October 22, 2007

Output Caching

In the next three posts or so I will be covering the types of caching available in ASP.Net 2.0 and MOSS. The first of these three types is output caching. Output caching is the ability to render a page once and then store the HTML output of that page in cache for a set period of time. Each user (and I use this term fairly loosely for caching; I will explain in a little bit) that hits the page after it is cached will receive the cached page. The output cache caches a page based on type of user, either authenticated or anonymous, and by the type of browser. Yes, out caching stores each page for each browser type, so the Mozilla rendering of a page will be separate than the IE rendering.

This type of output saves both on processor speed and the number of round trips to the database which can be pretty slow depending on your MOSS configuration.

Thus type of caching is only available if you have the MOSS Publishing Infrastructure feature enabled. If your site is built on the Publishing Portal or Collaboration Portal then you have this by default.

Go to Site Actions - Site Settings - All Site Settings and go to Cache Profiles to set up the profile and its settings. You can have profiles for different types of users, one for anonymous, or one for authenticated users.

You need to give the profile a title and you can give it a display name and description. You can then check the checkbox for Performa ACL check. This will check for security trimming when caching. If you do not need security trimming then uncheck the box to increase performance.

You then need to check the box labeled Enabled to enable the caching on that profile. This check box allows you to easily trouble shoot a profile by enabling and disabling in one click.

You then set the duration to hold the cached pages before re-rendering the page. This number is in seconds. A good rule of thumb is to start at about 2 hours and check the performance of your server and adjust the cache duration accordingly.

The Check for Changes box if checked will check to see if the page has been changed. If it has it will re-render the page and store it in the cache. If the page has not changed then the cache will be used. If you check this box it will decrease performance as the site now has to check each page for changes before it renders the page.

You can then choose to vary the cache profile based on custom parameters, HTTP headers, query string parameters, or User rights. For most people you can leave the defaults that come with the base profiles.

Cacheability defines where the page gets cached and is one of the following: none - the page is not cached, server - the page is cached on the server, private - the page is cached in the browser, server and private - the page is cached at both the server and the browser.

The last two check boxes allow for caching for authenticated users and for contributors.

After you have created the profile you then need to enable the caching and select which profile to use for both anonymous users and authenticated users. There are also some check boxes on this page to allow sub-sites and page layouts to override the caching of the rest of the site. This can come in handy if there are a couple of pages that render code differently depending on a selection. You may need to turn caching off on that page layout for the page to render correctly.

You can also enable debugging information in the pages. This is handy to test whether pages are staying in the cache and to make sure they are staying in there for the entire duration time. This will place a timestamp on the very last line of the HTML source that will give you the profile that rendered the cached page and what time and date the page was cached.

Caching requires a lot of fine tuning for performance of your server or server farm. Dedicate the time, though, and your site will run smoothly and scale easily.

Here is a wonderful article on performance tuning and caching that will aid you on your journey:

http://blogs.msdn.com/ecm/archive/2006/11/08/how-to-make-your-moss-2007-web-site-faster-with-caching.aspx

In the next post I will explain object caching and how to track it.

Labels: , , ,

Sunday, October 14, 2007

When a Cache is Not a Cache

The first problem area I had with our project was when we made the Internet-facing site live. We set up output caching and BLOB caching to cache pretty much all of the site. We also setup the Object cache. We figured we had our bases covered because we tried these things on our development site and they increased page load time significantly.

The site went live and it was doing well for most of the morning when 11 o'clock hit and we now had 3 time zones worth of people hitting the site. We suddenly noticed that the processor was getting pegged at 100% and that the amount of memory used was increasing quite quickly.

After spending a week looking at the performance monitor for various counters and checking Microsoft documentation, we had finally figured out that some of the memory leak was due to not cleaning up SPRequest objects. We cleaned up all of our custom code and this improved the site marginally. We also began tweaking the output cache to find a good balance and settled on 4 hours for pages to be cached. The BLOB cache works wonderfully, it caches images, and other Binary Large Objects. It is awesome to see whole pages load without images filtering in one at a time.

The site was still not running quite right and we had noticed on the object cache counter that objects were being thrown out of the cache as soon as they were put in. I began monitoring the SharePoint logs and found that pages were being invalidated and thrown out of the cache. So this led to a call to Microsoft. After the same questions and sending them an SPSReports CAB file, they came back saying that the object cache does not cache custom code objects. This didn't fare well as almost every page of our site has custom code on it. After looking over the options and Microsoft telling me that there was no expected date for a fix, we decided to monitor the site some more and noticed that the page output was still being cached, so the object caching not storing our code was only going to be a problem for the first person to hit that page. We decided that this was acceptable performance for the site.

In the next few blog posts I will explain the various types of caching and how they can help your MOSS site's performance and scalability (especially if an Internet-facing site).

Labels: , , , , , , ,

Thursday, October 11, 2007

Can I assist you?

Over the last few months I have been supporting the MOSS installation of an Internet facing site that I had been creating for a client over the last 6 months or so. With the support comes finding issues in code, performance, and other areas. One of these areas could be classified as interesting things about the SharePoint code itself.

I have been on the phone twice with Microsoft support for various issues and both times the tech has asked the same questions about the MOSS installation and any error messages that I had encountered. Then they usually ask me to install SPSReports from the CodePlex site, http://www.codeplex.com/spsreport. This tool gathers information from all of the system logs, the SharePoint logs, the registry, 12 hive, and various other areas. It compiles it into a CAB file that you can send to Microsoft so they can replicate your MOSS configuration on their end and figure out what the issue is.

If you plan on doing any customizations at all on MOSS or WSS then you may want to have this program installed on your development server, so when the time comes you are prepared to bundle up the file and send it to Microsoft.

The next few posts will be some of the many issues I have run across over the last couple months with MOSS.

Labels: , , , ,

Thursday, September 20, 2007

Chopped Column Names in Sharepoint

While working on a Sharepoint customization project, there were many areas where I needed to create custom site columns. Not thinking much about limits or restrictions I named the columns so that they were meaningful for me and others who may be supporting the site down the road. In a couple of pages I needed to run a query that would pull back information from the custom column (which I added to some custom lists). The custom column was named LFI CAD Possible Answers. When I tried to run the query by substituting in the _x0020_ for spaces, the query would not run.
Well one of my co-workers had written a utility for browsing the Sharepoint object model and getting back various things such as lists, GUIDs, column names, and most importantly the internal name for lists and columns. So although you name a custom column one thing, internally Sharepoint names it something else. For the most part this naming follows what you named the column except that spaces are replaced with _x0020_. Another limitation of the internal name is that it can only be 32 characters long. So if I take my custom column name and add the _x0020_ to the spaces we have: LFI_x0020_CAD_x0020_Possible_x0020_Answers. So take the first 32 characters of that name and you get: LFI_x0020_CAD_x0020_Possible_x00. This was the internal name for that column. The minute I adjusted my query to use that as the name of the field, the query ran fine.

Another side point to the column name and internal column name. We had created a custom column called E-mail. Well the internal name for this column becomes Email without the hyphen. The hyphen is just dropped completely from the column name. So be aware of these limitations when creating custom columns.

Labels: , , , , ,

Wednesday, May 2, 2007

Adding the bulk of Your Site Using Features

To add content to your Sharepoint site using features you need to create a feature.xml file or include the following elements into an existing elements.xml file.

To add content you make use of the Module element.


<Module Name="MainMasterPages"
Path="Masterpages"
Url="_catalogs/masterpage"
RootWebOnly="TRUE"></Module>

In the module element you give it a name attribute that will help you identify different modules. The Path attribute is the location of the files to upload in relation to the elements.xml file. The URL attribute is the URL on the Sharepoint site that you want the files to go. The RootWebOnly attribute is used to say if the file should only be available to the root web. Most of the time you can leave this true.

Within the Module element you place File elements that specify the actual files to upload to Sharepoint.


<File Url="main.master"
Type="GhostableInLibrary">
<Property Name="ContentType" Value="Landscape Master Page" />
<Property Name="Title" Value="Main Master Page" />
<Property Name="MasterPageDescription" Value="Master page for the home page." />
</File>

In the File element you specify the URL that you want the file to have in Sharepoint using the URL attribute. The Type attribute determines if the file will show up in the list or document library. If you set it to GhostableInLibrary then the file will show up. Inside the File elements you can place Property elements to provide additional information about the file. The property elements always consist of a name attribute that represents the name of the data and a value attribute that is the actual data.

In the example above we have a property called ContentType and it has a value that is the type of content that this file is, mainly a master page. The title property allows you to display the title that shows up in lists and when users can change the master page. The MasterPageDescription property allows you to provide a more detailed description of the file for users.

You can follow this same format to upload pretty much any type of content that you have. I have used it to upload master pages, style sheets, and images to the sites and folders that I wanted.

Following are some more links with more detailed information about uploading content:

http://www.andrewconnell.com/blog/archive/2006/12/20/5451.aspx

http://msdn2.microsoft.com/en-us/library/ms441170.aspx

Labels: , , ,

Friday, April 20, 2007

The Filling is the Best Part

In the previous post I showed how to create a list template. In this post we will create an instance of the list and populate it with actual data. This is the real power of a feature for lists. You create the basic Feature.xml and tell it where to look for the Elements.xml file.

In the Elements.xml file you create the following code:


<ListInstance
FeatureId="00BFEA71-DE22-43B2-A848-C05709900100"
Id="1999"
Description="Custom list for the LFI design groups."
TemplateType="100"
Title="LFI Design Groups"
Url="Lists/LFIDesignGroups">
<data>
<rows>
<row>
<field name="Title">35: Chill®</field>
</row>
<row>
<field name="Title">35: Mingle®</field>
</row>
</rows>
</data>
</listinstance>

In the listinstance node you place the feature ID GUID for the feature to base your list instance off of. In the sample code above this is the GUID for the feature of the basic list template. If you do not place a GUID in here, the documentation says that Sharepoint will substitute it with a default one. Don't ever do this as it can screw up the base list templates for the entire server. Always place a GUID in there yourself.

The documentation says that ID is optional but I always include it. It can be pretty much anything, an integer, a string, a GUID. As far as I can tell it is not used by Sharepoint. The only restriction is that it must be unique within the feature.

The description is what you want to show up for the list description. The template type is also grabbed from the list template that you are basing your list on. In this case the list template of 100 is for a custom list.

The title is the title you want to give the list.

The URL is what you want the URL of the list to be.

Inside the listinstance is a data node that contains a rows node and then a row node for each row in the list. Within a row node you place a field node and specify the name of the column that you are going to fill. In this case it is Title. You then place the data inside the node. In this case we are inserting two rows with data of 35: Chill and 35: Mingle. You can place as many nodes as you need to fill your list.

This feature works best if you need to fill a list with static data. You can easily recreate the list later or move the list to a new server such as staging or production.

For more information on list instances:

http://msdn2.microsoft.com/en-us/library/ms476062.aspx

And for more options for a field element:

http://msdn2.microsoft.com/en-us/library/ms437580.aspx

Labels: , , ,