Wednesday, July 8, 2009
SharePoint regional settings double value bug
Here is an interesting one...
I had to set the regional settings of my SharePoint site to Hungarian, so the DateTime fields get localized properly. This has been discussed previously here.
However, I started experiencing problems with the double values. Explanation by example:
(myField is of type SPFieldNumber)
double myDouble = 123.45;
myField.DefaultValue = myDouble.ToString();
Basically, I am trying to assign the value of a type double variable as the default value of my SharePoint field (which I am about to render).
At this point, when I check (using Quick Watch in Visual Studio) the value of myField.DefaultValue it shows 123,45 (which is ok, for the localization I am using). However, myField.DefaultValueTyped shows 12345. Looks like SharePoint is ignoring the fact that the site is localized and does not consider ',' to be in the role of a '.'
The solution I came up with is:
double myDouble = 123.45;
myField.DefaultValue = myDouble.ToString(CultureInfo.InvariantCulture);
Perhaps it is not the best, but at least the output now is:
myField.DefaultValue = "123,45"
myField.DefaultValueTyped = 123.45
Which is what is expected! Let me know if you know of a better way!
Read more on this article...
Tuesday, July 7, 2009
stsadm command line error
There is a common problem with stsadm being executed from the command line. For example, you run:
stsadm -o installfeature -name [featurename]
And you get "Command line error." in the console. It is because you have copy/pasted the command from your browser or somewhere else. There is a problem with the encoding.
Solution: Type the command in the console window, don't paste it.
Hope this helps. Read more on this article...
Monday, July 6, 2009
Installing WSS/MOSS on Windows 2008 x64 Step-by-Step guide
- Install IIS - WSS 3.0 requires IIS where the central administration site will be initially installed and after that all the SharePoint web applications. You can find some more details of how to do that here Installing IIS 7.0 on Windows Server 2008.
- Download WSS - Download Windows SharePoint Services 3.0 x64 with Service Pack 2 from the microsoft web site.
- Choose the installation - When you run the installation you need to select the Advanced option.

- Choose the Server Type - The Web Front End option should be selected

- Specify configuration database settings - Since all the servers in the farm will share one configuration database in the farm mode we have the chance to select a database for it on a different server, which is what we want in our deployment scenario. At this point we need to create an account that will be used to connect to the configuration database. Since our database is on a different server we must create it as domain account. A good practice is to call this account SPConfigAcct, so you know what it is being used for. Here is how the wizard page looks like after the needed information was provided:

- Configure SharePoint Central Administration Web Application - at this step the port number of the central administration application should be selected and the type of the security provider - either NTLM or Kerberos. The Kerberos provider is recommended but it requires some additional configuration by the domain administrator - the right Service Principal Names (SPNs) should be added.

- Complete the configuration - if the configuration was completed successfully you should see the following wizard page with overview of the WSS configuration:

- Configure Windows SharePoint Services Search service - two new domain accounts needs to be created at this point. One account for the search service itself (SPSearchAcct) and another one which will be used by the search service to access all the content (SPCrawlAcct). In order to configure the search service you will need to go to Central Administration -> Operations -> Services on Server:
As you can see initially the search service is stopped since it is still not configured. When you select the search service you will be able to provided the needed information.
At this point you need to provide the two accounts - SPSearchAcct and SPCrawlAcct that was previously created. NOTE: when you provide the accounts make sure that you also add the domain name. Otherwise you will not be able to start the search service and an error will be shown. - Setup the outgoing e-mail settings
Now that we have our WSS installed we can create our web application that will contain our site collection with the site which we want to use for our development.- Create the new web application - to create the new web application you need to go to Central Administration -> Application Management and select the Create or extend Web Application:

After you click on the Create a new Web Application you will be presented the screen where you have to provide the settings for the new web application:
You can change the port on which the web application will be created at, which in our case is 9000.
We need to create a domain account for the application pool of our new web application. Following our naming convention we can name the account SPContentPoolAcct and provide the name (together with the domain name) and the password in the Application Pool section. For the database server where the content database will be created we select the same sql server as we did for the configuration database. By default the database name created for the new web application has the name WSS_Content_{GUID}, but I found out that it is much more easier to give it a more meaningful name, which will help us find it faster in the SQL server. A good name is WSS_Content_{Port Number}. - Create Site Collection - once the web application is created we need to create the site collection together with the root site.

At this point you need to provide the site template that will be used for the root site and the account of the site administrator, who will be responsible for managing the site. 
- if you are creating custom SharePoint application in most of the cases you will use the API, being it from the custom pages you create or from external applications that are connecting to the particular SharePoint site. I've seen couple of cases when the developers are using the root site for their application and when it comes the moment to move it to production where it should be in a subsite it happens that it is not working correctly.
- sometimes it happens that there is something bad in your site as a result of improper provisioning, missing files, etc and it is much faster to recreate everything than trying to figure out what exactly is wrong. Having your application provisioned to a subsite will give you the opportunity to only delete this particular subsite. If you delete the root site you will delete all its child sites, which is not what we want to do.
Read more on this article...
Friday, July 3, 2009
Check if user belongs to K2 role
In your SharePoint (or other) / K2 solution, you may find yourself in a situation where you want to check if the current user belongs to a given K2 role. Perhaps, you want to allow only users who belong to particular K2 role to view a page, or you want to show a different page depending on the K2 role the user belongs to.
Surprisingly, K2 API does not provide a convenience method to do that. You have to implement it yourself. Even further, K2 Roles may contain ActiveDirectory groups and you will have to check if the current user belongs to them! In this article, we will discuss how all this is being done.
In the project where you will be implementing this method, make sure you have references to those libraries (which should be in the GAC):
- SourceCode.HostClientAPI
- SourceCode.Security.UserRoleManager.Management
First, let's think about the signature of our method. It needs to return a boolean and receive a K2 role name as parameter:
public static bool CurrentUserBelongsToK2Role(string roleName)
That should do it. Now let's start implementing the method. We need to get the name of the current user.
string loginName = WindowsIdentity.GetCurrent().Name;
(By the way, WindowsIdentity is in System.Security.Principal)
What we have so far is:
public static bool CurrentUserBelongsToK2Role(string roleName)
{
string loginName = WindowsIdentity.GetCurrent().Name;
// TODO: Implement further
return false;
}
Not much. We need to get an instance of K2's UserRoleManager class. We will create a separate method which returns us an instance of this object:
public static UserRoleManager GetUserRoleManager()
{
UserRoleManager userRoleManager = new UserRoleManager();
userRoleManager.CreateConnection();
userRoleManager.Connection.Open(GetSCConnectionStringBuilder().ToString());
return userRoleManager;
}
And implement a second method, to get us a GetSCConnectionStringBuilder object.
public static SCConnectionStringBuilder GetSCConnectionStringBuilder()
{
SCConnectionStringBuilder connectionString = new SCConnectionStringBuilder();
connectionString.Authenticate = true;
connectionString.Host = ConfigurationSettings.AppSettings["K2Server"];
connectionString.Integrated = true;
connectionString.IsPrimaryLogin = true;
connectionString.Port = uint.Parse(ConfigurationSettings.AppSettings["K2ServerPort"]);
return connectionString;
}
Basically, this method will read K2Server and K2ServerPort keys from your config and use them for connection to K2. You are free to implement it any way you like. Now, when we have those two helper methods, we can finally do:
UserRoleManager userRoleManager = GetUserRoleManager();
Using the Role Manager, we will try to get an object representation of the K2 role we are checking against:
Role role = userRoleManager.GetRole(roleName);
if (role == null)
{
throw new Exception("Role " + roleName + " not found!");
}
So this piece of code will throw an exception in case the role we are trying to check does not exist. If it exists, our Role object will not be null. Now we need to enumerate the RoleItems within the role. Basically, it will give us all the users (or AD groups) included in this K2 role:
foreach (RoleItem roleItem in role.Include)
{
string currentRoleName = roleItem.Name;
}
Again, let's see what we have so far:
public static bool CurrentUserBelongsToK2Role(string roleName)
{
string loginName = WindowsIdentity.GetCurrent().Name;
UserRoleManager userRoleManager = GetUserRoleManager();
Role role = userRoleManager.GetRole(roleName);
if (role == null)
{
throw new Exception("Role " + roleName + " not found!");
}
foreach (RoleItem roleItem in role.Include)
{
string currentRoleName = roleItem.Name;
// TODO: Implement here
}
return false;
}
So far so good, at least we are now able to enumerate all items within a K2 role! Let's keep going and finish the implementation as marked in bold above.
The currentRoleName object could be returned to us in the format of K2:DOMAIN\Name. We need to normalize it to DOMAIN\Name (i.e. remove the 'K2:' part).
if (currentRoleName.IndexOf(':') > -1)
{
currentRoleName = roleItem.Name.Split(':')[1];
}
Now... the roleItem object we have (in the foreach statement) is of base type RoleItem. There are two classes which inherit from it - UserItem and GroupItem. This means, the current roleItem could be any of the two, which will help us understand if we are dealing with a single user or an ActiveDirectory group added to the K2 role. Let's detect this:
if (roleItem is UserItem)
{
// TODO: Implement
}
else if (roleItem is GroupItem)
{
// TODO: Implement
}
Now we know when we are dealing with user and when with AD group. We just need to implement both cases. Implementing the UserItem case is as easy as comparing the currentRoleName to the loginName:
if (currentRoleName.ToLower() == loginName.ToLower())
{
// Found a USER match
userRoleManager.Connection.Close();
return true;
}
Implementing the GroupItem case is a little bit trickier. First, we need to implement a method which will return us a string array of the AD groups the current user belongs to, so we can later compare against them. Here is the code you will need:
private static string[] GetCurrentUserADGroups()
{
List
// Get the Groups of the current user
foreach (IdentityReference group in WindowsIdentity.GetCurrent().Groups)
{
// Ad the group to the list
NTAccount ntAcctGroup = group.Translate(typeof(NTAccount)) as NTAccount;
groups.Add(ntAcctGroup.Value.ToLower());
}
return groups.ToArray();
}
With the help of this method, we can get back to the implementation of the GroupItem case now:
else if (roleItem is GroupItem)
{
string[] loginNameADGroups = GetCurrentUserADGroups();
}
Then, we simply check if the loginNameADGroups contain the currentRoleName string (which in this case is an AD group).
if (loginNameADGroups.Contains(currentRoleName.ToLower()))
{
userRoleManager.Connection.Close();
return true;
}
And at the end of our CurrentUserBelongsToK2Role method we just have to add:
// No match found
userRoleManager.Connection.Close();
return false;
And that's all! So, let's see now how the whole thing looks:
public static bool CurrentUserBelongsToK2Role(string roleName)
{
string loginName = WindowsIdentity.GetCurrent().Name;
UserRoleManager userRoleManager = GetUserRoleManager();
Role role = userRoleManager.GetRole(roleName);
if (role == null)
{
throw new Exception("Role " + roleName + " not found!");
}
foreach (RoleItem roleItem in role.Include)
{
string currentRoleName = roleItem.Name;
// Is it K2:DOMAIN\Name ?
if (currentRoleName.IndexOf(':') > -1)
{
// Get DOMAIN\Name
currentRoleName = roleItem.Name.Split(':')[1];
}
if (roleItem is UserItem)
{
if (currentRoleName.ToLower() == loginName.ToLower())
{
// Found a USER match
userRoleManager.Connection.Close();
return true;
}
}
else if (roleItem is GroupItem)
{
string[] loginNameADGroups = GetCurrentUserADGroups();
if (loginNameADGroups.Contains(currentRoleName.ToLower()))
{
userRoleManager.Connection.Close();
return true;
}
}
}
// No match found
userRoleManager.Connection.Close();
return false;
}
Of course, you can find the whole project attached here.
Read more on this article...
Thursday, July 2, 2009
How to create a K2 SmartObject
How does K2[blackpearl] integrate with other systems? One standard way to do this is through Web Services. And how does K2[blackpearl] integrate with Web Services? It is done through SmartObjects. So we have: K2 <-> SmartObject <-> Web Service <-> Other System
"A K2 SmartObject can encapsulate data that does not currently reside in an existing system, such as data about an employee's extracurricular activities, his or her family, or hobbies and interests. By using a SmartObject for employee information, all data about an employee is accessed in the same place, and updates are applied to the data at its source. It is important to remember that SmartObjects do not store or cache data. Once created, a SmartObject can be used and reused in a variety of ways; within K2 Designer for Visio, integrated for use in an InfoPath form, a workflow deployed via K2 Studio, in a report, in SharePoint, etc."
SmartObject is all that and also it is the link between K2 and Web Services. So let's see step by step how do we consume a Web Service method.
1. Create Web Service
First, let's create a simple web service which we will be consuming later. For the purposes of the demonstration, it will be as simple as returning you the sum of two values.
I will use Visual Studio 2008 to create a new 'ASP.NET Web Service Application' project.

Let's create the simple method 'Sum'. See screenshot below:

Of course, you should test it first:

For the curious, it returned 3 ;)
Ok, now we need to publish it somewhere in the network and make sure we can access it! Right click the solution, Publish.

'Publish' it somewhere on your local hard drive and make sure you map it in IIS to a virtual directory. You can do that by going into IIS, choose existing site (or create a new one), right click on it -> New Virtual Directory, browse to the path where you Published the service and keep clicking Next.
Let's assume you published your service to http://localhost:86/SampleService/SampleService.asmx, which in the local network (if you have one) maps to http://192.168.0.1:86/SampleService/SampleService.asmx.
We are done with the service, it's in the network, it works, it is ready to be registered as a service instance.
2. From K2 Workspace add it to Dynamic Web Service
Ok, now we have to add it to the 'Dynamic Web Services' list. Open your K2 Workspace. Go to Management -> Management Console -> Expand the servername -> SmartObjects -> Services -> Dynamic Web Service.

Click on the Add button and populate the URL field.

Click Next. In the next screen set names to something that makes sense to you and click Save. Let's call it 'Sample Service'.

You have now registered a service instance. Let's verify that by going to the K2 server and opening C:\Program Files\K2 blackpearl\ServiceBroker (or C:\Program Files (x86)\K2 blackpearl\ServiceBroker) and run BrokerManagement.exe. On the first screen, click on 'Configure Services'.
Under services -> DynamicWebService -> serviceinstances there should be an instance with a name 'SampleService'.

3. Create SmartObject
We are good to create a SmartObject around it now. Start a new instance of Visual Studio and create a new K2 SmartObject project.

The first thing you do is switch to Advanced mode.

Then Remove All methods that were created for you. We will start from scratch.

Then click on +Add and run the wizard that shows up in Advanced mode (it's a checkbox on the first page).

Name the method Sum, make it Read type.

In the next screen, Configure Method Parameters, we will add the two parameters that we have to pass to the method - intA and intB. Make them 'Number's.

At the end, it should look like this:

Click Next. On the next screen you have to select the Service Method we will be executing. This is easy. First click Add, to start adding a service method call to the SmartObject. A new popup will show. There is a field 'Service Object Method' and next to it there is a '...' button. Click on the button. It will open the Context browser. Browse through ServiceObjects Server -> Sample Service -> Sum -> Read.

Click Add in the Context Browser. You will see:

We need to map some fields now. See the 'a' and 'b' input properties? They are the web service method input parameters. We have to 'bind' them to the intA and intB fields we created for the SmartObject.
Click on the first one ('a'), click 'Assign' button and a small pop up will appear. Set 'Map to' to 'SmartObject Method Parameter' and from the second dropdown select 'intA'.

Then click ok and do the same thing for 'b', but select 'intB' instead. For 'ReturnValue' - click on it, Assign, set 'Map To' to SmartObject Property (i.e. you are storing the result in a property of the SmartObject). Then click Create.

You should end up with:

Click OK and finish the wizard.

Make sure you name your SmartObject properly.

4. Deploy
Right click the solution and do Rebuild. It should compile with no errors. Let's deploy the SmartObject to our K2 Server. Right click the project -> Deploy. Follow the wizard (Next, Finish) and your SmartObject should be deployed.
5. Test
Let's test your SmartObject. Log into the K2 server and go back to C:\Program Files (x86)\K2 blackpearl\ServiceBroker. Run the file SmartObject Service Tester.exe.
Expand the SmartObjects section, find your SampleServiceSmartObject, expand it until you see the Sum method.

Right click on it, Execute method. Populate the input fields and click 'Sum' button. Promptly, you should see the result displayed.

We just invoked the SmartObject, which talked to the WebService, retrieved result for us and was visualized in the test utility. Everything works perfectly, your SmartObject is ready to be consumed from a workflow!
6. Consume in Workflow
Create a new K2 Process Project in Visual Studio. I will reuse the K2 solution we already have and just add the new project to it.

From the Toolbox, drag and drop a new SmartObject event. On the second page, Wizard will prompt you for SmartObject Method. Browse to our Sum method in the SampleServiceSmartObject.

Click Add. It should look like this:

On the next screen, you have to provide values for the SmartObject input properties. This could be K2 DataFields from your process, or actual hardcoded sample values. For the purpose of the demo, we will just bind them to '2' and '54'. Select the property, click Assign button, set value, click ok. It should look like this:

On the next screen, we have to bind the return result to something. Let's bind it to a DataField in the process which we will create. Click on the ReturnValue, click 'Assign', in the popup click on the '...' button which will open the Context Browser. In this browser, either select already existing DataField, or create a new one on the fly (right click -> create). Make sure the field is Integer (for this demo). Select it, click 'Add', then 'Ok' in the pop up.

It should look like:

Click finish. You just finished adding a SmartObject event to your workflow. After this event is executed, you will have the value of 'a' + 'b' in the SumValue data field. I.e. you are consuming the SmartObject (and hence the Web Service).

Now you just need to finish the rest of the workflow ;)
7. Change the URL of the service
What if the address of your Web Service changes? Simple, go to the K2 server and open the C:\Program Files (x86)\K2 blackpearl\ServiceBroker folder. Run the BrokerManagement.exe app. On the first screen, click on 'Configure Services'.

Find your service instance again:

Right click on it -> Configure Service. It will open a pop up. There you can change the URL field and click Save.

And that's it, your service instance is now pointing to the right URL.
Basically that's all. Now you know how to consume web services from your K2 Workflows, utilizing SmartObjects!
Hope this helps!
Read more on this article...
SharePoint bug tracking
So you have this SharePoint project you are working on... And as with any project, you need a bug tracker in order to communicate properly with the QAs and BAs in the company. What do you use? OnTime? Mantis? Bugzilla?
Why not SharePoint itself? It's already (presumably) set up for you, users are familiar with it and you are only couple of steps away from setting up your own Issue Tracker.
To get it up and running, here is what you should do, step by step:
1. Open your SharePoint web site as admin
2. Click on 'View All Site Content' (usually top left) or open http://server:port/_layouts/viewlsts.aspx
3. Click on 'Create' to create a new list (shortcut: http://server:port/_layouts/create.aspx)

4. From the 'Tracking' column select 'Issue Tracking'
5. Type in the name and description of the list where issues will be tracked
You may want to select 'Yes' on 'Send e-mail when ownership is assigned', to send emails to the users assigned to issues.
6. Click 'Create' button and your bug tracker is created. You should see a list with the name you have given on step 5. This is the tracker, open it. Click 'New Item' to see the default fields and their values you get.

7. Time to customize according to your needs. Go to the List Settings of your issue tracking list.

8. Scroll down to the Columns section. You will want to modify the highlighted columns - Issue Status, Priority and Category, for starter.

9. Click on any of them, let's say 'Issue Status'. Scroll down to the 'Additional Column Settings'

You can now change the possible statuses your issues may have. For example, you may want to add 'Acknowledged', 'Postponed', 'Invalid', 'Cannot fix', etc... You may also change the default value or make the field required (a smart choice). When you are happy with your changes, click Ok.
10. Do the same for Category and Priority.
11. You can extend your tracker further, by adding extra columns. Let's say, Product (as you may be working on different products at the same time) or Version (as your product may have many production versions). To do this, go to the Settings of the list again (as in step 7). Scroll down to columns and click Create Column.

12. Type in the name of your column and select its type. In this case, I will name it 'Product Version' and make it a dropdown list.

13. Make the column required (if you want to), specify the available choices and select the type of the control (dropdown, radio buttons or check boxes).

Then click Ok and you are all set.
14. Let's test your bug tracker. Go to the list and create a new item

15. Fill all the fields you want to and click Ok. You have now logged a new Issue.

16. To customize your tracker further, you may want to create new Views, which sort and arrange the items in categories, assignees and what not

17. You may need to add users (give permissions) to the site or the list.
Now you have your own tracker, embedded in your SharePoint environment. Hope this was helpful!
Read more on this article...
Wednesday, July 1, 2009
SharePoint Development tools list
SharePoint development is not always as straight-forward as we all want it to be. However, there are a lot of tools which can make our job easier. Here is a list of some of our favorite tools for SharePoint development:
SharePoint Manager 2007
"The SharePoint Manager 2007 is a SharePoint object model explorer. It enables you to browse every site on the local farm and view every property. It also enables you to change the properties (at your own risk). This is a very powerfull tool for developers that like to know what the SharePoint holds of secrets. "
STSDEV: Simple Tools for SharePoint 2007 Development
"STSDEV is a proof-of-concept utility application which demonstrates how to generate Visual Studio project files and solution files to facilitate the development and deployment of templates and components for the SharePoint 2007 platform including Windows SharePoint Services 3.0 (WSS) and Microsoft Office SharePoint Server 2007 (MOSS)."
WSPBuilder
"A SharePoint Solution Package (WSP) creation tool for WSS 3.0 & MOSS 2007"
Windows SharePoint Services 3.0 Tools: Visual Studio 2008 Extensions, Version 1.2
"Tools for developing custom SharePoint applications: Visual Studio project templates for Web Parts, site definitions, and list definitions; and a stand-alone utility program, the SharePoint Solution Generator."
U2U CAML Query Builder
"CAML (Collaborative Application Markup Language) is an XML-based query language that helps you querying, building and customizing Web sites based on Windows SharePoint Services. The XML elements define various aspects of a WSS site. The tool will help you build, test and execute your CAML Queries. "
I don't think anybody wants to write CAML Queries 'by hand'
SharePoint Timer Scheduler
"Sharepoint Timer Scheduler gives you a way of running your Timer Job from a list in the Root Web of your Sharepoint site. Excellent way of giving Site Administrators access to Timer Jobs so they can be run immediately or rescheduled. No need to code custom SPJobDefinition classes."
Basically, this is a 'tool' which allows you to reschedule timer jobs without the need to redeploy a solution! Great!
Pako Simeonov's Tool
Pako Simeonov wrote a great tool which fixes errors of the kind 'Feature {Guid} for list template is not installed in this farm. The operation could not be completed'
What are your favorite tools?
Read more on this article...
SharePoint DateTime Format and Regional Settings
Now, how exactly do you control the DateTime string representation and formatting in a SharePoint application?
Normally, in an ASP.NET Web Application, it depends on the Regional Settings (Control Panel -> Regional and Language Options) of the user running the application pool of the site. Unless you override the Culture options in the code.
So when I tried to 'localize' the DateTime representation for my SharePoint site, by changing the app pool user's Regional settings, at first I was surprised it didn't do the job. DateTime was still shown in the default en-US format.
This means SharePoint overrided the Regional settings of its application pool user. It took me a couple of minutes before I realized that for SharePoint applications, the DateTime formatting is controlled by opening the site -> Site Actions -> Site Settings -> Site Administration (column) -> Regional Settings

There, you have the option to fine tune the regional settings for the site, which will also affect the DateTime formatting.

After setting it to the one you desire, you save the settings and next time you open the site, DateTime will be formatted according to the locale you have selected.
Hope this helps!
Read more on this article...
Tuesday, June 30, 2009
Append new line to SPFieldMultiLineText in SharePoint
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...
Passed 70-541 exam today
Read more on this article...
Monday, June 29, 2009
Beware using GetFieldValueAsHtml or GetFormattedValue with DateTime fields
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...
Monday, June 22, 2009
STSADM Technical Reference for Microsoft Office Sharepoint Server 2007
http://technet.microsoft.com/en-us/office/sharepointserver/cc948709.aspx Read more on this article...
Tuesday, June 16, 2009
Fixing 'Feature {Guid} for list template is not installed in this farm. The operation could not be completed'
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.
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.
Monday, June 15, 2009
How to create List Templates and List Instances with VSeWSS 1.3
- 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.
- 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.
- Open the SharePoint Solution Generator 2008 and select List Definition
- 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.
- 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.
Read more on this article...
Thursday, June 11, 2009
iTechnology Forms Accelerator Videos
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...
Tuesday, June 9, 2009
The right way to SPWeb.EnsureUser in SharePoint
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...
Monday, June 8, 2009
SPTimerScheduler for SharePoint Improvement
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...
Friday, June 5, 2009
Sending email from SharePoint
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...
Tuesday, June 2, 2009
Sample SharePoint project localization Part 2 of 2
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...
Sample SharePoint project localization Part 1 of 2
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.

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...








