14. Juli 2011: Warum kompliziert...
Es gibt Tage, an denen man die Welt nicht mehr versteht. Letztens war einer dieser Tage. Ich programmierte ein kleines Tool um Labels auf einem Zebra Drucker auszugeben. Dachte ich Anfangs noch das die Erstellung des Layouts in ZPL die größte Herausforderung werden würde, wurde ich alsbald eines besseren belehrt.
ZPL – das sei an dieser Stelle ausdrücklich erwähnt – ist eine schnörkellose, schnell erlernbare Beschreibungssprache. Hat man sich erst einmal an die Syntax gewöhnt, kann man in kürzester Zeit ansprechende Layouts erstellen. Das man die Anweisungen in Klartext via TCP/IP direkt an den Drucker übergeben kann, macht es auch sehr angenehm die Schnittstelle in eigenen Programmen zu implementieren – ganz gleich welche Sprache man benutzt.
So verwundert es im Nachhinein nicht wirklich, dass dies nicht die befürchtete Herausforderung darstellte. Nun sollte das Tool noch Daten aus einem MS SQL Server laden um diese in das Label-Layout einzufügen. Es ist nicht mein erstes Programm, dass Daten aus einem MS SQL Server laden sollte – und um die Dinge noch einfachere zu machen wählte ich VB.NET als meine Programmiersprache. Doch hier begann die Verzweiflung zuzuschlagen…
Da ich keinen direkten Zugriff auf die Datenbank hatte, musste ich den Umweg über die SSRS gehen. Naiv wie ich bin, ging ich davon aus das dies kein Problem darstellen sollte – gibt es doch entsprechende Objekte und Klassen im .NET Framework. Und siehe da: Man kann sogar mit wenigen Mausklick eine Referenz auf einen SSRS Server anlegen (wenn man die URL kennt und manuell entsprechend aufbereitet):
Aber… da habe ich wohl zu einfach gedacht. Denn trotz mannigfaltiger Ansätze, dem quer-lesen dutzender Artikel und des testens nahezu jeder Funktion der ReportingService2005-Klasse musste ich nach zwei Tagen aufgeben. Zwar konnte ich den Server ansprechen und auch diverse Aktionen durchführen – allein Daten auszulesen war mir nicht möglich. Und darum ging es ja nun schließlich.
Also besann ich mich auf eine althergebrachte Methode: XML. Und ein XML-Export stellt für die SSRS kein Problem dar (wenn man die URL entsprechend manuell aufbereitet). Hier also meine kleine Funktion für all jene, die gegebenenfalls in dieselbe Bredouille geraten:
Dim url As String = _
<ServerURL> + _
<ReportPath> + _
"&rs:Command=Render&rs:Format=XML&" + <ParameterName> + "=" + <ParameterValue>
Dim httpReq As WebRequest = _
WebRequest.Create(url)
With httpReq
.Credentials = CredentialCache.DefaultCredentials
End With
Dim httpDoc As WebResponse
Try
httpdoc = httpReq.GetResponse()
Catch ex As Exception
Debug.Print(ex.Message)
Return False
End Try
Dim xmlDoc As New XmlDocument()
Dim xmlNodes As XmlNodeList
Dim xmlNode As XmlNode
Dim xmlAttr As XmlAttribute
Try
xmlDoc.Load(httpDoc.GetResponseStream())
Debug.Print(xmlDoc.InnerXml)
Catch ex As Exception
Debug.Print(ex.Message)
Return False
End Try
Return True
End Function
15. Januar 2010: Google-Maps Suche
Schon interessant, wie man manchmal bei eigentlich selbstverständlichen Sachen forschen und tricksen muss. Zuletzt bei der Anzeige von diversen Adressen in einer Karte von Google-Maps. Hier sollte der Kartenausschnitt auf die angezeigten Ergebnisse zentriert und der Zoom entsprechend gesetzt werden. Standard-Funktionen gibt es dafür leider nicht, aber mit einem kleinen Trick funktioniert es dennoch recht geschickt:
var geocoder = new GClientGeocoder();
map.setCenter(new GLatLng(25.783662, -80.189466), 9);
var viewport = new GLatLngBounds();
GEvent.addListener(map, 'markeradded', function(){
map.setCenter(viewport.getCenter());
map.setZoom(map.getBoundsZoomLevel(viewport)-1);
});
geocoder.getLatLng("1900 N BAYSHORE DR 4109, Miami FL 33132", function(point){
var marker = new GMarker(point);
GEvent.addListener(marker, 'click', function(){
marker.openInfoWindowHtml("<b>1900 N BAYSHORE DR 4109, Miami FL 33132</b><br /><a href='details/93'>Show Details</a>");
});
map.addOverlay(marker);
if(!viewport.contains(point)){
iewport.extend(point);
}
GEvent.trigger(map, 'markeradded');
});
Der Trick besteht darin im Eventhandler der Marker den hinzugefügten Punkt einem GLatLngBounds-Objekt zu übergeben. Mit Hilfe dieses Objekts kann der Kartenausschnitt neu angepasst werden.
Interessant ist dabei, dass panTo() durch den direkt anschließenden Aufruf von setZoom() nicht korrekt funktioniert. Mit setCenter() gibt es allerdings keine Probleme.
8. Juli 2009: Web-Reiniger
AJAX (Asynchronous JavaScript and XML) war auch so einer Neuerung in der Webentwicklung, die ich verpasst zu haben dachte. Tolle Sachen wurden damit gemacht. Web 2.1 könnte man sagen (und drüber streiten). Dabei ist das aller erschreckend einfach gemacht:
try{
h = new XMLHttpRequest();
}
catch (e){
try{
h = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e){
try{
h = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e){
return(false);
}
}
}
/* Send Data */
h.open('POST', u, true);
h.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
h.setRequestHeader("Content-length", data.length);
h.setRequestHeader("Connection", "close");
h.onreadystatechange = function(){
if(h.readyState == 4 && h.status == 200){
toggleVisibility('processing');
e = h.responseText;
if(e == "1"){
toggleVisibility('success');
}else{
toggleVisibility('failure');
document.getElementById('errormessage').innerHTML = e;
}
}
}
h.send(data);
Gefühlt würde ich ja sagen, dass das funktionieren eher auf Glück und User-Vertrauen (samt aktuellem Browser) basiert. Aber der Erfolg dergleichen spricht ja eher eine andere Geschichte.
8. Juli 2009: Der alte K(r)ampf
Zugegeben, es ist schon einige Jährchen her das ich mich intensiver mit HTML auseinandersetzen musste. CSS war seinerzeit eine tolle Sache, die sich gerade durchzusetzen abzeichnete, Internet Explorer war der Browser schlechthin und Firefox nannte sich noch Phoenix. Und schon seinerzeit war es eine Herausforderung sondergleichen eine Webseite so zu schreiben, dass sie auf allen Browsern in etwa gleich aussah – oder zumindest zumutbar.
Das hat sich bis heute leider nicht wirklich geändert. Auch wenn es wesentlich leichter geworden ist sein Ziel zu erreichen. Immerhin kann man jetzt einfach ein zusätzliches Stylesheet schreiben, dass IE-spezifische Änderungen vornimmt.
<!--[if IE]>
<link rel="stylesheet" type="text/css" href="three14_ie.css" />
<![endif]-->
Tolle Sache!
4. Februar 2009: Textpattern bei 1und1
Nachdem diese Domain ja mittlerweile bei ihrem neuen Hoster angekommen ist, habe ich heute dann auch den letzten Fehler behoben: Saubere URLs in Textpattern. Ein wenig kniffelig, aber durchaus einfach zu lösen. Mit folgender .htaccess-Datei:
Options +FollowSymLinks
RewriteBase /
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.+) - [PT,L]
RewriteRule ^(.*) index.php
8. November 2006: Lokalisierung: EqDKP-Raid Progression
So ein DKP-System ist schon was feines: Je nachdem an wie vielen Raids man teilgenommen hat, hat man auch bessere/schlechtere Chancen an ein begehrtes Item zu gelangen. Die Implementation beim Kreis der Macht ist, nicht zuletzt durch einen sehr vernarrten Gildenleiter, auch entsprechend aufgebohrt – schließlich ist es die Statistik-Seite Nummer Eins der Gilde.
Eine der neuesten Erweiterungen, die eingebunden werden sollte, war das PlugIn Raid Progression zur Verfolgung des Fortschritts in Instanzen. Leider Gottes kam es jedoch nicht mit unserer Art des Eintragens von Raids zurecht. Ein Problem, das scheinbar auch andere Leute haben:
looks nice.
is there a change to modify the plugin, so ite can handel with other Raidnotes like this http://www.seniorenraid.de/set/listraids.php
Nun, die Lösung soll niemandem vorbehalten bleiben:
if(empty($row['raid_note'])) continue;
$elements = explode(" ", str_replace(", ", " ", $row['raid_note']));
foreach($elements as $killed_mob)
foreach($instance as $event => $mobs)
foreach($mobs as $mob => $count)
if(@strstr($mob, $killed_mob))
$instance[$event][$mob] = $row['raid_id'] . ':' . $row['event_id'];
/* Old code
if ( in_array_multi_key($row['raid_note'], $instance) )
{
$instance[$row['raid_name']][$row['raid_note']] = $row['raid_id'] . ':' . $row['event_id'];
}
*/
// Extension end
Einzubauen ist das ganze in die Datei /dkp/plugins/rp/index.php. Die Stelle sollte sich dem geneigten Leser aus dem auskommentierten Code von selbst erschließen.
7. November 2006: Serialisierte Instanzen
Abwärtskompatibilität ist eine tolle Sache. Sie verspricht, dass man die Entwicklungen auf der momentanen Version auch in Zukunft noch nutzen kann und dadurch nicht laufend mit der Wartung seiner alten Programme beschäftigt ist.
Was ich jedoch gestern erfahren musste, hat immer noch einen faden Beigeschmack. Es ging darum, dass eine Webseite mit komplexen adhoc-Berechnungen die zugrunde liegenden Instanzen eines Objektes in einer Datenbank-Tabelle zwischenspeichern sollte. Nichts leichter als das:
$so = serialize($o);
Das Zurückholen in den Applikationskontext zu einem späteren Zeitpunkt ging ebenso leicht:
$o = unserialize($so);
In meiner Test-Umgebung (PHP 4.3.3) hat das alles wunderbar funktioniert. Toll, dachte ich, dass das so einfach geht. Doch zu früh gefreut. In der Produktiv-Umgebung (PHP 4.4.4) fehlten den so wieder ins Leben zurück gerufenen Instanzen auf einmal die Methoden, was neben unschönen Fehlermeldungen auch zu einem Ausfall der Anwendung geführt hat. Laut PHP-Dokumentation soll man, um genau dies zu vermeiden, eine kleine Korrektur vornehmen:
$so = unserialize(implode("", $so));
Leider führte auch das nicht zum gewünschten Ergebnis. Blieb also nur der Biss in den sauren Apfel und ein entsprechend ausschweifender Work-Around (die Daten werden jetzt in einer eigenen Cache-Tabelle zwischengespeichert).
