Lies diesen Artikel und viele weitere mit einem kostenlosen, einwöchigen Testzugang.
Im Beitrag „Benutzerverwaltung mit verschlüsselten Kennwörtern“ stellen wir eine Lösung vor, in der wir die Berechtigungen von Benutzergruppen an Datenbankobjekten definieren. Dort benötigen wir ein Formular, mit dem wir die Berechtigungen für die einzelnen Objekte für die jeweilige Benutzergruppe definieren wollen. Dazu wollen wir eine Matrix anzeigen, welche die Berechtigungen und die Objekte als Spalten- und Zeilenüberschriften anzeigt und die Möglichkeit bietet, in den Zellen anzugeben, ob die Berechtigung gesetzt ist oder nicht. Dies erledigen wir mit einem Webbrowser-Steuerelement, indem wir die notwendigen Elemente per HTML darstellen.
Der erste Entwurf des Formulars sieht wie in Bild 1 aus. Wir haben dem Formular ein Kombinationsfeld namens cboBenutzergruppeID hinzugefügt sowie ein Webbrowser-Steuerelement namens objWebbrowser.
Bild 1: Erster Entwurf des Formulars
Das Kombinationsfeld soll die Benutzergruppen aus der Tabelle tblBenutzergruppen anzeigen, und zwar so, dass das Feld BenutzergruppeID als gebundene Spalte ausgeblendet wird und nur die Bezeichnung der Benutzergruppe erscheint. Dazu weisen wir der Eigenschaft Datensatzherkunft die folgende Abfrage zu:
SELECT BenutzergruppeID, Benutzergruppe FROM tblBenutzergruppen ORDER BYBenutzergruppe;
Damit nur die Benutzergruppe angezeigt wird, stellen wir die Eigenschaft Spaltenanzahl auf 2 und die Eigenschaft Spaltenbreiten auf 0cm ein.
Für das Zusammenstellen des HTML-Code benötigen wir noch die Objektbibliothek Microsoft HTML Object Library, die wir wie in Bild 2 über den Verweise-Dialog des VBA-Editors hinzufügen (Menüeintrag Extras|Verweise).
Bild 2: Verweise des VBA-Projekts
Gewünschte Darstellung
Wir möchten nicht einfach nur eine Darstellung mit Kontrollkästchen oder ähnlichen Steuer-elementen, sondern einmal etwas Besonderes programmieren: nämlich eine Darstellung, bei der die gesetzten Rechte in Grün und die nicht gesetzten Rechte in Rot dargestellt werden. Das sieht dann etwa für die Benutzergruppe Administratoren wie in Bild 3 aus.
Bild 3: Setzen der Berechtigungen über verschiedene Farben
Funktionen des Formulars
Das Formular soll, wie oben erwähnt, die Auswahl einer der Benutzergruppen über das Kombinationsfeld oben im Formular ermöglichen. Nach der Auswahl sollen die Berechtigungen für die gewählte Benutzergruppe angezeigt werden.
Außerdem wollen wir die folgenden Funktionen durch Anklicken der verschiedenen Bereiche der HTML-Seite im Webbrowser-Steuerelement realisieren:
- Wenn der Benutzer auf die rot markierte Berechtigung Keine klickt und diese so auf Grün einstellt, sollen automatisch die übrigen Berechtigungen für die Tabelle für diesen Benutzer auf Rot eingestellt werden.
- Wenn der Benutzer die grün markierte Berechtigung Keine anklickt und diese so auf Rot einstellt, sollen die übrigen Berechtigungen für diese Tabelle auf Grün eingestellt werden.
- Wenn der Benutzer eine der Berechtigungen Lesen, Anlegen, Ändern oder Löschen anklickt, während diese Rot ist, soll diese auf Grün eingestellt werden und umgekehrt.
- Dabei gilt die folgende Sonderregel: Wenn er die letzte grün markierte Berechtigung (also Lesen, Anlegen, Ändern oder Löschen) anklickt und diese so auf Rot einstellt, soll automatisch die Berechtigung Keine auf Grün eingestellt werden.
- Klickt der Benutzer auf einen der Spaltenköpfe Keine, Lesen, Anlegen, Ändern oder Löschen, werden alle Einträge dieser Spalte auf Grün eingestellt. Handelt es sich um den Spaltenkopf Keine, werden alle anderen Spalten komplett auf Rot eingestellt. Handelt es sich um eine der anderen Spalten, wird diese komplett auf Grün eingestellt und die Spalte Keine logischerweise komplett auf Rot.
Wenn Keine grün ist, müssen also alle anderen Spalten für diese Tabelle rot sein, wenn eine der anderen Spalten grün ist, muss Keine rot sein.
Initialisieren des Formulars
Im Klassenmodul des Formulars deklarieren wir die folgenden Variablen im allgemeinen Teil, das heißt, die Variablen sind modulweit zugreifbar:
Dim WithEvents objWebbrowser As WebBrowser Dim WithEvents objDocument As HTMLDocument Public colCells As Collection Public colCellWrapper As Collection
Die ersten beiden Variablen referenzieren später das Webbrowser-Steuerelement sowie das darin angezeigte Dokument. Die übrigen beiden, colCells und colCellWrapper, sind Collection-Objekte, welche Objekte aufnehmen, die wir auf Basis zweier weiter unten beschrieben Klassen erstellen €“ und zwar für jedes Element der Tabelle aus der Kopfzeile und den farbig zu markierenden Tabellenzellen.
Beim Ã-ffnen des Formulars weisen wir der Variablen obj-Webbrowser das Steuer-element ctlWebbrowser.Object zu. Außerdem stellen wir die Eigenschaft TimerInterval auf 50 ein, was dazu führt, dass die Ereignisprozedur Bei Zeitgeber 50 Millisekunden nach dem Einstellen dieser Eigenschaft aufgerufen wird:
Private Sub Form_Open(Cancel As Integer) Set objWebbrowser = Me!ctlWebbrowser.Object Me.TimerInterval = 50 End Sub
Die durch das Ereignis Bei Zeitgeber ausgelöste Prozedur zeigt zunächst eine leere Seite im Webbrowser-Steuerelement an und stellt die Variable objDocument auf die Eigenschaft Document des Webbrowser-Steuerelements ein. Dann stellt sie TimerInterval auf 0, damit das Ereignis Bei Zeitgeber nicht nochmals ausgelöst wird. Schließlich stellt sie das Kombinationsfeld cboBenutzergruppeID auf den ersten Eintrag der Datensatzherkunft ein und ruft die Prozedur TabelleErstellen auf. Dieser übergibt sie die aktuell im Kombinationsfeld ausgewählte Benutzergruppe als Parameter, damit die Prozedur die dazu gehörenden Berechtigungen im Webbrowser-Steuerelement anzeigt:
Private Sub Form_Timer() objWebbrowser.Navigate "about:blank" Set objDocument = objWebbrowser.Document Me.TimerInterval = 0 DoEvents Me!cboBenutzergruppeID = Me!cboBenutzergruppeID.ItemData(0) TabelleErstellen Me!cboBenutzergruppeID End Sub
Die Prozedur TabelleErstellen wird außerdem aufgerufen, wenn der Benutzer im Kombinationsfeld cboBenutzergruppe einen anderen Eintrag auswählt. Dazu legen wir die folgende Prozedur an, die durch das Ereignis Nach Aktualisierung des Kombinationsfeldes aufgerufen wird:
Private Sub cboBenutzergruppeID_AfterUpdate() TabelleErstellen Me!cboBenutzergruppeID End Sub
CSS-Definition
In der nachfolgend mit der Prozedur TabelleErstellen zusammengestellten HTML-Tabelle verwenden wir auch einige Zeilen CSS-Code. Diese stellen wir mit der folgenden Funktion zusammen. Die ersten CSS-Anweisungen legen die Schriftart, Schriftgröße, Ausrichtung und Rahmeneigenschaften für die Tabellen fest:
Public Function CSS() Dim strCSS As String strCSS = strCSS & "table {" strCSS = strCSS & " font-family: Calibri;" strCSS = strCSS & " text-align: center;" strCSS = strCSS & " width: 100%;" strCSS = strCSS & " border-collapse: collapse;" strCSS = strCSS & "}" strCSS = strCSS & "td.columnHeader {" strCSS = strCSS & " font-weight: bold;" strCSS = strCSS & " border-bottom: 1px solid black;" strCSS = strCSS & "}" strCSS = strCSS & "td.rowHeader {" strCSS = strCSS & " font-weight: bold;" strCSS = strCSS & " border-right: 1px solid black;" strCSS = strCSS & " text-align: left;" strCSS = strCSS & "}"
Dann folgen zwei Definitionen für CSS-Klassen, welche die Hintergrundfarben für die roten und grünen Zellen festlegen:
strCSS = strCSS & "td.redcell{" strCSS = strCSS & " border-color: white;" strCSS = strCSS & " border-width: 1px;" strCSS = strCSS & " border-style: solid;" strCSS = strCSS & " background: red;" strCSS = strCSS & "}" strCSS = strCSS & "td.greencell{" strCSS = strCSS & " border-color: white;" strCSS = strCSS & " border-width: 1px;" strCSS = strCSS & " border-style: solid;" strCSS = strCSS & " background: green;" strCSS = strCSS & "}"
Dann benötigen wir noch zwei Klassen für die Breite der ersten Spalte (50%) und die der folgenden fünf Spalten (jeweils 10%):
strCSS = strCSS & ".th1 {" strCSS = strCSS & " width:50%;" strCSS = strCSS & " text-align:left;" strCSS = strCSS & "}" strCSS = strCSS & ".th2 {" strCSS = strCSS & " width:10%;" strCSS = strCSS & " text-align:left;" strCSS = strCSS & "}"
Schließlich fehlt noch eine CSS-Klasse namens scrolling, welche die Höhe des zu scrollenden Bereiches festlegt und dass dieser gescrollt werden kann:
strCSS = strCSS & ".scrolling {"
strCSS = strCSS & " height:300px;"
strCSS = strCSS & " overflow:auto;"
strCSS = strCSS & "}"
CSS = strCSS
End Function
Tabelle zusammenstellen
Nach dieser Vorbereitung kommen wir zur Prozedur TabelleErstellen, deren ersten Teil Sie in Listing 1 finden. Diese Prozedur deklarieren wir als öffentliche Prozedur, damit wir später von Klassenobjekten, die wir von dieser Prozedur aus erstellt haben, erneut auf diese Prozedur zugreifen können, um die Tabelle neu zu erstellen.
Public Sub TabelleErstellen(lngBenutzergruppeID As Long) Dim db As DAO.Database Dim rstSpaltenkoepfe As DAO.Recordset Dim rstZeilen As DAO.Recordset Dim rstWerte As DAO.Recordset Dim objTable As MSHTML.HTMLTable Dim objRow As MSHTML.HTMLTableRow Dim objCell As MSHTML.HTMLTableCell Dim objCSS As Object Dim objCellHeaderWrapper As clsCellHeaderWrapper Dim objInnerTable As MSHTML.HTMLTable Dim objDiv As MSHTML.HTMLDivElement Set colCells = New Collection Set colCellWrapper = New Collection Set objCSS = objDocument.createStyleSheet("") objCSS.cssText = CSS Set db = CurrentDb objDocument.body.innerHTML = "" Set objTable = objDocument.createElement("Table") objDocument.body.appendChild objTable Set objRow = objTable.insertRow Set objCell = objRow.insertCell objCell.className = "columnHeader rowHeader th1" Set rstSpaltenkoepfe = db.OpenRecordset("SELECT BerechtigungID, Berechtigung FROM tblBerechtigungen", dbOpenDynaset) Do While Not rstSpaltenkoepfe.EOF Set objCell = objRow.insertCell objCell.className = "columnHeader th2" objCell.innerText = rstSpaltenkoepfe!Berechtigung Set objCellHeaderWrapper = New clsCellHeaderWrapper With objCellHeaderWrapper Set .cell = objCell .BerechtigungID = rstSpaltenkoepfe!BerechtigungID .BenutzergruppeID = Me!cboBenutzergruppeID Set .Form = Me End With colCellWrapper.Add objCellHeaderWrapper rstSpaltenkoepfe.MoveNext Loop
Listing 1: Prozedur TabelleErstellen, Teil 1
Die Prozedur erwartet die ID der Benutzergruppe, deren Berechtigungen an den verschiedenen Tabellen der Datenbank in der HTML-Tabelle dargestellt werden sollen. Sie deklariert eine ganze Reihe von Variablen. Die drei Variablen rstSpaltenkoepfe, rstZeilen und rstWerte nehmen die Recordsets auf, deren Daten wir in der HTML-Tabelle abbilden wollen. Wie wir diese genau füllen, erfahren Sie weiter unten.
Dazu benötigen wir einige Objekte, mit denen wir die Objekte der HTML-Seite definieren und hierarchisch anordnen. Wir wollen in diesem Beispiel nicht wie etwa in dem Beispiel zum Beitrag HTML-Tabellen mit fester Kopfzeile (www.access-im-unternehmen.de/1188) den HTML-Code in einer String-Variablen zusammensetzen, sondern diesmal das Objektmodell für HTML verwenden (DOM, Document Object Model).
Die ersten Anweisungen der Prozedur erzeugen jeweils neue Collection-Objekte für die beiden Variablen colCells und colCellWrapper, die wie im Kopf des Klassenmoduls deklariert haben. Dann erstellen wir die Klasse, welche den Code der Funktion CSS als Wert der Eigenschaft cssText aufnimmt. Außerdem benötigen wir ein Database-Objekt für den Zugriff auf die Objekte der aktuellen Datenbank.
Für das mit der Variablen objDocument referenzierten Objektvariablen für das Dokument im Webbrowser-Steuerelement stellen wir die Eigenschaft body.innerHTML auf eine leere Zeichenkette ein. Dann erstellen wir für objDocument mit der createElement-Methode ein neues Element des Typs Table und hängen es mit der appendChild-methode an das body-Objekt von objDocument an. Als Nächstes fügen wir unterhalb des Table-Objekts aus objTable mit der insertRow-Methode ein neues Row-Element ein und referenzieren es mit objRow. Darin fügen wir nun ein erstes Cell-Objekt ein, welches der linken, oberen Zelle entspricht, die keinen Text aufnehmen soll. Dazu verwenden wir die insertCell-Methode von objRow und speichern das Ergebnis in objCell. objCell ist das erste Objekt, dem wir CSS-Klassen zuweisen. Das erledigen wir über die Eigenschaft className, der wir den Text columnHeader rowHeader th1 zuweisen. Dadurch nimmt das Element die in den drei CSS-Klassen td.columnHeader, td.rowHeader und th1 angegebenen Attribute an.
Danach füllen wir die erste Recordset-Variable namens rstSpaltenkoepfe, und zwar mit den Feldern BerechtigungID und Berechtigung der Datensätze der Tabelle tblBerechtigungen. Diese durchlaufen wir anschließend in einer Do While-Schleife über alle Einträge des Recordsets. In der Do While-Schleife erstellen wir jeweils ein neues Cell-Objekt und referenzieren diese wieder mit objCell. Wir weisen über die Eigenschaft className die beiden CSS-Klassen columnHeader und th2 zu. Außerdem wollen wir den in den Zellen anzuzeigenden Text definieren. Diesen entnehmen wir dem Feld Berechtigung des aktuellen Datensatzes des Recordsets rstSpaltenkoepfe.
Ereignisse für Spaltenköpfe
Für diese Spaltenköpfe, jeweils einer für jeden Berechtigungstyp, wollen wir nun jeweils eine Ereignisprozedur definieren, die beim Anklicken des Spaltenkopfes ausgelöst wird. Das können wir nicht so einfach wie bei einer Schaltfläche in einem Access-Formular erledigen, denn es handelt sich ja bei den Tabellenzellen nicht um Steuerelemente, die wir dem Formularentwurf hinzufügen und deren Ereigniseigenschaften wir dann definieren und mit Ereignisprozeduren hinterlegen können. Es ist etwas komplizierter. Wir benötigen für jedes mit Ereignissen zu versehende Element eine eigene Instanz einer Klasse, die dann wiederum auf das darin deklarierte Element verweist und die gewünschten Ereignisse des Elements implementiert.
Wir schauen uns die Sache erst einmal der Seite unserer Prozedur an, mit der wir die HTML-Tabelle erstellen. Dort legen wir für jedes Cell-Element für eine Berechtigung ein neues Objekt auf Basis der Klasse clsCellHeaderWrapper an und speichern es in der Variablen objCellHeaderWrapper. Diesem weisen wir dann über die Eigenschaft cell die Objektvariable objCell zu. Die Eigenschaft BerechtigungID erhält den Wert des Feldes BerechtigungID des aktuellen Datensatzes des Recordsets rstSpaltenkoepfe. Die Eigenschaft BenutzergruppeID erhält den Wert des Kombinationsfeldes cboBenutzergruppeID des Formulars. Außerdem stellen wir über die Eigenschaft Form noch einen Verweis auf das Formular ein (Me).
Da wir objCellHeaderWrapper schon im nächsten Schleifendurchlauf mit den Eigenschaften des nächsten Cell-Objekts füllen, speichern wir die aktuelle Instanz noch schnell mit der Add-Methode in der Collection colCellWrapper. Danach bewegen wir den Datensatzzeiger zu nächsten Datensatz und beginnen die Schleife von vorn, bis alle fünf Berechtigungen durchlaufen sind. Damit steht schon einmal die erste Zeile der Tabelle.
Tabellenkörper hinzufügen
Den zweiten Teil der Prozedur TabelleErstellen finden Sie in Listing 2. Hier füllen wir das Recordset rstZeilen mit allen Datensätzen der Tabelle tblTabellen. Bevor wir diese durchlaufen, legen wir noch einige weitere Elemente an. Dem Objekt objTable fügen wir mit insertRow eine neue Zeile hinzu, die wir in objRow speichern. Dieser fügen wir diesmal nur eine einzige Zelle hinzu, die sich aber durch den Wert 6 für die Eigenschaft colSpan über die kompletten sechs Spalten der ersten Zeile der Tabelle erstreckt.
Set rstZeilen = db.OpenRecordset("SELECT * FROM tblTabellen", dbOpenDynaset) Set objRow = objTable.insertRow Set objCell = objRow.insertCell objCell.colSpan = 6 Set objDiv = objDocument.createElement("Div") objCell.appendChild objDiv objDiv.className = "scrolling" Set objInnerTable = objDocument.createElement("Table") objDiv.appendChild objInnerTable Do While Not rstZeilen.EOF Set objRow = objInnerTable.insertRow rstSpaltenkoepfe.MoveFirst Set objCell = objRow.insertCell objCell.innerText = rstZeilen!Tabelle objCell.className = "rowHeader th1" Set rstWerte = db.OpenRecordset("SELECT BerechtigungszuordnungID, BenutzergruppeID, TabelleID, " _ "BerechtigungID FROM tblBerechtigungszuordnungen WHERE BenutzergruppeID = " & lngBenutzergruppeID _ & " AND TabelleID = " & rstZeilen!TabelleID, dbOpenDynaset) Do While Not rstSpaltenkoepfe.EOF rstWerte.FindFirst "BerechtigungID = " & rstSpaltenkoepfe!BerechtigungID If Not rstWerte.NoMatch Then colCells.Add ZelleHinzufuegen(objRow, "greencell", rstWerte!BerechtigungszuordnungID, _ rstSpaltenkoepfe!BerechtigungID, rstZeilen!TabelleID, lngBenutzergruppeID) Else colCells.Add ZelleHinzufuegen(objRow, "redcell", 0, rstSpaltenkoepfe!BerechtigungID, _ rstZeilen!TabelleID, lngBenutzergruppeID) End If rstSpaltenkoepfe.MoveNext Loop rstZeilen.MoveNext Loop Inzwischenablage Replace(Replace(objDocument.documentElement.innerHTML, "></", ">" & vbCrLf & "</"), "><", ">" _ & vbCrLf & "<") End Sub
Listing 2: Prozedur TabelleErstellen, Teil 2
Wir erstellen in objDiv ein neues Div-Objekt für objDocument und fügen es mit der appendChild-Methode in das Objekt objCell ein. Diesem Div-Element weisen wir die Klasse scrolling zu. Das Div-Element umfasst also den scrollbaren Bereich der Tabelle €“ wir wollen ja, dass nur der untere Teil der Tabelle gescrollt wird und die Spaltenköpfe am oberen Rand der HTML-Tabelle fixiert werden.
Danach erstellen wir ein weiteres Table-Element, das wir dem Div-Element unterordnen. Dieses Table-Element füllen wir dann in einer Do While-Schleife über alle Datensätze des Recordsets rstZeilen mit der entsprechenden Anzahl Zeilen beziehungsweise Row-Elementen.
In der Do While-Schleife kommen wir nun auf das Recordset rstSpaltenkoepfe zurück, dessen Datensatzzeiger wir mit der MoveFirst-Methode wieder an die erste Position verschieben. Bevor wir dieses Recordset nutzen, folgen noch einige neue Elemente: Zunächst ein erstes Cell-Element, das wir dem aktuellen objRow-Element, also der neuen Zeile der HTML-Tabelle, unterordnen. Diesem ersten Element weisen wir als Text den Namen der Tabelle aus dem Feld Tabelle des Recordsets rstZeilen zu. Außerdem stellen wir die CSS-Klassen rowHeader und th1 für die Eigenschaft className ein.
Damit haben wir die erste Zelle der aktuellen Zeile der HTML-Tabelle mit dem Namen der Tabelle gefüllt, deren Berechtigungen nun in Form von roten oder grünen Kästchen folgen. Dazu füllen wir das Recordset rstWerte mit den Daten der Tabelle tblBerechtigungszuordnungen, deren Benutzergruppe der im Kombinationsfeld ausgewählten Benutzergruppe und deren Tabelle der Tabelle für die aktuelle Zeile entspricht.
Die Datensätze des Recordsets rstSpaltenkoepfe durchlaufen wir nun in einer untergeordneten Do While-Schleife. Nun suchen wir im Recordset rstWerte nach dem Datensatz, dessen Feld BerechtigungID dem Wert des Feldes BerechtigungID des aktuellen Datensatzes des Recordsets Spaltenkoepfe entspricht.
Ist ein solcher Datensatz vorhanden, wurde diese Berechtigung für die Kombination aus Benutzergruppe und Tabelle in der Tabelle tblBerechtigungszuordnungen zugeordnet. Dies prüft die folgende If…Then-Bedingung, welche den Wert von rstWerte.NoMatch untersucht.
Ist die Berechtigung für die Kombination aus Tabelle und Benutzergruppe gesetzt, fügen wir der Collection colCells ein neues Objekt hinzu, das wir mit der Funktion ZelleHinzufuegen ermitteln. Dieser übergeben wir als Parameter das Row-Objekt aus objRow, den Namen der zuzuweisenden Klasse (im Falle einer Berechtigungszuweisung greencell), die Werte der Tabellen tblBerechtigungszuordnungen, tblBerechtigungen und tblTabellen aus den jeweiligen Recordsets sowie die ID der Benutzergruppe.
Wenn kein solcher Datensatz vorhanden ist, wird ebenfalls die Funktion ZelleHinzufuegen aufgerufen. Diesmal wird allerdings der Wert 0 für den Parameter mit der Berechtigungszuordnung übergeben sowie der Klassenname redcell.
Auf diese Weise durchläuft die Prozedur alle Spalten für die aktuelle Tabelle und dann die Spalten in den Zeilen für die übrigen Tabellen. Die letzte Anweisung fügt zu Kontrollzwecken den so erstellen HTML-Code in die Zwischenablage ein €“ Sie können diesen dann etwa in einen Texteditor kopieren, um den Code zu kontrollieren.
Hinzufügen einer Zelle mit der Funktion ZelleHinzufuegen
Die Anweisungen der Funktion ZelleHinzufuegen haben wir in eine eigene Funktion ausgegliedert, weil wir in der zuletzt beschriebenen If…Then-Bedingung in der Prozedur TabelleErstellen sonst annähernd den gleichen Quellcode zwei Mal abgebildet hätten.
Die Funktion sieht wie in Listing 3 aus. Sie erwartet die weiter oben bereits beschriebenen Parameter. Sie erstellt ein neues Cell-Objekt und ordnet dies dem mit dem Parameter objRow übergebenen Row-Objekt unter. Dann stellt sie die Eigenschaft ID dieses Objekts auf den Wert des Parameters lngBerechtigungszuordnung fest. Außerdem stellt Sie die Eigenschaft className auf den entsprechenden Parameter ein, also entweder greencell oder redcell.
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
Testzugang
eine Woche kostenlosen Zugriff auf diesen und mehr als 1.000 weitere Artikel
diesen und alle anderen Artikel mit dem Jahresabo