Syntax Highlighter

sábado, 29 de enero de 2011

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 \Library\Services\Mail\MailServerType.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 Files\Microsoft\Exchange\Web Services\1.1\Microsoft.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 ~\DesktopModules\Admin\HostSettings\hostsettings.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!

sábado, 22 de enero de 2011

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

domingo, 16 de enero de 2011

Cómo instalar RazorHost en DotNetNuke después de un upgrade

Siguiendo el hilo de ayer sobre WebMatrix y DotNetNuke, los siguientes posts van a ir dedicados a ir viendo cómo integrar distintas utilidades como Twitter o Facebook en nuestro portal a través de los Helpers, que vienen a ser una especie de scriptlets de uso muy sencillo.

Abrimos este primer post solucionando un posible problema de instalación del módulo RazorHost que es el encargado de ejecutar los scripts Razor. Hay que tener en cuenta que si partes de la instalación 5.6.1 (el próximo miércoles 19 de enero sale la versión definitiva) posiblemente no tendrás este problema ya que vendrá preinstalado. Yo me he encontrado con el problema de instalación en un portal del que he hecho un upgrade de la versión 5.6.0 a la 5.6.1 RC. 

El problema

Una vez que hemos hecho el upgrade de la versión 5.6.0 a la 5.6.1, nuestra intención es añadir el módulo RazorHost para añadir scripts Razor en nuestro portal, pero éste no aparece en la lista de módulos disponibles. Esto es debido a que la actualización no instala el módulo de forma predeterminada y hay que realizarlo a través del interfaz de instalación de extensiones en Host > Extensiones. 

Sin embargo, al añadir el módulo nos da un error de instalación del que no hay ninguna descripción. Tampoco se observa nada en el visor de eventos de DNN con más información.


La solución

Para dar con la solución, he decidido realizar una instalación manual del módulo RazorHost a través del asistente de instalación que se encuentra en Host > Extensiones. Para ello, hay que realizar una copia del archivo /Install/Module/RazorHost_01.00.00_Install.resources con el nombre /Install/Module/RazorHost_01.00.00_Install.zip (el asistente de instalación de extensiones sólo reconoce esta extensión). Al seguir los pasos del asistente nos da la primera pista del error:

Al parecer la instalación hace uso de la clase System.Tuple, que no encuentra instalada en el portal. Esta clase pertenece a .NET Framework 4.0. 


Si en nuestro PC tenemos instalado .NET Framework 4.0, entonces ¿porqué da este error de instalación? La  incógnita se resuelve al revisar la configuración del pool de aplicaciones sobre el que se está ejecutando la aplicación. Por defecto, la instalación de WebMatrix de DNN ha configurado la aplicación en un pool con .NET 2 (Integrated).


Al modificar la configuración del pool de aplicaciones y seleccionar .NET 4, el módulo RazorHost se instala sin problemas. Habrá que tenerlo en cuenta para futuras instalaciones a través de WebMatrix.

Una vez que hemos instalado el módulo, ya podemos seleccionarlo de la lista de módulos a agregar a una página, con lo que ya podemos ponernos a trastear agregando Helpers a nuestro portal.


Espero que sirva de ayuda. 

¡Happy coding!






sábado, 15 de enero de 2011

Introducción a WebMatrix + DotNetNuke

El pasado 13 de enero tuvo lugar el evento CodeMash 2011 en el que se lanzó WebMatrix. Sinceramente no había trasteado hasta ahora con el producto, había oído hablar de él hace tiempo, de la sintaxis Razor que se utiliza, etc. pero muy por encima. Pero unos twits de @esmsdn despertaron mi curiosidad al decir que estaban haciendo una demo en vivo desplegando un portal DotNetNuke con la herramienta. ¿Desplegando DotNetNuke con WebMatrix? ¿Cómo? Y luego otro hablando de despliegues de Umbraco, Drupal, ...¿perdón? Nada, tarea para el fin de semana.

Y una vez que he estado trasteando hoy y visto alguna guía de iniciación a WebMatrix para aclarar conceptos, me he quedado realmente boquiabierto. 

¿Qué es WebMatrix? Es una herramienta de Microsoft con la que se pueden crear proyectos ASP.NET de forma sencilla -recalco lo de forma sencilla porque con ese eslógan abrieron la sesión de CodeMash sobre WebMatrix.

Pero no sólo ASP.NET, y aquí es donde empieza el baile de novedades y siglas de los que he podido percatarme: 
  • se trabaja de igual modo con PHP, mySQL, SQL Server, de forma integrada en el entorno de trabajo. También integración con Visual Studio y control de código fuente para soluciones más avanzadas
  • se están generando infinidad de addons y scriptlets -denominados Helpers- para integrar en nuestras webs módulos de conexión con terceros (p.e. añadir Twitter o Facebook, realizar una compra PayPal, o interactuar con almacenamiento en Azure con 1 línea de código)
  • una infinidad de plantillas web y motores CMS open source como punto de partida (ahora mismo cuento 39) que están integradas con la herramienta: DotNetNuke, WordPress, Umbraco CMS, ASP.NET MVC 3, nopCommerce, mojoPortal, Drupal, Moodle, Joomla, SugarCRM, MonoX, y un largo etc.
  • publicación y actualización directa de los websites
  • generación de informes: rendimiento, SEO, etc. para corregir errores comunes
Y todo esto...¡GRATIS! ¿Cómo? Sí...¡GRATIS!


Mi primer portal DNN con WebMatrix

Lo primero que quise comprobar después de ver a la gente de DNN en la presentación, fue seguir unos sencillos pasos para crear un sitio web con DotNetNuke a través de la herramienta. En menos de 15 minutos, ya estaba funcionando y otros 5 después publicado en Internet. ¡Mola!

Para empezar, hay que instalar WebMatrix a través del Web Platform Installer. Para ello se puede seguir este enlace. Una vez instalado, podemos seguir descargando e instalando plantillas para tenerlas ya preconfiguradas en WebMatrix y no tener que descargarlos en el momento del primer uso de las plantillas. En esta prueba descargué la plantilla de DotNetNuke para ver qué tal iba:



NOTA: a día de hoy, la versión de DNN que se descarga es la 5.6.0. La que presentaron en CodeMash es la 5.6.1 RC del 13 de enero que tiene soporte Razor y que ahora mismo es Beta, por lo que tuve que descargarme manualmente  desde CodePlex esa versión para ver lo de los Helpers. En la sesión comentaron que el día 19 de enero la versión será definitiva, por lo que no habrá que hacer ningún paso adicional a partir de esa fecha.

Una vez instalado, se abre una simple interfaz para seleccionar lo que queremos hacer: construir un sitio web a partir de la galería de plantillas -también se puede abrir un sitio DNN pre-existente con el botón derecho del ratón:




A continuación nos sale la galería...y se me ponen los pelos de punta :). Seleccionamos DNN, le ponemos el nombre al sitio y le damos a continuar:


El paso siguiente es la selección del tipo de base de datos -DNN sólo soporta SQL Server. Hay una peculiaridad a tener en cuenta. El asistente busca una instancia local de SQL Express, dándote la opción de instalarla si no la encuentra, aunque también te da la opción de seleccionar un servidor de base de datos remoto. En el caso de que tengas una edición superior instalada en local (SQL Standard, por ejemplo) el asistente no la detectará, por lo que debes seleccionar la opción "remoto" y apuntar a tu PC.


Comienza el proceso de instalación que completa con éxito:

Inmediatamente después se abre el portal en WebMatrix listo para trastear con él. Simplemente pulsando sobre el enlace que nos propone se abre el navegador para realizar la configuración de primer uso de DNN para instalar los módulos, etc.-no explico eso aquí ya que no difiere en nada de la configuración de cualquier portal DNN.


A través de las pestañas de la barra inferior izquierda, podemos acceder a distintas opciones como acceder a los ficheros del portal -véase integración con Visual Studio-, manipular la base de datos o obtener informes de rendimiento y SEO del portal.





Volviendo a la pestaña principal, pulsamos sobre el botón de publicación. Esta publicación realiza un proceso de búsqueda de cambios desde la última publicación, con lo que tenemos la opción de subir sólo los ficheros que han sido modificados desde ésta. El proceso también realiza una prueba inicial de compatibilidad de publicación para comprobar que se puede publicar sin problemas.

Para publicarlo lo podemos hacer a través de FTP o un Web Deploy, y la herramienta también nos ayuda a buscar un proveedor de hosting en caso de no disponer de alguno. En mi caso, he utilizado publicación por FTP en un servidor en GoGrid y la subida ha transcurrido sin problemas.


Una vez que pasamos por el taller de chapa y pintura, ya tenemos nuestro portal funcionando en la red. Nótese que en la imagen he remarcado el nuevo módulo que da soporte Razor al portal y a través del cual podremos utilizar todos los Helpers de terceros, como Facebook, Twitter, PayPal o Azure (ver enlaces al final del post). Esa parte la voy a dejar para un segundo post, ya que si no éste se va a hacer eterno. El vídeo de CodeMash sobre el módulo de eventos me dio una idea para el registro de eventos de TenerifeDev :)



Conclusión

La frase de Leonardo da Vinci con la que abrieron las sesiones lo resume todo: "La simplicidad es la máxima sofisticación". Tener a nuestra disposición una herramienta como esta, de acabado excepcional y con integración con las últimas tecnologías en lo que se refiere a CMS, redes sociales, Azure, PayPal, etc. es casi impensable, pero...¿gratis? Y vuelvo a recalcar...¿GRATIS? 

Para todo lo demás... :)

Os dejo dos enlaces por si queréis seguir ampliando conocimientos. Mañana más.

jueves, 13 de enero de 2011

Nuevo evento TenerifeDev: MadeinLocal.com

Después de la buena aceptación que tuvo el primer evento TenerifeDev el pasado 22 de diciembre seguimos buscando temas para dar a conocer ideas interesantes y apuestas por el desarrollo de software en nuestro territorio.

Una de estas interesantes y novedosas ideas llevada a cabo con éxito es MadeinLocal.com, una red social que conecta al usuario con sus amigos y con lo mejor de la ciudad al mismo tiempo. Esta red social desarrollada por emprendedores canarios ha ganado numerosos premios internacionales:
Pues en TenerifeDev queremos dar a conocer de primera mano cómo han conseguido este éxito a través de dos charlas ofrecidas directamente por los responsables del mismo. Hemos dividido el evento en dos partes: la primera para introducir MadeinLocal.com y los pasos que se han seguido desde el punto de vista de emprendeduría para hacer posible la idea; la segunda para ver con más detalle los aspectos tecnológicos y las tendencias a nivel internacional.

El calendario de las charlas es el siguiente:

MadeinLocal.com, la red social canaria en la web, en tu iPhone y en Android
Fecha y hora: lunes 7 de febrero de 17:30h a 19:30h (GMT)
Lugar: Salón de grados de la Escuela Técnica Superior de Ingeniería Informática (ETSII)
Descripción: MadeinLocal.com es una red social que conecta al usuario con sus amigos y con lo mejor de la ciudad al mismo tiempo. Esta red social desarrollada por emprendedores canarios ha ganado numerosos premios internacionales
Ponente: Cristina Acevedo
Formulario de registro >

El uso de las nuevas tecnologías en MadeinLocal.com
Fecha y hora: lunes 14 de febrero de 17:30h a 19:30h
Lugar: Salón de grados de la Escuela Técnica Superior de Ingeniería Informática (ETSII)
Descripción: Hablaremos de las diferentes tecnologías que integran los productos de Madeinlocal.com y las tendencias a nivel internacional. La charla contará con la presencia de Manuel Acevedo, responsable de desarrollo de Madeinlocal, en videoconferencia desde Suiza.
Ponente: Cristina Acevedo y Manuel Acevedo (videoconferencia)
Formulario de registro >

Si quieres seguir sus noticias y novedades puedes unirte al grupo de MadeinLocal.com en Facebook. Para ir abriendo boca, os dejo una entrevista a Manuel Acevedo, CEO de MadeinLocal.com:



domingo, 9 de enero de 2011

DotNetNuke en Azure: usando Blob Storage

Hoy abrimos el primer post del año retomando el trabajo pendiente sobre la migración de DNN a la plataforma Windows Azure, esta vez con la utilización de Azure Storage para el almacenamiento de contenidos.

Partiendo del módulo "FileManager" que viene de serie con el portal, podemos observar que se pueden utilizar 3 tipos de soporte de archivos para los archivos -ver post de Mitchel Sellers para información ampliada:

  • Sistema de archivos (estándar): la gestión de archivos se realiza sobre el sistema de ficheros tradicional, tratándose de una simple colección de carpetas y archivos en disco;
  • Sistema de archivos (seguro): similar a la anterior salvo que utiliza internamente el "truco" de agregarles la extensión ".resources" a cada archivo con lo que no se puede tener acceso directo por URL a los mismos, sino que hay que utilizar un mecanismo de procesamiento de ficheros a través del archivo "LinkClick.aspx";
  • Base de datos (seguro): este sistema lo que hace es almacenar el contenido de los ficheros en SQL Server, con lo que hay que utilizarlo con cuidado dependiendo del tamaño de los archivos que se suban al mismo o la base de datos crecerá demasiado -recordemos que el coste de disco es mucho más barato que el de SQL Server- además de tener un procesamiento adicional en servidor para la manipulación de los mismos.
Al llevar el portal a Azure, nos encontramos rápidamente con el dilema de dónde colocar los contenidos:
  • Si usamos sistema de archivos -independientemente de si es en modo seguro o estándar- nos topamos con el problema de que al reciclarse el webrole ante cualquier eventualidad, los archivos desaparecen ya que se vuelve a desplegar una nueva imagen del webrole. Una solución podría ser reescribir el código para que utilice LocalStorage en este sistema de almacenamiento, aunque también habría que retocar toda la parte de sincronización de los sistemas de archivos entre las distintas instancias del webrole;
  • Si usamos el sistema de base de datos, nos encontramos con los problemas descritos anteriormente. Imaginad almacenar un vídeo de varios Gb en SQL Azure, aparte de ser una burrada nos come el presupuesto :)

Azure Blob Storage al rescate

Una solución idónea, es la de utilizar Azure Blob Storage para el almacenamiento de contenidos. Si estos contenidos son públicos, no hace falta ni siquiera que el portal esté migrado a Azure, sino que se podría usar Blob Storage simplemente para almacenamiento en Azure de los contenidos -con todas los beneficios que aporta- mientras que el portal puede seguir estando alojado en tu hosting de siempre.

Echando un vistazo a la versión de DotNetNuke Community -recuerden que siempre trabajo con la versión completa con código fuente para mayor flexibilidad en los desarrollos- realizaremos una serie de modificaciones para que podamos interactuar con Azure desde el mismo portal. 

Comenzando por los tipos de sistemas de archivos, nos encontramos con el enumerado "StorageLocationTypes" al que le vamos a agregar un nuevo valor, que será por el que discriminaremos más tarde en todas las operaciones:

        Enum StorageLocationTypes
            InsecureFileSystem = 0
            SecureFileSystem = 1
            DatabaseSecure = 2
            AzureStorage = 3
        End Enum

A continuación modificamos el módulo FileManager para agregar la opción de poder crear carpetas de este tipo:
Private Sub BindStorageLocationTypes()
   ...
  ddlStorageLocation.Items.Add(New ListItem(Localization.GetString("AzureStorage", Me.LocalResourceFile), "3"))
End Sub

Con ello, agregamos a la lista desplegable el nuevo tipo de carpetas (los archivos creados en un tipo de carpeta "heredan" el tipo de almacenamiento) y podemos crear carpetas de este tipo.


Ahora toca modificar el código para que cuando se cree un tipo de carpetas no cree la estructura lógica en el sistema de archivos. Realmente para las carpetas no se debe hacer nada más, ya que en Blob Storage no existe el concepto de carpetas, sino que todos los archivos cuelgan de un mismo container. El concepto de directorios virtuales parte de la idea de utilizar en el nombre de los blobs el carácter "/", con lo que da la apariencia de estar organizados de tal modo.

La rutina a modificar es "AddFolder" de FileSystemUtilities.vb

Public Shared Sub AddFolder(ByVal _PortalSettings As PortalSettings, ByVal parentFolder As String, ByVal newFolder As String, ByVal StorageLocation As Integer, ByVal uniqueId As Guid)
           
          .......
If StorageLocation <> FolderController.StorageLocationTypes.AzureStorage Then
   Dim dinfo As New System.IO.DirectoryInfo(parentFolder)
   Dim dinfoNew As System.IO.DirectoryInfo = New System.IO.DirectoryInfo(parentFolder & newFolder)
   If Not dinfoNew.Exists Then
      dinfoNew = dinfo.CreateSubdirectory(newFolder)
   End If
   FolderPath = dinfoNew.FullName.Substring(ParentFolderName.Length).Replace("\", "/")
Else
   FolderPath = (parentFolder.Replace(ParentFolderName, "") & newFolder).Replace("\", "/")
End If

'Persist in Database
AddFolder(PortalId, FolderPath, StorageLocation, uniqueId)
End Sub

Una vez que podemos crear las carpetas de tipo Azure, todos los archivos que creemos dentro serán del mismo tipo. Al pulsar el botón "Subir archivo" nos redirige a la página de upload. Seleccionamos un archivo y para que este se suba correctamente a nuestro Blob Storage hay que realizar las siguientes modificaciones:

1) Añadir dos entradas en el web.config -o en el archivo de configuración del servicio si tienes migrado el portal a Azure :)- con los datos de configuración:

<add key="WindowsAzure.DataStore.ConnectionString" value="UseDevelopmentStorage=true"/>
<add key="WindowsAzure.DataStore.RootContainer" value="dotnetnuke"/>

2) Agregar una función para la creación del container de Blobs en Azure:

Public Shared Function GetAzureRootContainer() As CloudBlobContainer

' Setup the connection to Windows Azure Storage
Dim storageAccount As CloudStorageAccount = CloudStorageAccount.Parse(Config.GetSetting("WindowsAzure.DataStore.ConnectionString"))
Dim _BlobClient As CloudBlobClient = storageAccount.CreateCloudBlobClient()

' Create the root container
Dim _BlobContainer As CloudBlobContainer = _BlobClient.GetContainerReference(Config.GetSetting("WindowsAzure.DataStore.RootContainer").ToLower())
_BlobContainer.CreateIfNotExist()

' Setup the permissions on the container to be public
Dim permissions As New BlobContainerPermissions()
permissions.PublicAccess = BlobContainerPublicAccessType.Container
_BlobContainer.SetPermissions(permissions)

' Create the Portals container
Return _BlobContainer
End Function

3) Modificar la función "AddFile" de FileSystemUtilities.vb para subir el archivo a Azure:

Private Shared Function AddFile(ByVal PortalId As Integer, ByVal inStream As Stream, ByVal fileName As String, ByVal contentType As String, ByVal length As Long, ByVal folderName As String, ByVal closeInputStream As Boolean, ByVal clearCache As Boolean, ByVal synchronize As Boolean) As String

...
            'Calculate hash/save file to db/save file to filesys
            Select Case folder.StorageLocation
                Case FolderController.StorageLocationTypes.InsecureFileSystem, FolderController.StorageLocationTypes.SecureFileSystem
                    'Save file to File Storage
...                   
                Case FolderController.StorageLocationTypes.DatabaseSecure
'Save file to Secure Database
... 
                Case FolderController.StorageLocationTypes.AzureStorage
'Save file to Azure Storage

                    Dim blobContainer As CloudBlobContainer = GetAzureRootContainer()
                    Dim blobName As String = "Portals/" & PortalId & "/" & objFile.RelativePath
                    Dim blob As CloudBlob = blobContainer.GetBlockBlobReference(blobName)
                    blob.Properties.ContentType = contentType
                    blob.UploadFromStream(inStream)

                    'Add file to Database
                    intFileID = objFileController.AddFile(objFile)

            End Select
... 

End Function

Finalmente, modificando la función DownloadFile de FileSystemUtilities.vb, redireccionamos las peticiones de descarga del archivo al blob en Azure Storage:

Public Shared Function DownloadFile(ByVal PortalId As Integer, ByVal FileId As Integer, ByVal ClientCache As Boolean, ByVal ForceDownload As Boolean) As Boolean
Dim blnDownload As Boolean = False

' get file
Dim objFiles As New FileController
Dim objFile As DotNetNuke.Services.FileSystem.FileInfo = objFiles.GetFileById(FileId, PortalId)
If Not objFile Is Nothing Then
Dim filename As String = objFile.FileName

...

' check folder view permissions
If FolderPermissionController.CanViewFolder(objFolder) Then
Dim blnFileExists As Boolean = True
 ...
' download file
If objFile.StorageLocation = FolderController.StorageLocationTypes.AzureStorage Then
Dim objResponse As HttpResponse = HttpContext.Current.Response
Dim FileURL As String = objFiles.GetAzureBlobUri(objFile)
If FileURL = "" Then
objResponse.Write("Error : No se encontró el Blob en Azure Storage")
Else
objResponse.Redirect(FileURL)
blnDownload = True
End If

ElseIf blnFileExists Then
                        'Stream the file to the response
 ...
End If
End If

Return blnDownload

End Function

Viendo los resultados 

En la imagen siguiente se puede observar cómo queda el módulo FileManager con soporte para Azure Blob Storage:


Usando una herramienta como Azure Storage Explorer podemos ver cómo se ha creado la estructura dentro del Blob Container:


Del mismo modo, al pulsar sobre el enlace en el módulo del FileManager, podemos comprobar que está funcionando correctamente la URL del blob:


El resumen de archivos modificados es:
  • WebSite/DesktopModules/Admin/FileManager/FileManager.ascx.vb - Para agregar el item al dropdownlist
  • WebSite/Web.config - Para añadir dos entradas de configuración
  • Library\Services\FileSystem\FileController.vb - Controlador de ficheros
  • Library\Services\FileSystem\FolderController.vb - Controlador de carpetas
  • Library\Services\FileSystem\FileSystemUtilities.vb - Rutinas de manejo de archivos y carpetas

Conclusión 
Agregar el soporte para Azure Blob Storage no es una tarea complicada gracias a que de partida el portal está bien diseñado para poder ampliarlo en este sentido. En este post he simplificado a la creación de carpetas y archivos en Azure para un mejor entendimiento, faltando los métodos de borrado, modificación de nombres, etc. Si alguien quiere que se los muestre que ponga un comentario, aunque como se ve no es muy complicado de realizar.

Quiero recalcar que estas modificaciones las he realizado sobre la versión original de DotNetNuke Community que no está migrada a Azure, con lo que este capítulo de la serie de posts sobre DotNetNuke puede aplicarse independientemente y cualquiera puede hacerlo.

Espero que haya sido de interés. Cualquier duda ya sabes, escribe un comentario.

Un saludo,

David Rodríguez



Related Posts Plugin for WordPress, Blogger...