Integrando Exchange Online Services con DotNetNuke

Una de las características que nos encontramos cuando tenemos alojado DotNetNuke en Azure es que no disponemos de un servidor SMTP para el envío de los correos de notificación que usa el gestor de contenidos.

La solución pasa por una de las siguientes alternativas muy bien expuestas en el post de Christian Weyer:

  • Usar un proveedor externo que pueda ayudar en el envío de correos
  • Conectar a tu servidor SMTP On-Premises
  • Usar APIs/Servicios expuestos por terceros para el envío

Bajo el último enfoque entran los servicios expuestos por Exchange Online, servicio que a través del pago de 5$ al mes/usuario podemos contar con la capacidad de un servidor de Exchange alojado en la nube –recordad que podéis probar la suite BPOS durante un periodo de 30 días sin compromiso.

Para usar los servicios de Exchange Online desde DNN realizaremos una serie de modificaciones en el portal para usar los servicios web que nos ofrece. El WSDL de estos servicios está accesible según la región donde se hayan solicitado los mismos:

Asia y Pacífico (APAC) https://red003.mail.apac.microsoftonline.com/ews/Services.wsdl
Europa (EMEA) https://red002.mail.emea.microsoftonline.com/ews/Services.wsdl
Norte América (NA) https://red001.mail.microsoftonline.com/ews/Services.wsdl

El WSDL nos ofrece una completa información acerca de los webmethods disponibles para operar remotamente con Exchange, aunque para simplificar su uso utilizaremos la EWS Managed API 1.1, que podéis descargar desde este enlace.

WSDL

En los pasos siguientes vamos a realizar unas pequeñas modificaciones en nuestro portal DNN para usar estos servicios web de Exchange Online.

Modificar el DNN core para conectar con BPOS

Como lo que deseamos es ampliar la funcionalidad actual del DNN para soportar BPOS y permitir seguir seleccionando un servidor SMTP, comencemos por modificar las opciones de configuración del host –para realizar estas modificaciones será necesario disponer del código fuente de DotNetNuke Community, que pueden descargar la última versión desde este enlace.

1. Añadir el enumerado MailServerType en el proyecto DotNetNuke.Library, en el archivo LibraryServicesMailMailServerType.vb (crear el archivo):

Namespace DotNetNuke.Services.Mail
    Public Enum MailServerType
        SMTP
        BPOS
    End Enum
End Namespace

2. Añadimos en la clase DotNetNuke.Entities.Host.Host la propiedad MailServerType:

''' <summary>
''' Gets the MailServerType (SMTP or BPOS)
''' </summary>
Public Shared ReadOnly Property MailServerType As DotNetNuke.Services.Mail.MailServerType
   Get
      Return CType(HostController.Instance.GetInteger("MailServerType"), DotNetNuke.Services.Mail.MailServerType)
   End Get
End Property

3. Agregamos una referencia a la EWS Managed API. La instalación no la coloca en la GAC para prevenir problemas con otras posibles instalaciones futuras –ese es el motivo que pone en la documentación-, así que tenemos que buscar la librería que por defecto se instala en la ruta: C:Program FilesMicrosoftExchangeWeb Services1.1Microsoft.Exchange.WebServices.dll. Aseguraos de poner el ensamblado como “Copia local=true” para que se copie automáticamente el ensamblado en el “bin” de DotNetNuke al generar el core.

AddReference

Assembly

4. Importar el espacio de nombres Microsoft.Exchange.WebServices.Data a través de las propiedades del proyecto

5. Añadir un discriminador en la rutuna SendMail de la clase “Mail” para detectar si el envío es a través de Exchange Online:

' Send using BPOS instead of SMTP
If Host.MailServerType = MailServerType.BPOS AndAlso SMTPServer = Host.SMTPServer Then
   Return SendMailBPOS(MailFrom, MailTo, Cc, Bcc, ReplyTo, Priority, Subject, BodyFormat, BodyEncoding, Body, Attachments, SMTPServer, SMTPUsername, SMTPPassword)
End If

6. Agregar la rutina del envío de mensajes usando la API de Exchange:

        Public Shared Function SendMailBPOS(ByVal MailFrom As String, ByVal MailTo As String, _
            ByVal Cc As String, ByVal Bcc As String, ByVal ReplyTo As String, _
            ByVal Priority As MailPriority, ByVal Subject As String, _
            ByVal BodyFormat As MailFormat, ByVal BodyEncoding As System.Text.Encoding, ByVal Body As String, _
            ByVal Attachments As List(Of Attachment), ByVal WebServiceURL As String, _
            ByVal Username As String, ByVal Password As String) As String

            SendMailBPOS = ""
            Try
                Dim service As New ExchangeService(ExchangeVersion.Exchange2007_SP1)
                service.Url = New Uri(WebServiceURL)
                service.Credentials = New System.Net.NetworkCredential(Username, Password)

                Dim objMail As EmailMessage = New EmailMessage(service)
                objMail.From = New EmailAddress(MailFrom)
                If MailTo <> "" Then
                    objMail.ToRecipients.Add(MailTo)
                End If
                If Cc <> "" Then
                    objMail.CcRecipients.Add(Cc)
                End If
                If Bcc <> "" Then
                    objMail.BccRecipients.Add(Bcc)
                End If

                If ReplyTo <> String.Empty Then objMail.ReplyTo.Add(New EmailAddress(ReplyTo))
                Select Case Priority
                    Case MailPriority.High : objMail.Importance = Importance.High
                    Case MailPriority.Low : objMail.Importance = Importance.Low
                    Case Else : objMail.Importance = Importance.Normal
                End Select
                objMail.Subject = HtmlUtils.StripWhiteSpace(Subject, True)
                objMail.Body = New MessageBody(CType(IIf(BodyFormat = MailFormat.Html, BodyType.HTML, BodyType.Text), BodyType), Body)

If Attachments.Count > 0 Then
objMail.Save()
' Add Attachments, converting from MailAttachments to Exchange Attachements
For Each myAtt As Attachment In Attachments
Dim mFilename As String = IO.Path.GetTempFileName()
Using fStream As New IO.FileStream(mFilename, IO.FileMode.OpenOrCreate)
Dim bArray As Byte()
ReDim bArray(CInt(myAtt.ContentStream.Length))
myAtt.ContentStream.Read(bArray, 0, CInt(myAtt.ContentStream.Length))
fStream.Write(bArray, 0, bArray.Length)
fStream.Close()
End Using
objMail.Attachments.AddFileAttachment(mFilename).Name = myAtt.Name
' It's is necessary to call .Save() in order to upload the attachment to the server
objMail.Save()
Next
End If
' send the message
objMail.SendAndSaveCopy()
Catch objException As Exception
' mail configuration problem
If Not IsNothing(objException.InnerException) Then
SendMailBPOS = String.Concat(objException.Message, ControlChars.CrLf, objException.InnerException.Message)
LogException(objException.InnerException)
Else
SendMailBPOS = objException.Message
LogException(objException)
End If
End Try

End Function

 

Añadir posibilidad de configuración

Ahora toca modificar el interfaz de usuario para agregar soporte de configuración a los superusuarios del portal. Para ello, realizamos los pasos siguientes.

1. Abrir el archivo ~DesktopModulesAdminHostSettingshostsettings.ascx y buscamos en el marcado la tabla “tblSMTP”

2. Modificamos el marcado para agregar la posibilidad de indicar el tipo de servidor. También modificamos las etiquetas “tr” correspondientes a la autenticación y SSL para poder mostrarlas y ocultarlas desde el código de servidor.

    <tr>
        <td class="SubHead" style="width: 250px">
            <dnn:Label ID="plMailServerType" Text="Server type:" ControlName="cboMailServerType" runat="server" /> 
        </td>
        <td align="left">
            <asp:DropDownList ID="cboMailServerType" runat="server" AutoPostBack="true">
                <asp:ListItem Value="SMTP" resourceKey="MailServerTypeSMTP">Servidor SMTP</asp:ListItem>
                <asp:ListItem Value="BPOS"  resourceKey="MailServerTypeBPOS">Exchange Online</asp:ListItem>
            </asp:DropDownList>
        </td>
    </tr>

3. Agregamos las entradas correspondientes en los archivos .resx de recursos en la subcarpeta App_LocalResources

Resources

4. Modificamos la rutina ShowHideSMTPCredentials() y agregamos código en el evento de selección de la lista para mostrar/ocultar parámetros según esté seleccionado el tipo de servidor.

        Private Sub ShowHideSMTPCredentials()
            If optSMTPAuthentication.SelectedValue = "1" OrElse cboMailServerType.SelectedValue = "BPOS" Then
                trSMTPPassword.Visible = True
                trSMTPUserName.Visible = True
            Else
                trSMTPPassword.Visible = False
                trSMTPUserName.Visible = False
            End If
            rowSMTPAthentication.Visible = cboMailServerType.SelectedValue = "SMTP"
            rowSMTPEnableSSL.Visible = rowSMTPAthentication.Visible
        End Sub

        ''' <summary>
        ''' Fires when the Mail server type dropdownlist changes
        ''' </summary>
        Protected Sub cboMailServerType_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cboMailServerType.SelectedIndexChanged
            Try
                ShowHideSMTPCredentials()
            Catch ex As Exception
                ProcessModuleLoadException(Me, ex)
            End Try
        End Sub

5. Modificamos la rutina BindData() para volcar la propiedad configurada en el dropdown

'...
cboMailServerType.SelectedValue = Entities.Host.Host.MailServerType.ToString()
ShowHideSMTPCredentials()
'...

6. Modificamos el evento cmdUpdate_Click para guardar el dato:

 HostController.Instance.Update("MailServerType", [Enum].Parse(GetType(MailServerType), cboMailServerType.SelectedValue), False)

7. Con esto podemos configurar tanto el servidor SMTP como el servidor BPOS según nos convenga:

SMTPConfig

BPOSConfig

Al pulsar sobre el enlace “Probar” que trae incorporado DNN, realiza un envío de mensaje de prueba, que como podemos observar procesa correctamente nuestro Exchange Online.

EnvioCorrecto

Conclusión

En este artículo hemos visto la posibilidad de realizar envíos de correos desde DNN a través de Exchange Online. Para ello hemos utilizado la EWS Managed API 1.1, que facilita el manejo de los servicios web expuestos por Exchange.

Si en este ejemplo lo que hemos visto es cómo realizar los envíos a través de estos servicios, los servicios que ofrece la API son numerosos pudiéndose realizar casi cualquier cosa.

¿Cuándo comenzaremos a ver módulos desarrollados para explotar las características de Exchange desde nuestro portal DNN? Voy a ver si veo alguno por SnowCovered Sonrisa

Espero que os haya servidor de ayuda.

Un saludo y happy coding!

Examinando Razor-Helpers

Continuando con la serie de artículos sobre WebMatrix, Razor y su integración con DotNetNuke, hoy vamos a echar un vistazo a los Helpers que están a nuestra disposición para agregar funcionalidad de terceros a nuestros portales de una manera sencilla y rápida.

Si bien en este artículo me he centrado en el uso desde DotNetNuke, la información es totalmente válida para la integración con cualquier otro portal o tecnología como ASP.NET MVC3 que implementen las librerías de extensión que dan soporte a Razor.

Libraries

NOTA IMPORTANTE: hay una librería (System.Web.WebPages.Deployment) que no está incluida en la instalación actual de DotNetNuke y que viene incluida en la instalación de MVC 3. Hay que tenerla en el equipo para que funcione correctamente. A través de este enlace tenéis más información sobre el error.

Actualmente, la lista de Helpers no es que sea muy grande, pero hay buenos y útiles ejemplos. Esperemos que en un futuro este ecosistema siga extendiéndose para facilitar las integraciones con terceros.

Twitter Helper

Para comenzar hoy con un ejemplo muy sencillo, echémosle un vistazo al Helper de Twitter con el objetivo de incluir un módulo en la barra lateral de nuestro sitio.

Para ello, agregamos un módulo RazorHost a una página cualquiera –para este ejemplo he creado una página en blanco en el sitio:

Twitter

Al pulsar el botón de añadir, hemos agregado un módulo en blanco RazorHost con el título Twitter. Ahora hay que entrar a configurarlo, ya que hasta que no se le indique el script Razor que tiene ejecutar no hará nada.

Para hacerlo, hay que tener en cuenta que los administradores del portal sólo pueden seleccionar scripts predefinidos por los superusuarios, por lo que si se desea agregar y/o modificar un script es necesario iniciar sesión como super-usuario.

ModAdminModSuperUser

TwitterScript

TwitterResultEntrando como superusuario y pulsando sobre el botón “Edit Script”, nos sale el interfaz de edición de Razor Script. Tenemos la suerte que el Helper de Twitter viene como uno de los ejemplos en la instalación de DotNetNuke, con lo que si lo seleccionamos y modificamos unos cuantos parámetros –ver documentación completa del Helper de Twitter– ya tenemos la integración con Twitter resuelta al pulsar el botón “Guardar Script y Volver”.

 

Conclusión

Como ya hemos visto, integrar con RazorHost un Helper de terceros es sumamente sencillo, siendo el widget de Twitter un buen ejemplo. En los siguientes artículos iremos viendo la integración con otros Helpers más interesantes, como pueden ser la integración con Facebook, PayPal o ¡Windows Azure! Sí también hay helper de Windows Azure para manejar tablas y blobs.

Por lo que he visto, WebMatrix está pensado para desarrollos que no sean complejos ni que vayan a requerir un alto grado de mantenimiento, con un enfoque hacia el desarrollador iniciado que necesite una herramienta sencilla pero con un alto grado funcional.

Si bien trabajar con Razor puede que haga recordar que volvemos a trabajar con ASP tradicional (no ASP.NET), creo que encaja muy bien en aquellos desarrollos cortos en presupuesto y tiempo. Tampoco hay que olvidar la posibilidad de desarrollar nuestros propios helpers Razor para que puedan ser integrados, con lo que también podría facilitar la integración de distintos roles en los equipos de desarrollo.

¿Vosotros qué opináis?

Un saludo y ¡Happy Coding!

Helloworld Geeks.ms

GeeksDespúes de unos días algo ajetreados, estoy de vuelta ante el Live Writer dispuesto a hacer un resumen de las cosas que he ido viendo durante la semana y que por una serie de acontecimientos no me ha dado tiempo de comentar.

Antes de entrar en materia, quiero agradecer a Rodrigo Corral el permitir unirme a Geeks.ms como miembro activo. Sé que tiene una agenda apretadísima y aún así ha sacado tiempo para hacer que pueda participar en esta comunidad.

También quería agradecer a José Fortes, Alberto Díaz y a Josue Yeray el apoyo desinteresado que me han brindado y hacerme ver las cosas desde otro punto de vista.

A modo de presentación, para los que no me conozcan desde Geeks, he sido desarrollador, analista y responsable de equipo de desarrollo durante 14 años, especializado en sistemas de reservas para compañías marítimas y aéreas. Los últimos años he estado dedicando todo mi interés hacia el desarrollo en la nube y Windows Azure, y por una carambola el año pasado acabé metiéndome de lleno también con DotNetNuke al conseguir migrar el portal a Windows Azure. Así que últimamente mis posts se centran en experiencias sobre Windows Azure y DNN, aunque pueden esperar que escriba sobre cualquier cosa sobre desarrollo .NET, SOA o TFS. En este enlace tenéis la entrada con la que abrí mis paseos por la nube en la que podéis conocerme mejor.

A la gente que ha venido siguiéndome en mi blog habitual, comentarles que publicaré de igual modo en ambos blogs, con lo que podrán encontrar el mismo contenido que publicaré en Geeks. Si quieres saber más sobre qué es Geeks puedes echarle un vistazo a esta entrada de Rodrigo Corral en la que se explica con mayor detalle su filosofía.

Espero que mis artículos sigan siendo de interés y enriquecerme con vuestros comentarios.

Un saludo,

David Rodríguez