Tuesday, June 30, 2009

Append new line to SPFieldMultiLineText in SharePoint

By Hristo Yankov

Do you have a multiline text box field in your SharePoint site and ever wondered how to append a new line to it? Are you confused whether you should use \r\n, Environment.NewLine or perhaps even #xD #xA to mark a line break? Allow me to save you the time and give you a straight answer.


The first thing you have to do is convert the SPListItem that represents your field into SPFieldMultiLineText.
SPFieldMultiLineText multilineField
= item.Fields.GetField(COLUMN_NAME) as SPFieldMultiLineText;


Then you can use its GetFieldValueAsHtml method to get the value in the form of HTML (which will preserve the multiple lines):
string text = multilineField.GetFieldValueAsHtml(item[COLUMN_NAME], item);

You should use '<br/>' as a line break:
string sharePointNewLine = "<br/>";
text = text
+ sharePointNewLine + sharePointNewLine
+ "Appended Text:" + sharePointNewLine
+ "This text is appended on a new line!";


And after that you just update your item:
this.Web.AllowUnsafeUpdates = true;
item[COLUMN_NAME] = text;
item.Update();
this.Web.AllowUnsafeUpdates = false;


So if the original text in the field was "Test test test", after executing this code, it would read:
Test test test

Appended Text:
This text is appended on a new line!

Simple? As long as you remember that text should be retrieved as HTML and you should use '<br/>' as a line break, yes.

Here is the full code snippet, ready to use:
public void AppendTextToMultiline(SPListItem item)
{
string COLUMN_NAME = "Multiline Column Name";
string sharePointNewLine = "<br/>";
SPFieldMultiLineText multilineField = item.Fields.GetField(COLUMN_NAME) as SPFieldMultiLineText;

if (multilineField != null)
{
// Get the field value as HTML
string text = multilineField.GetFieldValueAsHtml(item[COLUMN_NAME], item);

// Append the text
text = text
+ sharePointNewLine + sharePointNewLine
+ "Appended Text:" + sharePointNewLine
+ "This text is appended on a new line!";

this.Web.AllowUnsafeUpdates = true;
item[COLUMN_NAME] = text;
item.Update();
this.Web.AllowUnsafeUpdates = false;
}
}


Hope this helps!
Read more on this article...
Bookmark and Share

Passed 70-541 exam today

By Pako Simeonov

I finally got some time between project assignments and decided to pass the 70-541: Microsoft Windows SharePoint Services 3.0 - Application Development exam. The test contains 59 questions, but actually the ones that are included in the final score are less - i believe 55. The time to complete the exam was 3 hours and I can say that it is more than enough. It took me a little bit more than 1 hour to complete the test, but I was in a hurry to finish it in order not to get stuck in the LA traffic going back to Orange County. The final score I got was 900 out of 1000. The passing score for this test is 700, which is equal to probably around 40 right answers out of 55 questions. 



Here is what the test covers: 

1. Deploying Windows SharePoint Services and Custom Components
2. Creating Site and Feature Provisioning Components
3. Creating Metadata and Workflow Provisioning Components
4. Developing Windows SharePoint Services Components by Using the .NET framework
5. Manipulating Site Content by Using the API
6. Manipulating Site Configuration by Using the API

It just took me few days to refresh my knowledge by reading the Inside Microsoft Windows SharePoint Services 3.0 book and with the practical experience I have with SharePoint it was more than enough to prepare for the test and I would recommend you not to waste money on anything else. The only thing that the book doesn't cover is the SharePoint Records Management, but there are a few articles out there that are more than enough to prepare yourself. 

Read more on this article...
Bookmark and Share

Monday, June 29, 2009

Beware using GetFieldValueAsHtml or GetFormattedValue with DateTime fields

By Hristo Yankov

Watch out when using GetFieldValueAsHtml or GetFormattedValue with SharePoint DateTime fields! They both expect the value to be in UTC! However, SPListItem[FieldName] returns its value in Local Time zone and marks it as Unspecified.


Basically, what you have to do prior to calling one of the two methods above is make sure your time is in UTC:

DateTime localTime = (DateTime)listItem["SomeDateTime"];
DateTime universalTime = this.Web.RegionalSettings.TimeZone.LocalTimeToUTC(localTime);
string correctlyConverted = listItem["SomeDateTime"].GetFieldValueAsHtml(universalTime);
Read more on this article...
Bookmark and Share

Monday, June 22, 2009

STSADM Technical Reference for Microsoft Office Sharepoint Server 2007

A nice Silverlight application "STSADM Technical Reference for Microsoft Office Sharepoint Server 2007"

http://technet.microsoft.com/en-us/office/sharepointserver/cc948709.aspx Read more on this article...
Bookmark and Share

Tuesday, June 16, 2009

Fixing 'Feature {Guid} for list template is not installed in this farm. The operation could not be completed'

By Pako Simeonov

"Feature {Guid} for list template is not installed in this farm. The operation could not be completed." Probably this is the message that I get the most when I'm developing a project that has a high usage of list created from list templates. You can find useful one of my previous articles on that topic - How to create List Templates and List Instances with VSeWSS 1.3




Here is a screenshot of a message that that I got recently and it happened just after I deployed a SharePoint solution using the VSeWSS 1.3 that had couple of content types, list templates and list instances.


Although the message is about a list template not being installed you can pretty quickly guess that the problem is the list instance that is using the list template since actually the message was shown when you tried to open the list itself. My first reaction was to check the SharePoint
FEATURES folder (%Program Files%\Microsoft Shared\Web Server Extensions\12\Features ) for a feature with that Guid and as I expected I couldn't find anything there that matches the identifier from the error message. The hardest part is to find a starting point for the troubleshooting which can give us some directions to what to look for and in this case since the problem is in the list instance the most obvious thing is to actually see what kind of properties SharePoint has for this particular list and is there a one that has something to do with the Guid.

Using one of my favorite tools (SharePoint Manager 2007) that helps me a lot during the troubleshooting of all the problems I face during my SharePoint development it is pretty easy to quickly check all the properties that a list has. Luckily enough for me I found what I was looking for and right away had the idea what is wrong and what is causing the error. The field that contained the Guid from the error message was a value of the TemplateFeatureId list property. So here is a quick explanation of what actually happened:

During the deployment of a feature using the VSeWSS 1.3 the old one is being retracted and thus the feature files are being removed from the 12 Hive folder of SharePoint and meanwhile a new Guid is being generated by SharePoint internally which is then being used instead of the fake one in the schema.xml, listdefinition.xml and instance.xml files in your Visual Studio .NET project. Even when you open the instance.xml file you can see a message there saying the following:
The following Guid for FeatureId is used as a reference to the list definition, and it will be automatically replaced with actual feature id at deployment time.
So now we have the TemplateFeatureId of the list instance the same as the list template one. During a new deployment of the list template if there is already an existing list instance it is being preserved and no new one is being created, which is pretty much expected since we don't want to loose our already existing list with all its data just because we redeployed the list template. So what happens is that the list template feature that the list instance is pointing to is being replaced by the new one which has a new FeatureId generated by SharePoint, but the TemplateFeatureId of all the list instances that are instantiated from the list templates remain the same and thus when the list is being opened no such list template is being found and the exception thrown by SharePoint finally makes sense.

From the problem description above the solution seems to be pretty easy. We just need to change the TemplateFeatureId to the Guid of the newly deployed list template. The problem here is how do we map the old list template to the new one that was deployed. Of course for a man this is easy since we can quickly find the folder of the feature, take a look at the new Guid in the feature.xml and then change the TemplateFeatureId of the list, but we don't want to do that every single time we deploy our solution to SharePoint which can be pretty often during the development phase. The question here is can we automate this process, so the problem can be fixed just by clicking a button? The answer is Yes, we can automate the process and make it work in most of the cases. I've created a pretty convenient tool called List Instances Fix Tool that you can download from www.myitechnology.com



When you run it on the SharePoint server it will give you a list with all the Web Applications and their site collections that exist in the SharePoint Farm. You can easily see if there are bad list instances in a site by expanding the site collection. The tool will give you the total number of lists in this site and the count of the bad ones along with an exclamation icon if there is at least one bad one. Once you click on the site a list with all the bad list instances will be presented. The tool will try to guess which is the right list template, but if it can not do that you still have the option to select it manually from all the available list templates - activated or not.

I've also published the tool in codeplex - ListInstanceFix 1.0
Read more on this article...
Bookmark and Share

Monday, June 15, 2009

How to create List Templates and List Instances with VSeWSS 1.3

By Pako Simeonov

The task of every Sharepoint developer is to provide a solution that is easy to be deployed to the end systems of the client being it development, staging or production environment. In most of the cases a real life SharePoint project has a set of custom lists, document libraries and custom pages/web parts, so covering them will make the every day life of most of you a little bit easier. In this article I will cover the approach that I found most useful for myself from a developer stand point of view.




Once the VSeWSS 1.3 is being installed on the machine where Visual Studio .NET is located we get access to the Sharepoint project templates:



One of the available Sharepoint templates is the List Definition which is the one we can use to create a Sharepoint list. After selecting it we will have the chance to select what kind of template we want to create:



In our case we want to create a custom list, so we just have to select the Custom List base definition. However this is just a definition and what actually we want to do is to have a list instance based on that definition. We want to create the instance of the list automatically when we are deploying our application and not doing it manually. This can be done by just checking the Create an instance of this list option which will include one more file called instance.xml. This is how the list definition project looks like:



The files that we are interested in are the schema.xml, Instance.xml and ListDefinition.xml where the schema.xml is the most important one since it contains the definition of the list: fields, content types, views and forms and is the one that will be modified the most, while the other 2 files once created usually don't require any modifications. This is how the different files look like:
  1. ListDefinition.xml

  2. Instance.xml

  3. Schema.xml

Note: It is critical to have the Id attributes of the ListDefinition.xml and the Schema.xml and the FeatureId one in the Instance.xml synchronized all the time since they are used by Sharepoint to connect the different pieces together and if the mentioned identifiers are different usually this means troubles ones the feature is deployed to Sharepoint. Usually the result is either missing list instance or the list display form page shows you nothing but a standard browser message for missing page and you can not view the items in the list.

If we take a closer look at the Schema.xml we can see that actually our custom list has the Item content type as base one and that there are currently no fields defined, which is what we would expect for an empty list template. From this point on our task is to modify the list template in order to achieve our goal and we can do it the hard way or the easy way:
  1. Modify the schema.xml by hand. This includes creating the fields and content type definitions. I'm sure that there are people out there that are willing and are using this approach, but personally except for learning purposes I would never do that again since too many things can go wrong and it is just waste of time, although it is very helpful to know what is happening behind the scenes in order to troubleshoot the eventual problems that can occur and WILL occur later. This approach may work for a list or two but generally we have much more than that.
  2. Create the list using the SharePoint UI and export it using the SharePoint Solution Generator 2008 which comes with the VSeWSS 1.3. This approach allows us to create all we need using the SharePoint UI and later export it as Visual Studio .NET projects that have exactly the same structure as the one that was created using the Visual Studio .NET SharePoint template.
Without any doubt I would choose the second approach since it saves a lot of development time. Unfortunately it is not automating 100% of the work that needs to be done, but this is mostly related to the content types of the list. Even if we define a content type using the SharePoint UI that later is being used by the list in the result Visual Studio .NET project this content type is only included as embedded one in the schema.xml and when it is deployed to a different SharePoint there is no way to see it except from the list settings and thus there is no way to reuse it in different lists or use it as base of another content types. Of course since it is embedded in the schema.xml with the help of a console application that goes through the solution folder and searches for the schema.xml files and the Visual Studio .NET pre-build events we can easily automate the extraction of the content type in a separate xml that is being added to the project when we are re-building it and later being deployed as a separate content type feature automatically without the need to do anything extra, which is the ultimate goal of every developer. Of course probably there is a reason that this is currently not supported in the solution generator itself but writing such tool that covers 80% of the cases is not that difficult.

Here is what you need to do to export your lists/documents libraries using the solution generator:
  1. Open the SharePoint Solution Generator 2008 and select List Definition
  2. After you specify the url of the SharePoint site you will have the option to select a subset of all the site lists that exists on that site. In our case we just need the MyCustomList one.
  3. The last step of the process is to specify the name of the project and the location where the solution generator should create the Visual Studio .NET project.
Usually as in every real life project the lists will need to be modified - add or remove a field, etc. Once we have the project with the lists created this is an easy task. The modification can be done using the SharePoint UI and exported the same way. In most of the cases I usually modify the ListDefinition.xml and the Instance.xml files in order to change the titles and the descriptions of the lists and I don't want these files to be overwritten, so I generate the new definition in a temporary folder and just copy the Schema.xml in the real project, since it is the one that contains the list modifications. As I mentioned earlier you need to be careful here since during the new export the Id attribute of the List element in the Schema.xml file is being changed, so you need to propagate it to the Instance.xml and the ListDefinition.xml files.

Once we have the Visual Studio .NET project ready we can deploy the lists to different sites quite easily:


Note: Deploying to a site that still doesn't have existing list instances of the list templates is straightforward and will hardly ever cause a problem. However this is not the case if we just modified the list templates and are trying to re-deploy it to a site where it was deployed previously and we have already existing list instances created using the list templates. The chance that you will get the following message is pretty big: Feature {Guid} for list template '100' is not installed in this farm. The operation could not be completed. I've found this problem the painful way and after some analyses actually the reason for the error is pretty obvious. The resolution of this problem will be covered in Fixing 'Feature {Guid} for list template is not installed in this farm. The operation could not be completed' , where I will explain the exact reason and will show I handy tool I've developed that can automatically fix the problem for you.

Read more on this article...
Bookmark and Share

Thursday, June 11, 2009

iTechnology Forms Accelerator Videos

By Hristo Yankov

Make sure to check out the HD/HQ videos we have uploaded for you on YouTube at iTechnology's channel.

As of today, they are...


An overview, which gives you an idea what the product is about:


Installation tutorial:


And a step-by-step tutorial on how to create your first iTFA-enabled application:



And of course, visit us at our web site to get your copy!
Read more on this article...
Bookmark and Share

Tuesday, June 9, 2009

The right way to SPWeb.EnsureUser in SharePoint

By Hristo Yankov

At some point of time you may need to call (SPWeb).EnsureUser from your custom SharePoint web application. But this method can not be called by everyone, as it requires some high level permissions. You may also get an error similar to this one:



Your solution is to wrap the EnsureUser within RunWithElevatedPrivileges call. However, there is a big catch. If you use instances of SPSite or SPWeb, obtained prior to the RunWithElevatedPrivileges block, it won't work as expected because they are already associated to a non-elevated security context.

To illustrate it with code, here is WRONG usage of RunWithElevatedPrivileges:
SPWeb web = [... somehow obtained here...];

SPSecurity.RunWithElevatedPrivileges(delegate()
{
// NOTE: Wrong, do not use
SPUser someUser = web.EnsureUser(web.CurrentUser.LoginName);
});

And here is a CORRECT one:
SPWeb web = [... somehow obtained here...];

SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite elevatedSite = new SPSite(web.Site.ID))
{
SPWeb elevatedWeb = elevatedSite.OpenWeb(web.ID);
SPUser someUser = elevatedWeb.EnsureUser(web.CurrentUser.LoginName);
}
});

Basically we used the IDs of the Web and Site objects, obtained prior to the elevated block, and used them to create Site and Web object within the elevated context.
Read more on this article...
Bookmark and Share

Monday, June 8, 2009

SPTimerScheduler for SharePoint Improvement

By Hristo Yankov

At some point of time, you will need to create a Timer Job (scheduled task) for your custom SharePoint web solution. For example, you may need it to synchronize two systems.

Out of the box, you can create a SharePoint timer job, by inheriting the SPJobDefinition class and then creating a timer job installer, by inheriting SPFeatureReceiver. What this basically does is, during the activation of your feature, the installer will kick in and add your timer job to SharePoint timer jobs list. On deactivation of the feature, the timer job will be 'uninstalled'. This is associated to a huge inconvenience - you can't change the schedule of your task, unless you change your source code and redeploy. In a production environment this may cause a big stir.


This is where you can use SPTimerScheduler! It is really convenient and easy to install. After deploying the solution and activating the feature, you get a 'TimerJobSchedule' list, where you can define all your timer jobs. You just 'point' to the assembly, class and method you want to execute, pass constructor parameters (optional) and you are done! It allows for simple and easy task (re)scheduling, which is generally why you want to use this solution in the first place.

If there is one nice thing about the native SharePoint timer jobs, it is the fact that if your code throws an exception during execution, it will be logged by the SharePoint timer service in the Event Log. This is missing from SPTimerScheduler!

The purpose of this article is to show you how to extend this custom solution functionality so you can see what the error was, if any.

First, you need to download the source code of SPTimerScheduler from here. As of right now, the latest release is 1.1.0.0.

After you download the source, open SharePointerTimerJobScheduler.sln in Visual Studio. It may prompt you to remove the version control associated to the project, to which you agree.

We will start by adding one more column to the 'TimerJobSchedule' list, which will store the latest Exception message. Please note that the project stores the list template as TimerJobSchedule.stp file, which you can not edit directly. Generally, what you have to do is import the stp file to SharePoint, create an instance of this list type, edit it by adding one more column - LastExceptionMessage (multiline, plain) and then export it again. Overwrite the old stp file with the newly exported one. Here are detailed instructions:

1) Go to your SharePoint web site -> Site Actions -> Site Settings. Under 'Galleries', click on 'List Templates'.


2) Click Upload button -> "Upload Document"


3) Browse for "12\TEMPLATE\FEATURES\SharePointerTimerJobScheduler\TimerJobSchedule.stp" file from the source code and upload it.

4) Now you have added the SPTimerScheduler as a type of a custom list. Create a new instance of it. Click on 'Create' and under 'Custom Lists' you should see 'TimerJobSchedule'. Click on it.


5) Give the list instance a name and click Create. The go to the settings of the list and under 'Columns' click 'Create column' link.

6) Name the column 'LastExceptionMessage' and make it of type 'Multiple lines of text', 'Plain text' and click ok.

7) Go to the Site Settings -> List templates again and delete the TimerJobSchedule list template you imported on step 3. You need to do this as preparation for the next step.

8) Now go to the list you created (of type 'TimerJobSchedule'. Remember, you gave it a name at step 5) and open its settings. Under 'Permissions and Management' there is a link 'Save list as template'. Click on it.


9) Under 'file name' and 'template name' fields call it 'TimerJobSchedule' as it used to be when you first imported it. Do not include content and click OK button.

10) Go to site settings again -> List templates. You should see the TimerJobSchedule template there. Click on the Name field of the item, which is a link. Save the file TimerJobSchedule.stp back to 12\TEMPLATE\FEATURES\SharePointerTimerJobScheduler.

You have now effectively added one more column to the list - LastExceptionMessage.

Now we need to edit the code too. You should have the solution open in your Visual Studio. Open TimerJobScheduler.cs for edit and go to line 140. This is the catch statement, right after the 'RunTheTimerJobInstance(Item);' block. As you can see, logging logic is not yet implemented there. What you want to do is add the following code in this catch statement:

Item["LastExceptionMessage"] = NowDateTime + " - " + ex.Message;

Basically it will write a time stamped exception message into the 'LastExceptionMessage' column you have created earlier. As you can see, we don't even need logging components such as log4net at this point. We are using the column to store the message.

Right click the 'SharePointerTimerJobScheduler' project in Visual Studio and select Properties. Go to the Build Events tab and take a look at the "Post-build event command line" text box. There is an entry saying:
makecab /f solution.ddf"

Change it to:
makecab /f solution.ddf

I.e. - remove the " character at the end of the line, as it is there by mistake (I guess).


There is also an error in TimerJobSchedulerFeature.cs file of the source. Find the line:
AddListTemplateToGallery(@"C:\Program Files\Common Files\
Microsoft Shared\web server extensions\12\
TEMPLATE\FEATURES\TimerJobScheduler\TimerJobSchedule.stp");


And replace it with:
AddListTemplateToGallery(@"C:\Program Files\Common Files\
Microsoft Shared\web server extensions\12\
TEMPLATE\FEATURES\SharePointerTimerJobScheduler\TimerJobSchedule.stp");


I have notified the developer about those findings here.


After you are done with all of the above, rebuild the solution. The "Deployment\SharePointerTimerJobScheduler.wsp" will be refreshed. You need to redeploy it. You can do that by copying 'SharePointerTimerJobScheduler Source\Deployment\SharePointerTimerJobScheduler.wsp' (which is the output of the source project) into 'SharePointerTimerJobScheduler Release 1.1.0.0' (which was the release folder of the project, if you downloaded it. And you should!).

After deployment, by running 'SharePointerTimerJobScheduler Release 1.1.0.0\Install Feature.cmd' (and re-activation of the 'SharePointer Timer Job Scheduler' site feature) make sure the 'LastExceptionMessage' column is visible in your default list view, so you can monitor it for errors.


And that's all. Now just make sure that the method you are executing as a timer job, throws a meaningful exception, which will be visualized in the list column!
Read more on this article...
Bookmark and Share

Friday, June 5, 2009

Sending email from SharePoint

By Hristo Yankov

Did you ever need to send an email out, from your SharePoint custom web application? When you have such a task, perhaps the first idea that you have is to use System.Net.Mail namespace. However, this requires that your application maintains a setting for the SMTP server, reply address and etc. Wouldn't it be easier if you could delegate the task of storing this configuration to SharePoint (and its Administrator) and instead, just focus on sending out the actual email?


Your solution is the Microsoft.SharePoint.Utilities.SPUtility class! It has the very convenient method 'SendEmail'. This is basically SharePoint's native functionality for email delivery.

The first thing you (or the SharePoint Administrator) need to do is setup the Outgoing email SMTP server. Open Central Admin -> Operations -> Outgoing E-Mail Settings.



There, you need to set the Outbound SMTP server, From address and Reply-to address.


And this is exactly how you delegate SMTP setting storage to SharePoint. Now, how do you actually send email from your code?

First, it is always a good idea to check if the email server is set:
bool isEmailServerSet = SPUtility.IsEmailServerSet(web);

If this returns false, you should not bother trying to send the email. Instead, show an error message or notify the SharePoint administrator, to check the settings of the server. If it returns true, you are good to go:
SPWeb web = SPContext.Current.Web;
bool appendHtmlTag = false;
bool htmlEncode = false;
string toAddress = "test@example.com";
string subject = "Subject";
string message = "Message text";
bool result = SPUtility.SendEmail(web, appendHtmlTag, htmlEncode, toAddress, subject, message);

In some cases, you may need to run this code with elevated privileges:
SPSecurity.RunWithElevatedPrivileges(delegate()
{
bool result = SPUtility.SendEmail(web, appendHtmlTag, htmlEncode, toAddress, subject, message);
});

SendEmail method returns a boolean, indicating if sending the email was successful or not.

However, there is a catch in using SPUtility.SendEmail. Internally this method relies on having a SPContext. In other words - if you are trying to use SPUtility.SendEmail from a SharePoint timer, it will fail, because there will be no context. This is when you have no other choice, but use System.Net.Mail. But how can you still benefit from SharePoint storing your SMTP settings? You do it this way:

SPWeb web = new SPSite("http://example").RootWeb;
string toAddress = "test@example.com";
string subject = "Subject";
string messageText = "Message text";
string replyTo = web.Site.WebApplication.OutboundMailReplyToAddress;

MailMessage message = new MailMessage(replyTo, toAddress, subject, messageText);
message.IsBodyHtml = false;

string smtpAddress =
web.Site.WebApplication.OutboundMailServiceInstance.Server.Address;

SmtpClient smtp = new SmtpClient(smtpAddress);
smtp.Send(message);

So what did we do here? We use .NET's native MailMessage and SmptClient classes, but we still read the configuration from the SharePoint site we opened by URL.
Read more on this article...
Bookmark and Share

Tuesday, June 2, 2009

Sample SharePoint project localization Part 2 of 2

By Hristo Yankov

Hello,

This article assumes you are already familiar with Part 1 and the post preceding it.

Here, we discuss how to localize the content of the SharePoint custom web site project you already have created.


Normally, your ASPX page content would be similar to the following:




We want those 'hardcoded' strings to be localization enabled.

1. Create your default resource file.
You can create a subfolder 'Resources' in your project and put it there. Give it a specific name, in our case it is SampleLocalization.resx

2. Extract the strings we want to localize, into a resource file.
Let's assume we want to have the page title, button text and greeting message localized. We need to create a resource entry for each one of them. Your resource file should look similarly to:




3. Modify the Page attribute to contain Culture and UICulture tags.
It should be similar to:

<%@ Page="" Language="C#" MasterPageFile="~/_layouts/application.master" %>
Inherits="SampleCode.Localized, SampleCode, Version=1.0.0.0, Culture=neutral, PublicKeyToken=[Public Key Token]" Culture="auto" UICulture="auto" %>

4. Modify your page to refer to the resources, rather than the hardcoded strings




5. Make a copy of your resource file. This will be our second language. Let's make it Bulgarian.
Name it 'SampleLocalization.bg.resx'. Open it for editing and translate the text. For your convenience, here are the strings:

Button_Text - Бутон текст
Greeting - Здравейте, това е тест
Page_Title - Това е заглавието на страницата

Make sure you store it in the Resources folder, next to your SampleLocalization.resx file.

6. web.config modification
Open the web.config of the website that hosts your template page. Find (or add) your globalization node and modify it to contain Culture and UICulture attributes, equaling 'auto'.




7. Deployment
Do a full deploy of your project. Also, don't forget to copy your *.resx files (both of them) into the WSS VirtualDirectories App_GlobalResources. The path usually is:

C:\Inetpub\wwwroot\wss\VirtualDirectories\[Port Number]\App_GlobalResources


Open the web page in the browser and see the result. You can set language preference through the settings of your browser.

For IE:
Tools -> Internet Options -> General Tab -> Languages -> Add / Move Up / Move Down

For FF:
Tools -> Options -> Content tab -> Languages (bottom) Choose button -> Add / Move Up / Move Down


Add Bulgarian. Try how it works when Bulgarian is a first preference, or when it is last.

In English:




In Bulgarian:




Hope this is helpful,
Hristo Yankov
Read more on this article...
Bookmark and Share

Sample SharePoint project localization Part 1 of 2

By Hristo Yankov

Hello,

This is a step-by-step tutorial on how to create a localized SharePoint custom web application. In this first part, we will discuss the creation of the sample web application itself, while the second one, will emphasize on the localization.

Prerequisite is a Visual Studio 2008 with WSS extensions 1.3. You can also work with Visual Studio 2005, but you will have to do part of the deployment manually.


1. Start Visual Studio and create new project of type SharePoint.



I entered 'SPLocalization' as name for the project.

2. Select "Full Trust"




Note: Check debug tab of Project Setting to ensure it contain correct path to your SharePoint server.




3. Add new item to our project





4. Select 'Template' element in SharePoint section. Change file name to 'LocalizedPage.aspx'






5. Now you should see project structure like this:








6. Create subfolder LAYOUTS under Template folder


7. Create subfolder SPLocalization under LAYOURS folder


8. Move LocalizedPage.aspx to SPLocalization folder (I use drag and drop the file). Now you should have the following structure:






9. Now let's create a class that extends SharePoint layout pages. Add new class to your project.






10. Name it 'LocalizedPage.aspx.cs' and click "Add" button




11. Now few other steps ...

First add references to SharePoint and SharePoint.WebControls
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

Secondly make your class public, partial and derived from LayoutsPageBase

You should see something like this:


Now, since we have chosen GAC deployment model, we need to deploy our solution to Global Assembly and determine Public Key.

12. In Visual Studio Menu, Selection, Build -> Quick Deploy Solution -> Copy Binary(s)


This will compile our SharePoint project into DLL and deploy it to GAC. After deployment Output window will show that Deployment was successful:


13. Let's go to Windows Assembly folder and find our library


If you open Properties of our assembly, you will be able to copy its public key token. In my case it was 7a1fde53a908a4fe.

We will need Public Key Token for our aspx.page.

Now, let's return to our ASPX page.

14. Remove default text and add Page registration tag




<%




@







Page







Language




="C#"




MasterPageFile




="~/_layouts/application.master"




Inherits




="{ClassName}, {Assembly}, Version=1.0.0.0, Culture=neutral, PublicKeyToken={PublicKeyToken}"



%>

Now let's replace {ClassName} with SPLocalization.LocalizedPage {Assembly} with SPLocalization {PublicKeyToken} with Public Key Token we obtained from GAC.

Now you should see something like this:





Now let's add some content to the page and test if everything works.



15. Add ASP tag Content with ContentPlaceHolderID equals to PlaceHolderMain. Here is complete code





<





asp




:




Content







ID




="Main"







ContentPlaceHolderID




="PlaceHolderMain"







runat




="server">




Test message










asp




:




Content




>




Your Localized.aspx page should look like this:





<%




@







Page







Language




="C#"










MasterPageFile




="~/_layouts/application.master"










Inherits




="SPLocalization.LocalizedPage, SPLocalization, Version=1.0.0.0, Culture=neutral, PublicKeyToken=7a1fde53a908a4fe"



%>







<





asp




:




Content







ID




="Main"







ContentPlaceHolderID




="PlaceHolderMain"







runat




="server">






Test message












asp




:




Content




>






16. Let's make a full deploy. Visual Studio menu Build -> Deploy Solution.
After deployment is completed, Output window will show that Deploy was successful.





17. Now let's go and check manually if LocalizedPage.aspx was deployed.
Browse to 12 directory (by default it is "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12") then browse to TEMPLATE, then to LAYOUTS, then to SPLocalization. You should see our LozalizedPage.aspx was successfully deployed.





Now let's try to access this page through SharePoint

18. Open SharePoint url of your site with prefix "_layouts/SPLocalization/LocalizedPage.aspx".
In my case, it is "http://itech-vm-spdev/_layouts/SPLocalization/LocalizedPage.aspx"

When page is loaded you should see empty area but with our magic text: Test message




Now, let's add SPLinkButton SharePoint control

19. Change your ASPX page to include Register of Microsoft.SharePoint assembly and replace static text with SPLinkButton control.

Your ASPX page should look like this:




<%




@







Page







Language




="C#"




MasterPageFile




="~/_layouts/application.master"








Inherits




="SPLocalization.LocalizedPage, SPLocalization, Version=1.0.0.0, Culture=neutral, PublicKeyToken=7a1fde53a908a4fe"



%>




<%



@





Register





Assembly



="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Namespace



="Microsoft.SharePoint.WebControls"



TagPrefix



="cc1"


%>





<





asp




:




Content







ID




="Main"







ContentPlaceHolderID




="PlaceHolderMain"







runat




="server">






<





cc1




:




SPLinkButton







Text




=""







runat




="server"







ID




="myLink"







/>











asp




:




Content




>




20. Update your LocalizedPage class to include myLink definition. We will also assign Text value in OnLoad method. You class should look like this:





public







partial







class







LocalizedPage



:



LayoutsPageBase






{






protected







SPLinkButton



myLink;






protected







override







void



OnLoad(



EventArgs



e)





{





myLink.Text =



"Our test link"



;





}





}



21. Let's do full deploy again


22. Once it is successfully deployed. Open LocalizedPage in the browser again. You should see that now you have a link on the page. This link is provided by SharePoint SPLinkButton control.








In the next article, we will discuss localization of the sample application we created...






Read more on this article...
Bookmark and Share