Welcome to MSDN Blogs Sign in | Join | Help

SharePoint Connector for Confluence - How We Did It

A few months ago, we and Atlassian announced their SharePoint Connector for Confluence, which impressed both customers and analysts. Now, ThreeWill, a Microsoft Gold Certified Partner specializing in developing SharePoint-based solutions, which helped design and implement the SharePoint Connector, will describe how they did it.

<Lawrence />

Integrating Content and Search Results with SharePoint

Have you ever needed to integrate an external system with SharePoint, showing content from each system within the other? What if you needed to integrate search between SharePoint and the external system? How do you keep the user experience seamless if the systems use different authentication mechanisms? Have you wondered if this can be done if the external system is written in Java?

If you answered "yes," then read on. Along the way you will learn some of the internals of SharePoint 2007 web parts, Microsoft Office SharePoint Server (MOSS) 2007 enterprise search, and Microsoft Single Sign-on (SSO).

Overview

This blog entry discusses how three developers integrated MOSS 2007 with Confluence, an enterprise wiki, in about 2 months time. It discusses work behind implementing the features for the SharePoint Connector for Confluence as shown in the diagram below.

Those features are broken out as follows:

Feature    

Primary Technology

Content Embedding

Web Parts

Integrated Search

MOSS Enterprise Search

Single Sign-on (SSO)

Microsoft SSO Service

Content Embedding

The goal for content embedding is to show Confluence content within SharePoint and to show SharePoint content within Confluence. The approaches for this were:

  • Use Confluence plug-ins to show SharePoint content such as list data and document links within Confluence.
  • Use SharePoint web parts to show Confluence wiki pages and Confluence wiki page hierarchy within SharePoint.

Confluence Plug-In

The Confluence plug-in allows the user to enter SharePoint specific Confluence wiki markup within a Confluence wiki page. The plug-in utilizes the built-in SharePoint web services (primarily Lists.asmx) to retrieve list content. A simple example of the markup might be the following.

{sp-list:test doc library|doc}

The resulting section of the wiki page would look like:

One interesting aspect of the plug-in was a technique used to provide an external link for editing a document in a SharePoint document library. The link is shown in a Confluence wiki page, but allows the user to click the link, edit the document, and save the changes back to SharePoint as seamlessly as it is done within a SharePoint document library.

SharePoint Web Parts

Two web parts were created to show Confluence content within SharePoint:

  • Confluence Page Web Part
  • Confluence Space Pages Web Part

Both web parts use Confluence web services to obtain Confluence content. Following a section for each web part, we discuss how the web parts are administered.

Confluence Page Web Part

The Confluence Page web part shows a Confluence wiki page within a SharePoint web part.

The example above shows a Confluence wiki page that displays news via RSS. The high-level work of displaying the page content within the web part is shown below:

RemotePage page = ConfluenceHelper.GetConfluencePage(this.PageId);

this.TitleUrl = page.url;

pageContent = ConfluenceHelper.RenderContent(this.SpaceKey, this.PageId);

pageContent = GetCleanPageContent(pageContent);

_literalPage.Text = pageContent;

This is simplifying things a bit, but the ConfluenceHelper class basically has several methods that wrap Confluence web service proxy calls. In this case, the GetConfluencePage wraps the Confluence getPage web method and the RenderContent wraps the Confluence renderContentAsUser web method. In addition, regular expressions are used to properly prefix all URLs within the Confluence page content with the Confluence host and to remove some inline CSS styles that were conflicting with the rest of the SharePoint page. Finally, the pageContent is simply applied to the Text property of a Literal control. All of this happens within the OnPreRender override of the web part.

The image above also shows an "Edit Confluence Page" verb on the web part menu. This is simply done by overriding the get of WebPartVerbCollection within the web part as shown below.

public override WebPartVerbCollection Verbs

{

get

{

// See if the user has the ability to edit the page - if so, provide a link

List<WebPartVerb> verbs = new List<WebPartVerb>();

if (_pageId != -1)

{

try

{

if (ConfluenceHelper.HasEditPermission(_pageId))

{

WebPartVerb editPageVerb = new WebPartVerb(this.ID + "_editPageVerb",

String.Format(

"window.open('{0}/pages/editpage.action?pageId={1}','EditPage')",

ConfluenceConfiguration.Instance.ConfluenceSite, _pageId));

editPageVerb.Description = "Edit the Confluence wiki page.";

editPageVerb.Text = "Edit Confluence Page";

editPageVerb.ImageUrl = Constants.WebPartTitleIconImageUrl;

verbs.Add(editPageVerb);

}

}

catch (Exception ex)

{

TraceHelper.TraceError("ConfluencePageWebPart", "get_Verbs",

"Unable to check edit permissions.", ex);

}

}

WebPartVerbCollection allverbs = new WebPartVerbCollection(base.Verbs, verbs);

return allverbs;

}

}

Once again we use our web service proxy wrapper, this time to determine if the user can edit the page. Note that caching should be done on operations such as this within the get of WebPartVerbCollection as it may be called multiple times when a single web part is displayed.

A more interesting aspect of the web parts is the editor part. Editor parts are used to provide a custom editor for web part properties. Without an editor part, custom web part properties are typically text boxes shown in the "Miscellaneous" section when you edit your web part properties (enumeration web part properties do show as a drop down). In this case, we want to allow the user to select a page for the web part. Here is what our editor part looks like:

Confluence has the concept of spaces which are similar to SharePoint sites. Every Confluence wiki page lives within a space and a Confluence installation can contain many spaces. The editor part to the left shows a drop-down list of spaces with a tree view of pages within the selected space. Since pages can contain other pages, this is a hierarchical list and shown as a tree view.

To make this work, we override the CreateEditorParts method on the web part as shown below.

public override EditorPartCollection CreateEditorParts()

{

// The EditorPartCollection object is immutable so we have to use

// the following approach to return the base editor part collection

// along with our editor part

// Create a list of editor parts containing our editor part

List<EditorPart> editorParts = new List<EditorPart>(1);

EditorPart part = new ConfluencePageEditorPart();

part.ID = this.ID + "_confluencePageEditorPart";

editorParts.Add(part);

// Return a new EditorPartCollection containing the base editor parts

// and our editor part

return new EditorPartCollection(base.CreateEditorParts(), editorParts);

}

As you can see, it references a ConfluencePageEditorPart. This class inherits from EditorPart which is an extension of a Panel (a WebControl). In this particular editor part we override CreateChildControls and OnPreRender to display the content you see in the image further above. We happen to use our own custom DropDownList control to encapsulate the list of Confluence spaces and a custom TreeView control to encapsulate the pages within a space. This was done because they are both used in the Confluence Space Pages Web Part discussed further below.

The primary difference between a UserControl and an EditorPart, is that the EditorPart must be able to communicate to the web part. This is done through two methods: SyncChanges and ApplyChanges.

A web part is in charge of storing and retrieving the web part property values. SyncChanges is used to take the web part property values within the web part and provide them to the EditorPart so it can properly display the current settings.

public override void SyncChanges()

{

ConfluencePageWebPart pageWebPart = this.WebPartToEdit as ConfluencePageWebPart;

if (pageWebPart != null)

{

// Store the synced values to be handled later in OnPreRender

_syncedPageRenderMode = pageWebPart.PageRenderMode.ToString();

_syncedSpaceKey = pageWebPart.SpaceKey;

_syncedPageId = pageWebPart.PageId;

}

}

Typically, the EnsureChildControls method can be called to make sure that CreateChildControls is already called and then the properties can sometimes be applied directly to controls within the editor part. However, in our case the processing was a little more complex so we stored the property values in member variables to be used later within OnPreRender.

The ApplyChanges method is the corollary to SynChanges. It allows the editor part to provide web part property values to the web part.

public override bool ApplyChanges()

{

this.EnsureChildControls();

ConfluencePageWebPart pageWebPart = this.WebPartToEdit as ConfluencePageWebPart;

if (pageWebPart != null)

{

pageWebPart.PageRenderMode = (ConfluencePageWebPart.RenderMode)

(Enum.Parse(typeof(ConfluencePageWebPart.RenderMode),

_pageRenderModeDropDownList.SelectedValue));

pageWebPart.SpaceKey = _spaceDropDownList.SelectedValue;

if (_pagesTreeView.SelectedValue != null &&

_pagesTreeView.SelectedValue.Length > 0)

{

pageWebPart.PageId = Int64.Parse(_pagesTreeView.SelectedValue);

pageWebPart.Title = _pagesTreeView.SelectedNode.Text;

}

else

{

pageWebPart.PageId = -1L;

pageWebPart.Title = DefaultTitle;

}

}

return true;

}

Confluence Space Pages Web Part

The Confluence Space Pages web part shows the list of pages within a single Confluence space as shown below. The tree view of pages should look familiar. It uses the same Control created and used by the editor part for the Confluence Page web part.

The corresponding editor part for this web part simply allows the user to select a space from the space drop-down list. It also uses the same custom control used by the editor part for the Confluence Page web part to show the spaces.

The option to show the space list allows the user to show the drop down list of spaces within the body of the web part. This web part uses the same concepts discussed for the Confluence Page web part above including communication between the editor part and the web part.

Web Part Administration

In order to use the web parts, we need to know how to access Confluence. This could have been done through web part properties, but we decided to configure how to access Confluence within site settings.

We did this by creating a custom administration page. To provide access to the page we needed to create a custom action within our feature's "elements.xml" file.

<CustomAction

    Id="ConfluenceSettings"

    GroupId="SiteAdministration"

    Location="Microsoft.SharePoint.SiteSettings"

    Sequence="33300"

    Title="Confluence Settings"

    Rights="ManageWeb,BrowseUserInfo">

    <UrlAction Url="_layouts/Atlassian/ConfluenceSettings.aspx" />

</CustomAction>

Describing the details behind the deployment of the features and SharePoint solution file is beyond the scope of this blog entry, but the result of the above custom action within our deployed feature results in a site settings page with our "Confluence Settings" link.

The administration page itself is a SharePoint application page that inherits from LayoutsPageBase. You can use existing application pages under TEMPLATE\LAYOUTS within the 12 hive as a starting point. Simply find an existing page that is similar to what you want and copy it.

For your code behind you will want to declare protected page variables for each named control you want to access within your code, for example:

protected InputFormTextBox ConfluenceSite;

protected InputFormCheckBox UseSsoCheckBox;

Then you can populate the controls within OnLoad, OnPreRender, etc. and update SharePoint button click event handlers. In our case we stored the Confluence settings for the site within a property bag within the site. Each SharePoint site has a property bag you can use to store data. This allows us to store information without having to use a hidden list or a separate database.

Here is the code for saving the property values.

private void SetSiteProperty(string propertyName, string value)

{

if (SPContext.Current.Web.Properties.ContainsKey(propertyName))

{

SPContext.Current.Web.Properties[propertyName] = value;

}

else

{

SPContext.Current.Web.Properties.Add(propertyName, value);

}

SPContext.Current.Web.Properties.Update();

}

Retrieving the property values uses similar code, but we recursively look to the parent site's property bag if we cannot find the property at our site. This allows for simple inheritance of properties on sub-sites.

private string GetSiteProperty(string propertyName)

{

return GetSiteProperty(SPContext.Current.Web, propertyName);

}

private string GetSiteProperty(SPWeb site, string propertyName)

{

string propertyValue = String.Empty;

if (site.Properties.ContainsKey(propertyName))

{

propertyValue = site.Properties[propertyName];

}

else

{

if (!site.IsRootWeb)

{

// Look in the parent site if this property is not set in this site

propertyValue = GetSiteProperty(site.ParentWeb, propertyName);

}

}

return propertyValue;

}

Integrated Search

The goal for integrated search is to have both SharePoint and Confluence show the same search results containing content from both systems.

Integrated Search Overview

Integrated search was implemented by setting up MOSS to search Confluence and redirecting the Confluence search to the MOSS search site. The latter was done rather easily through a custom Confluence plug-in setting, but the former is discussed below.

Initially we considered writing a custom Protocol Handler to search Confluence, but quickly realized that using the Web Site content source within MOSS was going to be much quicker to implement. Unfortunately, we found that the Web Site search did not work if it required forms-based authentication, which is a typical setup for Confluence. Fortunately, our network engineer, Jerry Rasmussen, found a knowledgebase article discussing the problem with a solution (via a hot fix). Note that the hot fix is part of SP1, but there are still no data entry screens for forms-based authentication.

The manual setup for search was fairly involved requiring the user to configure several entities, one of which required editing an XML file and loading it through an AddRule.exe tool described in the knowledgebase article. This was due to the fact that the hot fix only updated the API, but not any data entry screens. Due to this involved process, we created custom search configuration screens.

In the end, using the hot fix provided in the KB article worked but was not a complete solution. This is because web site searches from MOSS do not store ACL information when the content is being crawled by the search engine. The solution was to implement a custom security trimmer. Note that Business Data Catalog (BDC) searches require a custom security trimmer as well, but SharePoint, file share, and Exchange public folder searches store ACLs and therefore do not need a custom security trimmer.

The custom search configuration screens and custom security trimmer are discussed below.

Custom Search Configuration

What we needed was an easy way to configure the following search items:

  • Content Source
  • Crawl Rule (with forms-based authentication configuration)
  • Scope
  • Registered Security Trimmer

To allow for the searching of multiple Confluence sources we decided to create a screen for managing the list of Confluence Search Sources and for creating an individual one. As done with the site administration screen for the web parts, we need to have a custom action that references our configuration screen.

<CustomAction

Id="ConfluenceSearchSettings"

GroupId="Search"

Location="Office.Server.ServiceProvider.Administration"

Sequence="33300"

Title="Confluence Search Settings" >

<UrlAction Url="_layouts/Atlassian/ManageConfluenceSearch.aspx" />

</CustomAction>

Unlike the web parts, this custom action is defined within a feature scoped to the farm level instead of the site collection. The result of this custom action is the link shown within the Shared Services Administration home page under the "Search" group. The Shared Services Administration home page is found by going to SharePoint 3.0 Central Administration and clicking on the SSP link (e.g., "SharedServices1").

Similar to the site administration screen, we used existing SharePoint administration screens to create our two administration screens.

To keep the created entites together a tagging mechanism was used. The ContentSource class has a Tag property that we used to store a semicolon delimited string of IDs for the security trimmer, crawl rule, and scope.

The more difficult aspect of this was figuring out how to register a security trimmer programmatically. Documentation on how to do this with stsadm is easy to find, but figuring it out how to do it without shelling out to stsadm was a little more challenging.

Custom Security Trimmer

A custom security trimmer is a .NET interface that is run at query time to determine the URLs within the search results to which the current user has access. The primary method for a security trimmer is the CheckAccess method.

public BitArray CheckAccess(

IList<string> documentCrawlUrls,

IDictionary<string, object> sessionProperties)

The implementation of the security trimmer was to take a subset of the provided documentCrawlUrls by culling out pages we did not want to show in search results. This was a way to implement a more sophisticated exclusion crawl rule than you can through the search configuration. Then we simply call a Confluence web service that takes in a set of URLs and returns an array of Booleans. Finally, we merge the skipped URLs with the Confluence permissions and return a BitArray.

Since this security trimmer runs at query time, performance must be a consideration. However, the security trimmer is only provided a relatively small number of URLs if the ratio of URLs accessible by the user to total URLs is relatively high. Basically the query engine wants to show enough results to fill a page. If the search results page shows 10 results per page, the query engine may provide 15 URLs at a time. If the user has access to less than 10, another 15 may be requested. When the user requests the next search result page, the security trimmer is invoked again.

If you write your own security trimmer, we recommend that you utilize .NET tracing for debugging purposes. You can also break into the code with a debugger, but you will likely need to include a Debugger.Break method for the debugger to attach properly.

Single Sign-On (SSO)

One of the primary goals of Confluence and SharePoint integration effort was to make sure the products could work together seamlessly with a minimal amount of authentication requests presented to the user. Another goal was to implement this without requiring additional products and intrusive configuration requirements. We chose to use the SSO service that comes with MOSS as a base for SSO functionality and to rely on custom code in the Confluence web parts and related Confluence Administrative Settings page.

SSO Overview

The following diagram and number references below it provide a high-level view of how SSO works between SharePoint and Confluence.

(1) The client browser will typically be configured with Windows Authentication/NTLM to access SharePoint.

(2) The Confluence web parts use the default MOSS SSO provider to look up stored credentials for "Confluence".

(3) The Confluence web parts will use the returned SSO credentials to access a Confluence web service to retrieve a list of pages or content for a specific page.

(4) The Confluence page content will return a SSO ticket and related XMLHTTP JavaScript methods to retrieve and forward Confluence SSO credentials from the Client to Confluence. This is used for seamless accessibility of images stored within Confluence or when the user clicks links within the web part that access Confluence.

(5) The Confluence page will begin rendering/processing the XMLHTTP JavaScript methods on the Client.

(6) The Client will "redeem" the SSO ticket to retrieve SSO credentials and use the credentials to authenticate the client.

(7) The Client complete rendering content, including images, links, etc. within the context of the SSO credentials.

Microsoft SSO Service

The Microsoft SSO Service provides a way for a user to provide an individual set of credentials to use for back-end service/web service impersonation. The individual set of credentials are stored and associated with a user's Windows-based credentials.

Why Did We Choose the Microsoft SSO Service?

  • The SSO Service is built into MOSS and does not require additional products.
  • The service immediate provides the ability to pass individual user credentials to the Confluence web service. This automatically allows Confluence to trim returning content within the context of the correct user.
  • The service supports the ability to plug in a different/custom SSO provider implementation.

Pros/cons of the Microsoft SSO Service:

Pros    

Cons

  • Service and basic SSO provider implementation Included with MOSS.
  • Simple/yet powerful.
  • Pluggable way to replace the out-of-the-box SSO provider; works very well for providing credentials to a web service.
  • Fairly simple to configure (depending on server configuration).
  • Must use Windows Authentication.
  • Requires credentials to be recaptured and stored in a separate repository (security risk).
  • Note: To eliminate an extra prompt for credentials, SSO credentials could be generated by an automated process.
  • Requires additional code to allow the browser to receive and impersonate credentials.

SSO Configuration

The following references helped to provide the specific configuration steps required to get the SSO service up-and-running for a Confluence-SharePoint integration environment. The first two links provide the basic Microsoft SSO service reference material. The third link helped to provide insight on the Microsoft SSO service itself and the relationship between service accounts, roles, etc.

SSO Code Samples

The following sections reference the areas of custom code and configuration used to implement a simple SSO solution. Note that some codein the sections below has been omitted (denoted with "…"). Most of the omitted code is unrelated to SSO functionality and has been left out to avoid confusion and clutter.

Client JavaScript Rendering

The Confluence Page Web part has SSO code to render client script to the browser. This is done within the web part's CreateChildControls overload. The code checks to see if the Microsoft SSO service has been configured for the "Confluence" application. If it has been configured, it uses the built-in SSO provider to reserve a credential ticket. This ticket is a generated string token that can used later to retrieve a user's credentials.

try

{

if (ConfluenceConfiguration.Instance.UseSso)

{

if (!this.Page.ClientScript.IsStartupScriptRegistered("Confluence"))

{

//Reserve a credential ticket from SSO Service

string ssoTicket = null;

Credentials.ReserveCredentialTicket(

Credentials.SingleSignonTicketType.Default,

ref ssoTicket);

this.Page.ClientScript.RegisterClientScriptBlock(

typeof(System.Web.UI.Page), "Confluence",

String.Format(SsoClientScriptFormatted,

HttpUtility.UrlEncode(ssoTicket)), true);

}

}

}

catch (SingleSignonException singleSignOnEx)

{

//Swallow this exception, we will not be generating a ClientScriptBlock for

//client-side authentication...

}

The code above renders the following JavaScript to the browser. Most if it is in the SsoClientScriptFormatted constant, with the exception of the SSO ticket highlighted in yellow.

var xmlHttp;

function createXMLHttpRequest()

{

if (window.ActiveXObject)

{

xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");

}

else if(window.XMLHttpRequest)

{

xmlHttp = new XMLHttpRequest();

}

}

function getCredentials()

{

url = "_layouts/Atlassian/ConfluenceSettings.aspx?getCredentials=true&id=<ticket>;

xmlHttp.open('GET',url,true);

xmlHttp.onreadystatechange=sendAuthRequest;

xmlHttp.send(null);

return false;

}

function sendAuthRequest()

{

if (xmlHttp.readyState==4)

{

xmlHttp.open('GET', xmlHttp.responseText, false);

xmlHttp.send(null);

}

}

createXMLHttpRequest();

getCredentials();

The call to createXMLHttpRequest is called as the browser is rendering one of the Confluence web parts. This call sets up the xmlHTTP object that is appropriate for the browser (Internet Explorer 6/7, Firefox, etc.). The next call to getCredentials makes a separate HTTP request to ConfluenceSettings.aspx to redeem the ticket/token that was originally send to the browser. Once the call to ConfluenceSettings.aspx returns, another request is sent to Confluence with the specific user's credentials. This establishes a session (and authenticates the user) between Confluence and the user's browser session.

Confluence Page Request

In order for the Confluence Page web part to render the Confluence page content, the web part must request the data from Confluence using the SSO credentials. This is done within the OnPreRender overload of the web part.

try

{

RemotePage page = ConfluenceHelper.GetConfluencePage(this.PageId);

}

catch (SingleSignonException singleSignOnEx)

{

int ssoErrorCode = singleSignOnEx.LastErrorCode;

if (ssoErrorCode == SSOReturnCodes.SSO_E_CREDS_NOT_FOUND)

{

try

{

ISsoProvider provider = SsoProviderFactory.GetSsoProvider();

string strSSOLogonFormUrl = provider.GetCredentialManagementURL(

ConfluenceConfiguration.Instance.SsoApplicationName).AbsoluteUri;

pageContent = "Click <b><a href=" + strSSOLogonFormUrl +

">here</a></b> to save your credentials for the Confluence Application.";

}

catch

{

pageContent = "Credentials not found.";

}

}

}

The code checks to determine if SSO credentials have been stored for the current user. If they have not been, the call to GetConfluecePage will throw an exception. This exception will be handled by the SingleSignonException handler which provides a link so the end-user can provide SSO credentials.

SSO Settings and Authentication Ticket Verification Code

The ConfluenceSettings.aspx page has been set up to respond with a Confluence login URL if a particular query string is provided with an appropriate token. This is used by the JavaScript emitted further above.

Below we show snippets of the OnLoad method for the page. This method pulls the credentials for the current user with a call to the GetCredentials method. A separate call is made to GetCredentialsUsingTicket. If both the username/password match from the credentials returned from both calls, then the credentials are returned to the browser through the JavaScript request. Note that to keep the code snippet smaller, we skip some interop code required to extract the username and password from the SSO provider.

The call to GetCredentialsUsingTicket is executed with elevated priveleges. This had to be done because the call requires SSO administrative-level privileges. This also requires the SSO Administrative account to be a site collection administrator.

//Check to see if the request is for ticket redemption for user credentials

if (Page.Request.QueryString.ToString().ToLower().Contains("getcredentials"))

{

//Make sure the user has been authenticated and pull the SSO

//credentials for the ticket and verify they match the current user

string ssoTicket = HttpUtility.UrlDecode(Page.Request.QueryString["id"]);

ssoTicket = ssoTicket.Replace(" ", "+"); //Loss of "+" during encoding/decoding...

SPSecurity.RunWithElevatedPrivileges(

delegate()

{

using (SPSite site = new SPSite(Web.Site.ID))

{

using (SPWeb web = site.OpenWeb())

{

//Retrieve the credentials using the ticket

Sso.Credentials.GetCredentialsUsingTicket(

0,

ConfluenceConfiguration.Instance.SsoApplicationName,

ssoTicket,

ref rgTicketCreds);

}

}

}

);

if (userName != string.Empty && password != string.Empty)

{

if (userName == rgTicketCreds[0] && password == rgTicketCreds[1])

{

Page.Response.Write(string.Format(

ConfluenceConfiguration.Instance.ConfluenceSite +

"/login.action?os_username={0}&os_password={1}&login=Log+In&os_destination=",

rgTicketCreds[0], rgTicketCreds[1]));

}

}

Page.Response.End();

}

Summary

We covered the primary points of content embedding (especially web parts), integrated search, and single sign-on. We didn't have the space here to cover some other aspects of this project, such as how we deployed the SharePoint Connector for Confluence into two SharePoint features and provided a MSI for installation. Nor did we get into our agile process for working with the Atlassian team on the other side of the US, not to mention their development team in Australia – that resulted in a couple of late nights!

We would like to thank Lawrence Liu and Deb Bannon of Microsoft for introducing us to Atlassian. We would also like to thank Jonathan Nolen of Atlassian for managing the requirements and giving guidance as well as Brendan Patterson who wrote the Confluence plug-in for SharePoint.

About the Authors

Kirk Liemohn – Kirk is a Principal Software Engineer with ThreeWill. His recent project experience includes enterprise search projects as well as a business analysis portal. Kirk manages a SharePoint blog at http://www.implementingsharepoint.com.

Chris Edwards – Chris is a Senior Software Engineer with ThreeWill. Chris' project roles have ranged from development/technical lead to development resource. He is certified as MCSD using Microsoft .NET and as MCTS: SharePoint Services 3.0, Application Development. Chris manages resource links related to WSS at http://wssresourceguide.com.

Published Thursday, January 10, 2008 6:33 PM by sptblog

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# MSDN Blog Postings &raquo; SharePoint Connector for Confluence - How We Did It

# re: SharePoint Connector for Confluence - How We Did It

Since you are using MOSS Enterprise Search to index the Confluence application, how did you accomplish security trimming of the Confluence Content.

thanks

Friday, January 11, 2008 11:26 AM by Peter Charles

# Link Listing - January 11, 2008

Announcements Southeast Valley .NET User Group [Via: jguadagno ] AJAX ASP.NET 2.0 AJAX Applications...

Saturday, January 12, 2008 3:41 AM by Christopher Steen

# Link Listing - January 11, 2008

Link Listing - January 11, 2008

Saturday, January 12, 2008 3:41 AM by Christopher Steen

# re: SharePoint Connector for Confluence - How We Did It

Regarding the security trimming, read the article a little closer esp. the "Integrated Search Overview" and "Custom Security Trimmer" sections and respond if you have a specific question.

In the "Integrated Search Overview" we introduce security trimming along with providing a link to Microsoft's overview on custom security trimming.  In the "Custom Security Trimmer" section we discuss our implementation. The formatting had a little problem in the translation to this blog, so if you look right above the custom security trimmer section we have a link to how we register a custom security trimmer programmatically.

Sunday, January 13, 2008 6:05 PM by Kirk L

# re: SharePoint Connector for Confluence - How We Did It

Can the outbound protocol for the credentials sent by the SSO service be in Kerebros? Can this be set somewhere? I have a request from a client to acesss a web service that uses Kerebros.. Thanks

Thursday, May 29, 2008 3:54 PM by harishpa

Leave a Comment

(required) 
required 
(required) 
 
Page view tracker