Friday, September 17, 2010

Include Javascript and CSS Compression in Visual Studio

For a frontend developer it is a must to compress his javascript and css files, before deploying a website to a productive environment. In this post I'll demonstrate how you can automate this process in Visual Studio.

First of all download the YUI Compressor for .Net. (3.5)

Extract the zip file and copy the to files into a folder in your visual studio solution. I have a folder called "Libraries" for third part dll files.

YUI Dlls


Create a file "MSBuildCompress.xml" in a folder you want. I put this file in my "_Tools" folder.

Custom Compress Msbuild Task


Put the following xml in your newly created xml file.

<?xml version="1.0" encoding="utf-8"?>
<!-- Author Baris Bikmaz -->
<!-- MSBUILD Task to compress css and js files with the yui compressor -->
<Project xmlns="http://schemas.microsoft.com/developer/MsBuild/2003">
    <UsingTask
        TaskName="CompressorTask"
        AssemblyFile="../Libraries/Yui/Yahoo.Yui.Compressor.dll" />


    <PropertyGroup>
        <CssOutputFile Condition=" '$(CssOutputFile)'=='' ">$(OutputFolder)\css\main.css</CssOutputFile>
<CssDebugFile>$(OutputFolder)\css\main.debug.css</CssDebugFile>
        <JavaScriptOutputFile Condition=" '$(JavaScriptOutputFile)'=='' ">$(OutputFolder)\js\main.js</JavaScriptOutputFile>
    </PropertyGroup>
    
    <Target Name="CompressAndMergeFiles">
        <ItemGroup>
            <JavaScriptFiles Include="$(OutputFolder)\js\*.*" Exclude="$(OutputFolder)\js\main.*js"/>
            <CssFiles Include="$(OutputFolder)\css\*.*" Exclude="$(OutputFolder)\css\main.*css" />
        </ItemGroup>

<Message Text="========== Compressing Css and Js Files in $(OutputFolder) ==========" Importance="high" />

        <CompressorTask
            CssFiles="@(CssFiles)"
            DeleteCssFiles="false"
            CssOutputFile="$(CssOutputFile)"
            CssCompressionType="YuiStockCompression"
            JavaScriptFiles="@(JavaScriptFiles)"
            ObfuscateJavaScript="True"
            PreserveAllSemicolons="False"
            DisableOptimizations="Nope"
            EncodingType="Default"
            DeleteJavaScriptFiles="false"
            LineBreakPosition="-1"
            JavaScriptOutputFile="$(JavaScriptOutputFile)"
            LoggingType="ALittleBit"
            ThreadCulture="en-au"
            IsEvalIgnored="false"
            />

<Message Text="========== Creating debug file ==========" Importance="high" />
<ReadLinesFromFile File="%(CssFiles.Identity)">
<Output TaskParameter="Lines" ItemName="lines"/>
</ReadLinesFromFile>
<WriteLinesToFile File="$(CssDebugFile)" Lines="@(Lines)" Overwrite="true" />
    </Target>
</Project>

1. Check here that the path to the yui library is correct:


<UsingTask TaskName="CompressorTask"
 AssemblyFile="../Libraries/Yui/Yahoo.Yui.Compressor.dll" />

2. The propertygroup defines the files and their location which are generated by the compressor


<PropertyGroup>
 <CssOutputFile Condition=" '$(CssOutputFile)'=='' ">$(OutputFolder)\css\main.css</CssOutputFile>
 <CssDebugFile>$(OutputFolder)\css\main.debug.css</CssDebugFile>
 <JavaScriptOutputFile Condition=" '$(JavaScriptOutputFile)'=='' ">$(OutputFolder)\js\main.js</JavaScriptOutputFile>
</PropertyGroup>


3. The ItemGroup defines the files which should be compressed by the YUI compressor. I exclude all the files which will be generated because all css files, compressed and uncompressed are in the same folder. You can change this if you want.


<ItemGroup>
  <JavaScriptFiles Include="$(OutputFolder)\js\*.*" Exclude="$(OutputFolder)\js\main.*"/>
  <CssFiles Include="$(OutputFolder)\css\*.*" Exclude="$(OutputFolder)\css\main.*" />
</ItemGroup>

4. The CompressorTask does the compression. The files to compress and the the output files are given ase arguments.

<CompressorTask
            CssFiles="@(CssFiles)"
            DeleteCssFiles="false"
            CssOutputFile="$(CssOutputFile)"
            CssCompressionType="YuiStockCompression"
            JavaScriptFiles="@(JavaScriptFiles)"
            ObfuscateJavaScript="True"
            PreserveAllSemicolons="False"
            DisableOptimizations="Nope"
            EncodingType="Default"
            DeleteJavaScriptFiles="false"
            LineBreakPosition="-1"
            JavaScriptOutputFile="$(JavaScriptOutputFile)"
            LoggingType="ALittleBit"
            ThreadCulture="en-au"
            IsEvalIgnored="false"
            />

5. The last step merges the css files and creates a main.debug.css which is not compressed. This is useful in debug enviroments, where the frontend developer want to examine the css with firebug to find the location of a style.

<ReadLinesFromFile File="%(CssFiles.Identity)">
<Output TaskParameter="Lines" ItemName="lines"/>
</ReadLinesFromFile>
<WriteLinesToFile File="$(CssDebugFile)" Lines="@(Lines)" Overwrite="true" />


6.) Now put some css and js in to your css and js folder. When you look at the ItemGroups XML Node you will see that my css and js are under the same folder, like in the image below.


7. Now test the configuration. Open a dos console and type the command:


"c:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild.exe" "C:\MyProject\_Tools\MSBuildCompress.xml" /p:OutputFolder="C:\MyProject\MasterTemplate\12\Template\Layouts\MyProject"

The parameter "OutputFolder" is used in the .xml file to define the folder where the css and js folders are. I have following structure in my project. If you have another stucture you have to adapt the xml file.
If the command is successfull, you can copy it. The next step is to put this command into visual studio.

Open the properties of the project that contains the js and css files. Click on build Events and put the following text into the text field (Pre-Build Event)

Pre-build event

Wednesday, September 15, 2010

Get User Email in MatchPoint

If you want to show the user name with a email link in MatchPoint, you can use the SPHelper.GetUser() method. In a DocLib you can use the fields "Editor" or "Modified_by".

In the following example I have a ListDataProvider with a XslTransformer. The first thing we have to do is a mapping.

Mapping of the DataItem


If the field you want to show (Editor) is of type SPUserField you should cast it to String with ToString() or DisplayString.

Now you can you the mapping in the XSLT.

XSLT

SharePoint and reverse proxy

Yesterday I had to deploy a new SharePoint Solution to one of our customers. We had tested the Solution on our local machines and on the staging server. Everything was fine. After we installed the solution on their integration and quality server we realized that some WebParts threw an exception:

"System.ArgumentException: Value does not fall within the expected range.     at Microsoft.SharePoint.Library.SPRequestInternalClass.GetMetadataForUrl(String bstrUrl, Int32 METADATAFLAGS, Guid& pgListId, Int32& plItemId, Int32& plType, Object& pvarFileOrFolder)     at Microsoft.SharePoint.Library.SPRequest.GetMetadataForUrl(String bstrUrl, Int32 METADATAFLAGS, Guid& pgListId, Int32& plItemId, Int32& plType, Object& pvarFileOrFolder)     at Microsoft.SharePoint.SPWeb.GetMetadataForUrl(String relUrl, Int32 mondoProcHint, Guid& listId, Int32& itemId, Int32& typeOfObject, Object& fileOrFolder)     at Microsoft.SharePoint.SPWeb            fce4297d-3e08-415c-81f5-09bb40ea0820"

The only difference betweeen our enviroment and theirs was a reverse proxy. After some logging I found out that the method "HttpContext.Current.Request.Url.Scheme" returns "http://domain/list" and the webapplication runs on "https://domain". The returned url is used to access a list by SPWeb.GetList(), which returns null. The reverse proxy is situated in front of the web application and changes the connection to https. But the httpcontext within the application returns http.

Learning was that you should build up your staging environment the same way your customer does.

Wednesday, September 8, 2010

How to setup a Mysite in SharePoint 2010

In this post I'll show you how to create and setup a mysite in SharePoint 2010. I will create a new webapplication for the mysite but you can also use an existing webapplication. Ensure yourself that the "User Profile Service" is running. The MySite needs this service to function. You can check the blog post "Configuring the User Profile Service" for more information.


User Profile Service


First of all if you want to host your mysite on a new webapplication create a new webapplication. Therefore go to the Central Administration -> Application Management -> Manage Web Applications. Here click on "New" in the ribbon above and create a new webapplication which will host the mysite.

Create a new webapplication
Properties of the mysite webapplication

Now you have to configure a managed path, where all personal sites will be created. Normally we use "personal" as managed path.To create a managed path  go to Central Administration -> Application Management -> Manage Web Applications , mark the webapplication by clicking on it, then click on "managed path" in the ribbon.


Create a managed path


If you create your mysite on an existing webapplication you should create a secondary managed path "mysite".


On the dialog that pops up create a managed path "personal". Select "wildcard inclusion".







After all this settings are done, you should also check if the "User Profile Service" is associated with the webapplication containing the mysite. To check this go to "Central Administration -> Application Management -> Manage Web Applications", select the Mysite web application and click on "Manage Service Connections" in the Ribbon. In SP2010 you don't have a Shared Service Provider, so you have to associate each service with a webapplication.

Check Service Connections of the webapplication


In the dialog check if the "User Profile Service Application Proxy" is checked. If not select "custom" in the dropdown and check it.

Associated Service Connections

When the webapplication for the mysite is ready, you have to create a new site collection with the "MySiteHost" template.  Go to "Cental Administration -> Application Management -> Create Site Collections. Ensure yourself that the Mysite webapplication is selected in the dropdown. Give the site a name like "mysite". If you create the mysite on a webapplication that already contains a site collection, you should create the mysite on the manage path "mysite", othewise leave it blank. Select the "MySiteHost" template (under the Enterprise tab) and create the site collection. The mysite is only created when the "User Profile Service" is running. So if you have an error, check if your service is started.

Create a MySiteHost site-collection


Now you have to activate the "Self-Service Site Creation" so that the site can be created, when the user enters his mysite the first time. Therefore mark the MySite webapplication and click on "Self-Service Site Creation" in the ribbon.

Activate Self-Service Site Creation

Select "On" and click OK. (Probably you should do this step after creating a sitecolletin on the my site webapplication. In my case I have mysite on an existing webapplication with a sitecollection.)



The lasts steps are to configure the "User Profile Service" to create the Mysite on the paths and webapplications that we defined.  Go to "Central Administration -> Application Management ->Manage Service Applications". Select or click the "User Profile Service Application" and click "Manage" in the ribbon .

Manage the User Profile settings


In the administration page click on "Setup My Sites" to open the settings for the mysites. Enter here the "MySite Host Location". This is the full url to the site collection that contains the MySiteHost Site, we created above. Secondary enter the "Personal Site Location". This is the managed path we created on the webapplication that will be used to create the mysites. I read in many blogs that the read permission level should only the set to the "Authenticated Users" group.



That's it. The "Welcome Menu"should now contain a new menu entry "My Site".

Mysite menu link

Tuesday, September 7, 2010

Configuring the User Profile Synchronization Service in SharePoint 2010 - Step by Step

Yesterday I had the problem that my "User Profile Synchronization Service" (UPS) did not started. So I took a look over the Guidance provided by Microsoft. This really helped me but I also realized that this can be frustrating for someone who doesn't know how to setup this service. If you want a successful setup it is important that you follow exactly this provides guidance step by step. This blog post builds up on this guidance.

First of all you must be a farm account to do all this configration and SharePoint is not a stand-alone installation (See msdn). I had also problems with FQDNs when I installed SharePoint. Don't use FQNs like sp2010.domserver.com when you setup SharePoint. Lastly there is a bug in the creation of the Sync DB which will prevent the UPS provisioning from succeeding. Before we start UPS we must fix this up. (see Powershell bug)

! (Update October 2010)    DO NOT INSTALL THE OCTOBER 2010 CUMULATIVE UPDATE for SharePoint Server or Project Server !! It kills the UPS .


General Knowledge

The Profile Synchronization Service, short UPS, is a new sharepoint service within the new Service Oriented Architecture (SOA) of SharePoint. It is a wrapper around Forefront Identity Manager (FIM) services—two Windows services that are installed (but initially set to “disabled”) on each server in the farm. But don't start these services manually. The UPS starts them, when you correctly installed the UPS on SharePoint.


1. Check your services

First check if you already have a "User Profile Service Application". To check that go to your Central Administration -> Application Management -> Manage Service Applications.


if  you already have a service installed go on with step 3 else go ahead.


2. Create a new User Profile Service Application

If you don't have a such service than you have to create one. Click on "New" in the ribbon and select "User Profile Service Application".



Enter all the infos needed for the profile service. The one important things here are the security account for this application pool and the name of the service. The user should be a farm admin and should have the rights to logon into the server where the AD sits. As service name you should pick here a name different than a used one. I read in other blogs that this could make problems (see paulgrimley).

Create a new user profile service application


3. Put your account to the local admin group


The Setup account for the service must also be a member of the Administrators group on the server where the User Profile Synchronization service is deployed. I read in other blogs that you shoul reboot the server when you do that. SharePoint caches user accounts probably an iisreset and a timer reset should also work.

Add account to local admin group

After the service has started, you can remove the account from the group.


4. Create an account with Replicate Directory Changes on the domain

To sync the AD and you Profile Store you'll need a special  sync account e.g. "sp_sync", that must also have the permission "Replicate Directory Changes" on the Domain Controller (See "How to set Replication Directory Changes"). The links above describe exactly how you can do that. This property must be set on the active directory domain.


5. Start the Service


Go back to the Central Administration -> Application Management -> Manage Services on server and start both the "User Profile" and the "User Profile Synchronization Service". It takes about 10 minutes to start the service, so be patient. Refresh the page in intervalls to see if the services have started.

Start User Profile Services


When the User Profile Synchronization Service has started check the "Services" if the both Forefront services are enabled. Do an IIISRSET if the Service is on the same server as the application server.


After all the changes I had the problem, that I could not open the settings for the "User profile synchronization service". After searching a while I found that the application pool of the "SharePoint Web Services Root" was stopped. Starting this again helped to open the settings again.




6. Check Netbios and FQDN

You should check if the Netbios Domain Name is equal to the Full Qualified Domain Name. To check that open the DOS Command and type in "SET". You will see lots of environment variables. Check the variables "USERDNSDOMAIN" (FQDN) and "USERDOMAIN" (Netbios). If USERDOMAIN is equal to the first part of USERDNSDOMAIN that everything is fine. If not you must set some properties on your User Profile Service Application. See: http://blogs.msdn.com/b/russmax/archive/2010/03/20/sharepoint-2010-provisioning-user-profile-synchronization.aspx


7. Setup a synchronization connection


Now create some users in your AD and set up the "Synchronization Connection". To setup this connection go to Central Administration -> Manage Service Applications and click on "User Profile Service Application" link. On the new page click on "Configure Synchronization Connections".



Click on "Create New Connection" and fill in all the needed infos. Click on "Populate Containers" to select the container you want. Use the Sync Account with the "Replication Directory Changes" rights as account for the sync.

Now you can start the synchronization by clicking on "Start profile synchronization" in the "User Profile Service Application" page.

I had the following error "MOSS MA not found". After starting "Forefront Identity Manager Service" within the service manager, the problem was gone.

Additional Helpers: