JoelBlogs - Joel Jeffery's Microsoft 365 Blog

Microsoft 365, SharePoint, Teams and Office 365 Architecture, Development, Administration and Training

  • Home
    • Sitemap
  • Articles
    • #SPThingADay
    • SharePoint Online
      • SharePoint Online – Drag and Drop and Large File Uploads
    • SharePoint 2016
    • SharePoint 2013
      • Content Database Changes to the AllUserData Table
    • SharePoint 2010
      • Administration
        • Disable CRL Checking
        • Excel 2010 & PowerPivot
        • Limits & Thresholds
        • PeoplePicker AD Errors
        • Recycle Bin Behaviour
        • Renaming a Server
        • Service Pack 1
        • Unattended Installs
        • Uninstall All SharePoint 2010 Solutions via PowerShell
        • User Alert Management
        • Virtualised SharePoint
        • Visio Stencils for Administrators
      • Development
        • Audience Membership Workflow Activity
        • Base Types, Lists & Content Types
        • BCS & Offline Sync
        • Debugger Skipping Lines
        • Development Laptop Spec
        • Enabling JavaScript IntelliSense
        • Event Receivers & Deployment Jobs
        • FavIcons & SPUrl
        • Google Maps Sandbox Web Part
        • Group By Content Type for List Views
        • Locale Stapler / Master or Default Locale
        • Removing Default Editor Parts
        • Sandbox Embedding Resources
        • Solution Sandbox Introduction
        • SPPersistedObject
        • Restoring Deleted SPSites in SP1
        • SPWebConfigModification 1
        • SPWebConfigModification 2
        • STSADM copyappbincontent vs. Install-SPApplicationContent
        • Workflows for Beginners
        • Workflow InitiationData Seralizer
    • SharePoint 2007
      • Alternate Access Mappings
      • Excel Services
      • Excel Services UDFs & Excel Client 2007
      • Experiences from the Field
      • InfoPath & Forms Server
      • Kerberos & SSRS
      • Records Management
      • Web Application Service
      • WSS vs MOSS
  • Training
    • SharePoint Admin Links
  • Downloads
    • Summary Slides for PowerPoint
    • CodePlex Projects
      • Audience Membership Workflow Activity
      • Google Maps Sandbox Web Part
      • Group By Content Type in List Views
      • Locale Stapler / Master or Default Locale
      • SharePoint Outlook Connector
  • Hire Me!
    • MCP Transcript
    • Résumé/CV

SharePoint 2010 Audience Membership Workflow Activity Condition for Designer Workflows

October 29, 2011 by Joel Jeffery

One of my students tonight asked if it was possible to add a condition to a SharePoint Designer 2010 declarative workflow to detect if the initiating user is a member of a particular audience.

There’s nothing built-in to deliver this in SharePoint 2010.

So I knocked-up the following solution based upon the excellent reference implementations of workflows from the SharePoint Prescriptive Guidance Pack at spg.codeplex.com.

I’ve put the full version of my source code and a completed release up on CodePlex.

Firstly, the .Actions file, which must be deployed to 14\\Template\\Xml\\1033\\Workflow:

<?xml version="1.0" encoding="utf-8" ?>
<WorkflowInfo>
  <Conditions And="and" Or="or" Not="not" When="If" Else="Else if">
    <Condition Name="User is member of audience"
        FunctionName="IsUserMemberOfAudienceCondition"
        ClassName="joelblogs.co.uk.WorkflowActivities.AudienceActivity.AudienceMemberActivity"
        Assembly="joelblogs.co.uk.WorkflowActivities.AudienceActivity, 
          Version=1.0.0.0, Culture=neutral, PublicKeyToken=d926d259b11539d4"
        AppliesTo="all"
            UsesCurrentItem="True">
      <RuleDesigner Sentence="The user is a member of audience %1">
        <FieldBind Id="1" Field="_1_" Text=""/>
      </RuleDesigner>
      <Parameters>
        <Parameter Name="_1_" Type="System.String, mscorlib" Direction="In" />
      </Parameters>
    </Condition>
  </Conditions>
</WorkflowInfo>

Next, the workflow activity class itself:

using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WorkflowActions;
using Microsoft.Office.Server.Audience;
using System.Workflow.ComponentModel;
using System.ComponentModel;

namespace joelblogs.co.uk.WorkflowActivities.AudienceActivity
{
    /// <summary>
    /// Windows Workflow Activity for SharePoint 2010. Checks if
    /// Initiating User is a member of the specified Audience.
    /// Written by Joel Jeffery, 2011-10-28.
    /// </summary>
    class AudienceMemberActivity : Activity
    {
        /// <summary>
        /// Returns whether the user exists in the specified audience or not
        /// -- signature to match SharePoint Designer Requirement
        /// </summary>
        /// <param name="workflowContext">Environment for activity</param>
        /// <param name="listId">ID of the list the workflow is running on (unused)</param>
        /// <param name="itemId">Item ID of the item the workflow is running on (unused)</param>
        /// <param name="siteUrl">The audience name to determine whether the user is in it or not</param>
        /// <returns>True if site exists, false if not </returns>
        public static bool IsUserMemberOfAudienceCondition(
            WorkflowContext workflowContext, string listId, int itemId, string audienceName)
        {
            string exception;
            return (IsUserMemberOfAudience(
                workflowContext.InitiatorUser.LoginName, audienceName, out exception));
        }

        /// <summary>
        /// Determines whether [is user member of audience] [the specified login name].
        /// </summary>
        /// <param name="loginName">Name of the login.</param>
        /// <param name="audienceName">Name of the audience.</param>
        /// <param name="exception">The exception.</param>
        /// <returns>
        ///   <c>true</c> if [is user member of audience] [the specified login name]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsUserMemberOfAudience(string loginName, string audienceName, out string exception)
        {
            try
            {
                exception = null;
                SPServiceContext context = SPServiceContext.Current;
                AudienceManager audManager = new AudienceManager(context);
                return audManager.IsMemberOfAudience(loginName, audienceName);
            }
            catch (Exception e)
            {
                exception = e.ToString();
                return (false);
            }
        }

        public static DependencyProperty AudienceNameProperty = 
            DependencyProperty.Register("AudienceName", typeof(string), typeof(AudienceMemberActivity));

        [Description("The absolute URL of the site or site collection to create")]
        [Browsable(true)]
        [Category("joelblogs.co.uk Activities")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string SiteUrl
        {
            get { return ((string)base.GetValue(AudienceNameProperty)); }
            set { base.SetValue(AudienceNameProperty, value); }
        }

        public static DependencyProperty ExistsProperty = 
            DependencyProperty.Register("Exists", typeof(bool), typeof(AudienceMemberActivity));
        [Description("The result of the operation indicating whether the site exists or not")]
        [Browsable(true)]
        [Category("joelblogs.co.uk Activities")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public bool Exists
        {
            get { return ((bool)base.GetValue(ExistsProperty)); }
            set { base.SetValue(ExistsProperty, value); }
        }

        public static DependencyProperty ExceptionProperty = 
            DependencyProperty.Register("Exception", typeof(string), typeof(AudienceMemberActivity));
        [Description("The exception generated while testing for the existance of the site")]
        [Browsable(true)]
        [Category("joelblogs.co.uk Activities")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string Exception
        {
            get { return ((string)base.GetValue(ExceptionProperty)); }
            set { base.SetValue(ExceptionProperty, value); }
        }
    }
}

 

You’ll also need a terribly clever feature receiver implementation from the SPG that uses the SPWebConfigModification class to add AuthorizedType blocks to the web.configs throughout our farm, or our class won’t be loaded by WF.

using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace joelblogs.co.uk.WorkflowActivities.AudienceActivity.Features.AudienceTestActivity
{
    /// <summary>
    /// This class handles events raised during feature activation, deactivation, 
    /// installation, uninstallation, and upgrade.
    /// </summary>
    /// <remarks>
    /// The GUID attached to this class may be used during packaging and should not be modified.
    /// </remarks>

    [Guid("a91d2258-b39b-4ca4-8282-2565c061378d")]
    public class AudienceTestActivityEventReceiver : SPFeatureReceiver
    {
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            try
            {
                SPWebService contentService = SPWebService.ContentService;
                contentService.WebConfigModifications.Add(GetConfigModification());
                // Serialize the web application state and propagate changes across the farm. 
                contentService.Update();
                // Save web.config changes.
                contentService.ApplyWebConfigModifications();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                throw;
            }
        }

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            try
            {
                SPWebService contentService = SPWebService.ContentService;
                contentService.WebConfigModifications.Remove(GetConfigModification());
                // Serialize the web application state and propagate changes across the farm. 
                contentService.Update();
                // Save web.config changes.
                contentService.ApplyWebConfigModifications();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                throw;
            }
        }

        public SPWebConfigModification GetConfigModification()
        {
            string assemblyValue = typeof(AudienceMemberActivity).Assembly.FullName;
            string namespaceValue = typeof(AudienceMemberActivity).Namespace;

            SPWebConfigModification modification = new SPWebConfigModification(
                string.Format(CultureInfo.CurrentCulture,
                    "authorizedType[@Assembly='{0}'][@Namespace='{1}']" +
                    "[@TypeName='*'][@Authorized='True']", assemblyValue, namespaceValue),
                "configuration/System.Workflow.ComponentModel.WorkflowCompiler/authorizedTypes");

            modification.Owner = "joelblogs.co.uk";
            modification.Sequence = 0;
            modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
            modification.Value =
                string.Format(CultureInfo.CurrentCulture,
                "<authorizedType Assembly=\"{0}\" Namespace=\"{1}\" " + 
                "TypeName=\"*\" Authorized=\"True\" />", assemblyValue, namespaceValue);

            Trace.TraceInformation("SPWebConfigModification value: {0}", modification.Value);

            return modification;
        }
    }
}

You can download the full source code to my SharePoint 2010 Audience Membership Workflow Activity (Full Trust) here: http://spamwaft.codeplex.com.

Filed Under: Development, SharePoint 2010 Tagged With: SharePoint, SharePoint 2010, SharePoint Designer 2010, SharePoint Development, Workflow

SharePoint 2010, Favorites Icons (FavIcons) and SPUrl (SPUrlExpressionBuilder)

November 9, 2010 by Joel Jeffery

Time to burst another popular misconception.

SharePoint 2010 comes with a new server tag to help you set a Favourite Icon (FavIcon) for pages in your site. This is the <SharePoint:SPShortCutIcon> tag. The SPShortCutIcon tag lets you specify an IconUrl property. If we don’t mind hard coding our IconUrls, we can set it to something like:

<SharePoint:SPShortCutIcon IconUrl=”/Site Assets/favicon.ico” runat=”server” />

However, if you have many sites in a hierarchy, or many site collections, you might not want to have this hard coded.

SharePoint helps us out here with a handy thing called the SPUrlExpressionBuilder class. We can use the special server-side tag $SPUrl to format up Url strings that are relative to the current site or site collection.

If I wanted to use this technique with a related part of the page, the Site Logo Image <SharePoint:SiteLogoImage>, I could embed the following code into my master page:

<SharePoint:SiteLogoImage LogoImageUrl=”<% $SPUrl:~sitecollection/SiteAssets/icon.png%>” runat=”server”/>

It would be nice to use the same technique with FavIcons. Indeed, there are are a lot of blog sites that suggest this is possible. I’ve tried this out, and I have no idea how it could work.

The reason the above code works is that SPSiteLogoImage.LogoImageUrl is a System.String type. $SPUrl statements return a System.String. No problem there.

However, SPShortCutIcon.IconUrl seems to have broken this pattern, and is actually a System.Uri. Any attempt to use $SPUrl to format a relative Url results in a casting error from System.String to System.Uri.

Unable to cast object of type 'System.String' to type 'System.Uri'

Good news is, there is a workaround. It’s not pretty, but we can use $SPUrl to set the System.String value of the Text property on an asp:Literal control.

We then need to sandwich that in between two more asp:Literals to render a complete client-side <link rel=”shortcut icon” …/> tag.

<asp:literal runat=”server” Text=”&lt;link rel=’shortcut icon’ href='” /><asp:literal runat=”server” Text=”<% $SPUrl:~sitecollection/Site%20Assets/favicon.ico %>” /><asp:literal runat=”server” Text=”‘ type=’image/vnd.microsoft.icon’ /&gt;” />

I told you it wasn’t pretty, but at least we didn’t have to write any custom controls of our own. Here’s the HTML it outputs:

<link rel=’shortcut icon’ href=’/Site%20Assets/favicon.ico’ type=’image/vnd.microsoft.icon’ />

If anyone has a more elegant (read: less kludgy) solution to the problem, please post a comment!

Post Script:

Here’s a nicer solution from one of our readers: (Many thanks, Adam!)

<link rel=’shortcut icon’ href=’<% $SPUrl:~SiteCollection/Style Library/Images/favicon.ico %>’ />

 

Filed Under: SharePoint Tagged With: SharePoint Designer 2010, SharePoint Developer

Next Page »

Joel is a full-stack cloud architect who codes. He is a Microsoft Certified SharePoint Online, SharePoint Server and Azure specialist and Microsoft Certified Trainer.
He has over 20 years' experience with SharePoint and the Microsoft .NET Framework.
He's also co-founder of Microsoft Gold Partner JFDI Consulting Ltd. Read More…

Recent Posts

  • Microsoft Flow Tip #1 – Word Templates and Hiding Empty Repeating Sections
  • SharePoint PowerShell Tip #1 – Select-Object and FieldValues
  • Popular Misconceptions – Microsoft Teams relationship with SharePoint
  • Course: Microsoft 365 Certified Teamwork Administrator
  • Audience Targeted Searches in Modern SharePoint Online
MCT 2020-2021
Microsoft Teamwork Administrator Associate
Joel's Acclaim Profile
Joel's Microsoft Profile

Tags

Administration Architecture Certification Cloud Development freetraining Information Architecture intranets MCP Microsoft Microsoft Architecture Microsoft Azure microsoftsharepoint migration Mobile Development MOSS Office 365 office365 Office 365 Permissions PowerShell SaaS SharePoint SharePoint 2010 SharePoint 2010 Training SharePoint 2013 SharePoint Administration SharePoint Administrator SharePoint Architecture SharePoint Developer SharePoint Development sharepointia SharePoint Online sharepointonline SharePoint Search SharePoint Training SharePoint Videos Silverlight SOA SPThingADay TechEd 2007 Training Videos Windows Phone 7 WSS

Copyright © 2022 Joel Jeffery, SharePoint Architect