Intranet vs. Internet mittels CSS

Eine simple, aber dennoch einfache Möglichkeit, zwischen Zugriff aus dem Intranet und dem Internet zu unterscheiden, läßt sich durch Einbinden einer CSS-Style-Datei lösen, die nur im Intranet verfügbar ist.

Hintergrund

Für eine Applikation, die sowohl im Intra- als auch im Internet verfügbar ist, sollte ein Link eingeblendet werden, jedoch in Abhängigkeit davon, ob es sich um interne oder externe Nutzer handelt. Da es sich jedoch dabei um die Login-Maske handelt, die Authentifizierung also noch nicht stattgefunden hat (und damit eine Zuordnung des Nutzerkreises nicht möglich ist), mußte ein anderer Lösungsansatz gefunden werden.

Dabei wurde angenommen, das interne Nutzer nur durch das Intranet auf die Applikation zugreifen, so daß das Problem dadurch zu lösen war, indem auf einem nur von intern erreichbaren Server eine CSS-Style-Datei angelegt wurde. Diese CSS-Style-Datei wurde die in der Login-Maske eingebunden und hatte folgenden Inhalt:

#intLinkHidden {
    display: none;
    visibility: hidden;
 } 

#intLinkShown {
    display: block;
    visibility: visible;
 }

[In Rot: Die CSS-Style-Definition als Individualformat]

Im HTML-Head wurde die CSS-Datei eingebunden:

<link href="http://intranet.example.org/intranet.nsf/linkHider.css"
 type="text/css" rel="stylesheet">

Zusätzlich wurden im HTML-Code der Login-Maske CSS-Style-Klassen definiert:

.extLinkHidden {
    display: none;
    visibility: hidden;
 } 

.extLinkShown {
    display: block;
    visibility: visible;
 }

[In Rot: Definition der CSS-Style-Klassen]

Die beiden Links, die je nach Anwendungsfall eingeblendet werden sollen, wurden wie folgt definiert:

<a id="intLinkShown" class="extLinkHidden"
href="http://intranet.example.org/intranet.nsf/">
Forgot Password?</a>

<a id="intLinkHidden" class="extLinkShown"
href="http://internet.example.org/internet.nsf/">
 Forgot Password?</a>

[In Rot: Die Einbindung via id-Attribut bzw. class-Attribut]

Entscheidend ist hierbei der Umstand, daß Individualformat (Angabe mittels id-Attribut) dem Klassenformat bevorzugt wird. Kann der interne Server erreicht werden, muß sich der Nutzer im Intranet befinden: Die CSS-Datei kann geladen werden, womit die Links die CSS-Vorgabe des id-Attributes berücksichtigen. Ist die CSS-Datei nicht ladbar (und der Nutzer greift demnach via Internet zu), greifen die Angaben des class-Attributes.

Veröffentlicht unter Allgemein, CSS, HTML, Infrastruktur | Verschlagwortet mit , , , , , | Schreib einen Kommentar

Erzwungene „Locale“-Einstellung

Bei der Entwicklung einer Web-Applikation für ein britisches Unternehmen fiel auf, daß die Landeseinstellungen der XPage immer auf Deutsch eingestellt waren. Dies äußerte sich dahingehend, daß das Dojo Toolkit z.B. die Datumsauswahl des Datepickers immer im falschen Format angezeigt hat. Stellt man die Landeseinstellung des Browsers um, ändert sich auch die Clientseitige Darstellung der XPage.

Der Effekt ist darauf zurück zu führen, daß die Einstellung des Browsers herangezogen wird, um die XPage zu rendern. Welche Sprach- bzw. Landeseinstellung herangezogen wird, definiert der Client in seiner Anfrage: Beim Seitenaufruf sendet der Client eine Liste der bevorzugten Landeseinstellungen im HTTP-Request, die vom Domino-Server entsprechend interpretiert wird.

Hier ein Beispiel eines HTTP-Requests (weitere Details finden sich hier):

GET / HTTP/1.1
Host: www.google.de
User-Agent: Mozilla/5.0 (X11; U; Linux i686; ...
Accept: text/html,application/xhtml+xml,appl...
Accept-Language: de-de,de;q=0.8,en-us;q=0.5 ... 
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive

[Fett hervorgehoben: Liste der aktzeptierten Sprachen, nach Präferenz (gekürzt)]

Im generierten HTML-Codes findet man die Landeseinstellung für das Dojo-Toolkits in der Property „djConfig“, die beim Einbinden der JavaScript-Library ausgewertet wird:

<script type="text/javascript" src="/domjs/dojo-1.4.3/dojo/dojo.js"
djConfig="locale: 'de', parseOnLoad: true">
</script>

[Fett markiert: „djConfig“-Variable mit Angabe der Landeseinstellung „de“]

Will man verhindern, daß die Locale-Einstellung des Browsers herangezogen wird, reicht es nicht aus, das „xsp.properties“-File zu bearbeiten, denn das „locale“-Attribut wird von der XPages-Engine falsch interpretiert:

Der Eintrag

xsp.client.script.dojo.djConfig=locale: 'en'

führt zu folgendem HTML-Code:

<script type="text/javascript" src="/domjs/dojo-1.4.3/dojo/dojo.js"
djConfig="locale:'de', parseOnLoad: true, locale:\'en\'">
</script>

[Fett: generierte Variable, in Rot: Doppelter Eintrag mit Escape-Zeichen]

Es gibt keinen Weg, die fehlerhafte Konvertierung des Anführungszeichen zu verhindern. Alle denkbaren Kombinationen und Wege werden nicht korrekt umgesetzt (z.B. die Angabe in Anführungszeichen, HTML-encodiert etc), so daß es auf diesem Weg nicht möglich ist, die Einstellung zu beeinflussen.

Um dennoch eine Vorgabe der Landesvorgabe zu erzwingen, muß in den Application Properties im Domino Designer die Landeseinstellung fest vorgegeben werden.

Dazu legt man in den „Localization Options“ auf dem Reiter „XPages Properties“ eine Einstellung an, die man als Standardeinstellung wie im folgenden Screenshot definiert:

Nach einem „Clean“ des Projektes ist dies die einzige Landeseinstellung der Applikation, egal welche „Wünsche“ der Browser vorgibt. Im HTML-Code wird die gewünschte Vorgabe eingefügt.

<script type="text/javascript" src="/domjs/dojo-1.4.3/dojo/dojo.js"
djConfig="locale:'en', parseOnLoad: true">
</script>

[Fett hervorgehoben: djConfig-Variable / In Rot: die erzwungene Landeseinstellung]


Nachtrag:

Tritt der Fehler „Can’t find resource for bundle javax.faces.Messages, key [ISOCODE]„, liegt dies daran, daß der entsprechende Languagepack nicht installiert ist.

Ist KEINE Locatlization-Einstellung vorhanden, gibt der Browser vor, welches Sprachbundle verwendet werden soll. Sobald eine Localization-Einstellung vorhanden ist, tritt der Fehler nicht mehr auf (das Bundle vorausgesetzt).

Veröffentlicht unter Allgemein, Dojo Toolkit, Java Script, XPages | Verschlagwortet mit , , , , , | Schreib einen Kommentar

Dojo’s Stolpersteine

Das Dojo Toolkit enthält jede Menge interessanter und nützlicher Funktionen, z.B. dojo.fieldToObject(). Die Funktion ist sehr praktisch, da sie die Inhalte eines beliebigen Feldes in einen String bzw. einen Array konvertiert, ohne daß man sich um den Feldtyp kümmern muß.

Es gibt nur eine Besonderheit zu beachten: Zwar wird das jeweilige HTML-Element über die id referenziert, doch aus mir unerklärlichen Gründen (siehe unten) muß bei dem jeweiligen Feld das name-Attribut gesetzt sein, sonst liefert die Funktion null zurück.

<input type="text" id="ID" value="VALUE" name="NAME" />

<script type="text/javascript">
 dojo.addOnLoad( function(){
 var f = dojo.fieldToObject("ID");
 alert( f );
 });
</script>

[Fett hervorgehoben: id über die referenziert wird, und das nötige name-Attribut in rot.]

Dieses Verhalten ist bei allen Dojo-Varianten gleich (aktuelle Version 1.6).

 

Warum ist das so?

Wirft man einem Blick auf die Implementierung der Funktion, so wird das name-Attribut vorausgesetzt, sonst wird das Item nicht vearbeitet:

dojo.fieldToObject = function(/*DOMNode||String*/ inputNode){
 // summary:
 //        Serialize a form field to a JavaScript object.
 //
 // description:
 //        Returns the value encoded in a form field as
 //        as a string or an array of strings. Disabled form
 //        elements and unchecked radio and checkboxes are skipped. 
 //        Multi-select elements are returned as an array of string
 //        values.
 var ret = null;
 var item = _d.byId(inputNode);
 if(item){
 var _in = item.name;
 var type = (item.type||"").toLowerCase();
 if(_in && type && !item.disabled){
 if(type == "radio" || type == "checkbox"){
 if(item.checked){ ret = item.value }
 }else if(item.multiple){
 ret = [];
 _d.query("option", item).forEach(function(opt){
 if(opt.selected){
 ret.push(opt.value);
 }
 });
 }else{
 ret = item.value;
 }
 }
 }
 return ret; // Object
 }

[Fett hervorgehoben: Die Referenzierung erfolgt die Dojo-interne Funktion dojo.byId. In Rot die Überprüfung des name-Attributes.]

In meinen Augen gibt es keinen Grund für diese Überprüfung bzw. das Abbrechen, wenn kein name-Attribut vorhanden ist. Ändert man die Funktion ab und wirft diese Überprüfung raus, bereitet das keinerlei Probleme.

Was auch immer sich die Entwickler hierbei gedacht haben, der Sinn erschließt sich mir leider nicht. Aber ich lassen mich gerne eines besseren belehren…

Veröffentlicht unter Allgemein, Dojo Toolkit, Java Script | Verschlagwortet mit , , , , | Schreib einen Kommentar

Bug: „Unescape“ fehlerhaft implementiert (Domino 8.5.2)

Die Javascript-Funktion unescape ist Serverseitig fehlerhaft implementiert. Nach einem Sonderzeichen wird bei der Konvertierung das darauf folgende einfach ignoriert, wie durch folgendes Beispiel nachvollzogen werden kann.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.beforePageLoad><![CDATA[#{javascript:
print ( unescape("%20ABC%20DEF") );}]]></xp:this.beforePageLoad>
</xp:view> 

[Fett hervorgehoben: der Funktionsaufruf / In Rot: Encodierte Leerzeichen]

Eigentlich müsste beim Aufruf derXPage auf der Serverkonsole der String “ ABC DEF“ ausgegeben werden, doch verschluckt sich die Funktion und schneidet das A als auch das D einfach ab (die Leerzeichen werden allerdings korrekt umgesetzt):

Dies betrifft die Version 8.5.2FP17, unter 8.5.1 tritt der Bug nicht auf. Welche Versionen sonst noch betroffen sind, ist mir nicht bekannt.

Als Workaround kann die Java-Funktion java.net.URLDecoder.decode verwendet werden:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.beforePageLoad><![CDATA[#{javascript:
print ( java.net.URLDecoder.decode ("%20ABC%20DEF", "utf-8") );}]]>
</xp:this.beforePageLoad>
</xp:view>

[Fett hervorgehoben: Aufruf via Java. Wichtig ist die Angabe des Schemas, hier UTF-8 (zweiter Parameter)]

Veröffentlicht unter Allgemein, ServerSide JavaScript, XPages | Verschlagwortet mit , , , , , , | Schreib einen Kommentar

XPages und Reverse-Proxies

Beim Aufsetzen eines Reverse-Proxies gilt es im Zusammenspiel mit XPage-basierten Web-Applikationen ein Detail zu berücksichtigen: Für die Implementierung des Partial Refresh-Mechanismus mußte ein zusätzlicher HTTP-Header eingeführt werden, durch den u.a. Redirects an den Webbrowser gesteuert werden.

Da im Zusammenspiel mit AJAX-basierten Aktionen wie dem Partial Refresh nicht mit vom Server gelieferten HTTP-Responsecodes (302 bzw. 307)  gearbeitet werden kann, wird der HTTP-Response der HTTP-Header „X-XspLocation“ angehängt.

Beim Öffnen eines Server-seitigen Links wird so dem Browser mitgeteilt, wohin die Reise geht, und die eigentliche Umleitung wird Clientseitig (via JavaScript) durchgeführt. Wird der Header also nicht durch die Regeln im Reverse-Proxy überarbeitet, funktioniert die Applikation höchstwahrscheinlich nicht.

Hier ein kurzes Beispiel und die HTTP-Daten, die durch Klicken auf den Link gesendet werden.

Beispiel-Code eines Links in der XPage:

 <xp:link escape="true" id="link1" text="ServerSideLink">
 <xp:eventHandler event="onclick" submit="true"
 refreshMode="norefresh" execMode="partial">
 <xp:this.action>
 <xp:actionGroup>
 <xp:openPage name="/XPage2Open.xsp" />
 </xp:actionGroup>
 </xp:this.action>
 </xp:eventHandler>
</xp:link>

HTTP Request (Client an Server)

POST /xpage.nsf/current.xsp?$$ajaxmode=full&$$ajaxid=%40none HTTP/1.1
Host: blog.hasselba.ch
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0.1) ...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,...
Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: https://blog.hasselba.ch/xpages.nsf/current.xsp
Content-Length: 306
Cookie: DomAuthSessId=XXXXX; SessionID=XXXX
Pragma: no-cacheCache-Control: no-cache

[Fett hervorgehoben: Standard-Header bei AJAX-Requests]

HTTP Response (Server an Client)

HTTP/1.1 200 OK
Server: Lotus-Domino
Date: Thu, 09 Jun 2011 06:22:56 GMT
X-XspLocation: http://blog.hasselba.ch/xpages.nsf/XPage2Open.xsp
Content-Length: 20
Connection: Keep-Alive
Content-Encoding: gzip

[Fett hervorgehoben: Der Header mit dem neuen Ziel. Der Client öffnet die Seite via JavaScript]

Veröffentlicht unter Allgemein, XPages | Verschlagwortet mit , , , , , | 2 Kommentare

IFrames und das „src“-Attribut

Im Domino Designer (V8.5.2)  gibt es ein Problem mit der Einbettung von IFrames in XPages, denn der „<IFRAME>“-Tag wird nicht sauber unterstüzt. Das „src“-Attribut lässt leider keine multiplen URL-Parameter zu, eine Verkettung mittels Und-Zeichen läßt sich nicht speichern:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<iframe src="https://hasselba.ch/blog/?abc=123&def=456">
</iframe>
</xp:view>

Der Designer quittiert einen Speicherversuch mit dieser Meldung:

The reference of entity "def" must end with the ';' delimiter.

Damit man trotzdem mehrere URL-Parameter angegeben werden können, muss das Und-Zeichen URL-encodiert angegeben werden (also als „&amp;“):

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<iframe src="https://hasselba.ch/blog/?abc=123&amp;def=456">
</iframe>
</xp:view>
Veröffentlicht unter Allgemein, XPages | Verschlagwortet mit , , , , | Schreib einen Kommentar