lördag 30 januari 2010

Enkel text

Nu arbetar jag med att möjligheten att lagra enkla texter. I databasen ska det vara möjligt att lagra en rubrik (Headline), själva texten (Body), id på den som lagrat texten (Owner), utkast/privat/användare/alla (Readers?) och så de vanliga datumen (skapad, ändrad, borttagen).

Då användarna inte får skriva HTLM-kommandon eller annan kod så måste tecknet < översättas till <. ASP bryter dock programmet med larmar dock redan innan jag får möjlighet att göra översättningen: Serverfel i tillämpningsprogrammet /System9.

Det löser man dock med att lägga in direktivet validateRequest="false".

onsdag 27 januari 2010

Wordpress är bättre än Blogger

Nu har jag använt Blogger av och till samtidigt som jag använder Wordpress till min blogg om politik, mm. Och det är alldeles klart att Wordpress är avsevärt bättre när det gäller att formatera texten. Bloggers svaghet gör det besvärligt att plublicera programkod med speciellt typsnitt och färg. Den HTLM-kod som Blogger skapar är helt förfärlig. Märkligt att Google gör något så dåligt. Därmed inte sagt att Wordpress är perfekt vad gäller formgivningen, men skillnaden är avsevärd.

tisdag 26 januari 2010

Login(2)

Med den nuvarande versionen av Login.aspx går det att logga in i systemet nu, men systemet vet inte vem besökaren är. Ganska meningslöst med andra ord. Men det går att ändra på.

Men alla besökare får automatisk en identitet, en cookie, som systemet kan läsa. En cookie består av en text och några andra uppgifter. Texten kan se ut så här: mjtnde550hugrt2au4ycuk25.

Det första programmet bör göra, dvs i Page_Load, är att läsa cookien och sedan söka i databasen om det finns någon användare med denna cookie. Cookien finns i objektet HttpCookie som måste deklareras med satsen Dim cookie As HttpCookie. Denna deklaration görs lämpligen utanför Page_Load så att objektets innehåll är tillgängligt båda i Page_Load och proceduren Login1_Click som hanterar klick på loginformuläret.

I databasen finns cookien i fältet kaka som lagrar en sträng om 50 tecken. Databasen ska returnera användarens interna id och användarnamnet så SQL-satsen blir
SELECT id, kaka, username FROM [User] WHERE kaka='" & cookie.Value & "'".

Förbindelsen med databasen skapas med
Dim conn As SqlConnection
Dim connectionstring As String = ConfigurationManager.ConnectionStrings("System91").ConnectionString
conn = New SqlConnection(connectionstring)
Dim comm As SqlCommand
comm = New SqlCommand(sql, conn)

Själva läsningen görs inom Try... Catch. Eftersom flera uppgifter hämtas från databasen sker det med ExecuteReader().

Den centrala koden som utförs innan loginformuläret visas är följande

Dim cookie As HttpCookie
Dim Username As String = ""
Dim sql As String
Dim cookie As HttpCookie

If Not IsPostBack Then
cookie = Request.Cookies.Get("System9")
If cookie Is Nothing Then
Master.DisplayDataFromPage("")
else
sql = "SELECT id, kaka, username FROM [User] WHERE kaka='" & cookie.Value & "'"
Dim conn As SqlConnection
Dim connectionstring As String = ConfigurationManager.ConnectionStrings("System91").ConnectionString
conn = New SqlConnection(connectionstring)
Dim comm As SqlCommand
comm = New SqlCommand(sql, conn)
Try
conn.Open()
Dim reader As SqlDataReader = comm.ExecuteReader()
If reader.Read() Then
Userid = reader.Item("id")
Username = reader.Item("Username")
Master.DisplayDataFromPage(Username)
Else
Master.DisplayDataFromPage("Inte inloggad")
End If
Catch ex As SqlException
Message.Text += " Databasfel. " & ex.Number & ": " & ex.Message
Finally
conn.Close()
End Try
End If
End If

IsPostBack-variabeln informerar om användaren beställer samma sida igen. Här innebär kontrollen att Page_Load inte utförs igen när användaren klicka på loginformulärets knapp.
Med Master.DisplayDataFromPage uppdateras fält i MasterPage.DisplayDataFromPage är en procedure i MasterPage.vb som innehåller de satser som behövs för att uppdatera dessa fält. I detta fall ser proceduren ut så här:
Public Sub DisplayDataFromPage(ByVal message As String)
HeadUserName.Text = message
End Sub

Så har programmet utvecklats lite till. I nästa avsnitt tillkommer koden som tar hand om själva inloggningen och uppdaterar databasen med cookien.


torsdag 21 januari 2010

Menysystem och ny uppladdning

Det vanliga är att ha menyerna vertikalt till vänster. Det är nog inte alltför svårt att skriva den koden, men det finns en relativt vettigt objekt i ASP för hantering av menyn. Det heter SiteMapDataSource och det ska jag pröva.

Jag skapar ett utrymme för menyn med <div></div> och placerar "divven" till vänster med class="LeftMenuAbs". LeftMenuAbs är CSS-kommando som säger att menyn ska placeras 70 punkter från fönstrets överkant och 10 punkter från vänsterkanten.
.LeftMenuAbs
{ top: 70px; left: 10px; position: absolute;}

Innehållet placeras i en annan <div> med ContentAbs med 50px från överkanten och 150 punkter från vänster kant. Bredden är 600 punkter.
.ContentAbs
{top: 50px; left: 150px; position: absolute; width: 100%;}

Innehållet i menyn placeras i en XML-fil med namnet Web.sitemap. En första version ser ut så här:
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode url="" title="" description="">
<siteMapNode url="Default.aspx" title="Hem" description="" />
<siteMapNode url="UserAgreement.aspx" title="Användarvillkor" description="" />
<siteMapNode url="UserRegistration.aspx" title="Registrering" description="" />
</siteMapNode>
</siteMap>

Menykoden ligger i MasterPage.master ovanför
<asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
som markerar var Contentsidan innehåll visas. Menykoden ser ut så här:
<div class="LeftMenuAbs">
<a;sp:SiteMapDataSource ID="SiteMapDataSource" runat="server" ShowStartingNode="false" />
<asp:Menu ID="Menu" runat="server" DataSourceID="SiteMapDataSource">
</asp:Menu>
</div>

Objektet SiteMapDataSource kopplar sidan till xml-filen. Filnamnet behöver inte anges om den heter Web.sitemap. (Möjligen accepteras inte andra namn!) Meny-objektet är ett av de objekt som visar menyn. Ett alternativ är TreeView som visar en hiarkisk menu där grenarna kan öppnas och stängas med + och . Ett annat alternativ är SiteMapPath som visar besökaren var den varit.

Därmed fungerar menyn till vänster och innehållet ligger till höger. Just som det var tänkt. Sedan tar jag bort länkar i innehållssidorna och har en ny version att ladda upp.

User agreement

Systemets användarvillkor är inte omfattande. Det viktiga är att säga att systemet är under utveckling (som om det inte märks...), att PUL gäller, att systemet kan läggas ned när som helst, att ändringar kan ske utan förvarning.

Datum för publicering av villkoren bör visas vid inloggningen. Så här blev koden:

<h1>Användarvillkor 2010-01-21</h1>
<p>1. Detta system är en testversion och kan när som helst avvecklas. </p>
<p>2. Användarnas uppgifter behandlas enligt <a href="https://lagen.nu/1990:409">lagen om
företagshemligheter</a> och <a href="https://lagen.nu/1998:204">Personuppgiftslagen regler om personuppgiftsbiträde</a>.</p>
<p>3. Användaren svarar själv för säkerhetskopiering av sina registrerade uppgifter.</p>
<p>4. Användarvillkor kan ändras när som helst och gäller med omedelbar verkan.</p>
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="Login.aspx">Login<br />
<asp:HyperLink ID="HyperLink2" runat="server" NavigateUrl="UserRegistration.aspx">Användarregistrering</asp:HyperLink></p>

Tänkbara förbättringar:
- Lägga villkoren i databasen så att länkarna till Login och Användarregistrering kan skapas automatiskt liksom uppdatering av datum för aktuella villkor.

Nu med bara fyra sidor så känns ett menysystem angeläget. Så det får nog bli nästa steg.

Login (1)

När väl registreringen är på plats ska det bli möjligt att logga in. Hur görs enklast möjliga inloggning? För det krävs ett formulär med fälten användarnamn och lösenord.

<h1>Logga in</h1>
<asp:Label ID="Message" runat="server" Text="Meddelande" ForeColor="#FF3300"></asp:Label>
<p>Användarnamn<br /><asp:TextBox ID="UserName" runat="server"></asp:TextBox></p>
<p>Lösenord<br /><asp:TextBox ID="Password" runat="server"></asp:TextBox></p>
<asp:Button ID="Login1" runat="server" Text="Logga in" />


Vid klick på "logga in" kollas uppgifterna mot databasen.

Protected Sub Login1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Login1.Click
Dim sql As String
Dim Userid As Int32
Dim conn As SqlConnection
Dim connectionstring As String = ConfigurationManager.ConnectionStrings("System91").ConnectionString
conn = New SqlConnection(connectionstring)
Dim comm As SqlCommand
Message.Text = ""
sql = "SELECT id, username, password FROM [User] WHERE username='" & UserName.Text & "' and password='" & Password.Text & "'"
comm = New SqlCommand(sql, conn)
Try
conn.Open()
Userid = comm.ExecuteScalar()
If Userid = 0 Then
Message.Text += " Du är inte inloggad. Om du inte är registrerad kan du göra det <a href='Userregistration.aspx'>här</a>"
Else
Message.Text += " Du är inloggad. ."
End If
Catch ex As SqlException
Message.Text += " Databasfel. " & ex.Number & ": " & ex.Message ', ex.Gettype.ToString
Message.Text += " Du är inte inloggad. "
Finally
conn.Close()
End Try
End Sub

Sedan är användaren inloggad, men för hur länge? Det framgår av Microsofts dokumentation av Session. En enklare beskrivning finns på utmärkta www.w3schools.com sida om ASP Sessions. W3schools (inte Microsoft!) ger svaret: standardvärdet är 20 minuter om annat inte bestämts av systemet.

Så nu fungerar alltså inloggningen, men det är ganska värdelös för användaren kommer inte vidare. Hur bör loginfunktionen vidareutvecklas?
- Logout
- Användarvillkor
- Ingen automatisk utloggning
- Uppdatera sidhuvudet
- Gå till en ny sida

Utan dessa tillägg är det inte meningsfullt att publicera login.aspx på www.server42.se

måndag 18 januari 2010

Registreringen i produktion

För att kunna testa registreringen publikt så skapar jag först en enkel MasterPage med en variabel Webname och tilldelade den "System9".

Sedan fixade jag en förstasida med länk till användarregistreringen och kopierar sedan över dem till den publika webben: http:www.server42.se.

Efter en del trassel så fungerar sidan och jag kompletterar den med länkar till koden och en länk för att återgå till förstasidan.

söndag 17 januari 2010

Registrering av användare (5)

Att lägga till fältet Lösenord är mycket enkelt. Först kopierar jag koden för användarnamnet som larmar om antalet tecken är färre än fem. Och därefter ändrar jag insertsatsen så att den även inkluderar Password.
INSERT INTO [User] (Username, Password, email, type) VALUES ('" & UserName.Text & "', '" & Password.Text & "')

Nu fungerar registreringen acceptabelt, men flera tillägg bör göras:
- kolla att användarnamn och lösenord inte innehåller blanka tecken
- ta hand om fel beroende på att databasen inte är tillgänglig.
- visa användarvillkor
- godkänna användarvillkor som förutsättning för registreringen
- registrera epostadress
- möjlighet att beställa användarnamn och lösenord med hjälp av lösenordet
- sidhuvudet bör kompletteras med uppgift om att logga in alternativ uppgift om användarnamn.
- loginsidan bör ha en länk till registreringssidan.
- En indexsida dit besökare kommer först med länk till registreringen.

Jag tycker att det är viktigt att dessa delar fungerar som de brukar så att intresserade inte avstår ifrån att registrera sig bara för att det verkar konstigt.

Registrering av användare (4)

Efter att i huvudsak givit upp att hitta på Microsofts spaggettiwebb, med inaktuellt innehåll, så har jag skaffat "murach's ADO.NET 3.5 Linq and Entity Framwork with VB 2008". 700 sidor med förhoppningsvis aktuell information. 375 kr + moms på adlibris.

Jag avstår dock tills vidare med att använda Linq och fortsätter att använda ADO.NET och SQL direkt ett tag till.

Kollen av om användarnamnet redan finns i databasen ser nu ut så här


Message.Text = ""
Dim conn As SqlConnection
Dim connectionstring As String = ConfigurationManager.ConnectionStrings("System91").ConnectionString
conn = New SqlConnection(connectionstring)
Dim comm As SqlCommand
comm = New SqlCommand("SELECT id, username FROM [User] WHERE username='" & UserName.Text & "'", conn)

Try
conn.Open()
Userid = comm.ExecuteScalar()
'ExecuteScalar returnerar första kolumnen i första posten om den finns, annars 0. http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executescalar.aspx Userid =

Catch ex As SqlException
Message.Text += " Databasfel. " & ex.Number & ": " & ex.Message '
Finally
conn.Close()
End Try


Några kommentarer och erfarenheter
- ConfigurationManager hämtar databasens lösenord från web.config.
- Variabler deklarerade i Try-delen är lokala inom det blocket och inte tillgängliga i t.ex. catch. Så jag har nu lagt deklarationerna före Try.

- Felmeddelandet vid databasfel har kompletterats

Nästa fas är att lagra det nya användarnamnet i databasen.

If Userid = 0 Then
Dim sql As String
sql = "INSERT INTO [User] (Username) VALUES ('" & UserName.Text & "')"
comm = New SqlCommand(sql, conn)
Try
conn.Open()
Dim res As Integer = comm.ExecuteNonQuery()
Message.Text += " Användarnamnet är registrerat. "
Catch ex As SqlException
Message.Text += " Databasfel " & ex.Number & ": " & ex.Message '
Message.Text += " Användarnamnet registrerades inte. "
Finally
conn.Close()
End Try
Else
Message.Text += " Användarnamnet är redan registrerat. "
End If

Men, som jag skrev i förra avsnittet, så har denna lösning ett allvarligt fel. Om någon skriver att användarnamn som redan finns kan ju denna person därefter direkt logga in med denna användares namn. Därför måste användarnamet skyddas med ett lösenord. Det får hanteras i avsnitt 5.

fredag 15 januari 2010

Registrering av användare (3)

Nu ska användarnamnet läggas in i databasen. Först gäller det dock att stoppa om det uppstått databasfel. Det sker med ok = False efter Catch. Därefter kan ok-varibeln kollas före uppdateringen.

Dessutom ska uppdateringen endast ske om Userid=0. I annat fall får användaren bara ett meddelande.

Uppdateringen sker igen inom Try...Catch.

SQL-satsen för att lägga in användarnamnet i databasen kan se ut så här:
INSERT INTO [User] (Username) VALUES ('" & UserName.Text & "')

Kommandot Insert utförs med objektet SQLcommand. Det är enormt omfattande och komplicerat. Det måste finns enklare beskrivning av hur man gör. Efter mycket sökande i Microsofts spagettidokumentation hittar jag Beginner Developer Learning Center, väljer web track och hamnar till slut på Lesson 7: Databinding to user interface controls. Lektionen är riktigt gammal - använder ASP.Net 2.0 (nu gäller 3.5) och SQL 2005 (numera 2008).

ASP.NET Selected Walkthroughs finns det mer aktuella råd, men genomgående är att Microsoft föreslår mängder med objekt och verktyg som bara gör allt mer komplicerat. Det borde ju vara hur enkelt som helst att bara lägga in en post i en databas.

Nej, jag ger upp vad gäller MSDN och testar i stället asp.net - en webb MS driver specialiserad på ASP. Inte bättre! Kollar http://msdn.microsoft.com/en-us/aa336522.aspx.

onsdag 13 januari 2010

Registrering av användare (2)

I första avsnittet fanns det redan en databas och namnet var riktig angivet i koden. Men om databasen inte skulle finnas eller fungera så måste användaren få ett meddelande. Det åtgärdas med Message.Text = "Databasfel" efter Catch.

Dessutom ändras meddelandet om databasfel så att den texten läggs till ev tidigare meddelande: Message.Text += " Databasfel. "

Om användaren inte anger något namn utan bara klickar på registrera så måste det också komma ett meddelande. Och i det fallet ska det inte göras någon sökning i databasen. Det är lika bra att begära att användarnamnet ska innehålla minst fem tecken. Det åtgärdas med
Dim ok As Boolean = True
If UserName.Text.Length
< 5 Then
Message.Text += "Användarnamnet måste vara minst fem tecken långt. "
ok = False
End If
if ok then
'kod för databassökningen
end if

Denna kod kollar inte om användaren använt blanktecken. Det bör nog inte vara möjligt.

Nästa uppgift blir att avgöra om användarnamnet finns i databasen. Objektet SqlCommand har flera metoder för bearbetning av databasen. ExecuteScalar kan vara lämplig. Koden blir
Userid = comm.ExecuteScalar()
Message.Text = "Användarid=" & Userid
Om användarid (Userid) är 0 så finns det inte i databasen och ska lagras. Det sker i nästa avsnitt och då hanteras även ett betydelsefullt fel.

Registrering av användare (1)

Hur enkel kan man göra registrering av en användare? Eftersom man ska logga in sig med ett username så är denna uppgift ett minimum. Username måste lagras i en databas så registreringen kan kollas vid en inloggning.

Vid registreringen måste jag först kolla om användarnamnet redan finns i databasen och i så fall meddela användaren det. I annat fall kan jag lagra användarnamnet.

Så jag börjar då med det. Först skapar jag en MasterPage med två ContentPlaceholders - en i huvudet och en i bodyn.

Jag kommer genomgående använda språket Visual Basic och placera koden i en separat fil åtskild från designen som skapas automatiskt med HTLM, ASP, mm.

Skapar sedan ett nytt item med namnet UserRegistration med mallen Web Form och använder den just skapade MasterPagen.

I design lägger jag in texten "Användarnamn", en textruta som får namnet UserName och knappen "Registrera". Startar programmet med F5, den inbyggda lokala webbservern går igång och resultatet blir det avsedda. Nu ska koden läggas till som lagrar UserName i databasen.

Jag skapar en kodfil genom att klicka på knappen. Bestämmer mig för att komplettera sidan med meddelandefält Message så att jag kan visa ett meddelande skapat koden. Först visas meddelandet "Label" i rött. Sedan döper jag variabeln till Message och tilldelar den ett innehåll med message.text="Här kommer meddelandet". Det fungerar som det ska.

Nu gäller det att skapa kontakt med databasen, Då det är en känslig passage omger jag denna kod med Try och Catch som gör att ev fel i Try-delen fångas upp och kan hanteras i Catchdelen.

Första satsen är Dim conn As SqlConnection. SqlConnection är ett objekt som svarar för kontakten med MS SQL. Då objektet inte ingår som standard måste det importeras med Imports System.Data.SqlClient. Dessutom krävs Dim comm As SqlCommand. Objektet SqlCommand innehåller uppgifter om hur databasen ska accessas.

Därefter ska en variabel tilldelas namn på databasen. Det gör man på detta krångliga sätt:
Dim connectionstring As String = ConfigurationManager.ConnectionStrings("System91").ConnectionString. Objektet ConfigurationManager ger tillgång till information om databasen.

Därmed kan förbindelsen skapas med databasen: conn = New SqlConnection(connectionstring). Sedan kan objektet sqlcommando tilldelas sqlsatsen som t.ex. styr hämtningen av information från databasen.: comm = New SqlCommand("SELECT type FROM [User] WHERE username='" & UserName.Text & "'", conn). Själva hämtningen sker med conn.Open().

Hanteringen av data hoppar jag över för tillfället och markerar det bara med ett meddelande: message.text="Här ska data hanteras". I stället kompletterar jag koden så att databaskontakten kan testas genom att lägga till conn.Close().

Hela koden blir:
Message.Text = "Här kommer meddelandet"
Try
Dim conn As SqlConnection
Dim comm As SqlCommand
Dim connectionstring As String = ConfigurationManager.ConnectionStrings("System91").ConnectionString
conn = New SqlConnection(connectionstring)
comm = New SqlCommand("SELECT type FROM [User] WHERE username='" & UserName.Text & "'", conn)
conn.Open()
Message.Text = "Här ska data hanteras"
conn.Close()
Catch
End Try

Fortsättning följer i Registrering av användare (2).

Ett sista försök

Efter stor tvekan gör jag nu ett nytt försök att skapa en webb med ASP.Net. Det som fick mig att lägga av var Microsofts eländiga spagettidokumentation på MSDN och det resultatlösa letandet efter fel i deras Membershipsystem. Det senare funkade ibland och ibland var det fel. En sökning i MSDN med "ASP login fail" ger över 10.000 svar! Varför kunde jag inte klarlägga.

Nu startar jag dock ändå igång igen och gör ett sista försök.

Utvecklingen görs på en Dell Lattitude E6500, som förvisso har sina brister. Dem ska jag nog återkomma till. OS är Vista business.

Kodningen sker med Visual Developer 2008 Express Edition (gratis). Databas är MS SQL Express (gratis) som bl.a hanteras med SQL Server Management Server (också gratis).

Efter att ha testat en ny sida så publicerar jag koden genom att överföra den till webhotellet Space2u.se med hjälp av Filezilla (gratis). Space2u är tyvärr inte gratis, men jag har mycket god erfarenhet av dem under många år, så det var inte ett svårt val.

Resultat är tillgängligt på http://www.server42.se/. Där ligger fn rester av tidigare försök som ska tas bort om ett tag.

Min webbs funktion är mycket enkel. Det ska gå att registrera sig som användare. Det är allt. Mina planer därefter är enormt stora och dem kommer jag helt säkert inte uppnå, men som Steve Jobs sa the journey is the reward.

Nu kör jag igång. Webben kallar jag för System9. Följd med!