SharePoint Designer, BCS e Proxy

Qualche giorno fa mi sono imbattuto in un “errore” che mi ha fatto perdere un po’ di tempo. Ero, come mi capita spesso, presso un cliente e avevo la necessità di creare un nuovo External Content Type. Non era certo la prima volta che utilizzavo SharePoint Designer da quel cliente, anzi, e non ho mai avuto alcun problema, così quando ho ricevuto il messaggio di errore “The Business Data Connectivity Metadata Store is currently unavailable” ho pensato ad un errore del servizio BCS.

The Business Connectivity Service Metadata Store is currently unavailable

The Business Connectivity Service Metadata Store is currently unavailable

Tra ULS e Event Log non c’era però traccia di alcun messaggio di errore riconducibile al servizio BCS. Inoltre cliccando sull’icona “External Content Type” nel ribbon ricevo un ulteriore avviso, “401 Unauthorized”.

401 Unauthorized

401 Unauthorized

Dopo un po’ di ricerche ho trovato un post che, pur non presentando le stesse condizioni, mi ha messo sulla strada giusta (vedi link sotto). Il problema in realtà non è un problema, bello vero? :) No, scherzi a parte, l’errore che ho riscontrato non è da ricondurre ad un malfunzionamento del servizio BCS, ma semplicemente ad una difficoltà di comunicazione tra il client (SharePoint Designer) e il server.

Correggendo le impostazioni di connessione per escludere dal proxy gli url relativi alle macchine SharePoint, SharePoint Designer ha ripreso a funzionare come da attese. Curioso che solo i BCS abbiano questo comportamento, buona a sapersi per i prossimi clienti :)

-Riccardo

Trasformazione di stringhe in XSLT

In generale gestire la trasformazione di una stringa, sia in linguaggi client side che server side, è una cosa semplice. Non è però così  immediato farlo all’interno di una data view via XSLT.

Gogolando ho trovato un template XSLT (qui) che permette in modo molto semplice di trasformare “mele” in “pere” o, citando casi più probabili “&” in “%26”. Eccolo qui:

<xsl:template name="replaceCharsInString">
<xsl:param name="stringIn"/>
<xsl:param name="charsIn"/>
<xsl:param name="charsOut"/>
<xsl:choose>
<xsl:when test="contains($stringIn,$charsIn)">
<xsl:value-of select="concat(substring-before($stringIn,$charsIn),$charsOut)" disable-output-escaping="yes" />
<xsl:call-template name="replaceCharsInString">
<xsl:with-param name="stringIn" select="substring-after($stringIn,$charsIn)"/>
<xsl:with-param name="charsIn" select="$charsIn"/>
<xsl:with-param name="charsOut" select="$charsOut"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$stringIn"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

Il template si richiama, dove serve, passando tre parametri: la stringa originale, la stringa da sostituire, la stringa corretta.

<xsl:call-template name="replaceCharsInString">
<xsl:with-param name="stringIn" select="string(@iltuoTitolo)"/>
<xsl:with-param name="charsIn" select="'&amp;'"/><!-- ATTENZIONE, la stringa &amp; è inclusa tra apici singoli '&amp;' -->
<xsl:with-param name="charsOut" select="'%26'"/><!-- ATTENZIONE, anche questa stringa è contenuta tra apici singoli '%26' -->
</xsl:call-template>

Un aspetto interessante di questo template è che può essere utilizzato anche per gestire la presentazione dei campi di lookup a selezione multipla, trasofrmando un elenco di stringhe separate da punto e virgola in quello che volete (es. un elenco puntato/numerato). Per fare questo è necessaria una piccola modifica al template, giusto per inserire nell’output il codice HTML corrispondente alla nuova formattazione.

Un’altra trasformazione che spesso viene richiesta (per scelta o per forza vista l’estrema fantasia di editors e contributors… :)) è quella da maiuscolo a minuscolo o viceversa. In questo caso non è necessario utilizzare un template custom, è sufficiente creare due variabili e utilizzare la funzione OOTB translate. Ecco come fare (lowercase to uppercase, per gestire il processo inverso invertire la posizione delle due variabili nella funzione translate).

<xsl:variable name="vLowercaseChars_CONST" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="vUppercaseChars_CONST" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="normalizedChars" select="translate(@ilvostrocampoqui, $vLowercaseChars_CONST , $vUppercaseChars_CONST)" />

Una pecca di queste tre righe è quella di non prevedere la possibilità di inserire caratteri intermedi con uno stile diverso, come ad esempio una maiucola dopo un punto. Se qualcuno ha scritto un template per gestire questo aspetto è il benvenuto su queste pagine ;)

– 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 :)

Controlli di validazione in custom list form

Cari site builder in ascolto, vi è mai capitato di ricevere richieste di poter inserire controlli per la validazione dei dati inseriti in una lista SharePoint? Forse la domanda giusta sarebbe stata “quante volte”…

Inserire controlli di validazione è molto più semplice di quanto possiate immaginare, anche se bisogna tenere in considerazione un paio di aspetti. Ma andiamo per ordine, nelle prossime righe andremo a creare una custom list form nella quale verificheremo la correttezza (formale) di un indirizzo e-mail inserito dall’utente. Tralascio le azioni preliminari di creazione e configurazione della lista, che chiamerò ValidationControls, sapete come si fa vero? :)

Da SharePoint Designer apro il file NewItem.aspx relativo alla mia lista (in un progetto reale difficilmente aprirei e apporterei modifiche direttamente a questo file, chiudete un occhio se potete). Una volta nascosta la form originale (selezionando “Hidden” dalle proprietà della web part), inserisco una nuova custom list form selezionando la lista, il content type e il tipo di azione da supportare (in questo esempio non mi preoccuperò della magagna Attachment, chiudete l’altro occhio, ok?). Dal menù “Task Panes” di SPD seleziono “Toolbox” che mi farà apparire la lista di tutti i controlli disponibili per il mio sito, tra cui anche i controlli di validazione. Tra quelli disponibili aggiungerò alla mia custom list form un controllo di tipo “RegularExpressionValidator” che mi permetterà di sfruttare la grande flessibilità delle regular expressions.

 

A questo punto possiamo fare una prima prova (che fallirà tanto quanto la traversata del Titanic), inserire un controllo di validazione e legarlo manualmente ad un form field esistente. Nel tag <asp:regularexpressionvalidator … /> aggiungo manualmente la proprietà ControlToValidate ed inserisco come valore l’id del form field relativo alla colonna CustomerEmail. Riusltato: un errore che a seconda della configurazione del vostro web.config potrebbe apparire circa così:

Per fare in modo che il controllo di validazione non causi danni dovrò modificare il tipo di campo di input a cui lo voglio legare, trasformando il form field in Text Field. Per fare questo espando il menù “Common FormField Actions” e seleziono da “Format as” > “Text Box”.

A questo punto riconfiguro il controllo di validazione in modo che sia legato al nuovo campo di testo. Alcuni dei parametri più importanti sono ControlToValidate (permette di specificare quale campo di input debba essere validato), ErrorMessage (permette di specificare il messaggio di errore) e a mio parere anche Display (permette di specificare l’ingombro del controllo quando non visualizzato). Abbiamo inoltre la possibilità di impostare svariati parametri legati all’aspetto del messaggio che al momento tralascio.

Faccio un discorso a parte per il parametro “ValidationExpression”. Questa impostazione servirà per specificare il tipo di controllo da effettuare e nello specifico la regual expression che deve essere verificata. SPD ci viene incontro fornendoci un limitato numero di controlli preconfigurati, tra i quali (ma guarda un po’) anche il controllo sul formato dell’indirizzo e-mail.

Una volta salvata la pagina, provando ad inserire un dato diverso da un indirizzo e-mail otterremo il messaggio di errore configurato.

In questo esempio ho utilizzato un controllo di tipo RegularExpressionValidator in quanto abbiamo già a disposizione un pattern di verifica accettabile. Nel caso di controlli personalizzati (ad esempio lunghezza minima e massima di una stringa) all’interno di una custom list form non potremo usare una RegEx, a causa di caratteri “speciali” riservati ad XSL come ad esempio le parentesi graffe. Un modo per aggirare l’ostacolo può essere quello di utilizzare il controllo CustomValidator che ci permette di specificare una funzione JS che si occupi per noi di tutte le verifiche necessarie e, in questo caso, il solo limite è la fantasia di chi ci commissiona i progetti :D

– Riccardo