Seminar1, HTML & CSS



Relevanta dokument
Prova på-laboration i PHP Johan Sjöholm johsj@ida.liu.se Institutionen för datavetenskap, Linköpings universitet

Översikt. Installation av EasyPHP 1. Ladda ner från Jag använder Release Installera EasyPHP.

Alla filer som bearbetar PHP script ska avslutas med ändelsen.php, exempelvis ska en indexsida till en hemsida heta index.php

Del 1 och 2 HTML/CSS. Webbutveckling Laboration 1 Nicklas Bostedt

WP-Edit. Robin Larsson Martin Davik. Examensarbete, grundnivå, 15 hp Datavetenskap Internetteknologprogrammet

Hemsideutveckling för Anjool AB

Avancerade Webbteknologier

Programmeringteknik. Planering MÅL LABB: MOMENT LAB4 HTML - EXEMPEL HTML. Webbdelen

Asp.net mvc intro PER KVARNBRINK,

Labora&on 8 Formulär övningar/uppgi6er

Manual för Typo3 version 4.2

Game of 40. Regler och om sidan är in princip samma sak. Det som skiljer dem åt är att de inte har samma text.

Det här dokumentet är till för att ge en översikt över ASP.NET MVC samt hur WCF Services används från.net applikationer.

ASP.NET MVC. Copyright Mahmud Al Hakim Innehåll

Projekt Rapport. RaidPlanner. Jeanette Karlsson UD10

JAVASCRIPT. Beteende

Xhtml och CSS.Tillämpad fysik och elektronik Per Kvarnbrink (redigering Ulf Holmgren 2011)

Nya webbservern Dvwebb.mah.se

Manual för din hemsida

Laboration med Internet och HTML

Kom igång. Readyonet Lathund för enkelt admin. Logga in Skriv in adressen till din webbsida följt av /login. Exempel:

Modul 8 Hantering av indata

Sidan kommer inte läggas upp någonstans utan du redovisar den för mig på något handledningstillfälle.

Lathund Hemsida för Astma- och Allergiförbundets föreningar

JavaScript in SharePoint and not just for Apps. Wictor Wilén

CMS. - Content management system

Skapa din egen MediaWiki

Slutrapport YUNSIT.se Portfolio/blogg

Laboration 2: Xhtml och CSS.

Programbeskrivning. Chaos på Web. Version

MANUAL FÖR JÄGAREFÖRBUNDETS KRETSAR

Webbprogrammering. Sahand Sadjadee

Lektion 5 HTML, CSS, PHP och MySQL

Klient/server. Översikt. Lektion 1: Webbtekniker från Microsoft. Webbteknik från Microsoft. Klient/server. Designmönster. Utrullning.

Priskamp. En prisjämförelsesite Björn Larsson

FrontPage Express. Ämne: Datorkunskap (Internet) Handledare: Thomas Granhäll

Logga in på din hemsideadministration genom dina inloggningsuppgifter du fått.

MANUAL FÖR JÄGAREFÖRBUNDETS KRETSAR

Joomla CMS Del 2 av 2

Webbutveckling Laboration 1: HTML5 och CSS3.

Laboration 3 i kursen Produktion för tryckta medier och webb: Webbplatsproduktion med ett publiceringssystem

Innehåll Introduktion... 3 InteractiveScene.config... 3 Scener <scenes>... 3 Typsnitt <fonts>... 3 Övergångar <transitions>...

TUTORIAL: KLASSER & OBJEKT

Webbprogrammering - 725G54 PHP. Foreläsning II


IT-system. BUP Användarmanual

Essential Php Security Författare: Shiflett, Chris Antal sidor: 124 Förlag: O'Reilly

Labbrapport: HTML och CSS

MANUAL FÖR JÄGAREFÖRBUNDETS KRETSAR

Instruktioner. Innehåll: 1. Vad är Kimsoft Control (SIDA 2) 3. Hem (SIDA 2)

E-posthantering med Novell Groupwise WebAccess

Lab 5: ASP.NET 2.0 Site Navigation

L04.1 Marodören. Inledning. Mål. Genomförande. Uppgift 1 Hello World. Moment I

Labb LABB 1. Databassagan och en rundtur i databasers märkliga värld. Plushögskolan Frågeutveckling inom MSSQL - SU14

Manual för Typo3 version 4.04

Manual för visionutv.net Redigera

INSTALLATION...3 ATT KOMMA IGÅNG...3 PROGRAMMETS DESIGN...4 LÄGGA TILL TABELL...4 EDITERA TABELL...4 EDITERA RELATION...5 SPARA OCH AVSLUTA...

Appar med ryggrad. Introduktion till JavaScriptramverket Backbone

E12 "Evil is going on"

I den tidigare filen Manual Editor belystes grunderna för enkel uppdatering samt editorns utformning.

XtraMatLagning. August Ek och Oscar Johnson. TNM065 Dokumentstrukturer

1. Uppdateringsmodul (CMS)

Content Management System. Publiceringssystem

Elektronisk publicering TNMK30

Version 1.8.7A. Tidrapportering med ctimesheet

Övning (X)HTML 2. Sidan 1 av

Användarmanual TextAppen Online

PREMIUM COMAI WEBBKALENDER

Webbprogrammering TDDD52

Laboration 3 HI1024, Programmering, grundkurs, 8.0 hp

Labora&on 1 Introduk&on &ll utvecklingsmiljön övningar/uppgi<er

batklubben.eu s hemsida

TDP013. Webbprogrammering och interaktivitet. AJAX, CORS & jquery. Marcus Bendtsen Institutionen för Datavetenskap (IDA)

JavaScript. Innehåll. Historia. Document object model DHTML. Varför Javascript?

MVC med Javascript och Ajax. Filip Ekberg

Kursplanering Utveckling av webbapplikationer

Webbteknik för ingenjörer

<script src= "

Lektion 2 - CSS. CSS - Fortsätt så här

Nya Mina vårdkontakter. En presentation över det nya gränssnittet för invånare

Moodle2 STUDENTMANUAL

Räkna med ASP.NET MVC 3

SLUTRAPPORT. Sebastianlund.com. Individuellt mjukvaruutveckingsprojekt, 1DV430. Författare: Sebastian Lund WP11 Datum:

En grundkurs i hemsidor och hur de är uppbyggda

Grafiska användargränssnitt i Java

Användarmanual - OVK. Användarmanual OVK Version 1.5 Daterad:

Krav på webbläsare. Manual för arbetslöshetkassorna. De webbläsare som är kompatibla med portalen är minst Internet Explorer 6.x och Firefox 2.

Manual. Föreningsadministratör i medlemssystemet

Hja lp till Mina sidor

Vilken version av Dreamweaver använder du?

Kort om World Wide Web (webben)

PHP. Dynamiska webbsidor

Introduktion Till WordPress

Modul 6 Webbsäkerhet

12 Webb och kurshemsidor

Individuellt Mjukvaruutvecklingsprojekt

Användarhandledning Version 1.2

Grafiska användargränssnitt i Java

I den här labben ska vi använda oss av en trevlig nyhet i HTML5: Local Storage, för att implementera en sorts lokal gästbok.

Manual 1.0 för att posta nyheter på IF Åland orienterings hemsida (

Transkript:

Internet Applications, ID1354 Seminar1, HTML & CSS Seminar1, HTML & CSS Internet Applications, ID1354 Kim Hammar, kimham@kth.se 11/9-2014 1

Internet Applications, ID1354 Seminar1, HTML & CSS 1 Introduction Syftet med denna uppgift är att vi ska lära oss grunderna inom web-applikationer. Vi ska med enkel HTML samt CSS kod och en fungerande web-server skapa hemsidor som uppfyller kraven. Jag har arbetat individuellt med uppgiften och utvecklat i NetBeans IDE. Kraven på hemsidan är att den ska innehålla minst 4 sidor, index, Kalender & två sidor med recept. Företaget som hemsidan ska representera ska heta Tasty Recipes. Index-sidan ska innehålla länkar till alla recept (minst 2st) samt en kalender. Kalender-sidan ska innehålla en mat-kalender som innehåller klick-bara bilder på recept, bilderna ska vara länkar till recept-sidorna. En receptsida ska innehålla följande: Namnet på rätten, en bild, en lista med ingredienser, närings-fakta samt användar-kommentarer. Krav på alla sidor: alla sidor ska ha en liknande layout, de ska följa samma tema. I CSS ska vi specifikt välja vår layout, ingen sida ska ha default-värden som layout. Alla HTML- samt CSS-dokument måste klara en validering. Extra uppgifter (Ej obligatoriskt): sidan är anpassningsbar för olika skärmupplösningar, från 320x480/480x320 till 1920x1200 pixels. Sidan agerar identiskt i följande webb-läsare: Chrome 31, IE 10, Firefox 26, Safari 6. Jag anser att jag lyckats uppnå alla dessa krav på hemsidan. 2

Internet Applications, ID1354 Seminar1, HTML & CSS 2 Method Den förkunskap jag hade innan jag började arbeta med uppgiften var det vi gått igenom under föreläsningar, dvs. De olika språkens primära syften, vilken typ av struktur HTML och CSS-dokument bör ha. Min metodik för att lösa uppgiften gick till så att jag först planerade ungefär hur jag ville att min hemsida ska se ut och därefter gjorde jag research på internet om lämpliga html-taggar samt lämpliga properties/värden i mitt style-sheet. En sida jag besökte frekvent för att lära mig om specifika HTML-taggar och CSS-värden var www.w3schools.com När jag stötte på problem (när sidan inte uppförde sig som jag hade tänkt) felsökte jag problemen genom att använda web-developer samt firebug i Firefox, då kunde jag tydligt se var mina block-element samt inline-element började och slutade vilket gjorde det enkelt att justera fel i mitt CSS-dokument. XHTML syntax regler kunde jag ganska bra redan innan, och med hjälp av NetBeans IDE så var det inga problem för mina HTML samt CSS dokument att klara valideringen. Jag har även valt att göra båda extra-uppgifterna. För att göra min hemsida anpassningsbar för olika skärmupplösningar har jag använt: responsivegridsystem(www.responsivegridsystem.com) samt media-queries i min CSS-fil. responsivegridsystem (www.responsivegridsystem.com) bygger på att dela upp sidan i kolumner. Jag gillar den strukturen och det hjälpte mig att göra min hemsida anpassningsbar samt att lätt kunna placera element så som jag vill ha dem på sidan. CSS-koden inom media-queries blocket aktiveras endast när skärmens bredd är mindre än 480pixlar och skärmens höjd är mindre än 320 pixlar. Vid små skärmar kommer t.ex. hela headern att försvinna då den bara tar upp massa plats samt att huvudmenyn kommer visas vertikalt istället för horisontellt. För att försäkra mig om att min hemsida uppnått målen har jag testat den i alla relevanta skärmupplösningar. För att få min hemsida att agera indentiskt i flera olika webbläsare har jag använt ett resetstylesheeet som neutraliserar webbläsarnas grund-inställningar. För att testa att min hemsida uppnått målen har jag testat den i alla relevanta webb-läsare m.h.a. www.browserstack.com se Bilaga1. 3

Internet Applications, ID1354 Seminar1, HTML & CSS 3 Result Grund-layouten som gäller för alla mina sidor bygger på en <header> tagg innehållandes en logga och en paragraf. Under headern har jag sedan två block-element som flyter ihop med varandra och som har samma bakgrundsfärg. Det första block-elementet innehåller ett sökformulär och det andra block-elementet innehåller en <ul> meny med sidlänkar för sidnavigering. Det andra blockelementet innehåller faktiskt även en ytterligare <ul> meny som endast är synlig när man håller musen över <li> elementet Recipes i huvudmenyn, se Fig0. Den andra menyn som kallas sub-menu har lite speciellt css-kod som gör att den bara syns vid mouse-over: ul.menu li ul.sub-menu { display:none; //menyn syns inte. position: absolute; top: 30px; left: 0; width: 70px; } ul.menu li:hover ul.sub-menu { display:block; // menyn syns när vi håller musen över rätt <li> element. } 4

Internet Applications, ID1354 Seminar1, HTML & CSS Fig0. Ett UI screenshot utav About us-sidan eftersom den är tom (bara för att illustrera headern samt huvudmenyn som finns på alla sidor). Jag har tagit hjälp av responsivegridsystem (www.responsivegridsystem.com) för designa min hemsida T.ex. på mina recept-sidor så använder jag 4 olika kolumner för att placera ut recept-bild, ingredienser, metod och närings-fakta. Se fig 1. Det gör att på stora skärmar så hamnar kolumnerna på en rad som täcker hela sidans bredd, om skärm-upplösningen är för liten för att det ska vara möjligt så staplas kolumnerna istället på varandra se fig 2,3,4. Fig1. Ett screenshot av UI på recept-sidan för pannkakor för att illustrera hur jag använder responsivegridsystem. Fig2. En kodsnutt för att visa hur klasser som ska ha kolumn-layout defineras i HTML. 5

Internet Applications, ID1354 Seminar1, HTML & CSS Fig 3. En kodsnutt för att visa hur kolumnernas bredd defineras i CSS. Fig 4. En kodsnutt för att visa kolumnernas bredd på små skärmar. Vilket gör att dom staplas på varandra. Kommentarerna på recept-sidorna är hårdkodade men jag har även lagt till <input> element av typen text för Username, ett <textarea> element för input av kommentarer samt ett <input> element av typen submit för en submit-knapp. Se Fig5. 6

Internet Applications, ID1354 Seminar1, HTML & CSS Fig5. Hård-kodade kommentarer samt input rutor för kommentarer. Mat-kalendern är gjord som en tabell med <table> taggen. 7st kolumner för 7 veckodagar, 30 celler <td> för 30 dagar i September och 30/7 5 rader <tr>, se fig6. 30 celler i tabellen innehåller numrering (dag-numrering) och 2st celler innehåller bild-länk + numrering. Fig6. Screenshot av UI på kalender-sidan. 7

Internet Applications, ID1354 Seminar1, HTML & CSS Webbsidan har även en anpassningsbar layout som funkar på olika typer av skärmupplösningar, på väldigt små skärmar så blir headern (innehållandes logga och rubrik) överflödig då den bara tar upp plats, därför har jag i min media-query för små skärmar gjort så att headern inte syns, m.h.a. display:none;. Även bilderna inuti kalendern samt bilderna på recept-sidorna försvinner på små skärmar för att viktigare element ska synas bättre. En annan vital ändring för att anpassa sidan till små skärmar är att huvudmenyn visas vertikalt istället för horisontellt se Fig7 & Fig8. Det leder även till att jag gjort drop-down menyn synlig hela tiden istället för bara vid mouse-over. Responsivegridsystem gör även att de olika kolumnerna: ingredienser, metod & näringsfakta nu hamnar på varandra istället för brevid varandra. Fig7. Screenshot av recept-sida för 320x480pixlar 8

Internet Applications, ID1354 Seminar1, HTML & CSS Fig8. Screenshot av kalender-sida för 320x480pixlar För att göra hemsidan identiskt i flera webbläsare använder jag reset.css från kurshemsidan. 4col.css är ett stylesheet från responsivegridlayout.com för att dela upp hemsidan i kolumner och lättare göra den anpassningsbar för olika skärmupplösningar. Tasty.css är mitt egna stylesheet vilket också innehåller majoriteten av css deklarationerna som används. Se bilaga1 för screenshots i olika webbläsare. Dessa 3 stylesheets används i alla mina html-sidor. Se fig9. Fig9, länkar till stylesheets som används. 9

Internet Applications, ID1354 Seminar1, HTML & CSS Valideringsresultat enligt http://validator.w3.org/ och http://jigsaw.w3.org/css-validator/ : 4 Discussion Jag tycker att jag lyckades uppfylla alla målen för uppgiften samt att jag tillslut faktiskt fick hemsidan att fungera så som jag hade skissat upp innan jag börjat koda. Själva HTML-koden gick ganska snabbt att skriva och var enkel att förstå. Den mesta utav tiden gick till att jobba i CSS-dokumentet. I CSS har jag ingen som helst tidigare erfarenheter därför blev det mycket trial & error samt w3schools research om olika properties/värden osv. Jag stötte på många problem i mitt css-kodande men med hjälp av firebug och webdeveloper samt all information som finns på internet kunde jag lösa det mesta. Ett misstag jag gjorde vid användandet av reset.css för att få hemsidan att agera identiskt i flera webbläsare var att jag inte länkade till reset-stylesheet i mina sidor förnst jag var i princip klar med min design, detta gjorde att när jag länkade till reset.css så förstördes ju min design delvis eftersom att den var i vissa avseeenden byggd på webbläsarensgrundinställningar. Det gjorde att jag fick lägga ytterligare tid på att justera desingen. Så läxa till nästa gång blir att alltid länka med till reset-stylesheet i HTML-dokumenten innan jag börjar koda i CSS. 10

Internet Applications, ID1354 Seminar1, HTML & CSS 5 Comments about the course Tid spenderat på uppgiften: Alla föreläsningar samt övningen, plus ca 3h/dag från måndag-torsdag. 11

Internet Applications, ID1354 Seminar1, HTML & CSS Bilaga 1 Safari 5.0 Chrome 31 12

Internet Applications, ID1354 Seminar1, HTML & CSS Internet Explorer 10 Firefox 26 13

Internet Applications, ID1354 Seminar2, JavaScript Seminar2, JavaScript Internet Applications, ID1354 Kim Hammar, kimham@kth.se 24/9-2014 1

Internet Applications, ID1354 Seminar2, JavaScript Table of content Introduction 3 Method 4 Result 5 Discussion 15 Comments about the course 16 2

Internet Applications, ID1354 Seminar2, JavaScript 1 Introduction Syftet med denna uppgift är att vi ska lära oss i stort hur man programmerar webapplikationer med JavaScript. Syftet är även att vi ska bekanta oss med välkända ramverk för JavaScript. För att underlätta event-hantering och uppdateringar ska vi använda ramverket jquery. Vi ska även använda ett ramverk som underlättar arkitektur och design för JavaScriptklienten (programmering av vy-modell). Själva uppgiften går ut på att med hjälp av JavaScript och dessa ramverk förbättra hemsidan för Tasty-Recipes företaget som vi skapade till det första Seminariet. Inför detta seminarium är de obligatoriska kraven på hemsidan: Hemsidan ska ha en JavaScript-meny. Menyn ska synas på hemsidan samtliga sidor, Menyn ska ha etiketterna och sid-länkarna: Home, Recipes, Calender, Contact. Recipes ska visa en drop-down meny som innehåller länkar till samtliga recept. Contact ska visa en drop-down meny med länkar till sidorna: About us, Contact us. Det ska vara möjligt att skriva och editera kommentarer till recepten på recept-sidorna. All JavaScript kod ska evalueras på jslint (www.jslint.com). koden behöver inte klara valideringen men vi måste ta hänsyn till valideringen när vi utvärderar vår kod. Extra uppgifter (ej obligatoriska): Använd jquery syntax samt API i all JavaScript kod. Använd KnockOut-ramverket för att strukturera JavaScript-koden som krävs för vy-modellen. Det syftar på uppgiften att göra det möjligt att kommentera och editera recept. Jag har jobbat enskilt med uppgiften och jag anser att min hemsida uppfyller alla krav. 3

Internet Applications, ID1354 Seminar2, JavaScript 2 Method Mina förkunskaper i JavaScript inför denna uppgift var mycket små därför la jag ganska mycket tid innan jag började programmera på att verkligen förstå syntaxen som används i JavaScript samt ramverken: jquery & KnockOut. Min metodik för att lösa uppgiften grundade sig mycket i att göra research om lämpliga metoder samt deklarationer för att lösa uppgiften och sedan testa dem i JSFIDDLE (www.jsfiddle.net). När jag stötte på problem (när sidan inte uppförde sig som jag tänkt) felsökte jag min kod mha. web-developer/firebug i Firefox samt jsfiddle. För att skapa en JavaScript meny med drop-down menyer så har jag använt jquery metoder som innehåller event-hanterare med funktionen att när jag håller musen över element i min meny så kommer en funktion för att visa drop-down menyerna anropas och när jag tar bort musen från elementet så anropas en funktion för att gömma menyn igen genom att ändra css-värdet. Jag har även valt att göra extra-uppgifterna. För att strukturera koden för att skapa/editera/tabort kommentarer enligt MVVM modellen så har jag använt KnockOut ramverk. KnockOut har ett data-bind system som gör att man på ett kraftfullt sätt kan binda data till vyn/ui. Så för att kort sagt beskriva hur jag utan en server/databas lagrar data och uppdaterar vyn med data så använder jag en ObservableArray som innehåller datan. Vyn uppdateras dynamiskt genom en foreach-loop som itererar genom ObservableArrayen. 4

Internet Applications, ID1354 Seminar2, JavaScript 3 Result Del-uppgift: Menyn Det finns nu två nya sidor: About us och Contact us. Dessa sidor är tomma förutom att de har samma header och samma huvudmeny som alla andra sidor. Menyn för sid-navigering som finns på alla sidor bygger på en <ul> lista innehållandes <li> element. <li> elementen med etiketterna Recipes och Contact innehåller <ul> menyer i sig. Det är dessa nästlade <ul> listor som representerar drop-down menyerna. Se fig0. Dessa <ul> listor har som default värde i css: display: none; Vilket gör att dem inte syns. Det är här vi behöver programmering av beteendet hos klientsidan och jquery och JavaScript kod kommer in i bilden. För att ändra drop-down menyns synlighet så skapar jag två stycken funktioner i jquery (opensubmenu och closesubmenu) som sätter css propertyn display till: display:block; respektive display: none; se fig1. Min tanke med drop-down menyerna är att de bara ska synas när jag håller musen över recipes eller contact. För att definiera att det är just då som drop-down menyerna ska synas så skapar jag två ytterligare funktioner som bygger på eventhanterare till mouseover respektive mouseout. Med hjälp av dessa funktioner kan jag sedan avgöra exakt när opensubmenu och closesubmenu ska anropas. Se fig2. Fig0. Ett UI-screenshot för att visa huvudmenyn samt en utav drop-down menyerna. 5

Internet Applications, ID1354 Seminar2, JavaScript Fig1. Ett kodutdrag för att visa den lilla jquery kod som behövs för att ändra css-värden för drop-down menyn. Fig2. Ett kodutdrag för att visa JavaScript koden som definerar när closesubmenu och opensubmenu funktionerna ska anropas. Del-uppgift KnockOut: För att göra det möjligt att skriva kommentarer samt radera/editera kommentarer på recept-sidorna så har jag använt KnockOut ramverk. KnockOut är bra för att strukturera en view-model och på så sätt skapa en MVVM-struktur. View-Model har som uppgift att rapportera till vyn när ändringar sker. Det hela bygger alltså på ett observer-mönster. 6

Internet Applications, ID1354 Seminar2, JavaScript Vy-modellen är ett vanligt JavaScript objekt, men för att kunna dra nytta av KnockOuts observer-mönster (Viewmodel-view) så måste properties i objektet deklareras som observables. För att observera en kollektion och inte bara enskilda värden så används observablearray. Se fig 3. Fig3. Ett kod-utdrag för att visa viewmodel objektet. För att binda min vymodell till vyn så används dessa deklarationer i JavaScript filen: var vm = new viewmodel(); ko.applybindings(vm); Den kollektion som min observablearray entries observerar är en samling objekt av typen Comment. Comment är ett Objekt innehållandes obsevables för att observera användarnamn och kommentarer från vår vy (se fig4). Comment-objektet innehåller även 3 metoder (se fig5): addentry: addentry metoden adderar en instans av sig själv till observablearray entries. metoden skapar även en ny instans av sig själv inuti observablen comments (inuti viewmodel). removeentry: removeentry tar bort sig själv från observablearray entries. Edit: Edit byter värde på variabeln Editbool till false/true. Fig4. Ett kod-utdrag för att visa Comment-objektet. 7

Internet Applications, ID1354 Seminar2, JavaScript Fig5. Ett kodutdrag för att visa metoderna inuti Comment-objektet. I min html-vy för receptsidorna så har jag ett <input> element för username och ett <textarea> element för kommentaren. Dessa element har databindningarna username respektive newcomment. Under dessa två element finns ett <button> element med databindningen på click så anropas funktionen addentry. Se fig 6. Detta gör att varje gång någon postar en kommentar så kommer en ny ingång i observablearray att skapas. Input-elementen för kommentarer och username samt Send-knappen är alla deklarerade inom: <! ko with: comments --> <!-- /ko --> with betyder att vi skapar ett nytt bindande sammanhang. Vi bestämmer här att dessa element ska vara bundna till observablen comments. Och som man kan se i fig3 så skapar observablen comments nya objekt av Comment. Och det är ju Comment-objektet som innehåller den viktiga datan, våran observablearray består ju som sagt av en kollektion av Comment-objekt. Detta kanske blev lite rörigt beskrivet i skrift, men det är nödvändigt för att när man trycker på Send -knappen ska man anropa metoden addentry som kommer att addera en instans av Comment-objektet i vår Observable-array. 8

Internet Applications, ID1354 Seminar2, JavaScript Fig6. En print-screen för att illustrera hur man postar kommentarer För att uppdatera vyn efter observablearray så använder jag data-bindningen foreach. Foreach duplicerar en del av en markup (i detta fall ett <li> element) för varje ingång i en array (i detta fall en ObservableArray). Se fig7 9

Internet Applications, ID1354 Seminar2, JavaScript Fig7. Ett kodutdrag för att visa koden som uppdaterar vyn för varje ingång i ObservableArray. Som man kan se i fig7 så blir <li> elementet ganska omfattande. Detta är p.g.a. att det ska vara möjligt att radera och editera varje ingång i observablearray (username &comment). Variabeln Editbool som visades tidigare (se fig4) används för att uppdatera vyn efter false/true. Ifall Editbool är false (default-värdet) Så syns en Delete knapp samt en Edit knapp under varje ingång i arrayen. Se fig8. 10

Internet Applications, ID1354 Seminar2, JavaScript Fig8. Ett Screenshot för att visa layout för kommentarer när Editbool = false Vid knapptryckning på Delete knappen så tas ingången i arrayen bort (vi anropar removeentry) vid knapptryckning på Edit knappen så anropar vi Edit, Edit kommer byta värde på Editbool till false/true. Ifall Editbool är true så kommer username samt comment att bytas ut mot två textrutor: ett <input> element för username och en <textarea> element för kommentaren, dessa element har data-bindningar till våra observables username och newcomment. Edit-knappen kommer att försvinna och istället syns en Save-knapp, Save har har samma data-bindning som Edit vilket gör att vid knapptryckning så kommer funktionen Edit att anropas vilket leder till Editbool byter värde <input> och <textarea> försvinner och de nya värdena för username och newcomment kommer att uppdatera Comment-objektet. Comment-objektet finns ju redan i vår observablearray (Det är ju en redan postadkommentar) vilket betyder att en ändring i Comment-objektet leder till en ändring i ObservableArray vilket leder till att vyn uppdateras. Se fig9 & fig10 11

Internet Applications, ID1354 Seminar2, JavaScript Fig9. Ett Screenshot för att visa layouten för kommentarer när Editbool = true. 12

Internet Applications, ID1354 Seminar2, JavaScript Fig10. Ett screenshot för att visa layout vid knapptryckning på Save efter editering. All JavaScript kod är skriven inuti jquery deklarationen: $(document).ready(function() { }); Genom att skriva javascript koden inuti den JQuery deklarationen så kan vi vara säkra på att koden inte exekveras förnst hela DOM är laddat på ett säkert sätt. All JavaScript kod som inte hör till knockout är skriven enligt jquery syntax. (Jag försökte skriva även knockout deklarationer enligt jquery syntax, men resultatet blev inte som jag hade tänkt. Jag pratade även med labb-assistenten under workshop och vi kom fram till att det inte gick att blanda den kod som jag skrivit som berör knockout med jquery syntax. Framförallt p.g.a. att den KnockOut kod jag skrivit främst består av deklarationer av observables.) 13

Internet Applications, ID1354 Seminar2, JavaScript Vid körning av JavaScript koden i www.jslint.com uppstår en mängd fel, till majoriteten p.g.a: Unexpected character '(space)'. Den klagar även på objekt som definerats i KnockOut ramverket. 'ko' was used before it was defined. Jag har lyckats rätta till ett antal fel som jslint klagade på t.ex: Jag hade definierat mina opensubmenu och closesubmenu funktioner efter att jag anropat dem i koden, vilket ju såklart inte är rätt! Även om det funkade lika bra i praktiken. Däremot Fel som: Unexpected character '(space)'. 'ko' was used before it was defined. Expected 'var' at column 5, not column 11. Har jag inte lyckats lösa pga: 1. Jag förstår inte vad dem menar med unexpected character ( space ), jag antog såklart att det var ett mellanrum som var fel, men det hjälpte inte att ändra det! 2. Jag hittar inga alternativ på Jslint.com för att validera för olika ramverk som t.ex. knockout och jquery. Jag har personligen inga tidigare erfarenheter av jslint men utifrån mina kunskaper inom JavaScript så kan jag inte hitta något major-fel som jslint klagar på. Utan det verkar framförallt vara min formattering som inte följer den formattering som jslint kräver. Därför blir min slutsats och uvärdering av koden att den håller även om den inte är perfekt. För att bibehålla bra sammanhållning så har jag skrivit all javascript kod i separata filer. Se fig 11 Fig11. Ett kodutdrag för att visa vilka script som används (jquery ramverk, KnockOut ramverk samt min egna javascript fil). 14

Internet Applications, ID1354 Seminar2, JavaScript 4 Discussion Jag tycker att jag lyckades uppfylla alla målen för uppgiften. Själva jquery koden för att göra drop-down menyer tog inte så lång tid att skriva och förstå. Däremot KnockOut ramverket tycker jag var ganska så trixigt och komplicerat att använda för första gången! Men med hjälp av föreläsningsanteckningarna samt andra källor från internet som t.ex. www.knockoutjs.com & www.stackoverflow.com så kunde jag hitta bra info och förklaringar. Om jag ska vara självkritisk så gjorde jag det vanliga misstaget att jag började koda för tidigt. Jag tror att om jag hade lagt mer tid på att förstå principerna och målet med uppgiften så hade kodandet gått snabbare och mer smärtfritt, nu blev det så att jag fintade bort mig själv en aning i början. 15

Internet Applications, ID1354 Seminar2, JavaScript 5 Comments about the course Tid spenderat på uppgiften: Alla föreläsningar/övningar samt ca 5h/dag lördag-onsdag. 16

Internet Applications, ID1354 Seminar3, PHP Seminar3, PHP Internet Applications, ID1354 Kim Hammar, kimham@kth.se 5/9-2014 1

Internet Applications, ID1354 Seminar3, PHP Table of content Introduction 3 Method 4 Result 5 Discussion 19 Comments about the course 20 2

Internet Applications, ID1354 Seminar3, PHP 1 Introduction Det primära syftet med denna uppgift är att vi ska lära oss PHP-programmering, men förhoppningarna är även att vi ska bekanta oss med databaser och bli medvetna om programmets design och arkitektur. För att underlätta design och arkitektur är det rekommenderat att använda sig av ett PHPramverk. Själva uppgiften går ut på att vi ska fortsätta utveckla hemsidan för företaget Tasty Recipes, som vi har tagit fram under tidigare seminarium. Inför detta seminarium är kraven på hemsidan: Kommentarer som skrivs till våra recept-sidor ska sparas på servern mha. PHP-programmet. Kommentarerna ska alltså finnas permanent på sidan, de ska inte sparas på klientsidan. Det ska vara möjligt att registrera sig som användare på hemsidan, användares uppgifter ska sparas på servern. (Vi behöver alltså skapa en ny sida för registrering). Inför autensiering för användaren. Användaren ska kunna logga in med användaruppgifter som specifierats vid registrering. (Vi behöver alltså skapa en ny sida för inloggning). Endast inloggade användare ska ha tillstånd att skriva kommentarer på receptsidorna och sedan ska det finnas möjlighet för användaren att redigera endast sina egna kommentarer. Extra-uppgifter (ej obligatoriskt): Använd en databas för att spara kommentarer och användar-data. Använd ett php-ramverk (ex: FLIGHT) för att strukturera din php-kod. 3

Internet Applications, ID1354 Seminar3, PHP 2 Method Inför denna uppgiften la jag I vanlig ordning väldigt mycket tid på att studera och repetera föreläsningsanteckningarna samt Leifs exempel program. I och med att jag valt att göra båda extra uppgifterna dvs. att använda Flight-ramverk samt att spara data i en databas så la jag mycket tid på att test-köra och granska leifs exempelprogram, framförallt för att kunna förstå hur flight fungerar. I denna uppgift var det inte alls lika enkelt att felsöka min kod som det varit tidigare (då jag använt firebug m.m). Men för att lokalisera buggarna i min php-kod använde jag ofta kodsnutten: ordie(); Vilket gör att programmet krashar om min kod inte funkar som den ska. Jag har även kikat lite i php-konsolen men jag tycker faktiskt inte att det var till så mycket hjälp! Med hjälp av mycket debug-utskrifter i koden, ordie();, föreläsningsanteckningar och leifs exempel-program så kunde jag ändå lösa de flesta problem jag stötte på. Även om denna uppgiften rent tidsmässigt var betydligt mer krävande än föregående uppgifter. Det enda jag tyvärr inte lyckades lösa var att använda AJAX och JSON för att bygga vidare på min vy-modell från tidigare uppgift. Det var min absoluta ambition när jag började programmera denna uppgift men trots mycket möda och tid lyckades jag inte få det att fungera (Jag skulle gissa att det var pga. Någon minimal detalj som jag missat!). Men pga tidspress samt att det inte var något krav på denna uppgift så valde jag att strunta i AJAX och JSON och lösa uppgiften på annat sätt så att min hemsida uppfyller alla krav. För att spara data i min databas så har jag använt: Php-verktyget phpmyadmin samt mysql. Jag har skapat en ny databas vid namn: ID1354 ID1354 innehåller tables för users, meatballcomments, pancakescomments. För att möjliggöra registrering och inloggning har jag använt: Min databas samt cookies för att hålla reda på om användaren är inloggad eller inte. För att strukturera min kod enligt MVC samt undvika att skriva egen infrastruktur-kod har jag använt: PHP- ramverket flight. För att strukturera min kod enligt MVC har jag använt namespaces och separata klass-filer. Inspirerat av Leifs exempel-program. 4

Internet Applications, ID1354 Seminar3, PHP 3 Result Programstruktur: Programmets struktur bygger på MVC-mönstret kryddat med ett integrations lager som interagerar med databasen. Utöver vyn på serversidan finns förstås även en vy på klient-sidan. Alltså min struktur kan ses som att index.php är toppen av programmets struktur. Under index så finner vi klient-sidan vyn (som innehåller all HTML-kod). På samma nivå som klientsida vyn finns även flight-ramverk samt resources som innehåller css, bilder och javascript. Detta kan ses som nivåerna som finns på klient-sidan. På Serversidan finns på översta nivån ett namespace för alla vy-klasser. Under vy-klasserna finns ett namespace för Controllern. Under Controllern finns namespaces för model och integration. Model och integration är två separerade namespaces men man kan ändå se det som att de ligger på samma nivå då Controllern har direkt koppling till dem båda, för att nå integrations namespace så behöver controllern inte gå igenom modellen. För att illustrera mitt programs struktur: Request kommer till index, index gör anrop till vyn på serversidan. Vyn på serversidan anropar antingen integrations-namespace (om vi ska göra något med databasen) eller modellen. När serversida vyn är klar med sin aktion så anropar den klientsida-vyn. Klientsida-vyn visar innehållet för användaren. Extra-uppgift: PHP-ramverk. Jag har valt att strukturera min PHP-kod genom ramverket FLIGHT, därför tänkte jag börja med att förklara hur mitt program är uppbyggt innan jag går in på de obligatoriska uppgifterna. Flight är ett snabbt, enkelt och relativt litet php-ramverk. Jag personligen har aldrig tidigare använt ramverk för min php-kod men de största fördelarna jag upplevt med flight är möjligheten att definiera routes och auto-loading för mina php-filer. När man väl bekantat sig med Flight så ger det även en bra tydlighet i programmet framförallt p.g.a. indexsidan som gör att man kan enkelt kan följa programmets exekvering med startpunkt i Index. Mitt program kan beskrivas så här (med inspiration från Leifs chatprogram): Alla requests skickas till index.php, oavsett URL. I index så finns ett antal routes definierade se fig 0. Routing innebär helt enkelt att mappa URL till olika funktioner/filer. T.ex. så har jag mappat min bas-url + / till funktioner för att rendera min home-page. Det innebär att när ingen URL utöver bas-url är specifierad så hamnar man på home-sidan. 5

Internet Applications, ID1354 Seminar3, PHP Fig 0. Ett screenshot för att illustrera hur routingen från index.php går till. Som syns i bilden så är det en regel att varje gång vi gör ett anrop till vyn på serversidan så skickar vi med en instans av controllern. Det första som händer i index är att en session startas (om det inte redan är en igång) och att http-parametrarna defineras. En typisk request går till på följande sätt: Request kommer till index I index så hittas en route som matchar, den routen leder till att ett nytt anrop till server-sida vyn av metoden executeaction, executeaction är en generell metod som tar ett objekt av en klass som parameter och eventuellt andra parametrar också. I det objektet anropas sedan funktionen execute. executeaction är mappad till att göra detta, det är samma mekanism som används i routingen. Se kod: \Flight::map('executeAction', }); function($executor, $params = NULL) { $executor->execute($params); Execute är en funktion som alla mina klasser i vyn ärver från klassen AbstractExecutor. Execute leder till att eventuella extra parametrar definieras i det nyskapade objektet och sedan börjar exekveringen i metoden doexecute. doexecute är en metod som alla vy-klasser ärver och överskriver från AbstractExecutor. Exempel: 6

Internet Applications, ID1354 Seminar3, PHP När ingen URL är specifierad så kommer route / att matcha. Se fig1. Fig1. Kodutdrag för att illustrera default-route. Nytt objekt av klassen ShowHome(i servern-sida vyn) skapas och Controllern skickas med vid initieringen. Detta leder till att objektet börjar exekvera i doexecute. Se fig2. Fig2. Kodutdrag för att visa vägen för en simpel route från index till dess att vyn visas. doexecute i ShowHome anropar en metod i AbstractExecutor (renderhome();) som anropar klientsida-vyn för att visa home-page i vyn. Se fig3 & fig4. Fig3. Variabeln $home i AbstractExecutor är bunden till en sida i min klientsida-vy som heter home.php. Fig4. Funktionen renderhome(); i abstractexecutor. Funktionen kommer att rendera home.php till vyn. Klart! Home-page syns nu i vyn. Denna metodik används för att generera alla sidor, det enda som skiljer sig åt är vilka objekt som skapas I server-sida vyn samt hur dessa objekt är uppbyggda, ShowHome-objektet i exemplet var ju väldigt simpelt, andra mer omfattande sidor/instruktioner än Home har mer komplexa objekt (alltså anrop ner till modellen eller integrations-namespace på servernsidan), vilket illustreras längre ner i rapporten. 7

Internet Applications, ID1354 Seminar3, PHP Vad är abstractexecutor för typ av klass? AbstractExecutor har en viktig funktion i serversidavyn. Den sköter de som Flight inte erbjuder dvs: kod för att skapa sammansatta vyer, lagra och hämta controllern i superglobal $_SESSION samt en funktion setparams för att setta eventuella parametrar som skickas med (i mitt fall oftast POST-parametrar). I och med att AbstractExecutor innehåller kod som egentligen bör hanteras av ett ramverk (infrastruktur kod) så är det ganska komplicerad kod, därför har jag sneglat mycket och hämtat inspiration av leifs chat-program när jag programmerade den klassen. Obligatoriska uppgifter, 1: Spara recept-kommentarer på servern. Mina receptsidor innehåller formulär (<form>) med <input> element för Username och Comment. T.ex. formuläret för att skicka en kommentar på receptsidan meatballs har action="meatballcomment" Det gör att när vi skickar en kommentar så startas en request vi hamnar i index.php en route för /meatballcomment matchas. Inuti den specifierade routen så hämtas POST-parametrarna author och message från data ( data är FLIGHTs motsvarighet till $_POST). Sedan så skapas ett objekt av klassen NewMeatballComment och i samband med skapandet av det objektet så skickas postparametrarna samt controllern med som in-parametrar. Exekveringen fortsätter sedan precis som en request för home-page i exemplet ovan, skillnaden är att klassen NewMeatballComment är mer komplex än ShowHome. NewMeatballComment innehåller set-metoder för POST-parametrarna, samt metodanrop till controllern för att spara kommentarer i databasen. Se fig 5,6,7 och 8. 8

Internet Applications, ID1354 Seminar3, PHP Fig5. Kodutdrag för att visa set-metoderna i NewMeatballComment. Fig6. Kodutdrag för att visa doexecute i NewMeatballComment som anropar controllern. Metoden meatballusername anropas för att kolla om användarnamnet i kommentarsfältet stämmer överens med den inloggade användaren. Om användaren skrivit in sitt rätta användarnamn så anropas funktionen meatballcomment() med author och msg som inparametrar. Author och msg är variabler inuti NewMeatballComment som har initierats till värden från POST-parametrarna author och message. meatballcomment() lägger till kommentaren i databasen. Sedan anropas metoden rendermeatballs som tar meatballcomments som inparameter, meatballcomments är en php-fil i vyn som innehåller kod för att iterera genom databasen och skriva ut kommentarerna på skärmen. Se fig 9. Hur fungerar det med php-fil som in-parameter till rendermeatballs då? Jo det fungerar precis som i Leifs chat-program. Inparametern i detta fallet defineras som body_content och det finns även en fil definerad som header_content. Slutligen används en Layout fil som infogar dessa filer i en och samma mha. <?php echo $header_content;?> <?php echo $body_content;?> Detta är alltså vad som sker om checkusername(); returnerar true. Ifall checkusername returnerar false så skriver vi inte in något i Databasen utan renderar istället ett felmeddelande till användaren. Se fig 10. 9

Internet Applications, ID1354 Seminar3, PHP Fig7. Kodutdrag för att visa controllern som anropar integrationslagret. Meatball är ett objekt av klassen meatballcomment som finns i integrationslagret. Meatball skapas i controllerns konstruktor. Fig8. Kodutdrag för att visa metoderna i integrationslagret som 1: kollar om användarnamnet stämmer överens med den inloggade användaren. 2: connectar med databasen och skapar en ny entry med den nya kommentaren. Metoden för att kolla om användarens angivna username stämmer överens med den inloggade användarens username heter checkusername()(denna metod hör ihop med obligatorisk upg4: Endast Inloggade användare ska ha möjlighet att kommentera recepten. Denna metod gör att den inloggade användaren måste använda sitt eget Username när hen kommenterar.) Det första som händer i comment() är att vi connectar med databasen (jag använder phpmyadmin). Sedan så väljs den databas som ska användas, i detta fall: ID1354. 10

Internet Applications, ID1354 Seminar3, PHP Fig9. Ett kodutdrag för att visa koden som itererar genom databasen och skriver ut kommentarer i vyn. Fig10. Screenshot för att visa fel-meddelande. 11

Internet Applications, ID1354 Seminar3, PHP Obligatoriska uppgifter, 2: Möjliggör registrering där användaren anger nickname, username och password. Registreringen av användare sker enligt samma princip som kommentarer sparas på servern. Registrerings-sidan innehåller ett formulär (<form>) Där användaren anger Nickname, Username och Password. En request skickas till index.php där den matchande routen /dbreg leder till att ett objekt av klassen NewUserDB och som inparameter skickar vi med POST-parametrarna nickname, username och password. I NewUserDB börjar vi sedan exekvera i metoden doexecute. Se fig 11. Fig 11. Ett kodutdrag för att visa hur användar-registreringen går till. Användare som sparas i databasen går till på samma sätt som när kommentarerna sparas. $this->controller->register(username, pw, nickname) leder till anrop av controllern som leder till anrop av integrationslagret som leder till att vi tillför nya värden i databasen, enda skillnaden är att vi här väljer ett table i databasen som heter heter users. Men innan någon registrering sker så görs en validering! $this->controller- >user_exists(username) leder till anrop ner till integrationslagret och en metod som etar igenom users i databasen för att kolla om det redan finns en användare registrerad med det efterfrågade användarnamnet. $this->formsempty() kollar om alla fälten (nickname, username, password) är ifyllda. Se fig 12. 12

Internet Applications, ID1354 Seminar3, PHP Fig 11. Kodutdrag för att visa metoden formsempty(). Ifall all validering är godkänd så renderas en sida för registration succesful Ifall något fel uppstår (upptaget användarnamn/ej ifyllda fält) så renderas en sida med info om detta. Obligatoriska uppgifter, 3: Möjliggör autensiering (inloggning) för registrerade användare. Metoden för inloggning bygger också på ett formulär (<form>) där användaren specifierar username och password. En request kommer till index.php och där matchas en route för /userlog vilket leder till att ett objekt av NewLogin skapas. Vi hamnar i doexecute inuti NewLogin. I doexecute så anropas controllern -> integrationslagret och metoden login(username, password) Login kommer iterera genom databasen och leta efter en entry som matchar både användarnamn och password som angivits. Se fig12. Fig12. Kodutdrag för att visa metoden för att validera login-uppgifter. Notera md5- kryptering på password. Detta är nödvändigt eftersom password i min databas är md5- krypterade. Ifall inloggningsuppgifterna matchar någon entry i databasen så skapas ett objekt av User i modellen. Och renderloggedin anropas: $newuser = $this->controller->newuser($this->username, $this->pw); $this->renderloggedin('loggedin', 'newuser', $newuser); 13

Internet Applications, ID1354 Seminar3, PHP Notera att vi skickar med en path till sidan loggedin i vyn samt ett objekt av User. loggedin är en viktig sida då den kommer hämta username samt password från User-objektet mha. get-metoder som finns i klassen User i modellen och sätta cookies för användaren! Se fig 13. Fig13. Kodutdrag för att visa loggedin.php Efter att loggedin har exekverats så kommer den vanliga Log-in sidan att renderas. Log-in sidan innehåller dock kod för att kolla om en användare är inloggad eller inte. Se fig14. Ifall en användare är inloggad så syns en Log-Out knapp och ifall användaren inte är inloggad så syns den regulära login-sidan, dvs: ett formulär för inloggning. Fig14. Kodutdrag för att visa kollen som görs om användare är inloggade eller inte. Ifall användaren är inloggad så syns som sagt en LogOut knapp. Ifall användaren trycker på den så startas en request matchande route i index.php blir /LogOut nytt objekt av LogOut klassen skapas och som inparameter skickas POST-parametrarna username & password. Inuti doexecute metoden i LogOut klassen så anropas renderloggedin $this->renderloggedin('logout', 'newuser', $newuser); Notera nu att renderloggedin är samma metod som anropades vid inloggning (se lite högre upp I rapporten) Men denna gång skickar vi med en path till sidan logout.php i vyn. logout kommer att ta bort användarens cookies. Se fig 15. Fig 15. Kodutdrag för att visa hur cookies tas bort. Notera att tiden är negativ, det gör att cookien tas bort. 14

Internet Applications, ID1354 Seminar3, PHP Efter att logout.php exekverats så renderas log-in sidan som vanligt, och som jag nämnde tidigare så kollar log-in sidan om cookies är set eller unset. Obligatoriska uppgifter, 4: Endast inloggade användare ska kunna skriva kommentarer på recept-sidorna. Inloggade användare ska kunna editera sina egna kommentarer. Recept-sidorna använder precis som log-in sidan en funktion för att kolla om användare är inloggade mha. Isset-funktionen på cookies. Se Fig 14 längre upp!. Om användaren är inloggad syns ett formulär för att addera kommentarer (Samt en lista med existerande kommentarer). Om en användare inte är inloggad så ser receptsidorna ut såhär: se fig 16. Fig16.Screenshot för att illustrera recept-sidorna för användare som inte är inloggade. Användare har även möjlighet att redigera samt radera sina egna kommentarer. Som jag nämnt tidigare så är inloggade användare tvingade att använda sitt eget användarnamn vid kommentering (Se fig10.). Därför innehåller php-sidan som itererar igenom databasen och skriver ut existerande kommentarer även kod för att kolla om användaren är inloggad, och om användaren är inloggad så kollas även om användar-namnen på de existerande kommentarerna stämmer överens med den inloggade användarens användarnamn. Ifall användarnamnen matchar så ges möjlighet till redigering samt radering av kommentaren! Se fig 17. (Fig 17 kan ses som en fortsättning på koden i fig 9). 15

Internet Applications, ID1354 Seminar3, PHP Fig 17. Kod-utdrag för att visa koden som kollar om användaren ska ha möjlighet till redigering samt radering av kommentar och isåfall visas länkar för att redigera och radera kommentaren. Se fig 18. 16

Internet Applications, ID1354 Seminar3, PHP Fig18. Screenshot för recept-sidan för användare inloggad med användarnamn: Kim. Som vi kan se i fig18 så kan användare endast redigera och radera sina egna kommentarer. Om vi granskar fig17 så ser vi att Edit och Delete-knapparna är delar av formulär som innehåller gömda input-element som får värdet commentid. För att förtydliga så innehåller mina tables i databasen för kommentarerna 3st fält: comment_id, username, comment. Comment_id är den primära nyckeln och är den nyckeln som tillhandahåller en dynamisk numrering av alla entrys för kommentarer i databasen. Min tanke med de gömda input-elementen är således att när användaren trycker på Edit respektive Delete-knappen så skickas commentid med som POST-parameter till den matchande routen i index.php. 17

Internet Applications, ID1354 Seminar3, PHP Detta är väldigt viktigt för annars vet ju inte programmet vilken av kommentarerna i databasen som ska raderas/redigeras! Om användaren trycker på Edit-knappen så renderas en ny sida med ett formulär för att redigera kommentaren samt en Save-knapp. Ifall användaren trycker på save så kommer den entry i databasen som har rätt commentid att uppdateras. Se fig19. Fig19. Kod-utdrag för att visa koden som uppdaterar databasen. Radering av en kommentar går till på liknande sätt. Se Fig 20. Fig20. Kod-utdrag för att visa koden som raderar i databasen. 18

Internet Applications, ID1354 Seminar3, PHP 4 Discussion Jag tycker att jag lyckats uppnå alla målen för uppgiften. Det absolut svåraste med denna uppgiften för mig var att komma igång med php och lära sig strukturera mina filer för hemsidan som ett program. Det var ganska bökigt att komma igång och skriva php-kod för en existerande html/css/javascript sida, men när jag väl lyckats integrera phpkoden i mitt program så gick själva uppgifterna av bara farten. Detta har varit det svåraste och mest tidskrävande avsnittet i kursen hitills, men det är också den uppgiften där jag lärt mig allra mest! Om jag ska vara självkritisk så skulle jag nog fått grepp om php samt flights ramverk snabbare om jag gjort en helt ny enkel sida endast för att testa det nya språket och lära mig och sedan interagerat php-koden i min redan existerande sida för Tasty Recipes. Nu gick jag rakt på min existerande sida och det gjorde det svårare att förstå helheten i början. 19

Internet Applications, ID1354 Seminar3, PHP 5 Comments about the course Tid spenderat på uppgiften: Alla föreläsningar och övningar samt ca 4h/dag fredag söndag (9 dagar). 20

Internet Applications, ID1354. Non-functional requirements Seminar4, Non-functional requirements Internet Applications, ID1354 Kim Hammar, kimham@kth.se 15/10-2014 1

Internet Applications, ID1354. Non-functional requirements Table of content Introduction 3 Method 4 Result 5 Discussion 23 Comments about the course 24 2

Internet Applications, ID1354. Non-functional requirements Introduction Målet med denna labb är att vi ska lära oss att implementera grundläggande säkerhet och prestanda krav på vår hemsida. Vi ska även lära oss rent generellt att alltid ta säkerhetskrav och prestanda krav i beaktning när vi programmerar i webben. Förhoppningen är även att vi ska lära oss använda AJAX (ett sätt att hämta data från servern och uppdatera vyn, utan att behöva ladda om hela sidan). Att implementera AJAX kan därför också ses som ett sätt att förbättra prestandan på hemsidan. Själva uppgiften är alltså att fortsätta utveckla hemsidan för företaget Tasty Recipes som vi utvecklat under tidigare uppgifter. Denna gång ska dock ingen ny funktionalitet implementeras utan målet är att förbättra hemsidans säkerhet och prestanda. Inför detta seminarium är kraven på hemsidan: Implementera säkerhetshantering i följande delar för din hemsida/server: File System Security Input Filtering Password Encryption Cross Site Scripting Impersonation Extra-uppgifter (Ej obligatoriskt): Implementera prestanda förbättringar inom minst två av följande områden: Client-side validation Caching Persistent Connections 3

Internet Applications, ID1354. Non-functional requirements Method Denna uppgift var väldigt olik de tidigare uppgifterna då den framförallt varit inriktad på icke-funktionella krav. Det gjorde att jag fick ändra mitt tankesätt och metod lite då en stor del av uppgiften handlade om att bekanta sig med att konfigurera min web-server och göra den hyffsat säker mot diverse attacker. Även om det blev en hel del traditionell programmering också då jag valt att göra extra uppgiften och jobbat med att förbättra min hemsidas prestanda. Min metodik för att lösa den första uppgiften handlade mycket om att läsa på http://www.apache.org/ Samt min specifika webb-server http://localhost/xampp/ för att lära mig konfigurera min server. För att förbättra säkerheten när de gäller: lösenordskryptering, Cross site scripting, impersonation osv så har jag hämtat den mesta infon från föreläsningsanteckningarna. Jag har även valt att göra extra-uppgifterna För att implementera AJAX i kommentarsektionerna har jag: Tagit tillbaka min vy-modell med KnockOut och JavaScript som jag använde i Seminarium 2. Men modifierat den då vy-modellens entrys måste sparas i databasen samt att gamla entrys från databasen måste hämtas från servern varje gång sidan laddas om. Jag har även lagt till en <button> och metoder för att ge användaren möjlighet att uppdatera vyn utan att ladda om sidan. Vi kan skriva kommentarer i en annan webbläsare och ladda in dem utan att refresha sidan. För att implementera client-side validation har jag: Skyddat mitt Ajax-anrop till servern med if-satser som validerar användarens input. Detta betyder alltså att om användaren försöker posta en kommentar med ingen data/ invalid data så tar det stopp redan på klientsidan. JavaScriptmetoden kommer inte göra något anrop till servern utan istället loggas ett error meddelande i JavaScript-konsolen. För att implementera cachning har jag: Skrivit kod för att modifiera http-headers för mina sidor samt ändrat i.htaccess för att ha en generell cachning av större filer som t.ex. olika typer av bilder, pdf-filer osv. 4

Internet Applications, ID1354. Non-functional requirements Result Obligatoriska uppgifter : Säkerhet. File System Security: PHP är ett kraftfullt språk med bland annat möjligheter att accessa filer i vårt filsystem, exekvera kommandon och öpnna nätverks kontakter på servern. Men det finns en nackdel med att PHP är så kraftfullt och det är att det öppnar stora säkerhetshål och gör oss väldigt sårbara för diverse attacker om vi inte noga konfigurerar PHP-interpreterns access-rättigheter. Detta är framförallt relaterat till konfiguration inte programmering, och är därför serverberoende. Jag använder mig av XAMPP vilket är ett web-server paket innehållandes b.la. apache-server och php-interpreter. I Windows (som jag använder) så körs apache som localsystem user vilket betyder att den ges stora filsystem rättigheter. Apache rekommenderar därför att skapa en dedikerad användare med begränsade rättigheter i operativsystemet. Därför ändrar jag i httpd.conf filen i min apache-root. Se fig 0. Fig 0. Kodutdrag för att visa default-inställningarna för apache på min dator. Som syns i bilden så körs apache som default user: daemon. Vilket är samma sak som att den körs som root/admin. Vilket såklart är bekvämt, men inte lämpligt! Det gör oss sårbara för attacker. För att kolla vilken användare apache kör som så test-kör jag ett program med kodraden: 5

Internet Applications, ID1354. Non-functional requirements echo `whoami`; Och får då resultatet: Se fig 1. Fig1. Screenshot som bekräftar att apache kör som admin (kim). För att justera detta så skapar jag en ny användare som är dedikerad till att köra min webbserver (som apache har rekommenderat). Jag går även in och ändrar i httpd.conf filen till User: apache Group: apache Apache är namnet på min nya användare i operativsystemet. Nu startar jag apache som inloggad på apache-användaren och test-kör programmet med kodraden: echo `whoami`; Och får då resultatet: Se fig 2. Fig 2. Screenshot som bekräftar att apache nu körs som apache (min guest-användare). Så nu vet vi att apache inte körs som admin/root, utan som en användare med begränsade rättigheter i filsystemet. Vips så har vi skyddat oss en aning mot skadliga användare och attacker. Med dessa inställningar går det inte ens att starta apache om jag är inloggad som root/admin. Se fig 3. 6

Internet Applications, ID1354. Non-functional requirements Fig3. Screenshot på felmeddelande som visas vid försök att starta apache som root. För att begränsa http-access till mina php-klasser har jag konfigurerat httpd.conf (apachekonfigurationsfilen) ytterligare. Se fig 4. Fig4. Kodutdrag från apache-konfigurationsfilen där access till alla directory som heter classes i min serverroot är förbjudet. Jag använder databas-verktyget phpmyadmin för min databas, alltså inga separata filer så att förbjuda access till separata filer genom att ändra i min.htaccess fil är inte nödvändigt. Däremot har jag försökt stoppa andra användare att ha möjligt att accessa min databas se Fig5. Fig5. Kodutdrag, syftet med denna kodsnutt är att endast min IP-address ska ge tillåtelse att accessa localhost/phpmyadmin. Dock har jag inte fått det att fungera. Även min egen IPaddress blockas!, vilket lett att jag har kommenterat bort ovanstående kod. Input filtering: En viktig regel inom nätverksprogrammering: Lita aldrig på något som kommer ifrån klienten. Data som kommer från klienten måste alltid valideras (vi kan ju omöjligt på förhand veta om användaren är fredlig eller en illvillig attackerare). 7

Internet Applications, ID1354. Non-functional requirements Både server-sida och vy-sida validering är att önska. Server-sida validering är den som ger störst trovärdighet och säkerhet, men klient-sida validering är nyttigt för att förbättra prestandan! Om vi upptäcker ogiltiga parametrar utan att anropa servern så har vi tjänat tid. Jag har infogat både klient-sida validering och server-sida validering men just i detta stycke redovisar jag endast server-sida valideringen, klient-sida validering kan du hitta under rubriken Ej obligatoriska uppgifter: prestanda. För att validera användar input på servern så har jag skapat 2st metoder för att validering. Den första metoden kollar så att inget av datat är tomt (=null). Den andra metoden kollar så att datan bara innehåller giltiga symboler. Detta görs med olika typer av ctype_ funktioner beroende på vilken data som valideras. T.ex. Vid validering av username, password och nickname (som användaren anger vid registrering) Så används ctype_alnum() för password och username. Endast bokstäver och siffror är giltigt i username/password. Och ctype_alpha används för nickname. Endast bokstäver är giltigt i nickname. Se Fig6. Fig6. Kodutdrag för att visa funktioner för input-validering på serversidan. Just detta exemplet är för validering av username, password och nickname. Dessa funktioner anropas direkt i server-sida vyn innan någon annan aktion sker. se fig 7. 8

Internet Applications, ID1354. Non-functional requirements Fig7. Kodutdrag för att visa doexecute i server-sida vyn. Först kollas user_exists om användarnamnet redan är upptaget. Är det upptaget rendera error-sida. Sedan kollas om inget formulär är tomt med formsempty() sedan kollas att all data bara innehåller korrekta tecken om något formulär är tomt eller innehåller ogiltig data rendera error sida. Ifall datan klarar alla dessa tre valideringar så startas en process för att registrera användaren. $bool används i detta fallet endast som en data som skickas med vid felhantering för att avgöra vilken typ av errorsida som ska genereras. Password encryption Eftersom min hemsida har funktioner för registrering och inloggning för gemene användare så ställer det uppenbara krav på mig som utvecklare att använda en stark kryptering, lösenorden ska inte finnas tillgängliga i klartext för någon (inte ens mig!) utöver användaren. Inför tidigare seminarium har jag använt hash-algoritmen md5 för att kryptera lösenord innan de sparas i databasen. Men eftersom Leif upplyst oss om att md5 faktiskt är så snabb att det blivit möjligt att testa alla möjliga original-lösenord för att jämföra hash-nyckeln och att php 5.5. (den version jag använder) erbjuder ett hashing-api som är enkelt att använda och starkare kryptering än md5 så har jag bytt till den krypteringen istället. För att kryptera lösenorden som användaren matar in så används funktionen password_hash($pw, PASSWORD_DEFAULT); 9

Internet Applications, ID1354. Non-functional requirements Där $pw är användarens lösenord. PASSWORD_DEFAULT specifierar till php-motorn att default-hashing algoritmen ska användas. Se fig 8. Fig8. Kodutdrag för att visa krypteringen av lösenord innan användare registreras i databasen. Kodutdraget är från integrations-namespace och klassen som hanterar användarregistrering. Resultatet av denna hash-funktion blir det nya hashade-värdet för lösenordet och det är det värdet som sparas i databasen! Alltså även om någon mot förmodan skulle lyckas göra en attack och ta sig in i vår databas så finns där endast långa hash-värden. Side-note: Min table i databasen hade tidigare max-längden av 32 symboler men har nu ändrats till 255 för att få plats med de stora hash-värden som krypteringsfunktionen genererar. Denna nya kryptering medför även att ny kod måste införas i php-klassen som hanterar inloggning. För att verifiera att användarens angivna lösenord vid inloggning stämmer överens med hash-värdet i databasen så används funktionen: password_verify($password, $strhash) Funktionen tar original-lösenordet och det hashade värdet och jämför. Denna funktion anropas alltså i min php-klass som hanterar inloggning efter att hash-värdet i lösenordet som stämmer överens för användarnamnet hämtats. Se Fig9. 10

Internet Applications, ID1354. Non-functional requirements Fig9. Kodutdrag för att visa login-funktionen i php-klassen för inloggning som finns i integrationslagret. Funktionen hämtar användarens krypterade lösenord från databasen (om det finns någon användare med det användarnamnet dvs. annars returnerar vi false) Sedan jämförs det hashade värdet med originallösenordet mha. Funktionen password_verify(). Cross Site Scripting Cross site scripting betyder att en användare injekterar HTML-kod som sedan syns i en okänds användares webb-läsare utan vidare validering. HTML-koden injekteras oftast genom någon slags input-tjänst som webbsidan erbjuder användaren. T.ex. kommentars-funktionen eller inloggningsfunktionen som sidan för Tasty Recipes erbjuder. Några grundläggande regler som bör följas för att skydda sig mot Cross Site Scripting är: Infoga aldrig data någonstans i ett <script> element. Infoga aldrig data i ett HTML dokument Infoga aldrig data i ett attribut namn Infoga aldrig data i ett atttribut värde Infoga aldrig data i ett tag-namn Infoga aldrig data nånstans i css eller i en <style> tagg. Dessa regler följs i mitt program så därför är det framförallt i användar-input som sedan kan införas i mitt HTML-dokument som jag måste vara vaksam mot Cross Site Scripting. För att skydda mig mot detta använder jag funktionen: 11

Internet Applications, ID1354. Non-functional requirements htmlentities($msg, ENT_QUOTES); Funktionen tar någon typ av data ( i detta exempel en variabel som innehåller en kommentar för recept-sidorna) och gör om special-symboler som : < > & till entities (&lt, &amp osv.). Denna funktion används i alla php-klasser som tar emot något typ av användar-data. Se Fig 10. Fig 10. Kodutdrag för att visa hur jag skyddar mig mot Cross Site Scripting. Här i klassen NewMeatballComment i serversida-vyn. Impersonation (utge sig för att vara någon annan) För att skydda oss mot identitetsbedrägeriger så räcker det inte med att lösenorden är starkt krypterade i databasen, vi måste även försäkra oss om att lösenorden inte kan hijackas när de skickas från vyn till servern i klartext (innan vi hunnit kryptera!). För att försäkra sig om att de skickas på ett säkert sätt så bör de alltid skickas över protokollet https. Https är ett protokoll för krypterad transport av data för http-protokollet. Om vi inte använder https så skickas lösenordet i klartext och vem som helst med tillgång till kommunikationslänken kan se det! En sådan attack kallas för eavesdropping. HTTPS bör användas för alla requests som sker av en autensierad användare. Exempel i Fig 11. 12

Internet Applications, ID1354. Non-functional requirements Fig 11. Kodutdrag för att visa en typisk request som bör skickas över https. Detta formulär kommer skicka en request (som hamnar i index.php och matchar /userlog) med POSTparametrarna Username och Password. Speciellt password får ju självklart inte skickas i klartext och därför behövs https. EJ obligatoriska uppgifter: Prestanda AJAX Jag har implementerat AJAX i mina kommentarsfunktioner på receptsidorna. Egentligen är det väldigt lite ny-kod eftersom att jag använder ungefär samma javascript kod som för seminarium 2. Dvs: Jag använder ramverket KnockOut för att strukturera en vymodell som har som uppgift att rapportera till vyn när ändringar sker. Detta ger oss den MVVM struktur som vi efterstävar. Sidan behöver inte laddas om för att uppdateras. Jag tänkte inte gå in så mycket på att förklara vy-modellen då det finns förklaringar i rapporten från seminarium2. Det nya inför detta seminarium är som sagt att jag använder AJAX och JSON för att spara och hämta kommentarer från servern. Kommentarerna finns alltså inte bara sparade i vår vymodell (Om sidan laddas om så finns alla kommentarer kvar), Det som sker när data uppdateras är att vi skickar endast status-uppdateringar från servern till vyn, vi skickar inte om hela sidan. Vy-modellen kommer därför alltid att innehålla applikationens nuvarande tillstånd, och det fiffiga här är webbläsar-vyn reflekterar ju vy-modellens tillstånd mha. Observer-mönstret som KnockOut erbjuder, med bl.a. observablearray som används för att observera en kollektion av något slag. Det som händer när användaren trycker på send för en kommentar är att vi hamnar i JavaScript metoden addentry (tack vara data-bindning på Send-knappen). 13

Internet Applications, ID1354. Non-functional requirements I addentry så uppdateras vy-modellen med den nya kommentaren (Vi inför nytt värde i observablearray: entries inuti viewmodel). Men utöver det så skapas JSON-objekt av username och comment. Som sedan skickas som en POST-request till servern (precis som alla andra request hamnar den i index.php och sedan sker samma procedur för att spara kommentaren i databasen som skedde i mitt program till seminarium 3 (utan AJAX/JSON)). Se Fig 12 & 13. Fig12. Kodutdrag för att visa metoden addentry. Vymodellen uppdateras med ett objekt av Comment (Hela metoden addentry finns i objektet Comment, se rapport för seminarium 2 om det är oklart) och sedan skickas en AJAX-post request till servern och databasen uppdateras med username och comment. Utan att ens kontakta servern så kommer ju webbläsar-vyn nu att uppdateras mha. Foreachloopen i HTML-koden som itererar genom min observable-array, vyn uppdateras alltså utan att behöva ladda om sidan. Enda skillnaden i AJAX-POST requesten tillskillnad från den vanliga POST-requesten jag använde i seminarium3 är: $author = str_replace('"', "", $author); $msg = str_replace('"', "", $msg); Jag har ingen tidigare erfarenhet av att använda AJAX/JSON så det kanske finns ett smidigare sätt att lösa detta, men detta är iallafall min lösning på att JSON-objekten som skickas med som POST parametrar hamnar inom citationstecken. Ex: Kim lösenord Därför kör jag str_replace metoden innan jag sparar något i databasen. Fig 13. Kodutdrag för att visa routen i index.php som matchas med ajax/jquery metoden $.post(). 14

Internet Applications, ID1354. Non-functional requirements I och med att allt sparas på servern så har även metoden för att radera kommentarer uppdaterats. Se fig 14. Fig 14. Kodutdrag för att visa removeentry metoden. En annan stor nyhet i vy-modellen är getnewentries, getnewentries är en funktion för att göra ett AJAX-get request till servern för att hämta kommentarerna från databasen. Denna funktion anropas varje gång någon manuellt laddar om sidan eftersom att om vi laddar om sidan så nollställs vy-modellen. Se Fig 15 och 16. Fig 15. Kodutdrag för att visa hur funktionen getnewentries används. 15

Internet Applications, ID1354. Non-functional requirements Fig 16. Kodutdrag för att visa min vy-modell samt funktionen getnewentries. getnewentries gör en AJAX-get request och en route för /newcomments matchas i index.php. requesten kommer sedemera att resultera i ett anrop till controllern och sedan ner till integrationsnamespace för att hämta alla kommentarer och spara dem i en array som returneras. Se fig 17,18 och 19. När arrayen har returnerats så exekveras callback-funktionen som itererar genom arrayen som hämtats från servern och uppdaterar vymodellen (observablearray). Vilket ju leder till att även webbläsar-vyn uppdateras. Notera att observablearray bara uppdateras om kommentaren i fråga har ett större id än self.id i vymodellen, om så är fallet så uppdateras även self.id. Detta betyder att vymodellens variabel id alltid kommer att vara id för den sista kommentaren som syns i webbläsaren. Vad är syftet med detta då? Jo ifall en användare vill ladda in nya kommentarer utan att behöva ladda om sidan så finns det en <button> i användargränssnittet som har databindning till funktionen getnewentries. Och ifall användaren vill ladda in nya kommentarer så behöver vi ju bara ladda om de kommentarer som inte redan syns i webbläsarens-vy. Standardvärdet för self.id är således = 0. Vilket gör att så fort en användare refreshar eller besöker sidan så laddas alla kommentarer in. 16

Internet Applications, ID1354. Non-functional requirements Snabb repetition från min förra rapport: varje entry i mina tables innehållandes pancakescomments/meatballscomments har 3st nycklar username, comment, commentid. Där commentid är primary-key och har autoincrement. Alltså: Den senaste kommentaren kommer alltid ha det högsta comment_id värdet. Fig17. Kodutdrag för att visa anropen till följd av AJAX-get requesten. returnjson är en funktion i klassen AbstractExecutor som är inspirerad av Leifs exempelprogram. Funktionen är väldigt simpel och använder bara php-funktionen json_encode. Detta kodutdrag är från klassen getnewcomments i serversida-vyn. Fig18. Kodutdrag för att visa anropen till följd av AJAX-get requesten. Detta kodutdrag är från controllern. 17

Internet Applications, ID1354. Non-functional requirements Fig 19. Kodutdrag för att visa metoden som skapar och returnerar en array med alla kommentarer som finns i databasen och som inte redan finns uppladdade i webbläsarvyn. Detta kodutdrag är från klassen comments i integrationslagret. Som syns i dessa bilder till resultat av AJAX-post anropet så har alla en inparameter: id. ID är som sagt värdet på den sista kommentaren som finns i webbläsar-vyn. Vi kanske t.ex. har 5st kommentarer i webbläsarvyn. Och medans vi läser dessa kommentarer så har någon annan som är inne och kikar på sidan postat en ny kommentar, så det finns nu 6 kommentarer i databasen. När vi då trycker på knappen getnewentries så sker anropet ner till servern med inparameter : comment_id för den sista kommentaren (vi kan säga att ID är = 5) När anropet kommit till integrationslagret och nya kommentarer ska hämtas från databasen så itereras våran table i databasen och vi jämför ID med de olika comment_id för kommentarerna i databasen resultatet blir givetvis att endast 1 kommentar har större id än 5, och det betyder att endast en kommentar behöver skickas till vyn. Client-side validation Användarens input-parametrar valideras som vi sett tidigare i rapporten noggrant på serversidan, men det sker även en javascript-validering på klientsida. Detta är för att förbättra prestandan, om vi kan upptäcka invalida parametrar redan på klient-sidan så slippper vi göra anrop ner till servern i onödan, vilket leder till bättre prestanda och snabbare svarstid. Se fig 20. 18

Internet Applications, ID1354. Non-functional requirements Fig 20. Kodutdrag för att visa valideringen i metoden addentry, om denna validering godkänns så uppdaterar vi vy-modelllen och sedan gör vi anrop till servern för att uppdatera databasen. Ifall valideringen inte godkänns så händer ingenting förutom en console.log();, alltså är datan invalid så sker inget serveranrop, och vi sparar in requests bättre prestanda. För att se hela metoden addentry se fig 12. Caching Caching är en väldigt central del i en hemsidas prestanda. Min webbserver har en e-tag specifierad i headern för alla bilder, css och dylikt som default. Se Fig21. Fig 21. Screenshot för att visa att E-tag för en av mina bilder på sidan är satt. ETag är en mekanism som http erbjuder för att cacha hemsidor. Det går till så att Etagen och hemsidan kommer cachas och nästa gång vi gör en request för samma URL så kan cachen identifiera oss genom Etagen och på så sätt får vi en cachad hemsida. Se Fig 22. 19

Internet Applications, ID1354. Non-functional requirements Fig 22. Screenshot för att visa att alla requests hämtas från cachen förutom själva sidan (den högst upp), alla bilder, css osv cachas alltså men inte själva sidan. Om man kollar i headern på den requesten så ser vi: fig 23 Fig 23. Screenshot av Response Headern för alla sidor i mitt program. Som vi ser i bilden så finns det ingen cache-controll. Genom att definiera headern i koden kan man ändra response headern ex: Ger resultatet: 20

Internet Applications, ID1354. Non-functional requirements På så sätt kan man modifiera hur webbläsarens cachning ska gå till. Jag anser att min cachning är stabil och förbättrar prestandan avsevärt då bilder, css och javascript filer (som sällan ändras) cachas för en lång tid. Se fig 24. Fig 24. Kodutdrag för att visa en kodsnutt i min.htaccess fil som sätter expiration-date för bilder, css och javascript b.la. Jag fick inspiration och ide n från http://magentohostsolution.com/30-popular-htaccess-code-snippets/ Ifall jag rensar min webbläsares cache så kommer naturligtvis alla filer hämtas från servern, men så fort vi hämtat sidan en gång från servern så finns majoriteten av filerna sparade i webbläsarens-cache se fig22, vilket går snabbt att hämta! Se fig 25,26 för detaljer 21

Internet Applications, ID1354. Non-functional requirements Fig24. Lite prestanda-fakta hur min laddning av Calendersidan ser ut när jag har en tom cache. 451KB 1.92s. Fig25. Lite prestanda-fakta hur min laddning av Calendersidan ser ut när jag har en laddad cache. 6.3KB 810ms. Bilderna ovan illustrerar hur central cachning är vad det gäller prestandan. 22