Operation caused a stack overflow

Qualche settimana fa, preparando una demo su FAST Search for SharePoint 2010, ad un certo punto alcune delle web part della pagina dei risultati della ricerca (search refiner e core result web part) hanno cominciato ad andare saltuariamente in errore. L’errore era il “classico” Unable to display this Web Part. To troubleshoot the problem, open this Web page in a Microsoft SharePoint Foundation-compatible HTML editor such as Microsoft SharePoint Designer. If the problem persists, contact your Web server administrator.

L’errore, comune quando si commette un errore nella personalizzazione dell’XSLT di una Data Form Web Part, mi è parso molto strano a causa del fatto che non avevo affatto modificato i template XSLT di quelle Web Part.

Verificato il Correlation ID con le informazioni presenti nel ULS log ho trovato questo messaggio:

Error while executing web part: System.StackOverflowException: Operation caused a stack overflow.
at Microsoft.Xslt.NativeMethod.CheckForSufficientStack()
at <xsl:template match=”Result”>(XmlQueryRuntime , XPathNavigator )
at <xsl:apply-templates>(XmlQueryRuntime , XPathNavigator )
at <xsl:template name=”dvt_1.body”>(XmlQueryRuntime , XPathNavigator )
at Root(XmlQueryRuntime )  … (continua)

La causa di questo errore era nota da qualche tempo a Microsoft, di fatto se la trasformazione dei template XSLT impiega più di 1 secondo viene sollevata questa eccezione. Nel mio caso, date le risorse della VM, questo era assolutamente possibile, ma in vista della demo con il cliente dovevo assolutamente risolvere l’errore.

A partire dalla Cumulative Update di Febbraio 2012 è stato aggiunto un nuovo metodo grazie al quale, tramite Powershell, è possibile aumentare il timeout. E’ sufficiente uno script di tre righe, eccolo:

$farm = Get-SPFarm
$farm.XsltTransformTimeOut = 5
$farm.Update()

Data la soluzione, vale la pena comunque soffermarsi sulle motivazioni che portano a questo errore, cioè verificare attentamente le performance della farm e  revisionare i propri template XSLT.

– Riccardo

Campi Person or Group in SharePoint Designer 2010

Con questo post vorrei inaugurare una serie di post dove condividere brevi pillole sugli argomenti più vari, ovviamente sempre nell’orbita del mondo SharePoint.

Se ho capito bene come fare dovreste trovare anche un link dove verranno raggruppati tutti i post di questa categoria.

Lavorando con SharePoint Designer alla creazione di Data Form Web Part o alla personalizzazione di stili per la Content Query Web Part, capita spesso di imbattersi in campi di tipo Person or Group, pensate anche semplicemente ai campi Autore o Autore Ultima Modifica.

SharePoint Designer 2010 ci offre la possibilità di “consumare” il nostro dato in forma diversa. L’immagine seguente è un dettaglio dell’origine dati SharePoint, dove Nome è un campo di tipo Person or Group.

image

Se ne deduce facilmente che nei nostri template xsl potremo utilizzare tutti e quattro i formati:

A mio avviso l’aspetto più pratico è quello di poter utilizzare il nome nell’utente senza dover per forza di cose o applicare una normalizzazione del dato o “ereditare” tutta la struttura HTML a supporto di questo tipo di campo.

Happy SPD
– Riccardo

XSLT e comparazione tra date

Di recente un’amica mi ha chiesto se, e come, fosse possibile gestire una sorta di scadenza degli elementi visualizzati in un DataForm utilizzando XSLT. La necessità è quindi quella di verificare che la data impostata come scadenza in un custom metadata sia maggiore dell’ora attuale. Mi aspettavo che una banale comparazione tra date funzionasse correttamente, si tratta in fondo di data type uguali (in teoria…), ma anche no.

E’ necessario normalizzare le date nel formato che più ci conviene. Prima cosa ottengo la data (ed ora) corrente utilizzando l’estensione ddwrt:TodayIso() e memorizzo il valore in una variabile.

<xsl:variable name="cheoresono" select="ddwrt:TodayIso()"></xsl:variable>

A questo punto creo altre due variabili nelle quali memorizzo le due date da paragonare “trasformate” in un valore numerico. L’obbiettivo è quello di ottenere una stringa del tipo 201007261734 (YYYYMMAAHHmm) – @TEST è la mia colonna di tipo Data e ora:

<xsl:variable name="testconverted" select="number(concat(substring(string(@TEST),1,4), substring(string(@TEST),6,2), substring(string(@TEST),9,2), substring(string(@TEST),12,2), substring(string(@TEST),15,2)))" />
<xsl:variable name="cheoresonoconverted" select="number(concat(substring($cheoresono,1,4), substring($cheoresono,6,2), substring($cheoresono,9,2), substring($cheoresono,12,2), substring($cheoresono,15,2)))" />

A questo punto posso effettuare il confronto delle due date con il semplice xsl:if

<xsl:if test="$testconverted &gt; $cheoresonoconverted">@TEST è maggiore di questo momento (secondi esclusi)</xsl:if>

Non volendo inserire all’interno del template XSL questo tipo di filtro (forse più utile per formattazione condizionale che per filtro) potrei applicarlo allo stesso modo nella query XPath presente nella dataview all’interno del template dvt_1: 

<xsl:variable name="cheoresono" select="ddwrt:TodayIso()"></xsl:variable>
<xsl:variable name="cheoresonoconverted" select="number(concat(substring($cheoresono,1,4), substring($cheoresono,6,2), substring($cheoresono,9,2), substring($cheoresono,12,2), substring($cheoresono,15,2)))" />
<xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row[number(concat(substring(string(@TEST),1,4), substring(string(@TEST),6,2), substring(string(@TEST),9,2), substring(string(@TEST),12,2), substring(string(@TEST),15,2))) &lt; $cheoresonoconverted]"/>

Per quanto riguarda XSLT è tutto. E per quanto riguarda CAML… (traggo spunto da una maglietta di mio figlio che raffigura la Banda Bassotti) Maybe Next Time :)

-Riccardo

– UPDATE –

Con Barbara abbiamo scoperto un comportamento “strano” di ddwrt:TodayIso(). In alcuni ambienti questa funzione mostra data e ora della time zone GMT, nel nostro caso quindi risulta indietro di due ore. Per risolvere questo “problema” basta memorizzare la variabile cheoresono già formattata utilizzando l’estensione ddwrt:FormatDate(ddwrt:TodayIso(),1040,5). Una volta fatto questo bisogna solo prestare attenzione a modificare i parametri della funzione substring utilizzata nella conversione della data in formato AAAAMMGGHHmm.

Ringrazio ovviamente Barbara per avermi fatto notare questo comportamento :)