Bob Swart (aka Dr.Bob)
IntraWeb 5.1 met InterBase

IntraWeb 5.1.28 in Delphi 7
Deze maand wil ik even stilstaan bij de bouw van een IntraWeb toepassing met "twee gezichten", authenticatie alsmede database ondersteuning. Ik ga er overigens vanuit dat tenminste IntraWeb versie 5.1.19 (of hoger) is geïnstalleerd - liefst IntraWeb 5.1.28 (de laatste gratis versie die beschikbaar is voor Delphi 7 ontwikkelaars).
Doe File | New - Other en ga naar de IntraWeb tab van de Object Repository waar de twee IntraWeb 5.1 wizards te vinden zijn:

Dubbelklik op de New IntraWeb Application icon om de nieuwe IntraWeb Application Wizard te krijgen. De default keuze staat op een StandAlone Application met User Session. Da's prima, maar ik wil er ook een Data Module bij gebruiken, dus zet die optie ook aan:

We krijgen nu een project (Project1.dpr) met drie units erbij (voor de Server Controller, de data module, en de IntraWeb Form). Doe File | Save All om alles op te slaan, en bewaar daarbij het form (Unit1.pas) in IWFormMain.pas, de data module in DataModuleUnit.pas, de Server Controller in ServerController.pas, en het project zelf in IW42.dpr.


Server Controller
We beginnen nu met het instellen van de properties van de Server Controller in unit ServerController.pas. Als we de source code goed bekijken zien we dat er al een verwijzing naar de data module (type TDataModule1 uit de DataModuleUnit) is toegevoegd aan de TUserSession class. Bij het starten van een nieuwe sessie zal de OnNewSession event handler van de Server Controller ervoor zorgen dat er een nieuwe instantie van de TUserSession wordt aangemaakt, en die zorgt er vervolgens zelf voor dat de nieuwe instantie van de TDataModule1 (als veld in de UserSession) wordt gecreëerd.

  unit ServerController;
  interface
  uses
    SysUtils, Classes, IWServerControllerBase, IWBaseForm, HTTPApp,
    // For OnNewSession Event
    DataModuleUnit, IWApplication, IWAppForm;

  type
    TIWServerController = class(TIWServerControllerBase)
      procedure IWServerControllerBaseNewSession(ASession: TIWApplication;
        var VMainForm: TIWBaseForm);
    private
    public
    end;

    // This is a class which you can add variables to that are specific to the user. Add variables
    // to this class instead of creating global variables. This object can references by using:
    //   UserSession
    // So if a variable named UserName of type string is added, it can be referenced by using:
    //   UserSession.UserName
    // Such variables are similar to globals in a normal application, however these variables are
    // specific to each user.
    //
    // See the IntraWeb Manual for more details.
    TUserSession = class(TComponent)
    public
      DataModule1: TDataModule1;
      constructor Create(AOwner: TComponent); override;
    end;

  // Procs
    function UserSession: TUserSession;

  implementation
  {$R *.dfm}

  uses
    IWInit;

  function UserSession: TUserSession;
  begin
    Result := TUserSession(WebApplication.Data);
  end;

  procedure TIWServerController.IWServerControllerBaseNewSession(
    ASession: TIWApplication; var VMainForm: TIWBaseForm);
  begin
    ASession.Data := TUserSession.Create(nil);
  end;

  constructor TUserSession.Create(AOwner: TComponent);
  begin
    inherited;
    DataModule1 := TDataModule1.Create(Self);
  end;

  initialization
    TIWServerController.SetServerControllerClass;
  end.

De AppName property krijgt de waarde IW42 van me. Dit moet een unieke naam zijn als je de IntraWeb toepassing als (NT) Service wil installeren.
De AuthBeforeNewSession property krijgt de waarde True van me - dit levert tijdwinst op als "minder goedwillende bezoekers" langskomen, omdat het uitvoeren van authenticatie vóór het starten van een nieuwe sessie in ieder geval tot gevolg zal hebben dat het aanmaken van een nieuw TUserSession object (met data module) niet onnodig zal gebeuren. Voor de authenticatie zelf kunnen we gebruikmaken van de AuthList property, waarin we een aantal regels met username=password kunnen opnemen (net als ik voor het members-only deel van mijn website een paar keer heb gedaan). Tijdens demos voeg ik altijd op z'n minst de regel guest=guest toe, zodat we altijd gast-gebruikers kunnen ontvangen (hoe we die gebruikers kunnen beperken in hun mogelijkheden zien we in de Clinic).
Als alternatief - of uitbreiding - kunnen we ook op de OnAuthRequest event handler van de Server Controller reageren, bijvoorbeeld als volgt:

  procedure TIWServerController.IWServerControllerBaseAuthRequest(
    const AUserName, APassword: String; var AValid: Boolean);
  begin
    AValid := (AUserName = 'Bob') and (APassword = 'Swart')
  end;

De BoundIP property kan ik beter geen waarde geven, want daarmee geef ik aan dat de IntraWeb toepassing alleen naar inkomend verkeer op dat IP-adres zal luisteren (in combinatie met de waarde van de Port property, die ik wel een waarde geef, namelijk 7777).
De ComInitialization property is nodig als we met COM werken - bijvoorbeeld bij het gebruik van ADO of DataSnap communicatie componenten. Voor dbExpress heb ik deze property niet nodig, dus kan hij op ciNone blijven staan.
Als Description gebruik ik iets als "Delphi 7 Clinic IntraWeb 5.1 Application".
De Netscape4As32 property is een erg belangrijke voor mij, want hiermee geven we aan dat Netscape 4 zal worden behandeld als een HTML 3.2 compatible browser of device (net als PDA's). Deze property moet dus op True blijven staan, en in de SupportedBrowsers property moet ik tevens de subproperty brNetscape4 op True zetten, zodat Netscape 4 ook daadwerkelijk ondersteund zal worden (als HTML 3.2 browser dan).


TIWAppForm
Tijd om naar het IntraWeb Form te gaan in IWFormMain.pas. Dit form is afgeleid van TIWAppForm en maakt daarmee gebruik van HTML 4. We kunnen hier alle componenten van de IW Standard, IW Data, IW Control en IW Client Side op gebruiken (behalve de "32" componenten van IW Control).
Zet er op te beginnen maar een TIWLabel component op van de IW Standard tab (of een willekeurig ander IW component van de IW Standard tab). Geef de Caption property de waarde "IntraWeb en HTML 4". De source code van de IWFormMain.pas unit moet er nu - na opslaan - als volgt uitzien:

  unit IWFormMain;
  interface
  uses
    Classes, SysUtils, IWAppForm, IWApplication, IWTypes, Controls,
    IWBaseControl, IWControl, IWCompLabel;

  type
    TIWForm1 = class(TIWAppForm)
      IWLabel1: TIWLabel;
    public
    end;

  implementation
  {$R *.dfm}
  uses
    ServerController;

  initialization
    TIWForm1.SetAsMainForm
  end.

Let vooral op de aanroep van TIWForm1.SetAsMainForm in de initialization sectie die ervoort zorgt dat dit TIWForm1 (afgeleid van TIWAppForm) het main form is - voor HTML 4 clients dan.


TIWAppForm32
Tijd om een tweede gezicht(je) toe te voegen aan de IntraWeb toepassing. Doe File | New - Other, ga naar de IntraWeb tab van de Object Repository, en dubbelklik op de New Form icon voor de IntraWeb New Form dialoog:

Kies voor de Application Form 3.2, en klik op OK. Dit levert een nieuwe unit (Unit2.pas) die we kunnen opslaan in IWFormMain32.pas. Dit form is afgeleid van TIWAppForm32 en maakt daarmee gebruik van HTML 3.2. We kunnen hier alle componenten van de IW Standard 32, IW Data 32, en enkele componenten van IW Control op gebruiken.
De unit heeft niet de aanroep naar SetAsMainForm in de initialization sectie (op zich logisch: er hoeft maar één main form te zijn - maar wel eentje per target: HTML 4 of HTML 3.2). Om te zorgen dat dit nieuwe form gebruikt wordt als main form voor HTML 3.2 browsers moeten we een regel met TIWForm2.SetAsMainForm toevoegen aan de initialization sectie (die er nog niet is).
Zet vervolgens maar een TIWLabel32 component op van de IW Standard 32 tab (of een willekeurig ander IW component van de IW Standard 32 tab). Geef de Caption property de waarde "IntraWeb en HTML 3.2". De source code van de IWFormMain.pas unit moet er nu - na opslaan - als volgt uitzien:

  unit IWFormMain32;
  interface
  uses
    Classes, SysUtils, IWAppForm32, IWApplication, IWTypes, Controls,
    IWBaseControl, IWControl32, IWCompLabel32;

  type
    TIWForm2 = class(TIWAppForm32)
      IWLabel321: TIWLabel32;
    public
    end;

  implementation
  {$R *.dfm}

  uses
    ServerController;

  initialization
    TIWForm2.SetAsMainForm
  end.

Browser Test
Laten we eens kijken hoe dit eruit ziet, en of Netscape 4 inderdaad als "3.2" browser herkend wordt (dat zou betekenen dat IntraWeb 5.1 eindelijk weer ondersteuning biedt voor Netscape 4 - via HTML 3.2). Doe Run | Run (of F9) om te IntraWeb StandAlone toepassing te draaien:

Start nu Internet Explorer 6 en type http://localhost:7777 in de addressbalk. Het resultaat is als volgt (geen verrassingen hier):

En nu proberen we hetzelfde eens met de Netscape Navigator versie 4.7 (nog steeds mijn mail/news-reader en browser waarmee ik mijn eigen website test voor HTML-compatibility).
Het resultaat laat zien dat Netscape 4 inderdaad als "HTML 3.2" browser herkend wordt: in plaats van een TIWAppForm krijgen we een TIWAppForm32:

Om de HTML 3.2 output ook te kunnen testen zonder Netscape Navigator 4.7 op je machine te hoeven hebben, kunnen we ook de groene knop (vierde van links - de "Override browser type and use HTML 3.2 mode" knop) indrukken van de IntraWeb StandAlone toepassing. Als we vervolgens op de linker knop drukken (om de default browser te starten) dan krijgen we Internet Explorer - in HTML 3.2 mode. Scheelt wellicht weer een hoop extra moeite bij het testen.


Data Module
Tijd om naar de data module te gaan kijken in DataModuleUnit.pas. Dit is een gewone data module, zoals we die in Delphi gewend zijn, en daar kunnen we dus van alles op kwijt. Aangezien de Borland Database Engine (BDE) absoluut geen goed idee is om te gebruiken in een web server toepassing, maak ik hier gebruik van dbExpress om met de InterBase database te praten die bij Delphi 7 werd meegeleverd.
Zet een TSQLConnection component op de data module. Open de drop-down combobox voor de ConnectionName property en zet die op IBLocal (of een andere dbExpress driver naar DB2, InforMix of SQL Server als je die liever gebruikt). Klik eventueel met de rechter muisknop op de TSQLConnection component om de connection properties goed in te stellen (denk aan de User_Name en Password bijvoorbeeld, alhoewel je die ook door de gebruiker via de browser kan laten invoeren - liefst via een veilige SSL verbinding natuurlijk).

Merk op dat ik de Database niet alleen naar de lokatie van de employee.gdb database laat wijzen, maar ook als prefix het IP-adres (of de DNS naam) van de server zelf opgeef. Op die manier kan ik altijd een verbinding maken, ook als de database op een andere machine draait dan de web server zelf.
Zet daarna in ieder geval altijd de LoginPrompt property van de TSQLConnection component op False, want die kunnen we niet gebruiken in een web server toepassing.
Nu is het de beurt aan een TSQLDataSet component (die de functionaliteit van TSQLTable, TSQLQuery en TSQLStoredProc in zich herbergt), noem hem SQLdsCUSTOMER. Laat de SQLConnection property van de SQLdsCUSTOMER TSQLDataSet component wijzen naar de TSQLConnection component.
Dubbelklik nu op de CommandText property om de dbExpress Query CommandText Editor te starten. Gebruik deze om een SQL query te bouwen, zoals select * from CUSTOMER

We hebben nu nog drie componenten nodig (van de Data Access tab) om de data uit deze SQLdsCUSTOMER ook echt te gebruiken (dbExpress datasets zijn namelijk unidirectional en read-only, en dus niet altijd zonder meer te gebruiken). Zet een TDataSetProvider, een TClientDataSet en een TDataSource component op de data module. Noem de TDataSetProvider component dspCUSTOMER, noem de TClientDataSet component cdsCUSTOMER, en noem de TDataSource component dsCUSTOMER. Laat de DataSet property van de dspCUSTOMER TDataSetProvider component wijzen naar de SQLdsCUSTOMER TSQLDataSet component, de ProviderName property van de cdsCUSTOMER TClientDataSet component wijzen naar de dspCUSTOMER TDataSetProvider component, en tot slot de DataSet property van de dsCUSTOMER TDataSource component wijzen naar de cdsCUSTOMER TClientDataSet component.
De data module zou er nu als volgt uit moeten zien:


IW Data Tab
Tijd om de aandacht te verleggen naar de IW Data tab van het Component Palette (het gebruik van de meeste IW Standard componenten wijst zichzelf wel). De componenten van de IW Data tab zijn alleen bruikbaar op de TIWAppForm (en genereren HTML 4), terwijl de componenten van de IW Data 32 tab te gebruiken zijn op de TIWAppForm32 (die HTML 3.2 genereert).
Zet twee componenten naar de TIWLabel component op de TIWAppForm, namelijk TIWDBNavigator en TIWDBGrid. Zet vervolgens eerst de Align property van TIWLabel op alTop, daarna de Align property van de TIWNavigator op alTop, en tot slot de Align property van de TIWDBGrid op alClient. De Alignment property van TIWLabel kan op taCenter gezet worden, alhoewel dat niks uithaalt.
Om een verbinding te kunnen maken met de data module, zullen we de DataModuleUnit moeten toevoegen aan de uses clause (bijvoorbeeld met Alt+F11). Hierna kunnen we de DataSource property van de TIWNavigator en de TIWDBGrid op DataModule1.dsCUSTOMER zetten.

Merk op dat we geen data zien tijdens design-time (zoals "normaal" in Delphi) - zelfs niet als we de datasets actief maken tijdens design-time (iets wat niet aan te raden is overigens, omdat je het project dan moeilijk kunt openen op een andere machine die niet bij de InterBase database kan). Om de data te zien tijdens runtime, moeten we er voor zorgen dat de datasets tijdig geopend worden, bijvoorbeeld in de OnCreate event handler van de TIWAppForm, die daarvoor gewoon gebruik kan maken van de UserSession, daar de DataModule1 van, en daarvan de cdsCUSTOMER die - als hij aktief wordt - de rest van de dbExpress-keten in beweging zal zetten:

  procedure TIWForm1.IWAppFormCreate(Sender: TObject);
  begin
    UserSession.DataModule1.cdsCUSTOMER.Active := True
  end;

Zet nu van de TIWDBGrid component de dbIndicator subproperty van de Options property op True. Dit zorgt ervoor dat we kunnen zien wat het huidige record is (in het grid). Tijdens het draaien zien we dit als een extra eerste kolom met een sterretje erin (bij het zevende record in dit geval):

Het TIWDBGrid biedt overigens alleen maar read-only mogelijkheden om de data te bekijken, en niet om de data te wijzigen. Om data echt aan te passen moeten we bijvoorbeeld TIWDBEdits gebruiken (die handig we in het TIWDBGrid kunnen hangen, zoals ik tijdens de Clinic zal laten zien). Na nog wat properties wijzigen, kan het uiteindelijke resultaat er als volgt uit komen te zien:

Merk daarbij nog op dat we gebruik maken van een dbExpress dataset (via een TDataSetProvider en TClientDataSet), zodat we er dus wel rekening mee moeten houden dat alle Posts die we doen via de TIWNavigator alleen op de TClientDataSet plaatsvinden - en niet in InterBase zelf. Om ook in de InterBase database zelf de wijzigingen te posten moeten we bijvoorbeeld de volgende code schrijven in de OnAfterPost (en OnAfterDelete) event handler van de cdsCUSTOMER TClientDataSet component:

  procedure TDataModule1.cdsCUSTOMERAfterPostOrDelete(DataSet: TDataSet);
  begin
    (DataSet as TClientDataSet).ApplyUpdates(0)
  end;

IW Data 32 Tab
Laten we eens kijken wat de mogelijkheden zijn van de IW Data 32 tab - de componenten dus die HTML 3.2 kunnen genereren voor Netscape 4 en PDA devices. Vergeleken met de "normale" IW Data tab missen we enkele componenten, namelijk de TIWDBGrid, TIWDBFile, en - helaas - TIWDBNavigator. We zullen dus met-de-hand moeten navigeren (door zelf "next" en "prev" buttons neer te zetten bijvoorbeeld).

De bijbehorende source code voor de vier OnClick event handlers is als volgt (niet zo erg spannend):

  procedure TIWForm2.IWButton32FirstClick(Sender: TObject);
  begin
    UserSession.DataModule1.cdsCUSTOMER.First
  end;

  procedure TIWForm2.IWButton32PrevClick(Sender: TObject);
  begin
    UserSession.DataModule1.cdsCUSTOMER.Prior
  end;

  procedure TIWForm2.IWButton32NextClick(Sender: TObject);
  begin
    UserSession.DataModule1.cdsCUSTOMER.Next
  end;

  procedure TIWForm2.IWButton32LastClick(Sender: TObject);
  begin
    UserSession.DataModule1.cdsCUSTOMER.Last
  end;

Helaas komen de controls in de browser allemaal onder elkaar terecht: net als in een PDA zou moeten (maar in Netscape 4 niet nodig is). Dit is het default gedrag van de HTML 3.2 forms, en dus een "feature" en geen "bug".
Om wel enige controle over de controls te krijgen, en ze neer te zetten waar we ze hebben willen, moeten we een speciale Layout Manager gebruiken. Voor HTML 3.2 forms hebben we de keuze uit een TIWTemplateProcessorHTML32 en TIWLayoutMgrHTML32. De laatste lijkt in eerste instantie iets makkelijker in gebruik (maar ook iets breekbaarder), dus zet eerst een TIWLayoutMgrHTML32 op het HTML 3.2 form. Voordat je nu dubbelklikt om de HTML Layout Editor te krijgen is het zinvol om eerst de LayoutMgr property van het form naar de Layout Manager te laten wijzen. Dubbelklik daarna op de TIWLayoutMgrHTML32 component om de HTML Layout Editor te starten:

Ga naar de source tab, waar je HTML kunt editen of verwijderen. De HTML ziet er oorsponkelijk bijvoorbeeld als volgt uit:

  object IWLayoutMgrHTML321: TIWLayoutMgrHTML32
    Enabled = True
    HTML.Strings = (
      '<HTML>'
      '<HEAD>'
      '</HEAD>'
      '<BODY>'
      '{%IWLabel321%}<P></P>'
      '{%IWButton32First%}<P></P>'
      '{%IWButton32Prev%}<P></P>'
      '{%IWButton32Next%}<P></P>'
      '{%IWButton32Last%}<P></P>'
      '{%IWDBEdit321%}<P></P>'
      '{%IWDBEdit322%}<P></P>'
      '{%IWDBEdit323%}<P></P>'
      '{%IWDBEdit324%}<P></P>'
      '{%IWDynGrid1%}<P></P>'
      '</BODY>'
      '</HTML>')
    Left = 360
    Top = 120
  end

Zoals we zien, worden er speciale "IntraWeb" tags gebruikt om aan te geven waar de controls moeten komen. Helaas heb ik gemerkt dat de HTML Layout Editor extreem gevoelig is als je de HTML met de hand wil editen. Met name het selecteren van de <P></P> en dan op de <del> toets drukken om deze tekst weg te halen heeft nogal eens tot gevolg dat de HTML Layout Editor gewoon verdwijnt en daarmee ook heel Delphi meeneemt. Geen melding, geen pardon, zomaar "poef!" uit de lucht!
Het doel is uiteindelijk om de <P></P> achter de eerste drie IWButton32s te verwijderen (zodat deze op één regel komen), en de <P></P> achter de IWEdit32s te vervangen door een <BR>, maar dit lukt bij mij nooit echt, dus laat ik dit component maar links liggen (helaas).

TIWTemplateProcessorHTML32
Een stabieler component is de TIWTemplateProcessorHTML32 component. Zet dit component op het HTML 3.2 form (verwijder eventueel de TIWLayoutMgrHTML32) en zorg dat de LayoutMgr naar het TIWTemplateProcessorHTML32 component wijst. De TIWTemplateProcessorHTML32 werkt met dezelfde IntraWeb tags, en kan als startpunt de HTML gebruiken die we eerder in de TIWLayoutMgrHTML32 zagen. Voor mijn voorbeeld kom ik uiteindelijk op de volgende inhoud van een template:

  <HTML>
  <BODY>
  {%IWLabel321%}<P></P>
  {%IWButton32First%}{%IWButton32Prev%}{%IWButton32Next%}{%IWButton32Last%}<P></P>
  {%IWDBEdit321%}<BR>
  {%IWDBEdit322%}<BR>
  {%IWDBEdit323%}<BR>
  {%IWDBEdit324%}<P></P>
  {%IWDynGrid1%}
  </BODY>
  </HTML>

We moeten alleen zorgen dat de IWTemplateProcessorHTML32 Als we klaar zijn om het te proberen, kun je met F9 de IntraWeb toepassing weer draaien. Zorg ervoor dat je deze keer de knop met het kleine groene vierkantje (een PDA schermpje) ingedrukt hebt, zodat de "3.2" versie van de IntraWeb toepassing wordt gestart:

Door op de linker knop te drukken wordt de default browser gebruikt (dat is weer Internet Explorer), en deze keer zien we de "3.2" versie van de IntraWeb toepassing. Door op de First, Prev, Next en Last knoppen te drukken kunnen we navigeren door de customer tabel:

Uiteraard had ik ook hier edit en post/cancel functionaliteiten kunnen inbouwen, maar het gaat om het idee.

Wie met Netscape 4 nu verbinding maakt met de IntraWeb 5.1 toepassing zal ook de HTML 3.2 (PDA) output krijgen, waardoor nu ook weer deze kleine groep gebruikers (waaronder ikzelf soms) kunnen bedienen.

Meer Informatie
Wie meer mogelijkheden wil ervaren met IntraWeb 5.1 en Delphi 7, zou zeker een bezoek aan mijn Delphi IntraWeb Clinic op kunnen overwegen (ik ben een IntraWeb Authorized Trainer).
Mocht iemand nog vragen, opmerkingen of suggesties hebben, dan hoor ik die het liefst via .


This webpage © 1999-2006 by webmaster drs. Robert E. Swart (aka - www.drbob42.com). All Rights Reserved.