SharePoint Repository for NuGet is alive!

I’m happy to announce that my new open source project, SharePoint Repository for NuGet, is now live on Codeplex!

SPRNuGet is a powerful integration between NuGet, the renowned .net package management system and Microsoft SharePoint 2010.

SPRNuGet combines SharePoint 2010 powerful features such as security, publishing and UI to help you organize your code packages using build-in features of SharePoint such as folders, item level forms, ratings and enterprise keywords.

SPRNuGet is a site-collection level feature and once installed a new folder will be created to hold the NuGet packages. When a package is uploaded and successfully verified as a NuGet package you’ll be able to change its Metadata using the following form:

The form will allow you to set the following Metadata fields for a NuGet package:

  • Title
  • Description
  • Summary
  • Language
  • Copyrights
  • Version
  • Icon URL
  • Both the Latest Version and Absolute Latest version fields
  • The project URL
  • The project’s License URL
  • Release notes
  • The license acceptance field
  • Tags (from a SPRNuGet term store created during the installation process)
  • Rating

When completed, the package is added to the SharePoint folder as follows:

You can edit the package Metadata at any given time, add tags or change its rating.

When connected to Visual Studio 2010, SPRNuGet will show packages the exact same way you are used to from NuGet:

So what are you waiting for? Go have a look at SPRNuGet right now at http://sprnuget.codeplex.com and don’t forget to follow SPRNuGet on Twitter for the latest news and announcements!

Advertisements

Working with FAST Search For SharePoint 2010 Document Promotions Programmatically

As much as I love working with the FAST for SharePoint 2010 search engine, finding documentation for it online is quite a challenging task. This is why I’m going to try and document as much as possible about the little ‘gems’ I find while working with FAST.

Using FAST for SharePoint you can define documents to be promoted within the search results list, indicating that they are highly relevant results for a particular keyword or its synonyms. The promoted documents will appear at the top of the search result list.

While this feature works great when using OOTB Core Search Results web part, it fails miserably when using an extended Core Search Results web part (a web part that inherit from the base Core Search Results web part and extend its functionally, such as giving the developer full control over the FQL (Fast Query Language) query sent to the server).

Today I had a client who uses such web part and thus has to get the promoted documents urls programmatically so he can append an xrank property to the FQL query. This was much trickier task than expected. To get the promotions you first have to get the Search Setting group for your site. This can be done using the following code:

var ssaProxy = (SearchServiceApplicationProxy)SearchServiceApplicationProxy.GetProxy(SPServiceContext.Current);
if (ssaProxy.FASTAdminProxy != null)
{
    var fastProxy = ssaProxy.FASTAdminProxy;
    KeywordContext keywordContext = fastProxy.KeywordContext;
    var searchGroup = (from x in keywordContext.SearchSettingGroups where x.Name==SPContext.Current.Site.ID.ToString() select x).First();  
}

Once we have the search group, we need to get the keyword itself:

var getKeyWord = from x in searchGroup.Keywords where x.Term == searchKeyword select x;

Where searchKeyword is the term that was searched.

Keyword keyword = getKeyWord.ElementAt(0);

Now all we have to do is go through the collection of promoted items for that keyword, and look for a PromotedItem of type PromotedDocument:

foreach (var promo in keyword.Promotions)
{
foreach (var item in promo.PromotedItems)
{
if (item isPromotedDocument)
{
// your logic here
}
}
}

To get the URL for the promoted document use the following code:

(item asPromotedDocument).DocumentId

Once you have the url you can add it to your FQL query like

xrank(path:equals(“<DocumentId>”), boost=100000, boostall=yes)

I hope this article saves you a healthy dose of slamming your head against the wall while trying to figure out where the promoted documents urls are hiding!

Fast Tips for FAST for SharePoint #2

When trying to query for a document that has a question mark (?) in its name or address as part of a FQL query, you may notice weird behavior from FAST.

For example, let’s send a query that look for a document with the URL address of http://localhost/blogs.aspx?blogid=5:

string fqlQuery=@”and(path:(string(“”http://localhost/blogs.aspx?blogid=5″&#8221;, Mode=””PHRASE””)), filter(sitename:equals(“”Local SharePoint””)))”;

Sending the query as is, will cause FAST to automatically evaluate the question mark as a wildcard character that will match any single character. Since this is not our desired result, we need to tell FAST not to use wildcards in our specific string. This can be done easily using the wildcard parameter of string. Changing the query with this parameter will look as follows and will make sure you get the desired result:

string fqlQuery = @”and(path:(string(“”http://localhost/blogs.aspx?blogid=5″&#8221;, Mode=””PHRASE””, wildcard=off)), filter(sitename:equals(“”Local SharePoint””)))”;

Fast Tips for FAST for SharePoint #1

In the next couple of short articles I’m going to give some fast tips (pun intended) for developing with the FAST search engine for SharePoint 2010.

My first tip deals with setting Managed Metadata Properties for FAST without the use of PowerShell.

1) Start by going to your central admin->Manage service applications.

2) Click on your FAST Query SSA service:

3) Click on FAST search administration.

4) Click on Managed Properties.

5) Play with the properties as you wish 🙂

Working with SharePoint 2010 User Activity NewsFeed

I was recently tasked with creating an application that once used will add an entree to the user’s activity feed.

The activity feed is a fun and social feature (one of many) of SharePoint 2010, and its purpose is to present a feed of actions that the user or his colleagues performed on the site.

You can see how the activity feed looks on the following screen shot, taken from my local SharePoint development environment:

Now hopefully that got you excited enough to read on 😉

We are going to build a simple web part that will add an entree to the user activity feed. The web part will contain a single text box and a button that once pressed will add the entree to the user’s feed.

1) Open Visual Studio 2010, create a new Empty SharePoint Project with the name “ActivityFeedInteraction” and choose to deploy it as farm solution.

2) Right click on the solution name, choose “Add” and then “New Item”. Click on “Web Part” and name it “ActivityWebpart”

3) Right click on “References” and choose “Add Reference”. Add the following 3 dlls to your solution:

  • Microsoft.Office.Server
  • Microsoft.Office.Server.UserProfiles
  • System.Web

4) The activity feed is based upon activities our user chooses to follow on his “Newsfeed Settings” screen:

As we can see there are plenty of default activities that the user follows, and if we wish to tap in the user’s news feed we have to build a new activity for him to follow first. In our solution we will create that activity once the feature that installs the web part is activated.

In visual studio, expend the Features node, right click on “Feature1” and choose “Add Event Receiver” as seen on the following screen shot:

5) Open Feature1.EventReceiver.cs and uncomment the FeatureActivated method.

6) Add the following code to the method:

UserProfileManager pm = new UserProfileManager(SPServiceContext.Current);
UserProfile
currentUserProfile = pm.GetUserProfile(SPContext.Current.Site.OpenWeb().CurrentUser.LoginName);
ActivityManager actMgr = new ActivityManager(currentUserProfile);
ActivityType customActivityType = null;
ActivityApplication actApplication = null;
ActivityTemplate
customActTemplate = null;

Let’s explain what we did here: First we created a UserProfileManager object and passed the current SPService context to it, then we created a UserProfile object of the currently logged in user, then we created an ActivityManager object for the current user and finally we created an empty ActivityType, ActivityApplication and ActivityTemplate objects. We will use all of these later.

7) Add the following code right below the code you previously pasted:


if (actMgr.PrepareToAllowSchemaChanges())

{

}

else
{

SPDiagnosticsService
.Local.WriteTrace(0, new SPDiagnosticsCategory(“Custom Activity Creator”, TraceSeverity.High, EventSeverity.Error),
        TraceSeverity.High, string.Format(“The user {0} does not have the administrator rights on the User Profile service”,   
SPContext
.Current.Site.OpenWeb().CurrentUser.LoginName), “”);

throw new Exception(“The user dosent have the required permissions for this action”);
}

Before we go on with the creation of the activity we need to check if the user who install the feature have the required rights to create it. If he does – we go on with the creation, if not we write a message to SharePoint’s log and quit.

8) Now it’s time to add the code for creating the activity. First we create the activity application using the following code (paste it inside the if statement):

if (actMgr.ActivityApplications[“CustomActivity”] == null)

{
actApplication = actMgr.ActivityApplications.Create(“CustomActivity”);

actApplication.Commit();

actApplication.Refresh(false);

}

else

{

actApplication = actMgr.ActivityApplications[“CustomActivity”];

}

First we check if an activity application named CustomActivity exsist, if it doesn’t – we create it, if it does – we initialize our actApplication object to it.

9) Now that we have our ActivityApplication object ready, it’s time to move on to the ActivityType. Paste the following code right below the previous code blocks:

customActivityType = actApplication.ActivityTypes[“MyCustomActivity”];

if (customActivityType == null)
{

customActivityType = actApplication.ActivityTypes.Create(“MyCustomActivity”);

    customActivityType.ActivityTypeNameLocStringResourceFile = “CustomActivityResource”;
customActivityType.ActivityTypeNameLocStringName = “ActivityName”;

    customActivityType.IsPublished = true;
customActivityType.IsConsolidated = true;

customActivityType.AllowRollup = true;

customActivityType.Commit();

customActivityType.Refresh(false);

}

Like before, we first try to get our activity type from the ActivityApplication activity types collection, if it comes back null that means it doesn’t exists and we move on to creating it.
There are 2 lines of code in this block that we need to draw attention to:

customActivityType.ActivityTypeNameLocStringResourceFile = “CustomActivityResource”;

customActivityType.ActivityTypeNameLocStringName = “ActivityName”;

The first lines set the activity type resource file name and the second set the name inside the resource file for the activity name.

You might be wondering what is this resource file we are talking about, so fear not! We are going to create it next.

10) Right click on the solution name and choose “Add” and then “SharePoint Mapped Folders”. You should see the following dialog box:

Click on “Resources” and then OK.

11) Right click on the newly created “Resources” node in our solution and choose “Add” -> “New Item”. Find “Resource File” in the list of available templates and name it CustomActivityResource (this is how we set ActivityTypeNameLocStringResourceFile in the previous step).

12) Once the file is created, you will automatically be brought to the file editing screen. Replace “String1” with “ActivityName” (You’ve probably guessed that’s the same name as ActivityTypeNameLocStringName setting from the previous step) and give it a value of “My New Sweet Custom Activity!” This is the name that will be shown on the newsfeed settings screen we saw in step 4. You can add additional resource files in different languages and depending on the user’s language settings SharePoint will use the appropriate resource file for the user’s language.

13) We are now done with the ActivityType object and we are moving on to creating the last object in our activity – the template. Paste the following code right below the closing bracket for the last if statement:

customActTemplate = customActivityType.ActivityTemplates[ActivityTemplatesCollection.CreateKey(false)];

if (customActTemplate == null)
{

customActTemplate = customActivityType.ActivityTemplates.Create(false);

customActTemplate.TitleFormatLocStringResourceFile = “CustomActivityResource”;

customActTemplate.TitleFormatLocStringName = “Activity_Created”;
customActTemplate.Commit();
customActTemplate.Refresh(false);

}

Just like before, we check if the template we need already exists, if it doesn’t we create it. We then need to provide a resource file that holds a string that will act as a template for the user’s news feed. (In our code this template is called Activity_Created).

14) Open the resource file we previously created (CustomActivityResource) and add a new row with the following info:

Name:
Activity_Created
Value: {Publisher} wrote {Value} on the wall using a custom activity!

Think of the value text as the template for the activity. This template is how the activity will show on the user’s newsfeed. The template can use template variables like {Publisher} and {Value} that will be replaced by data we provide to the activity at run time. Other variables like {Link} or {Size} can also be used if needed.

15) That’s it! We are done with creating the activity! At this point, your method should look like this:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{

UserProfileManager
pm = new UserProfileManager(SPServiceContext.Current);
    UserProfile currentUserProfile = pm.GetUserProfile(SPContext.Current.Site.OpenWeb().CurrentUser.LoginName);
ActivityManager
actMgr = new ActivityManager(currentUserProfile);
ActivityType customActivityType = null;
ActivityApplication
actApplication = null;

ActivityTemplate
customActTemplate = null;

if(actMgr.PrepareToAllowSchemaChanges())
{
if
(actMgr.ActivityApplications[“CustomActivity”] == null)
{
actApplication = actMgr.ActivityApplications.Create(“CustomActivity”);
actApplication.Commit();
actApplication.Refresh(false);
}
else

{
actApplication = actMgr.ActivityApplications[“CustomActivity”];
}

        customActivityType = actApplication.ActivityTypes[“MyCustomActivity”];


if (customActivityType == null)

{

customActivityType = actApplication.ActivityTypes.Create(“MyCustomActivity”);

customActivityType.ActivityTypeNameLocStringName = “ActivityName”;

customActivityType.ActivityTypeNameLocStringResourceFile = “CustomActivityResource”;

customActivityType.IsPublished = true;

customActivityType.IsConsolidated = true;

customActivityType.AllowRollup = true;

customActivityType.Commit();

customActivityType.Refresh(false);

}

customActTemplate = customActivityType.ActivityTemplates[ActivityTemplatesCollection.CreateKey(false)];

if(customActTemplate == null)
{
customActTemplate = customActivityType.ActivityTemplates.Create(false);
customActTemplate.TitleFormatLocStringResourceFile = “CustomActivityResource”;
customActTemplate.TitleFormatLocStringName = “Activity_Created”;
customActTemplate.Commit();
customActTemplate.Refresh(false);
}
}
else

{
SPDiagnosticsService
.Local.WriteTrace(0, new SPDiagnosticsCategory(“Custom Activity Creator”, TraceSeverity.High, EventSeverity.Error),            
TraceSeverity
.High, string.Format(“The user {0} does not have the administrator rights on the User Profile service”,
SPContext.Current.Site.OpenWeb().CurrentUser.LoginName), “”);

        throw new Exception(“The user dosent have the required permissions for this action”);
}
}

16) Now that we have an activity let’s make use of it! Open “ActivityWebPart” node and double click “ActivityWebpart.cs”.

17) Add the following private variables:

private TextBox _tbActivity;

private Label _lblActivity,_lblInfo;
private Button _btnActivity;

 18) Add the following code to the CreateChildControl method:

_lblActivity = new Label() { Text = “Activity Text “ };

this.Controls.Add(_lblActivity);
_tbActivity = new TextBox();

this
.Controls.Add(_tbActivity);

this
.Controls.Add(new Literal() { Text = “<br/>” });

_btnActivity = new Button();

_btnActivity.Text = “Send Activity”;
_btnActivity.Click += new EventHandler(_btnActivity_Click);
this
.Controls.Add(_btnActivity);

this
.Controls.Add(new Literal() { Text = “<br/>” });

_lblInfo = new Label() { Visible=false };

this
.Controls.Add(_lblInfo);

19) Add the following method below CreateChildControl:

void _btnActivity_Click(object sender, EventArgs e)

{
UserProfileManager
pm = new UserProfileManager(SPServiceContext.GetContext(SPContext.Current.Site));

UserProfile
currentUserProfile = pm.GetUserProfile(SPContext.Current.Web.CurrentUser.LoginName);

ActivityManager
actMgr = new ActivityManager(currentUserProfile);

try
{

long
aId = actMgr.ActivityApplications[“CustomActivity”].ActivityTypes[“MyCustomActivity”].ActivityTypeId;

        if (aId != 0)
{

            CreateEvent(_tbActivity.Text, aId, currentUserProfile, actMgr);
_lblInfo.Text = “Activity created successfully!”;

}

else

_lblInfo.Text = “No Activity Found!”;

_lblInfo.Visible = true;

    }
    catch (Exception ex)
{

SPDiagnosticsService
.Local.WriteTrace(0, new SPDiagnosticsCategory(“Custom Activity Creator”, TraceSeverity.High, EventSeverity.Error), TraceSeverity.High, ex.Message,ex.StackTrace);

    }

}

Once the button is clicked the method gets the current user profile from the UserProfileManager object, and then gets the activity id from the activity we have created in the feature activated method. If we get an id we move on to the CreateEvent method which we will build in the next step, but if no id is returned the method changes the text on the info label to “No Activity Found” and quits.

20) Now let’s add the final peace of the puzzle: the CreateEvent method. Add the following code right below the closing bracket for _btnActivity_Click method

private void CreateEvent(string text, long aId, UserProfile currentUserProfile, ActivityManager actMgr)

{
Entity
publisher = new MinimalPerson(currentUserProfile).CreateEntity(actMgr);

ActivityEvent
activityEvent = ActivityEvent.CreateActivityEvent(actMgr, aId, publisher, publisher);

activityEvent.Name = “MyCustomActivity”;

activityEvent.ItemPrivacy = (int)Privacy.Public;

activityEvent.Owner = publisher;

activityEvent.Publisher = publisher;

activityEvent.Value = text;

activityEvent.Commit();

}

This method sets the publisher for the method (the person who initiated it), then creates an ActivityEvent object which will hold all the information about the event, setting its privacy, owner and publisher (in our example the owner and publisher are the same user, but if you want to publish events to other users, such as the current user colleagues – you will set different users in these properties) and finally we set the value property (which is what the {Value} template placeholder will use for rendering). Once we execute the Commit method, the event will show in the newsfeed!

21) Time to test our solution. Click on the solution name and in the properties window change “Active Deployment” to “No Activation”. Right click on the solution name and click Deploy.

22) Go to the site you deployed the solution to; click “Site Actions” and “Site Settings”. Click on “Site Collection Features” and activate “ActivityFeedInteraction Feature1”

23) Go to your “My Site” and click “Newsfeed Settings”. You should see our new activity is there and waiting for action!

24) Go back to your site, and add the “ActivityWebPart” web part to a page of your choice.

25) Type something in the text box and click “Send Activity”.

26) If all went well, you should see on your “My Profile” page a new entree for your activity which was based on the template we supplied earlier! The following screenshot shows an example:

That’s it! We have created a new custom activity and a web part that accompany it in order to add events to a user’s newsfeed.

I hope this post helped you understand how to use the activity feed, one of the best social features of SharePoint 2010.

If you want to download the final project then go right ahead and click here.