SharePoint 2007 + Outlook 2007: Guardar correos de forma masiva (VI)

Entradas anteriores de la serie:

SharePoint 2007 + Outlook 2007: Guardar correos de forma masiva (I)

SharePoint 2007 + Outlook 2007: Guardar correos de forma masiva (II)

SharePoint 2007 + Outlook 2007: Guardar correos de forma masiva (III)

SharePoint 2007 + Outlook 2007: Guardar correos de forma masiva (IV)

SharePoint 2007 + Outlook 2007: Guardar correos de forma masiva (V)


bender3

Ahora que parece que Bender está un poco más sobrio que de costumbre, prosigamos con la serie... que sabéis como se pone cuando no bebe.

Menú del día:

De primero: Cómo guardar un fichero en una biblioteca de documentos de SharePoint manteniendo el control de versiones.

De segundo: Cómo modificar los metadatos del fichero una vez subido a la biblioteca (From, To, Subject).

Postre: Pastel especial de la casa.

Pan, Vino y Café incluidos

El primero:

Es curioso, pero SharePoint en la versión actual no implementa “de serie” ningún mecanismo para subir documentos a una librería de documentos. Ahora que nos hemos quedado con la boca abierta, vamos a explicar algunos mecanismos para hacerlo por nuestra cuenta.

El bueno de mi tío Rodrigo hace un tiempo que publicó un post al respecto, en el cual se mostraba cómo subir ficheros a un SharePoint. Pero como él mismo decía, la pega estaba en que usando esta técnica no se disponía de historial de versiones. La alternativa era crearse un WebService propio, tal y como se explica en este artículo, aunque es una solución que no siempre se puede usar. Hoy veremos una tercera alternativa que permite hacerlo sin tener que recurrir a la creación de un WebService. La he probado en un servidor MOSS y en un par de WSS, uno local y el otro hospedado en un hosting en USA. Y en ambos funciona perfectamente, aunque en el segundo los tiempos se demoran un poco...

Describamos los elementos que necesitamos a continuación:

Para empezar vamos a crear una clase FileInfo para encapsular lo relativo al fichero que vamos a subir (nombre, propiedades, URI, y por supuesto los bytes):

public class FileInfo
{            
    public string m_URL;            
    public byte[] m_bytes;            
    public Dictionary<string, object> m_properties;            
    public ListInfo m_listInfo;            
    public bool m_ensureFolders = true;            
    private Uri m_uri;    
    
    public bool HasProperties            
    {                
        get 
        { 
            return m_properties != null && m_properties.Count > 0; 
        }            
    }            
 
    public string RelativeFilePath            
    {                
        get 
        { 
            return m_URL.Substring(m_URL.IndexOf(m_listInfo.m_rootFolder) + 1); 
        }            
    }          
    
    public Uri URI      
    {               
        get                
        {                    
            if (m_uri == null) m_uri = new Uri(m_URL);                    
            return m_uri;                
        }            
    }            
    
    public string LookupName            
    {                
        get  
        {                    
            if (m_listInfo != null && !string.IsNullOrEmpty(m_listInfo.m_listName))   
                return m_listInfo.m_listName;      
            return URI.LocalPath;           
        }            
    }            
    
    public FileInfo(string url, byte[] bytes, Dictionary<string, object> properties)    
    {                
        m_URL = url.Replace("%20", " ");     
        m_bytes = bytes;               
        m_properties = properties;     
    }                    
}

A continuación un método Upload con dos sobrecargas, que será el que invocaremos desde la ventana encargada de mostrar el progreso de la operación. Éste será el encargado de recibir la URL de destino, los bytes del fichero a subir (previamente debemos guardar el elemento de correo en un fichero o stream), las propiedades, y el elemento de correo para poder actualizar las columnas de metadatos. Este método Upload será el encargado de a su vez llamar al método TryToUpload, que realmente será el encargado de subir el fichero:

public bool Upload(string destinationUrl, byte[] bytes, Dictionary<string, object> properties, Outlook.MailItem item) 
{
    return Upload(new FileInfo(destinationUrl, bytes, properties), item);
}
 
public bool Upload(FileInfo fileInfo, Outlook.MailItem item)
{
    if (fileInfo.HasProperties)
        fileInfo.m_listInfo = m_lists.Find(fileInfo);
    bool result = TryToUpload(fileInfo, item);
    if (!result && fileInfo.m_ensureFolders)
    {
        string root = fileInfo.URI.AbsoluteUri.Replace(fileInfo.URI.AbsolutePath, "");  
        for (int i = 0; i < fileInfo.URI.Segments.Length - 1; i++)     
        {
            root += fileInfo.URI.Segments[i];
            if (i > 1) CreateFolder(root);
        }
        result = TryToUpload(fileInfo, item);
    }            
    return result;
}

Y para terminar, el método TryToUpload que se encarga de crear el objeto WebRequest necesario, crear el stream y actualizar los valores de las propiedades de los campos, para invocar finalmente el método UpdateListItems del servicio Web:

private bool TryToUpload(FileInfo fileInfo, Outlook.MailItem item)
{
    try
    {
        WebRequest request = WebRequest.Create(fileInfo.m_URL);
        request.Credentials = sharePointLists.Credentials;  
        request.Method = "PUT";
        byte[] buffer = new byte[1024];
        using (Stream stream = request.GetRequestStream())
        using (MemoryStream ms = new MemoryStream(fileInfo.m_bytes))
            for (int i = ms.Read(buffer, 0, buffer.Length); i > 0; i = ms.Read(buffer, 0, buffer.Length))
            {
                stream.Write(buffer, 0, i);
            }
        WebResponse response = request.GetResponse();
        response.Close();
        if (fileInfo.HasProperties)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<Method ID='1' Cmd='Update'><Field Name='ID'/>");
            sb.AppendFormat("<Field Name='FileRef'>{0}</Field>", fileInfo.m_URL);
            foreach (KeyValuePair<string, object> property in fileInfo.m_properties)
            {
                sb.AppendFormat("<Field Name='{0}'>{1}</Field>", property.Key, property.Value);
            }
            updateMetadataColumns(fileInfo, item, sb);
            sb.Append("</Method>");
            System.Xml.XmlElement updates = (new System.Xml.XmlDocument()).CreateElement("Batch");
            updates.SetAttribute("OnError", "Continue");
            updates.SetAttribute("ListVersion", fileInfo.m_listInfo.m_version);
            updates.SetAttribute("PreCalc", "TRUE");
            updates.InnerXml = sb.ToString();
            sharePointLists.Url = fileInfo.m_listInfo.m_webUrl + "/_vti_bin/Lists.asmx";
            XmlNode updatesResponse = sharePointLists.UpdateListItems(fileInfo.m_listInfo.m_listName, updates);    
            if (updatesResponse.FirstChild.FirstChild.InnerText != "0x00000000")
                throw new Exception("Could not update properties."); 
        }
        return true;
    }
    catch (WebException ex)
    {
        throw ex;
    }
}

 

Lo juntamos todo, compilamos, probamos a subir algunos mensajes varias veces y voilá! Si observamos el control de versiones veremos lo siguiente:

ControlVersiones

El segundo:

Si hemos observado con atención el código del método TryToUpload seguramente hemos visto una llamada a un método updateMetaDataColumns, al cual se le pasa el StringBuilder que estamos construyendo, para que nos agregue los valores de los metadatos (cuya sintaxis que expresarse en XML según la ayuda del método UpdateListItems). El código es muy sencillo y básicamente lo que hace es comprobar si en la lista destino existen las columnas de metadatos, y en caso afirmativo modificar su valor:

private void updateMetadataColumns(FileInfo fileInfo, Outlook.MailItem item, StringBuilder sb)
{
    var cols = from c in SharePointExtensions.getListColumns(
               sharePointLists, fileInfo.m_listInfo.m_listName)
               where c.Hidden == false && c.Sealed == false && c.ReadOnly == false
               select c;
    Dictionary<string, SPColumnInfo> columns = cols.ToDictionary(c => c.Name);
    if (columns.ContainsKey(Properties.Settings.Default.COL_SUBJECT) && item != null)
    {
        sb.AppendFormat("<Field Name='{0}'>{1}</Field>",
            Properties.Settings.Default.COL_SUBJECT, item.Subject);
    }
    if (columns.ContainsKey(Properties.Settings.Default.COL_FROM) && item != null)
    {
        sb.AppendFormat("<Field Name='{0}'>{1}</Field>",
            Properties.Settings.Default.COL_FROM, item.SenderName);
    }
    if (columns.ContainsKey(Properties.Settings.Default.COL_TO) && item != null)
    {
        sb.AppendFormat("<Field Name='{0}'>{1}</Field>",
            Properties.Settings.Default.COL_TO, item.To);
    }
    if (columns.ContainsKey(Properties.Settings.Default.COL_CC) && item != null)
    {
        sb.AppendFormat("<Field Name='{0}'>{1}</Field>",
            Properties.Settings.Default.COL_CC, item.CC);
    }
    if (columns.ContainsKey(Properties.Settings.Default.COL_BCC) && item != null)
    {
        sb.AppendFormat("<Field Name='{0}'>{1}</Field>",
            Properties.Settings.Default.COL_BCC, item.BCC);
    }
    if (columns.ContainsKey(Properties.Settings.Default.COL_SIZE) && item != null)
    {
        sb.AppendFormat("<Field Name='{0}'>{1}</Field>",
            Properties.Settings.Default.COL_SIZE, item.Size);
    }
    if (columns.ContainsKey(Properties.Settings.Default.COL_SENT) && item != null)
    {
        string dt = item.SentOn.ToString("yyyy-MM-ddTHH:mm:ssZ");
        sb.AppendFormat("<Field Name='{0}'>{1}</Field>",
            Properties.Settings.Default.COL_SENT, dt);
    }
    if (columns.ContainsKey(Properties.Settings.Default.COL_IMPORTANCE) && item != null)
    {
        string im = string.Empty;
        switch (item.Importance)
        {
            case Microsoft.Office.Interop.Outlook.OlImportance.olImportanceHigh:
                im = Properties.Settings.Default.COL_IMPORTANCE_HIGH;
                break;
            case Microsoft.Office.Interop.Outlook.OlImportance.olImportanceLow:
                im = Properties.Settings.Default.COL_IMPORTANCE_LOW;
                break;
            case Microsoft.Office.Interop.Outlook.OlImportance.olImportanceNormal:
            default:
                im = Properties.Settings.Default.COL_IMPORTANCE_MEDIUM;
                break;
        }
        sb.AppendFormat("<Field Name='{0}'>{1}</Field>",
            Properties.Settings.Default.COL_IMPORTANCE, im);
    }
}

El postre:

Ingredientes :
125 gr. de chocolate
60 gr. de mantequilla
125 gr. de azúcar
3 huevos
60 gr. de maizena

Receta :
Fundir el chocolate con la mantequilla a fuego lento.
Separar las yemas y las claras.
Mezclar el azúcar con las yemas de los huevos.
Añadir la maicena y después el chocolate fundido.
Montar las claras de huevo a punto de nieve y añadirlas lentamente a la mezcla.
Verter la mezcla en un molde donde se ha extendido la mantequilla previamente.
Hornearlo entre 20 ó 25 minutos a 180º.

Para terminar:

En los próximos artículos veremos cómo realizar este proceso de subida de ficheros de forma asíncrona usando callbacks, para poder continuar trabajando con Outlook y mostrar el progreso en una barra. También para cerrar la serie, veremos los pasos que hay que hacer para construir el proyecto de instalación de nuestro Add-In, y así poder distribuirlo.

Nos vemos!

Published 5/3/2009 14:57 por Lluis Franco
Comparte este post:

Comentarios

# re: SharePoint 2007 + Outlook 2007: Guardar correos de forma masiva (VI)

Thursday, March 19, 2009 11:30 PM por espinete

Qué siga la serie por favor !!!