Monday, 27 May 2013

Set Activities filter on contact's entity form to All on MS Dynamics CRM 2011

The default filter for the activities lookup view on the contact form is Next 30 days, which is something that I was asked to change, so this is what I did.

The SetView function was registered on the onload event of the contact form and a parameter of 'All' was passed to set the view

function SetView(Value)
{
   SetDefaultView = function (viewCombo, viewName, appGrid)
   {
      if (viewCombo.value != viewName)
      {
         viewCombo.value = viewName;
      }
   }
/*I don't think this is actually needed, too lazy to check */
   areaActivitiesFrame_OnReadyStateChange = function ()
   {
      if (this.readyState == "complete")
      {
         var frame = getiFrame("areaActivitiesFrame");
         var viewCombo = frame.contentWindow.document.getElementById("crmGrid_Contact_ActivityPointers_datefilter");
         var appGrid = frame.contentWindow.document.getElementById("AppGridFilterContainer");
         if (viewCombo.readyState == "complete")
         {
            SetDefaultView(viewCombo, defaultValue, appGrid);
         }
         else
         {
            viewCombo.onreadystatechange = function ()
            {
               if (this.readyState == "complete")
               {
                  SetDefaultView(this, defaultValue, appGrid);
               }
            }
         }
      }
   }

   if (document.getElementById(navActivities) != null)
   {
      document.getElementById(navActivities).onclick = function ()
      {
         loadArea(this,"areaActivities");
         var iframe =  getiFrame(areaActivitiesFrame);

         iframe.onreadystatechange = function ()
         {
            if (this.readyState == "complete")
            {
               var frame = getiFrame("areaActivitiesFrame");
               var viewCombo = frame.contentWindow.document.getElementById("crmGrid_Contact_ActivityPointers_datefilter");               
               var appGrid = frame.contentWindow.document.getElementById("AppGridFilterContainer");
               if (viewCombo.readyState == "complete")
               {
                  SetDefaultView(viewCombo, defaultValue, appGrid);
               }
               else
               {
                viewCombo.onreadystatechange = function ()
                {
                   if (this.readyState == "complete")
                   {
                      SetDefaultView(this, defaultValue, appGrid);
                   }
                }
               }

            }
         }
      }
   }
}

function getiFrame(iframeName)
{
 var frames=document.getElementsByTagName('iframe');

  for(var i =0 ; i < frames.length;i++)
  {
    if (frames[i].name == iframeName)
     {
      var theFrame = frames[i];
     }
  }
 return theFrame;
}

Wednesday, 22 May 2013

Comparing Calibre vs Instapaper mobi (Kindle books) conversion capabilities

I've been using Instapaper for a while now and the only thing that has kept me from subscribing is the poor quality of the conversions to mobi, which is the main usage I've given the site.

Generally speaking pictures are a no show on any converted to mobi website by Instapaper, there are formatting issues too, but until very recently this had not been a major problem, as it was pretty much the only game in town, but then I learnt about Calibre's news feature and after giving it a try, I've not really used Instapaper anymore. Admittedly I have had to write an application to use the news feature in Calibre in a similar way to Instapaper, but there you go.

Here's a side by side comparison of conversions from Calibre (left) and Instapaper(right):



As mentioned above, the first image of the article is missing, this is not a major issue, however all the code samples are images and thus do not appear on Instapaper's conversion, which makes the conversion next to useless in this particular example.


There is hardly any difference here between the two, I still like the Calibre conversion better as it's removed some of the useless links at the top.


Again, Calibre's conversion is a lot neater, the first page hardly contains any content in Instapaper's conversion. Plus the "twitter" text is not separated from the main article, given the impression that it's part of the article.


The code in Calibre's conversion is a lot neater to read than it is in Instapaper's. This time Instapaper packs more content in, but since it's code we are talking about, this is probably a bad idea, as it impacts negatively in the readability of it.

Instapaper beats Calibre in convenience, all one needs to do is have a bookmark on one's browser's toolbar and clicking on it will send the page to be stored in Instapaper's servers ready for conversion at a later time.

I realize that it's taken me a while to write this post and Instapaper might have got better in the meantime. 

Friday, 17 May 2013

MS Dynamics CRM 2011 Workflows not sending emails - Set Allow other Microsoft Dynamics CRM users to send E-mail on your behalf privilege programmatically

Today I had an issue where a workflow would fail to complete, oddly it was failing on a Send Email Activity, with the following error message:
You cannot send e-mail as the selected user. The selected user has not allowed this or you do not have sufficient privileges to do so.Contact your system administrator for assistance.
The issue is caused by users not having send as privileges set. Note that this will only occur if the sender is different from the owner of the email, which will be the case if you have workflows owned by the service account sending emails on user's behalf, a common enough business scenario.

Here's the method I used to allow this:

private void SetSendAsSetting(IOrganizationService service, Entity user, bool state)
{
    try
    {               
        UpdateUserSettingsSystemUserRequest updateRequest = new UpdateUserSettingsSystemUserRequest();
        UpdateUserSettingsSystemUserResponse updateResponse = new UpdateUserSettingsSystemUserResponse();
        
        Entity userSettings = new Entity("usersettings");        
        
        //Ensure that help language is set
        userSettings.Attributes["helplanguageid"] = 1033;
        userSettings.Attributes["issendasallowed"] = state;
        
        updateRequest.Settings = userSettings;
        updateRequest.UserId = userId;
        
        updateResponse = (UpdateUserSettingsSystemUserResponse)service.Execute(updateRequest);
    }
    catch (Exception ex)
    {
        Logger.Write(ex);
    }
}

Sunday, 12 May 2013

Set IIS website https binding programmatically (add certificate too)

I found myself needing to set a website's https binding for a few servers, this week and I wondered whether this could be done programmatically.

Although in this case the method, see below, is used to set https as a binding it can be easily modified to add any binding type to any website. I kind of did it as halfway house, I might modify it later to make it a little bit more robust and generic.

private static void SetBinding(string siteName, string bindingInfo, string fileName,string password)
{
    using (ServerManager serverManager = new ServerManager())
    {
        Site site = serverManager.Sites.Where(x => x.Name == siteName).SingleOrDefault();

        X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
        store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);

        X509Certificate2 certificate = new X509Certificate2(fileName, password);

        store.Add(certificate);

        if (site.Bindings.Any(x => x.Protocol.ToLower() != binding.ToLower()))
        {
            Binding binding = site.Bindings.Add(bindingInfo, certificate.GetCertHash(), store.Name);

            binding.Protocol = "https";
        }

        store.Close();
    }
}

Note that bindingInfo should be of this form "*:443:" if you want your website to listen on all ip addresses of your server.

The certificate needs to be of PKCS #12 vintage, i.e. with a .pfx or .p12  extension (don't just change the filename extenstion to .pfx if you have a .cer or .der certificate, it won't work.)

Don't forget to add the following namespaces (the library for the Microsoft.Web.Administration namespace is called the same as the namespace).

System.Security.Cryptography.X509Certificates;
Microsoft.Web.Administration;

Tuesday, 7 May 2013

Wiring events to a Popup control in Silverlight

Last week I was doing some work on a Silverlight application and I was struggling with an event not firing for a calendar control inside a popup control and it turns out that the reason is very simple, I was wiring the event to the popup rather than the calendar control inside the popup.

This is the method I used to create the calendar within the popup. 

private void CreateCalendar()
{
    if (calendar == null)
    {
        calendar = new Calendar();
        calendar.SetValue(Grid.ColumnProperty, 10);
        calendar.SetValue(Grid.RowProperty, 0);
        calendar.SetValue(Grid.RowSpanProperty, 7);
        calendar.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
      
        var StartDateBinding = new System.Windows.Data.Binding();
        StartDateBinding.Path = new PropertyPath("StartDate");
        StartDateBinding.Mode = System.Windows.Data.BindingMode.TwoWay;
        StartDateBinding.ValidatesOnDataErrors = true;
        calendar.SetBinding(Calendar.SelectedDateProperty, StartDateBinding);

        var displayDateBinding = new System.Windows.Data.Binding();
        displayDateBinding.Path = new PropertyPath("CalendarStartsOn");
        calendar.SetBinding(Calendar.DisplayDateStartProperty, displayDateBinding);

        popup.DataContext = viewModel;

        popup.Child = calendar;            

        calendar.MouseLeave += (o, e) =>
        {
            popup.IsOpen = false;
            LayoutRoot.Children.Remove(popup);
        };
  
 calendar.SelectedDatesChanged += (o, e) =>
        {
     CreateAppointment();
            popup.IsOpen = false;
            LayoutRoot.Children.Remove(popup);
        };

    }

}

The calendar and popup objects are global objects of the MainPage partial class (i.e. they can be found in main.xaml.cs) and the CreateCalendar method gets called on the constructor, via the AddPopUp method, see below

The popup is closed when a date is selected, in which case we create an appointment, method not shown, for the user or when the mouse leaves the calendar.

private void AddPopUp()
{    
    CreateCalendar();
    LayoutRoot.Children.Remove(popup);
    LayoutRoot.Children.Add(popup);
    
    popup.VerticalOffset = 50;
    popup.HorizontalOffset = Application.Current.Host.Content.ActualWidth - 100;
    
    popup.IsOpen = true;
}

Thursday, 2 May 2013

Set Regional Settings (culture) in Silverlight application.

Today I was looking at some issues with date formats, why in god's name do the american's use a different date system than everybody else?, and I ended up using the following code to set up the Regional Settings (No prizes for guessing where I work):

private void Application_Startup(object sender, StartupEventArgs e)
{
    this.RootVisual = new MainPage();
 
    Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
    Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-GB");
 
    var root = RootVisual as  MainPage;
    if (root != null)
    {
     root.Language = XmlLanguage.GetLanguage(Thread.CurrentThread.CurrentCulture.Name);
    }
}

This is in the App.xaml code behind file.

Note that annoyingly, this will not work in WPF.