Overview
Previously we have already discussed the possible ways for customization of People Editor,in particular it was demonstrated how to add initialization and validation capabilities on the client side. This time we are going to concentrate on some aspects of extensibility for People Editor control that are available on the client side.
The entry point for extending of the People Editor control on the client side is AfterCallbackClientScript property. It allows to specify the callback JavaScript function to be executed after People Entity value is resolved and control is initialized.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Callback function for People Editor AfterCallbackClientScript property | |
//ctx – People Editor Client Id | |
function AfterCallbackClientScript(ctx) {} |
The different approach is to override Entity Editor callback function itself, for example the code below demonstrates how to execute method before control is initialized
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Override EntityEditorCallback method | |
var EntityEditorCallback = (function () { | |
var EntityEditorCallbackOrig = EntityEditorCallback; | |
return function() { | |
var result = arguments[0]; | |
var ctx = arguments[1]; | |
if(typeof PreCallbackClientScript != 'undefined') | |
PreCallbackClientScript(result, ctx); | |
EntityEditorCallbackOrig(result, ctx); | |
}; | |
})(); |
The following example demonstrates the usage of the second approach.
Customize People Editor in Alerts subscription page to enable sending of alerts assigned to SharePoint Group
Sending alerts assigned to SharePoint Group is not supported by default in SharePoint 2010. The main idea of this solution is to enable the selection of SharePoint Groups in people editor on subscription pages and the sending alerts to individual users from these groups
How it works
In Alert subscription page (New or Edit forms) type or select in People picker dialog box the SharePoint Group name
After clicking “Check names” button or OK button in People picker the SharePoint group will be expanded to individual users in the text box as shown below
Implementation
So, the solution consist of the following tasks:
- activate SharePoint Groups for selection in people editor control
- expand a group entity to individual user entities in the text box
Enable SharePoint Group for People Editor via Delegate Control
As was noted earlier SharePoint Groups are not allowed for selection set on people editor control in Alerts subscription pages
In order to activate the selection of SharePoint Group in Alerts subscription pages the following control is intended (code behind):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class SendAlertsManager : UserControl | |
{ | |
#region Control Lifecycle | |
protected override void OnLoad(EventArgs e) | |
{ | |
if (IsAlertNewPage || IsAlertEditPage) | |
{ | |
EnableSendAlertsToSPGroup(); | |
Visible = true; | |
} | |
else | |
{ | |
Visible = false; | |
} | |
} | |
private void EnableSendAlertsToSPGroup() | |
{ | |
var pe = FindControl<PeopleEditor>(Page.Controls); | |
if(pe!= null) | |
{ | |
if(!pe.SelectionSet.Contains("SPGroup")) | |
{ | |
pe.SelectionSet += ",SPGroup"; | |
//pe.AfterCallbackClientScript = "expandGroupCallback"; | |
} | |
} | |
} | |
protected override void OnPreRender(EventArgs e) | |
{ | |
EnableSendAlertsToSPGroup(); | |
base.OnPreRender(e); | |
} | |
#endregion | |
#region Control Utilities | |
/// <summary> | |
/// Find control recursivelly | |
/// (http://weblogs.asp.net/eporter/archive/2007/02/24/asp-net-findcontrol-recursive-with-generics.aspx) | |
/// </summary> | |
/// <typeparam name="T"></typeparam> | |
/// <param name="controls"></param> | |
/// <returns></returns> | |
public static T FindControl<T>(System.Web.UI.ControlCollection controls) where T : class | |
{ | |
T found = default(T); | |
if (controls != null && controls.Count > 0) | |
{ | |
for (int i = 0; i < controls.Count; i++) | |
{ | |
if (found != null) break; | |
if (controls[i] is T) | |
{ | |
found = controls[i] as T; | |
break; | |
} | |
found = FindControl<T>(controls[i].Controls); | |
} | |
} | |
return found; | |
} | |
#endregion | |
#region Properties | |
private bool IsAlertNewPage | |
{ | |
get | |
{ | |
return | |
(Context.Request.Url.PathAndQuery.IndexOf("_layouts/subnew.aspx", | |
StringComparison.InvariantCultureIgnoreCase) > 0); | |
} | |
} | |
private bool IsAlertEditPage | |
{ | |
get | |
{ | |
return | |
(Context.Request.Url.PathAndQuery.IndexOf("_layouts/subedit.aspx", | |
StringComparison.InvariantCultureIgnoreCase) > 0); | |
} | |
} | |
#endregion | |
} |
Control with reference to JavaScript library – expand a group entity to individual user entities for picker editor control
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %> | |
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> | |
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> | |
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> | |
<%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %> | |
<%@ Import Namespace="Microsoft.SharePoint" %> | |
<%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> | |
<%@ Control Language="C#" AutoEventWireup="true" Inherits="SharePoint.ControlExtender.WebControls.SendAlertsManager" %> | |
<script type="text/javascript" src="/_layouts/YASPP/entityeditorutils.js"></script> |
Manifest file for registration of SendAlertsManager control via Delegate Control
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<Elements xmlns="http://schemas.microsoft.com/sharepoint/"> | |
<Control | |
Id="AdditionalPageHead" | |
Sequence="80" | |
ControlSrc="~/_controltemplates/SendAlertsManager.ascx"> | |
</Control> | |
</Elements> |
Expand SharePoint Group entity to individual Users entities (entityeditorutils.js)
Expand SharePoint Groups to individual User entities for People Editor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Expand Group entity to to individual Users entities | |
function expandGroupEntity(groupEntity, fnCallback) { | |
var context = new SP.ClientContext.get_current(); | |
var groups = context.get_web().get_siteGroups(); | |
var groupId = getSPGroupIdForEntity(groupEntity); | |
var group = groups.getById(groupId); | |
this.users = group.get_users(); | |
context.load(users); | |
context.add_requestSucceeded(onLoaded); | |
context.add_requestFailed(onFailure); | |
context.executeQueryAsync(); | |
function onLoaded() { | |
var userEntities = '<Entities>'; | |
var count = users.get_count(); | |
for (i = 0; i < count; i++) { | |
var userItem = users.itemAt(i); | |
userEntities += UserEntryToXml(userItem); | |
} | |
userEntities += '</Entities>'; | |
var results = GetEntities(userEntities); | |
fnCallback(groupEntity,results); | |
} | |
function onFailure(sender, args) { | |
fnCallback(groupEntity,null); | |
} | |
} | |
//Create Entity xml fron SPUser | |
function UserEntryToXml(userItem) | |
{ | |
return '<Entity Key="' + userItem.get_loginName() + '" DisplayText="' + userItem.get_title() + '" IsResolved="True" Description="' + userItem.get_loginName() + '"><MultipleMatches /></Entity>'; | |
} | |
function prepareUserEntities(result,ctx,fnCallback) | |
{ | |
var entities=GetEntities(result); | |
if (entities==null) | |
return; | |
for(var x=0;x<entities.childNodes.length;x++) | |
{ | |
var entity=entities.childNodes[x]; | |
if(isSPGroupEntity(entity)) { | |
expandGroupEntity(entity,fnCallback); | |
} | |
else | |
fnCallback(null,null); | |
} | |
} | |
function getSPGroupIdForEntity(entity) | |
{ | |
var groupId = –1; | |
var keys = EntityEditor_SelectNodes(entity,"Key"); | |
for (var i=0;i<keys.length;i++) { | |
if(keys[i].firstChild.nodeValue == "SPGroupID") { | |
groupId = parseInt(keys[0].nextSibling.firstChild.nodeValue); | |
break; | |
} | |
} | |
return groupId; | |
} | |
function isSPGroupEntity(entity) | |
{ | |
var isSPGroupEntity = false; | |
var keys = EntityEditor_SelectNodes(entity,"Key"); | |
for (var i=0;i<keys.length;i++) { | |
if(keys[i].firstChild.nodeValue == "SPGroupID") { | |
isSPGroupEntity = true; | |
break; | |
} | |
} | |
return isSPGroupEntity; | |
} | |
var EntityEditorCallback = (function () { | |
var EntityEditorCallbackOrig = EntityEditorCallback; | |
return function() { | |
var result = arguments[0]; | |
var ctx = arguments[1]; | |
prepareUserEntities(result,ctx,function (groupEntity,userEntities) { | |
if (groupEntity != null) { | |
result = result.replace(EntitytoHtml(groupEntity),UserEntitiestoHtml(userEntities)); | |
} | |
EntityEditorCallbackOrig(result, ctx); | |
}); | |
}; | |
})(); | |
//Serialize user entities to Html | |
function UserEntitiestoHtml(entities) | |
{ | |
var entityHtml = ''; | |
for(i=0;i<entities.childNodes.length;i++) | |
entityHtml += EntitytoHtml(entities.childNodes[i]); | |
return entityHtml; | |
} | |
//Serialize entity to Html | |
function EntitytoHtml(entity) | |
{ | |
var entityHtml = EntityEditor_XmlToString(entity); | |
entityHtml = entityHtml.replace(/<MultipleMatches\/>/g, '<MultipleMatches />'); | |
return entityHtml; | |
} | |
//Entity Editor Helper methods | |
//select nodes from xml entity | |
function EntityEditor_SelectNodes(xmlNode, tagName) | |
{ULSGjk:; | |
//if(document.implementation && document.implementation.createDocument) | |
//{ | |
var elems=xmlNode.getElementsByTagName(tagName); | |
if(elems.length > 0) | |
return elems; | |
return null; | |
//} | |
//else | |
//{ | |
// return xmlNode.selectNodes(tagName); | |
//} | |
} | |
//Serialize Xml to String | |
function EntityEditor_XmlToString(xmlData) { | |
var xmlString; | |
//IE | |
if (window.ActiveXObject){ | |
xmlString = xmlData.xml; | |
} | |
// code for Mozilla, Firefox, Opera, etc. | |
else{ | |
xmlString = (new XMLSerializer()).serializeToString(xmlData); | |
} | |
return xmlString; | |
} | |