maandag 6 oktober 2008

Dynamically generating styles in code and localization

Today I ran into a stupid problem: For a company I am working on webparts that support localization. The styles are generated dynamically, depending on the amount of items to show in a table. When using the English regionsettings, everything worked fine, but when switching to another region, for example Dutch, whole my table was screwed up. After comparing the html source files by hand, the sources were completely the same!

Then I decided to use a tool, named winmerge (i love that tool!). That tool showed me that the styles that were generated, did differ! With the dutch region settings, the style that was generated for my cells, had a relative percentage with a "," in it, while in english it was a ".".

dutch: style="left:8,34%;"
english: style="left:8.34%;"

woensdag 23 juli 2008

Infopath: "generate" a unique number for your document

Last week I was busy on an infopath form for a sharepoint document library in combination with an approval workflow. I was wondering how to generate a simple, unique number to make sure that my document wouldn't overwrite other documents with the same title.

After a bit experimenting, I came across the following solution:
- Generate a new data connection to retrieve data from a sharepoint document library.
- make sure to select 'ID' from the specified document library
- make sure to get all data, not only the data for the current document.

When storing the document, create a button with the following ruleset:
-Query the data connection that you just created
- insert a function to compute the max(@ID) + 1, this is the highest available ID in the sharepoint lib + 1, so its always unique.
- add it in front or in the end of your title
- save & close ;)

donderdag 12 juni 2008

Granting full access to a user on all site collections within a web application

Today i got a question from a colleague about how to grant full access to a user on all existing site collections. This is not the first time that i got that question, so i decided to put a small blogpost covering this subject.

It's quite easy to do, you just have to now how:

Go to SharePoint Central Administration.
Click on the Application Management tab.
Under Application Security click on Policy for Web Application
Click Add Users
Confirm your settings on the screen (defaults should be what you want) and click Next
Now enter your user or group of users
Click the box beside Full Control – Has full control.
Click Finish

Piece of cake, isnt it?

vrijdag 6 juni 2008

Iterating through user profiles

some time ago i wrote an article about iterating through user profiles for non-admins (click here).

Today, i had the same problem, but my solution that was given, isnt optimal. I had to iterate through about 50.000 user profiles, that was a bit slow ;) And, only administrators could iterate through those profiles, even RunWithElevatedPrivileges didnt work.

I am glad that Microsoft hotfixed this problem (on the 8th of may 2008), you can download it here:

KB 952294. Now everyone can iterate through user profiles..

dinsdag 20 mei 2008

restoring Inheritance

For my current project, I used Tzunami to migrate all the WebApps, SPSite and SPWeb structures. But I had one problem: the site owners werent allowed to view the lists and listItems on their sites anymore: this was due to the fact that Tzunami doesnt inherit SPRoleAssignments from the parentSite when migrating the items.

Following code resets the inheritance:

dinsdag 13 mei 2008

Starting Sharepoint 2007 development

Paul Andrew has made a blog entry with good information on starting sharepoint development.

It provides links to courses, free online courses by microsoft, MCTS exams and a sample program. It's good information for everyone who wants to start sharepoint development!

url: http://blogs.msdn.com/pandrew/archive/2008/05/01/getting-started-with-sharepoint-development.aspx

dinsdag 29 april 2008

More about events

As I had problems with the way events worked, I wanted to know more about the way they worked. below is bit of information about events that is good to know:

Two different kinds:
There are two different kinds of events: Synchronous and Asynchronous events.

* Synchronous events
- End with -ing (ItemAdding, ItemUpdating)
- Block the code as long the events arent finished
- Can (thus) make use of Session vars (HttpContext.Current is available)
- Can be used to prevent the event being finished. For example, with the ItemUpdating event you can verify the content being updated. Is the update not valid, the SPItemEventProperties.Cancel can be set to true and the SPItemEventProperties.ErrorMessage can be filled with an error message. This error message will not be displayed, but is written to the logfile. An error page will appear.
- dont contain the SPListItem in it's SPItemEventProperties (well, in the case of ItemAdding, that is, I didnt test it for the other events), so when you want to make changes to any of it's field you need to make use of the SPItemEventProperties.AfterProperties (see message below).

* Asynchronous events
- End with -ed (ItemAdded, ItemUpdated)
- Dont block the code.
- Can't (thus) make use of the Session (HttpContext.Current is null)
- Can't be reverted; When these events are fired, the item is already added, updated or deleted.
- contains the SPListItem. Changes can be made if you want to. This fires the (for example) ItemUpdating and ItemUpdated event. This can be turned off by using this.DisableEventFiring();

When creating a new list, events like FieldAdded and FieldAdding are not fired!

vrijdag 25 april 2008

modify SPListItem fields with vars from the Session in the itemAdding or itemAdded events

Yesterday, a colleague of me and I tried to alter a field of a SPListItem right after the moment that the item has been created. We stored a var in the sessionState and tried to read that one, so we could use that variable to alter the newly created SPListItem.

We found out that this was not as easy as we thought.

When using the itemAdded event, the httpContext is not available, so it's not possible to get the vars out of the HttpContext.Current.Session

When using the itemAdding event, the HttpContext is available, but the properties.ListItem is null then. This is expected behaviour, because the SPListItem is only filled with data from the database and in the itemAdding event, no write actions to the database have been executed yet.

In this case, the properties.AfterProperties can be used to modify a field on creation.

below is an example that shows whether the HttpContext and properties.ListItem are null or not shows how to modify properties when an item is added.



donderdag 17 april 2008

Crawl through all links

As I am still working on a migration project, the customer asked me if it was possible to check all urls in content editor webparts that I migrated. As i serialized all webparts on a 2003 to xml (for importing them to 2007), it was very easy for me to locate all content editor webparts, scan it's content and report the external, non-existant urls in it and change/report the internal, non-existant url's.

Reflection...

Today I got the question from a collegue if it was possible to instantiate a class, based on a result from a computation, without making use of an if-then-else, case or any other (logical) construction.


We found the answer in using reflection and attributes.
first of all, we created a class "ClassType":



After that, we created some classes, with each its own attribute:


after that, the attributes of a class in a certain assembly can be read (a little bit of pseudo code ;)

dinsdag 1 april 2008

Import user accounts

As you were able to read in my post before, I had a problem with disabled users. I decided that i didnt want any disabled users in my shared service provider, but how could i manage that? Luckily, there is a Microsoft KB article which helped me with not importing disabled accounts. By making smart use of the ADSearch, only enabled accounts are retrieved:

Adding users to a site

Today I was busy on migrating some data using the Tzunami tool, until I got the following error:

SPS2003HandlerException: Principal 'xxx\xxx' cannot be resolved. I was sure about it that the user existed in the SSP and but to be sure, I checked it a second time. And yes: it existed. So i tried to add that user manually to a site, but the user couldnt be added. I stumbled on that problem for about an hour, until i accidentally checked something in the AD:



Account disabled... SIGH!

vrijdag 28 maart 2008

iterating through user profiles

Today i was busy on migrating some userprofiles and for some reason i had to iterate through the existing userprofiles in the MOSS 2007 environment.

I got a nice, lovely errormessage saying that I wasn't allowed to enumerate through all userprofiles, because i didnt have the rights to "Manage user profiles".

Dave Hunter's Blog (http://www.davehunter.co.uk/Blog/default.aspx) provided me the solution:

iterate through a set number!

woensdag 19 maart 2008

Feature stapling - custom code OnSiteCreation

I wanted to enable some features upon a siteCollection creation, but i couldnt find an eventhandler which I could use to run some code in to activate those features. I found out that sharepoint is missing a OnSiteCreation event :(. After some search on the web, I found out some possibilities:

1) alter the SiteTemplates
This was no option for me: too much work for a lazy person like me
2) feature stapling
This was a technique I never heard of: A new, farm wide feature is created, with a
reference to a stapling.xml. That file is filled with:
* elements
* FeatureSiteTemplateAssociation Id: the feature that needs to be installed
* TemplateName

This way, features can be activated to some siteDefinitions (for example STS#1, BLANKINTERNET#0) or to the GLOBAL definition!

When making a custom feature with a OnFeatureActivated method, and that feature is deployed to GLOBAL, code can be executed ON SITE CREATION!

NOTE: the STS#1 (blank page) has a setting "AllowGlobalFeatureAssociations" set to false. This way, the STS#1 sitedefinition needs to be added to, when you want to execute custom code to be executed when any site is created.

Migration - QuickLaunch

It has been a long time ago since i made my last post, but that's because of my current project, there's a lot of pressure on it at the moment.

At the moment i am busy on migrating a SP2003 environment to a SP2007. Due to the customers wishes, this is a very time consuming project. I decided to write a nice tool to help me migrate all the webparts, it's data and the navigation, since we used Tzunami for the content migration. It helped me in some way, but i can say: it isnt worth it's money. 12000 Euro for a product that has a lot of bugs and misses a lot of functionality.

well, let me go further on the main subject ;) I was asked if it was possible to maintain all links in the quicklaunch and in the same order. At first, it didnt look too good for me: The object model of sharepoint 2003 didnt allow me to gather information about the quicklaunch menu.

So I decided to write a query to get the navigation information out of the database and stored into an xml file:




In SP2003, the order of Documents, PictureLibrary, Lists are in an order that is always the samen (when nog changed). In SP2007, the order depends on the order of creating doclibs and lists.

For example, when a list is created first and after that, a doclib is created, the menu "List" will be displayed above "Document Library" (or whatever its name is ;))

This problem is tackled with the following code:
- make all lists and doclibs invisible in the quicklaunchmenu:




afther that,

use this function to add items:



use the following function to add the items to the quicklaunch: they are added to the right subMenu


And use this function to put the subMenu items in the right order:

dinsdag 29 januari 2008

generic feature to install a custom masterpage

During my current job i needed to create a few new visual designs and install them as a masterpage. I searched the internet and found a custom masterpage changer, and used it. But it didnt fullfill my needs. WHen using this solution, i needed to have 7 assemblies for 7 masterpages. I changed the code and the feature.xml a bit to make it more generic. The code isn't too neat, it's more of quickly hacked ;)

How to use it?

Create a new dir with the name of your feature. Put an elements.xml, feature.xml and a dir with the name of your masterpage([name]). put the NewDesign.master, and custom css files (and maybe some other files) into that directory.

FeatureName
|- [Name]
|- Feature.xml
|- Elements.xml

put the ChangeMaster assembly into the GAC.


feature.xml:

<?xml version="1.0" encoding="utf-8" ?>
<Feature xmlns="http://schemas.microsoft.com/sharepoint/"
Id="90969656-A1C9-4f40-A95F-A3BDDF5723BF"
Title="Use Custom Master File"
Scope="Web"
ReceiverAssembly="CustomMaster, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=b2f164534d725b81"
ReceiverClass ="CustomMaster.ChangeMaster"
>
<ElementManifests>
<ElementManifest Location="Elements.xml"/>
</ElementManifests>
<Properties>
<Property Key="VisualDesignName" Value="[Name]" />
</Properties>
</Feature>



elements.xml:

<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="AddMasters" Url="_catalogs/MasterPage" >
<File Url="[NAME]/NewDesign.master" Type="GhostableInLibrary"
IgnoreIfAlreadyExists="True"/>
<File Url="[NAME]/custom.master" Type="GhostableInLibrary"
IgnoreIfAlreadyExists="True"/>
<File Url="[NAME]/custom.css" Type="GhostableInLibrary"
IgnoreIfAlreadyExists="True"/>
<File Url="[NAME]/custom_tables.css" Type="GhostableInLibrary"
IgnoreIfAlreadyExists="True"/>
</Module>
</Elements>


using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;

namespace CustomMaster
{
public class ChangeMaster : Microsoft.SharePoint.SPFeatureReceiver
{
public override void FeatureInstalled
(SPFeatureReceiverProperties properties)
{
}
public override void FeatureUninstalling
(SPFeatureReceiverProperties properties)
{

}
public override void FeatureActivated
(SPFeatureReceiverProperties properties)
{
string strKey = "VisualDesignName";
string name = properties.Definition.Properties[strKey].Value;

SPWeb CurrentWeb = properties.Feature.Parent as SPWeb;
string filler = "/";
if (CurrentWeb.ServerRelativeUrl == "/")
{
filler = "";
}
CurrentWeb.MasterUrl = CurrentWeb.ServerRelativeUrl + filler + "_catalogs/masterpage/" + name + "/NewDesign.master";
CurrentWeb.CustomMasterUrl = CurrentWeb.ServerRelativeUrl + filler + "_catalogs/masterpage/" +name + "/custom.master";
CurrentWeb.Update();
}
public override void FeatureDeactivating
(SPFeatureReceiverProperties properties)
{
SPWeb CurrentWeb = properties.Feature.Parent as SPWeb;

string filler = "/";
if (CurrentWeb.ServerRelativeUrl == "/")
{
filler = "";
}

CurrentWeb.MasterUrl = CurrentWeb.ServerRelativeUrl + filler + "_catalogs/masterpage/default.master";
CurrentWeb.CustomMasterUrl = CurrentWeb.ServerRelativeUrl + filler + "_catalogs/masterpage/default.master";
CurrentWeb.Update();
}
}
}