Exchange Server 2007 SP1, Outlook Web Access Customization: Part 3, NewHelpTicket.ASPX

Published 28 September 09 08:40 PM

I’ve seen a few cases come through where customer’s are trying to implement the customization features that were introduced with Exchange Server 2007 SP1.  There’s a few key areas that seem to cause trouble so I figured it would be worth walking through a simple scenario that covered many of the different aspects. (Link to Part 1 of the series, Link to Part 2 of the series)

It’s been a long time coming, but I’m finally getting around to completing this series of posts.  I had originally intended to use the standard Visual Studio generated proxy classes for this sample, but with the introduction of the EWS Managed API the code for this scenario becomes much easier to write.  The Managed API is currently available as a Release Candidate.  After you install the API, you will have to copy the DLL file from the install location (C:\Program Files\Microsoft\Exchange\Web Services\1.0) to a location where it can be found by your ASP.Net code.  For this sample you can place it in: C:\Program Files\Microsoft\Exchange Server\ClientAccess\Owa\bin

Now down to business.  This form is basically a very simple ASP.NET form to use the EWS Managed API to send the email as the user.  For demonstration purposes, I have simply sent the resulting form back to the originating user using the “ea” value from the QueryString.

Another interesting point is the code to impersonate the logged on user.  When implementing custom forms, you should be mindful of the identity that is being used.  In this example, the form is running in the same IIS Application Pool as OWA.  This means that it will have access to the session info.  I’ve used the identity of the logged on user available from the Page object and swapped the formatting of this value so that it can be used as a PrincipalName to impersonate the user.   The reason that I used the identity from the Request instead of relying on the Primary SMTP address in the Querystring is that the client can alter the QueryString.

NewHelpTicket.ASPX:

(Note:  This sample code is intended only to show the concepts involved and is not production ready!  If you have questions, feel free to submit them via the comments or contact me through the “Email” link above.)

<%@ Page Language="C#" AutoEventWireup="true" EnableSessionState="True"%>
<%@ Assembly Name="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" %>
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.Net" %>
<%@ Import Namespace="System.Net.Security" %>
<%@ Import Namespace="Microsoft.Exchange.WebServices.Data" %>
<%@ Import Namespace="System.Security.Cryptography.X509Certificates" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    
    protected void Page_Load(object sender, EventArgs e)
    {   // The following is a simple method for closing a window with client-side code on a button.
        bClose.Attributes.Add("onclick", "window.close();");
    }

    RemoteCertificateValidationCallback OriginalValidator;
    
    // This method is only needed if your EWS endpoint has a certificate that is not trusted.
    protected void DisableSSLVerification()
    {   //Save the original validator so that it can be restored
        OriginalValidator =
            ServicePointManager.ServerCertificateValidationCallback;
            
        //Replace with a dummy always-true validator
        ServicePointManager.ServerCertificateValidationCallback =
            delegate(Object obj, 
            X509Certificate certificate, 
            X509Chain chain, 
            SslPolicyErrors errors)
            {
                return true;
            };
    }

    protected void RestoreVerification()
    {
        //Restore the original validator
        ServicePointManager.ServerCertificateValidationCallback = OriginalValidator;
    }

    protected string GenerateXmlPayload()
    {
        //Leverage XmlDocument to put together some well formed xml to save
        //the form data.
        XmlDocument x = new XmlDocument();
        x.LoadXml("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<HelpTicket/>");
        XmlNode xTitle = x.CreateElement("Title");
        xTitle.InnerText = tbTitle.Text;
        XmlNode xDetails = x.CreateElement("Details");
        xDetails.InnerText = tbDetails.Text;
        XmlNode xDate = x.CreateElement("Date");
        xDate.InnerText = System.DateTime.Now.ToString();
        XmlNode xUser = x.CreateElement("User");
        xUser.InnerText = Page.User.Identity.Name; 
        x.DocumentElement.AppendChild(xTitle);
        x.DocumentElement.AppendChild(xDetails);
        x.DocumentElement.AppendChild(xDate);
        x.DocumentElement.AppendChild(xUser);
        return (x.OuterXml);
    }
    
    protected void bSubmit_Click(object sender, EventArgs e)
    {
        try
        {
            //Use Page.User.Identity (this is the logged in user) to generate
            //a principal name for the EWS impersonation.
            string[] parts = Page.User.Identity.Name.Split("\\".ToCharArray());
            string PrincipalName = parts[1] + "@" + parts[0];
            ExchangeService exchange = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
            exchange.ImpersonatedUserId =
                new ImpersonatedUserId(ConnectingIdType.PrincipalName, PrincipalName);
            //Set EWS endpoint to local host (may not work for all setups)
            exchange.Url = new Uri("https://localhost/ews/exchange.asmx");
            Microsoft.Exchange.WebServices.Data.EmailMessage message =
                new Microsoft.Exchange.WebServices.Data.EmailMessage(exchange);
            message.Subject = "HELPTICKET: " + tbTitle.Text;
            message.Body = "This is a HelpTicket Form";
            //Add XML Payload as file attachment.
            message.Attachments.AddFileAttachment("FormData.xml",
                System.Text.Encoding.Default.GetBytes(GenerateXmlPayload()));
            message.ToRecipients.Add(Request.QueryString["ea"]);
            //Set ItemClass so that our custom viewing form will be used.
            message.ItemClass = "IPM.Note.HelpTicket";
            //DisableSSLVerification(); //Uncomment these lines if your CAS server's certificate is not trusted
            message.SendAndSaveCopy();
            //RestoreVerification();    //Uncomment these lines if your CAS server's certificate is not trusted

            //Set fields to ReadOnly and hide/show appropriate buttons            
            bSubmit.Visible  = false;
            bClose.Visible = true;
            tbTitle.ReadOnly = true;
            tbDetails.ReadOnly = true;
        }
        catch (Exception ex)
        {
            //If we get an exception, dump it to the user
            lDiag.Text = ex.ToString();
        }
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>New Helpdesk Ticket</title>
</head>
<body style="background-color:#E0E0F0;">
    <form id="form1" runat="server">
    <div>
    <h2 style="text-align:center;">Create a new Helpdesk Ticket</h2>
        <table style="width:100%;border-style:groove;background-color:White;">
            <tr>
                <td style="width:150px;">
                    Issue Title</td>
                <td style="width:auto;">
                    <asp:TextBox ID="tbTitle" runat="server" Width="99%"></asp:TextBox></td>
            </tr>
            <tr>
                <td>
                    Details</td>
                <td>
                    <asp:TextBox ID="tbDetails" runat="server" Width="99%" Rows="10" 
                        TextMode="MultiLine"></asp:TextBox></td>
            </tr>
        </table>
        <br />
        <asp:Button ID="bSubmit" runat="server" Text="Submit" style="width:100%" 
            onclick="bSubmit_Click" />
        <asp:Button ID="bClose" runat="server" 
            Text="Ticket Submitted, Click to Close Window" style="width:100%" 
            Visible="False" />
    
    </div>
    <asp:Label ID="lDiag" runat="server" Text="" Font-Italic="True"></asp:Label>
    </form>
</body>
</html>

With the customizations in place from Part 1 and Part 2, and the NewHelpTicket.ASPX saved in C:\Program Files\Microsoft\Exchange Server\ClientAccess\Owa\forms\HelpTicket, you should get the following form when you click the “New Helpdesk Ticket” link from the “New” menu in Outlook Web Access.

image

In Part 4 of this series, I will present sample ViewHelpTicket.ASPX that can unpack the attached XML and display it in a format similar to the New Helpdesk Ticket form.

by Rick_H
Filed under: , , ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# HuaHua said on September 29, 2009 7:15 AM:

hi,when i used your code to impersonate user,some error happen,it tell me no permission to do impersonate  user,how can i fix it ?

# Rick_H said on September 29, 2009 7:42 AM:

I've seen this occur in some configurations where the check for impersonation rights is occuring in the context of Local System and it is not checking if the machine's AD account has the impersonation right.  You can workaround this issue by running this command through the Exchange Management Shell on the CAS server to explicitly add this right for Local System:

Add-AdPermission -Identity (Get-ExchangeServer -Identity (hostname)).DistinguishedName -User "S-1-5-18" -extendedRight ms-Exch-EPI-Impersonation

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required

This Blog

Syndication

Page view tracker