IFA
Det er mange måter å angripe dette på. Her er valgt å lage et javascript som sørger for at vi får sendt med en identifikasjon av knappen det er trykket på til tjeneren. Vi kan derfor ha en felles mottakksfunksjon, som er Page_Load, og så finne ut hva som har skjedd.
Javascriptet er slik:
function __doPostBack(eventTarget, eventArgument) { var theForm = document.forms['form1']; if (!theForm) { theForm = document.form1; } if (!theForm.onsubmit || (theForm.onsubmit() != false)) { theForm.__EVENTTARGET.value = eventTarget; theForm.__EVENTARGUMENT.value = eventArgument; theForm.submit(); } } function register(form) { var author=form.newauthor.value; var slogan=form.newslogan.value; if((author.length==0)||(slogan.length==0)) alert("Du må fylle inn både slagord og forfatter"); else __doPostBack('newslogan','') }
Skriptet forutsetter at følgende 2 "skjulte" inputfelter finnes i formen på siden:
<input code="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" /> <input code="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
Navngivingen er tilpasset den standarden .Net selv genererer når vi kopler opp en kontroll som f.eks. en listboks. Grunnen til av vi har brukt standarden er at den er kjent på tjenersiden. Det som skjer er altså at vi kaller __doPostBack med to parametre fra de knappene som skal formidle en stemme til et av slagordene, slik:
... <input code=""button"" id=""vote2"" value=""Stem på denne"" runat=""server"" onclick=""__doPostBack('vote2','1')"" /> ...
Funksjonen sørger for å sette verdier i de skjulte feltene og vi er sikre på at vi kan plukke opp disse på tjeneren. Funksjonen register kalles når brukeren ønsker å registrere et nytt slagord, og tar bare en minimalistisk inputkontroll før den samme mekanismen brukers til å sende tilsvarende data som identifiserer denne begivenhetene.
På tjenersiden plukker vi opp en postback slik:
protected void Page_Load(object sender, EventArgs e) { // we need to load the XML-file anyway String filename= String.Format(@"{0}\App_Data\Slogans.xml", Path.GetDirectoryName(Request.PhysicalPath)); // establish datastrucure DataAccess DataSource = new DataAccess(filename); if (IsPostBack) { // pick up cookies HttpCookieCollection cks = Request.Cookies; // what has happened ? a vote or a new slogan // we read what we need from postEventArgumentID // and postEventSourceID NameValueCollection formvar = Request.Form; String ButtonId = formvar[postEventSourceID]; String args = formvar[postEventArgumentID]; if (ButtonId.StartsWith("vote")) { String SloganId = ButtonId.Replace("vote", ""); // vote only once on each HttpCookie ck= cks["hasvoted"]; if ((ck == null)||(ck.Value!=SloganId)) { DataSource.AddVote(SloganId, 1); HttpCookie cook=new HttpCookie("hasvoted"); cook.Value=SloganId; Response.SetCookie(cook); } } else if (ButtonId.StartsWith("new")) { String NewSlogan = formvar["newslogan"]; String Author = formvar["newauthor"]; // submit only once HttpCookie ck = cks["hasmade"]; if ((ck == null) || (ck.Value != NewSlogan)) { DataSource.InsertSlogan(Author, NewSlogan); HttpCookie cook = new HttpCookie("hasmade"); cook.Value = NewSlogan; Response.SetCookie(cook); } } } // display all slogans within placeholder ControlCollection list = PlaceHolder2.Controls; HtmlGenericControl hc = new HtmlGenericControl("div"); hc.InnerHtml = DataSource.MakeSloganListSorted(); list.Add(hc); DataSource.Store(); }
DataLagring
Slagordene lagres i en enkel XML-fil som ser slik ut:
<?xml version="1.0" encoding="utf-8"?> <slogans nextid="15"> <slogan id="1" votes="7"> <content>IFA-esker i alle damevesker </content> <author>Lille Per</author> <date>2007-02-03</date> </slogan> <slogan id="2" votes="6"> <content>Alle i FIFA spiser IFA</content> <author>børre</author> <date>2007-2-11</date> </slogan> </slogans>
Dataene administreres slik, klassen DataAccess:
XmlDocument doc; String FileName; String LoadErrorMessage = ""; public DataAccess(String filename) { //load XML-file FileName = filename; try { doc = new XmlDocument(); doc.Load(FileName); } catch (Exception ex) { LoadErrorMessage = ex.Message; doc = null; } } public void InsertSlogan(String author,String slogan) { if (doc == null) return; // find next id XmlElement root = doc.DocumentElement; String nextId = root.GetAttribute("nextid"); XmlElement newSlogan=doc.CreateElement("slogan"); newSlogan.SetAttribute("id", nextId); XmlElement newauthor = doc.CreateElement("author"); newauthor.AppendChild(doc.CreateTextNode(author)); XmlElement content = doc.CreateElement("content"); content.AppendChild(doc.CreateTextNode(slogan)); XmlElement date = doc.CreateElement("date"); DateTime dt=DateTime.Now; date.AppendChild( doc.CreateTextNode( String.Format("{0}-{1}-{2}",dt.Year,dt.Month,dt.Day))); newSlogan.AppendChild(content); newSlogan.AppendChild(newauthor); newSlogan.AppendChild(date); newSlogan.SetAttribute("votes", "1"); root.AppendChild(newSlogan); root.SetAttribute("nextid", Convert.ToString(Convert.ToUInt16(nextId) + 1)); } public void AddVote(String sloganId,int d) { if (doc == null) return; String xpath = String.Format("//slogan[@id='{0}']", sloganId); XmlNodeList list = doc.SelectNodes(xpath); if (list.Count == 1) { int votes=Convert.ToUInt16( ((XmlElement)list[0]).GetAttribute("votes")); ((XmlElement)list[0]).SetAttribute("votes", Convert.ToString(votes + d)); } } public String MakeSloganListSorted() { if (doc == null) return String.Format(errorstring, LoadErrorMessage); XPathNavigator navigator = doc.CreateNavigator(); XPathExpression selectExpression = navigator.Compile("//slogan"); selectExpression.AddSort("@votes", XmlSortOrder.Descending, XmlCaseOrder.None, "", XmlDataType.Text); XPathNodeIterator nodeIterator = navigator.Select(selectExpression); StringBuilder Result = new StringBuilder(2000); while( nodeIterator.MoveNext() ) { XmlElement elt = (XmlElement) ((IHasXmlNode)nodeIterator.Current).GetNode(); String Author = elt.GetElementsByTagName("author")[0].InnerText; String Content = elt.GetElementsByTagName("content")[0].InnerText; String Id = elt.GetAttribute("id"); String Votes = elt.GetAttribute("votes"); Result.Append(String.Format(SloganEntry, Id, Content.Replace("\r\n","<br/>"), Votes, Author)); } return Result.ToString(); } // a simpler version public String MakeSloganListUnsorted() { if (doc == null) return String.Format(errorstring, LoadErrorMessage); XmlNodeList list = doc.GetElementsByTagName("slogan"); StringBuilder Result = new StringBuilder(2000); foreach (XmlElement elt in list) { String Author = elt.GetElementsByTagName("author")[0].InnerText; String Content = elt.GetElementsByTagName("content")[0].InnerText; String Id = elt.GetAttribute("id"); String Votes = elt.GetAttribute("votes"); Result.Append(String.Format(SloganEntry, Id, Content, Votes, Author)); } return Result.ToString(); } public void Store() { if (doc == null) return; // write to FileName XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; XmlWriter xr = XmlWriter.Create(FileName, settings); doc.WriteContentTo(xr); xr.Flush(); xr.Close(); }
Stringen som brukes til å formattere hvert slagord på siden er slik:
String SloganEntry = @" <div class=""oneslogan""> <fieldset style=""width: 340px""> <legend>{2} stemmer</legend> <div style=""margin:20px;color:blue;font-size:24px"">{1}</div> <div><span style=""margin-right:10px"">{3}</span> <input type=""button"" id=""vote{0}"" value=""Stem p� denne"" runat=""server"" onclick=""__doPostBack('vote{0}',{2})"" /> </div> </fieldset> </div> ";
Løsningen slik den foreligger har en rekke svakheter:
- Datalagringen burde kanskje vært gjort i en database ?
- Vi burde hatt muligheter for å legge inn kommentarer til forslagene, i tillegg til å stemme på dem
- Cookie handteringen er kanskje for streng
- Vi frisker opp siden komplett hver gang. Alternativer ?