Passionate about agile, web and mobile UruIT Blog

UruIT Blog

A nearshore dev team passionate about agile, web and mobile

Sponsors

News

UruIT at WEBLOGS asp.net

Cross-protocol login control with JSONP in SharePoint

Overview

This article describes the specific problem of creating a control in SharePoint, which will be located on an HTTP page and securely signs in a user through an SSL connection using HTTPS. Although the solution uses SharePoint exclusively, it can be easily modified for ASP.NET environments.

The problem

With the requirement that the user data should transferred securely to the server, the idea of a postback becomes unfeasible. At that point one might come with the idea of an asynchronous and secure request that carries the user data.

Those of you have worked with AJAX, might have stumbled upon asynchronous requests with jQuery more than once. While this type of requests allows building very rich internet applications, most of them have a key restriction regarding protocols and domains. Common requests cannot retrieve data from a different domain than the one hosting the application, and the protocol for the request must also be the same as the one used to retrieve the page.

Knowing of these limitations, our mind is focused to those AJAX requests that allow cross-protocol calls.

JSONP at the rescue

JSONP is an extension to the well known standard JSON, where the added "P" stands for "Padding". The padding is usually a JavaScript function call (might be any JavaScript code too) that is added by the server as a prefix to the data returned. These are two requests and responses from a server in both, JSON and JSONP formats:

JSON request:

        http://example.com/planets?json=true&planetid=4

JSON response:

        {
            "id" : 4,
            "name" : "Mars",
            "satellites" : 2
        }

JSONP request:

        http://example.com/planets?jsonp=true&planetid=4&jsonpcallback=callthisfunction

JSONP response:

        callthisfunction({            
            "id" : 4,
            "name" : "Mars",
            "satellites" : 2
        })     

As you can see, the only differences between both requests, is the "jsonpcallback" parameter, which is used as a function call to wrap the data in the response. This parameter can actually have any name, but in order to make it work with jQuery, it needs to end with the suffix "callback".

So, when a web page has a script tag with the previous request as the source, JSONP comes to life

        <script type="text/javascript" src="http://example.com/planets?jsonp=true&planetid=4&jsonpcallback=callthisfunction"></script>

Script tags can bring scripts from any domain, using any protocol. Using the previous script tags, not only brings the data from the server, but also calls

the callback function, allowing the client to run any desired code.

 

Putting all together

Now that we know the power of JSONP, we can use it to design our solution. On the client side, we will use a web part that uses jQuery to perform the request. On the server side, we will have an ASP.NET handler (.ashx) that logs the user in, or returns an error if the user or password supplied are invalid.

Let's begin with the server side. To publish the handler from SharePoint, we need the ashx file to look something like this:

    <%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Assembly Name="MyNamespace", Version=1.0.0.0, Culture=neutral, PublicKeyToken=a39c10d65c32c9c1, processorArchitecture=MSIL" %> 
    <%@ WebHandler Language="C#" Class="MyNamespace.MyHandler" %>
 

For the code file, we will use the SharePoint method "SPClaimsUtility.AuthenticateFormsUser" to perform the login. But also, we have to return a JSONP

response indicating whether that method succeeded or not. Let's see the easiest way of doing it:

    public partial class MyHandler : IHttpHandler
    {
        public bool IsReusable
        {
            get { throw new NotImplementedException(); }
        }
 
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/html";
            context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
            // Parse the JSON callback
            string callback = context.Request["callback"] ?? string.Empty;
            callback = Regex.Replace(callback, @"[^a-zA-Z0-9\?]", "");
            string user = context.Request["User"];
            string password = context.Request["Password"];
            if (SPClaimsUtility.AuthenticateFormsUser(context.Request.Url, user, password))
            {
                context.Response.Write(callback + "({ \"result\" : true });");
            }
            else
            {
                context.Response.Write(callback + "({ \"result\" : false });");
            }
        }
    }

From this code, you can see how we parse the "callback" parameter, parsing the basic characters on it, and we use it to send the response to the client, which is an object containing only the "result" boolean property.

Another thing interesting in this code is the "Access-Control-Allow-Origin" header, which filters the domains that can request data from this server. By using the character "*", we are allowing any domain to request data from our server.

Now, let's see the client side. When a user enters his information in the "txtUser" and "txtPassword" fields, this script does the trick:

        function onLogin(evt, message) {
            var user = $("#txtUser").attr("value");
            var password = $("#txtPassword").attr("value");
            $.getJSON(
                https://example.com/_layouts/MyNamespace/MyHandler.ashx?callback=?,
                {
                    User: user,
                    Password: password
                },
                function (data) {
                    if (data.result) {
                        // Login succeeded, redirect to home page:
                        window.location = "https://example.com/default.aspx"; 
                    }
                    else {
                        // Login failed, show error message without leaving the page:
                        document.getElementById('lblLoginResult').innerHTML = "Wrong Userid or Password";
                    }
                }
            );
        }

 

To perform a JSONP request in jQuery, we just perform a normal JSON request. jQuery automatically detects the string "callback=?" in the url and

performs the request as JSONP. The function we supplied as the callback, is called with a JavaScript object, which is parsed by jQuery from the response

sent by the server.

We now have both sides working. We have built a login control that works on every browser, protects the users' security, and efficiently uses

HTTP/HTTPS protocols.

 

Error handling

Although some of the errors can be seen on the browser's error console, there is no way to handle JSONP errors in JavaScript. This is one of the main drawbacks of the protocol. When an error occurs, it just fails silently.

 

Security concerns

As we saw, JSONP is a powerful tool that makes possible some scenarios that would otherwise be unfeasible. However, it should be used with extreme care, especially with cross domain requests. Remember that the padding is nothing more than JavaScript code that will be run by the user's browser. This gives the external server, the possibility to send a malicious script to start an attack.

 

Conclusions

We have seen JSONP in action, resolving a specific problem in a very simple way. However, we also saw the drawbacks of this approach. Like any other tool, it is useful to keep in mind how powerful it is, the kind of problems it addresses, and the weak points it presents.

 

Thanks for reading!

Alfonso Cora

Introducing PhoneSocial+ for Windows Phone by UruIT
marketplace Download PhoneSocial+ from the marketplace
pspluslogo

PhoneSocial+ is a new application for Windows Phone that lets you read your tweets from your phone. With PhoneSocial+ you will be able to:

  • Connect to your Twitter account to see your tweets, mentions list, inbox and favorites.
  • Listen to your tweets and private messages.
  • Send tweets, private messages, reply, or retweet them.
  • Add tweets to favorites to review them later.
psplus1 psplus2 psplus3
marketplace Download PhoneSocial+ from the marketplace
twitter Follow PhoneSocial+ on Twitter!
Grouping items in a Silverlight ListBox

[Click here to download sample code for this article]

Introduction

ListBox grouping is one of the features that Silverlight did not inherit from WPF, even though is a common requirement. Here we present a very simple yet powerful way to implement grouping in the ListBox control that also allows subgrouping and sorting.

This approach is supported on traditional Silverlight applications, and also on Windows Phone 7 applications.

 

Untitled2 Untitled

 

The problem

Although WPF supports grouping for the ListBox control, Silverlight doesn’t. The developer is restricted to load a plain, unstructured collection of items. However, the hierarchical scenario is very common in applications that display different types of data.

The solution

To overcome this issue, we will use a Binding Converter, which will group the items internally, adding to the ListBox a ContentControl element with a specific template, for each group header it creates, and a ContentControl with another template, for each item on each group it creates. Sounds complicated? It’s not, let’s do it!

Suppose you have a list of items that you want to display hierarchically. You proceed by creating the ListBox and binding its ItemsSource dependency property to the collection of items. In XAML it would look like this:

<ListBox x:Name="List" ItemsSource="{Binding}"/>

And the code-behind would look like this:

IEnumerable<ItemType> items = ...;

List.DataContext = items;

At this point, the ListBox shows the list of items without any structure. Now, let’s add a Converter to the binding. A converter is an instance of a class that implements IValueConterter, which has only two methods: Convert and ConvertBack. Before Silverlight binds the collection of items, it will call the Convert function, allowing us to transform the data as we want. The ConvertBack method will never be called for this type of binding, since this is a OneWay binding. For more information about data bindings, see http://msdn.microsoft.com/en-us/library/ms752347.aspx

So, all we need is a new class that implements the IValueConverter interface. In this case, “PetGrouper”:

public class PetGrouper : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // ...
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Because ConvertBack will never be called, we can leave it untouched, throwing a NotImplementedException. For the Convert method, we already know the type of data we are assigning to the DataContext. That same instance comes as an object in the value parameter. We can ignore the rest of the parameters in this case.

Next, we have to group and sort the data. For each group we create, we will add a control with a template designed for headers, and for each one of its items, we will add a control with a template designed for items.

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    IEnumerable<Pet> pets = value as IEnumerable<Pet>;
    List<ContentControl> groupedPets = new List<ContentControl>();
    foreach (IGrouping<string, Pet> animal in pets.GroupBy(x => x.Animal).OrderBy(x => x.Key))
    {
        groupedPets.Add(new ContentControl() { Content = animal.Key, ContentTemplate = App.Current.Resources["AnimalTemplate"] as DataTemplate });
        foreach (Pet pet in animal.OrderBy(x => x.Breed))
        {
            groupedPets.Add(new ContentControl() { Content = pet, ContentTemplate = App.Current.Resources["BreedTemplate"] as DataTemplate });
        }
    }
    return groupedPets;
}

AnimalTemplate and BreedTemplate are resources in App.xaml. In this example we are grouping the pets by animal type, sorting the groups by animal type name, and sorting each item on the groups by breed name.

The type IGrouping<string, Pet>, describes a list of groups whose headers (“keys”) are strings, and each one of them is populated by pets. The Key type may vary for different scenarios. For example if we were grouping by vertebrates and invertebrates, the Key would be a bool value, and we would have to add a significant text to the header content, so it doesn’t show “True” and “False”.

As you may note, this approach is unrestricted as to how the data is shown. You can group items on several levels, sort the items by any property, and even sort the groups themselves.

Finally, we have to link our converter to the binding. This is done by adding a resource in the page containing the ListBox, and linking the binding to that resource:

<UserControl.Resources>
    <local:PetGrouper x:Key="PetValueConverter"></local:PetGrouper>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
    <ListBox x:Name="List" ItemsSource="{Binding Converter={StaticResource PetValueConverter}}" />
</Grid>

Sample application

Here is a sample application that implements grouping and sorting in a ListBox.

The solution contains two projects: a traditional browser application, and a Windows Phone 7 application.

Conclusion

Even the Silverlight ListBox does not have native support for structured data; we can use a binding converter to customize the binding, creating the layout that best fits our case. We have seen that this approach is simple, straightforward, and yet incredible powerful, and it’s supported on classic Silverlight applications and Phone applications.

 

Alfonso Cora

Consuming OData from IPHONE

Hi,

We have seen in the previous post how to consume OData services from Windows Phone 7, this time I want to show you how to do it from an iPhone.

1. To consume OData from an iPhone App I have used a library called “OData Client for Objective-C”. It can be downloaded from here: http://odataobjc.codeplex.com/

The library contains some examples using the Netflix Catalog that can help you get familiar with more advanced stuffs than what are going to be explained in this article.

2. Once you downloaded the library the first thing you have to do on XCode is to add the references to the OData library.

This is done by accessing the Project Properties:

clip_image002

3. On the Build tab of the Properties window locate the section “Search Paths” and add the following reference in “Header Search Paths” and “Library Search Paths”:

clip_image004

Also make sure that you selected the recursive checkbox to both paths.

4. After both paths are added you need to add the library to the Frameworks folder of the solution, looking into the file system for the libMSODataLib.a located at this relative path inside the OData SDK folder: “/framework/bin/odatalib/lib/iPhoneSimulatorLibs/iPhone_Simulator_4.1/release/libMSODataLib.a”

clip_image006

5. After that’s done you will have the library for the project ready to be used.

6. Creating the proxy class.

To connect to OData you need to generate the proxy class. To do that open terminal, navigate to the folder where the odatagen executable is located and run this command:

./odatagen /uri=http://domain.com/YourService.svc /out=/Development/odataproxy

On the folder odataproxy you will find the generated proxy, now you need to import the “.h” and “.m” file into the classes folder of the project.

7. After that’s ready, use the proxy to connect to the server at any of the classes.

In this example, I assume that the proxy generated is called CustomersEntities.

With the next line you will have the proxy connected to the server:

CustomersEntities *proxy = [[CustomersEntities alloc]initWithUri:@”URL TO SERVICE” credential:nil];

And to get data simply do the following:

NSArray *customers;

customers = [proxy Customers];

With this example you will have a list of Customers loaded using OData services.

Of course, you can run much more complex queries using OData but that’s not the main focus of this article.

Here is a screen capture of my iPhone App consuming OData :)

clip_image008

Sebastián Rodríguez

Consuming OData from Windows Phone7

Hi,

In this article I want to show you quickly how easy, funny and powerful is to consume OData services in Windows Phone 7.

I am going to take this article from Microsoft as reference, adding screenshots and comments about the process.

So, in order to write your first Windows Phone 7 application that consumes OData, you may want to follow the following steps:

1. Download Windows Phone 7 OData Client libraries.

http://go.microsoft.com/fwlink/?LinkId=207900

The OData client library for Windows Phone generates HTTP requests to a data service that supports OData and transforms the entries in the response feed into objects on the client. Using this client, you can bind Windows Phone controls, such as ListBox or TextBox, to an instance of a DataServiceCollection class that contains an OData data feed. This class handles the events raised by the controls to keep the DataServiceContext class synchronized with changes that are made to data in the controls. For more information about using the OData protocol with Windows Phone applications, see Open Data Protocol (OData) Overview for Windows Phone.

Pasted from <http://msdn.microsoft.com/en-us/library/gg521145(v=vs.92).aspx>

clip_image001

2. Create a new Windows Phone application. In Solution Explorer, right-click the Solution, point to Add, and then select New Project.

3. In the Add New Project dialog box, select Silverlight for Windows Phone from the Installed Templates pane, and then select the Windows Phone Application template. Write a name for the project (e.g. 'WP7ODataSampleApp')

clip_image002

4. Click OK. This creates the application for Silverlight.

5. Generate the proxy classes by running the command datasvcutil.exe included in the  OData client libraries ZIP.

Go to Start->Run

Write 'cmd' and press Enter

clip_image004

Within the console, move to the directory where you have downloaded the OData client libraries.

For instance, '> cd D:\pablop\Documents\WP7\odata client libraries'

clip_image005

Then, run the datasvcutil.exe in order to generate the local proxy for the exposed OData services.

For the purposes of this article we are going to use the Northwind public OData services, exposed at the following Url: http://services.odata.org/Northwind/Northwind.svc

(you can take this as an example and actually use whatever OData service you have access to)

Take the following command line  as reference to generate the proxy:

datasvcutil /uri:http://services.odata.org/Northwind/Northwind.svc/ /out:.\NorthwindModel.cs /Version:2.0 /DataServiceCollection

clip_image006

You can check the generated proxy class file by running a 'dir *.cs' command, like shown below:

clip_image008

6. Move the generated file to the directory where the WP7 solution is located. You can do that just with Windows Explorer.

7. Add the file by selecting it from Project -> Add Existing item … or just by clicking on 'Show All Files' in Solution Explorer, right clicking then on the new .cs file  and choosing 'Include in Project'

clip_image010

clip_image011

8. Add a reference in the project to the System.Data.Services.Client.dll included in the OData Client library ZIP.

clip_image012

clip_image013

9. In the project, double-click the MainPage.xaml file.

Add the following attributes to your phone:PhoneApplicationPage node:

xmlns:my="clr-namespace:NorthwindModel"

d:DataContext="{d:DesignInstance Type=my:Customer, CreateList=True}"

Loaded="PhoneApplicationPage_Loaded">

Take the following xaml as a reference:

<phone:PhoneApplicationPage

    x:Class="ODataNorthwindPhone.MainPage"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"

    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    xmlns:my="clr-namespace:NorthwindModel"

    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"

    d:DataContext="{d:DesignInstance Type=my:Customer, CreateList=True}"

    FontFamily="{StaticResource PhoneFontFamilyNormal}"

    FontSize="{StaticResource PhoneFontSizeNormal}"

    Foreground="{StaticResource PhoneForegroundBrush}"

    SupportedOrientations="Portrait" Orientation="Portrait"

    shell:SystemTray.IsVisible="True" Loaded="PhoneApplicationPage_Loaded">

    <Grid x:Name="LayoutRoot" Background="Transparent">

        <Grid.RowDefinitions>

            <RowDefinition Height="Auto"/>

            <RowDefinition Height="*"/>

        </Grid.RowDefinitions>

        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">

            <TextBlock x:Name="ApplicationTitle" Text="Northwind Sales"

                       Style="{StaticResource PhoneTextNormalStyle}"/>

            <TextBlock x:Name="PageTitle" Text="Customers" Margin="9,-7,0,0"

                       Style="{StaticResource PhoneTextTitle1Style}"/>

        </StackPanel>

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

            <ListBox x:Name="MainListBox" Margin="0,0,-12,0" ItemsSource="{Binding}">

                <ListBox.ItemTemplate>

                    <DataTemplate>

                        <StackPanel Margin="0,0,0,17" Width="432">

                            <TextBlock Text="{Binding Path=CompanyName}" TextWrapping="NoWrap"

                                       Style="{StaticResource PhoneTextExtraLargeStyle}"/>

                            <TextBlock Text="{Binding Path=ContactName}" TextWrapping="NoWrap"

                                       Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>

                            <TextBlock Text="{Binding Path=Phone}" TextWrapping="NoWrap" Margin="12,-6,12,0"

                                       Style="{StaticResource PhoneTextSubtleStyle}"/>

                        </StackPanel>

                    </DataTemplate>

                </ListBox.ItemTemplate>

            </ListBox>

        </Grid>

    </Grid>

</phone:PhoneApplicationPage>

The 'd:DataContext' line above allows to create data bindings at design time for a data context that is assigned at run time. This way you will be able to see sample data within the Silverlight Visual Designer in  Visual Studio and Expression Blend. Obviously this line is not required but recommended :).

10. Replace ContentPanel grid with something like this:

      <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <ListBox x:Name="MainListBox" Margin="0,0,-12,0" ItemsSource="{Binding}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Margin="0,0,0,17" Width="432">
                            <TextBlock Text="{Binding Path=CompanyName}" TextWrapping="NoWrap"
                                       Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                            <TextBlock Text="{Binding Path=ContactName}" TextWrapping="NoWrap"
                                       Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
                            <TextBlock Text="{Binding Path=Phone}" TextWrapping="NoWrap" Margin="12,-6,12,0"
                                       Style="{StaticResource PhoneTextSubtleStyle}"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>

11. Open MainPage.xml and add the following namespaces:

using System.Data.Services.Client;

using NorthwindModel;

12. Add the following declarations to the MainPage class:

        private DataServiceContext northwind;

        private readonly Uri northwindUri =

            new Uri("http://services.odata.org/Northwind/Northwind.svc/");

        private DataServiceCollection<Customer> customers;

        private readonly Uri customersFeed = new Uri("/Customers", UriKind.Relative);

13. Add the following PhoneApplicationPage_Loaded method to the MainPage class:

        private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)

        {

            // Initialize the context and the binding collection

            northwind = new DataServiceContext(northwindUri);

            customers = new DataServiceCollection<Customer>(northwind);

            // Register for the LoadCompleted event.

            customers.LoadCompleted

                += new EventHandler<LoadCompletedEventArgs>(customers_LoadCompleted);

            // Load the customers feed by using the URI.

            customers.LoadAsync(customersFeed);

        }

The above code initializes the binding collection and registers the method 'customers_LoadCompleted' (to be created on next step) to be called when collection has been loaded.

Remember that in Silverlight, all data must be retrieved asynchronously.

14. Now, let's handle the LoadCompleted event by adding the customers_LoadCompleted method. Take the following code as reference:

void customers_LoadCompleted(object sender, LoadCompletedEventArgs e)

{

   if (e.Error == null)

    {

        // Handling for a paged data feed.

        if (customers.Continuation != null)

        {

            // Automatically load the next page.

            customers.LoadNextPartialSetAsync();

        }

        else

        {

            // Set the data context of the listbox control to the sample data.

            this.LayoutRoot.DataContext = customers;

        }

    }

    else

    {

        MessageBox.Show(string.Format("An error has occurred: {0}", e.Error.Message));

    }

}

When the LoadCompleted event is handled, the following operations are performed if the request returns successfully:

o The LoadNextPartialSetAsync method of the DataServiceCollection object is called to load subsequent results pages, as long as the Continuation property of the DataServiceCollection object returns a value.

o The collection of loaded Customer objects is bound to the DataContext property of the element that is the master binding object for all controls in the page.

o Now, we are ready to test our first WP7 OData sample application. Just press F5  and wait for the emulator to be loaded and run our app. (this typically takes several secs)

clip_image014

clip_image015

clip_image016

16. As shown above, the main page is retrieving customers from OData.

Hope you find this article useful in order to start to play and/or build robust solutions on top of OData and Windows Phone 7, two extremely powerful platforms. Better together, for sure! :)

PP.

HOW To hide items from “Settings” menu

In some cases it is necessary to hide some items from the side menu according to the CRM user’s assigned role. To do this we need to modify the SiteMap from the CRM.

The example given in this post is done on CRM 2011 but it also applies to CRM 4.

We are going to see:

· How to hide items from the Settings section.

· How to hide the whole Settings area.

Firstly, we are going to export the CRM SiteMap as follows:

Go to Settings > Solutions >

Create a new solution.

image

Complete data.

image2

In the “Add Existing” menu we select “SiteMap” option and then “Save and Close”.

image3

Once we create the solution we select and export it, by clicking the “Export” button.

Click “Next”

image4

Select “Customizations”

image5

Mark the “Unmaneged” choice. Click “Export” and save the file .zip.

image6

When extracting the zip we find the customizations.xml file which contains the customizations from our CRM, just the ones from the “SiteMap” in this case.

Inside the <SiteMap> node is the complete structure from the side menu.

The privilege level the user has over the entity assigned to the menu item means what is going to determine it to be shown for some users but not for others.

Example:

image7

In this case only those roles having the “Read” permission over the “Solution” entity will be able to see this menu item. This compels us to attach a role to an entity in order to determine if the menu item is visible or not.

So we have two options: to create an entity just to define the visibility of the menu item, or to use an already created entity not working with it in our solution, for example “Solution”.

To determine which permissions has a role over an entity we go to Settings > Administration > Security Roles

image8

In the role properties window we can assign or remove the permissions over the entities. In this case we remove the “Read” permission from the “Solution” entity.

In this example the users of the role “User” won’t have the “Business Management” option visible inside the “Settings” menu.

Keep in mind the privileges are set over the <SubArea> nodes, if it necessary to hide the “Settings” menu we need to hide all the <SubArea> nodes.

How to create a Multiple Selection Picklist in CRM 4.0

In CRM 4.0 by default we do not have a Picklist where to select multiple items. In the post below I am giving the details to get this functionality.

Firstly, we create a Picklist with all values.

ScreenShot005

Then, we create a text field which is going to store the selected values from the multi-value Picklist.

ScreenShot001

Using the following script we extend the Picklist functionality to allow it to select multiple values. We change new_mypicklist and new_mypicklistvalue by the names of previously created attributes.

The code was extracted from this post

var PL = crmForm.all.new_mypicklist;
var PLV = crmForm.all.new_mypicklistvalue;

if( PL != null && PLV != null )
{
  PL.style.display = "none";
  PLV.style.display = "none";

  // Create a DIV container
  var addDiv = document.createElement("<div style='overflow-y:auto; height:80px; border:1px #6699cc solid; background-color:#ffffff;' />");
  PL.parentNode.appendChild(addDiv);

  // Initialize checkbox controls
  for( var i = 1; i < PL.options.length; i++ )
  {
    var pOption = PL.options[i];
    if( !IsChecked( pOption.text ) )
      var addInput = document.createElement("<input type='checkbox' style='border:none; width:25px; align:left;' />" );
    else
      var addInput = document.createElement("<input type='checkbox' checked='checked' style='border:none; width:25px; align:left;' />" );

    var addLabel = document.createElement( "<label />");
    addLabel.innerText = pOption.text;

    var addBr = document.createElement( "<br />");
    PL.nextSibling.appendChild(addInput);
    PL.nextSibling.appendChild(addLabel);
    PL.nextSibling.appendChild(addBr);
  }

  // Check if it is selected
  function IsChecked( pText )
  {
    if(PLV.value != "")
    {
      var PLVT = PLV.value.split("||");
      for( var i = 0; i < PLVT.length; i++ )
      {
        if( PLVT[i] == pText )
          return true;
      }
    }
    return false;
  }

  // Save selected text
  crmForm.attachEvent( "onsave" , OnSave);
  function OnSave()
  {
    PLV.value = "";
    var getInput = PL.nextSibling.getElementsByTagName("input");

    for( var i = 0; i < getInput.length; i++ )
    {
      if( getInput[i].checked)
      {
        PLV.value += getInput[i].nextSibling.innerText + "||";
      }
    }
  }
}

Now, we are going to insert this code in the Form OnLoad() event. In order to do this we select Settings > Customizations > Customize Entities and choose the entity where we want to put the multi-value Picklist.

We go to the entity form and click in Form Properties and Edit the Event OnLoad().

 

ScreenShot004

Verify the checkbox “The Event is Enabled” is checked.

The field that contains the values “new_mypicklistvalue”, must be in the page. We can hide its label but we cannot completely hide it, as it is going to take the script.

As loading the page takes some time, in order to load the jscript making the field containing the values not visible, what we can do is to put that field in another tab, for example in the tab Notes which is always present by default.

 

ScreenShot003

 

So this way it is not seen when the page is loading.

Regards!

Santiago Gonnet

LINQ To SharePoint: Working with Created, CreatedBy, Modified and ModifiedBy

LINQ to SharePoint is a great tool to perform queries against a SharePoint server since the 2010 version. Unlike the classical CAML queries, it allows to use a strongly-typed entity model and LINQ query syntax to query list data.

 

The SPMetal command

The first step to use LINQ to SharePoint is to run the SPMetal tool in order to create the entity model from an existent SharePoint site. This tool is located at 14\bin. Here’s a sample on how to use it:

SPMetal /web:http://mysharepointsite:9999 /code:Model.cs

This command will create a C# code file containing the entity model, in 14\bin\Model.cs. After adding this file to our project, we can perform queries using LINQ to SharePoint. For example, this server-side code, outputs the titles for all the items in “MyList” where the title length is at least 10 characters long:

StringBuilder output = new StringBuilder();
using (ModelDataContext model = new ModelDataContext(SPContext.Current.Site.Url))
{
    foreach (MyListItem itemWithoutTitle in model.MyList.Where(x => x.Title.Length >= 10))
    {
        output.AppendLine(itemWithoutTitle.Title);
    }
}

The missing fields

By default, the Created, CreatedBy, Modified and ModifiedBy fields are not created by SPMetal. However, the framework offers a way of extending the object-relational mapping system of the LINQ to SharePoint provider. In other words, we can easily use those fields after telling LINQ to SharePoint how to retrieve and update them from the content database.

We will extend the base entity class of our model (“Item” class) in a new code file (we can call it “ModelExtensions.cs” for example):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Linq;
using Microsoft.SharePoint;

public partial class Item : ICustomMapping
{
    [CustomMapping(Columns = new String[] { "Modified", "Created", "Editor", "Author" })]
   public void MapFrom(object listItem)
    {
        SPListItem item = (SPListItem)listItem;
        this.Modified = (DateTime)item["Modified"];
        this.Created = (DateTime)item["Created"];
        this.CreatedBy = (string)item["Author"];
        this.ModifiedBy = (string)item["Editor"];
    }

    public void MapTo(object listItem)
    {
        SPListItem item = (SPListItem)listItem;
        item["Modified"] = this.Modified;
        item["Created"] = this.Created;
        item["Author"] = this.CreatedBy;
        item["Editor"] = this.ModifiedBy;
    }

    public void Resolve(RefreshMode mode, object originalListItem, object databaseObject)
    {
        SPListItem originalItem = (SPListItem)originalListItem;
        SPListItem databaseItem = (SPListItem)databaseObject;

        DateTime originalModifiedValue = (DateTime)originalItem["Modified"];
        DateTime dbModifiedValue = (DateTime)databaseItem["Modified"];

        DateTime originalCreatedValue = (DateTime)originalItem["Created"];
        DateTime dbCreatedValue = (DateTime)databaseItem["Created"];

        string originalCreatedByValue = (string)originalItem["Author"];
        string dbCreatedByValue = (string)databaseItem["Author"];

        string originalModifiedByValue = (string)originalItem["Editor"];
        string dbModifiedByValue = (string)databaseItem["Editor"];

        if (mode == RefreshMode.OverwriteCurrentValues)
        {
            this.Modified = dbModifiedValue;
            this.Created = dbCreatedValue;
            this.CreatedBy = dbCreatedByValue;
            this.ModifiedBy = dbModifiedByValue;
        }
        else if (mode == RefreshMode.KeepCurrentValues)
        {
            databaseItem["Modified"] = this.Modified;
            databaseItem["Created"] = this.Created;
            databaseItem["Author"] = this.CreatedBy;
            databaseItem["Editor"] = this.ModifiedBy;
        }
        else if (mode == RefreshMode.KeepChanges)
        {
            if (this.Modified != originalModifiedValue)
            {
                databaseItem["Modified"] = this.Modified;
            }
            else if (this.Modified == originalModifiedValue && this.Modified != dbModifiedValue)
            {
                this.Modified = dbModifiedValue;
            }

            if (this.Created != originalCreatedValue)
            {
                databaseItem["Created"] = this.Created;
            }
            else if (this.Created == originalCreatedValue && this.Created != dbCreatedValue)
            {
                this.Created = dbCreatedValue;
            }

            if (this.CreatedBy != originalCreatedByValue)
            {
                databaseItem["Author"] = this.CreatedBy;
            }
            else if (this.CreatedBy == originalCreatedByValue && this.CreatedBy != dbCreatedByValue)
            {
                this.CreatedBy = dbCreatedByValue;
            }

            if (this.ModifiedBy != originalModifiedByValue)
            {
                databaseItem["Editor"] = this.ModifiedBy;
            }
            else if (this.ModifiedBy == originalModifiedByValue && this.ModifiedBy != dbModifiedByValue)
            {
                this.ModifiedBy = dbModifiedByValue;
            }
        }
    }

    public DateTime Modified { get; set; }
    public DateTime Created { get; set; }
    public string CreatedBy { get; set; }
    public string ModifiedBy { get; set; }
}

For extended information of how the ICustomMapping interface works, you can check these MSDN articles: ICustomMapping Members and RefreshMode Enumeration.

After adding this file to our project, we can use Modified, Created, CreatedBy and ModifiedBy in our queries:

StringBuilder output = new StringBuilder();
using (ModelDataContext model = new ModelDataContext(SPContext.Current.Site.Url))
{
    DateTime date = DateTime.Parse(“Thu, 05 May 2011 12:46:00 GMT”);

    foreach (MyListItem itemCreatedAfterDate in model.MyList.Where(x => x.Created > date))
    {
        output.AppendLine(itemCreatedAfterDate.Title);
    }
}

Keep in mind that the Author and Editor fields identify users. These strings may have more information than what you need. An easy way of parsing this string to extract the information you need is to create a new SPFieldUserValue with the current SPWeb and the string. Then you can extract the actual SPUser from SPFieldUserValue.User.

I hope you find this code useful as I do. Enjoy!

Alfonso Cora.

How to deploy and test a Windows Phone 7 application into the phone?

Hi,

Working on some Windows Phone 7 development project, I came across the following question from the customer when releasing an sprint: ‘I have bought a phone. How can I deploy the app into it?’.

Easy question but not so easy answer if you are not familiarized with some aspects like: Windows Phone 7 locking, registration, App Hub membership Marketplace, and so on.

So, after some research I decided to share here my conclusions that could save time to somebody else when having to answer the same question:

  1. First, deploying means to distribute the .XAP file which you can get it after compilation (debug as well as release mode).
  2. Nevertheless, for deploying that .XAP into your phone you need to meet some requirements:
  • Windows Phone Developer tools must be installed (instructions here)
  • Your Windows Phone device must be registered (instructions here). Bear in mind that registrations implies:
    • having a valid App Hub developer account ($99 annual fee)
    • identity validation with GeoTrust which could take some days.
    • living / having a valid and legal address on one of these countries.

A registration walkthrough can be followed here.

  • The Zune client software must be installed on the host computer (downloadable from here)

After complying with the above requirements, you will need to run the Application Deployment Tool (included with Developer tools) in order to deploy the app to the device. Instructions here.

image

But, what happens if you cannot comply with one of the above requirements? for instance, my country is not listed? Some possible workarounds for the above:

  • Have your Windows Phone 7 device unlocked for developing purposes (nothing to do with telcos locking). ChevronWP7 seemed to be a great tool for doing that but currently seems to be discontinued (after the guys had some discussions with Microsoft :p).
  • Try this tool (it requires the phone to be unlocked) (I personally haven’t tried it yet)
  • On the meanwhile, use the emulator that is included in the developer tools :(

Now, what happens for instance if your customer is not technical and is not willing to install any development tool for deploying your app into his/her phone? In this case, according to this article there would be possible to submit an app to Marketplace that remains private and only available to be downloaded by the people we just want to. Sounds good (and logical btw) but it seems that this feature is still not available :(.

So, as far as I can see as workaround I guess you could use Marketplace, uploading a free app that asks for some kind of PIN when loads in order to work. This PIN should only be shared by you and your customer. Make sense? But, for uploading an app to Marketplace you must be registered as well as the app should pass the certification test which could put you in a loop :(.

If you have any comments / suggestions / news on this matter, please post a comment.

Hope this helps to save somebody else’s time.  Also hope Microsoft could give developers more tools in order to support offshoring scenarios where development is being done in different, not yet listed countries and customers are not so willing (or just cannot) install a bunch of dev tools just for deploying an app to their phones.

 

Pablo Peralta.

Twitter: @pabloperalta



Posted: Feb 24 2011, 12:21 AM by uruit
Filed under: ,
Checklist for testing and reviewing ssis packages

At UruIT we work a lot with SQL Server Integration Services and compiled several best practices found on the web that we already adopted. Many of them related to performance tips and conventions. At the end of this post I am sharing some usfeul links.

Nevertheless, we still couldn’t find something like a checklist that we could follow in order to say: ‘ok, if all this bullets are in green we can move forward to going live’. Hence, we built our own basic checklist which are already following in order to review packages. We use to apply this checklist in peer reviews too and now, decided to share with the community.

Please, if you find it useful and have feedback to improve it, I will be really glad to hear it.

So, here is the checklist:

Design and best practices

To check

Passed

Check if best practices have been followed

Best practices related to optimization/tuning/design have been followed.

OK

 

Naming convention at project/packages/tasks/component level is applied.

X

 

Package structure is aligned with Package Design best practices.

OK

 

 

 

Testing

To check

Passed

Check if testing has been completed.

Normal flow scenario

Verify all tables/rows have been imported.

 

Verify all data has been imported without any truncation (for each column).

 

Error flow scenario

Verify error flow when no input data source available.

 

 

Verify error flow when no output data source available.

 

 

Verify error flow related with input data quality (NULL values, strings instead of numeric values, etc.)

 

 

Logging

Check if chosen logging mechanism is in place and working properly for the selected events.

 

 

Deploying

Check if package(s) has been deployed and executed as expected in a proper testing environment.

 

 

Configuration

Check if chosen configuration mechanism is in place and working as expected.

 

 

 

This way, we only consider a package is completed if everything of the above is ‘OK’. Make sense? Simple an useful for us.

Hope you find it useful,

Juan Pablo Turielli.

 

Useful information that we compiled into our SSIS development standard:

Top 10 SQL Server Integration Services Best Practices

Considerations for High Volume ETL Using SQL Server Integration Services

SSIS: Package Naming Convention

SSIS: Suggested Best Practices and naming conventions

SSIS: Package design pattern for loading a data warehouse

 



More Posts « Previous page - Next page »