Customizing MySite : SharePoint 2007


MySite customization
In this article we will discuss how we can personalize MySite.
To customize the MySite we will use the Feature Stapling.  We will do the following :
-          Adding custom list instance to the MySite
-          Changing navigation and MySite Theme
-          Creating a new Page when MySite is being created.
-          Deleting WebPart from the Default page of the MySite.
First of all we must create a feature Staple. In this article we will use the WSPBuilder instead of VSeWSS.

Creating the Feature Staple

-          First of all we will create an WSP Project



After creating the WSP project we must create a new WSPBuilder item “Feature With Receiver”



Then we must create another feature without receiver (blank Feature) that we will call “BOSSMySiteStapling” .
Now we have to add some code and configurations to theses two features.

Setting the Scope of the Features:

Feature
Scope
BOSSMySiteStaple
Farm
FeatureStaplerMySite
Web


 In the BOSSMySiteStaple we must associate the MySite template with the FeatureStaplerMySite. For that we must add the following line in the “element.xml” of this feature.
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <FeatureSiteTemplateAssociation Id="GUID OF THE FEATUREMYSITE" TemplateName="SPSPERS#0"/>
</Elements>

The SPSPERS#0  => SPSPERS (which indicate that’s a MySite template) # 0 (indicate the default template of the MySite)
So if you are using different templates for the MySite , just add other lines with the corresponding template ID.
Now the Feature stapling is completed, we can add code we want in the FeatureActivated event.
Adding Code to the FeatureStaplerMySite.cs

Adding a custom list instance to our MySite.

SPWeb web = (SPWeb)properties.Feature.Parent;
            SPFeatureCollection featureCollect = web.Site.Features;
            featureCollect.Add(new Guid("GUID OF YOUR LIST INSTANCE"), true);

Changing navigation and MySite theme

Changing navigation

As a first step we will delete all the existing site navigation
List<SPNavigationNode> nodeQuicklanch= new List<SPNavigationNode>();
            foreach (SPNavigationNode node in web.Navigation.QuickLaunch)
            {
                nodeQuicklanch.Add(node);
            }
            foreach (SPNavigationNode node in nodeQuicklanch)
            {
                web.Navigation.QuickLaunch.Delete(node);
            }
To add a new navigation we can use the following
SPNavigationNode node_1 = new SPNavigationNode("NODE1","FormServerTemplates/NODE1Page.aspx");

Changing the Theme of MySite

That’s a very easy step; we have just to apply a new theme to the web.
web.ApplyTheme("Simple");

Creating a new Page programmatically when MySite is created.

We will use web service offered by WSS. Using the web.ProcessBatchData()
  SPList list = web.Lists["Form Templates"]; // retrieving the Form Templates library

            // Query to create a new page and store it in the Form Templates library.
            string postInformation =
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                "<Method>" +
                "<SetList Scope=\"Request\">" + list.ID + "</SetList>" +
                "<SetVar Name=\"ID\">New</SetVar>" +
                "<SetVar Name=\"Cmd\">NewWebPage</SetVar>" +
                "<SetVar Name=\"Type\">WebPartPage</SetVar>" +
                "<SetVar Name=\"WebPartPageTemplate\">1</SetVar>" +
                "<SetVar Name=\"Title\">TITLE OF YOUR PAGE</SetVar>" +
                "<SetVar Name=\"Overwrite\">true</SetVar>" +
                "</Method>";

            // Processing Query
            string processBatch = web.ProcessBatchData(postInformation);
Your page will be created and stored in the Form Templates library. You can should whatever list you want.
You can also choose the WebPartPageTemplate
 <SetVar Name=\"WebPartPageTemplate\">1</SetVar
And you have the ability to overwrite the file if it’s already exists on the Library.
<SetVar Name=\"Overwrite\">true</SetVar>"

Removing existing WebPart in the default page of MySite.

The Default.aspx exists only if the MySite is fully provisioned.  So we will create a method that will run in a separated thread and will test if the MySite has been provisioned or not. If it’s provisioned ,it will remove the existing webparts in the Default.aspx page.
public void removeWebPart(object web2) {


            bool provisionned = false;

            SPWeb web = null;
           
            while (provisionned== false) {

                SPSite site = new SPSite(((SPWeb)web2).Url);
                web = site.OpenWeb();
                provisionned = web.Provisioned;
               
                Thread.Sleep(10000);
           
            };

            SPFile thePage = web.RootFolder.Files["default.aspx"];
            SPLimitedWebPartManager manager = thePage.GetLimitedWebPartManager(PersonalizationScope.Shared);
            List<Microsoft.SharePoint.WebPartPages.WebPart> webParts = new List<Microsoft.SharePoint.WebPartPages.WebPart>();
            foreach (Microsoft.SharePoint.WebPartPages.WebPart webPart in manager.WebParts)
            {
                webParts.Add(webPart);
            }
            foreach (Microsoft.SharePoint.WebPartPages.WebPart webPart in webParts)
            {
                manager.DeleteWebPart(webPart);
                manager.Web.Update();
            }

            manager.Dispose();
            web.Close();
           
        }
The thread.Sleep is used to wait a moment before testing again the status of the web.provisioned .
Now in the FeatureActivated event, we must call our method using a ParameterizedThreadStart.
So we will add the following lines:
ParameterizedThreadStart threadDelWebpart = new ParameterizedThreadStart(removeWebPart);
Thread thread = new Thread(threadDelWebpart);
thread.Start(web);

How to localize site columns using resource files

Localizing SharePoint 2007 columns consist on

  •  Create resource file for each language.
  •  Add $Resources:<ResourceFilename>,<Resource key name>; to the display name of the site column.

In this article we will create a resource file BOSSContentTypes.fr-FR.resx and a BOSSContentTypes.resx .
The BOSSContentTypes.resx will contain the English labels and the other the French labels of our site columns.

This is a preview of the BOSSContentTypes.fr-FR.resx
the other file contains the same names but English values.

After creating theses two files , deploy them into the 12/Resources  folder.

Now we have to map our Display name with the values in the resource files.

this is a simple snippet from the definition of our content type.


  <Field ID="{79831820-B700-4786-AD10-69F54BED193B}" ShowInDisplayForm="TRUE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" Type="Text" Name="Mission" DisplayName="$Resources:BOSSContentTypes,Mission;"
         StaticName="Mission" Hidden="FALSE" Required="FALSE" Sealed="FALSE" />
  <Field ID="{0F37DF23-F2AF-48ac-8EC4-126220290747}" ShowInDisplayForm="TRUE"
         ShowInNewForm="TRUE" ShowInEditForm="TRUE" Type="Text" Name="Client" DisplayName="$Resources:BOSSContentTypes,Client" StaticName="Client" Hidden="FALSE" Required="FALSE" Sealed="FALSE" />
Now just see what you have done.

How to run Code with elevated privileges

I had to create 2 calendars , first one is shared and located on the Root site and the other is personnal and located on the mySite.
The content type contains one field  AssignedTo . when the event is inserted in the Shared Calendar , it must also be add to the personal calendar of the User in the AssignedTo field.
The user who added the event has no rights on the Personal Calendar of the User in the AssignedTo field.
So the solution was to run the code with elevated privileges.

To do such a thing just user the SPSecurity.RunWithElevatedPrivilges

SPSecurity.RunWithElevatedPrivileges(delegate()
  {


you need to open the site,web and list inside the RunWithElevatedPrivileges. 


SPListItem newitem = list.Items.Add();
                                                                             newitem["ContentTypeId"] =
                                                                                 properties.ListItem["ContentTypeId"];
newitem["Title"] = properties.ListItem["Title"];
newitem["Location"] = properties.ListItem["Location"]; 
newitem["EventDate"] =properties.ListItem["EventDate"];
newitem["EndDate"] = properties.ListItem["EndDate"];  
newitem.Update();
} );


How to get the DisplayForm of an SPListItem

this is a code snippet that get the display form of an SPListItem

using (SPSite site = new SPSite("http://yoursite"))
{
    using (SPWeb web = site.OpenWeb())
    {
        SPList list = web.Lists[0];
        SPListItem item = list.Items[0];
        string diplayForm= String.Concat(item.Web.Url, "/",                   item.ParentList.Forms[PAGETYPE.PAGE_DISPLAYFORM].Url, "?id=", item.ID.ToString());
    }
}

great thx to http://www.peppedotnet.it

How to get Users Id from a UserMulti Field

If you try to get the ToString() value of a type of UserMulti field you'll get something like
"1;#Username" and for multi-user fields "1;#User1;#15;#UserX". 


It is not necessary that you create a method that parses this string to get the users ID. You can do it like that :


            SPListItem currentListItem = properties.ListItem;
            Guid guid= new Guid("A1075591-DBC7-4b9d-99F4-E03061F7716E");
            SPFieldUserValueCollection values = (SPFieldUserValueCollection)currentListItem[guid];

            foreach (SPFieldUserValue value in values)
            {

                int valueID = value.LookupId;
                string valueTitle = value.LookupValue;

            }


If you have a field of type Lookup and accepting multiple values you can use SPFieldLookupValueCollection.

Retrieve field value after ItemAdded event using its GUID

To retrieve the value of an item after the event ItemAdded , we can use the guid of the corresponding field.

 public override void ItemAdded(SPItemEventProperties properties)
        {

            SPListItem currentListItem = properties.ListItem;
            Guid guid= new Guid("A1075591-DBC7-4b9d-99F4-E03061F7716E");
            string value = currentListItem[guid].ToString();

        }

we can also use the Index of the field or the fieldname .

How to add custom content type to a custom list

To add a custom content type to a custom list definition , the simple way to do is to add some entries in the schema.xml of the list.

 - Just add the ContentTypeRef of the contents types you want to use .
 - Add EnableContentTypes="TRUE" in the <List > tag

<List xmlns:ows="Microsoft SharePoint" Title="YOURTITLE" Direction="$Resources:Direction;" Url="Lists/MYCALENDAR" BaseType="0" EnableContentTypes="TRUE" Name="NAMEOFLIST" Id="66179222-6e7b-431b-a573-e076593c81bd" Type="106" xmlns="http://schemas.microsoft.com/sharepoint/">
    <MetaData>
    <ContentTypes>
      <ContentTypeRef ID="0x010200d1ae5eb6622645518535cffe71d43480">
        <Folder TargetName="EntretienDebutMission"/>
      </ContentTypeRef>
      <ContentTypeRef ID="0x010200f2c756888542479f9b66f82140a2de40">
        <Folder TargetName="Evenement"/>
      </ContentTypeRef>
    </ContentTypes>


Sometimes i don't really know the reason but some of the fields are not shown when trying to create a new item in the list. I solved this problem by copying the field definition in the contents type to the schema.xml of the list. and it works fine.
If some know the reason why some fields are not shown ! just comment

Error when trying to deploy a sharepoint solution using VSeWSS 1.3


 If you get the following error :

Error: System.ServiceModel.ProtocolException System.ServiceModel.ProtocolException: The content type text/html; charset=utf-8 of the response message does not match the content type of the binding (text/xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 1024 bytes of the response were: '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">  <html xmlns="http://www.w3.org/1999/xhtml">  <head>  <title>IIS 7.0 Detailed Error - 500.0 - Internal Server Error</title>  <style type="text/css">  <!--  body{margin:0;font-size:.7em;font-family:Verdana,Arial,Helvetica,sans-serif;background:#CBE1EF;}  code{margin:0;color:#006600;font-size:1.1em;font-weight:bold;}  .config_source code{font-size:.8em;color:#000000;}  pre{margin:0;font-size:1.4em;word-wrap:break-word;}  ul,ol{margin:10px 0 10px 40px;}  ul.first,ol.first{margin-top:5px;}  fieldset{padding:0 15px 10px 15px;}  .summary-container fieldset{padding-bottom:5px;margin-top:4px;}  legend.no-expand-all{padding:2px 15px 4px 10px;margin:0 0 0 -12px;}  legend{color:#333333;padding:4px 15px 4px 10px;margin:4px 0 8px -12px;_margin-top:0px;   border-top:1px solid #EDEDED;border-left:1px solid #EDEDED;border-right:1px solid #969696;   border-bottom:1px solid #969696;background:#E7ECF0;font-weight:bold;f'. ---> System.Net.WebException: The remote server returned an error: (500) Internal Server Error.    at System.Net.HttpWebRequest.GetResponse()

Solution > run the following command :

"C:\Windows\Microsoft.NET\Framework64\v3.0\Windows Communication Foundation\servicemodelreg.exe" -i

Add a user pickup field in a custom content type

It's seems to be difficult to create a pickup field for users or groups but it's really easy.

This is a way to to this using the xml schema of our content type.

< Field ID="{47A9E79B-705B-4586-B697-01B2609CEC1F}" Name="AffectedPersons" BaseType="Text" DisplayName="Personnes affectees" Type="UserMulti" Sealed="TRUE" ShowInDisplayForm="TRUE" ShowInEditForm="TRUE" ShowInNewForm="TRUE" ShowInListSettings="TRUE" List="UserInfo" UserSelectionMode="0" UserSelectionScope="0" Mult="TRUE" SourceID="http://schemas.microsoft.com/sharepoint/v3" / >

This sample allow us to select multiple users. To select only one user you can use Type="User" instead of Type="UserMulti" and you should remove the Mult property.

If you got the error : The 'UserSelectionMode' attribute is not allowed. you should install the hotfix for sharepoint. http://support.microsoft.com/kb/934613

Deleting fields inherited from parent content type

When inheriting from existing content type we inherit all fields.
Some fields are some time not necessary so we need to delete theses fields from our Content Type.

This is a sample code of how to remove the unwanted fields.



So the solution is using the < removefieldref > tag and use the GUID of the unwanted field for the ID param.
To get the GUID of the field ,you can access the 12 folder and go to template/features and then to the parent Content type. then open the Schema.xml of the parent content type and find the specific GUID.

Hope this will be useful