Eksempler
>IFA
IFA
Løsningen er prinsippielt slik:
Dataflyt IFA
Det er 6 brukerfunksjoner som er implementerte. En oversikt over funksjon og respons:
(Re)load siden | Hent og vis alle ordtak |
Registrer nytt ordtak | Hent og vis alle ordtak |
Stem på et ordtak | Hent og vis alle ordtak |
Se komentarene for et ordtak | Hent og vis det aktuelle ordtaket med kommentarer |
Skjul komentarene for et ordtak | Hent og vis det aktuelle ordtaket uten kommentarer |
Registrer en kommentar for et ordtak | Hent og vis det aktuelle ordtaket med kommentarer |
Javascript
Javascript-koden som går på vevsiden er slik:
var myRequest; var busy=false; var currentSID=1; var scriptpath= 'http://www.it.hiof.no/~borres/cgi-bin/ajax/ifax/ajaxscript.py'; //------------------------------------------------------- // general attempt to perform a request, if none is busy already function getRequestObject() { if(busy) return; if (window.XMLHttpRequest) myRequest = new XMLHttpRequest(); else if (window.ActiveXObject) { try { myRequest = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) { try {myRequest= new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) {myRequest=null;} } } else myRequest=null; } //----------------------------------------- // add a dummy random parameter to force refresh // in Internet Explorer function addXtra(params) { var now=new Date(); s='&xtra='+now.getMinutes()+''+now.getSeconds()+''+now.getMilliseconds(); return params+s; } //----------------------------------------------------------- // controlling and formatting parameterlists: function constructSloganParams(slogan,author) { if (slogan.length<10) { alert("Du må lage et skikkelig ordtak"); return ''; } if(author.length==0) { alert("Du må fylle ut hvem som har laget ordtaket"); return ''; } // we will use them slogan=slogan.replace(/\r\n/g,'\n'); slogan=slogan.replace(/\n/g,'<br/>'); var par='what=newslogan&slogan=' +escape(slogan)+'&author='+escape(author); return par; } function constructCommentParams(comment,author,sid) { if (comment.length<2) { alert("Du må lage en skikkelig kommentar"); return ''; } if(author.length==0) { alert("Du må fylle ut hvem som har laget kommentaren"); return ''; } // we will use them comment=comment.replace(/\r\n/g,'\n'); comment=comment.replace(/\n/g,'<br/>'); var par='what=newcomment&comment='+ escape(comment)+'&author='+escape(author)+'&slogan='+sid; return par; } //------------------------------------------- // process returns from requests // place the whole slogan package function prosessAllSlogans() { // if the request is complete and successfull if (myRequest.readyState == 4) { if ((myRequest.status == 200) || (myRequest.status == 304)) { elt=document.getElementById("allslogans"); elt.innerHTML=myRequest.responseText; } else alert("Problem med tilgang til data:\n" + myRequest.statusText); busy=false; } } // place one slogan function prosessOneSlogan() { // if the request is complete and successfull if (myRequest.readyState == 4) { if ((myRequest.status == 200) || (myRequest.status == 304)) { elt=document.getElementById("slogan"+currentSID); elt.innerHTML=myRequest.responseText; } else alert("Problem med tilgang til data:\n" + myRequest.statusText); busy=false; } } //------------------------------------ // functions and requests // request the whole sloganpackage // using get function loadAllSlogans() { getRequestObject(); if (myRequest) { myRequest.onreadystatechange = prosessAllSlogans; params='what=allslogans'; params=addXtra(params); myRequest.open("GET", scriptpath+'?'+params, true); myRequest.send(null); busy=true; } else{ alert("Nettleseren henger ikke med"); } } // register a new slogan and request the whole slogans package // using post function handleNewSlogan(form) { var params= constructSloganParams(form.new_slogan.value, form.new_slogan_author.value); if (params.length==0) return; getRequestObject(); if (myRequest) { myRequest.onreadystatechange = prosessAllSlogans; params=addXtra(params); myRequest.open("POST",scriptpath , true); myRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); myRequest.setRequestHeader("Content-length", params.length); myRequest.setRequestHeader("Connection", "close"); myRequest.send(params); busy=true; } else{ alert("Nettleseren henger ikke med"); } } //----------------------------------------- // control voting, only one vote pr slogan // using get function handleVote(sid) { // only one vote pr slogan elt=document.getElementById("voted"); v=elt.innerHTML; mark=("("+sid+")") if (v.indexOf(mark) != -1) { alert("Du har allerede stemt på denne"); return; } elt.innerHTML=v+mark; getRequestObject(); if (myRequest) { myRequest.onreadystatechange = prosessAllSlogans; params='what=vote&slogan='+sid; params=addXtra(params); myRequest.open("GET", scriptpath+'?'+params, true); myRequest.send(null); busy=true; } else{ alert("Nettleseren henger ikke med"); } } //------------------------------------------ // expand and collapse comments, get the actual slogan // using get function expandComment(sid) { getRequestObject(); if (myRequest) { myRequest.onreadystatechange = prosessOneSlogan; params='what=expand&slogan='+sid; params=addXtra(params); currentSID=sid; myRequest.open("GET", scriptpath+'?'+params, true); myRequest.send(null); busy=true; } else{ alert("Nettleseren henger ikke med"); } } function collapseComment(sid) { getRequestObject(); if (myRequest) { myRequest.onreadystatechange = prosessOneSlogan; params='what=collapse&slogan='+sid; params=addXtra(params); currentSID=sid; myRequest.open("GET", scriptpath+'?'+params, true); myRequest.send(null); busy=true; } else{ alert("Nettleseren henger ikke med"); } } // register a new comment and request the commented slogan // using post function handleNewComment(form,sid) { params= constructCommentParams(form.new_comment.value, form.new_comment_author.value,sid); if (params.length==0) return; getRequestObject(); if (myRequest) { myRequest.onreadystatechange = prosessOneSlogan; params=addXtra(params); currentSID=sid; myRequest.open("POST",scriptpath , true); myRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); myRequest.setRequestHeader("Content-length", params.length); myRequest.setRequestHeader("Connection", "close"); myRequest.send(params); busy=true; } else{ alert("Nettleseren henger ikke med"); } }
Det er flere ting å merke seg med skriptet:
-
Det bruker både GET og POST.
En GET er som vanlig, etter at vi har preparert parameterlista:myRequest.open("GET", scriptpath+'?'+params, true); myRequest.send(null);
En POST er slik, etter at vi har preparert parameterlista:myRequest.open("POST",scriptpath , true); myRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); myRequest.setRequestHeader("Content-length", params.length); myRequest.setRequestHeader("Connection", "close"); myRequest.send(params);
Det spiller ingen rolle for Pythonskriptet på serveren hvilken overføringsmetode vi bruker. -
Parametrene kodes med escape()
Det vi oppnår med dette er at alle ikke-ascii tegn kodes i formen %nn hvor nn er et 2-sifret hexadesimalt tall. Denne formen fører til at vi ikke får noen misforståelser med hva som er hva i parameterlista. Denne kodingen er nødvendig i dette eksempelet fordi vi skal kunne overføre norske tegn. - Vi legger til en "random" parameter, function addXtra(params). Vitsen med dette er å hindre Internet Explorer å hente den gamle cashede versjonen av siden.
- Vi handterer kun en request om gangen. Dette forenkler logikken på siden. Vi kan f.eks. huske et ordtaksnummer fra forespørselen er gitt til vi får svaret. I dette tilfellet er dette neppe noen begrensning i forhold til brukeren.
-
Vi husker hvem vi har stemt på. Nederst på siden ligger et skjult felt som oppdateres fra Javascriptet:
<div id="voted" style="display:none">Stemt på: </div>
-
Vi bruker regulære uttrykk i replace- for å erstatte alle forekomster at en tegnesekvens.
slogan=slogan.replace(/\r\n/g,'\n'); slogan=slogan.replace(/\n/g,'<br/>');
Pythonskriptet
Pythonskriptet på tjeneren er slik:
#! /usr/bin/python2.5 # -*- coding: latin_1 -*- import MySQLdb,cgi,urllib,datetime import cgitb; cgitb.enable() """ Retrieve and update database diverse. xslogan slogan_id BIGINT PRIMARY KEY, author TEXT poem TEXT votes INT xcomment comment_id INT PRIMARY KEY AUTO_INCREMENT, author TEXT words TEXT sid BIGINT """ RESULT_FILE='c:\\projects\\ifa\\result.html' HTML_PAGE="""<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <head> <link href="ifastyles.css" rel="STYLESHEET" /> <title>ifa contest</title> </head> <body> <h1>Results</h1> %s </body> </html> """ #------------------------------------- # HTML fragments HTML_ALL_SLOGANS_WRAPPER=""" <div> %s </div> """ HTML_SLOGAN_BLOCK_WRAPPER=""" <div id="slogan%s" class="one-slogan"> %s </div> """ HTML_SLOGAN_BLOCK_SHOW=""" <fieldset> <legend> <span class="bigvotes">%s</span> <span class="smallvotes">stemmer</span> </legend> <div class="slogan"> %s </div> <div class="slogan-author"> %s </div> <div style="text-align:right"> <input class="vote-button-text" type="submit" value="Stem p� denne" onclick="handleVote('%s');return false;"/> <input class="comment-button-text" type="submit" value="Vis kommentarer" onclick="expandComment('%s');return false;"/> </div> <!-- kommentarer --> <div id=comments%s> %s </div> </fieldset> """ HTML_SLOGAN_BLOCK_HIDE=""" <fieldset> <legend> <span class="bigvotes">%s</span> <span class="smallvotes">stemmer</span> </legend> <div class="slogan"> %s </div> <div class="slogan-author"> %s </div> <div style="text-align:right"> <input class="vote-button-text" type="submit" value="Stem p� denne" onclick="handleVote('%s');return false;"/> <input class="button-text" type="submit" value="Skjul kommentarer" onclick="collapseComment('%s');return false;"/> </div> <!-- kommentarer --> <div id=comments%s> %s </div> </fieldset> """ HTML_MAKE_NEW=""" <a name="newslogan"> </a> <div class="make-new-area" style="margin-top:40px"> <fieldset> <legend><span class="new-slogan-header">Lag et nytt slagord</span></legend> <form action="#"> <div class="new-slogan"> <div> <textarea name="new_slogan" cols="60" rows="6"></textarea> </div> <div> Fra: <input type="text" name="new_slogan_author" size="15"/> <input class="button-text" type="submit" value="Send inn" onclick="handleNewSlogan(this.form);return false;"/> </div> </div> </form> </fieldset> </div> """ HTML_COMMENT_BLOCK=""" <div class="comment"> <div class="comment-author"> %s: </div> <div class="comment-words"> %s </div> </div> """ HTML_COMMENT_BLOCK_WRAPPER=""" <div id="comments%s"> <fieldset> <legend>Kommentarer</legend> %s </fieldset> </div> """ HTML_NEW_COMMENT=""" <form action="#"> <div class="new-comment"> <div> Din kommentar:<br/> Fra: <input type="text" name="new_comment_author" size="15"/> </div> <div> <textarea name="new_comment" cols="40" rows="2"></textarea> <input class="button-text" type="submit" value="Send inn" onclick="handleNewComment(this.form,'%s'); return false;"/> </div> </div> </form> """ #---------------------------------------- # SQL statements SQL_ONE_SLOGAN="""SELECT * FROM xslogan WHERE slogan_id =%s;""" SQL_UPDATE_SLOGAN_VOTES="""UPDATE xslogan SET votes=%d WHERE slogan_id=%s;""" SQL_INSERT_SLOGAN="""INSERT INTO xslogan(slogan_id,author,poem,votes ) values('%s','%s','%s',1); """ SQL_ALL_SLOGANS="""SELECT * FROM xslogan ORDER BY votes DESC;""" SQL_COMMENTS_FOR_SLOGAN="""SELECT * FROM xcomment WHERE sid=%s; """ SQL_INSERT_COMMENT="""INSERT INTO xcomment(sid,author,words) values(%s,'%s','%s');""" #------------------------------------------------ # connect and execute a sql-request #------------------------------------------------ def connectAndExecute(sql): try: conn=MySQLdb.connect(host='frigg.hiof.no', user='student', passwd='student', db='bsdiverse') cursor=conn.cursor() cursor.execute(sql) result_tab=cursor.fetchall() conn.commit() cursor.close() conn.close() return result_tab except MySQLdb.Error, e: print "Error %d: %s" % (e.args[0], e.args[1]) return None #--------------------------------------------------- # Update the votes for one slogan #--------------------------------------------------- def updateSloganVotes(sid): result=connectAndExecute(SQL_ONE_SLOGAN%sid) votes=int(result[0][3])+1 result=connectAndExecute(SQL_UPDATE_SLOGAN_VOTES%(votes,sid)) #----------------------------------------------------- # We have a comment for a slogan, insert it in comments #------------------------------------------------ def insertComment(sid,comment,commentator): connectAndExecute(SQL_INSERT_COMMENT%\ (sid,urllib.quote(commentator),urllib.quote(comment))) #----------------------------------------------------- # We have a new slogan, insert it with one vote #------------------------------------------------ def insertSlogan(slogan,author): d=datetime.datetime.now() S='%04d%02d%02d%02d%02d%02d'%\ (d.year,d.month,d.day,d.hour,d.minute,d.second) slogan=slogan.replace('\r\n','\n') slogan=slogan.replace('\n','<br/>') result=connectAndExecute(SQL_INSERT_SLOGAN%\ (S,urllib.quote(author),urllib.quote(slogan))) #-------------------------------------------------- # produce one slogan, with or without comments # this block is without the outer <div id="sloganid"> # without: HTML_SLOGAN_BLOCK_WRAPPER #-------------------------------------------------- def getOneSlogan(sloganId,commentWanted): row=connectAndExecute(SQL_ONE_SLOGAN%sloganId) if row==None: return 'Feil i data' resultTxt='' sid=str(row[0][0]) author=urllib.unquote(str(row[0][1])) poem=urllib.unquote(str(row[0][2])) votes=str(row[0][3]) comments='' if commentWanted: comments=getAllComments(sloganId) resultTxt=HTML_SLOGAN_BLOCK_HIDE%\ (votes,poem,author,sid,sid,sid,comments) else: resultTxt=HTML_SLOGAN_BLOCK_SHOW%\ (votes,poem,author,sid,sid,sid,comments) return resultTxt #--------------------------------------------------- # produce a list of all slogan blocks # Each idetified by the outer <div id="sloganid"> # HTML_SLOGAN_BLOCK_WRAPPER #--------------------------------------------------- def getAllSlogans(commentedId): result=connectAndExecute(SQL_ALL_SLOGANS) if result==None: return 'Feil i data' resultTxt='' for row in result: sid=str(row[0]) author=urllib.unquote(str(row[1])) poem=urllib.unquote(str(row[2])) votes=str(row[3]) comments='' aSlogan='' if commentedId == sid: comments=getAllComments(commentedId) aSlogan=HTML_SLOGAN_BLOCK_HIDE%\ (votes,poem,author,sid,sid,sid,comments) else: aSlogan=HTML_SLOGAN_BLOCK_SHOW%\ (votes,poem,author,sid,sid,sid,comments) resultTxt+=HTML_SLOGAN_BLOCK_WRAPPER%(sid,aSlogan) resultTxt= HTML_ALL_SLOGANS_WRAPPER%resultTxt resultTxt+=HTML_MAKE_NEW return resultTxt #--------------------------------------------------- # produce a list of all comments for a slogan # including the identifying outer div # HTML_COMMENT_BLOCK_WRAPPER #--------------------------------------------------- def getAllComments(sid): result=connectAndExecute(SQL_COMMENTS_FOR_SLOGAN%sid) if result==None: resultTxt=HTML_NEW_COMMENT%sid return resultTxt resultTxt='' if len(result)==0: resultTxt='Ingen komentarer' else: for row in result: author=urllib.unquote(str(row[1])) com=urllib.unquote(str(row[2])) aComment=HTML_COMMENT_BLOCK%(author,com) resultTxt+=aComment resultTxt+=HTML_NEW_COMMENT%sid resultTxt= HTML_COMMENT_BLOCK_WRAPPER%(sid,resultTxt) return resultTxt #------------------------------------------------- # what is the job ? #------------------------------------------------- form=cgi.FieldStorage() print 'Content-type: text/html\n' RESULT_TXT='' if form.has_key('what'): what=form['what'].value if what=='allslogans': RESULT_TXT=getAllSlogans(-1) elif what=='newslogan': slogan='slogan' author='author' if form.has_key('slogan'): slogan=form['slogan'].value if form.has_key('author'): author=form['author'].value insertSlogan(slogan,author) RESULT_TXT=getAllSlogans(-1) elif what=='vote': sid='' if form.has_key('slogan'): sid=str(form['slogan'].value) updateSloganVotes(sid) RESULT_TXT=getAllSlogans('-1') elif what=='expand': sid='' if form.has_key('slogan'): sid=str(form['slogan'].value) RESULT_TXT=getOneSlogan(sid,True) elif what=='collapse': sid='' if form.has_key('slogan'): sid=str(form['slogan'].value) RESULT_TXT=getOneSlogan(sid,False) elif what=='newcomment': comment='comment' author='author' sid='' if form.has_key('comment'): comment=form['comment'].value if form.has_key('author'): author=form['author'].value if form.has_key('slogan'): sid=str(form['slogan'].value) insertComment(sid,comment,author) RESULT_TXT=getOneSlogan(sid,True) else: RESULT_TXT='ikke implementert' else: RESULT_TXT='vet ikke hva' print RESULT_TXT
Merk følgende:
- Skriptet produserer HTML-fragmenter som inneholder kode som i sin tur initierer nye forespørsler når de klikkes. Se f.eks. variabelen: HTML_SLOGAN_BLOCK_SHOW.
- Det er et problem med tegnkoding og databasen. Problemet er "korsluttet" ved å bruke urllib.quote() når data skal legges inn og urllib.unquote() når data hentes ut. Dette fører til at de dataene som ligger i databasen har erstattet alle ikke-asci tegn med %nn formatet, nn=2-sifret hexadesimalt tall. Ikke den vakreste av løsninger, men det virker.