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

SharePoint 2010 Modal Dialog Options: dialogReturnValueCallback

[UPDATE]

Barbara mi ha fatto notare giustamente un paio di imprecisioni nel javascript.

  1. Nel blocco seguente le note non sono commentate correttamente.

if(dialogResult == SP.UI.DialogResult.OK){
\ L’utente ha cliccato sul tasto Salva
} else{
\ L’utente ha cliccato sul tasto Annulla o ha chiuso la finestra modale
}

dovrebbero essere così

if(dialogResult == SP.UI.DialogResult.OK){
// L’utente ha cliccato sul tasto Salva
} else{
// L’utente ha cliccato sul tasto Annulla o ha chiuso la finestra modale
}

  1. Nella riga options.url = formUrl; c’è un errore di sintassi. formUrl era stato infatti definito con l’iniziale maiuscola, quindi: options.url = FormUrl;

Una precisazione infine su dove posizionare lo script. Non è necessario inserirlo nell’head della pagina. Tuttavia personalmente preferisco non avere script isolati nelle pagine, ma concetrare il più possibile  funzioni e stili in file esterni.

[/UPDATE]

In un progetto che ho seguito nelle scorse settimane ho avuto la necessità di personalizzare il comportamento di apertura e chiusura delle finestre modali di visualizzazione/modifica di una lista SharePoint.

Il mio obiettivo era gestire, oltre a dimensioni e titolo della finestra, il comportamento alla sua chiusura, eseguendo azioni differenti a seconda dell’azione che ha portato alla chiusura stessa: submit o cancel.

Prima Barbara (ottimo il suo post) poi Claudio mi hanno guidato sulla strada giusta, come sempre!

Nelle opzioni di apertura della nuova finestra (var options = SP.UI.$create_DialogOptions();) è possibile definire una funzione javascript custom che verrà eseguita alla chiusura (options.dialogReturnValueCallback = nomedellafunzione;).

Le funzioni che vengono utilizzate in questo modo accettano due parametri (dialogResult e returnValue). Sfruttando il primo di questi due parametri possiamo andare a controllare in modo molto semplice (dialogResult == SP.UI.DialogResult.OK) se l’utente ha effettuata il salvataggio dell’item o se ha chiuso la finestra / annullato l’operazione.

Facciamo un esempio “pratico”.

<a href=”javascript:ApriFinestraModaleConOpzioni(‘../editform.aspx’)>Modifica elemento</a>

Questo è un semplice link ad una funzione js, niente di nuovo, vero?

<script type=”text/javascript”>

function ApriFinestraModaleConOpzioni(FormUrl){
var options = SP.UI.$create_DialogOptions();
options.url = formUrl;
options.title = ‘Il titolo della nuova finestra’;
options.dialogReturnValueCallback = FunzioneDiUscita;
SP.UI.ModalDialog.showModalDialog(options);
}

// In questa prima funzione vengono istanziate le opzioni della nostra finestra modale. SP.UI.$create_DialogOption(); è l’oggetto che permette il “miracolo”;

function FunzioneDiUscita(dialogResult, returnValue){
if(dialogResult == SP.UI.DialogResult.OK){
\ L’utente ha cliccato sul tasto Salva
} else{
\ L’utente ha cliccato sul tasto Annulla o ha chiuso la finestra modale
}
}

// La seconda funzione non fa proprio nulla, è solo un esempio :)

</script>

A questo indirizzo trovate un approfondimento su tutte le opzioni disponibili nell’oggetto SP.UI.ModalDialog.showModalDialog,

http://msdn.microsoft.com/en-us/library/ff410058.aspx

mentre in questo trovate i possibili valori del parametro dialogResult

http://msdn.microsoft.com/en-us/library/ff409060.aspx

e ovviamente, ancora una volta, il post di Barbara

http://nonsolosharepoint.wordpress.com/2011/02/09/finestre-di-dialogo-sharepoint-2010/

Semplice e potentissimo, non trovate?

-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

SharePoint Web Services e DataForm

In molte occasioni, in mancanza di altri mezzi, mi è capitato di utilizzare i web services di SharePoint per la visualizzione di dati cross site collection o cross farm. Se a qualcuno dovesse suonare nuovo quello che ho scritto si tratta di utilizzare i vari servizi che si trovano nella virtual directory _vti_bin di ciascun sito SharePoint e sfruttare uno dei metodi messi a disposizione come data source (XML Web Services) di una Data Form Web Part. Uno di questi servizi è lists.asmx, uno dei suoi metodi GetListItems (es. http://www.ilmiosito.it/_vti_bin/lists.asmx?op=GetListItems).

L’utilizzo di questo tipo di origine dati nasconde però molte insidie (che qualcuno chiama “configurazioni” :)). Vediamo alcuni dei parametri che devono essere compilati e come:

  1. listName [Obbligatorio]: il nome è parlante, si tratta del nome (nome visualizzato) della lista da cui si vogliono leggere i dati;
  2. viewName [Opzionale]: in questo caso il nome trae in inganno, non si tratta del nome della vista, ma del suo GUID. Ci interessa? Eccome! Tramite l’impostazione di questo valore possiamo scegliere quali dati saranno visibili nella dataform, con quali filtri e in che ordine. In pratica ci permette di spostare la logica di selezione ed ordinamento dei dati sulla visualizzazione e non sulla query, dall’interfaccia grafica e non da SharePoint Designer. In teoria ci sarebbe un parametro specifico per ciascuna di queste funzionalità, ma in pratica non sono mai riuscito ad utilizzarli (mancanza mia? bug? Più probabile la prima, ma è da molto che regolarmente faccio dei tentativi, tutti vani…). Se non impostato viene considerato come valore di default la visualizzazione predefinita;
  3. rowLimit [Opzionale]: Permette di specificare il limite di elementi da selezionare. Mooolto importante dal mio punto di vista in quanto sovrascrive il limite impostato nella visualizzazione specificata in precedenza. Ad esempio, se la vostra visualizzazione ha un limite di 100 elementi visualizzati potrete impostare quì un valore molto più alto, per essere certi che vengano mostrati tutti i dati. Da quello che ho potuto constatare, per quanto riguarda il limite di elementi nella visualizzazione, non si tratta di un valore assoluto, ma anche del valore di elementi per pagina. Di conseguenza, per evitare di correre il rischio di avere visualizzazioni con un numero eccessivo di elementi trovo importantissimo valorizzare questo parametro.Questa caratteristica non è un bug, ma è dovuto al fatto che il metodo GetListItems supporta la pagina lato server. Oggi ho scoperto (mio malgrado) un’altra caratteristica che deriva da questo parametro, cioè che anche per i campi di lookup pare valga la stessa logica. Mi spiego: tra i dati che visualizzo è presente una colonna di tipo lookup. La lista di riferimento di questo metadata ha più di 100 elementi e nella visualizzazione di default è impostato proprio questo limite di elementi per pagina. Bene (‘nsomma), nella mia dataform venivano visualizzati solo i dati di lookup presenti nella prima paginazione. In questo caso ho dovuto scegliere la strada più veloce, modificare cioè le impostazioni sulla vista, ma sicuramente in un momento di maggiore calma provvederò a correggere i parametri della dataform;
  4. WebId [Opzionale]: Se valorizzato permette di ridefinire lo scope di azione del servizio, agendo cioè su siti differenti da quello utilizzato per la connessione;

Ho omesso da questo elenco quei parametri che mi hanno sempre causato errori (query, viewFields, QueryOptions). Nel caso dovessi riuscire nei prossimi giorni sarò più che lieto di farvelo sapere :)

-Riccardo

Formattazione condizionale nei page layout (o quasi)

L’argomento di questo post è un piccolo trick da cui spero possiate prendere spunto per le più disparate varianti. Partiamo come sempre dal descrivre l’esigenza: formattare diversamente un elemento di un page layout a seconda del valore di un page field. In questo esempio andremo a cambiare lo stile del titolo della nostra pagina a seconda del valore di un page field visibile unicamente in fase di edit della pagina.

Abbiamo quindi (almeno) due page field: Titolo – single line of text – e Categoria – choice -. In base al valore di Categoria il titolo avrà caratteristiche (font, colore, dimensioni, ecc…) differenti.

Per prima cosa inserisco nel page layout un elemento HTML (div) che conterrà il valore del page field di controllo.

<div id=”valoreCategoria” style=”display: none”>
<SharePoint:FieldValue FieldName=”Categoria” runat=”server” />
</div>

Definisco quindi la posizione nel page layout del titolo della pagina.

<div id=”titoloPagina” class=”Default-Style”>
<SharePoint:FieldValue FieldName=”Title” runat=”server” />
</div>

Subito dopo aver definito questo elemento richiamo la funzione JavaScript che si occuperà della modifica dello stile del testo.

<script language=”javascript” type=”text/javascript”>CambiaStile();</script>

Immagino che a questo punto abbiate già capito cosa fa questa funzione, i passaggi sono semplici:

  1. Selezione del contenuto dell’elemento “valoreCategoria”
    1. var myDivContent = document.getElementById(“valoreCategoria”).innerText
  2. Impostare in base al valore della variabile myDivContent la classe CSS dell’elemento “titoloPagina”
    1. switch (myDivContent){
    2. case “Categoria 1”:
    3. document.getElementById(“titoloPagina”).style.className = “Categoria1-Style”;
    4. break;
    5. case “Categoria 2”:
    6. document.getElementById(“titoloPagina”).style.className = “Categoria2-Style”;
    7. break;
    8. default:
    9. document.getElementById(“titoloPagina”).style.className = “Generico-Style”;
    10. }
  3. Voilà! :)

Questo come ho già detto è un piccolo trick, non applicabile a tutte le esigenze di formattazione condizionale, e soprattutto è un esemepio che se utilizzato in ambienti di produzione deve essere ottimizzato, sia dal punto di vista della compatibilità browser (FireFox non mi pare supporti innerText ad esempio), ma soprattutto dal punto di vista delle performance. La mia intenzione in questo caso è solo quella di farvi accendere qualche lampadina :)

– Riccardo (virtualmente in nave verso la Sardegna…)

Ancora su Data Form Web Part & SQL Server

In questo periodo sto lavorando molto sulla visualizzazione di dati provenienti da basi dati esterne (SQL Server). Esclusi BDC e terze parti quello che rimane è la tanto cara Data Form Web Part. In questo “giro” ho scoperto un parametro con il quale non mi era ancora capitato di incontrarmi/scontrarmi.

Ma andiamo con ordine. Lavorando con fonti dati SQL cerco sempre di non utilizzare query “in-line”, del tipo SELECT A,B,C FROM TABLE, ma di utilizzare stored procedure, spostando così la logica di selezione dati su SQL Server. Il mio obbiettivo è quello di semplificare tutte quelle attività legate all’estrazione dei dati e non alla loro presentazione.

Per rendere dinamici i dati estratti la stored procedure di turno richiedeva 4 parametri di cui solo due obbligatori. E qui viene il bello. Come impostazione predefinita i tag <asp:SqlDataSource … /> e <SharePoint:SPSqlDataSource … /> hanno la proprietà “CancelSelectOnNullParameter” impostata a True! Di conseguenza, prima di scoprirlo, non ero in grado di ottenere alcuna informazione dalla mia query.

Altre volte mi era capitato di lavorare con stored procedure che richiedevano più parametri, ma la query era differente da quest’utlima e impostando, lato Data Form Web Part, la proprietà ConvertEmptyStringToNull a False nella definizione dei parametri (nel data source) questo problema non mi si era mai posto. Peccato, mi avrebbe risparmiato un po’ di mal di testa :)

Infine… avete mai provato a concatenare due origini dati di tipo SQL Server che utilizzano entrambe stored procedure? Tutte le volte che ho provato io SharePoint Designer non si è dimostrato molto collaborativo… ho dovuto impostare data source e stylesheet a mano… comodo…

– Riccardo