1 Een webgebaseerd systeem voor het uitwisselen van zeldzame medische hersenscans Dieter De Coninck, Wim Verhamme Promotor: prof. dr. ir. Wilfried Phi...
Een webgebaseerd systeem voor het uitwisselen van zeldzame medische hersenscans Dieter De Coninck, Wim Verhamme
Promotor: prof. dr. ir. Wilfried Philips Begeleiders: dr. Ewout Vansteenkiste, Philippe Serbruyns Masterproef ingediend tot het behalen van de academische graad van Master in de toegepaste informatica
Vakgroep Telecommunicatie en informatieverwerking Voorzitter: prof. dr. ir. Herwig Bruneel Faculteit Ingenieurswetenschappen Academiejaar 2008-2009
Woord vooraf In ieder eindwerk is een dankwoord gericht aan een aantal personen zeker op zijn plaats. Immers, dikwijls is het zo, dat zonder de hulp van deze personen, de kwaliteit van het eindwerk nooit van een vergelijkbaar niveau zou kunnen zijn als het eindwerk dat nu onder uw neus ligt. Ook bij dit eindwerk is dat zeker niet anders en daarom willen wij van dit dankwoord dankbaar gebruik maken om een aantal personen te bedanken. Eerst en vooral is dat natuurlijk onze promotor, prof. dr. ir. Wilfried Philips. Zonder hem zou er van dit eindwerkonderwerp waarschijnlijk geen sprake zijn geweest, laat staan dat we dan de infrastructuur en kennis van de vakgroep TELIN hadden kunnen (ge/mis)bruiken. Een tweede woord van dank gaat ook uit naar onze eerste begeleider, dr. Ewout Vansteenkiste, die zijn enthousiasme voor het onderwerp als geen ander op ons wist over te brengen en ons steeds voorzag van alle nodige informatie. Ook onze tweede begeleider, Philippe Serbruyns, willen we zeker niet uit het oog verliezen voor zijn technische ondersteuning en raadgeving bij dit hele gebeuren. Buiten al hetgene hij ons geleerd heeft, zullen we zeker blijven onthouden “dat het niet is dat iets niet bestaat, maar dat je het gewoon nog niet gevonden hebt”. Ook de artsen, meer bepaald Paul Govaert en zijn team, van het Erasmus Medisch Centrum te Rotterdam willen we bedanken, omdat zij het volledig zagen zitten dat een stel studenten aan dit project begon. Uiteraard zijn er zoveel andere mensen in onze naaste omgeving die we ook willen bedanken, maar toch vestigen we graag nog even speciaal de aandacht op onze disgenoten, die ons onder de middag voorzagen van aangenaam gezelschap en een luisterend oor. Ook onze ouders willen we nog eens bijzonder bedanken, want zonder hun (financiële) steun, hadden we waarschijnlijk niet de kans gehad om deze ManaMa te volgen. Bedankt iedereen! Wim Verhamme en Dieter De Coninck 22 mei 2009
Inhoudsopgave Inhoudsopgave ....................................................................................................................................... I Lijst van figuren .................................................................................................................................... III Lijst van de gebruikte afkortingen ...................................................................................................... IV 1.
Lijst van figuren Figuur 1: werking van de select-velden voor ingave van de classifier “vessels affacted”. ....... 4 Figuur 2: overzicht van de opbouw van de webapplicatie. ..................................................... 11 Figuur 3: Startpagina .............................................................................................................. 23 Figuur 4: Upload stap 1/4: Fill in patient data ......................................................................... 27 Figuur 5: Upload stap 1/4: Vessels affected........................................................................... 29 Figuur 6: Upload stap 2/4: Please upload the data ................................................................ 36 Figuur 7: Upload stap 3/4: Checking uploaded data .............................................................. 39 Figuur 8: Upload stap 4/4: Randomizing/anonymizing the data ............................................. 42 Figuur 9: Menu gebruiker met userlevel 3 .............................................................................. 44 Figuur 10: Menu gebruiker met userlevel 2 ............................................................................ 45 Figuur 11: Menu gebruiker met userlevel 1 ............................................................................ 47
III
Lijst van de gebruikte afkortingen ASP CMS CSS DBMS DICOM HTML JSP PHP SHA SQL SSL W3C
Active Server Pages Content management system Cascading Style Sheets Database management system Digital Imaging and Communications in Medicine HyperText Markup Language JavaServer Pages PHP: Hypertext Preprocessor Secure Hash Algorithm Structured Query Language Secure Sockets Layer World Wide Web Consortium
IV
Inleiding
1. Inleiding In de neonatale afdeling van het Erasmus Medisch Centrum te Rotterdam wordt sinds jaren onderzoek verricht op hersenziekten bij zeer premature baby’s. Daarvoor worden in de diagnose meestal echografiebeelden en magnetische resonantiebeeldvolumes gebruikt. Aangezien het hier zeer premature kinderen betreft zijn bepaalde ziektebeelden echter heel zeldzaam en vaak ook moeilijk te beschrijven. Er is dan ook een nood aan het verzamelen van zoveel mogelijk pathologische data, liefst vanuit expertisecentra over de hele wereld. Omdat datatransfer niet triviaal is als het om medische beelddata gaat, zowel de grote beeldvolumes als vooral de privacy van de patiënten spelen een rol, gebeurt dit voorlopig vooral op een nationale, kleinschalige en ongecontroleerde manier. Dit project wil een eerste aanzet geven tot een veilige en snelle, webgebaseerde interface die deze datatransfer mogelijk moet maken. Het resultaat van dit project kan u (voorlopig) bekijken op https://telin.ugent.be/~wverhamm. Om de applicatie in al zijn aspecten te kunnen bekijken en testen werden volgende (voorlopige) inlogaccounts voorzien:
username: admin username: rotterdam username: user
password: 123 password: 123 password: 123
level: 1 level: 2 level: 3
1
Doelstellingen
2. Doelstellingen In dit eindwerk wordt een generiek webgebaseerd systeem ontwikkeld voor het veilig uploaden, opslaan en bekijken van medische beelden. Verschillende gebruikers moeten zich kunnen registreren en aanmelden via een webinterface. Eens aangemeld moeten ze zelf DICOM-data (medisch beeldformaat) kunnen opladen in een databankstructuur die ze dan achteraf gezamenlijk moeten kunnen raadplegen. Het anonimiseren van de DICOM-data wordt aan de artsen zelf overgelaten, en dit volgens de landspecifieke protocols. De gebruikers moeten ook via een gebruiksvriendelijk permissiesysteem kunnen definiëren welke gebruikers toegang krijgen tot de data en moeten ook gegevens kunnen verwijderen. Naast het opladen van beelddata moeten de artsen ook de mogelijkheid krijgen om een gestandaardiseerd datadocument mee te sturen die de nodige klinische data bevat. Dit laatste document wordt in overleg met de artsen in Rotterdam opgesteld. De implementatie van dit systeem zal gebeuren aan de hand van PHP, gekoppeld aan een databank (MySQL) voor het opslaan van vragen en antwoorden. HTTPS worden gebruikt om de dataoverdracht te beveiligen. Belangrijk bij dit alles is om in het achterhoofd te houden dat na ontwikkeling op een testserver de applicatie meer dan waarschijnlijk zal overgebracht moeten worden op een definitieve server. Cross-server compatibiliteit vormt dus een belangrijk punt, evenals crossbrowser compatibiliteit overigens.
2
Algemene aanpak
3. Algemene aanpak 3.1
Schermresolutie
Er werd gekozen om de webapplicatie te optimaliseren voor een schermresolutie 1024x768 pixels of groter. Enerzijds werd dit gedaan om praktische redenen: de applicatie geeft soms vrij grote tabellen als output terug. Om deze op een overzichtelijke manier te kunnen presenteren, bleek een resolutie van 800x600 pixels te klein. Anderzijds is de fractie internetgebruikers met een resolutie kleiner dan 1024x768 pixels zeer klein. Volgens statistieken van september 2008 van OneStat.com, provider van real-time webstatistieken, gebruikt nog amper 4% van de internetgebruikers de resolutie 800x600 pixels. Dit heeft, volgens OneStat veel te maken met de vrij recente opkomst van steeds grotere en breedbeeldmonitors.1 Als we bovendien aan ons doelpubliek denken, dat medische beelden moet kunnen analyseren op hun monitor, is het waarschijnlijk dat hiervan nog minder dan 4% een resolutie van 800x600 pixels gebruikt.
3.2
Lay-out
De lay-out van de webapplicatie werd bewust licht en functioneel gehouden om de gebruiker niet af te leiden van het eigenlijke doel van de applicatie. Door subtiele effecten in bijvoorbeeld de invulvelden van een formulier werd de lay-out wel veraangenaamd. Er werd een kleurschema met tinten van blauw en grijs gebruikt.
3.3
HyperText Markup Language (HTML)
Ook bij een dynamische website interpreteert de browser op het einde van de rit niet veel meer dan statische HTML-code. Om ons te verzekeren van een hogere browsercompatibiliteit werden alle pagina’s zo strikt mogelijk opgebouwd volgens de richtlijnen voor HTML 4.01 Transitional van het World Wide Web Consortium (W3C)2. Om ons hiervan te vergewissen werden alle pagina’s gevalideerd met de W3C Validator3. Op enkele pagina’s na bleken alle pagina’s correct te zijn. In het upload-proces werden twee pagina’s niet correct gevalideerd en konden de fouten ook niet gecorrigeerd worden. Een eerste fout betreft het feit dat op de pagina voor invoer van de extra informatie lege select-lijsten (die geen options bevatten) voorkomen. Dit is inderdaad het geval wanneer nog geen data in het formulier ingevuld werden. Op dat moment zijn de select-lijsten die dienen om de classifier “vessels affected” in te geven leeg. Zij worden pas opgevuld wanneer er links werkelijk opties aangeduid worden en verplaatst worden naar de resultatenlijst (zie Figuur 1). Vermits dit deel uitmaakt van het opgezette design, kan er niet aan deze fout tegemoet gekomen worden.
1
ONESTAT.com, (http://www.onestat.com/html/press-release-wide-screen-resolutions-extremely-popular.html) WORLD WIDE WEB CONSORTIUM, (http://www.w3c.org) 3 THE W3C MARKUP VALIDATION SERVICE, (http://validator.w3.org) 2
3
Algemene aanpak
Figuur 1: werking van de select-velden voor ingave van de classifier “vessels affacted”. Een aantal andere fouten komt voort uit de output die het uploadscript genereert. Zo genereert het script van de uploader bij het opstarten (wanneer nog geen bestanden voor uploaden geselecteerd werden) bijvoorbeeld een lege unsorted list
. Volgens de normen van het W3 Consortium moet zo’n lijst echter steeds minstens één list-item
bevatten. Later, bij het selecteren van op te laden bestanden, worden deze list-items wel ingevuld. Dit euvel kon echter niet verholpen worden, daar de werking van het script dan in het gedrang kwam.
3.4
PHP: Hypertext Preprocessor
Vele programmeer- en scriptingtalen (zoals ASP, JSP, PHP,…) lenen zich ertoe om dynamische websites te maken. Dikwijls (zoals bv. ASP en PHP) zijn deze in vele opzichten gelijkaardig, zijn ze relatief gemakkelijk aan te leren en bieden een gebruiksvriendelijke syntax. Ook de performanties liggen in dezelfde lijn. Het voordeel van ASP tegenover PHP is wel dat ASP iets beter object-georiënteerd programmeren toelaat. Nadeel is echter dan weer dat ASP enkel op Windows-platformen draait, terwijl PHP platformonafhankelijk is. Net omwille van het feit dat de applicatie platformonafhankelijk moet zijn (zie 2. Doelstellingen), werd ervoor gekozen om te werken met PHP. Daarenboven is PHP Open Source en kent deze taal een zeer grote gebruikerscommunity waardoor bugs en problemen vrij snel en efficiënt verholpen worden. In PHP is het mogelijk om via de functies include_once() en require_once() de inhoud van een bestand volledig te importeren in een ander bestand. De inhoud van het geïmporteerde bestand wordt dan volledig ingevoegd in HTML mode. Het invoegen van bestanden geniet uiteraard de voorkeur boven het hergebruiken van veelvoorkomende definities en functies. Op deze manier kunnen o.a. ook parameters gemakkelijk doorgegeven worden naar alle pagina’s die er gebruik van maken. Aanpassing van die parameters dient daarenboven dan slechts op één plaats te gebeuren i.p.v. in iedere pagina apart. Onder andere voor dit doeleinde werd ook in deze thesis gebruik gemaakt van deze functies voor bijvoorbeeld het opzetten van de connectie met de databank en het doorgeven van configuratieparameters. Ook voor het menu bovenaan iedere pagina of voor langere of complexere functies en klassen werd gebruik gemaakt van deze functies. Bijkomend voordeel van deze manier van werken is dat de broncode van iedere pagina aan overzichtelijkheid wint.
4
Algemene aanpak
3.5
MySQL
Alhoewel er tientallen verschillende pakketten op de markt zijn, komt de keuze van een database beheerssysteem (DBMS) in de praktijk bijna altijd neer op de keuze tussen MySQL of Oracle. Er werd voor verschillende redenen voor MySQL gekozen. Om te beginnen verschijnt MySQL onder de Open Source License wat het gebruik ervan gratis maakt, terwijl Oracle een commercieel pakket is. Dat alleen gelaten was één van de belangrijkste aandachtspunten bij de keuze de performantie. MySQL blijkt zonder problemen met grote datavolumes te kunnen omgaan, wat op langere termijn een voordeel kan zijn. Dit lijkt ook gestaafd te worden door de keuze voor MySQL van bijvoorbeeld Google en Facebook voor het gebruik in hun networking services. MySQL is ook een gangbare database voor opensource-fora en contentmanagementsystemen (CMS) of blogsoftware zoals XOOPS of Mambo en Joomla. Daarenboven is MySQL een lichter pakket (Oracle heeft zeer veel gespecialiseerde functies aan boord, welke niet nodig zouden zijn). Dat heeft o.a. ook tot gevolg dat MySQL in staat is om data sneller op te vragen. Ten slotte wordt MySQL veel in combinatie met PHP gebruikt, wat o.a. het ontstaan heeft gegeven aan een aantal PHP-functies die de interactie met MySQL vergroten. MySQL werkt daarenboven gemakkelijker dan Oracle, wat voor nieuwkomers zoals ons, een bijkomend voordeel was.4
3.6
phpMyAdmin
Het opstellen en bewerken van de MySQL-databank, de tabellen en de inhoud kan gedaan worden door het intypen van bepaalde commando’s in UNIX. Er werd echter in de Open Source Community een zeer handige applicatie op basis van PHP gemaakt welke dit alles ook mogelijk maakt via een soort gebruikersinterface, nl. phpMyAdmin. Deze interface was ook op de server geïnstalleerd en werd dan ook gebruikt om de meest courante handelingen op een gemakkelijke, snelle en platformonafhankelijke manier uit te voeren.5
3.7
JavaScript en Flash
JavaScript is een client-sided objectgeoriënteerde scriptingtaal die veel gebruikt wordt om webpagina’s dynamischer en interactiever te maken zodat typisch voornamelijk een verhoogd gebruiksgemak bekomen wordt. Zowel de naam als de syntax doen vermoeden dat JavaScript sterk gelijkt op de programmeertaal JAVA. Met de overeenkomsten in de syntax houdt de vergelijking echter op. Alhoewel JAVA ook in de webomgeving frequent gebruikt wordt, wordt het typisch toegepast in applicaties, zij het in de vorm van een soort mini-applicaties, zoals de applets en zoals deze in de webomgeving gebruikt wordt, of in de vorm van standalone applicaties.
4 5
WIKIPEDIA – MySQL, (http://nl.wikipedia.org/wiki/MySQL), 15 mei 2009 WIKIPEDIA – phpMyAdmin, (http://nl.wikipedia.org/wiki/PhpMyAdmin), 15 mei 2009
5
Algemene aanpak JavaScript kan echter nooit losgekoppeld van HTML voorkomen. Verder heeft JavaScript ook inhoudelijk meer gemeen met bepaalde functionele programmeertalen dan met JAVA en biedt het een prototype-gebaseerd overervingsmechanisme. Vandaag vindt JavaScript steeds meer toepassing, onder andere als onderdeel van AJAXtoepassingen (zie ook 5.1.5.2. Stap 2: Opladen van (meerdere) beelden). Volgens Wikipedia6 zijn de opvallendste kenmerken van JavaScript:
Prototype-gebaseerde overerving. Dit in tegenstelling tot de meeste gangbare objectgeoriënteerde programmeertalen, die klasse-gebaseerde overerving gebruiken. Functioneel programmeren. JavaScript is sterk beïnvloed door functionele programmeertalen zoals Self en Scheme. Zo zijn functies in JavaScript first-class, wat wil zeggen dat functies gewone objecten zijn. Verder ondersteunt JavaScript geneste functies en closures. Reguliere expressies. JavaScript heeft een ingebouwde ondersteuning met speciale syntax voor reguliere expressies. Deze is vergelijkbaar en tot op zekere hoogte compatibel met die van Perl. Objecten zijn arrays. Objecten in JavaScript zijn associatieve arrays.
Ondanks het feit dat de weergave van JavaScript en Flash afhankelijk is van de gebruikersinstellingen in de browser, kon het gebruik ervan niet vermeden worden. De mogelijkheid om bij de upload van de beelden meerdere beelden tegelijk te selecteren en op te laden, bijvoorbeeld, kan niet bewerkstelligd worden door enkel het gebruik van HTML en PHP. Inputvelden voor bestanden laten in HTML immers maar de waarde van een enkel bestand toe. In de praktijk, voor het opladen van meerdere bestanden, zou dat betekenen dat ieder bestand apart in een nieuw veld geselecteerd dient te worden. Voor een klein aantal bestanden kan dit nog gerechtvaardigd worden, maar voor meerdere bestanden betekent dit een serieuze beperking op de gebruiksvriendelijkheid. In gedachten houdend dat sommige medische beelden uit tot meer dan 50 aparte bestanden kunnen bestaan, werd er voor geopteerd om gebruik te maken van JavaScript en Flash. Op die manier kan de selectie van meerdere bestanden wel in een enkele klik gebeuren (zie ook 5.1.5.2. Stap 2: Opladen van (meerdere) beelden). Ook op andere plaatsen in de webapplicatie werd gebruik gemaakt van JavaScript. Zo steunt het menu bovenaan iedere pagina bijvoorbeeld op de combinatie JavaScript - CSS. Tenslotte werd ook in het formulier voor het toevoegen van bepaalde data bij de MRIbeelden gebruik gemaakt van JavaScript. Daar werd bij het invoeren van de classifier “vessels affected” gebruik gemaakt van verschillende multiple select velden: een select-veld links op het scherm lijst alle mogelijkheden op, waarna de mogelijkheden die van toepassing zijn via een druk op de knop verplaatst kunnen worden naar een select-veld rechts op het scherm (Figuur 1). Dit werd zo gedaan uit overwegingen naar overzichtelijkheid en
6
WIKIPEDIA – JAVASCRIPT, (http://nl.wikipedia.org/wiki/Javascript), 15 mei 2009
6
Algemene aanpak gebruiksgemak toe. Echter het specifiek aansturen van bepaalde velden in een formulier via PHP alleen is niet mogelijk gebleken. Zoals eerder vermeld houdt het gebruik van JavaScript en Flash wel in dat de gebruikersinstellingen in de browser dit moeten toelaten. Wanneer deze instellingen echter niet juist gemaakt zijn, wordt er op de homepagina van de applicatie een waarschuwing weergegeven.
3.8
Cascading Style Sheets (CSS)
Cascading Style Sheets zijn een manier om de vormgeving voor verschillende webpagina’s gemakkelijk en eenduidig vast te leggen. Dit gebeurt typisch door de informatie over de vormgeving te koppelen aan tags in de HTML-code. Die informatie kan ofwel in het document zelf voorkomen, ofwel opgenomen zijn in een extern document (de stylesheet) dat vervolgens via importatie gekoppeld wordt aan iedere individuele webpagina. Een bijkomend voordeel dat zulke stylesheet biedt is dat de logische en fysische structuur van elkaar losgekoppeld kunnen worden. Op die manier kan een consistente vormgeving over meerdere documenten bereikt worden.7 Naast het gemakkelijk invoeren van een consistente vormgeving via CSS, is een andere belangrijke reden om CSS te gebruiken het standaardiseren van webpagina’s zodat verschillende browsers dezelfde pagina op dezelfde wijze aan de gebruiker tonen. Evenals de HTML-standaard werd ook hiervoor de standaard vastgelegd door het World Wide Web Consortium (W3C). Alhoewel, zoals eerder aangehaald, de stijl en opmaak van de applicatie bewust strak en sober werd gehouden, werd gebruik gemaakt van CSS om deze te veraangenamen en de overzichtelijkheid te vergroten. Op de hele site is één stylesheet van toepassing die gevalideerd werd met de CSS Validator van het W3C8. De stylesheet bleek volledig correct.
3.9
Secure Sockets Layer (SSL)
Secure Sockets Layer (SSL) is een encryptie-protocol dat gebruikt wordt om communicatie op het internet te beveiligen. Naast een beveiligde verbinding tussen server en Internet levert het ook, via cryptografie, authenticatie.
7 8
WIKIPEDIA – CSS, (http://nl.wikipedia.org/wiki/CSS), 15 mei 2009 WORLD WIDE WEB CONSORTIUM - CSS VALIDATOR , (http://jigsaw.w3.org/css-validator), 16 mei 2009
7
Algemene aanpak SSL maakt, zoals deze te vinden zijn op Wikipedia9, gebruik van een aantal verschillende stadia: 1. Peer negotiation for algorithm support 2. Public-key encryption-based key exchange and certificate-based authentication 3. Symmetric cipher-based traffic encryption Het protocol bevindt zich tussen de applicatielaag en de transportlaag van het OSI-model. Alhoewel SSL beveiliging van gegevens aan kan bieden voor elk applicatieprotocol dat gebruik maakt van TCP, wordt het het meest van al gebruikt in combinatie met het HTTPprotocol. Voor het garanderen van een veilige gegevensoverdracht werd ook in onze webapplicatie gebruik gemaakt van het SSL protocol. Van zodra https:// voor de URL getypt wordt, zorgt het voor encryptie van alle verzonden gegevens.
3.10
Browsercompatibiliteit
Browsercompatibiliteit is een belangrijk gegeven bij het maken van een webapplicatie. Hier werd dan ook uitvoerig op getest door de applicatie op verschillende platformen in verschillende browsers te laten draaien. De applicatie werd getest op een Windows-, UNIXen MAC-platform voor alle A-grade browsers (Mozilla Firefox, Microsoft Internet Explorer, Opera en Safari)10. Over het algemeen draaide de applicatie zonder veel problemen op alle platformen en in alle browsers, uitgezonderd van Mozilla Firefox. In deze laatste browser doken problemen op met het gebruik van een zelfondertekend veiligheidscertificaat in combinatie met de upload functie van Flash. Deze laatste wordt gebruikt in de multi-file uploader waardoor deze in Firefox niet meer werkt. Dit probleem is bij Adobe al gekend vanaf Flash versie 8, en is nu in de huidige recentste versie (versie 10) nog steeds niet opgelost.11 In alle andere browsers doet de multi-file uploader het wel. Aangezien toch voor het gebruiksgemak geopteerd werd voor het gebruik van deze multi-file uploader en die functionaliteit niet bekomen kan worden door enkel gebruik te maken van PHP en HTML, resten er twee mogelijke oplossingen voor het probleem.
ofwel gebruik maken van een certificaat dat ondertekend werd door een Certification Authority ofwel het gebruik van Mozilla Firefox vermijden totdat het euvel in Flash verholpen wordt door Adobe
9
WIKIPEDIA – SSL, (http://nl.wikipedia.org/wiki/Secure_Sockets_Layer), 15 mei 2009 YAHOO DEVELOPER NETWORK , (http://developer.yahoo.com/yui/articles/gbs) 11 ADOBE, (http://bugs.adobe.com/jira/browse/FP-226), 18 mei 2009 10
8
Algemene aanpak Het spreekt voor zich dat de eerste oplossing naar gebruiksgemak toe de interessanste is. Verder vertoonde Microsoft Internet Explorer over zijn verschillende versies heen (v6, v7 en v8) een opmerkelijk verschil in de lay-out, ondanks het feit dat alle HTML code correct gevalideerd werd. De werking van de applicatie zelf komt hierdoor echter niet in het gedrang. In de onderstaande tabel (Tabel 1) wordt het gebruik van de browsers van de bezoekers van de site W3Schools.com weergegeven. Daaruit blijkt dat meer dan 50% van de gebruikers het net opgaat met een andere browser dan Internet Explorer. Tabel 1: Procentueel gebruikersaantal voor verschillende browsers. 2009 IE6 IE7 IE8 Fx Chrome S O April 15,4% 23,2% 3,5% 47,1% 4,9% 3,0% 2,2% Maart 17,0% 24,9% 1,4% 46,5% 4,2% 3,1% 2,3% Februari 17,4% 25,4% 0,8% 46,4% 4,0% 3,0% 2,2% Januari 18,5% 25,7% 0,6% 45,5% 3,9% 3,0% 2,3%
9
Algemene structuur en opbouw van de webapplicatie
4. Algemene structuur en opbouw van de webapplicatie 4.1
Algemene structuur van de applicatie en userlevels
In wat volgt zullen de functionaliteiten die de webapplicatie biedt bondig besproken worden. Een grafisch overzicht van die functionaliteiten en hoe ze in de structuur van de applicatie kaderen, kan je zien in Figuur 2. Hierbij dient echter de opmerking gemaakt te worden dat de functionaliteiten uitbreiden naargelang de userlevel van de gebruiker. Iedere hogere userlevel erft de functionaliteiten van een lagere userlevel over en krijgt daarenboven extra functionaliteiten. Er zijn drie userlevels die elk met een bepaald type gebruiker overeenstemmen. Gebruikers met level 3 (blauw op Figuur 2) zijn eigenlijk de standaard gebruikers: artsen die de applicatie zullen gebruiken om data en bestanden door te sturen naar de experts in Rotterdam. Gebruikers met level 2 (groen op Figuur 2) zullen veelal de experts in Rotterdam zijn, welke naast eigen opgeladen data ook alle andere data in de database te zien krijgen en aan iedere case een rapport kunnen koppelen. Tenslotte zijn er gebruikers met level 1 (rood op Figuur 2). Dit zijn de eigenlijke administratoren die gebruikers toegang kunnen verlenen tot de applicatie, die gebruikerdetails kunnen wijzigen, zelf nieuwe gebruikers kunnen aanmaken en gegevens uit de databank kunnen wissen. Het is aan te raden om van dit hoogste level slechts één of twee gebruikers te hebben, zodat de verantwoordelijkheden en aansprakelijkheden duidelijk afgelijnd kunnen worden. Evenals is het niet zo dat de administrator (gebruiker van level 1) dezelfde persoon hoeft te zijn als de databasebeheerder. De administrator hoeft eigenlijk geen technische details van de applicatie te kennen om er mee te kunnen werken, maar moet wel de kennis hebben om te kunnen inschatten of nagaan of een bepaalde gebruiker wel gerechtigd is om toegang te krijgen tot de applicatie. In dit geval houdt dat in dat de administrator zal moeten kunnen nagaan of een bepaald persoon wel een (kinder)arts is. Iedere gebruiker die toegang heeft gekregen tot de applicatie zal standaard de laagste level toegewezen krijgen, totdat de administrator daar anders over beslist. Waar de meeste omschrijvingen in Figuur 2 voor zich spreken, zal hieronder toch nog iets meer informatie over bepaalde functionaliteiten gegeven worden. Laten we beginnen met het uploadproces. Dit start met een formulier waar de gebruiker de nodige, minimale informatie (classifiers) kan opgeven omtrent de patiënt en zijn ziektebeeld. Deze gegevens zijn volledig anoniem en zullen nooit tot een bepaalde patiënt kunnen getraceerd worden. In een tweede stap zal de gebruiker de nodige medische beelden aan deze data kunnen koppelen door deze op te laden naar de server. De opladingsfunctionaliteit laat toe dat er meerdere bestanden tegelijkertijd geselecteerd en opgeladen kunnen worden. Nadat het opladen succesvol werd afgerond krijgt de gebruiker een overzicht te zien van alle data en de gekoppelde bestanden. Indien deze informatie correct bevonden wordt door de gebruiker, start het anonymisatieproces van de meta-data (tags) in de eventuele opgeladen DICOM-bestanden. 10
Algemene structuur en opbouw van de webapplicatie Na succesvolle anonimisatie worden de bestanden gezipt en op de server definitief weggeschreven. Er zijn een tweetal redenen waarom de bestanden gezipt op de server worden opgeslagen. Ten eerste is gebleken dat bij de beelden een compressiewinst tot 50% kan bekomen worden (gemiddeld 30-40%). Deze compressiewinst betekent enerzijds capaciteitswinst op de server en anderzijds kleinere bestanden die gedownload moeten worden. Een tweede reden om te zippen is dat bij het downloaden van de beelden, niet ieder bestand één voor één gedownload moeten worden (DICOM-beelden kunnen uit tientallen bestanden bestaan), maar ze allemaal tegelijkertijd gedownload kunnen worden. Dit komt uiteraard het gebruiksgemak van de applicatie ten goede.
Figuur 2: overzicht van de opbouw van de webapplicatie. Blauwe kaders duiden op functionaliteiten die voor iedere gebruiker beschikbaar zijn (userlevel 3). Groene en rode kaders duiden op extra functionaliteiten voor gebruikers met respectievelijk userlevel 2 en 1.
11
Algemene structuur en opbouw van de webapplicatie Iedere gebruiker kan zijn eigen gegevens en beelden uit de database opvragen in een soort inbox. Maar daarnaast kan de gebruiker deze gegevens ook gemakkelijk sorteren op een aantal parameters (zoals ID, datum, methode,…) zodat het zoeken in de gegevens gemakkelijker kan gebeuren. Gebruikers van level 2 hebben een gelijkaardige functionaliteit in “all data” en kunnen daarnaast de gegevens ook filteren op gebruikersnaam en “imaging method”. Tenslotte kan iedere gebruiker vlot zijn eigen wachtwoord en e-mailadres wijzigen. Administrators kunnen elk detail van iedere gebruiker wijzigen.
4.2
Structuur van de database
Over het algemeen hebben we de database zo proberen op te bouwen dat deze zo goed mogelijk zijn eigen integriteit bewaart. D.w.z. dat we op het niveau van de database zelf zoveel mogelijk controles hebben proberen in te bouwen om corrupte, foutieve of redundante informatie te vermijden. Een volledige structuur van de database is te vinden in bijlage 9.1. Databasestructuur. Hieronder zal slechts een globaal beeld geschetst worden en zullen een aantal keuzes voor bepaalde instellingen verder toegelicht worden.
4.2.1 De tabellen De database is opgebouwd uit vier tabellen. De tabel data slaat alle classifiers op die de artsen mee ingeven met de beelden, met uitzondering van de classifier “vessels affected” die in een aparte tabel vesslesAffected opgeslagen wordt. Op deze keuze komen we later terug. Ook de link naar de plaats waar de beelden die bij de data horen, zijn opgeslagen, zit in deze tabel. Alles wat te maken heeft met gebruikers en gebruikerbeheer wordt in de tabel users opgeslagen. Tenslotte wordt alles wat te maken heeft met de rapporten die later, na analyse van de beelden al dan niet toegevoegd kunnen worden aan de beelden, opgeslagen in de tabel reports. Er werd expliciet gekozen om de opgeladen medische beelden niet in de database zelf op te slaan, maar ergens apart op de server. Dit werd gedaan omdat sommige beelden zodanig groot (tot 500 MB en meer) kunnen worden, dat de performantie en snelheid van de database eronder zouden gaan lijden. In de tabel data wordt in de plaats daarvan een link opgeslagen die het pad beschrijft naar het .zip-bestand waarin de beelden opgeslagen werden. Zoals eerder reeds aangehaald wordt de classifier “vessels affected” niet in de tabel data opgeslagen, wat een logische keuze zou zijn aangezien alle andere classifiers wel in die tabel opgeslagen worden. Dit heeft vooral te maken met de complexe natuur van die classifier. Artsen moeten in de applicatie in staat zijn om uit een lange lijst mogelijke vessels diegene te selecteren die op de patiënt van toepassing zijn (zie bijlage 9.2. Overzicht classifiers). Vermits de omschrijvingen van deze vessels lang kunnen zijn en zeer veel verschillende onderlinge combinaties mogelijk zijn, zou één veld van het type varchar() niet steeds volstaan om al deze informatie op te slaan. Er zou dan geopteerd kunnen worden 12
Algemene structuur en opbouw van de webapplicatie voor een veld van het type text(), maar dat zou de overzichtelijkheid van de data in de tabel niet ten goede komen. Er zouden dus op zijn minst een aantal verschillende velden van het type varchar() aan de tabel data moeten toegevoegd worden. Dat maakte deze tabel echter zo groot, dat ook weer de overzichtelijkheid (en misschien de performantie) eronder kwam te lijden. Het was dan de vraag hoe de aparte tabel vesselsAffected opgebouwd moest worden. Een eerste idee was om voor iedere mogelijke vessel een apart veld te voorzien en in dat veld “0” of “1” te laten wegschrijven naargelang de vessel in het formulier al dan niet aangeduid werd. Dat zou echter wederom leiden tot een zeer grote en onoverzichtelijke tabel. Bovendien zou de tabel dan in de meeste gevallen waarschijnlijk ook weinig informatie bevatten, aangezien het leeuwendeel van de velden de waarde “0” zou hebben. Een andere reden was dat op die manier ook telkens zeer veel velden vanuit de applicatie zouden moeten opgevraagd worden, wat zowel de efficiëntie van het programmeren als de efficiëntie van de werking van de applicatie verkleinde. Tenslotte werd dan geopteerd voor een gulden middenweg. De tabel met mogelijke vessels werd opgesplitst 7 grote delen: “arterial cortical”, “arterial perforator”, “brainstem arteries”, “spinal cord arteries”, “sinus venous”, “deep vein” en “other veins” (zie bijlage 9.2. Overzicht classifiers). Ieder deel kreeg in de database een apart veld toegewezen. Alle vessels die in het formulier binnen een bepaald deel aangeduid worden, worden in hetzelfde veld opgeslagen, gescheiden door een komma (“,”). Als er gegevens uit de tabel opgevraagd worden, dient maar een beperkt aantal velden geadresseerd te worden. Bovendien kunnen de gegevens binnen één veld via de PHP functie split(‘,’) terug gemakkelijk van elkaar gescheiden worden. De tabel vesselsAffected wordt via een uniek ID aan de tabel data gelinkt. De tabel users bevat naast de informatie die ieder gebruiker bij het aanvragen van een login moet opgeven, nog twee andere velden die even speciale aandacht verdienen. Enerzijds is dat het veld allowAccess dat bepaalt of een bepaalde gebruiker al toegang tot de applicatie heeft gekregen of niet. Via het type enum(‘0’,’1’) wordt ervoor gezorgd dat dit veld geen andere waarden dan 0 en 1 aanvaardt. Standaard wordt de waarde in dit veld automatisch op 0 (geen toegang) geïnitialiseerd. Vergelijkbaar werkt het veld level dat het userlevel van iedere gebruiker bepaald (zie 4.1. Algemene structuur van de applicatie en userlevels). Standaard wordt dit veld automatisch op de waarde 3 (laagste level) geïnitialiseerd. De tabel users wordt via de unieke username verbonden met de tabel data. Tot slot bevat de tabel reports alle data gerelateerd aan de rapporten die na studie van bepaalde beelden aan die beelden gekoppeld kunnen worden. Deze tabel wordt via een uniek ID aan de tabel data gekoppeld.
13
Algemene structuur en opbouw van de webapplicatie 4.2.2 Vreemde sleutels Zoals uit de tekst hierboven blijkt zijn de tabellen vesselsAffected, users en reports via unieke waarden gekoppeld aan de tabel data. Door te werken met vreemde sleutels kan deze band ook aan de database duidelijk gemaakt worden. Dit heeft vooral voordelen naar integriteitbewaking toe. De tabellen vesselsAffected-data of users-data of reports-data worden door te werken met vreemde sleutels ondergebracht in een moeder-kind situatie. Een kind kan hierbij nooit voorkomen zonder een moeder. In een aantal concrete voorbeelden toegepast op onze database houdt dit het volgende in: een gebruiker die niet geregistreerd is in de tabel users kan nooit gegevens toevoegen aan de tabel data, in de tabel data kan nooit verwezen worden naar een record in de tabel vesselsAffected met een ID-nummer dat in deze laatste niet voorkomt… Omgekeerd geldt ook dat wanneer bv. een gebruiker uit de tabel users verwijderd wordt, automatisch alle records met die gebruiker in de tabel data verwijderd worden. Wanneer een gebruiker zijn username wijzigt wordt deze ook automatisch in alle records met die gebruiker in de tabel data gewijzigd… Om gebruik te maken van vreemde sleutels moet de “motor” van iedere tabel wel gewijzigd worden van MyISAM naar InnoDB. Daarnaast biedt InnoDB nog een aantal andere voordelen voor onze situatie vergeleken met MyISAM. Meer informatie over de verschillen tussen beide kan teruggevonden worden in de volgende paragraaf.
4.2.3 InnoDB vs. MyISAM Een tabel kan draaien op verschillende “motoren”. Standaard is die ingesteld op MyISAM. Deze dient echter gewijzigd te worden naar InnoDB om gebruik te kunnen maken van vreemde sleutels. Daarnaast zijn er nog meer verschillen tussen beide engines. Het grootste verschil tussen MyISAM en InnoDB is ongetwijfeld dat je met InnoDB relationele databases kunt maken en met MyISAM niet. InnoDB ondersteunt daarvoor o.a. transacties en vreemde sleutels. Meestal wordt MyISAM gebruikt wanneer vooral snelheid van de database belangrijk is. InnoDB wordt echter meestal verkozen wanneer data integriteit belangrijk is. Zoals reeds aangehaald werd, is dit één van de redenen waarom gekozen werd voor InnoDB. Alhoewel MyISAM sneller is dan InnoDB, kan van InnoDB daarenboven in het algemeen niet gezegd worden dat het een trage slak is. Integendeel, InnoDB blijft één van de snelste database engines, weliswaar voorafgegaan door MyISAM. InnoDB werd speciaal ontworpen voor maximale performantie bij het verwerken van grote datavolumes en behaalt een CPU-efficiëntie die niet geëvenaard wordt door andere relationele database engines. Uiteraard is ook dit is een voordeel, aangezien de database op langere termijn zeer veel gegevens kan bevatten.
14
Algemene structuur en opbouw van de webapplicatie Een laatste voordeel dat pleit voor InnoDB is het feit dat het tabellen en indexen opslaat in een zogenaamde tablespace, welke kan bestaan uit verschillende bestanden. Op die manier kunnen InnoDB tabellen van eender welke grootte zijn, zelfs op systemen waar de maximum bestandsgrootte is gelimiteerd tot 2 GB. Dit verschilt fundamenteel van bv. MyISAM tabellen waar iedere tabel als een apart bestand wordt opgeslagen en dus maar maximaal even groot kan zijn als de maximum toegelaten bestandsgrootte.12
4.3
Veiligheid
4.3.1 Gebruikerstoegang Een eerste minimale vorm van veiligheid is het beperken van de toegang tot de applicatie. In onze applicatie is het de bedoeling dat enkel (kinder)artsen die enige relevantie hebben met betrekking tot de inhoud van de databank, toegang krijgen tot de applicatie. Toegang tot de applicatie wordt gecontroleerd via een login systeem waarin iedere gebruiker zijn gebruikersnaam en wachtwoord moet opgeven. Indien een gebruiker nog geen toegang heeft tot de applicatie kan hij deze via een invulformulier bekomen. Na het invullen van het formulier wordt de administrator van de applicatie via mail op de hoogte gebracht van de nieuwe “inschrijving”. De gebruiker heeft standaard geen toegang totdat deze door de administrator verleend wordt. Bij de administrator ligt dus de verantwoordelijkheid van het screenen van iedere “inschrijving”.
4.3.2 Wachtwoordversleuteling De veiligheid bij het inloggen wordt nog eens verhoogd doordat het wachtwoord versleuteld in de database wordt opgeslagen, zodat dit zelfs na opvraging uit de database nietszeggend is. Moest een persoon met kwade bedoelingen er dus in slagen om bv. via SQL injection (zie 4.3.8. SQL injection) een aantal gebruikersnamen en wachtwoorden te weten te komen, kan deze, omwille van de versleuteling, nog steeds niets met deze gegevens aanvangen. De versleuteling van de wachtwoorden gebeurt aan de hand van het Secure Hash Algorithm (SHA), welk werd ontworpen door het Amerikaanse National Security Agency. Het algoritme berekent een soort samenvatting of digest van een reeks tekens. Dit digest is, met een zeer hoge waarschijnlijkheid uniek voor een gegeven tekenreeks of boodschap. Dit wil zeggen dat er dus slechts een zeer minimale kans bestaat dat twee verschillende boodschappen kunnen leiden tot eenzelfde digest. Of omgekeerd, dat het praktisch niet uitvoerbaar is om een boodschap te vinden die eenzelfde digest heeft als een andere boodschap. Daarnaast is het ook technisch niet uitvoerbaar om, vertrekkende van een gegeven digest, de bijhorende boodschap te vinden. 12
TAPO – DIFFERENCE BETWEEN MyISAM and InnoDB, (http://tapos.wordpress.com/2008/01/10/difference-between-innodband-myisam)
15
Algemene structuur en opbouw van de webapplicatie Deze beide kenmerken leiden ertoe dat elke willekeurige verandering van de boodschap met een zeer hoge mate van waarschijnlijkheid een andere samenvatting zal opleveren.13
4.3.3 Validatie van formulieren Om te beletten dat gebruikers foutieve, corrupte gegevens in de inputvelden zouden kunnen ingeven, wordt ieder veld, voor het verzenden van de data naar een volgende pagina of de databank, gecontroleerd op zijn inhoud. Op die manier kunnen nietszeggende gegevens gemakkelijk geweerd worden. Anderzijds laat een strikte validatie ook toe dat bepaalde gegevens geen schade kunnen toebrengen aan de database of de webapplicatie. Denk hierbij bv. maar aan het geval wanneer een gebruiker een deel van een SQL-instructie in het veld ingeeft (zie ook 4.3.8. SQL injection). Een eerste validatie bestaat erin om na te gaan of ieder veld dat ingevuld moet zijn, werkelijk ingevuld is. Een verdere validatie bestaat er dan in om na te gaan of de ingevulde gegevens ook correct kunnen zijn. Velden waar een natuurlijk getal als input verwacht wordt, kunnen bijvoorbeeld gemakkelijk gevalideerd worden via de PHP functie ctype_digit(). Wanneer validatie van alle velden in een formulier aangeeft dat de inhoud van de velden correct is, worden de gegevens naar de databank of een andere pagina gestuurd. Indien minstens één van de velden niet correct werd ingevuld, wordt het formulier niet verzonden, maar opnieuw getoond met de waarden die in eerste instantie door de gebruiker ingegeven werden. Daarnaast wordt er ook een foutmelding gegenereerd die specifiek aanduidt waar de fout zit. Het foutief ingevulde veld wordt eveneens gevisualiseerd door de veldomschrijving in een rode kleur te tonen. In de volgende paragrafen wordt in meer detail ingegaan op een aantal specifieke problemen die de kop opstaken bij de validatie van bepaalde velden. 4.3.3.1 Validatie van het wachtwoord Veiligheid van een webapplicatie met een geselecteerd aantal gebruikers begint bij het gebruik van goede, sterke wachtwoorden om in de applicatie in te loggen. Vermits veiligheid als één van de doelstellingen werd omschreven, werd er eerst goed nagedacht over wat een sterk wachtwoord inhoudt. Volgende eisen zijn het resultaat van dat denkproces:
Het wachtwoord moet minimaal 8 karakters lang zijn en maximaal 25 Het wachtwoord mag geen spaties bevatten En het moet voldoen aan minstens 3 van de volgende voorwaarden: Een hoofdletter bevatten Een kleine letter bevatten Een cijfer bevatten Een speciaal letterteken bevatten
13
WIKIPEDIA – SHA, (http://nl.wikipedia.org/wiki/SHA-familie)
16
Algemene structuur en opbouw van de webapplicatie De uitdaging lag nu in het ontwerpen van een validatiefunctie die nagaat of een wachtwoord aan al deze eisen voldoet. Vooral voor de laatste eis lag dit aanvankelijk wat ingewikkelder. Het idee voor het algoritme was om voor alle vier de subeisen afzonderlijk na te gaan of het wachtwoord aan de subeis voldoet of niet. Indien het aan een subeis voldoet, wordt een teller met één eenheid geïncrementeerd (na aanvankelijke initialisatie op de waarde 0), na controle van alle vier de subeisen zou de teller dan minstens drie moeten bedragen opdat het wachtwoord een sterk wachtwoord betreft. De implementatie van zulk algoritme zou echter inhouden dat voor iedere subeis apart het hele wachtwoord karakter per karakter doorlopen moet worden wat weinig efficiënt is vermits eenzelfde handeling viermaal herhaald zou moeten worden. Daarom werd het algoritme en zijn implementatie aangepast. Alvorens het wachtwoord gecontroleerd wordt, wordt de string die het wachtwoord bevat nu eerst in zijn individuele karakters opgesplitst via de PHP functie str_split(). Deze functie schrijft ieder karakter weg als een element van een array. Vervolgens wordt de array van de karakters doorlopen, waarbij ieder opgeslagen karakter getest wordt op iedere subeis via een constructie van IFvoorwaarden. Om onderscheid te kunnen maken tussen hoofd- en kleine letters wordt van het karakter eerst nagegaan of het al dan niet een alfabetische letter betreft via de PHP functie ctype_alpha(). Indien dit het geval is, wordt vervolgens getest of het een hoofd- of kleine letter betreft via respectievelijk de PHP functies ctype_upper() en ctype_lower(). Indien dit niet het geval is, wordt getest of het karakter een cijfer is via de PHP functie ctype_digit(). Is dit weer niet het geval wordt tenslotte getest of het een speciaal karakter is. Hiertoe bestaat geen specifieke PHP functie, maar het karakter wordt gezien als een speciaal karakter als het niet alfanumerisch is (!ctype_alnum()). Daarbij wordt wel de voorwaarde gesteld dat het geen spatie mag zijn. Telkens een karakter aan een specifieke voorwaarde voldoet wordt een voor die voorwaarde unieke teller verhoogd. Om verder in het script gemakkelijk te kunnen te bepalen of minstens drie van die tellers verschillend zijn van 0, worden de tellers in een array opgeslagen (geïnitialiseerd op [0, 0, 0, 0]). 4.3.3.2 Validatie van het e-mailadres Ook validatie van het e-mailadres was een belangrijk gegeven. Gebruikers moeten immers een geldig e-mailadres opgeven omdat alle latere correspondentie (bij bv. wijziging van persoonlijke gegevens) tussen de applicatie en de gebruiker via dat e-mailadres gebeurt. Checken of een opgegeven e-mailadres werkelijk bestaat is een nagenoeg onmogelijke opdracht, vooral als dit in real-time moet gebeuren. Toch zijn er een beperkt aantal zaken die kunnen nagegaan worden om te kunnen bepalen of de structuur van het adres op zijn minst correct is. Zo ligt het voor de hand dat het geen spaties mag bevatten, dat er een “@” in moet voorkomen, dat er na de “ @” minstens één “.” moet voorkomen, al mag deze niet vlak achter de “@” staan en mogen er na de laatste punt nog maximaal 3 lettertekens (en minimaal 2 tekens) voorkomen. Maar zelfs in dat geval zou iemand nog een volledig zinloos e-mailadres, zoals [email protected], kunnen opgeven. We wilden het graag dus nog iets 17
Algemene structuur en opbouw van de webapplicatie strikter. In de extensie (na de laatste punt) mag zo bijvoorbeeld maar een beperkt aantal lettercombinaties voorkomen (ofwel een geldige landcode (ISO-316614), ofwel een top level domeinnaam, zoals .edu, .com., .gov, … (RFC1591)). Dat zijn maar een beperkt aantal regels waar wij aan konden denken, maar we konden ons wel voorstellen dat in een wereld, zoals het internet, die op bijna niets anders dan protocols is gebaseerd, ook wel ergens het protocol voor een correct e-mailadres moet beschreven zijn. In de RFC-documenten RFC1035, RFC2821 en RFC2822 van de Internet Engineering Task Force (IETF15) is een beschrijving te vinden van alle voorwaarden waaraan een e-mailadres moet voldoen. Frappant genoeg blijkt daaruit dat een e-mailadres zoals customer/[email protected] ook geldig is (zie ook RFC3696). Hieronder volgt een oplijsting van de voorwaarden waaraan een e-mailadres moet voldoen: 1. Een e-mailadres bestaat uit een lokaal deel en een domeindeel, van elkaar geschieden door een “@”-teken (RFC2822, 3.4.1.) 2. Het lokale deel kan alfabetische-, numerische en een aantal speciale tekens (!, #, $, %, &, ', *, +, -, /, =, ?, ^, _, `, {, |, } en ~) bevatten. Tussen al deze tekens kan eventueel een punt (“.”) voorkomen, maar dit mag niet aan het begin of het einde van het lokale deel voorkomen. Tevens mogen geen twee punten (“.”) elkaar opvolgen (RFC2822, 3.2.4.) 3. Het lokale deel mag bestaan uit een string (inclusief spaties en “@”) tussen aanhalingstekens (“) (RFC2882, 3.2.5.) 4. Quoted pairs (zoals bijvoorbeeld “\@”) zijn geldige componenten van het lokale deel (RFC2822, 4.4). 5. De maximale lengte van het lokale deel is 64 karakters (RFC 2821, 4.5.3.1.) 6. Een domein bestaat uit labels van elkaar gescheiden door “.” (RFC 1035, 2.3.1.) 7. Domeinlabels beginnen met een alfabetisch karakter, eventueel gevolgd door 1 of meer alfabetische of numerische karakters of een “-“.Het label wordt afgesloten door een alfabetisch of numerisch karakter (RFC1035, 2.3.1.) 8. De maximum lengte van een domeinlabel is 63 karakters (RFC1035, 2.3.1.) 9. De maximumlengte van het gehele domein is 255 karakters (RFC 2821, 4.5.3.1.) 10. Het domein moet volledig gekwalificeerd en oplosbaar (resolvable) zijn door een type A of type MX DNS adresrecord (RFC2821, 3.6.) De uitdaging lag erin om deze tien regels ook in een validatiefunctie te gieten. De oplossing werd gevonden in de vorm van een aangepaste versie van de code zoals deze beschikbaar is op Linux Journal16. De regels 2-5 omvatten regels met betrekking tot het lokale deel, de regels 6-10 met betrekking tot het domeindeel. Een eerste logische stap lag er dus in om het opgegeven e-mailadres in die twee delen op te splitsen. Uit regels 3 en 4 volgt echter dat een e-mailadres meer dan één “@”-teken kan bevatten zodat voor de opsplitsing geen gebruik gemaakt kon worden van de PHP functie explode(), immers dat zou bijvoorbeeld 14
INTERNATIONAL ORGANIZATION FOR STANDARDIZATION – ISO-3166: COUNTRY CODES (http://www.iso.org/iso/country_codes.htm), 18 mei 2009 15 IETF REQUEST FOR COMMENTS PAGES, (http://www.ietf.org/rfc.html), 13 mei 2009 16
LINUX JOURNAL, (http://www.linuxjournal.com/article/9585), 14 mei 2009
18
Algemene structuur en opbouw van de webapplicatie voor een e-mailadres met twee “@” resulteren in een array met drie elementen. Een oplossing lag er in om gebruik te maken van de functie strrpos() daar deze enkel op zoek gaat naar de laatste plaats waar een bepaald karakter voorkomt in een string. Komt er nergens een “@” in het e-mailadres voor, dan geeft de functie strrpos() de boolean false als resultaat en is het e-mailadres ongeldig. $atIndex = strrpos($email, "@"); if (is_bool($atIndex) && !$atIndex) { return false; }
Eénmaal beide delen van elkaar gescheiden, kan gemakkelijk de lengte ervan bepaald worden (regels 5 en 9). Als één van deze testen bovendien faalt, is er geen reden om het systeem nog verder te belasten met andere, meer ingewikkelde testen. Ook het testen voor een punt (“.”) aan het begin of einde van het lokale deel is redelijk straight-forward. Evenals het testen op het voorkomen van twee opeenvolgende punten (“.”) via het volgende stukje code: else if (preg_match('/\\.\\./', $local)){ return false; }
De rest van regel 2 kan zich ook vertalen in een reguliere expressie. Enkel moet hierbij in het hoofd gehouden worden dat wel alle mogelijke tekens mogen voorkomen indien het lokale deel een gequote string is of indien het karakter ge-escaped werd (voorafgegaan wordt door “\”) (regels 3 en 4). Beide kunnen gecombineerd worden door eerst te testen voor een geldige opeenvolging van toegelaten tekens volgens regel 2. Indien deze test faalt, d.w.z. er zijn niet toegelaten tekens in het lokale deel, moet nagegaan worden of die tekens al dan niet ge-escaped zijn of voorkomen in een gequote string. else if (!preg_match('/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.])+$/',str_replace("\\\\","",$local))) { if (!preg_match('/^"(\\\\"|[^"])+"$/',str_replace("\\\\","",$local))) { return false; } }
Daarmee is het volledige lokale deel gevalideerd en rest dus nog het domeindeel (op de lengte na). Regel 7 kan weer gecontroleerd worden aan de hand van een reguliere expressie en de PHP functie preg_match(). Terwijl regel 8 gevalideerd kan worden door het domeindeel via de explode() functie bij iedere punt te splitsen en voor ieder deel dat zo bekomen wordt, na te gaan of het niet langer is dan 63 karakters. Regel 10 leek aanvankelijk onmogelijk om te implementeren, maar na het doorzoeken van de vele functies op php.net, bleek er een functie te bestaan die deed wat hij moest doen, nl. checkdnsrr(). Deze functie doet niets anders dan in DNS te zoeken naar records van een bepaald type, welke een bepaalde host bevatten.
19
Algemene structuur en opbouw van de webapplicatie Het laatste stukje code voor de validatie van een e-mailadres wordt zo: else if (!(checkdnsrr($domain,"MX") || checkdnsrr($domain, "A"))) { return false; }
4.3.4 Error handling Alle errors en warnings die door ofwel PHP ofwel mySQL gegenereerd worden, worden via een functie opgevangen en omgevormd. Op die manier wordt voorkomen dat de originele foutmeldingen op het scherm getoond worden. Deze gegenereerde foutmeldingen bevatten dikwijls informatie over de onderliggende server- of databasestructuur, welke uiteraard personen met kwade bedoelingen graag te weten zouden komen. PHP errors worden opgevangen door het instellen van een custom error handler, terwijl SQL errors opgevangen worden door in de PHP die() functie de error handler functie op te roepen. In beide gevallen zal de error of warning ook in een logbestand weggeschreven worden. Deze log kan voor verdere ontwikkelingsdoeleinden van pas komen. De handlers worden in iedere pagina ingevoegd via de require_once() functie.
4.3.5 Afschermen van de directory ./data Alle succesvolle uploads worden opgeslagen als .zip-bestanden in de directory ./data op de webserver. Indien rechtstreeks naar deze directory gesurft zou worden door het intypen van het pad in de browser, wordt de inhoud van de hele directory opgelijst aangezien deze geen index heeft. Daarmee gepaard gaande kunnen de bestanden ook één voor één van de server afgehaald worden door rechtstreekse adressering (uiteraard enkel indien de bestandsnaam gekend is). Er werd echter voor geopteerd om dit laatste toe te laten zodat de artsen eventueel gemakkelijk een link naar het bestand naar hun collega’s kunnen doorsturen. Om rechtstreekse adressering van de bestanden door gokken tegen te gaan bestaan de bestandsnamen van de bestanden uit het ID-nummer gevolgd door 10 random gegenereerde tekens (hoofd- en kleine letters en cijfers). Afscherming van de bestanden in de directory ./data werd bekomen door een index.php te plaatsen in de map die via <META http-equiv='Refresh' content='0; URL=../home.php'> de gebruiker naar de homepage leidt.
20
Algemene structuur en opbouw van de webapplicatie 4.3.6 Secure Socket Layer protocol Voor het garanderen van een veilige gegevensoverdracht werd gebruik gemaakt van het SSL (Secure Socket Layers) protocol. Dit is een protocol dat op de server geïnstalleerd wordt en zorgt voor beveiliging en encryptie van alle verzonden gegevens van zodra https:// voor de URL getypt wordt. Wanneer echter voor de eerste keer naar een beveiligde pagina op de server gesurft wordt, krijgt de bezoeker een beveiligingswaarschuwing van zijn browser dat het beveiligingscertificaat niet vertrouwd kan worden omdat het zelfondertekend is. De gebruiker dient dan eenmalig via zijn browserinstellingen aan te geven dat hij het certificaat vertrouwd door er een uitzondering voor toe te voegen, waarna het certificaat voor altijd aanvaard zal worden en alle gegevens secuur verzonden kunnen worden. Dit voorkomt dat indien de gegevens ergens onderschept worden op de internetsnelweg, ze ook bruikbaar en begrijpbaar zijn voor de persoon die ze onderschepte (zie ook 3.9. Secure Sockets Layer (SSL)).
4.3.7 URL-wijziging Een ander mogelijk veiligheidslek zou kunnen ontstaan wanneer iemand gokt naar de naam van de pagina die laadt nadat correct werd ingelogd. Indien geen beveiliging aanwezig zou zijn, zou een juiste gok betekenen dat een persoon het inloggen kan omzeilen. Dit kan echter op een simpele manier voorkomen worden door te werken met een sessievariabele die de gebruikersnaam bijhoudt nadat een persoon correct is ingelogd. Op iedere pagina die geladen wordt kan dan nagegaan worden of deze sessievariabele al dan niet leeg is. Is deze leeg zal de pagina niet laden en de gebruiker doorverwezen worden naar de loginpagina. URL-wijziging kan ook op een ander niveau plaatsgrijpen, door bijvoorbeeld variabelen die via de GET-methode met de URL meegegeven worden, te wijzigen in de hoop zo andere gegevens op het scherm te krijgen dan de welke de gebruiker enkel toegestaan zijn om te zien. In principe komt deze veiligheidslek niet voor wanneer geen gebruik van de GETmethode gebruikt wordt. In de ontwikkelde applicatie is dat echter niet volledig het geval en wordt de inhoud van myaccount.php gegenereerd op basis van parameters in de URL. Iedere parameter die in de URL echter meegegeven wordt kan in dit geval slechts een aantal discrete waarden aannemen. Door op deze waarden te controleren alvorens de inhoud geladen wordt of een andere instructie uitgevoerd wordt, wordt dit soort veiligheidslek al voor een groot deel gedicht. Door die waardecontrole dan ook nog eens te koppelen aan de controle van de userlevel of gebruikersnaam van een gebruiker, kan helemaal voorkomen worden dat de gebruiker informatie te zien krijgt die niet voor zijn ogen bedoeld is. Het nauwgezet controleren van de parameters die via de URL meegegeven worden, vormt ook een vangnet tegen SQL-injection via de URL (zie 4.3.8. SQL injection). Tegelijkertijd laat het werken met deze parameters in de URL een verhoogd gebruiksgemak toe voor meer ervaren gebruikers. Wanneer een gebruiker bv. zoveel beelden heeft opgeladen dat zijn inbox van verzonden beelden zeer veel verschillende pagina’s bevat zodat het zoeken naar een bepaald beeld lang kan duren, kan hij via het doorgeven van een 21
Algemene structuur en opbouw van de webapplicatie geldig ID-nummer in de URL rechtstreeks naar de specifieke informatie surfen die voor hem van belang is.
4.3.8 SQL injection Een laatste, vaak onderschat, veiligheidsrisico houdt SQL-injection in. Een kwaadwillend persoon kan SQL-injections uitvoeren door bepaalde GET- of POST-variabelen te veranderen zodat een databasequery helemaal aangepast wordt. Op die manier wordt er misschien vertrouwelijke informatie op het scherm getoond of wordt de structuur van de database aanpast. Dit kan erge gevolgen hebben voor de webapplicatie en de onderliggende database. SQL-injectie via POST-variabelen kan voornamelijk tegengegaan worden door een sterke validatie van de gegevens die in het invoerveld van het formulier ingevuld werden (zie 4.3.3. Validatie van formulieren). Om SQL-injectie via de GET-variabelen tegen te gaan bestaan er naast het eerder aangehaalde strikt controleren van de parameters die meegegeven worden, ook een aantal handige PHP functies, zoals bv. mysql_real_escape_string(). Deze functie vangt SQL specifieke karakters op door er een backslash (“\”) voor te plaatsen. Hierdoor weet het systeem dat enkel het letterteken bedoeld wordt, en niet meer de scheidende functie van het afbakenen van gegevens. Dit “escapen” kan trouwens ook gebruikt worden als bijkomend afweermiddel tegen SQLinjectie via de POST-variabelen.
22
De applicatie in meer detail
5. De applicatie in meer detail. 5.1
Login / aanvraag login / vergeten wachtwoord
5.1.1 Login-pagina De login-pagina is de eerste pagina die de gebruiker ziet wanneer hij naar de website surft (zie Figuur 3). Deze werd bewust sober gehouden en biedt de gebruiker enkel de mogelijkheid om in te loggen. Er is eveneens de mogelijkheid om een login aan te vragen en om een nieuw wachtwoord te bekomen indien men het vergeten is. Deze drie pagina’s zijn ook de enige die vrij beschikbaar zijn voor iedereen zonder login. Deze startpagina kan zo gelaten worden, maar kan eventueel uitgebreid worden tot de webstek van de neonatale afdeling van Rotterdam. Eventueel kan deze pagina achterwege gelaten worden en kunnen de login velden geïntegreerd worden op een andere website.
Figuur 3: Startpagina Zoals reeds besproken is de validatie van de inputvelden van groot belang (zie 4.3.3. Validatie van formulieren). Gezien iedere buitenstaander (met of zonder kwade bedoelingen) toegang heeft tot deze pagina’s werd er ruim aandacht besteed aan deze beveiliging. Iedere gebruiker moet zich identificeren met zijn login en wachtwoord. Na invoer van deze gegevens wordt hij automatisch doorgestuurd naar de homepage. Als men probeert in te loggen wanneer de account nog niet geactiveerd is, krijgt men de melding ‘your account has not yet been activated.’ In alle andere gevallen is het de melding ‘Combination username password is incorrect.’ Zo kan men geen willekeurige gebruikersnamen proberen om te testen of ze aanwezig zijn in de databank. De login gegevens worden doorgegeven via de POST-methode en dit om de gegevens achter de schermen te houden voor privacy. Enkel $_POST[‘password’] bevat het wachtwoord in cleartext. Voor de verificatie van het wachtwoord wordt 23
De applicatie in meer detail sha1($_POST[‘password’]) vergeleken met de hash die zich in de databank bevindt. Na
de controle van de input worden de gebruikergegevens opgeslagen in sessievariabelen. Doorheen de volledige duur van het bezoek worden gegevens zoals gebruikersnaam en userlevel regelmatig aangesproken.
5.1.2 Request login Wanneer een gebruiker toegang wenst tot de website kan hij gebruik maken van een webformulier dat hij via een link op de startpagina vindt. We hebben ervoor geopteerd om de gebruiker zijn login en wachtwoord zelf te laten kiezen. Dit ontlast de databankbeheerder van deze taak. Uiteraard is het mogelijk, indien de beheerder van de site het wenst, om de code aan te passen zodat de gebruiker zijn login niet mag kiezen. Dit kan gewenst zijn om een uniforme logica te houden bij het beheer van de gebruikersnamen. De databankbeheerder kan dan zelf een gebruikersnaam toekennen wanneer hij de aanvraag behandelt. Het formulier wordt gecontroleerd op elke input. Wanneer een veld niet ingevuld is of niet voldoet aan de minimumeisen worden de namen van de desbetreffende velden in het rood gemarkeerd. De gebruiker krijgt eveneens een uitleg over de reden van de foutmelding. We hebben ervoor gezorgd dat de data die correct was de eerste maal in het formulier aanwezig blijft om de gebruiker de herhaling van alle input te besparen. De twee belangrijkste velden voor een correcte validatie zijn het wachtwoord en het emailadres. Omdat de beste beveiliging maar zo sterk is als het (soms zwakke) wachtwoord hebben we een password policy ingevoerd. De gebruiker kan via een link de policy raadplegen om tot een sterk wachtwoord te komen. Deze policy bevat de regels die in acht genomen moeten worden om een sterk wachtwoord te vormen (zie ook 4.3.3.1. Validatie van het wachtwoord). Daarnaast worden ook nog enkele tips gegeven hoe men te werk kan gaan. Het e-mailadres wordt gevalideerd volgens de regels van de opbouw van een adres. Er wordt o.a. gecontroleerd of er een @ aanwezig is, of er een extensie is, … (zie 4.3.3.2. Validatie van het e-mailadres voor meer informatie). Beide velden worden tweemaal gevraagd om de kans op fouten te vermijden. Eenmaal alle velden gevalideerd zijn, worden de gegevens doorgestuurd naar de databank. De gebruiker voegt zich dus zelf toe in het systeem. Naast de eigen ingevulde velden wordt een laatste veld allowAccess standaard ingevuld op 0. Zolang allowAccess status 0 heeft zorgt een controle op de startpagina ervoor dat de gebruiker niet kan inloggen. Na het uploaden van deze gegevens ontvangt de gebruiker een email ter bevestiging van zijn aanvraag met de boodschap om de goedkeuring af te wachten. Tegelijkertijd ontvangt een administrator met userlevel 1 een email om de aanvraag te melden. Aan wie deze email gestuurd wordt kan in de config-file aangepast worden. Zoals verder uitgelegd zal worden kan een gebruiker met userlevel 1 een nieuwe aanvraag goedkeuren of weigeren.
24
De applicatie in meer detail 5.1.3 Forgot password Uiteraard moet er een mogelijkheid zijn om een nieuw wachtwoord aan te vragen. Aangezien we enkel een hash van het wachtwoord opslaan in de databank kunnen we de gebruiker zijn wachtwoord niet per email doorsturen en moest er in een andere mogelijkheid voorzien worden. We hadden er eerst aan gedacht om, bij de aanvraag van een login, de gebruiker uit een aantal vragen te laten kiezen waarop hij een antwoord moet geven. Bvb. wat is de naam van uw favoriete huisdier, wat is uw favoriete kleur. Dit is een methode die vaak gebruikt wordt bij emailaccounts. We hebben er echter voor gekozen om de gebruiker zo min mogelijk te belasten en deze methode niet te gebruiken. In de plaats daarvan wordt er een nieuw willekeurig wachtwoord gegenereerd op basis van de volgende functie, zoals gevonden op php.net 17: function rand_str($length = 10, $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890') { // Length of character list $chars_length = (strlen($chars) - 1); // Start our string $string = $chars{rand(0, $chars_length)}; // Generate random string for ($i = 1; $i < $length; $i = strlen($string)) { // Grab a random character from our list $r = $chars{rand(0, $chars_length)}; other }
}
// Make sure the same two characters don't appear next to each if ($r != $string{$i - 1}) $string .=
$r;
// Return the string return $string;
Het nieuwe wachtwoord bestaat uit een combinatie van 10 willekeurige tekens. Elk teken mag meerdere malen optreden in de string, maar niet na elkaar. De gebruiker wordt gevraagd om zijn login en e-mailadres in te voeren. Wanneer deze combinatie correct is wordt een email met het nieuwe wachtwoord verstuurd naar het e-mailadres van de gebruiker. In de email wordt er eveneens gevraagd om zo vlug mogelijk in te loggen en het wachtwoord aan te passen. Er kan altijd sprake zijn van kwaad opzet indien iemand de combinatie gebruikersnaam emailadres van een andere gebruiker kent. Er wordt bijgevolg een nieuw wachtwoord gegenereerd maar we gaan ervan uit dat enkel de rechtmatige gebruiker toegang heeft tot zijn emailaccount. Indien dit niet het geval is ligt het beveiligingsprobleem bij de gebruiker. 17
PHP.NET, (http://be.php.net/rand), 24 juni 2009
25
De applicatie in meer detail 5.1.4 Home Wanneer de gebruiker ingelogd is komt hij op de homepage waar hij welkom wordt geheten. Op basis van de userlevel die opgeslagen is in een sessievariabele na het inloggen krijgt de gebruiker een aangepast menu te zien. Omdat het menu op elke pagina terugkomt, werd de code opgeslagen in het bestand header_logged.php en wordt er op elke pagina gewerkt met een require_once(). Het bestand header_not_logged.php heeft dezelfde lay-out als voorgaande, maar zonder het menu en wordt getoond op de publieke pagina’s die voor iedereen toegankelijk zijn. Oorspronkelijk hadden we ervoor gekozen om niet met JavaScript te werken en het menu gewoon statisch te laten. JavaScript werkt namelijk client-sided wat mogelijks foutmeldingen kan opleveren wanneer het bij de gebruiker niet geactiveerd is. We hebben dit veranderd wanneer duidelijk werd dat de Multi-uploader enkel kan werken met JavaScript. Bijgevolg is de gebruiker toch verplicht om JavaScript geactiveerd te hebben en komt een eventuele waarschuwing al bij het inloggen van de site en niet enkel wanneer men wil uploaden. Na een tekst die de gebruiker verwelkomt en in enkele zinnen het doel van de applicatie uitlegt hebben we een legal disclaimer geplaatst. We hebben namelijk te maken met gevoelige persoonlijke informatie die niet zomaar uit handen mag gegeven worden. Wikipedia meldt het volgende over privacy18: […] Privacy is een ruim begrip: het gaat onder meer om de bescherming van persoonsgegevens, de bescherming van het eigen lichaam en dergelijke. Privacy is onderdeel van veel ethische kwesties. Ethiek bindt formeel niet, wetgeving wel. Rechtsnormen zijn echter mede gebaseerd op ethische overwegingen van de wetgever. Het recht op privacy werd in België vastgelegd in artikel 22 van de Belgische Grondwet. Daarnaast wordt de materie hieromtrent meer specifiek geregeld door de wet van 8 december 1992 voor de bescherming van de persoonlijke levenssfeer ten opzichte van de verwerking van persoonsgegevens, het Koninklijk besluit van 13 februari 2001 ter uitvoering van diezelfde wet van 8 december 1992 en het Koninklijk besluit van 17 december 2003 tot vaststelling van de nadere regels met betrekking tot de samenstelling en de werking van bepaalde sectorale comités opgericht binnen de Commissie voor de bescherming van de persoonlijke levenssfeer. Een onafhankelijk overheidsorgaan, de Commissie voor de bescherming van de persoonlijke levenssfeer (CBPL), is verantwoordelijk voor de controle ervan. In Nederland wordt het recht op privacy vastgelegd in de artikelen 10 tot 13 van de Nederlandse Grondwet. Ook hier wordt dat recht in een aantal andere teksten verder uitgewerkt. De verwerking van persoonsgegevens wordt bijvoorbeeld sinds 1 september 2001 voor een groot deel nader bepaald in de Wet bescherming
18
WIKIPEDIA – PRIVACY, (http://nl.wikipedia.org/wiki/Privacy)
26
De applicatie in meer detail persoonsgegevens (Wbp). Hiernaast regelen o.a. de Wet Politieregisters en de Wet gemeentelijke basisadministratie de bescherming van persoonsgegevens. De Nederlandse organisatie die zich dient bezig te houden met de privacy van zijn inwonende burgers is het College Bescherming Persoonsgegevens (CBP).[…] 5.1.5 Upload Het core-gedeelte van de applicatie is uiteraard de upload module. De gebruiker kan er per geval een dossier uploaden met alle betrokken informatie. We hebben ervoor gekozen om het uploaden onder te verdelen in 4 stappen:
invullen van patiëntgegevens, versturen van data, controleren van verstuurde data, anonimiseren / comprimeren
Deze onderverdeling geeft de gebruiker een duidelijk beeld van de evolutie van zijn upload en helpt fouten voorkomen. Gezien de grootte van sommige bestanden is het opportuun om de correctheid van de gegevens zo vroeg mogelijk op te volgen. 5.1.5.1 Stap 1: Invullen van patiëntgegevens Een dossier bestaat uit patiëntgegevens en beelden. Om de privacy van de gegevens te behouden wordt er gevraagd om de beelden anoniem te versturen met enkel medisch relevante informatie (zgn. classifiers). De vereiste informatie werd vooraf bepaald vanuit Rotterdam en wordt als een invulformulier aangeboden (zie Figuur 4).
Figuur 4: Upload stap 1/4: Fill in patient data
27
De applicatie in meer detail De basis van het invulformulier is een eenvoudige tabel geschreven in HTML. De meeste elementen zijn voorzien in menu’s waar de gebruiker uit kan kiezen. Gezien de hoeveelheid dropdown boxen hebben we een functie gemaakt om die te automatiseren. Dit heeft eveneens het voordeel dat nieuwe elementen gemakkelijk toegevoegd kunnen worden. De keuzes worden in een array geplaatst en de functie genereert automatisch de HTML-code. De functie voor de checkboxen is gelijkaardig. FUNCTION arrayOption($array,$post){
}
echo ""; foreach($array as $item) { $t = "\n"; }
De gegevens worden opgeslagen in de databank samen met de locatie van de beelden. Elke gebruiker kan zijn verstuurde beelden (zie 5.1.6. My account) opvragen samen met de ingevulde informatie. Zoals reeds hoger vermeld worden alle velden gecontroleerd of ze ingevuld zijn, want alle velden (behalve opmerkingen) zijn immers verplicht. De gebruiker wordt op de hoogte gebracht welke velden aandacht verdienen. Eén van de classifiers die met ieder beeld mee moet opgeladen wordt, is de classifier “vessels affected”, welke de getroffen vaten in de hersenen omschrijft. Een volledige oplijsting in een tabel van alle mogelijkheden en combinaties die hierbij mogelijk zijn (zie bijlage 9.2. Overzicht classifiers) zou het in te vullen formulier op de webapplicatie zodanig uitbreiden, dat overzichtelijkheid en gebruiksgemak eronder zouden lijden. Om die redenen werd gezocht naar een andere oplossing, welke werd gevonden in de gedaante van multiple select-lijsten. Dit zijn gewone HTML select-lijsten <SELECT> waaraan het attribuut “multiple” werd meegegeven, zodat meerdere mogelijkheden tegelijkertijd in de lijst aangeduid kunnen worden. Om dit soort lijsten goed te kunnen gebruiken, dient men echter specifieke toetscombinaties met CTRL en SHIFT te kennen (zoals CTRL+A). Deze toetscombinaties zijn echter lang niet bij iedereen gekend en dus werd er, om het gebruiksgemak, te vergroten verder gezocht naar een uitbreiding in functionaliteit van deze lijsten. Die functionaliteituitbreiding werd bekomen door te werken met twee aan elkaar gekoppelde multiple select-lijsten. Door een knop “add” kan men vanuit de ene lijst gemakkelijk items verplaatsen naar de andere lijst. Deze laatste lijst bevat dan op het einde van de handeling alle getroffen vaten die van toepassing zijn op het beeld waarmee ze verstuurd worden (zie Figuur 5).
28
De applicatie in meer detail
Figuur 5: Upload stap 1/4: Vessels affected
29
De applicatie in meer detail Om deze functionaliteit te implementeren werd gezocht naar hints voor een passend PHPscript, maar deze werden niet gevonden. Met JavaScript bleek dit echter wel te kunnen, omdat hiermee heel vlot naar bepaalde objecten in het document kan verwezen worden of deze zelfs aangestuurd kunnen worden. Er werden uiteindelijk twee functies geschreven. De functie SelectMoveRows() staat in voor het verwisselen van een item tussen twee lijsten, steeds van lijst SS1 naar lijst SS2. Deze functie werd gebruikt zoals ze gevonden werd op johnwbartlett.com19. function SelectMoveRows(SS1,SS2) { var SelID=''; var SelText=''; for (i=SS1.options.length - 1; i>=0; i--) { if (SS1.options[i].selected == true) { SelID=SS1.options[i].value; SelText=SS1.options[i].text; var newRow = new Option(SelText,SelID); SS2.options[SS2.length]=newRow; SS1.options[i]=null; } } SelectSort(SS2); }
De for-lus zorgt ervoor dat ieder item in de lijst SS1 afgelopen wordt. Voor dat item wordt vervolgens gekeken of het geselecteerd is of niet. Indien dat het geval is, worden zowel de waarde, als de tekst die aan het geselecteerde item gekoppeld zijn opgeslagen in een variabele. Vervolgens wordt met deze variabelen een nieuw Option object aangemaakt (standaard object in JavaScript), waarna dit nieuwe object achteraan de lijst SS2 wordt weggeschreven. Het verplaatste item wordt uiteraard in de lijst SS1 gewist. De functie SelectSort() die ook vanuit de functie SelectMoveRows() opgeroepen wordt, zorgt ervoor dat, wanneer een item van de ene lijst naar de andere verplaatst wordt, dit item steeds terug op zijn originele plaats die het eerder in de lijst innam, komt. Deze functie is eveneens van johnwbartlett.com. function SelectSort(SelList) { var ID=''; var Text=''; for (x=0; x < SelList.length - 1; x++) { for (y=x + 1; y < SelList.length; y++) { if (SelList[x].text > SelList[y].text) { ID=SelList[x].value; Text=SelList[x].text; SelList[x].value=SelList[y].value; SelList[x].text=SelList[y].text; SelList[y].value=ID; SelList[y].text=Text; } } }
19
JOHN W BARTLETT, (http://www.johnwbartlett.com/CF_tipsNtricks/index.cfm?TopicID=86), 16 april 2009
30
De applicatie in meer detail }
De twee lussen doorlopen beide de volledige lijst, waarbij de binnenste lus steeds één item op de buitenste lus voor ligt. M.a.w. wanneer de buitenste lus een bepaald item beschouwt, zal de binnenste lus steeds het daaropvolgende item beschouwen. Wanneer het item dat de binnenste lus beschouwt voor het andere item dat de buitenste lus beschouwt, moet komen, zullen beide items omgewisseld worden. Items kunnen aan de hand van deze twee functies gemakkelijk tussen twee lijsten uitgewisseld worden door de lijsten te verbinden met een button, die als je erop klikt, via het onClick() event, de functie SelectMoveRows() aanroept. Een probleem ontstond wanneer bij het verzenden van het formulier enkel het item dat aangeduid was in de tweede lijst, doorgegeven werd. Dit probleem werd omzeild door een derde functie selectallList() te implementeren die niets anders tot doel heeft dan voor het verzenden alle items in de lijsten te selecteren. Hiertoe werden de namen van de lijsten waarvoor dit moest gebeuren, opgenomen in een array, welke vervolgens doorlopen wordt. Hierbij wordt van iedere lijst de lengte bepaald, zodat de lijst vervolgens item per item doorlopen kan worden en ieder item geselecteerd kan worden. function selectallList() { var myArteries = new Array(); myArteries[0] = "arteriesCorticalSelected[]"; myArteries[1] = "arteriesPerforatorSelected[]"; myArteries[2] = "arteriesBrainstemSelected[]"; myArteries[3] = "arteriesSpinalCordSelected[]"; myArteries[4] = "veinsSinusSelected[]"; myArteries[5] = "veinsDeepVeinSelected[]"; myArteries[6] = "veinsOtherSelected[]"; for (i=0;i<myArteries.length;i++) { var aselect = document.uploadForm[myArteries[i]]; var aselectLen = aselect.length;
}
}
for(j=0; j
Hiermee bleek het probleem echter nog niet opgelost. Nu werd slechts steeds het laatste item uit de volledig geselecteerde lijst doorgegeven. Na enig zoekwerk bleek dat PHP de geselecteerde items behandelde als string en niet als array waarbij ieder element van de array overeenkomt met een geselecteerd item. Dit kon snel verholpen worden door de namen van de lijsten die doorgegeven moesten worden, aan te vullen met “[]”. Bijvoorbeeld, een lijst met de naam ListName kreeg de nu de naam ListName[].