Webslice, böse SEO Falle

Monday, March 08, 2010 11:30:25 PM (Mitteleuropäische Zeit, UTC+01:00)

Im Rahmen des EYSA Wettbewerbs hatte ich den ein oder anderen Webslice entwickelt. Unter anderem hab ich auch einen Webslice für das dotnet-forum.de erstellt, der die aktuellen Beiträge anzeigt.
Ein Webslice ist nichts anderes, als ein DIV Container in der HTML-Seite, der mit einem Inlinestyle-Attribut  ausgeblendet wird (style="display: none").
Durch die CSS Klasse hslice erkennt der IE8 diesen DIV Container als Webslice und zeigt ihn nach der Installation an:

webslice

So weit so gut, das Webslice funktioniert. Als ich das Webslice damals erstellt hatte, habe ich mich an Beispielen aus der MSDN orientiert und die Headline des Webslice als H2 eingebaut:

Webslice-code 


Und genau diese Notation hat den Besucherstrom, der über Google ins Forum kommt, drastisch reduziert. Google hat diese H2 Headline für so wichtig gehalten, das dieser Text in den Google Suchergebnissen immer als Headline angezeigt wurde. Bsp:

serps 

Wer klickt schon auf "Aktuelle Beiträge aus dem dotnet-forum.de" wenn man nach "WMI Abfrage für Ereignisprotokoll" gesucht hat?

Das Verhalten von Google ist für mich vollkommen unverständlich, denn zum einen ist der DIV Container ausgeblendet und zum anderen ist auf der entsprechenden Seite eine ordentliche META Description und eine H1 Headline vorhanden. Beide sind besser für die Headline der Suchergebnisse geeignet.

Behelfen kann man sich, in dem man das H2 entfernt und durch ein SPAN ersetzt:

<span class="entry-title">Aktuelle Beiträge aus dem dotnet-forum.de</span>

Kick it on dotnet-kicks.de

Suchanbieter für eine Webseite erstellen

Sunday, May 03, 2009 7:50:12 PM (Mitteleuropäische Sommerzeit, UTC+02:00)

Von vielen Webseiten kennt man Suchanbieter, die sich in das kleine Suchfenster aller gängigen Browser integrieren lassen. Mit diesen Suchanbietern kann auf der entsprechenden Webseite gesucht werden, auch wenn man sich gerade nicht auf dieser Webseite befindet. Dabei macht der Suchanbieter nichts anderes, als die bereits vorhandene Query-URL der Webseite aufzurufen und das gesuchte Wort an diese URL zu übergeben. Bei den meisten dynamischen Webseiten ist so eine Suchseite schon vorhanden. Hier zum Beispiel die URL für dotnet-snippets.de:

http://dotnet-snippets.de/dns/Default.aspx?query=Mathe

Um die Suchseite und den Browser zusammen zu bringen hat Amazon.com bzw. A9.com die OpenSearch Technik entwickelt. Die Integration dieser Technik in eigene Seiten ist einfach.
Als erstes legt man eine XML Datei mit dem folgenden Inhalt an.

<?xml version="1.0" encoding="UTF-8" ?> 
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>dotnet-snippets.de</ShortName>
<Description>Search .net Codesnippets</Description>
<Image>src="http://www.dotnet-snippets.de/dns/bilder/net.ico"</Image>
<Url type="text/html" template="http://dotnet-nippets.de/dns/default.aspx?query={searchTerms}" />
</OpenSearchDescription>

Diese Datei enthält die folgenden Parameter, die für dotnet-snippets.de gültig sind. Diese sollten für die eigene Webseite angepasst werden. Ich habe die Parameter als Beispiel drin gelassen.

ShortName: Eine kurze Beschreibung, zB. der Name der Webseite
Description: eine Beschreibung, die aussagt was auf der Webseite zu finden ist
Image: eine URL zu einem so genannten FavIcon, ein Icon welches 16x16 Pixel groß sein sollte
(Diese FavIcons kann man sehr komfortabel hier kostenlos erstellen: http://www.favicon-generator.de/)
Url type: Das ist die URL der Suchseite mit dem Platzhalter {searchTerms}. In diesen Platzhalter fügt dann der Suchanbieter den gesuchten Text ein.

Nach dem Hochladen dieser XML Datei auf den Webserver muss die Datei noch verlinkt werden. Den Link platziert man im Head der Webseite:

<link title="dotnet-snippets.de - Suche" type="application/opensearchdescription+xml" rel="search" href="http://dotnet-snippets.de/dns/Tools/dns.xml" />

Der Title und der href muss hier wieder individuell angepasst werden. Verwendet man eine ASP.NET Masterpage, sollte der Link im Head der Masterpage positioniert werden.

Wurde die XML Datei richtig verlinkt, erkennt nun der Browser den Suchanbieter und zeigt dies an:

suchanbieter

Möchte man den Besucher der Webseite auf den Suchanbieter hinweisen, kann man einen Link anbieten, um den Suchanbieter zu installieren:

<a href="javascript:window.external.AddSearchProvider("URL zur XML Datei");" onclick="">Suchprovider installieren</a>

Weiterführende Informationen und optionale Parameter für die XML Datei befinden sich auf der Webseite von opensearch.org und in der MSDN.

Ich denke, das ist eine sinnvolle Ergänzung für jede dynamische Webseite, die kaum Arbeit kostet.

Kick it on dotnet-kicks.de

Einfacher Formularspamschutz mit JavaScript

Thursday, April 23, 2009 4:52:20 PM (Mitteleuropäische Sommerzeit, UTC+02:00)

Im dotnet-forum.de kommt es immer wieder vor, das sich Spammer registrieren. Diese Spammer registrieren sich unter zufällig erzeugten Namen und das war’s. Sie posten nichts und hinterlassen nicht einmal einen Link im Profil. Was die Spammer mit diesen Registrierungen erreichen wollen, ist mir ein Rätsel. Sicher ist nur, dass diese Anmeldungen manuell durchgeführt werden, das habe ich durch ein paar Experimente herausgefunden. Was die Spammer scheinbar noch nicht mitbekommen haben, ist, dass sie nach wenigen Minuten verbannt werden. Ein verbannter User kann sich nicht einmal anmelden. Das automatische Verbannen erreiche ich mit Hilfe eines Spamfilters, den ich für den CommunityServer entwickelt habe. Der Filter findet bekannte Spamadressen über einen externen Dienst und nutzt zusätzlich eine Blacklist, die ich selbst pflege. Somit arbeitet er sehr zuverlässig. Diesen Spamfilter habe hier schon einmal vorgestellt.

Im zweiten Schritt der Spambekämpfung habe ich etwas früher angesetzt, damit die Spammer sich erst gar nicht anmelden können. Ich habe nach einer Lösung gesucht, mit der ich Personen aussperren kann, die kein Deutsch können. Das hat nichts mit meiner politischen Einstellung zu tun, sondern damit, dass Spammer oft aus Ländern kommen, in denen die Arbeitskraft wenig kostet…

Zur Lösung des Problems gab mir Thomas einen entscheidenden Tipp. Er hat eine serverseitige Lösung mit dem Namen The Riddle entwickelt. Der User, der das Formular absenden möchte, muss zwei Zahlen addieren und das Ergebnis ausgeschrieben in eine TextBox eintragen. Damit setzt man voraus, dass der User etwas deutsch kann und ggf. auch mit Umlauten umgehen kann. Zuerst wollte ich die Lösung von Thomas übernehmen, habe mich aber später dafür entschieden, eine clientseitige Variante davon zu entwickeln. Das hat den Vorteil, dass Assemblys des zugrunde liegenden Systems (im meinem Fall CommunityServer) nicht angefasst werden müssen. Es wäre auch auf nicht-.NET Systeme leicht übertragbar.

Spamschutz

Bei meiner Lösung ist der Absende-Button deaktiviert wenn das Formular geladen wird, hat man das Ergebnis richtig ausgeschrieben, wird der Button aktiviert und das Registrierungsformular kann abgesendet werden.

Es folgt das kurze JavaScript:

var numbers = new Array("null", "eins", "zwei", "drei", "vier", "f&uuml;nf", "sechs", "sieben", "acht", "neun", "zehn", "elf", "zw&ouml;lf", "dreizehn", "vierzehn", "f&uuml;nfzehn", "sechzehn");
var number1;
var number2;
var SendButtonID = ""; // ID des Absende - Buttons eintragen!

function InitializeRiddle() {
number1 = GetRandomNumber();
number2 = GetRandomNumber();
document.getElementById("task").innerHTML = "<b>Spamschutz: Addiere " + numbers[number1] + " und " + numbers[number2] + " !</b><br />Das Ergebnis muss <b>ausgeschrieben</b> werden.<br />Beispiel: sieben und sechs ergeben dreizehn!";
}

function CheckResult() {
var result = number1 + number2;
var resultToCheck = document.getElementById("result").value.toLowerCase();
resultToCheck = resultToCheck.replace(/ö/g, '&ouml;');
resultToCheck = resultToCheck.replace(/ü/g, '&uuml;');

if (numbers[result] == resultToCheck) {
document.getElementById(SendButtonID).disabled = false;
}
else {
document.getElementById(SendButtonID).disabled = true;
}
}

function GetRandomNumber() {
var randomNumber = Math.random();
randomNumber = 8 * randomNumber;
randomNumber = Math.round(randomNumber);
return randomNumber;
}

Den Spamschutz kann hier ausprobiert werden.

Fazit: So ein Schutz kann es den Spammern immer nur erschweren ihr Vorhaben umzusetzen und somit hab ich auch die Spammer nicht vollständig aussperren können. Es war allerdings ein deutlicher Rückgang an russischen und indischen Registrierungen spürbar. Außerdem schützt es das Forum vor Usern die nicht 6 und 7 addieren können ;-)

Kick it on dotnet-kicks.de

ADO.NET Data Services (Astoria): XML nicht als RSS Feed anzeigen

Friday, August 22, 2008 12:01:51 AM (Mitteleuropäische Sommerzeit, UTC+02:00)

Eins der neuen Features im .NET Framework 3.5 SP1 sind die ADO.NET Data Services, diese sind auch unter dem Namen Astoria bekannt.

Wer die Data Services nutzen möchte und den Internet Explorer verwendet, sollte die folgende Einstellung vornehmen:

Extras -->Internetoptionen --> Inhalte --> Feeds --> Einstellungen --> Feedleseanzeige einschalten --> deaktivieren

Mit Hilfe dieser Einstellung werden die XML Daten vom Internet Explorer nicht als RSS Feed angezeigt.
Das Ergebnis ist deutlich besser lesbar ;-)

Feedanzeige

Kick it on dotnet-kicks.de

Der Einsatz von HTTP 301 und 302 Statuscodes in ASP.NET

Sunday, July 06, 2008 1:32:58 AM (Mitteleuropäische Sommerzeit, UTC+02:00)

Die Methode Response.Redirect() kennt vermutlich jeder ASP.NET Entwickler. Mit Hilfe dieser Methode wird ein Seitenbesucher auf eine andere Webseite umgeleitet. Doch was steckt dahinter? Wann sollte man die Redirect Methode nutzen und wann nicht?

Schauen wir uns an was passiert, wenn folgender Code ausgeführt wird:

Response.Redirect("http://dotnet-snippets.de/dns/Default.aspx");


Der Webserver antwortet dem Client mit dem HTTP Statuscode 302, dies bedeutet das die Webseite temporär umgeleitet wurde.
Zitat von Wikipedia:
Die angeforderte Ressource steht vorübergehend unter der im „Location“-Header-Feld angegebenen Adresse bereit (in HTTP/1.0 „Moved Temporarily“, RFC 1945). Die alte Adresse bleibt gültig.

Im Trace sieht dies folgendermaßen aus:

302  

Wenn eine Seite temporär unter einer anderen Adresse erreichbar ist, sollte Response.Redirect() verwendet werden. Ist eine Seite zukünftig immer unter einer anderen Adresse erreichbar, sollte der HTTP Statuscode 301 verwendet werden.
Der Statuscode 301 steht für eine permanente Umleitung. Zitat von Wikipedia:
Die angeforderte Ressource steht ab sofort unter der im „Location“-Header-Feld angegebenen Adresse bereit. Die alte Adresse ist nicht länger gültig.
Dies ist sehr wichtig für Suchmaschinen, denn somit bekommen Suchmaschinen die Information, dass die alte URL aus dem Index entfernt werden kann.

Wie kann erreicht werden, dass der Statuscode 301 gesendet wird?


HttpContext.Response bietet hierzu die Eigenschaft Status und die Methode AddHeader an:

HttpContext.Current.Response.Status = "301 Moved Permanently";
HttpContext.Current.Response.AddHeader("Location", newUrl);

Um diese 301 - Weiterleitung jederzeit komfortabel nutzen zu können bietet sich eine Extension Method an:

using System.Web;

namespace Welker.Extensions
{
    public static class Extensions
    {
        public static void RedirectPermanently(this HttpResponse response, string newUrl)
        {
            HttpContext.Current.Response.Status = "301 Moved Permanently";
            HttpContext.Current.Response.AddHeader("Location", newUrl);
        }
    }
}

Nach dem Einbinden der Erweiterungsmethode kann diese über folgenden Code aufgerufen werden:

Response.RedirectPermanently("http://live.com");

Im Trace kann man den Unterschied deutlich sehen:

301

Have Fun!

Kick it on dotnet-kicks.de

Das .NET BlogBook in geht in die 6. Runde

Monday, April 14, 2008 9:44:06 PM (Mitteleuropäische Sommerzeit, UTC+02:00)

Alle 3 Monate gibt es eine neue Version vom .NET BlogBook. Von Version zu Version werden nicht nur Fehler korrigiert, es kommen selbstverständlich neue Artikel von Kai Gloth und Norbert Eder hinzu.
Heute ist das .NET BlogBook in der 6. Ausgabe erschienen.
Es wurde mit Artikeln aus den Bereichen WPF und .NET Framework bereichert.

Der Download lohnt sich für jeden praxisorientierten .NET Entwickler.

Kick it on dotnet-kicks.de

dotnet-forum.de mit eigener Mitgliederkarte

Saturday, April 12, 2008 7:49:22 PM (Mitteleuropäische Sommerzeit, UTC+02:00)

Aus vielen Internetforen kennt man Mitgliederkarten, in denen zu sehen ist, wo welches Mitglied zu Hause ist. So kann man auf einen Blick erkennen, wer in der Nähe wohnt.
Für das dotnet-forum.de setze ich den CommunityServer ein, eine hervorragende Community Plattform mit sehr vielen netten Features.
Leider gibt es keine "Membermap".
Auf externe Dienste, wie zum Beispiel Frappr, wollte ich nicht zurückgreifen, weil sie kaum konfigurierbar und meiner Meinung nach, auch nicht schön sind.
Solch eine Karte kann mit bestehenden APIs relativ schnell entwickelt werden. Ich habe mich als Grundlage für die Microsoft Virtual Earth API entschieden. Schnell musste ich allerdings feststellen, dass die Microsoft Klassen und Methoden viel zu langsam sind um einige hundert User auf der Karte darzustellen. Dies liegt daran, dass die komplette Logik auf dem Client läuft. Wenn z.B. 200 User dargestellt werden sollen, muss das clientseitige JavaScript bei jedem Seitenaufbau alle geografischen Koordinaten bei Microsoft abfragen. Das dauert bei 200 Usern und einer schnellen Internetverbindung etwas über 20 Sekunden.

Ich habe mich entschieden, die geografischen Koordinaten auf dem Server in der Datenbank zu speichern. Dank des ASP.NET Ajax Frameworks ist es nicht sehr schwer, die Daten im Hintergrund vom Server zu laden.
Wenn ein User seinen Wohnort im Profil ändert oder ein neuer User hinzukommt, holt der Webserver sich nur einmal die neuen Geodaten.

Ich hab zwei weitere Gimmicks implementiert: Jeder User wird mit einem farbigen Pushpin angezeigt Dabei werden User, die gerade online sind, mit einem grünen Pin gezeigt. Der eigene Pushpin ist rot, alle anderen sind blau.
Die benötigten Daten frage ich mit Hilfe der CommunityServer API ab.
Weiterhin habe ich eine Umkreissuche implementiert, auch hier ist wieder die komplette Logik auf dem Server.

Die Karte ist ab sofort online und ist für jedes registrierte Mitglied des dotnet-forum.de nutzbar.
Es können natürlich nur Mitglieder angezeigt werden, die auch einen Wohnort im eigenen Profil angegeben haben. ;-)

UsermapIII
Die Umkreissuche

UsermapIV 
Mouseover Effekt

Kick it on dotnet-kicks.de

SQL Befehle einer Linq Abfrage ausgeben

Sunday, November 25, 2007 7:53:30 PM (Mitteleuropäische Zeit, UTC+01:00)

Daniel Walzenbach hat in seinem Blog gezeigt wie man die von Linq erzeugte SQL Abfrage in die Console umleiten kann.
Was machen nun die Webentwickler die keine Console haben?
Eine Möglichkeit ist, den Inhalt in den Debugger Output zu schreiben, dazu hat Kris Vandermotten eine kleine Klasse geschrieben die in seinem Blog zu finden ist.
Wer sich die SQL Abfrage im Browser ansehen möchte, kann  den StringWriter verwenden wie der folgende Code zeigt:

DataContext db = new DataContext();

StringWriter stringWriter = new StringWriter();
db.Log = stringWriter;

// Linq Abfrage

Response.Write(stringWriter.ToString());

Kick it on dotnet-kicks.de

ASP.NET: Externe und interne Links unterscheiden

Saturday, November 24, 2007 1:02:04 AM (Mitteleuropäische Zeit, UTC+01:00)

In Foren fällt in letzter Zeit immer öfter die Kennzeichnung externer Links auf.
Zum Beispiel in dieser Form:

outgoingLink 
(Screenshot von aspnetzone.de)

Wie kann in ASP.NET ermittelt werden, ob der Link auf eine interne oder externe URL zeigt?
Für diese Unterscheidung reicht ein Einzeiler:

/// <summary>
/// Determines whether the link is outgoing
/// </summary>
/// <returns>
///     <c>true</c> if [is outgoing link]; otherwise, <c>false</c>.
/// </returns>
private bool IsOutgoingLink(string url)
{
    return new Uri(Request.Url.AbsoluteUri).Host != new Uri(url).Host;
}

Die zweite Frage ist, wie kann in einem Text eine URL erkannt werden? Hier helfen Reguläre Ausdrücke weiter. Die folgende Funktion gibt true zurück wenn eine gültige URL übergeben wurde.

/// <summary>
/// Determines whether the specified candidate is a URL.
/// </summary>
/// <param name="candidate">The candidate.</param>
/// <returns>
///     <c>true</c> if the specified candidate is URL; otherwise, <c>false</c>.
/// </returns>
private bool IsURL(string candidate)
{
    Regex regex = new Regex(@"http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?");
    return regex.IsMatch(candidate);
}

Kick it on dotnet-kicks.de

Visual Studio 2008 – Meine Highlights

Wednesday, November 21, 2007 12:38:40 AM (Mitteleuropäische Zeit, UTC+01:00)

Nein, ich möchte nicht den einhundertsten Blogeintrag schreiben, dass das Visual Studio 2008 released wurde.
Vielmehr möchte ich meine persönlichen Highlights aufzählen:

Javascript-Degugger: Ab sofort braucht man keine Tools wie Firebug oder Fiddler mehr.

XAML Designer: unverzichtbar für die Entwicklung von WPF Anwendungen

Umschaltbare Frameworkversion: man kann zwischen den Versionen 2.0, 3.0 und 3.5 auswählen.

ASP.NET Vorschau beschleunigt:

Endlich muss man nicht mehr ewig warten, wenn von der Quellansicht zur Entwursansicht umgeschaltet wird.

Wer die vielen neuen Features in deutsch ausprobieren möchte, muss sich noch bis Anfang nächsten Jahres gedulden.
Die englischen Expressversionen gibt es hier.

Kick it on dotnet-kicks.de

Ausgeben der aktuellen Serverzeit mit ASP.NET AJAX

Monday, November 12, 2007 11:12:32 PM (Mitteleuropäische Zeit, UTC+01:00)

Immer wieder wird in unterschiedlichen Foren gefragt wie man die aktuelle Zeit des Webservers auf einer Webseite anzeigen kann.
Dies ist mit reinem ASP.NET nicht möglich, es sei denn, man bringt den Benutzer der Webseite dazu, im Sekundentakt (oder schneller) die F5 Taste zu drücken :-)
Wie kann das Problem also gelöst werden? Vielleicht mit JavaScript? Nein, denn JavaScript wird auf der Client-Seite ausgeführt, das bedeutet, es wird die Uhrzeit des Client Rechners angezeigt und ob die immer stimmt, lasse ich mal offen.  Ganz ohne JavaScript geht es trotzdem nicht.
Im folgenden Beispiel wird ein JavaScript in regelmäßigen Abständen die aktuelle Uhrzeit vom Server abfragen und das Ergebnis in ein Textfeld schreiben.
Die aktuelle Uhrzeit liefert ein Webservice der sich auf dem Server läuft.
Als Proxy zwischen JavaScript und Webservice dient ein ASP.NET AJAX ScriptManager:

<asp:ScriptManager ID="ScriptManager1" runat="server">
    <Services>
        <asp:ServiceReference Path="WebServiceServerTime.asmx" />
    </Services>
</asp:ScriptManager>

Im Scriptmanager wird der Webservice referenziert, der die Zeit als String zurückgibt.
Der Webservice liegt im gleichen Verzeichnis wie die Webseite, die die Zeit ausgeben soll. Der Webservice ist sehr übersichtlich:

using System;
using System.Web.Script.Services;
using System.Web.Services;

[WebService(Namespace = "http://tempuri.org/")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class WebServiceServerTime : WebService
{
    [WebMethod]
    public string GetTime()
    {
        return DateTime.Now.ToString();
    }
}

Wichtig ist hierbei das Attribut [ScriptService]. Mit diesem Attribut ist der Webservice auch für JavaScript-Clients verfügbar.
Kommen wir nun zur Client Seite, wie bereits erwähnt, geht es nicht ohne JavaScript:

<script language="javascript" type="text/javascript">

    function CallTimeservice() 
    {
        ret = WebServiceServerTime.GetTime(onGetTime);
        setTimeout('CallTimeservice()', 500);                        
    }

    function onGetTime (arg) 
    {
        var textfeld = document.getElementById('input1');          
        textfeld.value = arg;          
    }

</script>

Das dazugehörige Textfeld:

<input id="input1" readonly="readonly" type="text" />

Das Script beinhaltet eine Methode und einen Ereignishandler.
Die Methode CallTimeservice () wird alle 500 ms neu aufgerufen.
Die aktuelle Zeit die vom Webserver übertragen wurde, wird durch den Handler onGetTime() in das Textfeld geschrieben.
Leider gibt es in JavaScript keine Sleep() oder Wait() Methode desshalb muss die SetTimeout() Methode verwendet werden.

Warum habe ich 500 ms gewählt?
Das Nyquist-Shannon-Abtasttheorem besagt, dass die Abtastfrequenz mindestens doppelt so hoch sein muss wie das Ursprungssignal um Informationsverlust zu vermeiden.
Das bedeutet im Klartext: Die Zeit die auf dem Server mit DatTime.Now.ToString() abgefragt wird, ändert sich mit 1Hz, also muss die Zeit mit 2 Hz (f=1/s --> 1/2 Hz = 0,5s) abgefragt werden, damit der User einen gleichmäßigen Timer ticken sieht. Der Wert darf natürlich auch kleiner als 500ms sein, dies wäre genauer, erhöht aber die Netzwerklast des Servers zusätzlich.

Zum Erstellen des Projektes sollte man zuerst ASP.Net AJAX installieren, dann kann im Visual Studio eine „Ajax Enabled Webseite“ erstellt werden.

AjaxEnabledWebsite

Diese Vorlage hat den Vorteil, dass die Web.config für Ajax vorbereitet ist und dass sich der Scriptmanager in jeder .aspx Datei befindet.

Mit dieser kleinen Anleitung sollte es nicht schwer fallen einen Countdown oder ähnliches zu realisieren.

Vielleicht sollte Ebay auch mal drüber nachdenken.
So ein Auktionscountdown wäre sicher sinnvoll.

Kick it on dotnet-kicks.de

Popfly Explorer für das Visual Studio

Friday, November 09, 2007 6:20:21 PM (Mitteleuropäische Zeit, UTC+01:00)

Mit Microsoft Popfly ist es möglich, auch ohne Programmierkenntnisse, Webanwendungen online zu erstellen.
Mit der Weboberfläche lassen sich Mashups, Webseiten oder Webanwendungen erstellen, die dann mit anderen Popfly-Nutzern getauscht werden können. Popfly basiert auf JavaScript XML und natürlich auf Silverlight.

Nun gibt es auch einen Popfly Explorer für das Visual Studio.
Der Download ist kostenlos, es ist nur ein gültiger Popfly Account erforderlich.
Der Explorer kann auch in den Expressversionen eingesetzt werden.
Zum Download geht es hier…

Kick it on dotnet-kicks.de

ASP.NET: Emailadressen vor Spam schützen

Tuesday, November 06, 2007 10:18:34 PM (Mitteleuropäische Zeit, UTC+01:00)

Emailadressen im Internet im Klartext anzuzeigen, ist mit einem Problem verbunden, den Spambots. Diese Roboter durchsuchen Webseiten nach Emailadressen, um diese später mit unerwünschter Emailwerbung zu überfluten.
Eine Möglichkeit die Emailadresse für Spambots unsichtbar zu machen, ist die Emailadresse in eine Grafik umzuwandeln.
Das Umwandeln geht mit dem .NET Framework relativ einfach wie dieser Snippet zeigt.
Ausprobieren kann man das auf der Webseite email2image.com.
Die zweite Möglichkeit ist JavaScript:

<script language="javascript"><!--
var localpart="name";
var domainpart="domain.com";
document.write('<a href="mailto:'+localpart+'@'+domainpart+'">'+localpart+'@'+domainpart+'</a>');
//--></script>

Das Script kennt nur 2 Variablen den local-part (vor dem @) und den domain-part (nach dem @).
Wenn die Webseite aufgerufen wird die Emailadresse per document.write als Link in die Seite geschrieben. So wird verhindert, dass die Emailadresse im Quelltext der Seite auftaucht.
Da sich die meisten ASP.NET Entwickler nicht gern mit JavaScript rumärgern, habe ich die folgende kleine Methode geschrieben:

/// <summary>
/// Gets the JavaScript.
/// </summary>
/// <param name="email">The email.</param>
/// <returns></returns>
private string GetJavaScript(string email)
{
MailAddress emailadress = new MailAddress(email);
StringBuilder stringBuilder = new StringBuilder();

stringBuilder.AppendLine("<script language=\"javascript\"><!--");
stringBuilder.AppendLine(string.Format("var localpart=\"{0}\";", emailadress.User));
stringBuilder.AppendLine(string.Format("var domainpart=\"{0}\";", emailadress.Host));
stringBuilder.AppendLine("document.write('<a href=\"mailto:'+localpart+'@'+domainpart+'\">'+localpart+'@'+domainpart+'</a>');");
stringBuilder.AppendLine("//--></script>");
stringBuilder.AppendLine(string.Format("<noscript>{0}&nbsp;[at]&nbsp;{1}</noscript>", emailadress.User, emailadress.Host.Replace(".", "&nbsp;[dot]&nbsp;")));

return stringBuilder.ToString();
}

Der Aufruf der Methode ist denkbar einfach:

labelEmailAdresse.Text = "Kontakt: " + GetJavaScript(local@domain.com);

Die Darstellung der Emailadresse ist von einer Normalen Adresse nicht zu unterscheiden:

NoSpamJavaOn

Wird JavaScript vom Browser nicht unterstützt wird die Adresse in folgendem Format ausgegeben:

NoSpamJavaOff

Kick it on dotnet-kicks.de

Zellen im GridView deaktivieren

Saturday, November 03, 2007 1:41:07 PM (Mitteleuropäische Zeit, UTC+01:00)

In manchen Fällen ist es notwendig ein zum Beispiel einen Linkbutton im GridView zu deaktivieren. Dies kann der Fall sein, wenn ein bestimmter Datensatz nicht gelöscht werden darf:

GridView1

Dies kann im RowDataBound Event mit folgendem Code erreicht werden:

protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        if (e.Row.DataItem != null)
        {
            if (…) // Bedingung zum deaktivieren des Linkbuttons
                e.Row.Cells[0].Enabled = false; 
        }
    }

Dabei wird folgender HTML Code erzeugt:

<td disabled="disabled">
 <a onclick="return confirm('Wirklich Löschen?');" disabled="disabled" id="ctl00_CPH1_ GridView1_ctl10_LinkButton1" style="color:Red;">Löschen</a>
</td>

Dieser HTML Code wird vom IE7 sehr sinnvoll interpretiert, der Link wird ausgegraut wie im oberen Bild zu erkennen ist.
Wird die Seite mit dem Firefox aufgerufen, der das disabled Attribut anders auswertet, gibt’s eine böse Überraschung:

GridView2 

Der Link ist sichtbar und optisch von nicht gesperrten Links nicht zu unterscheiden. Man kann den Link anklicken, die JavaScript Sicherheitsabfrage wird angezeigt, aber der Eintrag wird nicht gelöscht weil der Firefox keinen Postback sendet.
Dieses Verhalten macht keinen Sinn und wird kein User akzeptieren.

Möchte man, das die Seite auf allen Browsern läuft, sollte folgender Code verwendet werden.

protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        if (e.Row.DataItem != null)
        {
            if (…) // Bedingung zum deaktivieren des Linkbuttons
                e.Row.Cells[0].Text = String.Empty;
        }
    }

So sind die deaktivierten Links zwar nicht mehr sichtbar aber die Lösung ist auf jeden Fall kundenfreundlicher.

GridView3

Folgender HTML Code wird dabei für die Zelle generiert:

<td></td>

Weiterführende  Informationen:
Elemante ausgrauen bei SelfHTML
"Wirlich Löschen?" im GridView

Kick it on dotnet-kicks.de