Hjemmesiden

 

Firma Joakim Dalby’s hjemmeside er blevet opbygget efter følgende hensigt:

 

  • Menustruktur med en menuline og drop-down menuer a la et Windows program
  • Det skal være enkelt at vedligeholde menupunkternes placering, tekst og links
  • Al tekst på hjemmesiden skal skrives i et tekstbehandlingsprogram, så det er nemt at forfatte og vedligeholde teksterne

 

Til opfyldelse af hensigten er der truffet følgende valg for opbygning af hjemmesiden og implementeringen er sker på følgende måde.

 

  • Al tekst skrives i Microsoft Word XP (2002/10.0) og gemmes først som doc-filer. Dokumentet indeholder en tabel med en bredde på 15,23 cm, hvori teksten skrives, så bredden bevares på hjemmesiden. Brugen af tabel i doku­men­tet svarer til brugen af <table>-tag i html. Dokumentets sideopsætninger er portræt med Top 2cm, Bund 2cm, Venstre 3cm og Højre margin 2,5cm. Skrifttypen er Verdana og skriftstørrelsen er 10. Billeder indsættes i doku­men­tet fra en fil, typisk som gif eller jpg. Links inden for dokumentet eller til andre dokumenter indsættes som HyperLinks. For at op­nå en lige højre margin be­nyt­tes orddelingsbindestreg (hyphenation), enten automatisk eller manuelt via [Ctrl][-] (se mere nederst på denne side), og til sidst sættes lige mar­gin. Hvis en browser ikke understøtter orddelingsbindestreger, så vises de ikke. Det er ik­ke en forudsætning for orddeling og lige margin, at Internet-brugeren har Word installeret på sin PC. Word dokumentet skrives via Web Layout vis­ning. Under dokumentets egenskaber angives en titel, der bliver vist øverst på papiret, når dokumentet udskrives via browseren.
  • Når Word dokumentet er gemt på normalvis i en doc-fil, så omdannes det til et HTML-dokument ved at gemme det som en Web side. Billeder i doku­men­tet bliver automatisk gemt i en undermappe.
  • Normalt er en PC-skærm egenskab for skriftstørrelse (højreklik på desktop, {Egen­skaber} {Indstillinger} [Avanceret]) sat til “Små skrifttyper (96 dpi=dots per inch)”, men er den ændret til “Store skrifttyper (120 dpi) = 125% normal størrelse”, så bliver Verdana 10 vist med større typer på skærmen hvorved teksten fylder mere i bredden, mens Verdana 8 bevarer sin størrelse.

 

  • En database til håndtering af menupunkterne opbygges i SQL Server 2000 som en typisk en-til-mange relation hvor moder-tabellen indeholder menuliniens hovedpunkter og barn-tabellen indeholder menuens underpunkter knyt­tet til hver hovedpunkt.
  • Udvikling af en C# applikation til indtastning og vedligeholdelse af menu-databasen. Kunne også være gjort i Access XP.

 

  • Data i menuen udtrækkes fra databasen som et XML-dokument via en stored procedure i T-SQL, hvor alle data er indsat som elementer.
  • XML-dokumentet verificeres af til tilhørende XSD-dokument, så alle tags og data kontrolleres i XML-dokumentet.
  • XML-dokumentet omdannes herefter til et HTML-dokument (HTML 4.01), som kan vises i en browser. Det sker via et programmeret XSLT dokument, som parser XML-dokumentet og genererer HTML-dokumentet. XSLT-dokumentet indeholder således HTML-tags, kald af JavaScripts funktioner gemt i en sepe­rat .js-fil og kald af style sheet gemt i et CSS-dokument.

 

  • Styring af de dynamiske drop-down menupunkter sker via et programmeret JavaScript indeholdende funktioner som kaldes fra HTML-dokumentet på e­gen­skaberne onMouseOver/Out og onClick
  • Et CSS-dokument (style sheet) håndterer de dynamiske farveskift i menuen og kaldes via class egenskaben i HTML-dokumentet.
  • C# applikationen sætter HTML-genereringen i gang ved at kalde en stored procedure i databasen, modtage XML-dokumentet med menuens data, foreta­ge verificeringen via XSD-dokument, og parsing af XSLT dokumentet, så der fås et HTML-dokument med referencer til CSS og js samt baggrunds­bil­le­de.

 

Menupunkter i en database svarer til et simpel Contents Management System der le­verer XML til HTML-genereringen og via XSLT-dokumentet leveres html-tags m.v. d.v.s. data og præsentation/funktionalitet er adskilt i hver sin fil.

 

Tekster i Word dokumenter gør det både nemt at forfatte dem med skrifttyper m.v. og senere at vedligeholde dem, og der kan indsættes billeder og links i dokumentet, så det til sidst bliver til et egentligt HTML-dokument.

 

Sitemap er også genereret via et andet XSLT-dokument, der tager det samme XML-do­ku­ment indeholdende menuens data, men omdanner det til et andet HTML-do­ku­ment til præsentation af et sitemap. Det samme XML-dokument bliver således om­dan­net via to XSLT-dokumenter til to HTML-dokumenter for selve menuen og for sitemap.

 

 

Note om bindestreger i Word

Den almindelige bindestreg [-] kaldes på engelsk (regular) hyphen, som har ansi-værdien #45. Den almindelige bindestreg tillader, at ordet deles over to linier netop hvor bindestregen står i ordet f.eks. ordet: HTML-dokument, over to linier: HTML- 

dokument.

 

Orddelingsbindestreg [Ctrl][-] kaldes på engelsk optional/soft hyphen, som har ansi-værdien #173. Orddelingsbindestregen vises kun, når ordet deles over to linier, og med [Ctrl][-] kan man selv indsætte disse bindestreger, hvis sprog/grammatikken ikke deler ordet optimalt.

 

Har man et ord indeholdende bindestreg f.eks. et telefonnummer, eksempelvis 39‑66‑26‑13 og man ikke ønsker, at det deles over to linier, så indsættes bin­de­stre­ger­ne som ikke-orddelingsbindestreger med [Ctrl][Shift][-] kaldet på engelsk non­breaking hyphen. I html angives denne bindestreg som &nbhy; eller &#8209;

 

Når vi er i gang med bindestreg, hvorfor så ikke lige nævne tankestregerne:

En enkelt tankestreg kaldes på engelsk en-dash  –

En dobbelt tankestreg kaldes på engelsk em-dash  —

 

 

Stored procedure i databasen der danner XML med menudata

CREATE PROCEDURE dbo.GetXMLMenuList

AS

BEGIN

SET NOCOUNT ON

 

SELECT '<?xml version="1.0" encoding="ISO-8859-1"?>'

SELECT '<DALBYXML CreatedDate="' + GetDate() +

       '" xmlns="urn:Menu Markup Language">'

SELECT '<MENUMAINLIST>'

 

SELECT MENUMAIN.MenuMainItem, MENUMAIN.MenuMainActive,

       MENUDETAILLIST.*,

       MENUDETAIL.MenuDetailItem, MENUDETAIL.MenuDetailLink,

       MENUDETAIL.MenuDetailActive, MENUDETAIL.MenuDetailSeparator,

       PAGEMARGIN.MarginWidth

FROM MENUMAIN AS MENUMAIN

     LEFT OUTER JOIN MENUDETAIL AS MENUDETAIL

       ON MENUMAIN.MenuMainId = MENUDETAIL.MenuMainId

     INNER JOIN SHOWWIDTHTYPELIST AS PAGEMARGIN

       ON PAGEMARGIN.ShowWidthType = MENUDETAIL.ShowWidthType,

     XMLNULL AS MENUDETAILLIST

WHERE MENUDETAIL.MenuDetailActive = 'ON'

ORDER BY MENUMAIN.MenuMainId, MENUDETAIL.MenuDetailId

FOR XML AUTO, ELEMENTS

 

SELECT '</MENUMAINLIST>'

SELECT '</DALBYXML>'

END

 

Eksempel på den dannet menu XML struktur

 

 

C# henter XML fra stored procedure, validerer og gemmer i xml fil

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.Data.SqlClient;

using System.Xml;

using System.Xml.Schema;

using System.IO;

private void GetXML()

{

string connectionstring = "Server=XXX;Database=Homepage;

                     Integrated Security=True;Connect Timeout=30";

SqlConnection connection = new SqlConnection(connectionstring);

SqlCommand command = new SqlCommand();

command.Connection = connection;

connection.Open();

command.CommandType = CommandType.StoredProcedure;

command.CommandText = "GetXMLMenuList";

//command.Parameters.Add("Param", SqlDbType.Int).Value = "8";

//command.Parameters.Add("Status", SqlDbType.Int).Direction =

                                   ParameterDirection.ReturnValue;

StringBuilder xmlString = new StringBuilder();

xmlString.Length = 0;

SqlDataReader reader = command.ExecuteReader

                       (CommandBehavior.SequentialAccess);

do  // Flere SELECT danner XML derfor modtages Multiple Result Sets

{

 while (reader.Read()) // Første gang læses recordset kolonner og

 {                     // peger/pointer flyttes til første datarække.

  xmlString.Append(reader.GetString(0)); // Datarækkens 1. kolonne.

 }                     // Ved anden gennemløb læses anden række og

}                      // der fortsættes indtil alle rækker er læst.

while (reader.NextResult());

 

reader.Close();

reader.Dispose();

reader = null;

//int status = (int)command.Parameters["Status"].Value;

//command.Parameters.RemoveAt("Param");

//command.Parameters.RemoveAt("Status");

command.Dispose();

command = null;

connection.Close();

connection.Dispose();

connection = null;

// Ved fejl i XML strukturen fås en runtime error der fanges her

XmlDocument xmlDoc = new XmlDocument();

XmlDeclaration xmlDecl = xmlDoc.CreateXmlDeclaration

                         ("1.0", "utf-8", "");

xmlDoc.InsertBefore(xmlDecl, xmlDoc.DocumentElement);

try

{

 xmlDoc.LoadXml(xmlString .ToString());

}

catch (XmlException ex)

{

 MessageBox.Show(ex.Message);

 return;

}

// Ved fejl i XML i forhold til schema fås undtagelse der fanges her

string xsdFile = @"C:\Menu.xsd";  // eksisterende schema indlæses

XmlSchemaSet xmlSchema = new XmlSchemaSet();

xmlSchema.Add("urn:Menu Markup Language", xsdFile);

xmlDoc.Schemas.Add(xmlSchema);

try

{

 xmlDoc.Validate(new ValidationEventHandler(ValidationEventHandler));

} // Ved fejl i XML i forhold til XSD udløser Validate en begivenhed

catch (Exception ex)

{

 MessageBox.Show(ex.Message);

 return;

}

// Gem den valideret XML fra stored proceduren i en fil

string xmlFile = @"C:\Menu.xml";

if (File.Exists(xmlFile))

    File.Delete(xmlFile);

// BOM (Byte Order Mark) tre tegn EF BB BF i starten af filen droppes

Encoding UTF8EncodingNoBOM = new UTF8Encoding(false);

FileStream fs = File.Open(xmlFile, FileMode.Create,

                          FileAccess.Write, FileShare.None);

StreamWriter sw = new StreamWriter(fs, UTF8EncodingNoBOM);

sw.WriteLine(xmlDoc.OuterXml);

sw.Flush();

sw.Close();

fs.Close();

}

private static void ValidationEventHandler(object sender,

                                       ValidationEventArgs args)

{

 throw new Exception(args.Message); // kaster en undtagelse der

}                                   // bliver fanget (catch) ovenfor.

 

 

C# validering af en XML fil mod en XML Schema Definition XSD fil inden den gemmes i en database gennem en stored procedure

Godt at validere indholdet af XML filen inden den skal gemmes i en database ved at overføre XML indholdet til en stored procedure der så fordeler data ud i flere ta­bel­ler og derved anvender de forskellige tags, elementer og attributter i XML’en som XSD validerer for at de finder og er opstillet korrekt.

private bool SaveXML()

{

string xmlFile = @"C:\Svar.xml"; //Fra Notesblok med danske tegn æøå

Encoding ANSIEncoding = Encoding.GetEncoding(1252); // codepage 1252

                                        // ISO88591 Encoding.Default

FileStream fs = File.Open(xmlFile, FileMode.Open,

                          FileAccess.Read, FileShare.None);

StreamReader sr = new StreamReader(fs, ANSIEncoding);

StringBuilder xmlString = new StringBuilder();

xmlString.Length = 0;

string line;

while ((line = sr.ReadLine()) != null)

{

 xmlString.Append(line);

}

line = String.Empty;

sr.Close();

fs.Close();

// Ved fejl i XML strukturen fås en runtime error der fanges her

XmlDocument xmlDoc = new XmlDocument();

XmlDeclaration xmlDecl = xmlDoc.CreateXmlDeclaration

                         ("1.0", "utf-8", "");

xmlDoc.InsertBefore(xmlDecl, xmlDoc.DocumentElement);

try

{

 xmlDoc.LoadXml(xmlString.ToString());

}

catch (XmlException ex)

{

 MessageBox.Show(ex.Message);

 return false;

}

// Ved fejl i XML i forhold til schema fås undtagelse der fanges her

string xsdFile = @"C:\Svar.xsd";  // eksisterende schema indlæses

XmlSchemaSet xmlSchema = new XmlSchemaSet();

xmlSchema.Add(null, xsdFile); // null fordi intet targetNamespace

xmlDoc.Schemas.Add(xmlSchema);

try

{

 xmlDoc.Validate(new ValidationEventHandler(ValidationEventHandler));

}

catch (Exception ex)

{

 MessageBox.Show(ex.Message);

 return false;

}

// Den korrekt valideret XML i xmlDoc overføres til stored procedure

try

{

string connectionstring = "Server=XXX;Database=SVAR;

                     Integrated Security=True;Connect Timeout=30";

SqlConnection connection = new SqlConnection(connectionstring);

SqlCommand command = new SqlCommand();

command.Connection = connection;

connection.Open();

command.CommandType = CommandType.StoredProcedure;

command.CommandText = "Save_XML_Svar";

command.Parameters.Add("xml", SqlDbType.NText).Value =

                                 xmlDoc.OuterXml;

command.Parameters.Add("Status", SqlDbType.Int).Direction =

                                 ParameterDirection.ReturnValue;

command.ExecuteNonQuery();

int status = (int)command.Parameters["Status"].Value;

//MessageBox.Show(status.ToString());

command.Parameters.RemoveAt("xml");

command.Parameters.RemoveAt("Status");

command.Dispose();

command = null;

connection.Close();

connection.Dispose();

connection = null;

}

catch(SqlException ex)

{

 MessageBox.Show(ex.Message);

 return false;

}

finally

{

 if (status == 1) // return værdien fra stored procedure

  return true;

 else

  return false;

}

}

 

I stored procedure dannes et xmlDoc objekt ved følgende T-SQL kommando:

EXECUTE sp_xml_preparedocument @xmlDoc OUTPUT, @xml

Det er ikke et krav, at XML filen indeholder encoding men anvendes

<?xml version="1.0" encoding="ISO-8859-1"?> eller

<?xml version="1.0" encoding="UTF-8"?>

giver kommandoen en fejlmelding:

»XML parsing error: Switch from current encoding to specified encoding not supported.«

Ønsker man encoding i XML filen kan denne i stedet anvendes:

<?xml version="1.0" encoding="Unicode"?>

 

Stored procedure i databasen der gemmer XML data i tabeller

CREATE PROCEDURE dbo.Save_XML_Svar @xml ntext

AS

BEGIN

SET NOCOUNT ON

DECLARE @ForbedringsRapportId int

DECLARE @xmlDoc int

EXECUTE sp_xml_preparedocument @xmlDoc OUTPUT, @xml

INSERT INTO dbo.FORBEDRINGSRAPPORT WITH(ROWLOCK)

(OpretDato, Projektnummer, Afdeling, Litra, Skovnummer, IndsenderNavn)

SELECT Getdate(), Projektnummer, Afdeling, Litra,

  -- er Skovnummer="" bliver værdien 0 der her oversættes til NULL

  CASE WHEN Skovnummer = 0 THEN NULL

       ELSE Skovnummer END AS Skovnummer,

  IndsenderNavn

FROM OPENXML(@xmlDoc, '/FORBEDRINGSRAPPORTSVAR/FORBEDRINGSRAPPORT', 1)

WITH (Projektnummer int, Afdeling smallint, Litra nvarchar(5),

      Skovnummer smallint, IndsenderNavn nvarchar(50))

IF @@ERROR <> 0

   RETURN(0)

SET @ForbedringsRapportId = SCOPE_IDENTITY() -- giver oprettet Id nr.

INSERT INTO dbo.FORBEDRINGSRAPPORTSVAR WITH(ROWLOCK)

(ForbedringsRapportId, ForbedringSpørgsmålId, Rækkefølge, Ja, Nej)

SELECT @ForbedringsRapportId, ForbedringSpørgsmålId, Rækkefølge,

       Ja, Nej

FROM OPENXML(@xmlDoc, '/FORBEDRINGSRAPPORTSVAR/SVARLISTE/SVAR', 1)

WITH (ForbedringSpørgsmålId int, Rækkefølge int, Ja bit, Nej bit)

IF @@ERROR <> 0

   RETURN(0)

-- Og mere til, her er bare vist et uddrag af stored proceduren.

EXECUTE sp_xml_removedocument @xmlDoc

RETURN(1)

END

 

Eksempel på indhold af Svar.xml

 

Eksempel på indhold af Svar.xsd