Dec 4, 2010

FAST Search Web Crawler – Part I

Many people don’t know that FAST Search for SharePoint contains two Web Crawler (I was one of them ;).
  • Web site indexing connector
    • Use when you have a limited amount of Web sites to crawl, without dynamic content.
  • FAST Search Web crawler
    • Use when you have many Web sites to crawl.
    • Use when the Web site content contains dynamic data, including JavaScript.
    • Use when the organization needs access to advanced Web crawling, configuration and scheduling options.
    • Use when you want to crawl RSS Web content.
    • Use when the Web site content uses advanced logon options
The Web site indexing connector can simply be configured in the central administration and is kind of limited. I really was dissatisfied, because it was not able to met my requirements. What are my requirements?
I want to crawl our enterprise wikis, team sites, profiles and especially the social bookmarks. This could easily be achieved with a SharePoint content source. BUT, I also want to crawl external (internet) pages that are referenced within wikis and social bookmarks. A big part of our company knowledge consist of hyperlinks to internet resources like articles and blog posts that has been collected and verified from our employees. I want that the crawler follows the external links and only crawl that page (1 hop, page depth 0). Additionally I want to crawl our internet blogs which are also an important part of our company knowledge (0 hops, page depth full). The SharePoint crawler is not able to follow external links at all. So I started to evaluate the simple web crawler, but unfortunately with no luck. The web crawler was only able to specify hops and page depth with no further constraints. For our blogs this would be okay, but not for the wikis and the social bookmarks. The configuration for the blogs and our homepage could easily be achieved through 0 hops and a full page depth. But for the wikis and the social bookmarks I need a more fine grained configuration. Crawl the intranet with 1 hop and full page depth and after the hop (the external site) just crawl this page (page depth 0).
image
This was not possible with the simple web crawler. I couldn’t believe that FAST would not be able to satisfy this requirement…
…and finally I found the FAST Search Web Crawler.
…but I also found a lack of documentation ;-) First of all forget the UI! This crawler can only be configured through command line tools and XML configs living on the FAST servers. The most important part is to create the Web Crawler Configuration as described here.

Web Crawler Configuration

<?xml version="1.0"?>
<CrawlerConfig>
<!-- Crawl collection name, must be unique for each collection.      -->
<!-- Documents are indexed in the collection by the same name.       -->
<DomainSpecification name="sp">
<SubDomain name="intranet">
<attrib name="start_uris" type="list-string">
<member>http://intranet</member>
</attrib>
<section name="include_uris">
<attrib name="prefix" type="list-string">
<member>http://intranet</member>
<member>http://mysite</member>
</attrib>
</section>
<section name="passwd">
<attrib name="http://intranet" type="string">
FastCrawl:pass@word1:contoso:auto
</attrib>
<attrib name="http://mysite" type="string">
FastCrawl:pass@word1:contoso:auto
</attrib>
</section>
<section name="crawlmode">
<!--Crawl depth (use DEPTH:n to do level crawling).-->
<attrib name="mode" type="string">FULL</attrib>
<!--Follow links from one hostname to another (interlinks).-->
<attrib name="fwdlinks" type="boolean">yes</attrib>
<!--Reset crawl level when following interlinks.-->
<attrib name="reset_level" type="boolean">no</attrib>
<attrib name="robots" type="boolean">no</attrib>
<attrib name="max_uri_recursion" type="integer">5</attrib>
</section>
</SubDomain>

<SubDomain name="full_depth_no_hops">
<attrib name="start_uris" type="list-string">
<member>http://dataone.de</member>
<member>http://iLoveSharePoint.com</member>
<member>http://aknauer.blogspot.com</member>
<member>http://www.markus-alt.de</member>
<member>http://aknauer.blogspot.com</member>
<member>http://bydprojekt.blogspot.com</member>
<member>http://www.andreaseissmann.de</member>
<member>http://cglessner.blogspot.com</member>

</attrib>
<section name="include_uris">
<attrib name="prefix" type="list-string">
<member>http://dataone.de</member>
<member>http://iLoveSharePoint.com</member>
<member>http://aknauer.blogspot.com</member>
<member>http://www.markus-alt.de</member>
<member>http://bydprojekt.blogspot.com</member>
<member>http://www.andreaseissmann.de</member>
<member>http://cglessner.blogspot.com</member>
</attrib>
</section>

<section name="crawlmode">
<!--Crawl depth (use DEPTH:n to do level crawling).-->
<attrib name="mode" type="string">FULL</attrib>
<!--Follow links from one hostname to another (interlinks).-->
<attrib name="fwdlinks" type="boolean">no</attrib>
<!--Reset crawl level when following interlinks.-->
<attrib name="reset_level" type="boolean">no</attrib>
</section>
<attrib name="max_uri_recursion" type="integer">5</attrib>
</SubDomain>

<!-- List of start (seed) URIs. -->
<attrib name="start_uris" type="list-string">
<member>http://intranet</member>
<member>http://dataone.de</member>
<member>http://iLoveSharePoint.com</member>
<member>http://www.markus-alt.de/blog</member>
<member>http://aknauer.blogspot.com</member>
<member>http://bydprojekt.blogspot.com</member>
<member>http://www.andreaseissmann.de</member>
<member>http://cglessner.blogspot.com</member>
</attrib>

<!-- Include and exclude rules. Each type of rule may contain a   -->
<!-- the following types: exact, prefix, suffix, regexp and file. -->
<!-- See "include domains" for an example.                        -->

<!-- Include the following hostnames in the crawl. If no hostnames -->
<!-- are specified, the crawler will crawl any hostname unless     -->
<!-- "include_uris" are specified, in which case only URIs         -->
<!-- those rules are crawled.                                      -->
<section name="include_domains">
<attrib name="exact" type="list-string"></attrib>
<attrib name="prefix" type="list-string"></attrib>
<attrib name="suffix" type="list-string"></attrib>
<attrib name="file" type="list-string"></attrib>
</section>

<!-- Include the following URIs in the crawl. -->
<section name="include_uris"></section>

<!-- The following hostnames will be excluded from the crawl, -->
<!-- even if they were included by include rules above.       -->
<section name="exclude_domains"></section>

<!-- The following URIS will be excluded from the crawl, -->
<!-- even if they were included by include rules above.  -->
<section name="exclude_uris"></section>

<!-- Crawl Mode -->
<section name="crawlmode">
<!-- Crawl depth (use DEPTH:n to do level crawling). -->
<attrib name="mode" type="string">DEPTH:0</attrib>
<!-- Follow links from one hostname to another (interlinks). -->
<attrib name="fwdlinks" type="boolean">no</attrib>
<!-- Reset crawl level when following interlinks. -->
<attrib name="reset_level" type="boolean">no</attrib>
</section>
<section name="passwd">
<attrib name="http://intranet" type="string">
FastCrawl:pass@word1:contoso:auto
</attrib>
</section>

<attrib name="robots" type="boolean">no</attrib>
<attrib name="max_uri_recursion" type="integer">5</attrib>
<!-- Delay in seconds between requests to a single site -->
<attrib name="delay" type="real">60</attrib>
<!-- Length of crawl cycle expressed in minutes -->
<attrib name="refresh" type="real">1440</attrib>
<!-- Maximum number of documents to retrieve from one site. -->
<attrib name="max_doc" type="integer">5000</attrib>
<!-- Let each Node Scheduler crawl this many sites simultaneously. -->
<attrib name="max_sites" type="integer">32</attrib>
<!-- Maximum size of a document (bytes). -->
<attrib name="cut_off" type="integer"> 5000000 </attrib>
<!-- Toggle JavaScript support (using the Browser Engine). -->
<attrib name="use_javascript" type="boolean"> no </attrib>
<!-- Toggle near duplicate detection. -->
<attrib name="near_duplicate_detection" type="boolean">no</attrib>

<!-- Inclusion and exclusion.                                   -->
<!--                                                            -->
<!-- The following section sets up what content to crawl and    -->
<!-- not to crawl.                                              -->
<!-- Only crawl HTTP/HTTPS (e.g., don't crawl FTP). -->
<attrib name="allowed_schemes" type="list-string">
<member> http </member>
<member> https </member>
</attrib>
<!-- Allow these MIME types to be retrieved. -->
<attrib name="allowed_types" type="list-string">
<member> text/* </member>
<member> application/* </member>
</attrib>

</DomainSpecification>
</CrawlerConfig>

The main domain specification for content collection sp (sp is the default collection for SharePoint) is configured to crawl a page depth of 0 (DEPTH:0) and to not forward to hyperlinks pointing to other domains (fwdlinks=false). The start uris contains the url to our intranet, homepage and blogs. At this level there isn’t any include uri or domain pattern specified. This means any url will match the rule. In the sub domain “intranet” I defined as start uri http://intranet/ and very important to only include uris that start with http://intranet/ or http://mysite/ to this sub domain. I also defined a page depth of full and to follow external urls. Every url that start with the previous defined url prefixes will follow that crawl rule. This means that the http://intranet/ will be crawled with full depth and also follows external links to other domains. But the followed domains (e.g. “http://microsoft.com”) will not match the intranet sub domain pattern and will fallback to the main domain specification which only allows to crawl the particular page. There is also a sub domain called “full_depth_no_hops” which includes our blogs and homepage in the include uri patterns with full page depth and no forwarding. Exactly what I want :-)

Deploy the configuration

  • Log on to the FAST Search Server
  • Copy your configuration to “%FASTSEARCH%\etc\” (e.g. MyCollection.xml)
  • Start the FAST Search PowerShell Shell
  • Ensure that crawler is started: nctrl.exe start crawler
  • Register the config: cawleradmin.exe --addconfig “%FASTSEARCH%\etc\MyCollection.xml”
  • (optional) Start a new crawl: cawleradmin.exerefetch
  • Monitor the fetch log: “%FASTSEARCH%\etc\var\log\crawler\node\fetch\sp”
Fetch log will look like this:
2010-12-03-17:03:43 200 REDIRECT  http://ilovesharepoint.com/ Redirect URI=http://cglessner.de/ 2010-12-03-17:03:44 200 NEW       http://bydprojekt.blogspot.com/ 2010-12-03-17:03:46 200 REDIRECT  http://www.markus-alt.de/ Redirect URI=http://www.markus-alt.de/blog 2010-12-03-17:03:46 200 NEW       http://aknauer.blogspot.com/ 2010-12-03-17:03:53 200 NEW       http://www.andreaseissmann.de/ 2010-12-03-17:04:43 302 REDIRECT  http://intranet/ Redirect URI=http://intranet/Pages/Home.aspx 2010-12-03-17:04:43 301 REDIRECT  http://www.markus-alt.de/blog Redirect URI=http://www.markus-alt.de/blog/ 2010-12-03-17:04:43 302 REDIRECT  http://dataone.de/Seiten/VariationRoot.aspx Redirect URI=http://dataone.de/de
….

The FAST Search Web crawler is a high end Web Crawler!

Keep in mind that you still need the SharePoint crawler because of the keywords, taxonomy and security. The web crawler will do an additional crawl. FAST can handle this duplicates. The second crawl is a drawback, but the result is worth it.
I will post more details (hopefully) soon…

Review Collaboration Days 2010

Last week has been the Collaboration Days Swiss, with about 300 attendees it has been the biggest SharePoint event in Switzerland so far. I liked the fact that the event has been community driven. Especially many thanks to Samuel Zürcher and Stefan Heinz for their effort and commitment that has contributed significantly to the success of the event. I believe this was a big step forward for the SharePoint Community Swiss. What I personally regret, is that I had to cancel two session (and missed the #SharePint ;) because of hoarseness. Thanks to Thorsten Hans and Nicki Borell for the great backup.

Interview with Eric Swift (Microsoft SharePoint General Manager)


Interview with Nicolette du Toit (Marketing Manager for Office at Microsoft Swiss)


I’m looking forward for the Collaboration Days 2011…

PS: Thanks to the HLMC stuff for all the ginger tea. I’m almost fit :-)

Nov 14, 2010

My Impressions from TechEd Europe 2010

Unfortunately I haven’t seen much sessions. On the one hand I’ve worked at the SharePoint TLC booth and on the other I’ve missed some sessions cause of interesting discussions with other geeks and partners. Some sessions I’ve seen was great, others wasn't. Especially I don’t like the boring, visionless TechEd keynotes. But the magic of the TechEd aren’t merely the sessions, but rather the all the people from different nations. It has been amazing to meet in person all the geeks from Europe – many soul mates :)

SharePinting at TechEd Europe 2010

If you think there’s an typo in the title, read this first: SharePoint by day, SharePint at night.

For sure we’ve organized a SharePint and I was responsible for choosing the location. I had picked out the PURO - an amazing sky lounge. But unfortunately the PURO didn’t let all the techies in – #FAIL!
No problem for the agile SharePoint geeks. Andrew Woodword just opened a second SharePint – the underground SharePint - at the Irish Pub. So we have had two simultaneous SharePints at one building, one at the 20th floor and one at the basement. In addition the two SharePints had been very different. Seems different styles of SharePints are emerging…

Top of the Roof SharePinting
Meanwhile at the basement - Underground SharePinting


Thanks for the great week to: @mysharepoint, @tlanni, @andrewwoody, @katko, @mosslive, @robinmeure, @danmc, @markvlunenburg, @rubykon, @draken, @jefffried, @atwork, Sven Maier, Patrick Heyde, AvePoint

Oct 28, 2010

My Event Tips for End of 2010

TechEd Europe 2010 (Berlin, 08-12.11.2010)

I will work at the Technical Learning Center (TLC) for Office/SharePoint. (Twitter: #tee10)

image

 

Social Software 2010 (Munich, 23-25.11.2010)

Conference about Social Software for Enterprise. I’ will hold a session called “Cave Painting to Social Computing” and will show examples with SharePoint 2010. Save 100€ with the promo code: ChGl_550. (Twitter: #soso)

image

 

Collaboration Days (Luzern, 01-02.2010)

The most important SharePoint Event in Switzerland. I will hold a 1 day SharePoint Development Workshop on 30.11.2010 and got two sessions: “Social Computing with SharePoint 2010”  and “Diving into the SharePoint 2010 Fluent User Interface”. Additionally I’m glad to support the content board from the SharePoint Community Switzerland.  (Twitter: #collabdays)

image

Aug 20, 2010

SharePoint Scripts On Demand (SP.SOD)

When you are working with Ribbon UI extensions, Client OM or with complex SharePoint 2010 java scripts, you may noticed a Java Script class called SP.SOD. Built-in SharePoint scripts use this class extensively. Unfortunately SP.SOD (I guess it stands for SharePoint Scripts On Demand) is very rarely documented – until yet :)

SharePoint 2010 makes extensive use of java scripts. These scripts are located in several different files. And commonly these scripts have dependencies to functions and classes defined in other script files. Means the built-in SharePoint scripts and for sure your own scripts have to care about when which script will be loaded. And exactly this is the job of SP.SOD class! SP.SOD is defined in init.js and will be load in the HTML head section automatically.

ScriptLink

SP.SOD works tightly together with the ScriptLink server control. ScriptLink is a SharePoint server control for registering java script files.
<SharePoint:ScriptLink ID="scriptLink1" runat="server" Name="My_Script.js" LoadAfterUI="true" OnDemand="false" />

In the “Name” property you should enter the name of the script file. The control assumes that the script is located in the layout’s root folder. You could also use a relative URL instead of the script name, but cause problems with OnDemand=true. The intention is definitely to use the script name. Putting your scripts directly into the layouts folder without a solution/feature specific subfolder seems to be against the best practice, but is required! I recommend using solution/feature prefix for the files instead of a folder.

Localizable” true instructs the control to search the script in the language specific subfolders of the layout’s folder (e.g. _layouts/1033/MyScript.js).

LoadAfterUI” determines where the script reference will be rendered in the HTML document. False inserts the reference into the HTML head. True inserts the reference at the end of the HTML body.

OnDemand” true specifies that the script should be loaded on demand. False will render the following script code:

<script type="text/javascript">
// <![CDATA[
document.write('<script type="text/javascript" src="/_layouts/my_script.js"></' + 'script>');
// ]]>
</script>

True will render:
<script type="text/javascript">RegisterSod("my_script.js", "\u002f_layouts\u002fmy_script.js");</script>

As you see “true” doesn’t insert the script reference directly, instead it calls the function RegistedSod. RegisterSod is the same as SP.SOD.registerSod(key, url). The script will not be loaded until it get explicitly requested (see SP.SOD.executeFunc)!

Instead of using SPScriptLink in a declarative way to register scripts you could also do this with some static methods of SPScriptLink in the code behind:


SP.SOD.executeOrDelayUntilScriptLoaded

SP.SOD.executeOrDelayUntilScriptLoaded(func, scriptName) schedules an asynchronous callback function (func) which will be called when the script has signaled finished loading. Signaled finished loading means that the script has called notifyScriptLoadedAndExecuteWaitingJobs(scriptName). All SharePoint built-in scripts will call notifyScriptLoadedAndExecuteWaitingJobs when they have finished loading. ExcuteOrDelayUntilScriptLoaded does not trigger loading an on demand script (SOD)!

There is a very similar function pair called executeOrDelayUntilEventNotified/notifyEventAndExecuteWaitingJobs. The only diffrence is that  the executeOrDelayUntilScriptLoaded/executeOrDelayUntilScriptLoaded function pair prefixes internally the scriptName with “sp.scriptloaded-“.

Example 1:

My_ApplicationPage.aspx
<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<SharePoint:ScriptLink ID="sl" runat="server" Name="My_Script.js" LoadAfterUI="true" OnDemand="false" />
<script type="text/javascript" language="javascript">

function myCallback() {
//sayHello is defined in MyScript.js
sayHello();
}

alert('1');

ExecuteOrDelayUntilScriptLoaded(myCallback, "my_script.js");

alert('2');

</script>
</asp:Content>

My_Script.js
alert('3');

function sayHello() {
alert('4');
}

SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs("my_script.js");

We have defined a script link for My_Script.js which will be inserted at the end of the HTML body (LoadAfterUI=true). In the inline script in the head we have registered the function myCallback to get executed when MyScript.js has signaled finished loading. The alerts will popup in the sequence 1-2-3-4. You could also wait for any built-in script to have finished loading e.g. executeOrDelayUntilScriptLoaded(myCallback, “sp.ui.js”).

You could also use the static method ScriptLink.RegisterDelayedExecutionScript of SPScriptLink to register a delayed script execution (instead of the line: ExecuteOrDelayUntilScriptLoaded(myCallback, "my_script.js");

SP.SOD.registerSod

With SP.SOD.registerSod(key, url) you can manual register an on demand script via JavaScript.
<script type='text/javascript'>RegisterSod('my_script.js', '/_layouts/my_script.js'); </script>

SP.SOD.executeFunc

SP.SOD.executeFunc(key, functionName, fn) is used to load on demand scripts (ScriptLink.OnDemand=true).
The “key” parameter must match to the ScriptLink’s Name property (Use small letters for key, because an issue with string normalizing in RegisterSodDep).

functionName” awaits a type name of an ASP.NET AJAX JavaScript class. ExecuteFunc first checks if the AJAX class has already been registered, when not it checks additionally if a SOD with this key has already been loaded and finally it will load the SOD. Load means adding dynamically the corresponding script tag to the HTML head. The check for the type helps to ensure that the script has not been already loaded via an usual script tag before. When you don’t want to work with AJAX JavaScript classes you can use “null” value for the functionName.

fn defines a callback that will be executed when the SOD has signaled finished loading.

Example 2:

My_ApplicationPage.aspx
<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<SharePoint:ScriptLink ID="sl" runat="server" Name="My_Script.js" LoadAfterUI="true" OnDemand="false" />
<!-- RegisterSod('my_script.js', '/_layouts/My_Script.js'); -->

<script type="text/javascript" language="javascript">

function myCallback() {
sayHello();       
}

alert('1');

SP.SOD.executeFunc("my_script.js", null, myCallback);

alert('2');

</script>
</asp:Content>

My_Script.js keeps unchanged.

The example is similar to example 1. The difference is that ScriptLink registers a SOD (ScriptLink.OnDemand=true) script. MyScript.js will not be loaded until it will be explicitly demanded with “SP.SOD.executeFunc(‘my_script.js’);”. When the SOD has signaled finished loading with “SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs(‘my_script.js’);”  myCallback will be executed. The alerts will popup in the sequence 1-2-3-4 again.

SP.SOD.registerSodDep

SP.SOD.registerSodDep(key, dep) can register SOD dependency. Key defines the SOD that depends on the SOD defined in dep. When the SOD in key will be requested, the SOD dependency will ensure that the SOD defined in dep will loaded before the actual SOD defined in key. You can also define a chain of dependencyies. This means the SOD in dep can also have dependencies, and the dependencies can have dependencies too and so on. The SOD loading mechanism keeps care to resolve all the required dependencies.

Example 3:

My_ApplicationPage.aspx
<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<script type="text/javascript" language="javascript">
function myCallback() {
sayHello();       
}

alert('1');

function myInit(){
SP.SOD.executeFunc("my_script.js", null, myCallback);
alert('2');
}

_spBodyOnLoadFunctionNames.push("myInit");

RegisterSod('my_script.js', '/_layouts/My_Script.js');
RegisterSod('my_script2.js', '/_layouts/My_Script2.js');

RegisterSodDep('my_script.js', 'my_script2.js');

</script>
</asp:Content>


My_Script.js
alert('4');

function sayHello() {
alert('5');
sayHello2();
}

SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs("my_script.js");
My_Script2.js
alert('3');

function sayHello2() {
alert('6');
}

SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs("my_script2.js");

The example registers two SODs via JavaScript (you could do this with the ScriptLink control as well) and defines that my_script.js depends on my_script2.js. When my_script.js will be requested via “SP.SOD.executeFunc(‘my_script.js’, null, myCallback);” SP.SOD will first load my_script2.js and then my_script.js. The alerts will popup in the sequence 1-2-3-4-5-6.

Summary

The idea of on demand script loading make really sense. SharePoint 2010 loads really a lot of JavaScripts - this takes time! So the idea is: first load the HTML and let it render by the browser so that the user is able to read the requested information as fast as possible. And in the second step load the behavior (the JavaScripts).

Jul 28, 2010

My Development Box

Here is the actual configuration of my development box. SharePoint and for sure .NET development works like a charm :)

Hardware

  • HP Elitebook 8540w
  • Processor: Intel Core i7 -820 Quad 1.73 GHz
  • RAM: 16 GB DDR3-SDRAM 1333 MHz (4x 4GB)
  • Display: 15.6” HD+ integrated 2 MP Webcam
  • Graphic: nVidia Quadro FX880M
  • HD: 500 GB SATA 7.200rpm
  • Connect: Intel 802.11 a/b/g/n , Bluetooth, UMTS

Software

For SharePoint demos and workshops I’m using the Contoso Hyper-V image. Because Contoso didn’t use setup best practices (e.g. admin service accounts) I use for development a from scratch installed SharePoint Hyper-V image.

Jul 18, 2010

User Defined Actions in Nintex Workflow 2010 (Domain Actions)

My favorite new feature in Nintex Workflow 2010 are User Defined Actions (UDA). An UDA is a kind of workflow template which can be used like any other Nintex action. More technically spoken it is an composite activity. Comfortable is that you can design them in the same easy way as an usual Nintex workflow. A technically minded workflow designer can create domain UDAs, maybe to encapsulate web service calls to a SAP system. And later on the business users can use them in their workflows without any technical background knowledge. Time for an example…

The “Send Tweet” User Defined Action

In this example we will create a UDA for posting Twitter status updates. Imagine we are an social marketing aware company and want to give our marketing team the ability to send tweets from Nintex workflows. They should be able to design the workflows themselves (usually approval workflows) and should not care about technical details. Perfect scenario for an UDA.

Site Actions > Nintex Workflow 2010 > Manage UDAs

image

Click “Create”

image

Start designing the UDA in the familiar Nintex WF designer

image

Define input and output parameters

image

Create an input parameter for the status update message (Message).

image

Drag an “Build dynamic string” action for building the URL and a “Web request” action for posting the update to the designer.

image

Configuration of the “Build Url…” action.

image  
http://api.twitter.com/1/statuses/update.xml?status={WorkflowVariable:Message}

The result variable “Url” can be created directly the config dialog via the “Variables” menu item in the ribbon.

Configuration of the “Post…” action.

image

You only need to configure Url, Username & Password and that the POST verb should be used.

Tip: Instead of defining the user credentials inline you can use a Nintex workflow constant of type credentials. The advantage is that you can change the credentials without changing and republishing the UDA. 

Configure UDA settings

image

The “Title” defines the display name of the action and the “Category” defines in which group it will be shown in the designer. Also nice is that you can define you own icons.

Publish the UDA

image

image

By default the UDA is scoped for the current team site, but you can promote it to the site collection or farm level. Additionally you can import and export UDAs.

Use the “Send tweet” action in a workflow 

image

The “Send tweet” action is now available in the “Twitter” group on the workflow actions menu on the left. Now the marketing team could easily use it within an approval action.

Configuration of “Send Tweet”

image  

The marketing team only have to provide the message for the Twitter status update and don’t care about any technical detail.

Test it

Start the workflow and approve the tweet.

image 

image 

You can easily encapsulate more Twitter API calls like this and build kind of a domain language. More ideas about Twitter and Nintex you can find on Markus Alt’s blog. Twitter is just a trivial example, you could also use this approach to design domain actions SAP or for any other system.

Jul 16, 2010

Amazing Nintex Workflow 2010 Promotion Video

Watch this amazing video from Mike Fitzmaurice about Nintex Workflow 2010. One of the coolest and most creative promotion videos I’ve ever seen!

Well done Mike :-)

SharePoint 2010 Development Beta Exams Passed

Happy to have passed the SharePoint 2010 development beta exams:

Exam 70-573: MCTS: Microsoft SharePoint 2010, Application Development
Exam 70-576: PRO: Designing and Developing Microsoft SharePoint 2010 Applications 

The the MCTS exam was moderate, but the PRO was hard.
Good luck.

Jun 15, 2010

“Unsocial” - FAST Search for SharePoint 2010

I’ve just noticed that FAST Search is not able to crawl social tags. Social tags are tags created with the Tags & Note board. Social tags are different from Enterprise Keywords (managed tags). Enterprise Keywords will be persisted with the list item itself in the Content DB and social tags will be persisted in the Profile DB. Though FAST Search is able to crawl managed tags pretty well, it doesn't support social tags at the moment. In turn the SharePoint Server OOB Search supports both. This a feature restriction of FAST Search. Microsoft’s edition comparison sheet suggests that there is only a plus, but this isn’t the truth for any particular case. I really miss the social tag crawling in FAST Search. Social tagging in combination with a great search like FAST would be a huge benefit for companies. Now I’ve three options:
  1. Use the OOB Search and miss all the great FAST Search features
  2. Use FAST and miss the social tag crawling and refinement
  3. Wait for third party tools…
FAST Search for “I like it”:
image
OOB Search for “I like it”:
image

There isn’t always a plus - that’s life!

Apr 21, 2010

Selective Restore? Yes! But which Backup?

Here a real user story and solution. One of our customers would be able to selective restore SharePoint content. Because he has already a great SQL backup strategy he decided for a third party tool from from Metalogix, Selective Restore Manager, which gives him the ability to selective restore from SQL backups or databases. Not a very extra extraordinary scenario. But which backup should he use? Maybe a document or site has been deleted accidently some weeks or months ago. Searching in all backups for the missing item would be very cumbersome. How we can solve this in an agile manner? Sure, with our good old friend PowerShell! The approach is a scheduled PowerShell script querying the SharePoint audit logs for delete events in all site collection from a specific web app and write the log entries in a usual SharePoint list on another web app (central admin). Writing this script has taken less than an hour. Even lunch has taken longer ;) I really love the speed, power and flexibility you gain with PowerShell!

The list looks like this (click to enlarge):

image

The script:

Apr 13, 2010

Review ShareCamp 2010

Last week end was the very first ShareCamp (BarCamp about SharePoint) at the Microsoft Headquarter Germany in Munich. We got about 200 participants, 45 session and the spirit was really amazing! I think we could fulfill our slogan born2share.

ShareCamp

You can find many more pictures here.

Thanks to all participants and sponsors for making this great event possible. I’m convinced there will be a ShareCamp 2011!

born2share.

Apr 5, 2010

Freshly Graduated MVP

I'm glad to announce that I am since the 1th of Avril a Microsoft SharePoint MVP. What for the actor is a Oscar, is for a Microsoft tech geek the MVP Award. This is a big honor for me and I would like to thank Microsoft and the SharePoint community!

Mar 10, 2010

SharePoint Disposing Myth: You have to dispose SPWeb explicitly!?

It seems to me that every post, article and book tells you to explicitly dispose every SPWeb (excepts them coming from SPContext). But is that right?
Not quite! Every SPSite tracks the SPWebs that it has emitted. A internal list named “m_openedWebs” holds these references. It doesn’t matter from which member of the SPSite you will get the SPWeb (RootWeb, OpenWeb, AllWebs, Add) the SPSite holds a reference on it. When you dispose the SPSite it loops through all SPWebs in “m_openedWebs” and calls the Close method on each. The dispose method of SPWeb does nothing else than calling it’s close method. Means when you dispose a SPSite, all SPWebs that it has emitted will be disposed implicitly! A construct like this
using(SPSite site = new SPSite("http://server/site"))
{
     using(SPWeb web = site.OpenWeb())
     {
       //...
     }
}
is not necessary, because the using for the SPSite will implicitly dispose all of it SPWebs. By the way if you call the dispose method of the SPWeb explicitly it will internally remove itself from the “m_openedWebs” list. Sure there are scenarios where explicitly disposing of SPWebs is important, for example when you loop through the AllWebs property of the SPSite with maybe hundreds of SPWebs. Because every SPWeb consumes a lot of memory! On the other hand you don’t have to be afraid when you pass a SPWeb reference across your API. When you finally dispose the SPSite, all SPWebs it has emitted will be disposed too and you will not cause a memory leak. I think the SharePoint team have implemented this pretty well.

When you create an instance of SPSite you are responsible to dispose it. When to dispose SPWeb that have been emitted by your SPSite depends on the scenario.


What could cause problems indeed is when you still use a SPWeb that has already been disposed, doesn’t matter if it has been disposed implicitly or explicitly. The SPWeb will be reopened but will not be listed in the “m_openedWebs” of the SPSite. Means it can not be implicitly disposed by the SPSite anymore. A better and approach would have been to throw an ObjectDisposedException on all members after the SPWeb has been disposed.

You don’t believe? Just take a look at the following snippet or even better let it run:
Console.WriteLine("Create a SPSite");
SPSite site = new SPSite("http://localhost:101");

Console.WriteLine("Open 5 SPWebs...");
SPWeb web1 = site.RootWeb;
SPWeb web2 = site.OpenWeb();
SPWeb web3 = site.OpenWeb("my");
SPWeb web4 = site.AllWebs[1];
SPWeb web5 = site.AllWebs.Add(Guid.NewGuid().ToString());

int openedWebs = (typeof(SPSite).InvokeMember("m_openedWebs",
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField,
null, site, null) as ICollection).Count;      

Console.WriteLine("Acutally opened SPWebs: " + openedWebs.ToString());

Console.WriteLine("Dispose web1 explicitly");
web1.Dispose();

openedWebs = (typeof(SPSite).InvokeMember("m_openedWebs",
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField,
null, site, null) as ICollection).Count;     

Console.WriteLine("Dispose the SPSite");         
site.Dispose();

openedWebs = (typeof(SPSite).InvokeMember("m_openedWebs",
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField,
null, site, null) as ICollection).Count;

Console.WriteLine("Acutally opened SPWebs: " + openedWebs.ToString());

Console.ReadKey();   


Any feedback?

Mar 1, 2010

Manage ASP.NET Providers with PowerShell

I’ve written this script to manage SQL users on a SharePoint box with form based security (FBA).

Usage:

Load the script
PS>. .\Manage_ASP_NET_Providers.ps1  “C:\..\web.config”

You have to provide the path to the web.config which contains the membership provider configurations.

The script will change the current app domain’s config path and then loads the System.Web Assembly. The sequence is important. If you want to change app config path later, you have to restart PowerShell and load the script again with another path.

Examples:

Get Membership Provider “sqlMembers”
PS> $provider = Get-MembershipProvider  “sqlMembers”

Add a new user (login, mail, question, answer)
PS> $provider | Add-MembershipUser “cglessner” “cg@test.de” “Best Portal” “SharePoint”

List first 1000 users
PS> $provider | Get-MembershipUser –maxResult 1000

….

 

Bye, Christian

Feb 10, 2010

ShareCamp – born2share.

the SharePoint Community Germany is conducting a first-of-its-kind BarCamp format on SharePoint, coined ShareCamp.

Our slogan is “born2share.” (spoken born to share point ;)

ShareCamp will be a two-day BarCamp event. BarCamps, also called non-conferences, that is looking for active contributors rather than passive consumers. There is no fixed, up-front agenda, but all participants are invited to present – contents will be decided upon among the participants. Spontaneous sessions replace the pre-prepared presentations found at traditional conferences. Developing ideas freely and mutual interaction are not only allowed, but explicitly encouraged.

ShareCamp addresses technical issues (installation, administration, migration, …) as well as social aspects of collaboration and their effects on company cultures. Best practices, helpful tools and add-ons – all topics of interest to the participants are welcomed, in particular special interest topics.
We expect a mixed audience of up to 200 SharePoint administrators, developers, end users, Microsoft staff and solution providers.

ShareCamp takes place April 10/11 at the Microsoft headquarters in Unterschleissheim (Munich); Microsoft will also serve as a diamant sponsor of the event.

Participation and catering is free of charge for all participants.

ShareCamp is non-profit oriented, but an independent event organized by volunteers from within the SharePoint community. ShareCamps are only made possible by the kind contribution of sponsors that allow for a such an event to be free of charge and to cover expenses for event site, infrastructure, and catering.
The idea a BarCamp about SharePoint was born at TechEd Europe by Michael Greth and myself. Here we go!

born2share