SharePoint 2013 People & Groups field as a dropdown with predefined choices

If you are in the SharePoint consulting business, by no doubt you have already heard the following customer requirement:

We need a SharePoint field, which allows the user to pick one of the few predefined groups we have defined there

That’s not possible out of the box, although possible with some custom development. There is many different options here, but the one I recommend and I find it to be a ‘best practice’, is the Field JSLink approach.

Below I will show you how to implement a JSLink, which changes the People & Groups field from this:

1

to this:

2

Ok, first thing first, you probably already have somewhere in your project the definition of the Field. Something like:


You need to add JSLink attribute to it. So:

JSLink="~site/SiteAssets/jquery-1.11.3.min.js|~site/SiteAssets/JSLink/MyCompany.JSLink.Fields.GroupsAsDropdownTest.js" />

You will find the full source code of the JSLink at the bottom of the post, but let’s quickly go over it.

First, we have the standard

SPClientTemplates.TemplateManager.RegisterTemplateOverrides(FieldCtx);

which tells SharePoint to use our custom JavaScript functions to render the field in the different display modes (Edit, New, etc).

Let’s review the function which renders the field in New/Edit mode, as it is the most interesting one. What it basically does is, render the field as SharePoint would render it OOB:

var html = jQuery(SPClientPeoplePickerCSRTemplate(ctx));
html.hide();
 
return html.outerHTML() + dropdownHTML;

but hide it, and next to it render our custom dropdown list, which is driven by the configuration specified in

Fields_Renderer.DropdownOptions

Important note: the groups listed in this structure should be valid ones!

Whenever the value in the custom dropdown list changes, we run the following function:

Fields_Renderer.SelectionChange

which uses the standard SharePoint people picker functionality to clear the current value in the hidden OOB people field and set a new one (defined as value attribute in the dropdown option).

In summary:

We use JSLink on the People & Groups field to hide the standard field control and render a custom dropdown next to it. Whenever the dropdown changes, it updates the value in the hidden OOB field. When the user presses ‘Save’ on the form, SharePoint persists the user field as it normally would.

Here is the source code:

https://github.com/hyankov/PeopleAndGroupsAsDropdown

Happy coding!

Advertisements

How to override the NewDocSet.aspx redirect

If you are opening the NewDocSet.aspx page in a dialog, similarly to this:

options.url = L_Menu_BaseUrl + "/_layouts/15/NewDocSet.aspx?List=" + {listGuid} + "&ContentTypeId=" + {CT} + "&RootFolder=" + L_Menu_BaseUrl + "/Lists/YourList";
options.title = "New Document Set";
options.autoSize = true; 
// Show dialog
SP.SOD.execute('sp.ui.dialog.js', 'SP.UI.ModalDialog.showModalDialog', options);

you may have noticed that after creation of the docset, the dialog redirects your page to the welcome page of the newly created doc set. If you are opening this dialog from some kind of a dashboard, this is usually an undesired behavior.

I have taken a look at the NewDocSet.aspx backend with ILSpy and I have determined that when the new Document Set is provisioned, the code end with a calls to this function – ‘window.frameElement.navigateParent’. Turns out this is a function defined by SharePoint, when you open a dialog. It basically redirects the parent window to a new URL. And NewDocSet.aspx uses this function to redirect the window to the Welcome page of the new doc set.

So, to override this behavior, we simply override the dialog’s window frame function (make sure you execute this AFTER the dialog is opened):

// Override the redirect behavior of NewDocSet.aspx
for (var i = 0; i < frames.length; i++) {
    // Find the frame of the dialog
    var frmWindow = frames[i];
    if (frames[i].location.href.indexOf(‘/_layouts/15/NewDocSet.aspx’) > -1) {
        // Override the function
        var prxy = frmWindow.frameElement.navigateParent;
        frmWindow.frameElement.navigateParent = function () {
            // Disregard the original function’s URL, refresh the page we are currently on
            prxy.apply(this, [window.location.href]);
        };
    }
}

The patter is called proxy pattern, by the way.  This code will refresh the page that opened the NewDocSet.aspx dialog, instead of redirecting to the doc set’s welcome page.

Good luck (with SharePoint, you need it).

SharePoint 2013 Refiner Multi-Value (‘Contains’ instead of an ‘Equals’)

You are in a situation where your Managed Property contains multiple values (e.g. you programmatically populate a Property Bag, which is added to the IndexedPropertyKeys and then exposed as a Managed Property). You add a Content Search Web Part on your page and also a refiner. In the Refiner panel you add your managed property and you are horrified by the following end result:

1

If you are reading this page you already know that such rendering causes a problem – the entries within the multi-value field are not ‘normalized’ to single entries, hence, you now can’t search only for items that have a property which contains only (e.g.) one of the values. You have already probably tried setting the ‘multi-value’ property of the Managed Property, but alas, no success.

Fear not, for I have a solution! It is delivered as a custom Refiner filter, which is based on the Filter_MultiValue.html OOB Refiner filter. Here it is: click here.

How to use it? Simple:

1. Drop the file into your Filters folder (Site Collection -> Settings -> Master pages and page layouts -> Display Templates -> Filters)

2. Make sure you Save, Check In and Publish the file

3. Go to your refiner and edit the web part properties -> Choose refiners

4. Select the newly added Filter

2

5. Ok -> Save page

And the result now is:

3

Much better! Selecting a refiner value and applying actually works too 🙂

4

For the curious, how does the custom Filter work? Well:

1. We set the var useContains = true; Although in my experience it has no effect on the end result

2. More importantly, we add one more for(;;) cycle in the code, which splits every filter item into separate filters:

5

So, instead of having:

[ ] Value 1, Value 2, Value 3, …

you will have:

[ ] Value 1

[ ] Value 2

[ ] Value 3

3. But MOST IMPORTANTLY, instead of hex-ing the refiner token, as the OOB filter does, we use the plain-text value:

6

This is crucial! Without this part the refiner will not work as expected.

So let’s compare an original query URL generated by the OOB Filter_MultiValue.html with the custom Filter_Contains_MultiValue.html.

Before:

#Default=
{
“k”:””,
“r”:[{
“n”:”HahnAirPMOProjectManagerDisplayValue”,
“t”:[“\”ǂǂ4c41425c41646d696e6973747261746f72\””],
“o”:”OR”,
“k”:false,
“m”:{“\”ǂǂ4c41425c41646d696e6973747261746f72\””:”LAB\\Administrator”}}]}

After:

Default=
{
“k”:””,
“r”:[{
“n”:”HahnAirPMOProjectManagerDisplayValue”,
“t”:[“LAB\\Administrator”],
“o”:”OR”,
“k”:false,
“m”:{“LAB\\Administrator”:”LAB\\Administrator“}
}]
}

That specific part seems to cause SharePoint to do ‘contains’ search, instead of an ‘equals’ search.

Hope this helps!

PS

Don’t forget to set your ‘delimiter’ variable to whatever string you use for separating the values within the field. It is currently set to ‘, ‘ (coma and space).

Update 1:

I have updated the source code to handle strings with backslash in them differently. i.e.:

– If the string has a backslash (e.g. ‘domain\admin’) – don’t put it withing brackets

– if the string has no backslash (e.g. ‘Farm Admin’) – put it in brackets, otherwise the KQL is invalid and causes an error

SharePoint document library list view webpart drag and drop

The following article assumes that you know how to add programmatically list view web parts on your page.

You might find yourself being in a situation in which you are programmatically adding a List View Webpart pointing to a document library on your site and you wonder why:

  • ‘add document’ link does not show
  • drag & drop does not show up or does not work

Basically, you have ended up with something along the lines of:

drag drop not working

First, and foremost, you have to change your code and instead of adding ListViewWebPart, you have to add XsltListViewWebPart (source).

var webPartToAdd = new XsltListViewWebPart
                        {
                            ChromeState = PartChromeState.Normal,
                            ChromeType = PartChromeType.None,
                            Title = "Documents List View",
                            ListName = web.Lists["Documents"].ID.ToString("B").ToUpper(),
                            XmlDefinition = web.Lists["Documents"].DefaultView.GetViewXml()
                        }

This will get you to the point where you see the drag&drop functionality (in IE only, I don’t see it in Firefox):

drop here

However, when you drop the file, you might get the error:

exception

The reason for this error is the fact that sp.js and sp.core.js are not registered on the page (SP.Utilities and SP.Utilities.CommandBlock are undefined)! I think this is a bug in SharePoint, because if you simply click Edit on the page and then save it (without changing anything), the web part will start working (because the scripts will now be registered on the page).

So, how do we overcome this issue? Well, I have determined that if we execute the following script on the page, the drag&drop in the web part will work fine:

SP.SOD.executeFunc('sp.js', 'SP.Utilities.Utility', function() { console.log('sp.js loaded'); });
SP.SOD.executeFunc('sp.core.js', 'SP.Utilities.CommandBlock', function() { console.log('sp.core.js loaded'); });

This code will include the scripts that SharePoint ‘forgets’ to include. You have variety of options how to register those scripts on the page. I have decided to include them as a content of a Content Editor WebPart which I provision on the same page where I provision the (xslt) list view web part:

// add script registration work-around (there is a bug in the framework, which does not register those scripts until we edit the page).
var xmlDoc = new XmlDocument();
var xmlElement = xmlDoc.CreateElement(“HtmlContent”);
xmlElement.InnerText = “<script type=’text/javascript’>SP.SOD.executeFunc(‘sp.js’, ‘SP.Utilities.Utility’, function() { console.log(‘sp.js loaded’); }); SP.SOD.executeFunc(‘sp.core.js’, ‘SP.Utilities.CommandBlock’, function() { console.log(‘sp.core.js loaded’); })</script>”;
var cewp = new ContentEditorWebPart
{
ChromeState = PartChromeState.Normal,
ChromeType = PartChromeType.None,
Title = “Drag and drop script registration workaround”,
Content = xmlElement
};

And then, viola!, the drag&drop works:

viola

 

I hope this helps,

Hristo

When using [TaxonomyField].SetFieldValue you get ‘set AllowUnsafeUpdates to true’ exception

In some situations (such as ajax requests and other non-postback methodologies), you might encounter the following error:

Updates are currently disallowed on GET requests.  To allow updates on a GET, set the ‘AllowUnsafeUpdates’ property on SPWeb

This might happen if you are trying to set a Term type of object in an SPList item’s field. e.g.:

SPListItem myItem = … ;

Term myTerm = … ;

var taxonomyField = myItem[“MyTaxonomyField”] as TaxonomyField;

taxonomyField.SetFieldValue(item, term);

myItem.Update();

If you execute this in the backend during an ajax request (or maybe some other use cases too) this will fail with the exception mentioned above.

If you are reading this article it means that  you have already set the SPWeb’s AllowUnsafeUpdates to true and the problem still persists. For some reason this is the default SharePoint behavior and it doesn’t matter how many times you set the AllowUnsafeUpdates to true, it will still be ignored. So, we need another solution.

You might be tempted to use a solution (1, 2) such as setting a TaxonomyFieldValue to the field, instead of Term directly. Such as:

string termString = String.Concat(thisYear.GetDefaultLabel(1033), TaxonomyField.TaxonomyGuidLabelDelimiter, thisYear.Id);

TaxonomyFieldValue tagValue = new TaxonomyFieldValue(string.Empty);
tagValue.PopulateFromLabelGuidPair(termString);
oField.SetFieldValue(i, tagValue);

 

That would be a mistake because such approach breaks the Search Crawl (i.e. the crawler won’t crawl your field properly). So what is the correct solution? As crazy as it might sound, it is to set the HttpContext.Current to null just prior to setting the term field and then restoring it back.

So, solution:

var httpContext = HttpContext.Current;

HttpContext.Current = null;

SPListItem myItem = … ;

Term myTerm = … ;

var taxonomyField = myItem[“MyTaxonomyField”] as TaxonomyField;

taxonomyField.SetFieldValue(item, term);

myItem.Update();

HttpContext.Current = httpContext;

 

What does this achieve? It lies to SharePoint that you don’t have a HttpContext (i.e. you are not triggering this code from the browser), so it won’t validate your context and won’t throw an exception about AllowUnsafeUpdates.

 

Best Regards,

Hristo

 

ChartPart for SharePoint

If you would like to generate some charts in Microsoft SharePoint 2007, after a quick search you will find that there is no such out-of-the-box functionality. You shouldn’t worry, though, as there is a free web part available at Codeplex, which has everything you need for your list-based charting needs. It is called “ChartPart for SharePoint” and is available here.

Prerequisites

In order to get it working on your server, you will need to have Microsoft .NET 3.5 SP1 and Microsoft Chart Controls for Microsoft .NET Framework 3.5 installed. The ‘installation package’ does not check for those prerequisites, so make sure you do that manually before you install. Most probably you already have .NET 3.5 SP1 installed, but chances are, the MS Chart Controls are not installed.

Installation

The deployment package consists of two WSP files (SharePoint solution deployment packages) – MSChartControls.wsp and ChartPart 2.0.wsp. First, you have to install the MSChartControls package. Login on your SharePoint server, start the Console (Start -> Run -> cmd) and type:

stsadm -o addsolution –filename MSChartControls.wsp
stsadm -o execadmsvcjobs

STSADM NOTE: Of course, you will have to choose between the convenience of not typing the full path to the stsadm.exe file or not having to type the full path to the WSP. So, you have to be in one of the two folders and type the full path to the other file.

This will add the solution to the SharePoint Central Administration -> Operations -> Solution Management. You can either use the User Interface it provides to deploy the added solution, or you can continue executing console commands, like this:

stsadm -o deploysolution -name MSChartControls.wsp -immediate –allowgacdeployment
stsadm -o execadmsvcjobs

Then, you have to activate the installed solution on Web Application level:

  1. Go to Central Administration
  2. Go to Application Management
  3. Select Manage Web Application Features
  4. Verify that you have selected the correct Web Application in the top right corner
  5. Click Activate on the Microsoft Chart Controls Feature

Now, you have to do something similar for the other WSP (Chart Part 2.0.wsp):

stsadm -o addsolution -filename “ChartPart 2.0.wsp”
stsadm -o execadmsvcjobs

Deploy the solution, either from Central Admin, or by using the console:

stsadm -o deploysolution -name “Chartpart 2.0.wsp” -immediate -allowCasPolicies -url <url>
stsadm -o execadmsvcjobs

Where <url> is the address of the web site you wish to deploy the webpart to. Now all you have to do is activate the feature on the selected Site Collection.

  1. Go to Site Settings for your top-level site
  2. Go To Site Collection Features
  3. Activate the ChartPart for SharePoint feature
  4. Now you can add ChartParts on every site in your Site Collection.

Usage

I created a new ‘Web Part Page’ on my site, to use for experimenting with the web parts. I chose ‘Full Page, Vertical’ layout template, as it  is one of the simplest and I preferred to have full width.

I chose to ‘Edit Page’ and clicked on ‘Add a Web Part’ link in the design mode of the page. A pop-up showing all available web parts appears and from there, I chose to add on the page the ‘ChartPart’ web part.


Once added on the page, you have to configure it. While the page is still in design mode, click on the little arrow down in the right upper corner of the web part. It will open a side menu. Click on ‘Modify Shared Web Part’. This will open the options of the web part.

Here, you can set the title of the web part (as it will appear on the page it is added to), the web site to pull data from, the specific list and its view. Then you have to select X and Y “Series” column. A bit of explanation… Only Number fields can be choses as “X-Series”. “Item Count” is always available to you. As “Y-Series” you have to specify a column of the SharePoint list.

To illustrate all this, I will configure my list to contain only two columns – “City” (single line of text) and “Population” (number field). Here is what my list looks like:

Now, I want to configure the Chart web part to visualize this statistics. I will set it up this way:

We click OK button and the generated chart looks like:

Customization

You can customize the chart in many ways. First. by selecting the ‘Chart Type’ for the chart. Choices are Column, Bar, Pie and so many others. Additionally, you can select ‘Style’ (Cylinder, Emboss, LightToDark, Wedge), which seems to work only on specific ‘Chart Types’. For example, it clearly works on a ‘Column’ chart type. Here is the difference between ‘Cylinder’ and ‘Wedge’:

There is many other fine tunings you can do, like color palette, size and etc. Also what’s really nice is that in the tooltip (when you hover a column in the chart) you will see an exact value, of the column.

Conclusion

ChartPart for SharePoint is a great component, if you want to visualize some statistics based on SharePoint list items. It is highly customizable, easy to install and use.