Contents tagged with SharePoint

  • Filtering and Sorting SharePoint List Views

    Introduction

    How do I filter thee? Let me count the ways!

    You should that SharePoint usually offers lots of ways to do more or less the same thing, and, of course, filtering a list view is no exception! In this post I am going to talk a bit about the ways that you can do this.

    Built-in URL Filtering

    SharePoint’s list view controls (XsltListViewWebPart, ListViewWebPart, etc) recognize a number of parameters from the query string that can be used to filter and sort automatically a list view, without requiring any additional effort.

    Filter by single value:

    http://server/Lists/Tasks/AllItems.aspx?FilterField1=PercentComplete&FilterValue1=.5

    FilterField must be an internal field’s name and FilterValue is case insensitive.

    Filter by single value and an operator:

    http://server/Lists/Tasks/AllItems.aspx?FilterField1=PercentComplete&FilterValue1=.5&FilterOp1=Neq

    The list of possible operators (FilterOp) is:

    • Eq: equal (default);
    • Neq: not equal;
    • Gt: greater than;
    • Lt: less than;
    • Geq: greater or equal to;
    • Leq: less or equal to;
    • BeginsWith: the start of a string;
    • Contains: part of a string.

    You can add up to 10 FilterField/FilterValue/FilterOp entries, the items must match all conditions (AND operator). If you wish to filter on an additional field of a lookup field, use this syntax:

    http://server/Lists/Tasks/AllItems.aspx?FilterField1=LookupField_x003a_AdditionalField&FilterValue1=1

    Filter by one of multiple exact values:

    http://server/Lists/Tasks/AllItems.aspx?FilterName=PercentComplete&FilterMultiValue=.5;1

    FilterName is also the internal name of the field and the values in FilterMultiValue are treated as case-insensitive. Separate then by ;.

    Filter by one of multiple partial values:

    http://server/Lists/Tasks/AllItems.aspx?FilterName=Author&FilterMultiValue=*Peres*;*Ricardo*

    http://server/Lists/Tasks/AllItems.aspx?FilterName=Author&FilterMultiValue=Ricardo*;*Peres

    The * character acts as a wildcard, matching anything before or after. Placing a term between * is the same as using FilterField/FilterValue with the Contains operator in FilterOp.

    Filter by a taxonomy field:

    If you want to filter by a managed metadata field, you can either search by the display name using FilterField and FilterValue, or you can use the ids of the terms. A little known fact is that all usages of managed metadata values – values of fields of managed metadata type – are stored in the TaxonomyHiddenList hidden list. If you want to filter by an id of a term, you need to use the following syntax (search by one of two values with FilterOp=In, values separated by commas):

    http://server/Lists/MyListWithTaxonomyField/AllItems.aspx?FilterField1=MetadataField&FilterLookupId=1&FilterValue1=1,2&FilterOp1=In

    Or by a single value (no need for FilterOp):

    http://server/Lists/MyListWithTaxonomyField/AllItems.aspx?FilterField1=MetadataField&FilterLookupId=1&FilterValue1=1

    The FilterLookupId=1 is only there to tell SharePoint that it needs to search not by value (FilterValue) passed in the query string, but by term pointed by it. You can find the ids for FilterValue in the TaxonomyHiddenList list.

    Sorting:

    http://server/Lists/Tasks/AllItems.aspx?SortField=PercentComplete&SortDir=Desc

    Possible values for SortDir are pretty obvious:

    • Asc: ascending (default);
    • Desc: descending.

    You can only sort by a single field.

    Filtering a XSLT Web Part by a Query String Parameter

    The idea is to add a ParameterBinding whose Location is QueryString and to use this parameter in a CAML query:

       1: <WebPartPages:XsltListViewWebPart runat="server" ListName="{A1ADD3D1-B21F-4F93-9B86-B1FE332424D0}" DisplayName="My List">
       2:     <XmlDefinition>
       3:         <View Type="HTML" DisplayName="All Items" Url="/Lists/MyList/AllItems.aspx">
       4:             <Query>
       5:                 <Where>
       6:                     <Or>
       7:                         <Gt>
       8:                             <FieldRef Name='SomeNumber'/>
       9:                             <Value Type='Number'>{param}</Value>
      10:                         </Gt>
      11:                         <Contains>
      12:                             <FieldRef Name='Author'/>
      13:                             <Value Type='Text'><![CDATA[{param}]]></Value>
      14:                         </Contains>
      15:                     </Or>
      16:                 </Where>
      17:             </Query>
      18:             <ViewFields>
      19:                 <FieldRef Name="SomeNumber"/>
      20:                 <FieldRef Name="Title"/>
      21:             </ViewFields>
      22:             <!-- all other properties ommitted for brevity -->
      23:         </View>
      24:     </XmlDefinition>
      25:     <ParameterBindings>
      26:         <ParameterBinding Name="param" Location="QueryString(param)"/>
      27:         <!-- all other parameter bindings ommitted for brevity -->
      28:     </ParameterBindings>
      29: </WebPartPages:XsltListViewWebPart>

    Notice that in case the param value is not present in the query string, the second condition in the Or disjunction will apply, which will try to match an empty string inside of the Author (Created By) field, which will always be true. Be careful, however, that while this works most of the time, if you specify a value for param that is indeed part of the creator of the field, it will be matched. As of now, I don’t have an 100% foolproof solution for this, if you know of any, do let me know!

    Filtering by a Form Field

    This is a variation of the previous technique, where we use a Location of Form in a ParameterBinding. In this case, however, filtering will only be applied when the page is post back. Add the following HTML to your page:

       1: <input type="text" name="param"/>
       2: <input type="submit" value="Search"/>
       3: <input type="button" value="Clear" onclick="document.location.href = document.location.href"/>

    And change the previous ParameterBinding to use the param input:

       1: <ParameterBinding Name="param" Location="Form(param)"/>

    The Search button will cause a postback, thus applying the filter, and the Clear button will navigate to the current page, using the GET verb, thus clearing the filter.

    If instead of HTML we want to use a server side control, we certainly can:

       1: <asp:TextBox runat="server" ID="param"/>

    Just change the ParameterBinding to point to the Text property of this control instead:

       1: <ParameterBinding Name="param" Location="Control(param,Text)"/>

    Filtering by a Web Part Connection

    Filter web parts:

    Another option is to filter by a web part that implements any of the filtering interfaces, used for supplying a value to another web part.

    SharePoint Enterprise contains a collection of filtering web parts. These cover a number of scenarios:

    • Filtering from a query string parameter;
    • Filtering from a text box;
    • Filtering from a date picker;
    • Filtering from a choice of values;
    • Filtering from a SharePoint list;
    • Filtering from a Business Connectivity Service;
    • Filtering from a SQL Server Analysis Services cube;
    • Filtering from current user properties (id, profile property);
    • Filtering from a field in the current page ().


    For the sake of simplicity, let’s pick filtering from the query string. In a web part page’s edit mode, add the Query String (URL) Filter web part to a web part zone:

    image

    If you prefer, you can also add it directly on the markup, just replace the QueryStringParameterName and FilterName for whatever you want:

       1: <%@ Register Tagprefix="SPSWC" Namespace="Microsoft.SharePoint.Portal.WebControls" Assembly="Microsoft.Office.Server.FilterControls, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
       2:  
       3: <SPSWC:QueryStringFilterWebPart runat="server" QueryStringParameterName="Status" FilterName="Status" />

    Next, add a connection from a list view web part in your page:

    image

    Again, you are free to do it in markup:

       1: <WebPartPages:SPProxyWebPartManager runat="server">
       2:     <SPWebPartConnections>
       3:         <WebPartPages:SPWebPartConnection ConsumerConnectionPointID="DFWP Filter Consumer ID" ConsumerID="g_ef253891_70bd_4996_bf8c_eb19d3d83012" ProviderConnectionPointID="ITransformableFilterValues" ProviderID="g_3c7bedb8_d6d7_42db_85d1_98feabfe5a63">
       4:             <WebPartPages:TransformableFilterValuesToParametersTransformer ConsumerFieldNames="Status" ProviderFieldNames="Status"/>
       5:         <WebPartPages:SPWebPartConnection>
       6:     <SPWebPartConnections>
       7: </WebPartPages:SPProxyWebPartManager>
       8: <asp:ScriptManagerProxy runat="server" />

    Do replace the ConsumerID and ProviderID by the actual IDs of your web parts, where ProviderID is the ID of the QueryStringFilterWebPart and ConsumerID is the ID of the list view web part and also the ConsumerFieldNames for the list field you want to filter on and ProviderFieldNames for the value you used in FilterName.

    Filter by a custom web part:

    All you need is to implement interface IWebPartField. Here’s a sample implementation of a web part that filters by a query string value, think of it as a poor man’s Query String Filter web part:

       1: public class QueryStringFilterWebPart : WebPart, IWebPartField
       2: {
       3:     [WebBrowsable(true)]
       4:     [WebDisplayName("Field")]
       5:     [WebDescription("The query string field")]
       6:     [Personalizable(PersonalizationScope.Shared)]    
       7:     public String QueryStringField { get; set; }
       8:  
       9:     PropertyDescriptor IWebPartField.Schema
      10:     {
      11:         get
      12:         {
      13:             return (TypeDescriptor.GetProperties(this)["QueryStringField"]);
      14:         }
      15:     }
      16:  
      17:     void IWebPartField.GetFieldValue(FieldCallback callback)
      18:     {
      19:         callback(this.Request.QueryString[this.QueryStringField]);
      20:     }
      21:  
      22:     public String Value
      23:     {
      24:         get
      25:         {
      26:             return (this.Request.QueryString[this.QueryStringField]);
      27:         }
      28:     }
      29: }

    After you deploy this web part and add it to a page, you will be able to create a connection to a list view web part so as to enable its filtering.

    Filtering an External List

    An external list is filterable by a finder operation, which is an operation of type Read List that you configure on the external content type. You can use SharePoint Designer to create views on the external list on top of an existing Read List operation. In the view’s page, the finder method – the name of the operation, not its display name – is specified as a Method entry in the CAML for the View element in the XSLT list view web part, for example:

       1: <View Name="{A3B221F8-A146-4880-9740-433195D4D788}" DisplayName="My Custom Finder" Url="/Lists/MyExternalList/MyCustomFinder.aspx">
       2:     <Method Name="MyCustomFinder"/>
       3:     <Query/>
       4:     <ViewFields/>
       5: </View>

    Of course, you can also specify parameters to a finder method (a filter):

       1: <Method Name=”MyCustomFinder”>
       2:     <Filter Name=”MyParameter” Value=”{0}”/>
       3: </Method>

    Filtering by XSLT

    The last technique is done on the “client-side”, through XSLT. This offers possibly the worst performance, since all records are retrieved before they are filtered, but the greatest flexibility, because you can apply conditional rules that would be very difficult, if not impossible, using the previous techniques. XSLT filtering can be applied on top of web part connections, external lists, etc.

    So, in your XSLT list view web part, inside xsl:stylesheet, add something like:

       1: <xsl:template match="/">
       2:     <xsl:for-each select="dsQueryResponse/Rows/Row">
       3:         <xsl:choose>
       4:             <xsl:when test="PercentComplete. &gt; .5">
       5:                 <p>Completion above 50%!</p>
       6:             </xsl:when>
       7:             <xsl:when test="PercentComplete. &lt; .5">
       8:                 <p>Completion below 50%!</p>
       9:             </xsl:when>
      10:             <xsl:otherwise>
      11:                 <p>Completion exactly 50%!</p>
      12:             </xsl:otherwise>
      13:         </xsl:choose>
      14:     </xsl:for-each>
      15: </xsl:template>

    This goes through all the returned records and outputs a message depending on the value of the PercentComplete field. Because it is a number field, we must use the syntax PercentComplete. (a dot in the end).

    Conclusion

    As you can see, a lot of functionality is available without having to resort to programming, SharePoint is very good at it. XSLT offers the greatest flexibility, because you can use conditional logic to express your exact intents. Whenever possible, however, try to filter records at CAML level, because less data will be returned.

    Read more...

  • SharePoint References

    I just updated my Excel I mentioned before with lots of hopefully useful SharePoint references:

    • Fields (enumeration, display name, internal name, id, type, CLR type, resource, group, description, etc);
    • Content types (enumeration, id, parent, group, description, etc);
    • List templates (enumeration, id, name, internal name, base type, category, description, etc);
    • Site templates (enumeration, name, code, id, description, etc);
    • List fields: fields of lists;
    • Document library fields: fields of document libraries;
    • SPDataSource fields: fields returned by SPDataSource.


    Get it here and give me your feedback.

    Read more...

  • The SPDataSource Control

    Introduction

    SPDataSource, together with XsltListViewWebPart, DataFormWebPart and ContentByQueryWebPart, is one of the most powerful tools that SharePoint provides. Because it can be used in different ways, it can also get complicated. In this post I am going to talk about is different modes and what can they give us. Fist, you need to know that the mode comes from the DataSourceMode property and can be one of:

    Let’s see them one by one.

    Webs

    Webs mode returns subsites of the current site or the root site. You need to supply a select parameter to SPDataSource named WebID, which can take one of the following values:

    • RootWeb: returns subsites of the root site;
    • SubWeb: returns subsites of the current site;
    • <ID>: returns subsites of a given site, indicated by its ID GUID.

    You can also pass a WebURL parameter, taking the relative URL of a site, or {sitecollectionroot} for the root site of the current site collection.

    SPDataSource will return one row for each site found, and for each site, it will return attributes that correspond to the properties of SPWeb, with the __sp prefix. They are:

    __spAlerts
    __spAllowAnonymousAccess
    __spAllowGlobalCatalog
    __spAllowP2P
    __spAllProperties
    __spAllUsers
    __spAlternateCssUrl
    __spAlternateHeader
    __spAnonymousPermMask64
    __spAnonymousState
    __spAudit
    __spAuthenticationMode
    __spAuthor
    __spAvailableContentTypes
    __spAvailableFields
    __spConfiguration
    __spContentTypes
    __spContext
    __spCreated
    __spCurrencyLocaleID
    __spCurrentChangeToken
    __spCurrentUser
    __spCustomizationMode
    __spCustomJSUrl
    __spDataRetrievalServicesSettings
    __spDefaultUrl
    __spDescription
    __spDocTemplates
    __spEffectiveBasePermissions
    __spEffectivePresenceEnabled
    __spEventHandlersEnabled
    __spEventReceivers
    __spExecuteUrl
    __spExists
    __spExternalSecurityProviderSetting
    __spFeatures
    __spFields
    __spFieldTypeDefinitionCollection
    __spFiles
    __spFirstUniqueAncestor
    __spFirstUniqueAncestorWeb
    __spFirstUniqueRoleDefinitionWeb
    __spFolders
    __spGroups
    __spHasExternalSecurityProvider
    __spHasUniquePerm
    __spHasUniqueRoleAssignments
    __spHasUniqueRoleDefinitions
    __spID
    __spIgnoreCheckOutLock
    __spIncludeSupportingFolders
    __spIsADAccountCreationMode
    __spIsADEmailEnabled
    __spIsParsingWebPartPage
    __spIsRootWeb
    __spLanguage
    __spLastItemModifiedDate
    __spLists
    __spListTemplates
    __spLocale
    __spMasterUrl
    __spCustomMasterUrl
    __spMeetingCount
    __spMeetingInformation
    __spModules
    __spName
    __spParentWeb
    __spParentWebId
    __spPermissions
    __spPersonalizationMode
    __spPortalMember
    __spPortalName
    __spPortalSubscriptionUrl
    __spPortalUrl
    __spPresenceEnabled
    __spProperties
    __spRecycleBin
    __spRegionalSettings
    __spReusableAcl
    __spRoleAssignments
    __spRoleDefinitions
    __spRoles
    __spRootFolder
    __spSafeControls
    __spServerNow
    __spServerRelativeUrl
    __spSite
    __spSiteGroups
    __spSiteUserInfoList
    __spSiteUsers
    __spSyndicationEnabled
    __spTheme
    __spThemeCssUrl
    __spTitle
    __spTypeCache
    __spUrl
    __spUserInfoListId
    __spUserIsSiteAdmin
    __spUserIsWebAdmin
    __spUsers
    __spViewMode
    __spViewStyles
    __spWebs
    __spWebTemplate
    __spWebTemplateId

    ListOfLists

    ListOfLists returns lists of a given site. It expects a WebID (same values as per the Webs mode) or a WebURL (relative address of a subsite) parameter, if none is supplied, it defaults to the current site. This mode allows restricting the lists to return by applying a CAML query to the SelectCommand property. For example, the following query filters lists of template 101 = Document Library in any of the subsites:

       1: <Webs Scope='Recursive'></Webs> <Lists ServerTemplate='101'></Lists>

    SPDataSource returns the following fields, which are equivalent to the properties with the same name in the SPList class, minus the __sp prefix:

    __spAlertTemplate
    __spAllowContentTypes
    __spAllowDeletion
    __spAllowMultiResponses
    __spAnonymousPermMask
    __spAnonymousPermMask64
    __spAudit
    __spAuthor
    __spBaseTemplate
    __spBaseType
    __spCanReceiveEmail
    __spContentTypes
    __spContentTypesEnabled
    __spCreated
    __spCurrentChangeToken
    __spDefaultView
    __spDefaultViewUrl
    __spDescription
    __spDirection
    __spDraftVersionVisibility
    __spEffectiveBasePermissions
    __spEmailAlias
    __spEnableAssignToEmail
    __spEnableAttachments
    __spEnableMinorVersions
    __spEnableModeration
    __spEnableSyndication
    __spEnableVersioning
    __spEventReceivers
    __spEventSinkAssembly
    __spEventSinkClass
    __spEventSinkData
    __spExcludeFromTemplate
    __spFields
    __spFirstUniqueAncestor
    __spFlags
    __spForceCheckout
    __spForms
    __spHasExternalEmailHandler
    __spHasUniqueRoleAssignments
    __spHidden
    __spID
    __spImageUrl
    __spInternalName
    __spItemCount
    __spItems
    __spLastItemDeletedDate
    __spLastItemModifiedDate
    __spLists
    __spMultipleDataList
    __spOnQuickLaunch
    __spOrdered
    __spParentWeb
    __spParentWebUrl
    __spPermissions
    __spProperties
    __spPropertiesXml
    __spReadSecurity
    __spReusableAcl
    __spRoleAssignments
    __spRootFolder
    __spRootFolderUrl
    __spSchemaXml
    __spSendToLocationName
    __spSendToLocationUrl
    __spShowUser
    __spTitle
    __spVersion
    __spViews
    __spWorkflowAssociations
    __spWriteSecurity

    CrossList

    CrossList performs a query in a number of lists, in pretty much the same way as SPSiteDataQuery does, returning items from these lists. It needs a CAML query in SelectCommand, one that you would pass to SPQuery, and you should specify there the fields to return and an optional query, like the following, which returns all items created by the current user:

       1: <Query><Where><Eq><FieldRef Name='Author'/><Value Type='Number'><UserID/></Value></Eq></Where></Query>

    Regardless of those, it will always return the following fields:

    ListId
    WebId
    ID

    List

    List returns items from a single list. It expects a WebID or WebURL parameter, defaulting to the current site if none is present, plus ListName, which is, of course, mandatory, and optionally RootFolder or FolderID (for a starting folder name, from SPFolder.Name, like Path/SubPath, or id, from SPFolder.UniqueId). Do set the UseInternalName and IncludeHidden property to true in SPDataSource. There’s another property that affects the results of List, which is Scope. It is similar to SPQuery. It’s possible values are:

    • Default: shows files and subfolders in the starting folder;
    • Recursive: shows all files in all folders;
    • RecursiveAll: shows all files and all folders;
    • FilesOnly: shows all files in the starting folder.

    The fields that are returned depends on the actual list, but the following should be always present:

    ContentTypeId
    Title
    _ModerationComments
    File_x0020_Type
    HTML_x0020_File_x0020_Type.File_x0020_Type.mapall
    HTML_x0020_File_x0020_Type.File_x0020_Type.mapcon
    HTML_x0020_File_x0020_Type.File_x0020_Type.mapico
    WF4InstanceId
    ID
    ContentType
    Modified
    Created
    Author
    Author.id
    Author.title
    Author.span
    Author.email
    Author.sip
    Author.jobTitle
    Author.department
    Author.picture
    Editor
    Editor.id
    Editor.title
    Editor.span
    Editor.email
    Editor.sip
    Editor.jobTitle
    Editor.department
    Editor.picture
    _HasCopyDestinations
    _HasCopyDestinations.value
    _CopySource
    owshiddenversion
    owshiddenversion.
    WorkflowVersion
    WorkflowVersion.
    _UIVersion
    _UIVersion.
    _UIVersionString
    Attachments
    _ModerationStatus
    _ModerationStatus.
    LinkTitleNoMenu
    LinkTitle
    LinkTitle2
    SelectTitle
    InstanceID
    InstanceID.
    Order
    Order.
    GUID
    WorkflowInstanceID
    FileRef
    FileRef.urlencode
    FileRef.urlencodeasurl
    FileDirRef
    Last_x0020_Modified
    Created_x0020_Date
    Created_x0020_Date.ifnew
    FSObjType
    SortBehavior
    PermMask
    FileLeafRef
    FileLeafRef.Name
    FileLeafRef.Suffix
    UniqueId
    SyncClientId
    ProgId
    ScopeId
    HTML_x0020_File_x0020_Type
    _EditMenuTableStart
    _EditMenuTableStart2
    _EditMenuTableEnd
    LinkFilenameNoMenu
    LinkFilename
    LinkFilename2
    DocIcon
    ServerUrl
    EncodedAbsUrl
    BaseName
    MetaInfo
    MetaInfo.
    _Level
    _Level.
    _IsCurrentVersion
    _IsCurrentVersion.value
    ItemChildCount
    FolderChildCount
    AppAuthor
    AppEditor

    ListItem

    Finally, ListItem returns fields from a single list item. You need to feed SPDataSource parameters WebID/WebURL, ListName and ListItemID/ListItemGUID, where ListItemGUID corresponds to the UniqueId property of SPListItem and ListItemID to ID. The fields that are returned depend on the list and content type, of course, but the following are always returned, in the case of a list:

    ID
    ContentType
    Created
    Modified
    Modified_x0020_By
    Created_x0020_By

    Or of a document library:
    SelectFilename
    FileLeafRef
    _dlc_DocId
    _dlc_DocIdUrl
    _dlc_DocIdPersistId

    Parameters

    Parameters are supplied in the SelectParameters collection and normally are one or more of:

    In SelectCommand, you can use these parameters, by referring their Name inside {}, for example:

       1: <View><ViewFields><RowLimit>{Count}</RowLimit></ViewFields></View>

    Accessing Result

    You can access the results in one of several ways:

    • Bind the SPDataSource to a data web part that works with XSLT, such as DataFormWebPart;
    • Use a grid control, such as SPGridView, to display fields, but you have to add columns yourself; one disadvantage is that you cannot see the contents of complex properties, such as, for example, __spParentWeb (the ParentWeb property of SPWeb);
    • Use instead a data-bound control, such as Repeater, where you can access public properties of returned fields, using Eval, for example, <%# Eval("__spParentWeb.Url") %>.

    Conclusion

    Together with DataFormWebPart, SPDataSource is a precious tool, which offers great flexibility without going into code. Learn to use it and it will become a great friend!

    Read more...

  • Getting the Current User in SharePoint XSLT

    SharePoint offers a couple of ways by which we can obtain the current user for use in a XSLT web part like DataFormWebPart or XsltListViewWebPart. All involve first setting parameters through the ParameterBindings property:

       1: <ParameterBindings>
       2:     <ParameterBinding Name="UserID" Location="CAMLVariable" DefaultValue="CurrentUserName"/>
       3:     <ParameterBinding Name="LogonUser_" Location="WPVariable(_LogonUser_)"/>
       4:     <ParameterBinding Name="LogonUser" Location="ServerVariable(LOGON_USER)"/>
       5:     <ParameterBinding Name="AuthUser" Location="ServerVariable(AUTH_USER)"/>
       6:     <ParameterBinding Name="RemoteUser" Location="ServerVariable(REMOTE_USER)"/>
       7: </ParameterBindings>
       8: <Xsl>
       9:     <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl asp" xmlns:asp="System.Web.UI.WebControls" xmlns:ddwrt2="urn:frontpage:internal" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime">
      10:         <xsl:output method="html" />
      11:         <xsl:param name="UserID"/>
      12:         <xsl:param name="LogonUser_"/>
      13:         <xsl:param name="LogonUser"/>
      14:         <xsl:param name="AuthUser"/>
      15:         <xsl:param name="RemoteUser"/>
      16:         <xsl:template match="/">
      17:             UserID: <xsl:value-of select="$UserID"/><br/>
      18:             ID: <xsl:value-of select="ddwrt:UserLookup($UserID, 'ID')" /><br/>
      19:             EMail: <xsl:value-of select="ddwrt:UserLookup($UserID, 'EMail')" /><br/>
      20:             Login: <xsl:value-of select="ddwrt:UserLookup($UserID, 'Login')" /><br/>
      21:             LogonUser_: <xsl:value-of select="$LogonUser_"/><br/>
      22:             AuthUser: <xsl:value-of select="$AuthUser"/><br/>
      23:             RemoteUser: <xsl:value-of select="$RemoteUser"/><br/>
      24:         </xsl:template>
      25:     </xsl:stylesheet>
      26: </Xsl>

    Here we see different kinds of parameters:

    • CAMLVariable: built in variables UserID and Today;
    • WPVariable: returns one of the predefined values for _WPID_ (web part client id), _WPQ_ (web part unique id in page),  _WPR_ (web part resources folder full URL),  _WPSRR_ (web part resources folder relative URL), _LogonUser_ (server variable LOGON_USER),  _WebLocaleId_ (current site locale, as in CultureInfo.LCID);
    • ServerVariable: returns one of the HTTP or IIS server- defined variables.


    This will return something like this:

    UserID: Ricardo Peres

    ID: 10

    EMail: ricardoperes@domain.com

    Login: i:0#.w|DOMAIN\ricardoperes

    LogonUser_: 0#.w|DOMAIN\ricardoperes

    AuthUser: 0#.w|DOMAIN\ricardoperes

    RemoteUser: 0#.w|DOMAIN\ricardoperes

    The ddwrt:UserLookup extension function returns a value depending on the second parameter; valid values are Id, Email and Login, and you can easily guess what they are for.

    You can find a reference for LOGON_USER, AUTH_USER, REMOTE_USER and UNMAPPED_REMOTE_USER here. In my development server, I get the same value for all variables.

    By the way, I posted a more advanced solution, which allows access to any profile properties. You can read it here.

    Read more...

  • Access Navigation Nodes in SharePoint XSLT

    SharePoint relies on ASP.NET Site Map Providers for generating navigation links on its default pages. Specifically, the default Web.config file registers a (big!) number of providers, which control different aspects of its navigation:

       1: <siteMap defaultProvider="CurrentNavigation" enabled="true">
       2:   <providers>
       3:     <add name="SPNavigationProvider" type="Microsoft.SharePoint.Navigation.SPNavigationProvider, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
       4:     <add name="SPSiteMapProvider" type="Microsoft.SharePoint.Navigation.SPSiteMapProvider, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
       5:     <add name="SPContentMapProvider" type="Microsoft.SharePoint.Navigation.SPContentMapProvider, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
       6:     <add name="SPXmlContentMapProvider" siteMapFile="_app_bin/layouts.sitemap" type="Microsoft.SharePoint.Navigation.SPXmlContentMapProvider, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
       7:     <add name="AdministrationQuickLaunchProvider" description="QuickLaunch navigation provider for the central administration site" type="Microsoft.Office.Server.Web.AdministrationQuickLaunchProvider, Microsoft.Office.Server.UI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
       8:     <add name="SharedServicesQuickLaunchProvider" description="QuickLaunch navigation provider for shared services administration sites" type="Microsoft.Office.Server.Web.SharedServicesQuickLaunchProvider, Microsoft.Office.Server.UI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
       9:     <add name="GlobalNavSiteMapProvider" description="CMS provider for Global navigation" type="Microsoft.SharePoint.Publishing.Navigation.PortalSiteMapProvider, Microsoft.SharePoint.Publishing, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" NavigationType="Global" EncodeOutput="true" />
      10:     <add name="CombinedNavSiteMapProvider" description="CMS provider for Combined navigation" type="Microsoft.SharePoint.Publishing.Navigation.PortalSiteMapProvider, Microsoft.SharePoint.Publishing, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" NavigationType="Combined" EncodeOutput="true" />
      11:     <add name="CurrentNavSiteMapProvider" description="CMS provider for Current navigation" type="Microsoft.SharePoint.Publishing.Navigation.PortalSiteMapProvider, Microsoft.SharePoint.Publishing, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" NavigationType="Current" EncodeOutput="true" />
      12:     <add name="CurrentNavSiteMapProviderNoEncode" description="CMS provider for Current navigation, no encoding of output" type="Microsoft.SharePoint.Publishing.Navigation.PortalSiteMapProvider, Microsoft.SharePoint.Publishing, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" NavigationType="Current" EncodeOutput="false" />
      13:     <add name="GlobalNavigation" description="Provider for MOSS Global Navigation" type="Microsoft.SharePoint.Publishing.Navigation.PortalSiteMapProvider, Microsoft.SharePoint.Publishing, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" NavigationType="Combined" Version="14" />
      14:     <add name="CurrentNavigation" description="Provider for MOSS Current Navigation" type="Microsoft.SharePoint.Publishing.Navigation.PortalSiteMapProvider, Microsoft.SharePoint.Publishing, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" NavigationType="Current" Version="14" />
      15:     <add name="SiteDirectoryCategoryProvider" description="Site Directory category provider" type="Microsoft.SharePoint.Portal.WebControls.SiteDirectoryCategoryProvider, Microsoft.SharePoint.Portal, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      16:     <add name="MySitePersonalQuickLaunchProvider" description="MySite personal quick launch provider" type="Microsoft.SharePoint.Portal.MySitePersonalQuickLaunchProvider, Microsoft.SharePoint.Portal, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      17:     <add name="MySiteHostTopNavigationProvider" description="MySite host top navigation provider" type="Microsoft.SharePoint.Portal.MySiteHostTopNavigationProvider, Microsoft.SharePoint.Portal, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      18:     <add name="GlobalNavigationSwitchableProvider" description="Provider for MOSS Global Navigation, which maps to GlobalNavigation or GlobalNavigationTaxonomyProvider according to Navigation Settings" type="Microsoft.SharePoint.Publishing.Navigation.SwitchableSiteMapProvider, Microsoft.SharePoint.Publishing, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" DefaultTargetProviderName="GlobalNavigation" Version="15" />
      19:     <add name="CurrentNavigationSwitchableProvider" description="Provider for MOSS Current Navigation, which maps to GlobalNavigation or CurrentNavigationTaxonomyProvider according to Navigation Settings" type="Microsoft.SharePoint.Publishing.Navigation.SwitchableSiteMapProvider, Microsoft.SharePoint.Publishing, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Version="15" DefaultTargetProviderName="CurrentNavigation" />
      20:     <add name="GlobalNavigationTaxonomyProvider" description="Provider for MOSS Global Navigation, driven by the Taxonomy feature" type="Microsoft.SharePoint.Publishing.Navigation.TaxonomySiteMapProvider, Microsoft.SharePoint.Publishing, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Version="15" />
      21:     <add name="CurrentNavigationTaxonomyProvider" description="Provider for MOSS Global Navigation, driven by the Taxonomy feature" type="Microsoft.SharePoint.Publishing.Navigation.TaxonomySiteMapProvider, Microsoft.SharePoint.Publishing, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Version="15" />
      22:     <add name="MySiteMapProvider" description="MySite provider that returns areas and based on the current user context" type="Microsoft.SharePoint.Portal.MySiteMapProvider, Microsoft.SharePoint.Portal, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      23:     <add name="MySiteLeftNavProvider" description="MySite Left Nav provider that returns areas and based on the current user context" type="Microsoft.SharePoint.Portal.MySiteLeftNavProvider, Microsoft.SharePoint.Portal, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      24:     <add name="MySiteDocumentStaticProvider" description="MySite Document library provider that returns static links to lists in the site" type="Microsoft.SharePoint.Portal.MySiteDocumentStaticProvider, Microsoft.SharePoint.Portal, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      25:     <add name="MySiteSitesPageStaticProvider" description="MySite Document library provider that returns static links to lists on the Sites page" type="Microsoft.SharePoint.Portal.MySiteSitesPageStaticProvider, Microsoft.SharePoint.Portal, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      26:     <add name="MySiteSubNavProvider" description="MySite Sub Nav provider that returns areas and based on the current user context" type="Microsoft.SharePoint.Portal.MySiteSubNavProvider, Microsoft.SharePoint.Portal, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      27:     <add name="EduTopNavProvider" description="Top Nav Site Map Provider for Education My Site Host" type="Microsoft.Office.Education.WebUI.EduTopNavProvider, Microsoft.Office.Education.WebUI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      28:     <add name="MySiteHostQuickLaunchProvider" description="Quick Launch Site Map Provider for Education My Site Host" type="Microsoft.Office.Education.WebUI.EduQuickLaunchProvider, Microsoft.Office.Education.WebUI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      29:   </providers>
      30: </siteMap>

    It is possible to use a Site Map Data Source (SiteMapDataSource) to feed a DataFormWebPart with navigation information, so that you can play with it using XSLT.

    For example, let’s pick one of the providers, SPNavigationProvider, which is registered, appropriately, as SPNavigationProvider. This one is capable of returning the global navigation (Global Navigation) and the quick links (Current Navigation). In order to select one or the other, we supply a special key to its StartingNodeUrl property:

    • sid:1000: Home Page;
    • sid:1002: Global Navigation;
    • sid:1025: Current Navigation;
    • sid:1040: Search.

    In case you are wondering, these values are defined in the public properties of the SPNavigation class.

    This example will list all attributes of all the nodes:

       1: <WebPartPages:DataFormWebPart runat="server">
       2:     <DataSources>   
       3:         <asp:SiteMapDataSource SiteMapProvider="SPNavigationProvider" ShowStartingNode="false" StartingNodeUrl="sid:1002" runat="server" />   
       4:     </DataSources>
       5:     <Xsl>
       6:         <xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" version="1.0" exclude-result-prefixes="xsl msxsl ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal">
       7:             <xsl:output method="html"/>
       8:  
       9:             <xsl:template match="/">
      10:                 <xsl:for-each select="dataRoot">                    
      11:                     <xsl:for-each select="NavigateUIData">
      12:                         <p>
      13:                             Name: <xsl:value-of select="@Name"/>
      14:                             Value: <xsl:value-of select="@Value"/>
      15:                             NavigateUrl: <xsl:value-of select="@NavigateUrl"/>
      16:                             Description: <xsl:value-of select="@Description"/>
      17:                             <xsl:if test="substring(@NavigateUrl, string-length(@NavigateUrl) - string-length('.aspx') + 1) = '.aspx'">
      18:                                 (ASPX)
      19:                             </xsl:if>
      20:                             <xsl:if test="substring(@NavigateUrl, string-length(@NavigateUrl) - string-length('/') + 1) = '/'">
      21:                                 (Subsite)
      22:                             </xsl:if>
      23:                         </p>                                                            
      24:                     </xsl:for-each>
      25:                 </xsl:for-each>
      26:             </xsl:template>                                
      27:         </xsl:stylesheet>
      28:     </Xsl>
      29: </WebPartPages:DataFormWebPart>

    You can see that the SiteMapDataSource returns XML in the form:

       1: <dataRoot>
       2:     <NavigateUIData Name="Some Name" Value="Some Value" NavigateUrl="Some Url" Description="Some Description" />
       3: </dataRoot>

    Attributes are mostly self-explanatory, but Name and Value have the same content and Description always appears to be empty. For subsites, the NavigateUrl attribute will end in a /. Depending on the provider, it is possible to have nested NavigateUIData nodes.

    If you set the ShowStartingNode property to true, you will also get a root node containing the name (for sid:1000 it is Home, for sid:1002 it is SharePoint Top Navigation Bar, for sid:1025 it is Quick Launch and for sid:1040 it is Search) and URL of the current site

    Unfortunately, there isn’t much documentation (read: none) about these providers, except, of course, their methods and properties, so you have to figure out yourself. One thing that can be useful is this XSLT that will give you all the nodes and their attributes:

       1: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl asp" xmlns:asp="System.Web.UI.WebControls" xmlns:ddwrt2="urn:frontpage:internal">    <xsl:output method="html" />
       2:     <xsl:template match="*">
       3:         <xsl:param name="path" select="''" />
       4:         <xsl:variable name="previous" select="count(preceding-sibling::*[name()=name(current())])" />
       5:         <xsl:variable name="countprevious" select="count(following-sibling::*[name()=name(current())])" />
       6:         <xsl:variable name="fullpath" select="concat($path, '/', name(), substring(concat('[', $previous + 1, ']'), 1 div ($countprevious or $previous)))" />
       7:         <p><xsl:value-of select="$fullpath" /></p>
       8:         <xsl:apply-templates select="@*|*">
       9:             <xsl:with-param name="path" select="$fullpath" />
      10:         </xsl:apply-templates>
      11:     </xsl:template>
      12:     <xsl:template match="@*">
      13:         <xsl:param name="path" select="''" />        
      14:         <xsl:value-of select="concat($path, '/@', name())" />: <xsl:value-of select="." />
      15:         <br/>
      16:     </xsl:template>
      17: </xsl:stylesheet>

    Read more...

  • Retrieving SharePoint User Profile Properties in XSLT

    A common request is to be able to retrieve user information from XSLT (XsltListViewWebPart, DataFormWebPart, etc). It is possible to obtain some information through server variables (LOGON_USER), CAML variables (UserID) or ddwrt:UserLookup extension function. It works, to some extent, but sometimes it is not enough.

    It is well known that one can have server controls in XSLT stylesheets. As it happens, the ProfilePropertyValue control retrieves any value from the user profile. It just needs to have a ProfilePropertyLoader declared before it, which can be placed in the master page. For example:

       1: <SPSWC:ProfilePropertyLoader LoadFullProfileOfCurrentUser="true" runat="server"/>
       2: <SPSWC:ProfilePropertyValue PropertyName="FirstName" ApplyFormatting="false" runat="server"/>

    Just remember to register the Microsoft.SharePoint.Portal.WebControls assembly with the SPSWC tag prefix, either in the Web.config or in the page itself.

    But, what about XSLT? Well, if the SPSWC tag prefix is registered in Web.config, we can do this:

       1: <xsl:template name="GetProfilePropertyValue">
       2:     <xsl:param name="Property"/>
       3:     <SPSWC:ProfilePropertyValue PropertyName="{$Property}" ApplyFormatting="false" runat="server" />
       4: </xsl:template>
       5:  
       6: <xsl:variable name="Property">WorkEmail</xsl:variable>
       7:  
       8: <xsl:variable name="WorkEmail">
       9:     <xsl:call-template name="GetProfilePropertyValue">
      10:         <xsl:with-param name="Property" value="$Property"/>
      11:     </xsl:call-template>
      12: </xsl:variable>
      13:  
      14: Work Email: <xsl:copy-of select="$WorkEmail"/>

    With this technique, you can retrieve any of the profile properties. Ah, don’t forget to add:

       1: xmlns:spswc="Microsoft.SharePoint.Portal"

    To the <xsl:stylesheet> declaration.

    Happy SharePointing! Winking smile




    Read more...

  • Exposing SharePoint Context to XSLT

    Picking on my previous post, I am going to show how you can access SharePoint (and ASP.NET context) from XSLT.

    Create a class like this one:

       1: public class ContextExtensions
       2: {
       3:     public Object HttpContext(String expression)
       4:     {
       5:         return (DataBinder.Eval(System.Web.HttpContext.Current, expression));
       6:     }
       7:  
       8:     public Object SpContext(String expression)
       9:     {
      10:         return (DataBinder.Eval(SPContext.Current, expression));
      11:     }
      12: }

    Follow the process described in my post to register it, apply tag mappings and register the controls as safe. Then, on the XSLT, you can access any properties (and properties of properties) of both HttpContext.Current and SPContext.Current instances! The only thing you can’t do is call methods. This is a limitation of DataBinder.Eval.

    Some examples:

       1: User.Identity.Name: <xsl:value-of select="my:HttpContext('User.Identity.Name')"/>
       2: SPFormContext.FormMode: <xsl:value-of select="my:SpContext('FormContext.FormMode')"/>
       3:  
       4: <xsl:variable name="TasksItemCount" select="my:SpContext('Web.Lists[&quot;Tasks&quot;].ItemCount')"/>
       5: Web.Lists["Tasks"].ItemCount: <xsl:value-of select="$TasksItemCount"/>
       6:  
       7: <xsl:variable name="ListItemName" select="my:SpContext('ListItem[&quot;Name&quot;]')"/>
       8: ListItem[&quot;Name&quot;]: <xsl:value-of select="$TasksItemCount"/>

    Notice that when you are to evaluate expressions with “, you must do it in two steps:

    1. Define a variable that takes the expression;
    2. In that variable, replace “ for &quot.

    Hope you find it useful!

    Read more...

  • Displaying All Fields in a SharePoint Data Source

    So, you want to know which fields your data source is returning. You can use the following XSLT:

       1: <xsl:for-each select="/dsQueryResponse/Rows/Row/@*">
       2:     <p>Name: <xsl:value-of select="name()" /> Value: <xsl:value-of select="." /></p>
       3: </xsl:for-each>

    Very handy, even if I say so myself! Winking smile

    Update: this can also be useful.

    Read more...

  • Custom XSLT Functions in SharePoint

    Wouldn’t it be good if SharePoint would allow the registration of custom functions that could be used in XSLT stylesheets? Well, as it happens, it does!

    Some of the classes that allow XSLT processing – DataFormWebPart, ContentByQueryWebPart - have a virtual method called ModifyXsltArgumentList that can be used for registering custom functions. Sadly, XsltListViewWebPart, XlstListFormWebPart, among others, are sealed, and thus do not allow this.

    Let’s write a class that inherits from DataFormWebPart (you could the same for ContentByQueryWebPart) and register our own functions:

       1: public class CustomDataFormWebPart : DataFormWebPart
       2: {
       3:     protected override void ModifyXsltArgumentList(ArgumentClassWrapper argList)
       4:     {
       5:         argList.AddExtensionObject("urn:MyFunctions", new MyFunctions());
       6:         base.ModifyXsltArgumentList(argList);
       7:     }
       8: }

    Let’s forget about the MyFunctions class and pretend that it has a few public instance methods that we want to expose to SharePoint. Mind you, because this class will be instantiated in a SharePoint context, it will have access to SPContext.Current, and from here, to the whole of SharePoint!

    In a XSLT script, we need something like this in order to access our extension functions:

       1: <xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:dsp="http://schemas.microsoft.com/sharepoint/dsp" version="1.0" exclude-result-prefixes="xsl msxsl ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal" xmlns:my="urn:MyFunctions">
       2: ...
       3: </xsl:stylesheet>

    Did you notice xmlns:my=”urn:MyFunctions”? This should be identical to the namespace that you used inside ModifyXsltArgumentList.

    And to call one of the functions:

       1: <xsl:value-of select="my:Function()"/>

    Again, you need to use the same prefix, my in this case. You can add parameters, as long as they are of base types (strings, number, date/time, booleans, and so on), and the same goes for the result type.

    I hear you say: “OK, but now I have to replace all declarations of DataFormWebPart for this new CustomDataFormWebPart!”. Not really, dear reader, ASP.NET offers a great solution for this: tag mapping. In a nutshell, we can ask ASP.NET to replace all instances of a control for another class that inherits from the control’s class.

    Now, we could this in several ways, but the best one is to have a feature targeting a WebApplication with a feature receiver that performs a Web.config modification:

       1: public override void FeatureActivated(SPFeatureReceiverProperties properties)
       2: {
       3:     var tagMappingDataFormWebPartModification = new SPWebConfigModification();
       4:     tagMappingDataFormWebPartModification.Path = "configuration/system.web/pages/tagMapping";
       5:     tagMappingDataFormWebPartModification.Name = "tagMappingDataFormWebPartModification";
       6:     tagMappingDataFormWebPartModification.Sequence = 0;
       7:     tagMappingDataFormWebPartModification.Owner = "WebConfigModifications";
       8:     tagMappingDataFormWebPartModification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
       9:     tagMappingDataFormWebPartModification.Value = String.Format("<add tagType=\"{0}\" mappedTagType=\"{1}\"/>", typeof(DataFormWebPart).FullName, typeof(CustomDataFormWebPart).AssemblyQualifiedName);
      10:  
      11:     var webApp = properties.Feature.Parent as SPWebApplication;
      12:     webApp.WebConfigModifications.Add(tagMappingDataFormWebPartModification);
      13:     webApp.Update();
      14:     webApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();
      15: }

    When this feature is activated, it adds a line in the Web.config file, tagMapping section, that tells ASP.NET to replace DataFormWebPart for CustomDataFormWebPart in all markup declarations. Of course, it won’t affect code: new DataFormWebPart() will still return a DataFormWebPart!

    All is not done, yet. We still need to mark our CustomDataFormWebPart control as safe for declaration in web pages. We do that by adding an entry to the SafeControls section of the Web.config. For a change, I’m going to do this in the SharePoint Solution Manifest, Package.xml:

       1: <Solution xmlns="http://schemas.microsoft.com/sharepoint/" SolutionId="e860c419-b399-497d-a313-545f994753c8" SharePointProductVersion="15.0">
       2:   <Assemblies>
       3:     <Assembly Location="$SharePoint.Project.AssemblyFileName$" DeploymentTarget="GlobalAssemblyCache">
       4:       <SafeControls>
       5:         <SafeControl Assembly="$SharePoint.Project.AssemblyFullName$" Namespace="MyNamespace" TypeName="*" Safe="True" />
       6:       </SafeControls>
       7:     </Assembly>
       8:   </Assemblies>
       9:   <FeatureManifests>
      10:     <FeatureManifest Location="MyFeature\Feature.xml" />
      11:   </FeatureManifests>
      12: </Solution>

    Notice the $SharePoint.Project.AssemblyFileName$ and $SharePoint.Project.AssemblyFullName$, these are two examples of tokens that will be replaced by Visual Studio when it deploys the solution.

    And that’s it! Your custom XSLT extension functions are ready to use!

    Read more...