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.