Come ci può suggerire il titolo con questo articolo vedremo come sia possibile modificare o estendere l’interfaccia utente (UI) di SharePoint. Diciamo subito che la personalizzazione dei menu, del ribbons, delle pagine e dei controlli in generale è un passaggio quasi obbligatorio per chi lavora con la piattaforma SharePoint e gli scenari possibili sono tanti, per cui caliamoci in un esempio concreto dandoci queste ‘specifiche’ :
Supponiamo di avere due liste personalizzate con le quali si vuole gestire l’anagrafica degli ordini con il classico modello ‘master-details’. Per prima cosa aggiungiamo due liste ‘custom’ : ‘Ordini testata’ e ‘Ordini dettaglio’ rispettivamente così composte:
Non soffermiamoci sulla struttura e sottoliniamo solo la presenza del campo di LookUp nella lista di dettaglio ‘Ordine numero’.
La personalizzazione ci deve permettere di ottenere questa funzionalità:
Selezionando un ordine nella lista ‘Testata’ si deve visualizzare il ‘Dettaglio’ corrispondente della lista ‘Dettaglio’.
Questi i macro-step da realizzare:
- Sviluppare con Visual Studio una Feature che modifichi l’ ‘Edit Control Block’ (ECB) della lista ‘Testata’ per indirizzare l’utente in una pagina intermedia.
- Aggiungere una vista sull’elenco ‘Dettaglio’ con un filtro sul campo ‘Ordine numero’, il cui valore è passato tramite query string.
- Aggiungere con SharePoint Designer una pagina “aspx” e un file ‘.jasvascript’ che conterrà del codice che fa uso del ‘Client-Object Modell’.
Iniziamo aggiungendo delle risorse che ci serviranno più avanti.
- Aggiungiamo la pagina ‘RecuperaNumeroOrdine.aspx’ in ‘SitePages’.
- Aggiungiamo una ‘Style Library’ e aggiungiamo un file di tipo ‘Javascript’ che chiameremo ad esempio :MyFunction.js, inizialmente vuoto.
- Procuriamoci il file : jquery-1.9.1.min.js da qui Jquery e aggiungiamo anche questo alla libreria.
Step 1: Custom Action sulla lista ‘Ordini Testata’.
Premessa : Esistono diversi modi per modificare l’ECB della lista, in questo esercizio svilupperemo una soluzione ‘Sandbox’ che conterrà una Feature per installare la personalizzazione. Nello specifico la personalizzazione non è altro che un file ‘XML’ contenente appunto l’elemento ‘Custom Action’. Questo elemento con tutti gli attributi è descritto bene qui : Custom Action – MSDN.
Nel caso in esame non utilizzeremo tutti gli attributi, ma è sempre bene conoscere l’elemento che si utilizza nella sua completezza e in questo la documentazione in linea è molto precisa. Fine Premessa.
Per configurare la “custom action” partiamo con il crere un nuovo progetto in Visual Studio, selezionando : SharePoint 2013 -> Progetto vuoto.
Diamogli un nome significativo, come ad esempio : CustomActionsOrdini e selezioniamo come tipo di distribuzione ‘Sandbox Solution’.
Fatto questo, aggiungiamo alla soluzione un elemento seguendo queste indicazioni : tasto destro sul nome del progetto -> Aggiungi -> Nuovo Elemento.
Selezioniamo un elemento di tipo : Modulo e diamogli un nome del tipo :TestataCustomActions.
Visual Studio aggiungerà in automatico due file:
-Elements.xml
-Sample.txt
A questo punto la “solution” sarà questa:
Eliminiamo il secondo file e apriamo il primo, che conterrà la nostra Custom Action.
All’interno di ‘Elements.xml’ copiamo il codice seguente:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<CustomAction
Location="EditControlBlock"
RegistrationType="List"
RegistrationId="{499BA418-228E-499C-A322-943BF6B6BC86}"
GroupId="GestioneOrdini"
Id="MS.CustomOrdini"
Title="Dettaglio dell' ordine"
Rights="ViewListItems,EditListItems"
Description="Visualizza il dettaglio dell'ordine."
>
<UrlAction Url="/SitePages/RecuperaNumeroOrdine.aspx?ItemId={ItemId}&ListId={ListId}" />
</CustomAction>
</Elements>
Vediamo nel dettaglio le proprietà che abbiamo utilizzato
- Location-> indica l’oggetto da modificare, in questo caso appunto l’ECB
- RegistrationType-> la custom action può essere attivata su un oggetto specifico, una lista specifica o un content type specifico. Nel caso in esame voglio modificare l’ECB solo di una specifica lista, quindi questa proprietà combinata alla successiva, fa in modo che la modifica si attivi solo per la lista ‘Ordini Testata’.
- RegistrationId-> indico la lista specificando il GUID. Per ricavare il valore si può, ad esempio, aprire la lista con SharePoint Designer e tra le proprietà visibili c’è anche l’ID.
- Id->identificativo univico.
- Title->Testo da visualizzare nel menu
- Description->Descrizione visibile passando il mouse sull’opzione del menu.
- Rights->Indica quali permessi siano necessari per visulizzare l’opzione. E’ una proprietà molto importante perché ci permette di condizionare la personalizzazione in maniera coerente rispetto i permessi che ha l’utente.
- UrlAction-> Questo elemento è un ‘child’, può essere presente solo uno di questo elemento nella custom action. Nell’esempio in esame ho indicato la pagina : RecuperaNumeroOrdine.aspx che sarà spiegata nel dettaglio più avanti. L’aspetto da sottolineare è il seguente : la query string contiene queste informazioni:
- ItemId={ItemId}
- ListId={ListId}
La prima variabile conterrà l’ID delle’elemento nella lista ed il secondo conterrà l’ID della lista. Con queste due informazioni posso recuperare l’elemento dalla lista e accedere a tutti i valori; anticipo che è quello che faremo nella pagina :RecuperaNumeroOrdine.aspx
Installiamo e attiviamo la feature, facendo il ‘Deploy’ da Visual Studio e verifichiamo la personalizzazione! Se ha funzionato vedremo questo:
Completiamo la prova cliccando sul link, in questo momento dovremo solo visualizzare la pagina che abbiamo aggiunto all’inizio.
Step 2: Vista sull’elenco di dettaglio.
Aggiungiamo una vista sulla lista di dettaglio, includendo tutti i campi personali e con queste caratteristiche:
Filtro
Come filtro abbiamo aggiunto la variabile : {Param1} che vedremo dopo a cosa serve.
Group by
Totali
Salviamo la vista e proviamola inserendo nella URL alla fine dell’indirizzo un espressione per il filtro valido cioè : Param1=AB100 (avendo inserito un dettaglio su questo ordine):
Riepiloghiamo.
Abbiamo le due liste collegate tra loro tramite il campo di ‘LookUp’ ‘Ordine numero’.
Abbiamo modificato l’ECB della lista ‘Ordine Testata’ per andare in una pagina intermedia.
Abbiamo aggiunto una Vista sulla liste ‘Ordine Dettaglio’ che mi consente di filtrare sul campo ‘Ordine numero’, passando un valore in query string.
A questo punto potrebbe sorgere un dubbio : perché non uso l’ActionUrl per andare direttamente nella vista passando il valore del numero di ordine? Rivediamo l’ActionUrl:
/SitePages/RecuperaNumeroOrdine.aspx?ItemId={ItemId}&ListId={ListId}
Il motivo è il seguente : nella query string non posso accedere a qualunque campo della lista sono ‘limitato’ ad alcuni attributi. Il compito della pagina intermedia è quindi quella di accedere alla lista ‘Ordini Testata’ recuperando la riga dell’ordine dalla lista ‘Testata’ (in base al valore di ‘ItemId’) e con un redirect ‘client-side’ passare alla nostra vista aggiungendo nella query string il valore del campo ‘Ordine numero’.
Ci manca questo ultimo pezzo…procediamo!
Step 3: Funzioni client-side e pagina intermedia.
Apriamo il sito con “SharePoint Designer” e iniziamo con modificare la pagina ‘aspx’ :RecuperaNumeroOrdine.aspx.
In corrispondenza del TAG : PlaceHolderAdditionalPageHead; aggiungiamo i riferimenti alle risorse che dovremmo già aver aggiunto in precedenza e inseriamo la chiamata alla funzione client-side ‘ RecuperaNumOrdine()’ che vedremo più avanti:
<asp:Content ContentPlaceHolderId="PlaceHolderAdditionalPageHead" runat="server">
<meta name="CollaborationServer" content="SharePoint Team Web Site" />
<SharePoint:ScriptBlock runat="server">
var navBarHelpOverrideKey = "WSSEndUser"; </SharePoint:ScriptBlock>
<SharePoint:RssLink runat="server"/>
<script type="text/javascript" src="../Style Library/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="/_layouts/15/sp.runtime.js"></script>
<script type="text/javascript" src="/_layouts/15/sp.js"></script>
<script type="text/javascript" src="../Style Library/MyFunction.js"></script>
<!--Chiamo la funzione-->
<script type="text/javascript">
$(document).ready(function () {
RecuperaNumOrdine();
});
</script>
</asp:Content>
Passiamo quindi al contenuto del file Javascript e apriamo il file ‘MyFunction.js’. Quello che ci serve è accedere tramite il ‘client-object modell’ alla lista e all’elemento specifico della lista per recuperare l’ordine e accedere finalmente alla proprietà che ci interessa:
Funzione JS principale:RecuperaNumOrdine.
//Definizioni variabili
var titleList="Ordini Testata";
var idItem="";
var itemID;
//Definizioni variabili:Fine
function RecuperaNumOrdine(){
try {
//recuperiamo dalla query string la ID della lista e dell'elemento in lista
idItem=getUrlVars()["ItemId"];
//
var ctx = new SP.ClientContext.get_current();
var list = ctx.get_web().get_lists().getByTitle(titleList);
var query = "<View><ViewFields><FieldRef Name='ID'/></ViewFields></View>";
var camlQuery = new SP.CamlQuery();
//Query CAML per recuperare l'elemento
camlQuery.set_viewXml(
'<View><Query><Where>' +
'<Eq><FieldRef Name=\'ID\'/><Value Type=\'Number\'>' +
idItem+ '</Value></Eq>' +
'</Where></Query></View>'
);
itemID= list.getItems(camlQuery);
ctx.load(itemID, 'Include(ID,Numero_x0020_Ordine)');
ctx.executeQueryAsync(Function.createDelegate(this, this.queryDettaglio),
Function.createDelegate(this, this.error));
} catch (e) {
alert('OPS!Si è verificato un errore :-(. ' +'\n'+e.get_message());
}
}
In questa funzione, ci sono da evidenziare un paio di aspetti:
- ctx.get_web().get_lists().getByTitle(titleList) -> accediamo alla lista in base al titolo.
- camlQuery.set_viewXml -> impostiamo la query che rispetta la sintassi CAML per filtrare sulla colonna di sistema “ID” che rappresenta la chiave nella lista.
- ctx.load(itemID, ‘Include(ID,Numero_x0020_Ordine)’) -> con questo passaggio stiamo chiedendo di farci restituire solo i campi specificati. Questo passaggio è fondamentale per l’aspetto delle prestazioni. Riducendo il numero di campi che ci facciamo restituire, diminuisce il “peso” della struttura del file XML restituito e aumentano quindi le prestazioni.
Funzione per recuperare il parametro dalla Query String.
function getUrlVars() {
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for (var i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
Funzioni: “queryDettaglio” ed “error”.
function queryDettaglio() {
var NumeroOrdine;
try {
NumeroOrdine= itemID.getItemAtIndex(0).get_item('Numero_x0020_Ordine')
//Con il numero di ordine passao alla lista del dettaglio
//con la vista parametrizzata
window.location.replace('/Lists/Ordini%20Dettaglio/Dettaglio.aspx?Param1='
+NumeroOrdine);
} catch (e) {
alert('OPS!Si è verificato un errore :-(. ' +'\n'+e.get_message());
}
}
//
function error(sender, args) {
alert('OPS!Si è verificato un errore :-(. ' + args.get_message()
+ '\n' + args.get_stackTrace());
}
La funzione “queryDettaglio” viene eseguita solo se la richiesta ‘client-side’ va a buon fine e accedendo al primo elemento nell’oggetto ‘itemID’ (abbiamo filtrato sulla chiave) e da qui prendiamo il valore del campo che ci interessa.
Finalmente con una banale istruzione ‘window.location.replace..’ andiamo nella lista eseguendo la vista che accetta il filtro in query string.
Alla prossima!