Skip to main content

Blog de Vicente Pazo

Go Search
Home
  

Blog de Vicente Pazo > Categories
Comparando fechas en CAML desde una aplicación .Net

Cuando queremos construir una consulta CAML desde .NET puede ocurrir que necesitemos incluir una condición para comparar fechas.

Si insertamos la fecha tal cual no nos ejecutará bien la consulta:

<Gt><FieldRef Name=""Created"" /><Value Type=""DateTime"">" + dtFrom.ToShortDateString() + @"</Value></Gt>

 

Esto sería lo que usaríamos desde un principio, a no ser que sepamos que éste es el método que realmente funciona :P

 

<Gt><FieldRef Name=""Created"" /><Value Type=""DateTime"">" + dtFrom.ToString("s") + @"</Value></Gt>

 

Este formato transforma la fecha así:

17/02/2010 -> 2010-02-17T12:00:00Z

Y con este formato SÍ se pueden comparar fechas.

Cuando necesitéis comprobar la sintaxis de vuestra query generada es buena idea usar alguna aplicación externa para ejecutarla y corregirla. Yo suelo usar U2U CAML, que podéis bajar gratuitamente desde aquí.

Leer los Attachments de un Item en SharePoint y .NET

Hoy estaba intentando leer los attachments (adjuntos) de un elemento de una lista de SharePoint.

Cada SPLitsItem tiene una colección llamada SPAttachmentCollection, donde puedes saber si el ítem tiene attachments y obtener campos sobre esa colección. En un principio parecería lógico que un SPAttachmentCollection es una colección de algún objeto como SPAttachment… pero no! Simplemente es un array de strings!!

De hecho, no existe un objeto SPAttachment. Para acceder al contenido de esa colección deberíais hacer algo así:

 

foreach (string fileName in item.Attachments)
{
    SPFile file = item.ParentList.ParentWeb.GetFile(item.Attachments.UrlPrefix + fileName);
}

 

Otra opción sería acceder al folder donde se encuentran esos files y recorrerlos, pero la versión de arriba parece más fácil:

 

SPFolder folder = web.Folders["Lists"].SubFolders[list.Title].SubFolders["Attachments"].SubFolders[listitem.ID.ToString()];

foreach (SPFile file in folder.Files)

{

  // Something useful here

}

 

Usando Web Services de SharePoint 2007 en .NET

SharePoint proporciona una serie de web services para facilitarnos un poco la vida.

Podéis ver el listado en esta página del MSDN.

En este ejemplo vamos a centrarnos en uno de los más útiles, el que nos permite hacer consultas a listas.

Evidentemente, podríamos crear nuestro objeto SPQuery y usarlo para obtener los resultados con el método SPList.GetItems, pero con el web service tendremos el resultado con los nombres internos originales de las columnas de la lista.

Primero añadimos el web service, desde el Solution Explorer, en la carpeta References, añadimos una Web Reference:

Y en el popup introducimos la url del top level site del site collection y "/_vti_bin/lists.asmx".

Recordad que los web services que proporciona Sharepoint los encontramos en la 12 hive (C:\Program Files\Common Files\Microsoft Shared\web server extensions\12), en la carpeta "ISAPI".

Vti_bin es virtual folder que apunta a esa carpeta física.

Una vez insertada esa URL obtendréis un listado con los métodos que publica el web service de lists:

 

 

Como veis tenemos muchas funcionalidades, así que antes de programar cualquier cosa, os recomiendo que comprobéis que esa funcionalidad no está implementada en algún web service.

Dentro del listado tenemos el método GetListItems.

Podéis cambiar el nombre del a Web reference, que por defecto es el nombre de la máquina, y entonces seleccionáis "Add Reference".

En este ejemplo la referencia la llame "WSLists"

Dentro de vuestro código podéis instanciar el web service de este modo:

 

WSLists.Lists webServiceLists = new getEventsRSS.WSLists.Lists();

 

Con ese objeto ya podéis accede a sus propiedades y métodos, como por ejemplo, cambiar en tiempo de ejecución la URL a la que hace referencia:

 

webServiceLists.Url = sURL;

 

Para hacer la consulta necesitamos:

  • El nombre de la lista.
  • El GUID de la view, para que las propiedades (pej query, fields, rowlimit…) se cojan directamente de la view. Estos parámetros se pueden sobreescribir poniéndolos explícitamente en la llamada al método (si la view tiene un rowlimit de 100 pero le pasamos el parámetro rowlimit de 200, se usará 200).
  • La quey en formato CAML.
  • Los fields que queremos visualizar. Tiene que ser de tipo System.Xml.XmlNode.
  • Rowlimit para especificar el número de elementos devueltos por la consulta. Si es null se devuelven todos.
  • queryOptions. También en Xml para indicar opciones como la inclusión de las columnas obligatorias y otras opciones del objeto SPQuery.

Este sería un ejemplo de uso:

private static void CallWebService(string sEventsList, SPQuery query, string sURL)

{

XmlDocument xmlDoc = new System.Xml.XmlDocument();

 

XmlNode ndQuery = xmlDoc.CreateNode(XmlNodeType.Element, "Query", "");

XmlNode ndViewFields = xmlDoc.CreateNode(XmlNodeType.Element, "ViewFields", "");

XmlNode ndOptions = xmlDoc.CreateNode(XmlNodeType.Element, "QueryOptions", "");

 

ndOptions.InnerXml = "<ExpandRecurrences>TRUE</ExpandRecurrences>";

 

ndQuery.InnerXml = query.Query;

ndViewFields.InnerXml = "<FieldRef Name=\"RecurrenceData\" />";

 

WSLists.Lists webServiceLists = new getEventsRSS.WSLists.Lists();

 

webServiceLists.Url = sURL;

 

webServiceLists.Credentials = System.Net.CredentialCache.DefaultCredentials;

 

XmlNode ndListItems = webServiceLists.GetListItems(sEventsList, null, ndQuery, ndViewFields, null, ndOptions, null);

}

 

De esta forma obtendríamos un resultado así:

 

 

Como veis tenemos los campos originales, y la consulta nos ha costado muy poco de hacer.

 

 

 

Modificar el formato de fecha en el Enhanced Content Query Webpart

En la aplicación que estamos desarrollando vamos a hacer uso del Enhanced Content Query Webpart (ECQWP). Este webpart permite hacer consultas a listas (y a sites enteros) y mostrar los resultados según un XSLT.

Este XSLT lo podeis modificar para mostrar los resultados con el layout que queráis. Podeis consultar la excelente página de Heather Solomon (especialista en layout de Sharepoint) y , en especial, su sección para "customizar" el layout del ECWP (aquí teneis el del CQWP, que es el que viene por defecto en MOSS, pero el desarrollo del xslt es muy parecido en el ECQWP).

Si añadís un tipo fecha al XSL, es posible que no aparezca en el formato que deseáis. Por ejemplo, tenemos este ECWP:

 

 

Nosotros queríamos aplicarle un formato de fecha dd.mm.yyyy (propio del alemán). Para ello tuvimos que usar la función de XSLT ddwrt:FormatDate. Esta función pide tres parámetros: la fecha a formatear, el LCID (identificador de lenguaje) y un código para indicar qué tipo de formato queremos, según esta tabla:

 

 

La tabla está con el LCID 1033, del inglés, pero se puede aplicar con cualquier otro LCID. En nuestro caso era el 1031. Tenéis una lista con los LCIDs aquí.

Por lo tanto, aplicamos esta función:

<xsl:value-of select="ddwrt:FormatDate(string(@Modified),1031, 1)"/>

 

Y obtuvimos este resultado:

 

 

Styling en Content Query Webpart: Parámetros CursPos y Last

Si creamos un template propio en el ItemStyle.xml, es posible que queramos saber en qué posición está el cursor que lee los ítems de la consulta, para, por ejemplo, crear una tabla en el primer elemento y cerrarla en el último. Estos parámetros son "CurPos" y "LastPos".

Existe un archivo "ContentQueryMain.xsl" que define los templates que se usarán en el ItemStyle.xsl. En uno de esos templates (que es un conjunto de estilos o acciones que se aplican a los elementos, algo así como una función en xsl), exactamente OuterTemplate.CallItemTemplate, se pasan los parámetros a nuestro template de ItemStyle.xml. Éste es el OuterTemplate.CallItemTemplate original:

 

<xsl:template name="OuterTemplate.CallItemTemplate">

<xsl:param name="CurPosition" />

<xsl:choose>

<xsl:when test="@Style='NewsRollUpItem'">

<xsl:apply-templates select="." mode="itemstyle">

<xsl:with-param name="EditMode" select="$cbq_iseditmode" />

</xsl:apply-templates>

</xsl:when>

<xsl:when test="@Style='NewsBigItem'">

<xsl:apply-templates select="." mode="itemstyle">

<xsl:with-param name="CurPos" select="$CurPosition" />

</xsl:apply-templates>

</xsl:when>

<xsl:when test="@Style='NewsCategoryItem'">

<xsl:apply-templates select="." mode="itemstyle">

<xsl:with-param name="CurPos" select="$CurPosition" />

</xsl:apply-templates>

</xsl:when>

<xsl:otherwise>

<xsl:apply-templates select="." mode="itemstyle">

</xsl:apply-templates>

</xsl:otherwise>

</xsl:choose>

</xsl:template>

 

Hay un choose (como un switch en c#) y para cada uno de los templates (que estarán definidos en el ItemStyle.xml) hay un when (como un case en c#). Si añadimos un template nuestro en ItemStyle.xsl, deberemos añadir un nuevo when adjuntarle los parámetros que queríamos:

 

<xsl:when test="@Style='TreenovumTemplate'">

<xsl:apply-templates select="." mode="itemstyle">

<xsl:with-param name="CurPos" select="$CurPosition" />

<xsl:with-param name="Last" select="$LastRow" />

</xsl:apply-templates>

</xsl:when>

 

De esta forma ya podremos usar esos parámetros en nuestro template de ItemStyle.xsl:

<xsl:template name="TreenovumTemplate" match="Row[@Style='TreenovumTemplate']" mode="itemstyle">

 

<xsl:param name="CurPos" />

<xsl:param name="Last" />

 

.

.

.

.

 

<a href="{$SafeLinkUrl}" target="{$LinkTarget}" title="{@LinkToolTip}">

<xsl:value-of select="$DisplayTitle"/>

c: <xsl:value-of select="$CurPos"/>

</a>

 

.

.

.

.

 

</xsl:template>

 

Y el resultado sería este:

 

Quitar icono de “!New” en las listas de SharePoint 2007

En las listas de Sharepoint, por defecto, al insertar un Nuevo elemento, nos aparece el icono de "!New" al lado del ítem:

 

 

En algunas ocasiones querremos cambiar el comportamiento de este icono, de forma que, o bien no aparezca, o bien esté una cantidad de días distinta a la puesta por defecto.

Para cambiar este comportamiento usaremos el comando stsadm (alias la navaja suiza de Sharepoint) para, con un simple comando, adaptar el icono a nuestras necesidades.

Lo primero es abrir la ventana de comandos de nuestro servidor: Inicio > Ejecutar > cmd

Desde allí, y mediante el comando cd, navegar hasta el "12 hive": C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN

Ahora ejecutaremos el comando stsadm con estos parámetros:

stsadm.exe –o setproperty –pn days-to-show-new-icon –pv (número de días) –url (url de vuestro servidor)

Si en número de días ponemos un cero, el icono no aparecerá al insertar un elemento en cualquier lista de Sharepoint

Date format when querying the search service

When you query the search service, be careful with format dates. You have to use this format:

YYYY-MM-DD

if you use another format, you'll get a HRESULT:0x80040E07 error message

Beware when sending emails with SPUtility.SendEmail

When you use SPUtility.SendEmail() method to send mails in Sharepoint, be careful with body size.

It is truncated if size is more than 2048 characters!

To avoid that you have to use the .net classes to send mail, but you need to know the smtp server that will send the message.

Use this:

YourSite.WebApplication.OutboundMailServiceInstance.Server.Address

"An error has occurred while accessing the SQL Server database or the Office SharePoint Server Search service" error

I received this error when accessing the "user profiles and properties" section (Central Admin > Shared Services Administration > KSA_SSPAdmin).

If I accessed "View import log" I received this one:

The search service is currently offline. Visit the Services on Server page in SharePoint Central Administration to verify whether the service is enabled. This might also be because an indexer move is in progress.



Obviously, the indexer was not in progress and the service was enabled.

Googling, I found this page:

http://ablog.apress.com/?p=1406

And after assigning the indexer to the service, I get no more errors.

Customize events order with nested user controls

We have a user control inside another one. The insider user control has a button that set a public property of the insider user control:




Imagine that you want to get the public property after clicking the button in UserControl2.

This will be the default order:




We (obviously) can't change this order, but we can include some new events to run code after the click of usercontrol2:

- Create a public event property in Usercontrol2:

public event CommandEventHandler SiteClicked;

- This function will be called in the click event of Usercontrol2:

 
protected void LinkButtonClick_site(Object sender, CommandEventArgs e)
    {
        LinkButton lnk = (LinkButton)sender;
        lnk.CssClass = "selectedSite";
        SiteClicked(this, e);     
    }


- Add and define the event handler in UserControl1:

usercontrol2.SiteClicked += new CommandEventHandler(SetSelectedWeb);

//...

protected void SetSelectedWeb(object sender, CommandEventArgs e)
    {
        usercontrol2.SetSelectedWeb(e.CommandArgument.ToString());

        if (usercontrol2.selectedWeb != null)
        {
            lblSourceWeb.Text = usercontrol2.selectedWeb.Title;
        }
    }




And that is!!

1 - 10 Next