Friday, June 25, 2010

This Blog Has Moved

Just wanted to let everyone know that as of this week, new posts to this blog can be located on the Sitecore Community, under Technical Blogs. Here's a direct link to this blog at its new home.

I've already posted a couple of articles:
Hope to see you there!

Thursday, May 27, 2010

Keeping Media Metadata In Sync

When media files are uploaded to Sitecore, a Sitecore item is created. This item represents the media file in Sitecore. When you want to display information about the media file, you are displaying information about the item. When the file is updated, Sitecore ensures that the fields on the item stay in sync with the file.

Well, Sitecore does this for fields it is aware of. This is handled by the Sitecore.Resources.Media.Media class (and the classes that inherit from it). Thanks to the ImageMedia class, when you upload an image file, the height and width are read. Sitecore does not include a class to handle Microsoft Word documents, so metadata from those files is NOT read.

Creating a class to read metadata from Word documents, or any other file, is not hard to do. Sitecore is already set up to allow it. You just need to write the code that can read the metadata from the media file. The Sitecore Experience blog has a post that explains how to do this.

But what if you want a change to go the other direction? What if you want to update the document title in a Word document using Sitecore? This is also possible, but requires a little more work. In this post I'm going to explain how to do this for a docx file.

Triggering code when the Sitecore item is saved
When the Sitecore item is changed, code needs to run that updates the corresponding media file. This is accomplished by hooking into the "item:saved" event in web.config (or other config file).

<events>
<event name="item:saved">
<handler type="Sitecore.Marketing.MediaLibrary.MediaItemSavedHandler, Sitecore.Marketing.MediaLibrary" method="OnItemSaved"/>
</event>
</events>


And the MediaItemSavedHandler class must be defined. The rest of the code in this post goes inside this class.

public class MediaItemSavedHandler
{
protected void OnItemSaved(object sender, EventArgs args)
{
}
}



Determining if the item represents a media file
Since this event handler applies to all items, it is important to determine if the item that was saved represents a media file. This can be accomplished by checking the item's template ID.

Item item = Event.ExtractParameter(args, 0) as Item;
bool isDocx = item.TemplateID.ToString().Equals("{7BB0411F-50CD-4C21-AD8F-1FCDE7C3AFFE}");
if (!isDocx)
{
return;
}


Determining if the title has actually changed
Just because the item saved event is triggered doesn't mean it was triggered by a change to the title. The EventArgs parameter will indicate if the title has changed:

ItemChanges changes = Event.ExtractParameter(args, 1) as ItemChanges;
Field titleField = item.Fields["Title"];
bool hasTitleChanged = (changes.HasFieldsChanged && titleField != null && changes.IsFieldModified(titleField));
if (!hasTitleChanged)
{
return;
}
MediaItem media = new MediaItem(item);



Updating the media file
Next you need to update the media file in Sitecore. Access to Sitecore media files is available through the WebDAV interface, but communicating with the WebDAV server is a topic for a separate post. I'm going to take the easy way out here: I'm going to read the current file to a temp file, update the temp file, then replace the file in Sitecore.

Additionally, I'm not going to include all of the code needed update the docx file. I'll show you how to read a file from the media library and how to write a file to the media library.

If you're familiar with how .NET handled I/O, there's not much to accessing the media file. Here is an example of how to read and write Sitecore media files.

protected string CreateTempFile(MediaItem media)
{
string fileName = Path.GetTempFileName();
using (FileStream file = new FileStream(fileName, FileMode.Open))
{
Stream stream = media.GetMediaStream();
byte[] buffer = new byte[1024];
int count = stream.Read(buffer, 0, 1024);
while (count > 0)
{
file.Write(buffer, 0, count);
count = stream.Read(buffer, 0, count);
}
}
return fileName;
}

protected void ReplaceFileInSitecore(string fileName, MediaItem mediaItem)
{
Media media = MediaManager.GetMedia(mediaItem);
using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
media.SetStream(stream, "docx");
}
}



Want to learn more?

Monday, May 24, 2010

Browser Settings for Sitecore

I am a big fan of using a virtualized development environment (I use VirtualBox). By running everything in a virtual machine, it's much easier to test systems, upgrade software, and keep my laptop from getting cluttered. For me, the greatest advantage of working in a virtual machine is that I can easily roll-back changes I've made.

But working in a virtual machine has resulted in me installing a lot more software than I would have otherwise. Today, most installers are pretty streamlined and don't require much more than a little patience.

Sometimes steps are required that cannot be handled by an installer and must be performed manually. One of my least favorite of these is Internet Explorer configurations.

Sitecore recommends Internet Explorer be configured in a certain way. The Internet Explorer Configuration Reference available on SDN explains how and why.

The following code can be copied into a reg file and run in order to set the browser settings recommended in the SDN reference. I'm including this information for demonstration purposes only. Use at your own risk.

UPDATED: 09/17/2010 - Certain revisions of Sitecore 6.2 and 6.3 may be easier to navigate if the option "allow script-initiated windows without size or position constraints" is disabled. The settings below set that value to enabled.

======
Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\2]
"2001"=dword:00000000
"2004"=dword:00000000
@=""
"DisplayName"="Trusted sites"
"PMDisplayName"="Trusted sites [Protected Mode]"
"Description"="This zone contains websites that you trust not to damage your computer or data."
"Icon"="inetcpl.cpl#00004480"
"LowIcon"="inetcpl.cpl#005424"
"CurrentLevel"=dword:00000000
"Flags"=dword:00000043
"1200"=dword:00000000
"1400"=dword:00000000
"2007"=dword:00010000
"MinLevel"=dword:00010000
"1A10"=dword:00000001
"1001"=dword:00000001
"1004"=dword:00000003
"1201"=dword:00000003
"1206"=dword:00000003
"1207"=dword:00000000
"1208"=dword:00000000
"1209"=dword:00000003
"120A"=dword:00000003
"120B"=dword:00000000
"1402"=dword:00000000
"1405"=dword:00000000
"1406"=dword:00000003
"1407"=dword:00000000
"1408"=dword:00000000
"1409"=dword:00000000
"1601"=dword:00000000
"1604"=dword:00000000
"1605"=dword:00000000
"1606"=dword:00000000
"1607"=dword:00000003
"1608"=dword:00000000
"1609"=dword:00000000
"160A"=dword:00000000
"1800"=dword:00000001
"1802"=dword:00000000
"1803"=dword:00000000
"1804"=dword:00000000
"1809"=dword:00000003
"1A00"=dword:00020000
"1A02"=dword:00000000
"1A03"=dword:00000000
"1A04"=dword:00000003
"1A05"=dword:00000001
"1A06"=dword:00000000
"1C00"=dword:00010000
"2000"=dword:00000000
"2005"=dword:00000000
"2100"=dword:00000000
"2101"=dword:00000000
"2102"=dword:00000000
"2103"=dword:00000000
"2104"=dword:00000000
"2105"=dword:00000000
"2106"=dword:00000000
"2200"=dword:00000003
"2201"=dword:00000000
"2300"=dword:00000001
"2301"=dword:00000003
"2400"=dword:00000000
"2401"=dword:00000000
"2402"=dword:00000000
"2600"=dword:00000000
"2700"=dword:00000000
"TemplateIndex"=dword:00011000
"1806"=dword:00000001
"2500"=dword:00000003



======

Thursday, April 1, 2010

Authentication Using the TC Integration Module 1.1

Happy April Fools Day. All things are subject to extra suspicion today, but I assure you this blog post is legit. Now then...

In my last post I walked through the installation process for the Telligent Community integration module for Sitecore. In this post I will cover the topic of how authentication works in a little more detail.

The majority of this post is just explanatory information. The instructions on how to get the integration to work (meaning how to get the 2 cookies created) are at the end. I put a "+" in front of those items in case you just want to get the integration to work and will worry about understanding it later :-)

Separate user accounts
Sitecore and TC maintain their own, separate user accounts. These accounts can stay in sync because they share the same user name. After the integration is configured, Sitecore should be used to manage accounts. This includes creating new accounts, setting passwords and assigning roles.

If you are using the integration module with an existing TC instance, the TC users need to be "migrated" to Sitecore. It's not a true migration because the user accounts will still exist in TC. What it does is creates corresponding accounts in Sitecore, so TC users can be authenticated by Sitecore.

Migrating existing TC users
Before you migrate existing TC users, be aware of a couple of things:
  • New accounts will be created in Sitecore.
  • All existing TC users will be migrated. The migration script does not currently support any kind of filtering to control which users are migrated.
  • Existing TC content is not modified in any way.
  • Existing TC users will be renamed in TC. The new user name will be the fully-qualified Sitecore user name. This is the combination of a domain with the user name, such as extranet\rickfielder.
  • Do not run the migration script more than one time.
  • Do not run the migration script if the integration is already in use (meaning Sitecore users have already logged into TC).

TC user migration script

To migrate existing TC users:
  1. Log into Sitecore as an administrator.
  2. Navigate to http://your-server/sitecore modules/Shell/CSSCIntegration/Security/csinstall.aspx.
  3. Select the Sitecore domain the TC users should be created in.
  4. Click the "move users" link.
TC user migration script output

The migration script generates 2 XML files. These files are written to "[sitecore web root]\sitecore modules\Shell\CSSCIntegration\Security":
  • MovedUsers.xml describes the new accounts created in Sitecore. This file contains the username and password for each migrated user. Be aware that passwords are XML-encoded and may not necessarily work if copied directly from this file.
  • FailedUsers.xml describes the TC users who could not be created in Sitecore.
Post-migration considerations
Unfortunately, the migration script doesn't provide any way for an administrator to specify any properties on the Sitecore users that get created. Aside from the Sitecore domain the users are added to. So if you want TC users to be assigned a certain Sitecore role, this is something that you must do manually after the migration.

Another thing to to consider after migrating users is that login, logout, password maintenance and new account creation are all handled by Sitecore. Since TC provides users the ability to do these things, you should hide those link in TC user interface.

Authentication in Sitecore
When using this integration, all authentication is handled by Sitecore. When a user navigates to TC, TC looks for some cookies that Sitecore has created in order to know

When a user logs into Sitecore, 2 cookies are created. These cookies identify that a user has been authenticated and extra information from Sitecore about the user. Since Sitecore and TC must be a part of the same domain, both cookies will be available to TC when the browser navigates to TC. So the user is not automatically logged into TC. Rather, the cookies are set so that when the user navigates to TC, TC can automatically log him in at that point.

The process by which these cookies are created is the same whether the user logs into a published Sitecore website or the Sitecore client - provided Sitecore is configured to create these cookies.

This is not done automatically. The design allows you to control if and when this happens. For example, you may want content authors to have access to TC but not website users. Later in this post I'm going to walk through the configuration needed in order to instruct Sitecore to create these cookies for both published Sitecore websites and the Sitecore client.

But what are these cookies and where do they come from?

Writing the authentication cookie
The first cookie is the authentication cookie. Sitecore creates a System.Web.Security.FormsAuthenticationTicket instance. This object identifies the user who has authenticated. It also contains some data that needs to be transmitted to TC. Specifically, it contains the TC roles that the TC user is assigned.

The name of the authentication cookie is specified in Sitecore's web.config file, in the "authentication" section. My Sitecore instance is configured to use the name ".CommunityServer".

If you're interested in seeing the code that creates the authentication cookie, look at the following:

assembly: Sitecore.CommunityServerSecurityIntegration.SitecoreSide.dll
namespace: Sitecore.CommunityServerSecurityIntegration.SitecoreSide
class: SecurityUtil
method: CreateCookies

Writing the user profile cookie
The second cookie is the user profile cookie. This cookie contains user profile values that are read from Sitecore and made available to TC, which sets those values on the corresponding TC user (or uses those values to create a new TC if one doesn't already exist).

The following is an example of the (unencrypted) user profile cookie value:

Email=Johnson@localhost.com&PublicEmail=Johnson@localhost.com&CommonName=John Johnson

The values that are set in this cookie are determined by the "mappedProfileProperties" section from the Sitecore configuration file "CommunityServerIntegration.config". The only exception is the "Email" value, which is transmitted automatically.

Reading the authentication cookie
When a user clicks a link that takes him to TC, the 2 cookies are available to TC. Just like Sitecore, TC is configured use an authentication cookie named ".CommunityServer".

The TC security extension that was configuring during installation specifies that TC should use the following class to handle forms authentication:

assembly: Sitecore.CommunityServerSecurityIntegration.CommunityServerSide.dll
namespace: Sitecore.CommunityServerSecurityIntegration.CommunityServerSide
class: SitecoreFormsAuthentication

The "ProcessRequest" method is responsible for reading the authentication cookie. After the method confirms that the cookie is valid, it reads the roles that were passed from Sitecore. This ensures the proper roles are assigned to the TC user.

TC handles adding and removing roles from users who already exist in TC, but what about users who don't? Remember, all user management is handled in Sitecore, so it's possible - and even likely - that a user will exist in Sitecore but not in TC. TC will automatically create a new user in this case and will set the roles on the user. The "PostCreateUser" method does this.

Reading the user profile cookie
The "ProcessRequest" method also reads the user profile cookie. It just checks to make sure a value has been set. The underlying TC security extension infrastructure takes the information in that cookie and uses it to create a TC user if necessary and to populate the TC user.

How does Sitecore know when - and if - to write the cookies?
You may not want every Sitecore user to have an account created in TC. Therefore, it is important that you retain some control over when the cookies are written. If no cookies are written, then no single sign-on to TC will occur. The cookies are only written if you want them to be. The next sections explain how to configure this.

+Configuring a Sitecore website to support single sign-on
Website can handle authentication in a variety of ways. This integration supports all of those. However your site's login logic works, add a call to the following method after it runs. This will cause Sitecore to create the 2 cookies that are required for single sign-on to TC:

assembly: Sitecore.CommunityServerSecurityIntegration.SitecoreSide.dll
namespace: Sitecore.CommunityServerSecurityIntegration.SitecoreSide
class: SecurityUtil
method: AfterLogin

You also need to ensure the cookies are deleted when the user logs out. Add the following method call before your code ends the user's session:

assembly:
Sitecore.CommunityServerSecurityIntegration.SitecoreSide.dll
namespace: Sitecore.CommunityServerSecurityIntegration.SitecoreSide
class: SecurityUtil
method: Logout

+Configuring the Sitecore client to support single sign-on
Just like when supporting single sign-on from a published Sitecore website, when configuring single sign-on from the Sitecore client you need to instruct Sitecore to create (and delete) the 2 required cookies.

Hooking into Sitecore's authentication functionality is done by modifying the "loggedIn" and "logout" pipelines. These pipelines are defined in Sitecore's web.config file.

Add the following processor as the first step in the "loggedIn" pipeline:

<processor mode="on" type="Security.CreateCSCookieProcessor, Sitecore.CommunityServerSecurityIntegration.SitecoreSide" />

Add the following processor as the last step in the "logout" pipeline:

<processor mode="on" type="Security.RemoveCSCookieProcessor, Sitecore.CommunityServerSecurityIntegration.SitecoreSide" />


Next steps
After the integration module is installed and configured, it's pretty much smooth sailing. Accessing TC content can be handled in a number of way. I described the options in my introduction to the integration post.

Want to learn more?

Wednesday, March 31, 2010

Installing the TC Integration Module 1.1

In my last post I provided an overview of the Telligent Community integration module for Sitecore. While the module comes with a lot of documentation - including instructions on how to install the module - sometimes it's helpful to have someone guide you through the process. That's what I'm going to do.

TC integration module architecture
Before I get into the actual installation instructions, it's important to understand what you're going to install. I will explain each section when I get to it, but here's a quick overview:
  • Authentication is handled by Sitecore using ASP.NET forms authentication.
  • Sitecore is responsible for creating an authentication cookie that TC uses to provide single sign-on.
  • Sitecore passes user information to TC so TC knows which roles the user has been assigned to.
  • TC content is exposed to Sitecore through a REST API.
  • Additions to the Sitecore client are installed using a Sitecore package.
Pre-requisites
Before you attempt to install the TC integration module version 1.1, be aware of the following:
  • You must be running Sitecore 6.1 or 6.2.
  • You must be running Telligent Community 5.0 SP1. Version 5.5 might work, but I haven't tested it to know for sure.
  • Sitecore and Telligent Community must be running in the website or TC must be running in a domain of the Sitecore server. For example, if TC is using the domain "tc.test-server.com", then Sitecore must be using the domain "test-server.com".
  • You must have read/write permissions on the folders in which Sitecore and TC are installed.
  • You must have the ability to upload files to the folder in which Sitecore and TC are installed.
  • You must have an administrator account on Sitecore and TC
If you have all of these things ready, you are safe to proceed. Don't be overwhelmed by the number of steps. It's not as bad as it may look :-)

Step 1 - Download the integration module.
The integration module can be downloaded from the Sitecore's SVN server. The easiest way to get the module is to use an SVN client for Visual Studio like AnkhSVN. You want to download the trunk code since this contains the latest bug fixes.


Step 2 (optional) - Compile the .NET assemblies.
You don't have to recompile the .NET assemblies, but I think it's a good idea because it will ensure that you are using the latest available code. Here's a description of each of the C# projects that make up the integration module so you understand what you are compiling:
  • Sitecore.CommunityServerSecurityIntegration.CommunityServerSide - This assembly is deployed to the TC server. It contains code to support single sign-on as well as extensions to TC's REST API.
  • Sitecore.CommunityServerSecurityIntegration.SitecoreSide - This assembly is deployed to the Sitecore server. It contains code that allows Sitecore to create the cookies that are used by TC and to migrate existing TC users into Sitecore.
  • Sitecore.CommunityServersIntegration.Controls - This assembly is deployed to the Sitecore server. It is where the components that are used to access TC content are defined.
  • CSComponentWizard - This assembly is deployed to the Sitecore server. It is where Sitecore client user interface additions are implemented.

Step 3 - Deploy assemblies to TC.
Copy the assembly "Sitecore.CommunityServerSecurityIntegration.CommunityServerSide.dll" to the TC server. The assembly should go in the bin folder.


Step 4 - Add a security extension in TC.
New functionality can be added to TC using extensions. A security extension allows TC to use cookies that are created by Sitecore for single sign-on.

In order to use an extension, TC must be configured to allow extensions to run. In the "communityserver.config" file in TC, set the following attribute:
  • node path:/CommunityServer/Core/extensionModules
  • attribute name: enabled
  • attribute value: true
Next you need to specify that TC should use a custom class to handle forms authentication. In the extensionModules node you just modified, find the FormsAuthentication security extension. Hint: look for <add name="FormsAuthentication"... Replace the node with the following:

<name="formsauthentication" name="FormsAuthentication" extensiontype="Security" type="Sitecore.CommunityServerSecurityIntegration.CommunityServerSide.SitecoreFormsAuthentication, Sitecore.CommunityServerSecurityIntegration.CommunityServerSide" allowautouserregistration="true" userprofilecookie="CSUserProfile" useencryptedcookie="false" profilerefreshinterval="1" />

Here's a breakdown of what the attributes mean:
  • name - The name of the extension you are configuring.
  • extensionType - TC supports a variety of extensions. This specifies that this extension deals with security.
  • type - The name of the class (and assembly) that implements the extension.
  • allowAutoUserRegistration - When a user logs in from Sitecore, an authentication cookie is created. TC uses that cookie to authenticate the user. If the user doesn't already exist in TC, this setting instructs TC to create the user.
  • userProfileCookie - The name of the cookie that contains user profile information about the user who has been authenticated by Sitecore. TC uses the cookie's value to set values on the TC user. Later you will configure the setting in Sitecore that sets this value.
  • useEncryptedCookie -Specifies if the value saved in the userProfileCookie is encrypted. Later you will configure the setting in Sitecore that sets this value.
  • profileRefreshInterval - This value specifies the frequency with which TC will refresh user information. The unit is in days, with 1 being the minimal value.

Step 5 - Enable REST API in TC.
TC provides access to its content through a REST API. This API must be enabled before Sitecore can use it.
  1. Log into TC using an administrator account.
  2. Navigate to the TC control panel and click the "Site Administration" link.
  3. Click the "Manage REST API" tab.
  4. Make sure the "Yes" option is selected and click the "Save" button.

Step 6 - Generate an application key in TC.
Sitecore uses a TC admin account in order to retrieve content. For this to work, Sitecore needs the TC admin user name and an application key.
  1. Log into TC using the admin account you intend to have Sitecore use.
  2. Edit the user's settings.
  3. Navigate to the "Site Options" tab.
  4. Scroll to the bottom of the screen and click the "Create and Edit Application Keys" link.
  5. In the "name" field enter "Sitecore" and click the "Generate" button.
  6. Write down the API key that is generated. You will need this value when you configure the connection from Sitecore to TC.

Step 7 - Modify web handlers in TC.
This integration module allows Sitecore users to filter and repurpose TC content. In order to provide Sitecore users with a variety of ways to do this, TC's REST API had to be extended.

This step involves changing the web handlers that process the REST calls. These web handlers can be found in the api\ folder under TC. The following files need to be modified. The class name in each file needs to be changed to the value following the file name:
  1. groups.ashx: CommunityServer.WebServices.Services.CSIGroupsService
  2. mediagallery.ashx: CommunityServer.WebServices.Services.CSIMediaGalleriesService
  3. membership.ashx: CommunityServer.WebServices.Services.CSIMembershipService
  4. search.ashx: CommunityServer.WebServices.Services.CSISearchService

Step 8 - Install the integration module deployment package in Sitecore.
The deployment package is available in the file named "TCSC Integration - Sitecore side-1.1.x.zip". Install this package using the Sitecore Installation Wizard.

The package includes a number of Sitecore items and files that are copied to the filesystem. Some of those files are .NET assemblies. If you recompiled the assemblies earlier, you will want to copy those assemblies into the bin\ folder in Sitecore.


Step 9 - Add a connection string in Sitecore.
Since TC is an external system that Sitecore is connecting to, an entry for TC must be added to Sitecore's connectionstring.config file. Add the following to the configuration file:

<add name="tc" connectionstring="siteUrl=http://your-server.com/tc;username=admin;apiKey=c00y45fd" />

Of course, the specific values you enter here will be different than the ones above. Here's an explanation of the values in the connectionString attribute:
  • siteUrl - The base URL to your TC server. If you enter the URL in a browser you should be taken to the TC homepage.
  • username - The name of the TC administrator account that Sitecore uses to communicate with TC. This must be the same account you used earlier to generate the API key.
  • apiKey - The API key value you generated earlier.

Step 10 - Modify the integration module configuration file in Sitecore.
One of the files that is created by the deployment package is the integration module configuration file. This file is located in the folder "app_config/include/". The name of the file is "CommunityServerIntegration.config".

Locate the node "/configuration/sitecore/settings". This node defines the basic settings used by the integration module. Here's a description of the settings:
  • CS.ProfileCookie - Remember the "userProfileCookie" attribute in TC's "communityserver.config" file? This value must match that.
  • CS.EncryptProfile - This value must match the "useEncryptedCookie" attribute value from TC's "communityserver.config" file.
  • CS.ConnectionStringName - This value must match the name of the connection string you configured in Sitecore's "connectionstring.config" file.
Using the values I've already configured, this is what my settings node looks like:

<settings>
<setting name="CS.ProfileCookie" value="CSUserProfile" />
<setting name="CS.EncryptProfile" value="false" />
<setting name="CS.ConnectionStringName" value="tc" />
</settings>



Step 11 - Configure forms authentication settings in Sitecore.
In order for single sign-on to work, both Sitecore and TC must use forms authentication. Additionally, the forms authentication settings for both must match. Make sure the "authentication" node in Sitecore's "web.config" file matches the node in TC's "web.config" file.

The important part here is the the authentication form's name and domain match. Here's the authentication node from my installation:

<authentication mode="Forms">
<forms name=".CommunityServer" protection="All" timeout="60000" loginurl="login.aspx" slidingexpiration="true" domain=".nicam.com" />
</authentication>

The domain attribute is required if TC is running in a subdomain. The dot before the domain name is needed in order for the browser to use the same cookie when sending requests to either the domain or the subdomain.

Also - and this may be common knowledge among developers but it can't hurt to be repeated - be sure your domain has at least one dot in it already. The cookie will not be written if your domain is simply "nicam". It needs to be "nicam.com".


Step 12 - Ensure the machineKey values in Sitecore and TC match.
The "machineKey" settings is important when using forms authentication in ASP.NET. Both Sitecore and TC must have the same "machineKey" configuration.

This configuration can be found in web.config. If no "machineKey" configuration exists, it must be added. Online tools are available - like this one - to generate the required settings. After you generate the configuration, add it to web.config as the last tag before the closing "system.web" tag (</system.web>).


Step 13 (optional) - Configure role and property mappings from Sitecore to TC.
Sitecore's "CommunityServerIntegration.config" file contains 2 sections that allow Sitecore user settings to be mapped to TC user settings.

The first mapping section is "rolesCorrespondence". This section allows you to specify user roles in Sitecore but still take advantage of security in TC. Sitecore roles are mapped to TC roles.

When the authentication cookie is created, the Sitecore user's roles are checked against this setting. The names of the appropriate TC roles are set in the authentication cookie. When single sign-on happens in TC, the custom forms authentication class that TC uses reads the roles from the authentication cookie and adds those roles to the TC user.

The following is an example of role mapping:

<rolesCorrespondence>
<role communityserverrole="Employees" sitecorerole="sitecore\Author" />
<role communityserverrole="Visitors" sitecorerole="extranet\Nicam" />
</rolesCorrespondence>


The second mapping section is "mappedProfileProperties". This is where Sitecore user profile properties are mapped to TC user profile properties. These values are passed to TC through the user profile cookie.

<mappedProfileProperties>
<property communityserverproperty="PublicEmail" sitecoreproperty="Email">
<property communityserverproperty="CommonName" sitecoreproperty="FullName">
</mappedProfileProperties>



Step 14 - Enable the HTML editor for display templates
Display templates determine how content retrieved from TC by the community component is formatted. Sitecore users are able control this presentation by using a rich text editor to edit display templates.

A couple of changes to the Sitecore file "/sitecore/shell/Controls/Rich Text Editor/Default.aspx" are required:
  1. Change the "Inherits" attribute to "Sitecore.CommunityServerIntegration.Editors.CSRADEditor". This class extends the standard Sitecore rich text editor to support the dropdown list that allows an editor to select the specific TC content fields to display.
  2. At the bottom of the file, add a link to the Javascript file "/sitecore modules/Shell/CSSCIntegration/RichText Commands.js". It is important that this be the last Javascript file that the browser loads. Otherwise the code in the file may be overriden by another script.

Next steps
The integration module is installed, but there's more work to do. Since Sitecore is handling authentication and is responsible for role management, existing TC users must be migrated into Sitecore. Also, Sitecore needs to be configured to create the cookies (authentication and user profile) that TC needs in order to sign a user in. I will cover these topics in my next post.

Want to learn more?

Monday, March 29, 2010

Introduction to Telligent Community Integration Module for Sitecore

The Sitecore Shared Source Library has a lot of interesting and useful projects that are available for free. In this post I want to introduce the Telligent Community integration module. I will explain what the module does and show that it looks like in action.

Sitecore website that incorporates Telligent Community content.

For those of you who don't know, Telligent Community is a .NET-based system for building and maintaining communities. Inside Telligent Community, applications provide blog, wiki, forum and media gallery functionality.

So what does this integration do? Two things. The first is integrated security. Single sign-on between Sitecore and TC is supported. Sitecore can be used to manage TC users and roles.

The second is the ability to repurpose TC content in Sitecore. The integration provides a variety of ways to incorporate TC content into a Sitecore website, including a wizard that allows Page Editor users to select the TC content they want to use. It also provides a variety of ways to control how that content is presented, including a rich-text editor for building display templates.

I will save the technical details on how all this works for later posts. For now I want to focus on what the integration does rather than how it does it. The easiest way to do this is through some videos. And if you're only going to watch one video, I suggest video #2.

Video 1: Security integration features
This video covers the module's security integration features. It demonstrates how single sign-on works and how Sitecore is used for role management.



Security integration using the TC integration module 1.1.

Video 2: Content repurposing
The integration module provides a number of ways to repurpose content. This video explains these options. It also covers the "community component", which is a Sitecore control that allows Sitecore content authors to repurpose Telligent Community content through a wizard interface.


Content repurposing using the TC integration module 1.1.


Video 3: Controlling presentation logic
This video demonstrates how the integration module allows custom presentation logic to be defined in Sitecore and applied to Telligent Community content.


Controlling presentation logic using the TC integration module 1.1.


Want to learn more?

Thursday, March 25, 2010

Custom Fields, Part 3

In my last post I explained how to create a custom user interface for editing a name in Page Editor. I left off suggesting that there's a problem with the current implementation. And there is: it is possible to circumvent the user interface by using the inline editing feature provided by the default field renderer.

Inline editing circumvents my new user interface.

Why is that happening? Well, as far inline editing is concerned, Sitecore doesn't have any special instructions on how to handle a name field. Therefore, Sitecore treats the field like a single-line text field.

Since I created this special user interface to handle the name field, I want Sitecore to use that interface. And while in some cases being able to use inline editing to modify a field value may be advantageous, in this case I want it disabled.

In this post I'm going to cover 3 items:
  1. Creating a custom field type to make it easier to work with the components of a name (currently only first and last names, but could later be expanded to more).
  2. Controlling how the standard Field Renderer web control behaves when it is used to render a custom field.
  3. Configuring a custom field renderer control to make it easier to configure the display formatting for a name field
Step 1 - Create new field type
My first step it to create a new field type. This will make it much easier for people working with the Sitecore API to work with name fields. Since I will need to work with field names in a later step, I will be the first person to benefit from this addition.

Also, this code will serve as a replacement for the NameFieldUtils class. Same logic, different location.
public class NameFieldType : CustomField
{
public static string GetFullName(string firstName, string lastName)
{
var name = new string[2];
name[0] = firstName;
name[1] = lastName;
return String.Join(" ", name);
}
public static string GetFirstName(string fullName)
{
string[] parts = fullName.Split(' ');
if (parts.Length >= 1)
{
return parts[0];
}
return string.Empty;
}
public static string GetLastName(string fullName)
{
string[] parts = fullName.Split(' ');
if (parts.Length >= 2)
return parts[1];
return string.Empty;
}
public NameFieldType(Field innerField) : base(innerField) { }

public static implicit operator NameFieldType(Field field)
{
if (field != null)
return new NameFieldType(field);
return null;
}

public string FirstName
{
get { return NameFieldType.GetFirstName(InnerField.Value); }
}

public string LastName
{
get { return NameFieldType.GetLastName(InnerField.Value); }
}
public string FullName
{
get { return InnerField.Value; }
}
}
Step 2 - Register new field type
When working with content using the Sitecore API, a class is used. Different classes support different types of field. Sitecore needs to know which class should be used for which field type. This mapping is specified in the FieldTypes.config file.

In order for Sitecore to be able to resolve the "Name Field" type to the NameFieldType class I just created, I need to add the following to the FieldTypes.config file:
<fieldType name="Name Field" type="sctest.NameFieldType,sctest" />

Step 3 - Write code that controls the inline editing interface
Next I need to tell Sitecore how a name field value should be displayed in Page Editor. There are a couple of things I want to configure. First, I don't want users to be able to enter a value inline. I want all name values to be entered through my custom interface. I need to disable inline editing. Second, I want my custom interface to appear when a user clicks a name value in Page Editor.
public class GetNameFieldValue
{
public void Process(RenderFieldArgs args)
{
switch (args.FieldTypeKey)
{
case "name field":
{
args.DisableWebEditContentEditing = true;
args.WebEditClick = "return Sitecore.WebEdit.editControl($JavascriptParameters,'sctest:EditNameButton');";
break;
}
}
}
}

Step 4 - Configure Sitecore to use the code
The Sitecore's renderField pipeline is responsible for rendering fields in Page Editor. The pipeline is defined in web.config. One of the steps in the pipeline is to determine what value should be rendered for a specific field. An addition to this pipeline is needed since I want to change the field renderer's default behavior.

I need to add a processor to the renderField pipeline. The processor will use the class I just created. The path to this pipeline in web.config is /configuration/sitecore/pipelines/renderField. The line should be added before the processor that uses the type Sitecore.Pipelines.RenderField.AddBeforeAndAfterValues. After I make my addition the renderField pipeline looks like the following:
<renderField>
<processor type="Sitecore.Pipelines.RenderField.SetParameters, Sitecore.Kernel"/>
<processor type="Sitecore.Pipelines.RenderField.GetFieldValue, Sitecore.Kernel"/>
<processor type="Sitecore.Pipelines.RenderField.ExpandLinks, Sitecore.Kernel"/>
<processor type="Sitecore.Pipelines.RenderField.GetImageFieldValue, Sitecore.Kernel"/>
<processor type="Sitecore.Pipelines.RenderField.GetLinkFieldValue, Sitecore.Kernel"/>
<processor type="Sitecore.Pipelines.RenderField.GetInternalLinkFieldValue, Sitecore.Kernel"/>
<processor type="Sitecore.Pipelines.RenderField.GetMemoFieldValue, Sitecore.Kernel"/>
<processor type="Sitecore.Pipelines.RenderField.GetDateFieldValue, Sitecore.Kernel"/>
<processor type="Sitecore.Pipelines.RenderField.GetDocxFieldValue, Sitecore.Kernel"/>
<processor type="sctest.GetNameFieldValue, sctest"/>
<processor type="Sitecore.Pipelines.RenderField.AddBeforeAndAfterValues, Sitecore.Kernel"/>
<processor type="Sitecore.Pipelines.RenderField.RenderWebEditing, Sitecore.Kernel"/>
</renderField>

After I compile my code I can test my changes. I can no longer directly edit a name field value. In addition, when I click the value in Page Editor, my custom user interface appears.

The next thing I want to do is create a custom field renderer in order to provide some extra functionality for my custom field type.

Step 5 - Create a new field renderer
My new field renderer is going to allow a developer to specify a formatting for a name. By default the first name followed by last name will be displayed. This is the value the default field renderer uses. But my custom field renderer is going to support options that will display only the first name, only the last name, and the last name followed by the first name, in addition to the default display format.

In order to accomodate this I need a custom field renderer that supports a format option. Since a developer is able to set the format option value, I need to make sure this value is made available to Sitecore in the way Sitecore expects. The following class does these things:

Assembly name: sctest.dll
Namespace: sctest
Class name: NameFieldControl
Base class: Sitecore.Web.UI.WebControls.FieldControl
public class NameFieldControl : Sitecore.Web.UI.WebControls.FieldControl
{
public string Format { get; set; }

protected override void PopulateParameters(Sitecore.Collections.SafeDictionary<string> parameters)
{
parameters.Add("format", this.Format);
base.PopulateParameters(parameters);
}
}

Step 6 - Implement support for formatting options
Now that I have the ability to support a format value, I need to implement the logic to use that value to determine the text that is displayed when a name field is displayed. That is going to require a modification to the GetNameFieldValue class (the class that I added to the renderField pipeline).

The format value that was set in the previous step is available in the pipeline through the RenderFieldArgs parameter. I use that value to determine how to format the name field's value.
public void Process(RenderFieldArgs args)
{
switch (args.FieldTypeKey)
{
case "name field":
{
//
//
NameFieldType field = args.Item.Fields[args.FieldName];
string displayValue = null;
//
//
var format = args.Parameters["format"];
switch (format)
{
case "FirstNameOnly":
displayValue = field.FirstName;
break;
case "LastNameOnly":
displayValue = field.LastName;
break;
case "LastCommaFirst":
displayValue = string.Format("{0}, {1}", field.LastName, field.FirstName);
break;
case "Complete":
default:
displayValue = field.FullName;
break;
}
args.Result.FirstPart = displayValue;
args.DisableWebEditContentEditing = true;
args.WebEditClick = "return Sitecore.WebEdit.editControl($JavascriptParameters,'sctest:EditNameButton');";
break;
}
}
}

Step 7 - Use the custom field renderer
Now I'm ready to use my custom field renderer. I need to add the following code to the top of my sublayout:
<%@ Register TagPrefix="sctest" Namespace="sctest" Assembly="sctest" %>

And then I need to use my new field renderer:
<sctest:NameFieldControl ID="NameFieldControl1" runat="server" Field="Name" Format="LastCommaFirst" />
When I view my site, I see that the name field is formatted as specified.

Custom field renderer in action.

Next steps
This is the end of my 3-part series on custom fields. You can download the finished source code here.

Want to learn more?
See my previous post for good links.