Tuesday, July 17, 2012

SharePoint: Showing Last Published Version to Authors

Quick 5 minute post! Hope it helps!

Recently, our content authors wanted to see the most recent published version without going to Version History. Unfortunately, they hate to use SharePoint’s Version History and let me mention – they hate the ribbon too Sad smile.

Anyways, they were okay with a button put in the side bar of the page which can only be displayed to Authors. And they wanted to view the page in a new window so that they can compare with draft version (thank god, they were okay with comparing the content manually Smile)

Rather than accessing the versions on server side, we decided to access the version information only when a button is clicked. Once again the wonderful Client Side Object model saved a lot of work for us. The script on click of button would look this (and that’s it for today)



this.ctx = SP.ClientContext.get_current();
this.page = ctx.get_web().getFileByServerRelativeUrl(window.location.pathname);
ctx.load(page);

ctx.executeQueryAsync(Function.createDelegate(
this, this.ShowVersion), Function.createDelegate(this, this.ShowError));
function ShowVersion() {
var versionID = page.get_uiVersion();
if (versionID < 512)
SP.UI.Notify.addNotification(
'This page was never published.', false);
else if (versionID % 512 == 0)
SP.UI.Notify.addNotification(
'You are currently viewing Published Version.', false)
else{
window.open(window.location
+ '?PageVersion=' + 512*(versionID % 512), '_blank')
}

}
function ShowError() {
SP.UI.Notify.addNotification(
'Unable to perform the action. Please report the issue.', false)
}

Saturday, January 21, 2012

Where is my Rating Feature?

Many of you know new new Rating FEAURE of SharePoint 2010. There are many resources out there to learn about using Rating Feature but here, I would talk about what it takes to make the Rating Feature available to you.

I will not waste time, ALL of following must be TRUE for rating feature to be available for your lists:

1. You must be using SharePoint Server product. According to Compare SharePoint Editions, Rating is NOT available to SharePoint Foundation that comes free with Windows Server 2008.

2. You must have configured User Profile Service in Central Administration.

3. The User Profile Service must be associated with your Web Application

Note: Step 2 and 3 are described here (and many other places, I am sure)

http://dxjo.net/dxit/2011/09/activate-and-enable-rating-in-sharepoint-2010/

4. This is perhaps not documented at many places, but you must NOT have created your site from a Blank site template. If yes, you are out of luck. But wait… there is one last chance, if you started with blank site template and you are okay with enabling Publishing Features at site collection level, you can still make it available! 

DISCLAIMER: I do not recommend and I have not tested the side effects of enabling publishing feature on sites created from blank site template. Personally, I would try to avoid it!

Alternatively, you can just try enabling the hidden rating FEATURE by using following PowerShell command. When I tested following, I was able to enable rating on announcement rating and save my rating, the average rating was also calculated depending upon the scheduled jobs. I did not test further:

Enable-SPFeature 915c240e-a6cc-49b8-8b2c-0bff8b553ed3 –URL http://yoururl.com

Also note that SharePoint staples the Rating FEATURE to following Site Templates:




<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="STS#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="STS#2" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="MPS#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="MPS#1" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="MPS#2" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="MPS#3" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="MPS#4" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="WIKI#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="BLOG#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SGS#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="BDR#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="OFFILE#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="OFFILE#1" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="PWA#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="PWS#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPS#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSMSITE#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSTOC#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSTOPIC#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSNEWS#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSNHOME#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSSITES#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSBWEB#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSCOMMU#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSREPORTCENTER#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSPORTAL#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SRCHCEN#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="PROFILES#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="CMSPUBLISHING#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSPORTAL#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPS#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSREPORTCENTER#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSTOC#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSTOPIC#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSNEWS#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSNHOME#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSSITES#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SRCHCEN#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="CMSPUBLISHING#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="BLANKINTERNET#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="BLANKINTERNET#1" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="BLANKINTERNET#2" />


The Rating FEATURE is hidden and Publishing Feature depends on it and that’s the reason it becomes available to you when you activate Publishing Feature.

Thursday, January 12, 2012

Sending Emails to SharePoint Group

Recently, I came across a situation where I had to include SharePoint group in To field of “Send Email” activity in a reusable workflow designed in SharePoint Designer. I just included it thinking that it would work (just like hundreds of other developers I guess) but I was wrong. Well, that was my last activity in the workflow. After it was deployed, it would NOT send emails to members of the SharePoint Group and worse than that, it would not show any error, the workflow appears completed but it does NOT terminate itself but the page gets published. As a result, content authors cannot start a new workflow because previous workflow was not terminated even though item was published. SharePoint not sending emails is understood but I would be happier it it logged something in the workflow history or terminated the workflow. It took me many change/deploy cycles before I could figure it out.

I posted a question on here: Definitive answer to sending emails to SP Group from Workflow and on MSDN forum too, but did not get an answer for couple of days and then thought about figuring out solution that works for me (yes, there could be other solutions out there).

Solution and Assumptions: It was safe to assume in my situation that SharePoint users will directly be added to the group to which I need to send emails.

So the simplest solution was to get develop a custom activity which can be consumed by SharePoint designer. The activity outputs list of emails given the SharePoint group or individual’s name. The designer can take the output and store it in Workflow variable. And finally, the variable is used in “To” field of the Send Email Activity.

So following solution needs to be revisited (or tested) if the SharePoint group includes other groups or active directory groups.

Code

Here is my activity looks like:

using System;
using System.ComponentModel;
using System.Linq;
using System.Workflow.ComponentModel;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;
using Microsoft.SharePoint.WorkflowActions;
namespace MyProduct.Workflows.Activities
{
public class ExpandSharePointGroup : Activity
{
public static DependencyProperty __ContextProperty = System.Workflow.ComponentModel.DependencyProperty.Register("__Context",
typeof(WorkflowContext), typeof(ExpandSharePointGroup));

[Description(
"Context")]
[Browsable(
true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public WorkflowContext __Context
{
get
{
return ((WorkflowContext)(base.GetValue(__ContextProperty)));
}
set
{
base.SetValue(__ContextProperty, value);
}
}


public static DependencyProperty GroupNameProperty = DependencyProperty.Register("GroupName",
typeof(string), typeof(ExpandSharePointGroup));

[Description(
"SharePoint Group Name")]
[Category(
"MyProduct Workflow Activities")]
[Browsable(
true)]
[DesignerSerializationVisibility
(DesignerSerializationVisibility.Visible)]
public string GroupName
{
get
{
return ((string)
(
base.GetValue(GroupNameProperty)));
}
set
{
base.SetValue(GroupNameProperty, value);
}
}

public static DependencyProperty FieldValueProperty = DependencyProperty.Register("FieldValue",
typeof(string), typeof(ExpandSharePointGroup));

[Description(
"Field Value")]
[Category(
"MyProduct Workflow Activities")]
[Browsable(
true)]
[DesignerSerializationVisibility
(DesignerSerializationVisibility.Visible)]
public string FieldValue
{
get
{
return ((string)
(
base.GetValue(FieldValueProperty)));
}
set
{
base.SetValue(FieldValueProperty, value);
}
}



protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{

SPWeb web
= (SPWeb)__Context.Web;

try
{
if (string.IsNullOrEmpty(GroupName))
{

return ActivityExecutionStatus.Closed;
}

string strOutput = string.Empty;
strOutput
= Helper.ResolveToEmailName(__Context, GroupName);
FieldValue
= strOutput;
}
catch (Exception ex)
{
// TODO: Log error here
return ActivityExecutionStatus.Faulting;
}


return ActivityExecutionStatus.Closed;
}

}

}

In order to make sure that above activity is available to SharePoint designer, we need to deploy a .Actions file to {SharePointRoot}\Template\1033\Workflow which is shown below:


<?xml version="1.0" encoding="utf-8"?>
<WorkflowInfo Language="en-us">
<Actions>
<Action Name="Expand SharePoint Group Members"
ClassName
="FULL CLASS NAME"
Assembly
="FULL ASSEMBLY NAME"
AppliesTo
="all"
Category
="My Custom Workflow Activities">
<RuleDesigner Sentence="Expand Email for %1 it in: %2">
<FieldBind Field="GroupName" Text="SharePoint Group" DesignerType="TextBox" Id="1"/>
<FieldBind Field="FieldValue" Text="Field Value" DesignerType="ParameterNames" Id="2"/>
</RuleDesigner>
<Parameters>
<Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext" Direction="In" DesignerType="Hide" />
<Parameter Name="GroupName" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="FieldValue" Type="System.String, mscorlib" Direction="Out" />
</Parameters>
</Action>
</Actions>
</WorkflowInfo>

Finally, you need to add a web.config entry under authorizedTypes section:



<authorizedType Assembly="Full Assembly Name" Namespace="Your Name space" TypeName="Class Name or *" Authorized="True" />


Note: After your assembly is deployed in GAC, Actions file is deployed on SharePoint root and web.config entry is added (as shown above), you need to close the SharePoint Designer and open it again before you can see your activity in list of available actions.


Hope this helps!

Tuesday, December 13, 2011

Expand-collapsible FAQ for your Publishing Pages

I have come across an expand-collapsible FAQs requirement as part of publishing pages a few times. While there are many ways to achieve this including a custom field or custom control/web part accessing external list, in my most recent project, I thought about and implemented a much simplified solution. The solution looks like:

1. Have a FAQ field of type: “Full HTML with Formatting and Constraints of Publishing”

2. Provide custom two custom styles to choose from: Question and Answer

3. In display mode, hide the Answers using jQuery when the page loads

4. In display mode, attach click event on question to toggle the answer

5. And finally, provide required training to your content authors so that they can author FAQs on publishing pages.

In following sections, I will expand the steps above and provide necessary technical details.

I am assuming that you have a column of type “Full HTML with Formatting and Constraints of Publishing” added to your Publishing Page Content Type and corresponding Page Layout has been provisioned to Master Page Gallery.

We need two CSS files provisioned (preferably) in “Style Library”.

FAQDisply.css

SNAGHTML2b1380

FAQEdit.css


SNAGHTML2c3c64 


Note: Feel free to use different styles but make sure that you follow the naming convention of styles as required by the Rich HTML editor, failing to do so will NOT make the styles available to you in the editor toolbar.


Also make sure that in your page layout, above CSS files are included in “PlaceHolderAdditionalPageHead” content placeholder. FAQEdit should be included only in edit mode and it should be referred after FAQDsply.css file as shown below:


image


Now, let’s look at the required jQuery code (assuming jQuery library is already plugged in your Page Layout/Master Page). The code is commented below for an understanding:


SNAGHTML3a3ed4





Once above infrastructure is in place, go ahead and create a new publishing page using your Page Layout. If everything is working fine, you should see Two styles available when you edit the rich HTML field as shown below:


SNAGHTML416671


Typing Question/Answer and choosing appropriate styles may look like below. Note that answer is not hidden in Edit mode because 1) our Jquery does not execute in edit mode and 2) In edit mode, we are making sure that the DIV tag containing the answer has display=block style.


SNAGHTML49787a


When you view the page in Display Mode, the answer will not be displayed until you click on the answer.


Hope this helps you implement similar requirements…

Thursday, September 22, 2011

Quickly Testing if JQuery is referenced properly on your page

I have observed a lots of developers quickly test the jQuery or SPServices references by putting a script like and make sure it works:

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

$(document).ready(function() {
alert("jQuery");
alert($().SPServices.SPGetCurrentSite());
});
</script>

While this approach works, it requires to put the actual script on the page, only to be removed later.

There is a better way to check it instead of actually writing script on the page. Thanks to the IE Developer Toolbar. Here are the quick steps:

1. Reference your jQuery (and SPServices) in your page

2. Open your page in Internet Explorer on which IE Developer toolbar is installed. Note that you have IE 8 and above, it comes with it by default

3. Hit F12 to activate it the IE Developer Toolbar

4. Click on Script Tab, the beauty of IE Developer toolbar is that it allows you to run Ad-hoc JavaScript on your page as shown below:

SNAGHTMLaadd08

To make sure jQuery is referenced properly and it is functional on your page is to type a simple jQuery function like alert($(‘title’).html()). If it displays browser title like shown below, you are all set with the wonder of jQuery!

image

Many developers use SPServices library for some wonderful enhancements to the Forms (and for everything else it provides, of course). To test integration of SPServices (and jQuery as well because SPServices depend on jQuery), you may use similar approach but a different function to test and here it is:

alert($().SPServices.SPGetCurrentSite());

Hope this helps!

Wednesday, September 7, 2011

The Extensible and Reusable FEATURE Framework

As we all know there are two methods to develop/deploy various artifacts using SharePoint FEATURES : Declaratively using Feature.xml and element files and Programmatically.

Most examples that use programming approach either directly writes code in Feature Activated, Feature Deactivating etc. or use helper methods to promote the reusability of the code. While the second approach ensures reusability, it is not extensible, meaning, you must change code to change the actions that you want to perform in the Feature Receiver class. In my recent SharePoint implementation, I came across the need for extensibility and ended up writing a mini framework for the same.

Following are the elements of the framework and how they are connected to each other:

1. IFeatureAction class which has one method with following signature:

void Execute(object target, XmlElement actionData)

2. A Class named FeatureActionReceiver which is derived from SPFeatureReceiver. Overrides FeatureActivated and FeatureDeactivating methods.

3. The overriden FeatureActivated method looks for FeatureActivated.xml file under {Feature Root}\Actions directory and processes the file (more on processing later). Similarly, FeatureDeactivating looks for FeatureDeactivating.xml file under {Feature Root}\Actions directory

4. The Actions file (FeatureActivated.xml or FeatureDeactivating.xml) contains one of more Actions in following format:

<Actions>

<Action ActionName=”” Class=”” Assembly=””>

<AnyValidXML>

</Action>

<Action ActionName=”” Class=”” Assembly=””>

</Action>

</Actions>

4. Action Processing: the framework class reads the action one after another and for each action, it loads the specified Assembly and Specified class which must implement IFeatureAction class. After creating instance of the Action, it gives an opportunity of providing the target object (on which action needs to be performed) to the parent FeatureReceiver class by calling a virtual method “ProvideTargetObject”. The framework passes action name so that the feature’s receiver class can write a switch statement and provide appropriate target object to the framework. If no object is provided by Feature’s Receiver class, by default, framework will pass Feature’s Parent (depending on scope of feature) to the Execute method of the Action.

5. Now your Feature’s Receiver class must derive from FeatureActionReceiver  instead of SPFeatureReceiver and override FeatureActivated and FeatureDeactivating methods. Here, the Feature’s receiver must call Base.FeatureActivated and Base.FeatureDeactivating methods so that framework can process those actions. Apart from that, as usual other code can be written in the Feature Receiver.

Now, everything that I want to do in Feature Receiver is externalized in a set of Action Classes and those Action classes must get the parameters from XML file (the parameters varies from one project to another) while performing the actions. So that’s about reusability. In addition, performing a new action means writing a new action (if existing action does not satisfy the need, of course) and updating those XML files so that the action will be executed in FeatureActivated or FeatureDeactivating. That’s extensible!!

To give you a few examples, I developed following actions:

1. Create List – It would create list instance, add content types, set other properties, set the views and add fields to the view

2. Create Terms – Accepts a set of Term Groups, Term Sets and a hierarchical Terms data. This will ensure that all terms and term sets required by your application are provisioned using Features

3. Set Master Page – Sets the specified master page for the specified web

4. Create Web – creates the webs using specified Title and Web template

5. Add Feature – Activates a Feature. In my case, it was required to activate some features after creating the web so that my content authors do not need to perform the action

.. the list goes on and on..

I hope all dots are connected. Unfortunately, I cannot share the code here but let me know if you need more information about it.

Wednesday, July 13, 2011

Know CAML Queries executed by SharePoint server–CAML Profiling

Did you ever want to know what CAML queries are executed by SharePoint Server?

Well, for troubleshooting and learning purpose, it is not a bad idea. After all, SQL Profiler has been helping us troubleshoot a lot of issues.

There may be products out there but I figured out a way to do it without spending extra bucks! And here it is…

Following facts helped me think about the solution:

1) SharePoint’s code does a very good job at logging everything it does and everything it encounters (if you are lucky enough to figure it out). The challenge is to figure out under what area and category CAML queries are logged (if at all being logged). Once this is figured out, watching it real time is easy, see next step

2) The ULS Viewer is the tool which allows you to filter the events that you want to watch and watch them real time as they are logged to ULS logs by SharePoint code.

Follow the steps below to see it in action:

1. Go to Central Administration-> Monitoring –> Configure Diagnostic Logging.

Expand the SharePoint Foundation Area and check Monitoring and select Verbose level (selecting anything else would not work)

SNAGHTML924829

image

2. I did IIS Reset after changing the setting. Not sure if that is required though.

3. Download and run ULS Viewer. Select ULS Logs as your data source as shown below:

SNAGHTML98d1a3

4. Now modify the Filter (Access it from Edit –> Modify Filter) to filter messages that contains the CAML word as shown below:

image

And that should be it! Go to any Lists and select a view, since views use CAML behind the scene, you should see and entry and watch the CAML that was executed.