25 april 2014

SharePoint 2013 List Item Attachment

Het heeft even een tijdje geduurd, maar hier ben ik dan weer met een nieuwe blogpost. Het onderwerp waar ik het dit keer over ga hebben is de functionaliteit in SharePoint waarbij een document als attachment bij een list item gevoegd kan worden.

Het probleem waar ik mee geconfronteerd werd, was dat men de attachments graag als lijstje van korte klikbare links in een SharePoint 2013 app wilde weergeven. Omdat dit ietwat ingewikkelder is dan het weergeven van een willekeurige column/field uit een lijst, vind ik het de moeite waard om in een blog uit te leggen hoe je bovenstaande voor elkaar krijgt. SharePoint apps bestaan uitsluitend uit client side code, dus we gaan met javascript aan de slag.

Voor ik begin wil ik nog even vermelden dat ik deze blogpost heb kunnen schrijven dankzij mijn collega’s Mirjam van Olst en Laurens Ruijtenberg, die mij hebben geholpen het mysterie op te lossen.

Wat je nodig hebt

In deze blogpost ga ik er vanuit dat je al een werkende SharePoint omgeving hebt en een SharePoint solution, waarin je (onder andere) de list item attachments wil gaan tonen. Zelf werk ik met Visual Studio 2013, dat is het makkelijkst om SharePoint code te debuggen. De gratis versie heet Visual Studio Express en is hier verkrijgbaar.

Om list item attachments op te halen, heb je vanzelfsprekend een SharePoint list nodig. Zelf heb ik een list met de naam ‘My Items’. Deze naam zal ik, voor de duidelijkheid, ook blijven gebruiken in de code. Je kan de list aanmaken via de solution (best practice), of een list aanmaken via de user interface van SharePoint. Vul je SharePoint list alvast met wat list items en voeg wat attachments aan die items toe. Dat scheelt later weer een hoop hersenkraken over waarom het niet werkt (als er geen attachments zijn, kunnen ze immers ook niet worden weergegeven).

Verder heb je jQuery en de ‘jQuery library for SharePoint Web Services’ nodig. De laatste vind je hier. jQuery zelf kan je via Visual Studio installeren (Tools -> Extensions and Updates).

We gaan in deze blogpost uit van 3 bestanden:

  • Een HTML template waarin we onze content gaan weergeven.
  • Een Javascript bestand welke bepaald wat we gaan weergeven, hoe we het gaan weergeven en waar we het gaan weergeven. Omdat dat ik zelf met de MVC methode heb gewerkt, is in mijn geval de Javascript verdeeld over 2 lossen bestanden. Doe wat je het prettigst vind.
  • Een CSS bestand om de noodzakelijke opmaak toe te voegen.

Beginnen

In het HTML template gaan we een korte structuur aanmaken, zodat we straks een plekje hebben om de list item attachments weer te geven. Ik schrijf niet de complete SharePoint pagina uit, alleen het stukje waar het hier om gaat.

<div class="container">
<h3 id="title">Attachments</h3>
<h3 id="listAttachments"></ul>
</div>

Het is niet veel: Een div om het geheel te positioneren, een header en een lijst waarin ieder attachment straks als list item wordt ingevoegd.

Data aanroepen

Dan komen we nu toe aan het grootste en moeilijkste deel, de javascript code. Er zijn een aantal dingen die we met javascript moeten gaan regelen:

  • We moeten de applicatie gaan vertellen wat we willen zien
    De query bestaat uit XML en die moeten we gaan opbouwen via Javascript.
  • We moeten de applicatie gaan vertellen hoe we het willen weergeven.

Query

De eerste stap is het aanroepen van de SharePoint list en definiëren wat we willen zien van die list.

var MyItemsRepository = function () {
this.QueryListOptions = function (query, viewFields, options) {
var result;

$().SPServices({
operation: "GetListItems",
async: false,
listName: "My Items",
webURL: commonSiteCollectionUrl,
CAMLQuery: query,
CAMLViewFields: viewFields,
CAMLQueryOptions: options,
completefunc: function (xData, Status) {
result = xData;
}
});

return result;
}

// Verderop in de tutorial maken we nog een stukje code aan dat nodig is om de attachments op te roepen, zet die code hier
}

var myItemsRepository = new MyItemsRepository();

In dit stukje code wordt een call gemaakt naar SharePoint en wordt de list met de naam ‘My Items’ aangeroepen. Hier wordt achter de schermen de ‘jQuery library for SharePoint Web Services’ voor gebruikt. Vervolgens zien we een aantal parameters Waar de lettercombinatie CAML in staat. CAML staat voor ‘Collaborative Application Markup Language’. Dit is een op XML gebaseerde taal die in SharePoint wordt gebruikt om fields en views te definiëren. De waarden van deze CAML parameters die je in de bovenstaande code ziet, zijn variabelen die we later gaan definiëren.

CamlDesigner2013

Als je veel verstand hebt van CAML en/of XML, zou je de queries zelf kunnen schrijven. Dit is echter niet nodig, want er bestaat een heel handige tool voor: CamlDesigner2013. Deze vind je hier.

Omdat ik niet zo goed ben in CAML en XML, ga ik deze tool gebruiken om de juiste query te genereren. Als je de tool wilt gebruiken is het van belang dat je hem installeert op een machine die toegang heeft tot je SharePoint site. De CamlDesigner2013 gaat namelijk een connectie maken met de site, om de site (en dus ook onze list) uit te kunnen lezen.

Omdat deze tutorial niet over CamlDesigner gaat, ga ik niet uitgebreid beschrijven hoe het werkt. Ik vertel slechts kort welke stappen je moet nemen in de tool om het voor nu gewenste resultaat te bereiken.

  1. Verbind CamlDesigner als eerste met de SharePoint site waarin de betreffende list staat.
    Rechts bovenin staat een knop ‘Connection’.
  2. Selecteer links in het menu de juiste list (in mijn geval ‘My Items’).
  3. Nu gaan we de code genereren:
    1. Klik, in het middelste venster bovenin, op ‘Where’.
      Sleep het field ‘ID’ naar de rechterkant en typ ‘id’ in de input.
      Je ziet nu onderin de code verschijnen die je hebt gegenereerd.
    2. Klik, weer in het middelste venster bovenin, op ‘ViewFields’.
      Sleep nu het field ‘Attachments’ naar de rechterkant.
      (zie de code weer verschijnen onderin)
    3. Klik, weer in het middelste venster bovenin, op ‘Query Options’.
      Vink ‘Include Attachments URL’s’ aan.

De XML code die we nodig hebben is nu gegenereerd. We kunnen het nu gaan toepassen in het volgende stukje javascript.

Query Part II

In de Javascript code hieronder komt de code die we met de CamlDesigner hebben gemaakt weer terug. De output van de CamlDesigner is hier als waarde toegevoegd aan de variabelen (query, viewFields en options) die we in het volgende stukje code hebben aangeroepen.

Vergeleken met wat de CamlDesigner heeft uitgespuugd, maken we nog een paar kleine wijzigingen:

  1. Rondom de <Where> tag zetten we nog een <Query> tag. Dat heeft de CamlDesigner namelijk nog niet voor ons gedaan.
  2. In de <Value> tag (binnen de <Where> tag), heeft de CamlDesigner een foutje gemaakt. Die heeft er namelijk Type='Counter' van gemaakt, maar wij willen graag Type='Text' zien.
  3. In dezelfde <Value> tag willen we niet ‘id’ als onderdeel van de XML, maar we willen dat daar via Javascript de id wordt aangeroepen. Verander daarom ‘id’ naar ‘" + id + "’ zoals in onderstaande code.

Je uiteindelijke code moet dus vergelijkbaar zijn met onderstaande code.

// Plaats onderstaande code op de plaats waar de comment in het stuk code eerder in deze tutorial dat aangeeft

this.GetById = function (id) {
var query = "<Query>
<Where>
<Eq>
<FieldRef Name='ID'/>
<Value Type='Text'>" + id + "</Value>
</Eq>
</Where>
</Query>";

var viewFields = "<ViewFields>
<FieldRef Name='Attachments' />
</ViewFields>";

var options = "<QueryOptions>
<IncludeAttachmentUrls>TRUE</IncludeAttachmentUrls>
</QueryOptions>";

var data = this.QueryListOptions(query, viewFields, options);

var count = $(data.responseXML).SPFilterNode("rs:data").attr("ItemCount");

if (count > 0) {
return data;
}

return null;
}

Voor de leesbaarheid van deze tutorial heb ik wat extra linebreaks en spacing aan de code toegevoegd. Normaal gesproken staat de gehele XML code op één regel.

Back to the point: Wat doet deze code nou precies?
Als eerste wordt een anonieme function gedefinieerd, die verwijst naar de variabele ‘MyItemsRepository’ (zie het stukje javascript eerder in deze tutorial) via het this keyword. this.GetById roept dus een item uit de list ‘My Items’ aan, aan de hand van de ID’s van de items. Vervolgens zorgen de variabelen ‘query’, ‘viewFields’ en ‘options’ dat de CAML query wordt samengesteld, die de juiste gegevens uit onze SharePoint list haalt.
Hierna maken we een variabele ‘ data’ aan, die de bovenstaande 3 variabelen samenvoegt en als laatste wordt van het geselecteerde item ieder (kunnen er meerdere zijn) attachment opgehaald. Het if statement op het eind kijkt of er überhaupt attachments zijn. Als dat zo is worden ze opgehaald en als er geen attachments zijn is de output null.

Data weergeven

Nu dat we de data hebben opgehaald, gaan we door middel van Javascript bepalen hoe we die data willen weergeven. We gaan de attachments in HTML list items weergeven, we gaan er klikbare links van maken en we gaan de URL’s inkorten.

De code die dat doet ziet er als volgt uit:

var MyItemsController = function () {

var MyItem;

this.showMyItemDetails = function (I_ID) {
if (myItem != "0") {

var xData = MyItemsRepository.GetById(I_ID);

if (xData != null) {
$(xData.responseXML).SPFilterNode("z:row").each(function () {

// Attachments weergeven
if ($(this).attr("ows_Attachments")) {
liHtml = [];

var attachments = $(this).attr("ows_Attachments").split(';#');

if (attachments !== undefined && attachments != null) {
for (var i = 0; i < attachments.length; i++) {
if (attachments[i] !== undefined && attachments[i] != null) {
if (attachments[i].length > 1) {
var fileName = attachments[i].split('/');

liHtml.push('<li><a href="' + attachments[i] + '">' + fileName
[fileName.length - 1] + '<a/></li>');

}
}
};
}

$('#listAttachments').html(liHtml.join(""));
}
});
}
}
}
}

Deze code begint met het kijken of er list items zijn, en als dat zo is worden die list items opgeroepen. Vervolgens begint onder de comment ‘Attachments weergeven’ de code die de Attachments aanroept en weergeeft. Als eerste wordt het field ‘ows_Attachments’ (zo heet het attachments field in de SharePoint list) opgezocht, waarvan de inhoud vervolgens wordt omgezet in een array.

Daarna wordt de variabele ‘attachments’ aangemaakt, waarin de opgehaalde attachments worden gesplitst. Dat is nodig omdat de attachments standaard gezamenlijk als één lange string worden weergegeven. Ieder attachment begint met ;#, dus daar gaan we op splitsen. Omdat de ;# voor ieder attachment staat, inclusief voor het eerste attachment, denkt de code dat ook voor het eerste attachment nog een item is. Die moeten we eruit filteren om te voorkomen dat het resulteert in een leeg eerste (HTML) list item in de uiteindelijke output.

De volgende stap is dat we met de for loop door de attachments heen gaan. In attachments[i] zit nu de URL van het attachment. De code vervolgt zich met twee if statements. De eerste kijkt of er überhaupt attachments aanwezig zijn, de tweede kijkt of de lengte van de attachment URL (attachments[i]) groter is dan 1. Met deze laatste if filteren we het eerste lege array item dat door de split in de array terecht is gekomen.

Vervolgens gebruiken we var fileName = attachments[i].split('/'); welke zorgt dat de totale URL van het attachment wordt gesplit op de /. De fileName variabele bevat nu een array met in elk item van de array een stukje van de URL. Met behulp van fileName[fileName.length - 1] vragen we het laatste stukje van de URL van het attachment op, de echte bestandsnaam.

Hoe werkt dit dan?
Nou, fileName.length geeft het totaal aantal items in de array terug. Als de array 1 item zou bevatten dan zou fileName.length ‘1’ zijn. Omdat de index van een array bij ‘0’ begint, verwijst fileName[fileName.length] naar een niet bestaand item en dus ontstaat er een foutmelding.

Ter illustratie:

laten we nu de ‘- 1’ weg, dan zou de code, in het geval van 4 items in de array, gaan zoeken naar item nummer 4 terwijl hij nummer 3 moet hebben.

Enfin, Door bovenstaande kan je dus in plaats van een hele lange URL, alleen de bestandsnaam weergeven, wat het lijstje attachments een stuk leesbaarder maakt.

In de regel erna worden de attachments in een <li> en een <a> gestopt, zodat de output een keurige HTML list wordt met een klikbare link in ieder list item. De laatste regel vervolgens, koppelt de output aan de <ul> met het id (#) listAttachments, zodat onze output in dat element wordt weergegeven.

Afronden

We zijn er bijna. Het laatste wat we nu nog gaan doen is een paar regeltjes CSS toevoegen, zodat onze HTML output toonbaar is. Natuurlijk kan je dit zelf naar wens aanpassen.

#container {
float:left;
height:200px;
width:300px;
}

#container h3 {
float:left;
width:100%;
}

#listAttachments {
float:left;
width:100%;
margin:10px 0;
padding:0;
}

#listAttachments li {
float:left;
width:100%;
margin:5px 0;
}

Bovenstaande CSS zorgt voor een box van 200x300 pixels met bovenin de header ‘Attachments’ en daaronder de lijst met attachments, keurig onder elkaar weergegeven met wat ruimte tussen ieder element.

Nu is het tijd om je solution te deployen. Als je solution is geïnstalleerd en de feature(s) is/zijn geactiveerd, zie je nu je attachments op de pagina waarop je de HTML hebt toegevoegd.