Contents tagged with SharePoint

  • Cascading Drop Down Lists in SharePoint

    Introduction

    A common request is the ability to have one drop down list (the child) being filled after another one (the parent) has; this is usually called cascading drop down lists.

    There are several solutions for this, most of what I’ve found use JavaScript. It isn’t required, and a very simple solution exists, both for SharePoint 2010 as well as 2013.

    Set Up

    First, let’s create two lists: Parent and Child. Connect the Child to the Parent through a lookup field that points to the Parent’s ID field and call this lookup Parent. Add some records to it.

    SharePoint Designer

    On SharePoint Designer create a new web part page. Find some space and add two DropDownList controls:

       1: <asp:DropDownList runat="server" ID="parentList" DataSourceID="parentDataSource" DataTextField="Title" DataValueField="ID" AutoPostBack="true" Width="200px" />
       2:  
       3: <br/>
       4:  
       5: <asp:DropDownList runat="server" ID="childList" DataSourceID="childDataSource" DataTextField="Title" DataValueField="ID" Width="200px" EnableViewState="false"/>

    Next, add two SPDataSource controls:

       1: <SharePoint:SPDataSource runat="server" DataSourceMode="List" SelectCommand="&lt;View&gt;&lt;/View&gt;" ID="parentDataSource">
       2:     <SelectParameters>
       3:         <asp:Parameter Name="ListID" DefaultValue="{828362D7-E519-4522-86AF-DF90AC023719}"/>
       4:     </SelectParameters>
       5: </SharePoint:SPDataSource>
       6:  
       7: <SharePoint:SPDataSource runat="server" DataSourceMode="List" SelectCommand="&lt;View&gt;&lt;Query&gt;&lt;Where&gt;&lt;Eq&gt;&lt;FieldRef Name=&quot;Parent&quot;/&gt;&lt;Value Type=&quot;Lookup&quot;&gt;{parent}&lt;/Value&gt;&lt;/Eq&gt;&lt;/Where&gt;&lt;/Query&gt;&lt;/View&gt;" ID="childDataSource">
       8:     <SelectParameters>
       9:         <asp:Parameter Name="ListID" DefaultValue="{F83375F4-D4EB-48AF-8CD9-5E773BC7692F}"/>
      10:         <asp:ControlParameter ControlID="parentList" PropertyName="SelectedValue" Name="parent" />
      11:     </SelectParameters>
      12: </SharePoint:SPDataSource>

    Pay attention to a couple of things:

    • We are retrieving lists from the same site, but it is possible to get them from different sites as well, just change the DataSourceMode property and the SelectCommand accordingly;
    • You need to replace the ListID values in each Parameter for the proper ones: the GUID of the Parent list and the GUID of the Child list;
    • I am using the standard ID and Title columns, but you can use whatever you want;
    • If you give other name to the lookup field other than Parent, you need to change the SelectCommand property accordingly (FieldRef element);
    • You can give any name you like to the DropDownList controls, but if you change the name of the parent one, don’t forget to update the ControlID property of the ControlParameter in the child’s SPDataSource;
    • For editing the child SPDataSource’s SelectCommand property, you should use the Tag Properties window, because the XML there is not encoded;
    • Disable View State for the child DropDownList by setting EnableViewState to false;
    • Leave AutoPostBack set to true for the parent DropDownList, this causes the page to reload when the parent changes.

    Improvements

    If you prefer to have the parent DropDownList initially unselected, use the following markup instead:

       1: <asp:DropDownList runat="server" ID="parentList" DataSourceID="parentDataSource" DataTextField="Title" DataValueField="ID" AutoPostBack="true" Width="200px" AppendDataBoundItems="true">
       2:     <asp:ListItem Selected="true" Text="" Value="-1"/>
       3: </asp:DropDownList>

    AppendDataBoundItems makes sure that any items added from a data source control are appended to any existing ones.

    Another useful improvement would be to wrap all controls inside an UpdatePanel. This way, when the parent DropDownList changes, it won’t cause a full page postback.

    Of course, you can have any number of nested DropDownList controls, just follow the same logic.

    Read more...

  • New Features in SharePoint Designer 2013 Workflows

    SharePoint Designer 2013 workflows brought along new and quite welcome features. I will list my favorite ones.

    First, the ability to define stages (blocks of instructions) and to jump (yes, goto-style!) between them. This permits a better organization of our code and because of conditional jumps, even includes the ability to reuse code and to loop between stages.

    image

    Next one is stage-level loops. We have two flavors: one where the loop count is predefined from an Integer variable and the other where a condition is used to determine its end.

    image

    Then we have a new type of variables, Dictionary, and three associated actions: build a dictionary with values, get an item from a dictionary and count the number of items in it. Each Dictionary item can be of a different type, and it is even possible to have items of type Dictionary. A Dictionary can be indexed by its key or by position, which is very useful to use inside loops.

    image

    It is now possible to start list and site workflows, but only SharePoint 2010 are supported. Workflows can be started synchronously, in which case, the originating workflow will wait for the result, or asynchronously, aka, fire and forget. Depending on the workflow selected, it may be necessary to specify values for its parameters.

    Also new is the ability to assign tasks and start approval processes.

    image


    image

    A perhaps not so used one is the capability to start a document translation process. The name is misleading, since it can also be used to translate list fields. The result translation is stored in another list.

    Finally, we have the ability to call an HTTP web service. This will mostly used to call REST-style web services, but nothing prevents us from calling SOAP, since we can build a SOAP request using the string utility actions that the Designer offers. We can specify both request contents and headers, and retrieve the result, headers and HTTP status code. The problem is, SharePoint Designer workflows can only process results coming as JSON, not XML, but there are a number of translation web services that can be used to turn XML into JSON.

    image

    Finally,

    All in all, very useful additions! Smile

    Read more...

  • Looping Through List Items in SharePoint 2013 Designer Workflows

    SharePoint 2013 Designer workflows now has two new interesting options: the ability to call HTTP web services and the option to loop over some code a number of times. This, together with the new REST API, which supports querying lists and returning data in JSON, allows iterating through list items in a workflow, something that was not possible before.

    In order to demonstrate this, let’s create a new Site Workflow in SharePoint Designer, that will iterate through the Tasks list:

    image

    Call it Process Tasks, for example, and make sure you select SharePoint 2013 as the platform type.

    In the workflow designer, let’s start by creating a new stage, call it Retrieve Tasks:

    image

    In it, we add a new Set Workflow Variable action which creates a new String variable called url with the value “http://sp2013/_api/web/lists/getbytitle('Tasks')/items”. This uses the new REST API, and you can pass in additional options, such as for ordering by the DueDate field in descending order:

    http://sp2013/_api/web/lists/getbytitle('Tasks')/items?$orderby=DueDate desc

    or filtering:

    http://sp2013/_api/web/lists/getbytitle('Tasks')/items?$filter=DueDate gt DateTime’2014-07-31T00:00:00’

    Next, we add a Dictionary variable (Build a Dictionary action), call it requestHeaders, and initialize it as this:

    image

    Both “Accept” and “Content-Type” entries are of the String type and they both contain the value “application/json;odata=verbose”, SharePoint REST API understands this and sets the response content type appropriately as JSON. If we don’t pass these values, the output would come as XML.

    Following, add an Call an HTTP Web Service action and set its properties. The request will be the url variable:

    image

    Response content will go to a new variable called responseContent:

    image

    Response headers will go to a new variable called responseHeaders:

    image

    And the same goes for the response code (variable responseCode):

    image

    Then we set the request headers to be the requestHeaders variable we created just now, by clicking on the properties for the Call an HTTP Web Service action:

    image

    Now, create a new stage, call it Process Tasks, and, at the end of the initial stage, add a Go to Process Tasks action.

    On the Process Tasks stage, add a Get an Item from a Dictionary action, set the item as d/results, the source variable reponseContent and the output to a new variable of type Dictionary called list. Then count items from this list variable using a Count Items in Dictionary action and store the result in a new Integer variable called count. This variable will tell us how many times we have to loop. Finally, create a new Integer variable called index and set it to 0 (Set Workflow Variable), this will be the loop index.

    Next, add a loop (Loop n Times), call it Loop Task Items, and set the loop variable to count. Inside the loop, get value d/results([%Variable: index%]) using a Get an Item from a Dictionary action from responseContent and store it in a new Dictionary variable called item. Get some fields (Get an Item from a Dictionary) from the item variable, such as Title and DueDate  (mind you, these will be task item fields) and store them in appropriate variables and do whatever you want with them, like logging its contents (Log). Time to increment the loop counter: add a Do Calculation action and in it increment the index variable into a new Integer variable called indexPlusOne. Then set the index variable to be the indexPlusOne (Set Workflow Variable). Finally, exit the loop and set the workflow status (Set Workflow Status action) to Finished. At the end of the stage, select Go to End of Workflow.

    That’s it. Your workflow should look like this:

    image

    The new functionality makes SharePoint Designer workflows much more powerful than before. I will continue to talk about it in the following posts.

    Read more...

  • Adding Suggestions to the SharePoint 2010 Search Programatically

    There are numerous pages that show how to do this with PowerShell, but I found none on how to do it with plain old C#, so here it goes!

    To abbreviate, I wanted to have SharePoint suggest the site collection user’s names after the first letters are filled in a search site’s search box. Here’s how I did it:

       1: //get the Search Service Application (replace with your own name)
       2: SearchServiceApplication searchApp = farm.Services.GetValue<SearchQueryAndSiteSettingsService>().Applications.GetValue<SearchServiceApplication>("Search Service Application") as SearchServiceApplication;
       3:   
       4: Ranking ranking = new Ranking(searchApp);
       5:  
       6: //replace EN-US with your language of choice
       7: LanguageResourcePhraseList suggestions = ranking.LanguageResources["EN-US"].QuerySuggestionsAlwaysSuggestList;
       8:  
       9: foreach (SPUser user in rootWeb.Users)
      10: {
      11:     suggestions.AddPhrase(user.Name, String.Empty);
      12: }
      13:  
      14: //get the job that processes suggestions and run it 
      15: SPJobDefinition job = SPFarm.Local.Services.OfType<SearchService>().SelectMany(x => x.JobDefinitions).Where(x => x.Name == "Prepare query suggestions").Single();
      16: job.RunNow();

    You may do this, for example, on a feature. Of course, feel free to change users for something else, all suggestions are treated as pure text.

    Read more...

  • More SharePoint 2010 Expression Builders

    Introduction

    Following my last post, I decided to publish the whole set of expression builders that I use with SharePoint. For all who don’t know about expression builders, they allow us to employ a declarative approach, so that we don’t have to write code for “gluing” things together, like getting a value from the query string, the page’s underlying SPListItem or the current SPContext and assigning it to a control’s property.

    These expression builders are for some quite common scenarios, I use them quite often, and I hope you find them useful as well.

    SPContextExpression

    This expression builder allows us to specify an expression to be processed on the SPContext.Current property object. For example:

       1: <asp:Literal runat="server" Text=“<%$ SPContextExpression:Site.RootWeb.Lists[0].Author.LoginName %>”/>

    It is identical to having the following code:

       1: String authorName = SPContext.Current.Site.RootWeb.Lists[0].Author.LoginName;

    SPFarmProperty

    Returns a property stored on the farm level:

       1: <asp:Literal runat="server" Text="<%$ SPFarmProperty:SomeProperty %>"/>

    Identical to:

       1: Object someProperty = SPFarm.Local.Properties["SomeProperty"];

    SPField

    Returns the value of a selected page’s list item field:

       1: <asp:Literal runat="server" Text="<%$ SPField:Title %>"/>

    Does the same as:

       1: String title = SPContext.Current.ListItem["Title"] as String;

    SPIsInAudience

    Checks if the current user belongs to an audience:

       1: <asp:CheckBox runat="server" Checked="<%$ SPIsInAudience:SomeAudience %>"/>

    Equivalent to:

       1: AudienceManager audienceManager = new AudienceManager(SPServiceContext.Current);
       2: Audience audience = audienceManager.Audiences["SomeAudience"];
       3: Boolean isMember = audience.IsMember(SPContext.Current.Web.User.LoginName);

    SPIsInGroup

    Checks if the current user belongs to a group: 
       1: <asp:CheckBox runat="server" Checked="<%$ SPIsInGroup:SomeGroup %>"/>

    The equivalent C# code is:

       1: SPContext.Current.Web.CurrentUser.Groups.OfType<SPGroup>().Any(x => String.Equals(x.Name, “SomeGroup”, StringComparison.OrdinalIgnoreCase));

    SPProperty

    Returns the value of a user profile property for the current user: 
       1: <asp:Literal runat="server" Text="<%$ SPProperty:LastName %>"/>

    Where the same code in C# would be:

       1: UserProfileManager upm = new UserProfileManager(SPServiceContext.Current);
       2: UserProfile u = upm.GetUserProfile(false);
       3: Object property = u["LastName"].Value;

    SPQueryString

    Returns a value passed on the query string:
       1: <asp:GridView runat="server" PageIndex="<%$ SPQueryString:PageIndex %>" />

    Is equivalent to (no SharePoint code this time):

       1: Int32 pageIndex = Convert.ChangeType(typeof(Int32), HttpContext.Current.Request.QueryString["PageIndex"]);

    SPWebProperty

    Returns the value of a property stored at the site level:
       1: <asp:Literal runat="server" Text="<%$ SPWebProperty:__ImagesListId %>"/>

    You can get the same result as:

       1: String imagesListId = SPContext.Current.Web.AllProperties["__ImagesListId"] as String;

    Code

    OK, let’s move to the code. First, a common abstract base class, mainly for inheriting the conversion method:

       1: public abstract class SPBaseExpressionBuilder : ExpressionBuilder
       2: {
       3:     #region Protected static methods
       4:     protected static Object Convert(Object value, PropertyInfo propertyInfo)
       5:     {
       6:         if (value != null)
       7:         {
       8:             if (propertyInfo.PropertyType.IsAssignableFrom(value.GetType()) == false)
       9:             {
      10:                 if (propertyInfo.PropertyType.IsEnum == true)
      11:                 {
      12:                     value = Enum.Parse(propertyInfo.PropertyType, value.ToString(), true);
      13:                 }
      14:                 else if (propertyInfo.PropertyType == typeof(String))
      15:                 {
      16:                     value = value.ToString();
      17:                 }
      18:                 else if ((typeof(IConvertible).IsAssignableFrom(propertyInfo.PropertyType) == true) && (typeof(IConvertible).IsAssignableFrom(value.GetType()) == true))
      19:                 {
      20:                     value = System.Convert.ChangeType(value, propertyInfo.PropertyType);
      21:                 }
      22:             }
      23:         }
      24:  
      25:         return (value);
      26:     }
      27:     #endregion
      28:  
      29:     #region Public override methods
      30:     public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context)
      31:     {            
      32:         if (String.IsNullOrEmpty(entry.Expression) == true)
      33:         {
      34:             return (new CodePrimitiveExpression(String.Empty));
      35:         }
      36:         else
      37:         {
      38:             return (new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(this.GetType()), "GetValue"), new CodePrimitiveExpression(entry.Expression.Trim()), new CodePropertyReferenceExpression(new CodeArgumentReferenceExpression("entry"), "PropertyInfo")));
      39:         }
      40:     }
      41:     #endregion
      42:  
      43:     #region Public override properties
      44:     public override Boolean SupportsEvaluate
      45:     {
      46:         get
      47:         {
      48:             return (true);
      49:         }
      50:     }
      51:     #endregion
      52: }

    Next, the code for each expression builder:

       1: [ExpressionPrefix("SPContext")]
       2: public class SPContextExpressionBuilder : SPBaseExpressionBuilder
       3: {
       4:     #region Public static methods
       5:     public static Object GetValue(String expression, PropertyInfo propertyInfo)
       6:     {
       7:         SPContext context = SPContext.Current;
       8:         Object expressionValue = DataBinder.Eval(context, expression.Trim().Replace('\'', '"'));
       9:  
      10:         expressionValue = Convert(expressionValue, propertyInfo);
      11:  
      12:         return (expressionValue);
      13:     }
      14:  
      15:     #endregion
      16:  
      17:     #region Public override methods
      18:     public override Object EvaluateExpression(Object target, BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context)
      19:     {
      20:         return (GetValue(entry.Expression, entry.PropertyInfo));
      21:     }
      22:     #endregion
      23: }

     

       1: [ExpressionPrefix("SPFarmProperty")]
       2: public class SPFarmPropertyExpressionBuilder : SPBaseExpressionBuilder
       3: {
       4:     #region Public static methods
       5:     public static Object GetValue(String propertyName, PropertyInfo propertyInfo)
       6:     {
       7:         Object propertyValue = SPFarm.Local.Properties[propertyName];
       8:  
       9:         propertyValue = Convert(propertyValue, propertyInfo);
      10:  
      11:         return (propertyValue);
      12:     }
      13:  
      14:     #endregion
      15:  
      16:     #region Public override methods
      17:     public override Object EvaluateExpression(Object target, BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context)
      18:     {
      19:         return (GetValue(entry.Expression, entry.PropertyInfo));
      20:     }
      21:     #endregion
      22: }

     

       1: [ExpressionPrefix("SPField")]
       2: public class SPFieldExpressionBuilder : SPBaseExpressionBuilder
       3: {
       4:     #region Public static methods
       5:     public static Object GetValue(String fieldName, PropertyInfo propertyInfo)
       6:     {
       7:         Object fieldValue = SPContext.Current.ListItem[fieldName];
       8:  
       9:         fieldValue = Convert(fieldValue, propertyInfo);
      10:  
      11:         return (fieldValue);
      12:     }
      13:  
      14:     #endregion
      15:  
      16:     #region Public override methods
      17:     public override Object EvaluateExpression(Object target, BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context)
      18:     {
      19:         return (GetValue(entry.Expression, entry.PropertyInfo));
      20:     }
      21:     #endregion
      22: }

     

       1: [ExpressionPrefix("SPIsInAudience")]
       2: public class SPIsInAudienceExpressionBuilder : SPBaseExpressionBuilder
       3: {
       4:     #region Public static methods
       5:     public static Object GetValue(String audienceName, PropertyInfo info)
       6:     {
       7:         Debugger.Break();
       8:         audienceName = audienceName.Trim();
       9:  
      10:         if ((audienceName.StartsWith("'") == true) && (audienceName.EndsWith("'") == true))
      11:         {
      12:             audienceName = audienceName.Substring(1, audienceName.Length - 2);
      13:         }
      14:  
      15:         AudienceManager manager = new AudienceManager();
      16:         Object value = manager.IsMemberOfAudience(SPControl.GetContextWeb(HttpContext.Current).CurrentUser.LoginName, audienceName);
      17:  
      18:         if (info.PropertyType == typeof(String))
      19:         {
      20:             value = value.ToString();
      21:         }
      22:  
      23:         return(value);
      24:     }
      25:  
      26:     #endregion
      27:  
      28:     #region Public override methods
      29:     public override Object EvaluateExpression(Object target, BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context)
      30:     {
      31:         return (GetValue(entry.Expression, entry.PropertyInfo));
      32:     }
      33:     #endregion
      34: }

     

       1: [ExpressionPrefix("SPIsInGroup")]
       2: public class SPIsInGroupExpressionBuilder : SPBaseExpressionBuilder
       3: {
       4:     #region Public static methods
       5:     public static Object GetValue(String groupName, PropertyInfo info)
       6:     {
       7:         groupName = groupName.Trim();
       8:  
       9:         if ((groupName.StartsWith("'") == true) && (groupName.EndsWith("'") == true))
      10:         {
      11:             groupName = groupName.Substring(1, groupName.Length - 2);
      12:         }
      13:  
      14:         Object value = SPControl.GetContextWeb(HttpContext.Current).CurrentUser.Groups.OfType<SPGroup>().Any(x => String.Equals(x.Name, groupName, StringComparison.OrdinalIgnoreCase));
      15:  
      16:         if (info.PropertyType == typeof(String))
      17:         {
      18:             value = value.ToString();
      19:         }
      20:  
      21:         return(value);
      22:     }
      23:  
      24:     #endregion
      25:  
      26:     #region Public override methods
      27:     public override Object EvaluateExpression(Object target, BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context)
      28:     {
      29:         return (GetValue(entry.Expression, entry.PropertyInfo));
      30:     }
      31:     #endregion
      32: }

     

       1: [ExpressionPrefix("SPProperty")]
       2: public class SPPropertyExpressionBuilder : SPBaseExpressionBuilder
       3: {
       4:     #region Public static methods
       5:     public static Object GetValue(String propertyName, System.Reflection.PropertyInfo propertyInfo)
       6:     {
       7:         SPServiceContext serviceContext = SPServiceContext.GetContext(HttpContext.Current);
       8:         UserProfileManager upm = new UserProfileManager(serviceContext);
       9:         UserProfile up = upm.GetUserProfile(false);
      10:         Object propertyValue = (up[propertyName] != null) ? up[propertyName].Value : null;
      11:  
      12:         propertyValue = Convert(propertyValue, propertyInfo);
      13:  
      14:         return (propertyValue);
      15:     }
      16:  
      17:     #endregion
      18:  
      19:     #region Public override methods
      20:     public override Object EvaluateExpression(Object target, BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context)
      21:     {
      22:         return (GetValue(entry.Expression, entry.PropertyInfo));
      23:     }
      24:     #endregion
      25: }

     

       1: [ExpressionPrefix("SPQueryString")]
       2: public class SPQueryStringExpressionBuilder : SPBaseExpressionBuilder
       3: {
       4:     #region Public static methods
       5:     public static Object GetValue(String parameterName, PropertyInfo propertyInfo)
       6:     {
       7:         Object parameterValue = HttpContext.Current.Request.QueryString[parameterName];
       8:  
       9:         parameterValue = Convert(parameterValue, propertyInfo);
      10:  
      11:         return (parameterValue);
      12:     }
      13:  
      14:     #endregion
      15:  
      16:     #region Public override methods
      17:     public override Object EvaluateExpression(Object target, BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context)
      18:     {
      19:         return (GetValue(entry.Expression, entry.PropertyInfo));
      20:     }
      21:     #endregion
      22: }

     

       1: [ExpressionPrefix("SPWebProperty")]
       2: public class SPWebPropertyExpressionBuilder : SPBaseExpressionBuilder
       3: {
       4:     #region Public static methods
       5:     public static Object GetValue(String propertyName, PropertyInfo propertyInfo)
       6:     {
       7:         Object propertyValue = SPContext.Current.Web.AllProperties[propertyName];
       8:  
       9:         propertyValue = Convert(propertyValue, propertyInfo);
      10:  
      11:         return (propertyValue);
      12:     }
      13:  
      14:     #endregion
      15:  
      16:     #region Public override methods
      17:     public override Object EvaluateExpression(Object target, BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context)
      18:     {
      19:         return (GetValue(entry.Expression, entry.PropertyInfo));
      20:     }
      21:     #endregion
      22: }

    Registration

    You probably know how to register them, but here it goes again: add this following snippet to your Web.config file, inside the configuration/system.web/compilation/expressionBuilders section:

       1: <add expressionPrefix="SPContext" type="MyNamespace.SPContextExpressionBuilder, MyAssembly, Culture=neutral, Version=1.0.0.0, PublicKeyToken=xxx" />
       2: <add expressionPrefix="SPFarmProperty" type="MyNamespace.SPFarmPropertyExpressionBuilder, MyAssembly, Culture=neutral, Version=1.0.0.0, PublicKeyToken=xxx" />
       3: <add expressionPrefix="SPField" type="MyNamespace.SPFieldExpressionBuilder, MyAssembly, Culture=neutral, Version=1.0.0.0, PublicKeyToken=xxx" />
       4: <add expressionPrefix="SPIsInAudience" type="MyNamespace.SPIsInAudienceExpressionBuilder, MyAssembly, Culture=neutral, Version=1.0.0.0, PublicKeyToken=xxx" />
       5: <add expressionPrefix="SPIsInGroup" type="MyNamespace.SPIsInGroupExpressionBuilder, MyAssembly, Culture=neutral, Version=1.0.0.0, PublicKeyToken=xxx" />
       6: <add expressionPrefix="SPProperty" type="MyNamespace.SPPropertyExpressionBuilder, MyAssembly, Culture=neutral, Version=1.0.0.0, PublicKeyToken=xxx" />
       7: <add expressionPrefix="SPQueryString" type="MyNamespace.SPQueryStringExpressionBuilder, MyAssembly, Culture=neutral, Version=1.0.0.0, PublicKeyToken=xxx" />
       8: <add expressionPrefix="SPWebProperty" type="MyNamespace.SPWebPropertyExpressionBuilder, MyAssembly, Culture=neutral, Version=1.0.0.0, PublicKeyToken=xxx" />

    I’ll leave it up to you to figure out the best way to deploy this to your server! Winking smile

    Read more...

  • SharePoint 2010 Field Expression Builder

    OK, back to two of my favorite topics, expression builders and SharePoint. This time I wanted to be able to retrieve a field value from the current page declaratively on the markup so that I can assign it to some control’s property, without the need for writing code. Of course, the most straight way to do it is through an expression builder.

    Here’s the code:

       1: [ExpressionPrefix("SPField")]
       2: public class SPFieldExpressionBuilder : ExpressionBuilder
       3: {
       4:     #region Public static methods
       5:     public static Object GetFieldValue(String fieldName, PropertyInfo propertyInfo)
       6:     {
       7:         Object fieldValue = SPContext.Current.ListItem[fieldName];
       8:  
       9:         if (fieldValue != null)
      10:         {
      11:             if ((fieldValue is IConvertible) && (typeof(IConvertible).IsAssignableFrom(propertyInfo.PropertyType) == true))
      12:             {
      13:                 if (propertyInfo.PropertyType.IsAssignableFrom(fieldValue.GetType()) != true)
      14:                 {
      15:                     fieldValue = Convert.ChangeType(fieldValue, propertyInfo.PropertyType);
      16:                 }
      17:             }
      18:         }
      19:  
      20:         return (fieldValue);
      21:     }
      22:  
      23:     #endregion
      24:  
      25:     #region Public override methods
      26:     public override Object EvaluateExpression(Object target, BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context)
      27:     {
      28:         return (GetFieldValue(entry.Expression, entry.PropertyInfo));
      29:     }
      30:  
      31:     public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context)
      32:     {
      33:         if (String.IsNullOrEmpty(entry.Expression) == true)
      34:         {
      35:             return (new CodePrimitiveExpression(String.Empty));
      36:         }
      37:         else
      38:         {
      39:             return (new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(this.GetType()), "GetFieldValue"), new CodePrimitiveExpression(entry.Expression), new CodePropertyReferenceExpression(new CodeArgumentReferenceExpression("entry"), "PropertyInfo")));
      40:         }
      41:     }
      42:  
      43:     #endregion
      44:  
      45:     #region Public override properties
      46:     public override Boolean SupportsEvaluate
      47:     {
      48:         get
      49:         {
      50:             return (true);
      51:         }
      52:     }
      53:     #endregion
      54: }

    You will notice that it will even try to convert the field value to the target property’s type, through the use of the IConvertible interface and the Convert.ChangeType method.

    It must be placed on the Global Assembly Cache or you will get a security-related exception. The other alternative is to change the trust level of your web application to full trust.

    Here’s how to register it on Web.config:

       1: <expressionBuilders>
       2:     <!-- ... -->
       3:     <add expressionPrefix="SPField" type="MyNamespace.SPFieldExpressionBuilder, MyAssembly, Culture=neutral, Version=1.0.0.0, PublicKeyToken=29186a6b9e7b779f" />
       4: </expressionBuilders>

    And finally, here’s how to use it on an ASPX or ASCX file inside a publishing page:

       1: <asp:Label runat="server" Text="<%$ SPField:Title %>"/>

    Read more...

  • Fixing SharePoint 2010 Permission Problems on Windows 7

    I had a tough time trying to have SharePoint working perfectly on a Windows 7 development machine that was occasionally disconnected from the Active Directory (when I am home I must connect through a VPN). I mostly had problems with service applications such as User Profile, Managed Metadata, Business Connectivity Services and the like, and all I knew were cryptical messages such as “access denied” or “the service or application pool is not started”. I was sure that both the services and application pools were running under a domain account that had proper permissions on the SQL Server instance, and basically it was a fresh installation. Lots of people are having the same problem, apparently. After banging my head against the wall for several days, I remembered about farm (what I had) versus stand-alone (which I had never tried) installations. Bingo!

    Here’s what I did: I dropped all SharePoint databases and logins and reinstalled SP from scratch, only this time not in farm mode, but as stand-alone. After the SharePoint Configuration Wizard started, I cancelled it and started the Management Shell. I created the configuration database manually by using the New-SPConfigurationDatabase cmdlet where I specified a local account – something that the Configuration Wizard wouldn’t allow me to do. Then I restarted the Configuration Wizard and everything began working perfectly! Yes, I got some pre-configured service applications and also some content which I didn’t need, but I realized it was possible to drop and recreate everything the way I wanted to. All services and application pools are now running under local accounts, which is fine for my development needs. Really, Microsoft… Sad smile

    I hope this will bring light to someone facing the same problems!

    Read more...

  • What Makes SharePoint SharePoint

    SharePoint is ASP.NET, it is well known. Yet, sometimes, it may be difficult to understand why/how some features work, because we don’t usually see them in regular ASP.NET development. However, all of them are build upon standard ASP.NET/IIS functionality, which you can easily duplicate on non-SharePoint environments. Here are some clues.

    Read more...

  • SharePoint 2010 Leiria

    Ontem ocorreu o primeiro evento SharePoint 2010 Leiria, organizado pelo David Frazão, que me convidou, e no qual tive a honra de participar. Estavam presentes cerca de 12 pessoas, com experiências muito variadas de utilização do SharePoint. Foi muito interessante, sobretudo, se se vier a repetir!

    Read more...