Auf der Suche nach einer guten Lösung um E-Mails via Template versenden zu können habe ich heute ein interessantes Open-Source Projekt gefunden. Town Crier ist eine smarte Lösung, um nicht selber ständig so etwas per "string.replace" machen zu müssen - bzw. sich selber mit dem Gedanken auseinander setzen zu müssen eine Template-Engine in .NET zu entwicklen. Den .NET Source-Code für dieses Projekt kann hier im GitHub-Repository runterladen! Entwickelt wurde die Engine in C# und steht unte der "Lesser GNU Public Licence". Eine kurze Einführung findet man hier.
Bei einem Ur-Alt Portal (DotNetNuke 3.2.2) das noch hobbymäßig betreut habe, steht nun der Wechsel zu einer anderen Plattform an. Da der Benutzer aber kein neues Passwort bekommen sollten, müssen die existierenden Passwörter aus DNN exportiert werden. Die Passwörter wurden über das ASP.NET Membership Model verwaltet und die Passwörter sind nicht als Klartext sondern verschlüsselt in der Datenbank vorhanden. Ein simpler Export der Daten nutzt also an dieser Stelle nichts. Daher habe ich mich heute Abend hingesetzt und eine Quick and Dirty-Lösung entickelt, die es erlaubt einen Webservice aufzurufen, der dann wiederum eine CSV-Datei auf dem Server erstellt. Warum dann einen Webservice? Nun, zunächst hatte ich mir das etwas anderes gedacht - bin dann aber aus lauter Faulheit dazu übergegangen die Daten direkt lokal zu speichen. Von Quellcode her nicht wirklich optimal und wohl alles andere als CCD-konform .. aber es funktionierte für meinen einmaligen Zweck. Sollte das noch öferts anstehen, muss man über eine allgemeingültigere Lösung nachdenken und auch die Export-Funktion anpassen. Obwohl der Code nicht gerade ne Refernz ist, möchte ich diesen veröffentlichen und vielleicht hilft es ja dem einen odere anderen ebefalls die Passwörter seiner DotNetNuke User zu entschlüsseln. An alle Kritiker des Datenschutzes: Ja das habe ich auch anbemängelt, allerdings wollte das mein Gegenüber so habe. Bitteschön UserWebService.zip (7,65 KB)
Im Breadcrumb Skinobject von DotNetNuke wird per Default immer der Seitenname angezeigt. Es kann aber durchaus Fälle geben, bei denen es sinnvoll ist nicht den Seitennamen sondern den Seitentitel anzeigen zu lassen. Der Seitennamen ist die Bezeichnung im Menü und der Seitentitel ist der Browsertitel. Wenn man nun anstelle vom Seitenamen den Seitentitel nutzen möchte, dann kann man dieses per Konfiguration der Komponente ganz einfach erreichen. Hier gibt es die Eigenschaft "UseTitle" und wenn diese mit dem Wert "true" belegt wird, beeinflusst das die Darstellung. Hier als Beispiel: <dnn:BREADCRUMB runat="server" id="dnnBREADCRUMB"
CssClass="Breadcrumb" RootLevel="0"
Separator=" > " UseTitle="True" />
Das Socialmedia Publisher Projekt wurde in der zwischenzeit um eine Anbindung an einen URL Shorter Dienst erweitert. Damit man für die unterschiedlichen Socialmedia Plattformen (Twitter, Facebook, etc.) möglichst flexible bleibt, wurde bei der Implementierung das auch in DotNetNuke sehr oft verwendete Provider Pattern verwendet. Der Vorteil dabei ist, dass erst zur Ausführungszeit entschieden wird, welche konkrete Implementierung verwendet wird. Somit ist es also möglich für Facebook z.B. TinyURL zu verwenden und bei Twitter bit.ly. Die dafür benötigte abstracte Klasse sieht so aus: public abstract class URLShorterProvider
{
#region [ Shared/Static Methods ]
private static URLShorterProvider _urlShorterProvider = null;
public static URLShorterProvider Instance(string typeName)
{
URLShorterProvider urlShorterProvider = null;
CacheManager uscProviderCache = null;
try
{
uscProviderCache = CacheFactory.GetCacheManager ("URLShorterProviderCache");
}
catch
{ }
if (uscProviderCache != null)
urlShorterProvider = (URLShorterProvider)uscProviderCache .GetData ("USC" + typeName);
if (urlShorterProvider == null)
{
urlShorterProvider = (URLShorterProvider)TC.Framework. ProviderPattern.ProviderBase.InitInstance(typeName);
if (uscProviderCache != null)
uscProviderCache.Add("USC" + typeName, urlShorterProvider, CacheItemPriority.High, null, new NeverExpired());
}
return urlShorterProvider;
}
#endregion
#region [ abstract methods ]
public abstract string ShortUrl(string url);
#endregion }
Genutzt wird hier das singelton Pattern in Kombination mit einem Caching. Die eigentliche Implementierung für TinyURL ist sehr einfach gehalten und sieht wie folgt aus: public class TinyURL : URLShorterProvider
{
///Die maximale Länge der URL, bevor diese gekürtzt wird.
private const int MAX_URL_LENGTH = 26;
public override string ShortUrl(string url)
{
try
{
if (url.Length < MAX_URL_LENGTH)
return url;
return new System.Net.WebClient().DownloadString ("http://tinyurl.com/api-create.php?url=" + System.Web.HttpUtility.UrlEncode(url));
}
catch(Exception exc)
{
Diagnostics.ExceptionHelper.HandleException(exc);
return "";
}
}
}Mit dieser Methode kann man nun sehr einfach und ohne großen Aufwand neue URLShoter-Dienste anbinden und per Konfiguration diese Dienste dann zuordnen.
Das bisher in der DotNetNuke Professional verfügbare RibbonBar Control Panel - eine Alternative zu dem normalen ControlPanel - steht jetzt auch für die Community Version zur Verfügung. Im Vergleich zu dem normalen Control Panel, hat der Bearbeiter hier deutlich mehr Möglichkeiten die wichtigen Parameter direkt zu beinflussen. Das RibbonBar Control Panel ist aufgeteilt in für Tabs / Bereiche: - Common Tasks
- Current Page
- Admin
- Host
Der Bereich Commen Tasks ist relativ identisch zu den bisher eingesetzten Control Panel. Zumindest auf den ersten Blick. Hier sind aber auch einige wirklich sinnvolle Erneuerungen aufgenommen. Bei der Neuanlage einer Seite kann man jetzt hier direkt die Informationen Name, Position in der Sitemap, Template, etc. definieren. Das kopieren von bereits existierenden Modulen von anderen Seiten wurde angepasst und kann man nicht nur eine Referenz von einem Modul nutzen, sondern auch ein komplett neues Modul mit den identischen Inhalten und Einstellungen erzeugen - also ein wirklicher 1:1 Kopiervorgang von einem bestehenden Modul. Der Bereich "Current Page" ist neu und bietet die Möglichkeit wichtige Parameter wie Name, Position in der Sitemap oder das vererben von Rechten direkt durchzuführen. Die beiden Bereiche Admin und Host zeigen die Punkte aus dem Administrationmenü an. Das finde ich ganz praktisch, das es immer wieder mal Unterseiten gibt, die über kein Hauptmenü verfügen und somit war man nie in der Lage direkt in einen Bereich von Admin oder Host zu gelangen.
Bei Anwendern die mit einer aktuellen Version vom Firefox 3.6 DotNetNuke administrieren möchten, haben ein Problem an das Kontext-Menü eines Modul zu kommen, da dieses einfach nicht sichtbar ist bzw. nicht angezeigt wird. Um dieses Problem zu beheben muss man in der JavaScript-Datei vom Solpartmenu eine Zeile verändern und dort den z-Index erhöhen. Die entsprechende JavaScriptdatei für das Menü befindet sich vom DotNetNuke-Root aus im Verzeichnis "/controls/SolpartMenu/spmenu.js". In der Zeile 948 befindet sich ein Eintrag: eMenu.style.zIndex = 1 Die Wert von 1 einfach erhöhren z.B. 10000 und anschliesend wird das Kontext-Menü von den DotNetNuke Modulen auch wieder im Firefox angezeigt.
In einem vorherigen Blogpost habe ich über meine Pläne eines Social Networks Publishing-Dienst berichtet. Mittlerweile haben diese Pläne bereits ganz konkrete Formen angemonnen. Eine Herausforderung an dem ganzen Dienst war es, die Aufbereitung der Nachrichten für die einzelnen Dienste wie Twitter, Facebook, etc. Jedes Netzwerk hat andere Möglichkeiten und ich wollte nicht das mögliche Beschränkung" eines einzelnen Social Network alle weiteren in Ihren Möglichkeiten beschränken. Damit man möglichst den vollen Umfang einer Plattform nutzen kann ist es also erforderlich, die jeweiligen Parameter konfigurierbar zu gestalten. Der Ablauf ist nun wie folgt: Es gibt eine zentrale Schnittstelle die z.B. eine Nachricht in der gesamten Länge aufnimmt. Bei der Übergabe wird gleichzeitig definiert für welche Social Plattformen die Nachricht bestimmt ist. Nun wird für jede Plattform die entsprechenden Einstellungen / Konfigurationen geladen, um anschließend die Nachricht für jede Plattform individuell aufzubereiten. Die "fertige" Nachricht wird in einer Datenbank gespeichert und zur Veröffentlichung frei gegeben. Das bedeutet natürlich das die eingehende Nachricht für jede anzusprechende Plattform einen eigenen Eintrag bekommt. Diese Redundanz ermöglicht es eine individuelle und optimierte Nachricht zur Verfügung zu stellen. Nebenbei läßt sich darüber auch eine zeitgesteuertes Veröffentlichung je Plattform realisieren. Die Anbindung eines URL-Shorter für die Links wird der nächste Schritte bei der Implementierung.
Vor ein paar Jahren habe ich mal eine Klasse entwickelt, die es erlaubt mit ASP.NET ein HTML-Formular abzusenden, ohne dieses wirklich auf der ASPX oder ASCX Seite im HTML Quellcode stehe zu haben. So etwas ist z.B. sehr nützlich, wenn man externe Schnittstellen mit Daten beliefern möchte. Die Basics dazu können in meinem ersten Artikel Posting Form Data with c# nachgelesen werden. Im Zuge der Entwicklung meines Social Networks Publishing-Dienst, kam ich heute aber an die Grenze der Klasse, da ich eine Schnittstelle nicht über das HTTP-Verb POST sondern GET ansprechen muss. Zunächst sah dieses recht einfach aus, da man bei der .NET Klasse HttpWebRequest das Verb über die Property METHOD definieren kann. Allerdings bekam ich beim Versenden des Formulars - oder viel mehr beim Aufruf der URL immer eine Fehlermeldung: "Inhaltsteil mit diesem Verbtyp kann nicht gesendet werden." Nach genauer Betrachtung was passiert war mir klar, dass es nicht direkt der Aufruf der URL ist, der diesen Fehler verursacht sondern viel mehr die Tatsache das die Klasse ja versucht einen Stream öffnet um die POST-Daten damit zu übermitteln. Bei der Methode GET ist dieses aber nicht notwendig und somit wird die Verarbeitung mit der Fehlermeldung abgebrochen. Damit die Methode "Send" der Klasse nun sowohl POST als auch GET verarbeiten kann, habe ich den Code wie folgt verändert. public string Send(string content2Post)
{
string sPostData = "" ;
//Zusammensetzen der Daten für die Übermittlung
if (string.IsNullOrEmpty(content2Post))
sPostData = GetContentToPost();
else
sPostData = content2Post;
Uri sUri = null;
HttpWebRequest request = null;
if (this.Method == "POST")
{
sUri = new Uri(this.Url) ;
request = (HttpWebRequest)WebRequest.Create(sUri);
request.Method = Method; // "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = sPostData.Length;
Stream writeStream = request.GetRequestStream();
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] bytes = encoding.GetBytes(sPostData);
writeStream.Write(bytes, 0, bytes.Length);
writeStream.Close();
}
else
{
sUri = new Uri(string.Concat(this.Url, "?", sPostData)) ;
request = (HttpWebRequest)WebRequest.Create(sUri);
request.Method = Method;
request.ContentType = "text/plain";
request.ContentLength = 0;
}
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
//Überprüft den HTTP-Statuscode
if (response.StatusCode != HttpStatusCode.OK)
return "N,-1, HTTPStatuscode:" +response.StatusCode.ToString() ;
Stream responseStream = response.GetResponseStream();
StreamReader readStream = new StreamReader (responseStream, Encoding.UTF8);
return readStream.ReadToEnd();
}
Nur wenn die Methode POST gewählt wurde, wird ein Stream genutzt um die zu übermittelnden Daten dort unterzubringen.
Die Bedeutung Social Networks / Social Media (bzw. Soziale Netwerke) wächst im Augenblick fast täglich. Twitter, Facebook, StudiVZ als Netzwerke aber auch die Social Bookmark Dienste von Google, Microsoft Live, etc. werden deutlich mehr genutzt. Diese Netzwerke werden nicht nur von Kindern und Computergeeks genutzt sondern auch von vielen Firmen, um darüber den Geschäftserfolg zu steigern. Wer nicht in den meisten Netzwerken vertreten ist, vergibt sich unter Umständen jede Menge Chancen. Jetzt kann es aber sehr Aufwendig sein, alle Netzwerke immer mit den Inhalten zu füllen – dabei lässt sich das wirklich gut automatisieren, da viele von den Social Networks (z.B. Twitter) eine API anbieten um Inhalte von externen Tools zu publizieren. Mittlerweile gibt es bereits einige Dienste im Internet, die in der Lage sind verschiedene Netzwerke mit einem Tool zu bedienen. Für meinen Teil habe ich mich entschieden eine eigene Anwendung zu schreiben – nein, nicht aus wirtschaftlichen Gründen sonder einfach weil ich Spaß daran habe – also keine Diskussionen bitte warum ich das Rad noch mal erfinden .. ich hab gerade Spaß dran  Derzeit plane ich ein Publishing-Dienst mit der WCF (Microsoft Windows Communication Foundation) zu erstellen, der über eine universelle Schnittstelle gefüttert werden kann, um dann schließend die Nachrichten in den angegebenen Netzwerken zeitgesteuert zu veröffentlichen. Zur Entkopplung zwischen der universellen Schnittstelle und dem eigentlichen Dienst kommt nach Planung eine Message Queue (MsMq) zum Einsatz, damit die Schnittstelle unabhängig von Dienst zur Verfügung steht um Nachrichten im Empfang zu nehmen. Der eigentliche SocialMedia-Publishing-Dienst soll sich dann darum kümmern das die Nachrichten entsprechend es gewählten Netzwerks aufbereitet werden – z.B. die Länge der Nachricht, Verwendung von URLShorter-Diensten (TinyUrl), usw. Die benötigten Informationen bzw. Konfigurationen werden in einer Datenbank gespeichert, wo auch die einzelnen aufbereiteten Nachricht abgelegt werden. Ein Veröffentlichungsprotokoll rundet dann die Anwendung ab, damit man verfolgen kann, wann welche Nachricht veröffentlicht wurde oder eben warum nicht. Zur Verwaltung des Publishing-Dienst stelle ich mir aktuelle eine kleine ASP.NET MVC Anwendung vor… Wenn jemand noch gute Ideen hat, dann immer her damit :)
Das DNN Control portal:url ist ja sehr flexibel und damit kann man vieles abfangen. Auwahl von Seiten, Verlinkungen, Benutzern, internes Dateisystem, etc. Wenn das Feld aber kein Pflichtfeld ist der Benutzer auch nachträglich noch die Möglichkeit haben soll den Werte wieder zu entfernen, dann muss bei dem Control eine Property setzen: required="False" Durch diesen Parameter erscheint dann "keine Auswahl" in der Drowdownbox.
Ich hatte das "Vergnügen" für die Integration eines Portals mit einem anderen die Schnittstellen zu definieren. Unsere Plattform ist natürlich eine ASP.NET Anwendung (DotNetNuke) und auf der Gegenseite wurde eine Umgebung mit PHP aufgebaut. Wir einigten uns auf einen Webservices ... mal davon abgesehen, dass mein Gegenüber sich noch nie ernsthafter mit den Webservice-Spezifikationen auseinandergesetzt hat ... ist das rein von der Interoperabilität doch bestens geeignet. In der Praxis war es dann aber nicht ganz so einfach, die Daten auch wirklich vom PHP-Code an den ASMX Dienst zu übertragen. Die Methode wurde zwar immer aufgerufen, leider kamen aber keinerlei Parameter an. Schweren herzens habe ich sogar den Kompromiss gemacht und hab in die Definition der Webservice-Methode einzelne Parameter aufgelistet - anstelle einer nachrichtenbasierten Kommunikation mit einem einzigen Parameter. Vereinfacht dargestellt sah die Methode bzw. die Definition der Schnittstelle so aus:
[Webmethod] public string Gibmirdaten(string name, stirng vorname) { . . . . return "Ergebnis: " + name + ", " + vorname; }
Man sollte meine, dass es in PHP genau so einfach ist, einen Webservice zu konsumieren, wie es in .NET ist (und dabei meine ich nicht so einen Schwachsinn wie Rechtsklick und Webverweis hinzufügen :)). Dank Google und einiger sehr hilfreicher Tutorials im Internet habe ich PHP-Krüppel es hinbekommen. Wichtig für die richtige Übertragung der Parameter ist die Umformung bzw. die Verpackung in ein Array. Die Methode new SoapParam(..) - wie man Sie des öfteren sieht - funktioniert leider nicht. Zumindest habe ich das in meine konkreten Beispiel und dem Zusammenspeil mit .NET festgestellt. Ein funktionierende SOAP-Client-Proxy (eigentlich tut es weh so etwas dazu zu sagen) sieht wie folgt aus: var $SOAP = new SoapClient('http://www.meinesite.de/ParterUserReg.asmx?WSDL');
var $params = array('name' => 'Müller', 'vorname' => 'Daniel');
$SOAP->Gibmirdaten($params ); Damit kann man die einzlenen Werte übermitteln. Wenn jemand einen anderen und vielleicht sogar besseren Weg kennt, dann bitte melden :)
Microsoft .NET auf dem iPhone? Das geht? Ja! Das Projekt MonoTouch liefert eine Runetime für das iPhone um dort Anwendungen mit .NET zu entwickeln oder viel mehr um unter in .NET entwickelten Anwendungen auf dem iPhone laufen zu lassen.
Anders als bisherige .NET Anwendungen wird hier aber kein MSIL Code erzeugt, sondern direkt ausführbarer Maschienencode - es gibt also keinen JIT-Compiler auf dem iPhone und ausgeliefert wird nativer Code. Derzeit gibt es MonoTouch nur einem eingeschränkten Betatester-Kreis, das erste offizielle Release ist aber bereits für September 2009 angekündigt. Lassen wir uns überraschen :)
Weiter Infos gibt es hier:
http://www.mono-project.com/MonoTouch
Mir fällt sehr oft auf, dass beim Aufbau einer Anwendung mit mehreren Projekten innerhalb einer Solution, immer wieder eine direkte Abhängigkeit geschaffen wird, indem man beim Projekt sagt: "Verweis hinzufügen" -> Projekte -> Das zu referenzierende Projekt. Gemacht wird so etwas z.B. wenn man die Datenobjekte / -definitionen getrennt von der Logik, Datenzugriff, etc. in einem eigenen Projekt verwalten möchte (was eigentlich so wieso immer getan werden sollte).  Solange man alleine an einem Projekt arbeitet mag das noch okay sein und funktionieren; arbeitet man aber in größeren Projektteams zusammen, wo nicht immer alle die komplette Solution inkl. SourceCode Kompilieren sollen / möchten / können, muss man regelmäßig die Referenzen manuell neu aufbauen. Ich empfehle immer nur die kompilierte Assembly zu referenzieren und alle genutzten Assemblies in einem gemeinsamen Ordner abzulegen. Dann ist es nämlich vollkommen egal, ob man die gesamte Solution, einen Teil oder sogar nur ein Projekt öffnet. Mehr Aufwand bedeutet das für die Konfiguration und Entwicklung auch nicht, da nur einmal bei der Anlage des Projektes ein Post-Build-Kommando in den Einstellungen des Projektes hinterlegt werden muss. Hier ein Beispiel dafür:  xcopy /y /d "$(TargetPath)" "$(SolutionDir)SharedAssemblies\" Der Parameter "/d" sorgt dafür das nur aktueller Versionen kopiert werden und "/y" unterdrückt die Nachfrage, ob vorhandene Dateien überschrieben werden sollen und macht dieses einfach automatisch. By the way... eine schöne Übersicht der XCOPY Parameter gibt es hier bei MicrosoftDamit nun die Reihenfolge der Kompilierung richtig ist und auch Veränderungen von z.B. Basisklassen zuerst neu erzeugt werden, muss man unter den Projektabhängigkeiten nun die einzelnen Projekte angeben. Das ist kein Verweis (!) veranlasst aber Visual Studio in der richtigen Reihenfolge die Projekte zu erstellen! that's it
Wie ich soeben in einem Blogbeitrag von Oliver S. gelesen habe ist das neues Silverlight Release 3 offiziell verfügbar. Neben vielen Erweiterungen im Multimediabereich (Live- und On-Demand True HD Smooth Streaming,Echte HD-Widergabe,Einzigartige Formatvielfalt) finde ich besonders interessante das es nun endliche eine Toolbox mit über 60 Controls (inkl. SourceCode) vorhanden ist. Das sollte die Entwicklung deutlich vereinfachen da Datagrid, Treeview, etc. "einfach" vorhanden sind. Ziemlich cool finde ich auch, dass Silverlight jetzt nicht nur im Browser sondern auch direkt als Desktopanwendung ausgeführt werden kann (okay, das musste wohl in Hinblick auf Flash auch integriert werden  ) Wer eine ausführliche Liste an Features haben möchte .. dem empfehle ich den kompletten Blog-Beitrag hier
Bei der Weiterentwicklung eines recht alten Portals stand ich nun vor der Aufgabe eine aktuelle Entwicklungsumgebung aufzusetzen. Die bisherige Umgebung war DNN 3.2.2 und nun wurde das Portal auf die aktuelle Version von DNN (4.x) aktualisiert. Da ich auch nicht mehr mit Visual Studio 2003 entwickeln wollte, habe ich den SourceCode das Modul mit dem Conversion Wizard auf die aktuelle VSS Version 2008 konvertiert. Das Projekt vom Type ClassLibrary konnte aber nicht vernüftigt debugged werden, daher habe ich beschlossen das Projekt als WAP (Web Application Project) weiterzuentwickeln. Dafür muss man (nach erfolgreichem Durchlaufen des Conversion Wizard von VS2008) die Projektdatei mit einem Editor öffnen und anschließend die Zeile <ProjectType>Local</ProjectType> gegen <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids> tauschen. Nach dem erneuten Öffnen der Projektmappe / des Projektes im Visual Studio wird diese nun als WAP angezeigt. Nun muss man nur noch die einzlnen ASCX / ASPX in eine Web-Anwendung konvertieren. Das ist auch sehr einfach zu erledigen: Die gewünschten Dateien Auswählen und im Kontextmenü den Punkt "In Webanwendung konvertieren" wählen. Nun werden im Hintergrund die *.designer.cs Dateien angelegt und gleichzeitig die Codebehind-Klassen als "partial" gekennzeichnet. Schon ist das Update von einer Class Library in ein Web Application Project fertig! Achso: Bitte die Konfiguration überprüfen und evtl. Anpassen...
Eine wirklich tolles neues Werkzeug ist die Software IETester, der die Entwicklung bzw. das Testen von neuen Webseiten deutlich vereinfacht. Man muss nicht alle IE dafür installiert haben und der Webentwickler benötigt nicht ständig 4 offene Browser / Anwendungen um die Seite zu sehen. Die einzelnen Browserversionen werden einfach in einem Tab geöffnet. Folgende IE-Versionen deckt der ITTester aktuell ab: - IE 5.5
- IE 6
- IE 7
- IE 8 beta 1
Aktuell befindet sich die Anwendung noch in einer sehr frühen Phase,
dennoch macht der IETester einen vielversprechenden Eindruck und läuft
recht stabil.
Bei der Umstellung / Migration der Entwicklungsumgebung von Visual Studio 2003 (für ein Projekt wurden bisher Module für DNN 3.2.2 erstellt) auf Visual Studio 2008 und die Projektart WAP (Web Application Projects), bekam ich immer folgende Meldung bei der Konfiguration vom Server (IIS):

Diese Konfiguration ist aber notwendig, damit man das WAP-DNN-Modul Projekt vernüftig debuggen kann. Die Meldung ist zunächst verwirrend, denn unter der angegebenen URL war bereits ein komplett lauffähiges DotNetNuke installiert und die URL war natürlich als Anwendung konfiguriert. Es konnte also nicht im IIS liegen (IIS unter Vista). Die Berechtigungen war auch richtig gesetzt und waren nicht der Grund für diese Meldung.
Die Lösung war dann folgende Konfiguration:
- Bei Projekt-URL habe ich den kompletten Pfad zum Modul eingetragen (also z.B. http://gmsshop/desktopmodules/shopsystem)
- Damit nun die Verweise z.B. in den ASCX Dateien richtig funktionieren muss zwigend die Option "URL des Anwendungsstam überschreiben" aktiviert werden.
- Dann die eigentliche URL der DotNetNuke Installation in das dann verfügbare Textfeld schreiben
Mit dieser Konfiguration startet Visual Studio 2008 das DotNetNuke Modul auch wieder im Debugmodus
Derzeit steht die letzte (für uns) Migration eines DotNuke-Portals von der Version 3.2.2 auf die aktuellste Version an. Da es sich hierbei um ein recht großes Portal handelt, bei dem der Communityaspekt im Vordergrund steht, wurden die Benutzerprofile hier sehr stark genutzt und erweitert. Dieses wurde in der Version 3.2.2 durch Veränderungen der web.config erreicht. Bei dem Testlauf einer Migration ist mir nun aufgefallen, dass die zusätzlich definierten benutzerdefinierten User-Propeties nicht mit in das neue Konzept übertragen werden. Um das zu erreichen, muss man das SQL-Upgradescript anpassen und die zu migrierenden Properties dort mit einfügen. Dafür muss man in das SQL-Script mit der Version 03.02.03 einige Änderungen machen: Die StoredProcedure AddDefaultPropertyDefinitions muss um die zusätzlichen Eigenschaften ergänzt werden - dabei wird der Datentyp definiert, die Gruppe, die Bezeichnung, Länge, etc. definiert. Später können diese Eigenschaften auch über die bekannte Benutzerprofilverwaltung angepasst werden. EXECUTE @RC = {databaseOwner}[{objectQualifier}AddPropertyDefinition] @PortalId, -1, @TextDataType, '', 'LowFett','Size' ,0, '', 21, 1, 250
EXECUTE @RC = {databaseOwner}[{objectQualifier}AddPropertyDefinition] @PortalId, -1, @TextDataType, '', 'LowFett','Weight' ,0, '', 21, 1, 250
EXECUTE @RC = {databaseOwner}[{objectQualifier}AddPropertyDefinition] @PortalId, -1, @TextDataType, '', 'LowFett','Eyecolor' ,0, '', 21, 1, 300
EXECUTE @RC = {databaseOwner}[{objectQualifier}AddPropertyDefinition] @PortalId, -1, @TextDataType, '', 'LowFett','Haircolor' ,0, '', 21, 1, 300Die Tabelle FlatProfile wird temp. angelegt um die Daten vom ASP-Membership zwischenzulagern. Auch hier müssen ein paar manuelle Änderungen vorgenommen werden, damit diese Tabelle für die zu migrierenden Daten auch jeweils eine Spalte bekommt - der SQL-Syntax der "normale" für den den SQL-Server - hier zwei Beispiele: [Eyecolor] [nvarchar] (300) NULL ,
[Haircolor] [nvarchar] (300) NULL ,
Dann muss die Ausführug von "INSERT INTO {objectQualifier}FlatProfile" um die entsprechenden Eigenschaften ergänzt werden: {databaseOwner}{objectQualifier}GetProfileElement('Weight',PropertyNames, PropertyValuesString) Weight,
{databaseOwner}{objectQualifier}GetProfileElement('Eyecolor',PropertyNames, PropertyValuesString) Eyecolor,
{databaseOwner}{objectQualifier}GetProfileElement('Haircolor',PropertyNames, PropertyValuesString) Haircolor,
Als letzer Schritt müssen dann noch ein paar zusätzliche SQL-Befehle eingefügt werden, damit die Daten aus der FLAT-Tabelle in die eigentlichen UserProfile-Tabelle geschrieben werden. EXECUTE {objectQualifier}TransferUsersFromFlatProfile N'Eyecolor'
EXECUTE {objectQualifier}TransferUsersFromFlatProfile N'Haircolor'
Wenn diese Schritte für jede zu migrieden benutzerdefinierte Property durchgeführt wurde, dann steht der Migration nichts mehr im Weg.
Bei der Entwicklung von DotNetNuke-Modulen (bzw. auch bei "normalen" ASP.NET Anwendungen) verwende ich recht häufig die Möglichkeit, Usercontrols (ascx) dynmaisch zur Laufzeit anzuzeigen / nachzu laden. Der entsprechende Quellcode dafür ist nicht wirklich neu und sieht so aus: PortalModuleBase objToLoad = this.LoadControl(_ControlToLoad) as PortalModuleBase;
objToLoad.ID = System.IO.Path.GetFileName(_ControlToLoad);
objToLoad.ModuleConfiguration = this.ModuleConfiguration;
this.Controls.Add(objOrderCtrl);
Wichtig dabei ist die Zeile, wo die ID des Controls gesetzt wird, damit die Resourcendateien durch DotNetNuke (durch die Property LocalResourceFile) angesprochen werden können. Bei der Verwendung einer Templateengine für ein Modul und der gleichzeitigen Verwendung von Standard ASP.NET 2.0 Validatoren, bekam ich im Browser immer die Javascript-Fehlermeldung "missing ; before statement". Nach kurzer Recherche habe ich die Ursache gefunden: In den ID's von den Controls wurde ein Punkt verwendet und damit kommt JavaScript bzw. der JavaScript-Code der ASP.NET Validatoren nicht zurecht. Nach Abänderung der ID Zuweisung auf folgende Zeile: objToLoad.ID = System.IO.Path.GetFileNameWithoutExtensi(_ControlToLoad);
funktionieren sowohl die Resourcendateien als auch die Validatoren wieder ohne Probleme.
Um in DotNetNuke für SEO besser geeignete URL's zu nutzen, muss man die web.config bearbeiten. Dieses läßt sich nur global und nich für jedes Portal einzeln aktivieren. <friendlyUrl defaultProvider="DNNFriendlyUrl">
<providers>
<clear />
<add name="DNNFriendlyUrl"
type="DotNetNuke.Services.Url.FriendlyUrl.DNNFriendlyUrlProvider,
DotNetNuke.HttpModules" includePageName="true" regexMatch="[^a-zA-Z0-9
_-]" urlformat="HumanFriendly"/>
</providers>
</friendlyUrl>Setzt man den Parameter urlformat auf "HumandFriendly", dann erhält man folgende URL: http://www.mydomain.com/DotNetnuke-installations-service.aspx Die unter DotNetNuke bezeichneten suchmaschinenfreundlichen URL's sehen wie folgt aus: http://www.mydomain.com/dotnetnuke/installation-service/tabId/566/default.aspx Eigentlich ist die Humanfriendly-Lösung auch deutlich besser für die Suchmaschine geeignet, die Namensgebung stammt wohl noch aus grauer Vorzeit - die Verwendung von HumanFriendly-Urls ist auf jeden Fall zu empfehlen. Jede Seite im DotNetNuke-Portal ist über die drei möglichen Adressierungen ansprechbar: http://www.mydomain.com/default.aspx?tabId=566 http://www.mydomain.com/dotnetnuke/installation-service/tabId/566/default.aspx http://www.mydomain.com/DotNetnuke-installations-service.aspx Eine Abwärtskompatibilität ist somit gegeben, was rein technisch ist das sehr angenehm ist. Aus Sicht der Suchmaschinenoptimierung ist diese Möglichkeit aber gefährlich, da hier die Gefahr des "Duplicated Content" entsteht und identische Inhalt über mehrere URL's erreichbar sind. Es ist also darauf zu achten, dass man eine saubere Weiterleitung einrichtet!
Eine wichtige Anlaufstelle für Webentwickler, Webdesigner und alle die sich mit HTML, CSS und JavaScript rumschlagen müssen ist SELFHTML. Unter http://visualselfhtml.org gibt es jetzt eine Webanwendung die eine sehr gute und gezielte Suche erlaubt. Ein Stichwortverzeichnis macht die Anwendung so richtig klasse. Diese Webanwendung basiert auf jQuery .... cool :)
Ich bin mir gar nicht sicher, ob es ein Grund zur Freunde ist, dass Mircosoft den IE 8.0 mittlerweile als Final veröffentlicht hat. Eigentlich freue ich mich ja immer über neue Softwareversionen und probiere alles sofort aus. Aktuell bin ich aber noch kritisch, ob man als Webagentur jetzt nicht noch mehr Ärger als ohnehin schon bekommt. Microsoft hat bei der mehr als zwei Jahre andauernden Entwicklung vom IE 8.0 viel Arbeit für de Bereiche Geschwindigkeit und Kompatibilität investiert aber bei dem Acid3-Test nur 20 von 100 möglichen Punkten bekommen. Dahingegen ist die vollständige CSS-2.1-Unterstützung und der der Acid2-Test keine Problem. Was Webentwickler bei der Kompatibilitätsansicht beachten müssen hat Kay Giza hier zusammengefasst Alle Leute die keine Lust auf ein automatisches Update haben, sollten folgendes Toolkit von Mircosoft benutzen, um ein Update zu deaktiveren.
Heute musste ich feststellen das auf einem Windows 2003 Server mit einer Instance vom MS SQL Server 2005 Express DotNetNuke die Arbeit eingestellt hat. Ein Blick in das Eventlog vom Server ergab, das sich der Benutzer nicht mehr am SQL-Server anmelden konnte.
Als ich die Eigenschaften des entsprechenden Datenbank-Benutzers überprüfen wollte, kam die Meldung:
Property IsLocked is not available for Login '[username]'. This property may not exist for this object, or may not be retrievable due to insufficient access rights. (Microsoft.SqlServer.Smo)
Ein Wechsel zwischen dem Anmeldemodus Windows-Auth und gemischter Modus betrachte keine Veränderung - diesen Tipp hatte ich in einem Forum gelesen.
Was geholfen hat war das absetzen vom folgendem SQL-Befehl: ALTER LOGIN [usermae] WITH PASSWORD=N'meintollespwd',CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF, DEFAULT_DATABASE=[master], DEFAULT_LANGUAGE=[us_english] GO ALTER LOGIN [username] ENABLE
Damit konnte sich DotNetNuke wieder anmelden und auch die Eigenschaften vom Benutzer waren wieder einsehbar und konnten über die Managmentkonsole vom SQL-Server modifiziert werden.
Die wirkliche Ursache habe ich noch nicht gefunden - zumindest läuft die Webanwendung wieder!
Aktuell beschäftige ich mich mit der Entwicklung einer kleinen praktischen Komponente, die ein Web 2.0 Popup-Fenster für DotNetNuke Module bereitstellt. Dabei geht es weniger darum eine Bilder anzeigen zu lassen, das Hauptziel ist die Nutzung für Bearbeitungsfenster. Ebenfalls soll das Modul auch in unser Videomodul einfließen, damit das Videos in einem Popup angezeigt werden können. Dazu vielleicht aber später mehr....
Auf eine google-Suche habe ich diese Seite gefunden, die 56 Skripts für Galliers, Slideshows auflistet und direkt zu den entsprechenden Websiten verlinkt.
Mein Favorit ist eigentlich lightbox aber über den Tellerran zu schauen kann ja nie schaden.
Nachtrag: Soeben habe ich einen Kommentar per E-Mail erhalten und mir wurde da http://highslide.com/ ans Herz gelegt, ist zwar nicht kostenfrei aber bietet schon eine ganze Menge an Funktionalität .... man hat doch wirklich die "Qual der Wahl"
In unserem DotNetNuke Shop Modul wird im Administationsbereich einiges mit ASP.NET AJAX durchgeführt. Bisher verlief die Installation der Entwicklungsumgebung auch immer ohne Probleme. Bei einem Kollegen, kam es aber zu einer echt blöden Fehlermeldung:
DotNetNuke.Services.Exceptions.ModuleLoadException: The base class includes the field 'pnlExtPrice', but its type (System.Web.UI.UpdatePanel) is not compatible with the type of control (System.Web.UI.UpdatePanel). ---> System.Web.HttpParseException: The base class includes the field 'pnlExtPrice', but its type (System.Web.UI.UpdatePanel) is not compatible with the type of control (System.Web.UI.UpdatePanel). ---> System.Web.HttpParseException: The base class includes the field 'pnlExtPrice', but its type (System.Web.UI.UpdatePanel) is not compatible with the type of control (System.Web.UI.UpdatePanel). at System.Web.Compilation.BaseTemplateCodeDomTreeGenerator. BuildFieldDeclaration(ControlBuilder builder) at System.Web.Compilation.BaseTemplateCodeDomTreeGenerator. BuildSourceDataTreeFromBuilder(ControlBuilder builder, Boolean fInTemplate, Boolean topLevelControlInTemplate, PropertyEntry pse) at System.Web.Compilation.BaseTemplateCodeDomTreeGenerator. BuildSourceDataTreeFromBuilder(ControlBuilder builder, Boolean fInTemplate, Boolean topLevelControlInTemplate, PropertyEntry pse) at System.Web.Compilation.TemplateControlCodeDomTreeGenerator.
BuildMiscClassMembers() at System.Web.Compilation.BaseCodeDomTreeGenerator.BuildSourceDataTree() at System.Web.Compilation.BaseTemplateBuildProvider. GenerateCode(AssemblyBuilder assemblyBuilder) at System.Web.Compilation.AssemblyBuilder.AddBuildProvider(BuildProvider buildProvider) --- End of inner exception stack trace --- at System.Web.Compilation.AssemblyBuilder.AddBuildProvider(BuildProvider buildProvider) at System.Web.Compilation.BuildProvidersCompiler.ProcessBuildProviders() at System.Web.Compilation.BuildProvidersCompiler.PerformBuild() at System.Web.Compilation.BuildManager.CompileWebFile(VirtualPath virtualPath) at System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile) at System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile) at System.Web.UI.TemplateControl.LoadControl(VirtualPath virtualPath) at System.Web.UI.TemplateControl.LoadControl(String virtualPath) at DotNetNuke.UI.Skins.Skin.InjectModule(Control objPane, ModuleInfo objModule, PortalSettings PortalSettings) --- End of inner exception stack trace ---
Eigentlich war alles so weit auch richtig installiert und in einer anderen DNN Installation lief AJAX ohne größere Probleme. Auf dem Rechner war neben ASP.NET 2.0 auch bereits .NET 3.5 installiert und ich dachte mir schon, das es irgendein Konflikt mit den Versionen sein muss. Eine Recherche im Web hat mich dann auf folgendes Ergebnis gebraucht:
In der web.config muss der Bereich "runtime" wie folgt angepasst werden: <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatePath="bin;bin\HttpModules;bin\Providers;bin\Modules;bin\Support;" /> <dependentAssembly> <assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35" /> <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0" /> </ependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Web.Extensions.Design" publicKeyToken="31bf3856ad364e35" /> <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0" /> </dependentAssembly>
</assemblyBinding> <runtime>
Danach läuft das Modul und die AJAX Funktionen - hier im speziellen das UpdatePanel - wieder ohne Probleme.
Heute habe ich mich ein wenig mit dem Thema PCI Compliance beschäftigt, da wir mit unserem DotNetNuke Shop einen Kunden an Worldpay angebunden haben. Mittlerweile ist es aber so, dass neben einem SSL Zertifikat auch ein PCI Compliance Scan vorgenommen werden muss.
Im Internet habe ich dafür auch einen ganz guten Dienst gefunden, der das online durchführt. Derzeit bin ich in der Testphase dieser Software - sieht im Augenblick aber sehr brauchbar aus. Besonders, da hier nciht nur ein Bereicht abgeliefert wird, sondern auch direkt Hilfstellung angeboten wird, wie man das Problem beheben kann. Jeder der ebenfalls eine Software für einen PCI Compliance Scan sucht - findet diese unter http://www.qualys.com.
Sollte jemand noch eine Alternative kenne, würde ich mich über eine kurze Nachricht oder einen Kommentar freuen 
Eine Meldung bei dem eigenen Kundenserver war "SSL Insecure Protocol Negotiation Weakness", das ein Problem bzw. ein erhöhtes Risiko mit SSL v2 darstellt. Dieses sollte / muss im Internet Information Server (IIS) deaktivert werden. In der Managementkonsole vom IIS gibt es dafür aber leider keinen Schalter und die Deaktivierung erfolgt manuell über die Registry. Wie man SSL v2 bei einem Windows Serverr mit IIS deaktivieren kann ist in der Knowledge Base von Microsoft nachzulesen: http://support.microsoft.com/kb/187498
Nach einem Reboot vom Server wird auch dieser Punkt vom PCI Scanner als "erledigt" dargestellt.
Das "I-Tüpfchen" für eine Website ist ein pasendes Favicon. Dotnetnuke hat bereits einen Platz in der Verzeichnisstruktur dafür vorgesehen.
Es ist ohne weiteres möglich im Root-Verzeichnis das Favicon von DNN zu ersetzen. Allerdings funktioniert dieses bei den aktuellen Version 4.9.0 nicht mehr. Es wird immer das Standard DNN-Favicon angezeigt. Zudem habe ich die Erfahrung gemacht, dass dieses oft nach einem DNN update überschrieben wird.
Wenn man im Portalverzeichnis (z.B. /Portals/xx/) die Datei Favicon.ico anlegt, dann wird diese Datei automatisch als Favicon für das aktuelle Portal verwendet.
Habe heute das Servicepack 1 für das Visual Studio 2008 und .NET 3.5 installiert. Nach der Installation konnte ich unter Visual Studio 2005 keine ASP.NET Anwendungen mehr debuggen. Als Fehlermeldung kam immer diese Meldung:
"Das Debuggen kann auf dem Webserver nicht gestartet werden. Authentifizierungsfehler bei der Kommunikation mit dem Webserver."
Das Durchsuchen der Supportdatenbank von Micrsoft hat mich dann auf folgenden Artikel "Fehler 401.1 beim Aufrufen einer Website, die integrierte Authentifizierung verwendet und mit IIS 5.1 oder IIS 6 gehostet wird" aufmerksam gemacht http://support.microsoft.com/kb/896861.
Bei mir hat diese beschrieben Methode zum Erfolg geführt:
- Klicken Sie auf Start und auf Ausführen, geben Sie regedit ein, und klicken Sie auf OK.
- Suchen Sie im Registrierungseditor den folgenden Schlüssel, und klicken Sie auf ihn:
- HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0
- Klicken Sie mit der rechten Maustaste auf MSV1_0, zeigen Sie auf Neu, und klicken Sie anschließend auf Wert der mehrteiligen Zeichenfolge.
- Geben Sie BackConnectionHostNames ein, und drücken Sie anschließend die [EINGABETASTE].
- Klicken Sie mit der rechten Maustaste auf BackConnectionHostNames. Klicken Sie dann auf Ändern.
- Geben Sie in das Feld Wert den Hostnamen bzw. die Hostnamen für die Sites ein, die sich auf dem lokalen Computer befinden, und klicken Sie danach auf OK.
- iisreset zum Neustarten des IIS ausführen.
Gerade beschäftige ich mich ein wenig mit dem Thema Caching und dabei kommt man ja irgendwie an Memcached nicht vorbei. Wollte memcached dabei als Service in Vista laufen lassen, doch beim Aufruf in der Commandozeile
c:\memcached\memcached.exe -d install
kommt ständig die Fehlermeldung:
"failed to install service or service already installed"
relativ schnell habe ich herausgefunden das es an den unzureichenden Rechten meines Benutzeraccounts unter Vista liegt. Damit es funktioniert muss man die Console als Administrator ausführen.
Dafür gibt es einen Shortcut: Wenn man bei der Ausführung der Anwendung Ctrl+Shift+Enter drückt, kann man sich den Weg über das Kontextmenü sparen.
Bei einem Server fand ich im Eventlog ständig folgenden Eintrag:
Ereigniscode: 3005 Ereignismeldung: Es ist eine unbehandelte Ausnahme aufgetreten. Ereigniszeit: 02.10.2008 01:22:41 Ereigniszeit (UTC): 02.10.2008 12:22:41 Ereignis-ID: cfc0fb0037c3473d9a93aed3556026c4 Ereignissequenz: 1658 Vorkommen: xx Ereignisdetailcode: 0 Anwendungsinformationen: Anwendungsdomäne: /LM/W3SVC/1/ROOT-xxxxxxxxx Vertrauensebene: Full Virtueller Anwendungspfad: / Anwendungspfad: x:\daten\websites\xxxxxx Computername: computer Prozessinformationen: Prozess-ID: 3028 Prozessname: w3wp.exe Kontoname: IrgendeinKonto Ausnahmeinformationen: Ausnahmetyp: CryptographicException Ausnahmemeldung: Zeichenabstände sind ungültig und können nicht entfernt werden. Anforderungsinformationen: Anforderungs-URL: http://xxxxx/WebResource.axd?d=yMHoWq6OlLLBSdXLzX0Yig2&t=633563938783503750
Anforderungspfad: /WebResource.axd Benutzerhostadresse: xxx.xxx.xx.xxx Benutzer: Ist authentifiziert: False Authentifizierungstyp: Threadkontoname: IrgendeinKonto Threadinformationen: Thread-ID: 5 Threadkontoname: IrgendeinKonto Identitätswechsel für: False Stapelüberwachung: bei System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast) bei System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) bei System.Security.Cryptography.CryptoStream.FlushFinalBlock() bei System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length, IVType ivType, Boolean useValidationSymAlgo) bei System.Web.UI.Page.DecryptStringWithIV(String s, IVType ivType) bei System.Web.Handlers.AssemblyResourceLoader.System.Web.IHttpHandler. ProcessRequest(HttpContext context) bei System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication. IExecutionStep.Execute() bei System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) *************************
Dieses Problem (oder besser gesagt diese Fehlermeldung) wird durch einen nicht existierenden <maschineKey> in der web.config ausgelöst. Nach der Erstellung eines gültigen <machineKey> ist die Fehlermeldung verschwunden.
Die schon lange erwartete neue Version von DotNetNuke mit dem Codenamen "Cambrian" hat einen weiteren Meilenstein erreicht. Die Version RC1 wird bei der OpenForce Europe vorgestellt und an Gruppe von Personen verteilt.
Sehr cool ist der Support von jQuery und XHTML, WCAG and ADA compliance. Benutzerrechte können jetzt nicht nur gewährt sondern auch entzogen werden, was in manchen Situationen das Leben verinfachen dürfte. Einige von den neuen Features wurden in die Version 4.9 übernommen und sind dort schon verfügbar.
Eine Übersicht der neuen Features von DotNetNuke 5.0 gibt es z.B. hier.
Wann es eine öffentliche Version gibt, ist bis jetzt aber noch nicht bekannt...
Das ich im letzten Jahr eine Videoportal Software auf Basis von DotNetNuke mit WCF Services im Backend entwickelt habe, ist hier ja schon ein paar mal erwähnt wurden. Die bis jetzt aufgesetzten Videoportale können natürlich auch die hochgeladenen Videos in Flash konvertieren (sorry Silverlight ). Aktuell wird dafür das Tool FFMpeg genutzt, dass auch einen guten Dienst leistet und recht flexibel ist. Durch die asyncrone Entkopplung der einzelnen WCF Services ist auch so ein Command-Line Tool im Hintergrund kein wirkliches timing Problem.
Heute habe ich per Zufall aber gesehen, dass es nun .NET Lib für die Konvertierung gibt - getestet habe ich diese noch nicht - könnte aber evtl. eine denkbare Alternative sein.
Laut Website auch für den Einsatz in webbasierter Software gut geeignet. Die Lib ist kostenlos, wer den SourceCode haben möchte, muss dafür ein paar Dollar hinlegen.
Mehr Infos gibt es hier: http://www.intuitive.sk/fflib/
Soeben habe ich eine wirklich nützlichen Dienst / Service auf webservicex.net gefunden. Diese Plattform hatte ich schon fast vergessen und bin nun durch einen Zufall darauf aufmerksam geworden.
Dort gibt es einen Web-Service, mit dem man E-Mailadressen online validieren - also auf Gültigkeit - prüfen kann. Wirklich eine praktischer Dienst ... und man muss sich nicht selber mit der Validierung rumschlagen. Die Validierung geht natürlich über eine RegEx hinaus und überprüft beim Mailserver, ob es dort diese E-Mail wirklich gibt. Bei meine 20 Testläufen wurde mir zumindest immer ein korrektes Ergebnis zurück geliefert...
Zu erreichen unter der Adresse: http://www.webservicex.net/ValidateEmail.asmx
Gerade kam ein Kunde mit folgendem Problem auf uns zu: Eine absolut identische Seite, gleiches System, gleicher inhalt. Einmal wurde die Seite über http://www.mydomain.de aufgerufen und einmal über http://google.mydomain.de. Der zweite Auruf wurde aber bei Google-Anaytics nie getrackt. Der Aufruf zu Analytics wurde erst gar nicht durchgeführt.... Nach kurzer Debugphase vom JavaScript (urchin.js) war der Fehler schnell gefunden. Das Script fragt ab, ob die Aufruf von einer Seite kommt, die irgendwie ".google.", "google.", usw. binhaltet. Dann wird das Script nicht weiter ausgeführt.
Das Debuggen von DotNetnuke Schedulern ist nicht so ganz einfach. Mit einem Break-Point und F5 ist es leider nicht getan an dieser Stelle, da die einzelnen Scheduler in einen separaten Thread laufen. Auch der Trink um Windows Services zu debuggen - das anhängen des Debuggers an den Prozess - funktioniert in diesem Fall einfach nicht.
Es gibt aber einen Weg, den Debugger per SourceCode-Aufruf zu starten. Das geht ganz einfach und ist mit einer Zeile erledigt:
System.Diagnostics.Debugger.Launch();
Dieser Aufruf bewirkt, dass man eine neue oder eine existierende Instanz von Visual Studio auswählen kann, um anschließend durch den Code zu steppen.
Ein paar weitere Infos gibt es bei Mircosoft-MSDN hier
Ich beschäftige mich gerade mal etwas intensiver mit dem AJAX-Framework - eigentlich ja schon ein alter Hut. Dabei habe ich ein DotNetNuke-Modul, dass zu einem ASMX-WebService Daten versendet und ein Ergebnis zurück bekommt.
namespace G.Modules.LicCalc {
[WebService(Namespace = http://tempuri.org/)] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [ScriptService] public class CalculatorService : System.Web.Services.WebService {
[WebMethod()] public string HelloWorld(LicCalcInfo info) { return "Hello World" + info.LinuxLicense.ToString(); } } }
Passend dazu gibt es eine Klasse "LicCalcInfo" - die wie DotNetNuke-typische Info-Class aufgebaut ist
using System;
namespace GMS.Modules.LicCalc.Business { public class LicCalcInfo { private int _windowsLicense; private int _linuxLicense;
public int LinuxLicense { get { return _linuxLicense; } set { _linuxLicense = value; } }
public int WindowsLicense { get { return _windowsLicense; } set { _windowsLicense = value; } }
} }
Wenn ich jetzt über JavaScript versuche die Methode "HelloWorld" aufzurufen und den Parameter LicCalcInfo übergeben möchte, wird bei der Erstellung der Klasse immer eine JavaScript-Exception ausgelöst. Das JavaScript sah so aus:
function HelloWorld() {
var info = new LicCalcInfo(); info.LinuxLicense = 10; DocuSnap.Modules.LicCalc.CalculatorService.HelloWorld(info, SucceededCallback);
}
function SucceededCallback(result, eventArgs) { alert(result); }
Das ganze funktioniert aber nur, wenn die Klasse "LicCalc" direkt in der ASMX Datei definiert wurde und sich außerhalb des Namespaces befindet. Als extern definierte Klasse habe ich die Einbindung nie geschafft. Die Lösung dabei ist aber mal wieder relativ einfach: Bei der Erzeugung des Objektes in JavaScript muss das Objekt mit dem kompletten Namespace angesprochen werden.. das hat mir dann ein Blick in die automatisch gernierte JavaScript-Datei veraten. Der Aufruf zur Erzeugung muss dann so geschrieben werden:
var info = new DocuSnap.Modules.LicCalc.Business.LicCalcInfo();
Dann kann die Klasse auch in JavaScript erzeugt werden und die Verarbeitung wird erfolgreich durchgeführt.
Eigentlich habe ich ja ein paar Module (News, FAQ) für DotNetNuke gesucht - dabei bin ich dann über ein Game gestolpert, dass jeder von uns kennt und auch gerne spielt: Schiffe versenken :)
Auf der Seite http://www.donein.net/Default.aspx?tabid=58 gibt es sowohl einen Downloadlink als auch die Möglichkeit, das Spiel direkt mal zu testen!
Viel Spaß :)
Wer sich wundert das die Funktion "Anmeldedaten merken" im IE nicht funktioniert und sich ebenfalls darüber ärgert, für den habe ich nun eine Lösung (vielmehr habe ich im Web eine gefunden). Im Blog auf www.dotnetnuke.com habe ich einen Beitrag gefunden der erklärt, wieso, weshalb, warum und was man dagegen tun kann. Hier ist der Beitrag...Prinzipell müssen zwei Einträge in der web.config angepasst werden, damit die Funktion das macht, was sie soll: <forms name=".DOTNETNUKE" protection="All" timeout="120" cookieless="UseCookies" />
<add key="PersistentCookieTimeout" value="20160" />
Der "Fehler" liegt auch weniger an DNN sonder viel mehr an der Behandlung von Cookies unter ASP.NET 2.0.
Im Netz gibt es zwei Seiten mutlimedia.katholisch.de und www.kirche.tv. Beiden Seiten wurde mit DotNetNuke umgesetzt und sollen so eine Art YouTube darstellen. Natürlich sehr stark spezialisiert auf den Bereich der katholischen Kirche. Die Architektur von diesen Videoportalen ist recht interessant, da hier beide Portale (getrennte Installationen) aus einem gemeinsamen Datenpool bedient werden. Der Lösungsansatz ist DotNetNuke mit WCF zu verheiraten. Die Module zur Darstellung von Videos, zum Upload von Videos und natürlich die Administration von Videos greifen dann nicht wie sonst üblich auf den DataProvider zurück, sonder befragen einfach die laufenden WCF-Services. Die eigentlich "anzeige Module" - sprich die *.ascx" - greifen aber wie gewohnt auf Controller-Klassen zu, durch die dann eine Kommunikation mit den WCF-Services ermöglicht wird. In den Controllern kann man dann z.B. auch Caching und ähnliches einbauen. Neben der WCF wurde auch die MSMQ (Message Queue) stark genutzt, was gerade im Zusammenspiel mit der WCF sehr viel freude bereitet und denkbar einfach ist. Der große Vorteil bei der Verwendung von MSMQ ist natürlich, dass Prozesse asynchron ablaufen können und der Benutzer nicht warten muss, bis der komplette Workflow-Prozess dahinter abgerabeitet ist. Ein kurzes Beispiel um das zu verdeutlichen: Ein Anwender macht einen Video - Upload durch das DNN-Modul; dieses macht eine erste Validierung der Daten und wenn diese in Ordnung sind, wird eine Nachricht mit allen Informationen in eine transaktionale Message Queue übertragen. Damit ist der Job für das DNN-Modul zunächst erledigt. Ein WCF-Service der permanent die Queue "im Auge" hat, holt die Nachricht ab und bearbeitet diese nun weiter - Daten werden in die Datenbank geschrieben, eine Nachricht zur Konvertierungs Queue abgesetzt, etc.). Gerade bei der Konvertierung von Videos kommt man um einen asynchron Verarbeitung nicht herum, da große Dateien längere Zeit in anspruch nehmen,. Die Konvertierung erfolgt übrigens in das Format Flash. Wenn das Video konvertiert wurde, wertet ein diesmal ei NT-Dienst das Ergebnis aus und berichtet den Status. Für die Konvertierung werden externe Programm eingesetzt und deshalb überwacht das ein NT-Dienst. [Dieser Workflow ist nur sehr grob beschrieben und beinhaltet noch deutlich mehr Schritte] Die beiden Portale laufen nun seit gut 6 Monaten sehr zuverlässig, stabil und schnell. Ein schöner Beweis das man mit DotNetNuke als Basis wirklich so ziemlich alles umsetzen kann und das man DotNetNuke auch wunderbar mit anderen Technologien nutzen kann. Wer ausführlichere Informationen dazu haben möchte, kann sehr gerne mit mir Kontakt aufnehmen...
Für ein aktuelles Projekt benötige ich von Plätzen / Anschriften die GEO-Koordinaten. Da zur Visualisierung so wieso Google-Maps später eingesetzt werden soll, liegt natürlichdie Google-API nahe. Dafür habe ich heute ein C# Klasse entwickelt, mit der man einfach über einen HTTPRequest das Ergebnis in XML zurück geliefert bekommt. Die Klassen analysiert das Ergebnis von Google auf Fehler und parst die Werte für Latitude und Longitude.
Zunächst habe ich mir das Core-Modul von DotNetNuke "MAPS" angeschaut, das ebenfalls die Google-Maps nutzt. Es funktioniert recht gut - nur war ich von der vorgehensweise docht etwas erschrocken: Da wird das Ergebnis wirklich noch mit String-Funktionen (Find, Left, SubString) auseinandergenommen um an die Werte zukommen. Hallo? Wofür gibt es einen XML-Parser, der für solche Aufgaben entwickelt wurde und auch deutlich schneller ist, bei der Analyse von XML. Das aber nur als Zwischenbemerkung an dieser Stelle 
Bei der Entwicklung der Klasse hatte ich zunächst Probleme mit XPath die Werte auszulesen. Da ich ja diesmal gezielt ein paar Daten aus dem XML-File benötige und nicht wirklich Node für Node durchgehen muss (wollte), war XPath meine erste Wahl. Mache das aber nicht so häufig und hatte zuerst schwirigkeiten, weil die Abfrage SelectSingleNode immer einen null-Value zurück lieferte. Nach kurzer Zeit viel mir dann aber auf, das Google einen Namespace im XML-Dokument stehen hat - so wie es ja eigentlich auch sein soll. Diesen Namespace muss aber bei einer XPath-Abfrage auf jeden Fall vorher dem Parser bekannt geben. Dann funktioniert die Abfrage auch ohne Probleme. Das sieht dann in etwa so aus:
XmlNamespaceManager nsManager = new XmlNamespaceManager(xmlDoc.NameTable); nsManager.AddNamespace("gm", "http://earth.google.com/kml/2.0");
///Es wird versucht den Statuscode zu ermitteln XmlNode nodeStatus = xmlDoc.SelectSingleNode("gm:kml/gm:Response/gm:Status/gm:code", nsManager);
In der Abfrage hier wird der Status-Cide abgefragt, um zu überprüfen ob die Anfrage erfolgreich war. xmlDoc ist dabei mein XML-Parser ( System.Xml.XmlDocument).
Die fertige C# Klasse steht hier zum Download bereit: C#-GoogleMaps-API-GEO-Koordinaten.zip (2,59 KB)
Heute habe ich versucht, den FCKEditor innerhalb eines ASP.NET Ajax Updatepanel dazu zu bewegen mir auch den eingegebenen Text auszuhändigen. Normalerweise ist ja die Einbindung und Nutzung vom FCKEditor innerhalb von DotNetNuke denkbar einfach. Also wie gewohnt die Arbeitsschritte ausgeführt und mal mutig F5 gedrückt. Leider mit dem Resultat, dass die Eigenschaft Text vom FCKEditor nach einem partial PostBack immer leer war und der Text zunächst mysteriös verschwand. Die Suchmaschine meines Vertrauen hat mir auch prompt ein paar Hinweise zu dem Thema ausgespuckt - gut, ich war wohl nicht alleine auf dieser Welt.
Auf der Seite http://jlcoady.net/archive/2007/03/30/fckeditor-work-inside-updatepanel wurde ein Lösungsansatz vorgestellt, der aber leider so nicht ganz funktioniert - zumindest im meinem Fall nicht.
private void Page_Load(object sender, EventArgs args) { Page.ClientScript.RegisterOnSubmitStatement( editor.GetType(), "editor", "FCKeditorAPI.GetInstance('" + editor.ClientID + "').UpdateLinkedField();"); }
Dieser Code-Snippet bracht mich nicht weiter, denn der Text wurde immer noch nicht zurück geliefert, nach einem Postback innerhalb vom UpdatePanel.
Auch der folgende Code sollte angeblich funktionieren, konnte aber meine FCKEditor auch nicht wirklich übereden, mal endlich seine Arbeit aufzunehmen.
this.Page.ClientScript.RegisterOnSubmitStatement( this.GetType(), "AjaxHack", "for ( var i = 0; i < parent.frames.length; ++i ) if ( parent.frames[i].FCK ) parent.frames[i].FCK.UpdateLinkedField();" );
Wie sich nun herausstellt, sind die Scripte vollkommen in Ordnung und funktionieren auch. Nur ist es empfehlenswert die Methode "RegisterOnSubmitStatement" nicht als Methode der Page aufzurufen sondern als Methode vom ScriptManager. Das sieht dann so aus:
ScriptManager sm = ScriptManager.GetCurrent(Page); if (sm != null) { ScriptManager.RegisterOnSubmitStatement(this.Page, this.GetType(), "FCKAjaxHack", "for ( var i = 0; i < parent.frames.length; ++i ) if ( parent.frames[i].FCK ) parent.frames[i].FCK.UpdateLinkedField();"); }
Oh Wunder, jetzt funktioniert auch der FCKEditor in der Zusammenstellung von DotNetNuke, ASP.Net Ajax und dem Updatepanel!
Habe soeben eine ziemlich schön (zumindest auf dem ersten Blick) ASP.NET Komponente entdeckt, die einem die relativ einfach Möglichkeit bietet serverseitige Variablen an den Client Browser zu übergeben. Dafür gibt es hier eine Klasse, die einem die Verwaltungsarbeit abnimmt.
Der Weg: var CustomerName = '<%= Customer.Name %>'; ist ja nicht wirklich elegant und ich persönlich finde es immer schlechten Stil, wenn man in einer ASPX-Seite anfängt und Serverseitigen Code schreibt. [Nein, bitte keine Grundsatzdiskussion - einfach meine Meinung :)]
Viel optimaler unter ASP.NET ist doch die folgende Möglichkeit: protected void Page_Load(object sender, EventArgs e) { wwScriptVariables scriptVars = new wwScriptVariables(); // *** Add any values static or dynamic scriptVars.Add("CustomerName", Customer.Name); // *** Done}
Auf dem Client kommt das dann in folgender Form an
Automatisch werden dann die definierten Variablen an den Browser als JavaScript übermittelt und sieht so aus:
<script type="text/javascript">
//<![CDATA[ var serverVars = { "name": "Müller, Maier, Schmitu" } //]]> </script>
Ein Zugriff innerhalb von JavaScript gestaltet sich auch denkbar einfach: var name = serverVars.name;
Also ich finde diese Lösung verdammt sexy und für den täglichen Alltag auf jeden Fall zu gebrauchen!
In den letzten Jahren habe ich so viel mit XML gearbeitet, dass es mir z.B. keine Probleme bereitet die Web.Config auch mit Notepad(++) zu bearbieten. Für alle die aber lieber eine grafische Benutzeroberfläche nutzen habe ich hier ein tolles gratis Editor für die ASP.NET web.config gefunden. Es trägt den Namen ASPHere und ist komplett kostenlos. Den Download vom Editor gibt es hier...
Hier ein Bild des XML (web.config) Editors.
Ich wünsche viel Spaß!
Auch während Weihnachten war das DNN-Core-Team wohl nicht ganz untätig und hat am 27.12.2007 den Download der DotNetNuke Version 4.8.0 frei gegeben. Es wurden viele Fehler korrigiert und neu ist der Support von DNN für den IIS (Internet-Information-Server) 7.0. Wer sich einen genauen Überblick von den Änderungen der aktuellen DNN Version machen möchte - Bitteschön, wie immer geht das im Bugtracking System support.DotNetNuke.com
In der kommenden Version von DotNetNuke (DNN 4.6.0) ist ein sehr interessantes Feature die Möglichkeit der Implementierung von neuen Authentifizierungssystemen jenseits von DNN. Nun ist es nicht nur mehr möglich sich gegen DotNetNuke oder einer ADS (Active Directory Service) zu authentifizieren, sondern eine Anmeldung kann nun durch:
Dafür wurde ein "neues" Providermodell für die Authentifizierung implementiert. In den letzten Tagen habe ich für einen Kunden bereits einen eigenen Provider für eine Anmeldung bei der zentralen Golf-Online-Seite erstellt und erste positive Erfahrungen damit gesammelt. Dafür muss man lediglich drei UI-Komponenten entwickeln: Login, Settings and LogOff – jeweils von Basisklassen abgeleitet (AuthenticationLoginBase, AuthenticationSettingsBase and AuthenticationLogOffBase). Die Konfiguration der einzelnen verfügbaren Provider wird über die Tabelle Authentication vorgenommen. Dort wird ganz einfach ein Prefix und die drei UI-Komponenten angegeben.
Es ist auch möglich dem Benutzer die Auswahl selber zu überlassen, mit welchem Dienst er sich Authentifizieren möchte – das find ich insgesamt schon ziemlich sexy.
Wenn sich der Benutzer über z.B. LiveID angemeldet hat, dann wird nach erfolgreicher Authentifizierung ein Benutzerkonto angelegt oder aber man kann diesen Login mit einem existierenden DNN-Benutzerkonto verknüpfen. In der Datenbank gibt es dafür die Tabelle: UserAuthentication
Das Core-Team von DotNetNuke hat in den letzten Versionen schon einiges an der Usability verbessert. Darunter gehört z.B. die Vererbung von Seiten-Rechten innerhalb der Seitenstruktur.
In vielen Projekten höre ich aber immer wieder, dass das ControlPanel (dort wo z.B. die Module ausgewählt werden) nicht flexibel genug ist und es Vorteilhaft wäre, wenn man anhand der DotNetNuke spez. Rollen die Funktionen ganz gezielt freischalten kann. Somit könnte man bestimmten Rollen ganz gezielt lediglich ein paar Module zur Verfügung stellen, die diese für ihre tägliche Arbeit benötigen. Der Vorteil liegt ganz klar auf der Hand: Die Komplexität für die Redaktuere wird reduziert. In vielen Fällen werden ja wirklich nur ganz wenige Module für die normale Arbeite benötigt (z.B.Text/HTML, Links, Images).
Seit ein paar Monaten verfolge ich eine Projekt, dass das DotNetNuke ControlPanel ersetzt (oder viel mehr eine Alternative bereit stellt). Mit diesem ControlPanel ist es nun endlich möglich die Berechtigungen innerhalb des Controlpanels sehr gezielt zu vergeben.
Hier kann man das Modul downloaden
Leider gibt es keine SourceCode Version von diesem Modul.
In der letzten Zeit findet man im Web immer wieder Popup-Fenster die wie folgt aussehen:

Die Umsetzung erfolgt via JavaScript und CSS und stellt kein großes Geheimnis dar. Wer sich allerdings trotzdem nicht die Mühe machen möchte (und niemand möchte wirklich etwas erarbeiten, was es schon gibt), für den habe ich hier einen tollen Link: http://orangoo.com/labs/GreyBox/. Auf dieser Seite gibt es eine Komponente (JavaScript) die einem bei der Umsetzung unterstützt.
Die Vorteile:
- Es wird nicht durch die PopUp-Blocker verhindet, da es ja kein neues Fenster ist
- Mit nur 22kb ist das Script recht klein
- Wird schon auf viele Websites eingesetzt und kann als stabil betrachtet werden
Im Augenblick beschäftige ich mich mit dem DNN NavigationProvider. um für einen Kunden ein CSS basiertes Menu zur Verfügung zu stellen. Ursprünglich dachte ich eigentlich, dass man auf das Rendern des Menüs Einfluß hat und bestimmen kann, wie das Menu auszusehen hat.
Nachdem ich den Sourcecode der mitgelieferten Provider analysiert habe .. kam zunächst recht schnell die Ernüchterung. Da wird in der class DNNMenuNavigationProvider auf eine Objekt vom Typ DNNMenu zugegriffen. Doch der Sourcecode scheint davon nicht mit im Standarddownload enthalten zu sein.
Mein erster Schreck - das ich das Rendern gar nicht beeinflussen kann - ging schnell vorbei. Zum einem ist natürlich die Klasse DNNMenu in dem Downloadpaket DotNetNuke WebControls enthalten und zum anderen ist die Nutzung dieses Objektes ja gar nicht notwendig.
Jetzt werde ich mal versuchen meinen eigenen Menuprovider zu entwicklen und bin gespannt wie das klappt!
Die ASP.NET AJAX Extensions haben das Betastadium verlassen und sind nun RTW (Ready-to-Web). Diese Bibliothek integriert sich vollständig in das ASP.NET 2.0 Framework und liefert sowohl serverseitige Funktionalität als auch eine plattformübergreifend clientseitige JavaScript-Bibliothek. Dadurch soll es möglich sein auch bestehende Anwendungen mit minimalem Aufwand AJAX fähig zu machen.
Eine erste Anlaufstelle für AJAX ist die Website: http://ajax.asp.net/
Der direkte Downloadlink zur AJAX Extension ist hier.
Nachdem ich nun mein neues Notebook habe und mir ein komplett neues System aufsetzen musste - bekam ich plötzlich einen ganz seltsamen Fehler bei DotNetNuke und der Verwendung des PopUp Kalenders. Hier wurde das Datum wahlweise mal im richtigen und dann mal wieder im falschen Format zurück gegeben. Nur zur Erklärung: Das richtige Datumsformat ist für mich das deutsche (weil deutsche Anwendung) also dd.MM.yyyy aber ich bekam immer wieder folgendes m/d/yyyy.
Zunächst dachte ich daran, das die es ein Konfigurationsfehler sei bzw. ich mir irgendein englisches Servicepack installiert habe (ja, ich geben zu: Ich hab ein deutsches Betriebssystem - sorry). Aber auch nach erneuter Installation des DotNet Frameworks, war keine Besserung in sicht :(
Dann hab ich ein wenig im Forum von DNN gesucht in bin da auch recht schnell auf eine Lösung gekommen:
Wer ein ähnliches Problem hat sollte ganz einfach in der Javascript-Datei "~/js/PopupCalendar.js" folgende Zeile verändern:
anchorVal = "<A HREF=\"javascript:window.opener.calPopupSetDate(window.opener.popCalDstFld,'" + (thisMonth+1) + "/" + monthDate + "/" + thisYear + "');window.opener.closeCalPopup()\">";
zu
anchorVal = "<A HREF=\" window.opener.calPopupSetDate(window.opener.popCalDstFld,'" + constructDate(monthDate,thisMonth+1,thisYear) + "');window.opener.closeCalPopup()\">";
Durch die Verwendung von "constructDate" wird sichergestellt, dass auch auf jeden Fall das richtige Format zurück geliefert wird, wenn ein Anwender im DotNetNuke Popup Kalender darauf klickt!
In der Version 4.4.0 ist dieser Fehler laut Gemini-Report behoben ... für alle anderen installierten Version sollten diese Fix manuell vornehmen!
Mit dem RangeValidator von ASP.Net ist es nicht so ohne weiteres möglich die Länge einer Eingabe (Strings) zu überprüfen. Durch die Verwendung eines regulären Ausdrucks kann man aber trotzdem diese Überprüfung durchführen.
So sieht das dann im Code aus: <asp:RegularExpressionValidator ID="checklength" runat="server" ErrorMessage="Error: invalid length" ValidationExpression="^\w{1,10}$" />
Der Ausdruck "\~w(1,10)$" besagt das die Eingabe maximal 20 Zeichen lang sei darf.
Natürlich kann man auch die Eigenschaft MaxLength des Controls Textbox nutzen, allerdings kann man damit keinen Minimumwert überprüfen.
Wer schon immer mal eine Datenbankmodell-Digramm der DNN-Datenbank haben wollte, kann diese nun einfach downloaden. Die Datenbankstruktur der DNN-Version 4.4.0 wurde dabei dokumentiert. Es stehen drei verschiednen Formate zur Verfügung:
- PDF
- VISO
- HTML Windows-Hilfe
Hier gib es den Download des ERD Modells
Das DotNetNuke mittlerweile einen sehr großen und brauchbaren Funktionsumfang hat muss an dieser Stelle nicht weiter erwähnt werden. Bei der Implementierung alle dieser Features stand aber leider der Punkt Geschwindigkeit (Performance) nie im Mittelpunkt. Das soll nun endlich mit der kommenden Version 4.4.0 verändert werden!
Durch folgende Maßnahmen soll die Geschwindigkeit verbessert werden:
(1) Code Refactoring
(2) Optimierung und verbesserte Einsatz des Caching
(3) Assembly Management
(4) Database
(5) Compression
(6) Page State
Wer genauer wissen möchte was sich hinter den einzelnen Punkten versteckt kann das im Blogeintrag von Charles Nurse hier nachlesen.
Die Ergebnisse der ersten Tests kann man hier nachlesen!
Auf die Version 4.4.0 dürfen wir also alle sehr gespannt sein :)
Auch wenn ich bis jetzt das Thema Ajax hier wohl mehr stiefmütterlich behandelt habe, habe ich dieses immer im Fokus. Das von Microsoft entwickelte Ajax-Framework steht nun als RC zur Verfügung und kann hier runtergeladen weden.
Jetzt kann man also über einen ernsthaften Einsatz nachdenken und ich werde das Thema nun endlich angehen.
Für einen Bekannten habe ich eine DotNetNukeinstallation (4.3.6) aufgesetzt und ihn ein wenig damit spielen lassen. Dabei hat er sich auch ein wenig mit den Profileigenschaften eines Users beschäftigt und diese Modifiziert. Das finde ich doch schon sehr gut und hätte ich mir eigentlich von Anfang an gewünscht ..
Da sein Portal lediglich den deutschsprachigen Raum anspricht hat er einfach die Eigenschaft TimeZone gelöscht. Leider findet das DotNetNuke gar nicht witzig und normale Anwender konnten sich ab diesem Zeitpunkt nicht mehr anmelden. Einige Komponenten benötigen diese Profileigenschaft und es kracht ganz schön böse, falls diese nicht im Profil enthalten ist.
Also: Vorsicht beim Löschen von Profileigenschaften - lieber ausblenden:)
Seit gestern gibt es die aktuelle Version von DotNetNuke auf der offiziellen Downloadseite zur Verfügung. Neben einigen Fehlern, Sicherheitslücken und Peformanceoptimierung wurde auch mal die Datensyncronisation verbessert. Genaue Informationen gibt es wie immer hier.
Die Deutsche Übersetzung findet man bei http://www.deutschnetnuke.de/
Ein sehr schönes Context-Menü für ASP.net 2.0 habe ich soeben hier entdeckt. Dabei wird die Smart-Tag-Technologie (bekannt z.B. aus dem VS.NET 2005) verwendet.
Ebenso gibt es unter http://www.daypilot.org/ einen Klasse ASP.NET Kalender im Look & Feel von Outlook 2003 ..
"Konflikt der Sortierung für die equal to-Operation kann nicht aufgeloest werden."
Was will der von mir???? Nach dem Umzug einer DotNetNuke - Site auf eine anderen Server bekam ich plötzlich im Log Viewer die Fehlermedlung
DotNetNuke.Services.Exceptions.ModuleLoadException: Konflikt der Sortierung für die equal to-Operation kann nicht aufgelöst werden. ---> System.Data.SqlClient.SqlException: Konflikt der Sortierung für die equal to-Operation kann nicht aufgelöst werden. at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream) at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior) at Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteReader(SqlConnection connection, SqlTransaction transaction, CommandType commandType, String commandText, SqlParameter[] commandParameters, SqlConnectionOwnership connectionOwnership) at Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteReader(String connectionString, CommandType commandType, String commandText, SqlParameter[] commandParameters) at Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteReader(String connectionString, String spName, Object[] parameterValues) at DotNetNuke.Services.Log.EventLog.DBLoggingProvider.Data.SqlDataProvider.GetLog(Int32 PageSize, Int32 PageIndex) at DotNetNuke.Services.Log.EventLog.DBLoggingProvider.DBLoggingProvider.GetLog(Int32 PageSize, Int32 PageIndex, Int32& TotalRecords) at DotNetNuke.Services.Log.EventLog.LogController.GetLog(Int32 PageSize, Int32 PageIndex, Int32& TotalRecords) at DotNetNuke.Modules.Admin.Log.LogViewer.BindData() at DotNetNuke.Modules.Admin.Log.LogViewer.Page_Load(Object sender, EventArgs e) --- Ende der internen Ausnahmestapelüberwachung ---
Nach ein wenig Forschung war die Lösung schnell gefunden. Die SP muss ein wenig verändert werden, damit der Fehler nicht mehr auftritt.
Die Stored Procedure "GetEventLog" muss von
CREATE TABLE #PageIndex ( IndexID int IDENTITY (1, 1) NOT NULL, LogGUID varchar(36) )
auf CREATE TABLE #PageIndex ( IndexID int IDENTITY (1, 1) NOT NULL, LogGUID varchar(36) COLLATE database_default )
geändert werden. Dann ist wieder alles im Lot!
Ich möchte jetzt hier gar keine Diskussion lostretten die über das Solpart-Menu herzieht. Fakt ist auf jeden Fall:
- Das Anpassen des Menü via CSS kann einem wirklich graue Haare bereiten
- Die Größe des Menü (bezogen auf die HTML Datei) ist sehr groß
- Die Suchmaschinenfreundlichkeit ist fast nicht gegeben
Aus diesen Gründen habe ich ein kleines niedliches CSS-Menu gebaut das mit HTML Aufzählungen, CSS und wirklichen Links (!) arbeitet.
Es ist noch kein perfektes Menu aber für meine derzeitigen Einsatzbereich wirklich ausreichend. Ein paar Erweiterungen und Optimierungen müssen da in Zukunft noch implementiert werden. Aber wie bei jedem Projekt gibt es auch hier mal eine Version 1.0.
Da ich sehr gerne und eigentlich fast alle DotNetNuke Module in C# entwickel ist auch dieses Modul ist c# geschrieben.
Die Einbindung in ein Skin ist relativ simple! Einfach folgendes in das Skin aufnehmen:
<%@ Register TagPrefix="gwc" Namespace="GaliNeo.Modules.Framework" Assembly="GaliNeoMenu" %>
und dann an der Stelle wo das Menü erscheinen soll:
<gwc:GaliNeoMenu runat="server" id="TestMenu"></gwc:GaliNeoMenu>
Natürlich läst sich damit so ziemlich jede Menü darstellen das sich mit den HTML Tags UL und CSS formatieren läßt. Hier ist es zunächst ein klassisches Drop-Down Menü....
Hier der Download des Source-Codes:GaliNeo.DotNetNukeMenu (1.0.0.0).zip (7,96 KB)
Ja ja ... i know. Wochenlang habe ich mein Blog vernachläßigt und jetzt jagt ein Beitrag den anderen Aber irgendwie habe ich mir heute ein wenig Zeit genommen um durch das www zu surfen.
Hier ist noch eine Seite, die man keinen DNN Skin Designe vorenthalten sollte:
http://www.xhtmlskins.com/
Auf dieser Seite geht es um die Erstellung von DotNetNuke Skins ohne Tabellen. Total sexy :)
Hier ein paar Blogbeträge zum Thema Barcodes und wie man diese unter DotNet erzeugen kann ohne 3rd-party Komponenten:
Letztes Jahr habe ich folgenden Blog-Eintrag verfasst. Darum ging es, wie man ein HTML-Form innerhalb von .NET simulieren kann.
Jetzt ist mir gerade aufgefallen, dass der Sourcecode der ausführenden Klasse dafür fehlt. Natürlich ist das der spannende und entscheidene Part für das Post der HTML-Form. Das möchte ich natürlich nicht so stehen lassen und hier kommt der fehlende Part!
using System; using System.Net; using System.Web; using System.IO; using System.Text ;
namespace GaliNeo.Framework { public class RemotePost { #region Private Members private System.Collections.Specialized.NameValueCollection Inputs = null ; private string _sUrl = "" ;
#endregion public RemotePost() { // // TODO: Fügen Sie hier die Konstruktorlogik hinzu // Inputs = new System.Collections.Specialized.NameValueCollection() ; }
#region Public Methods
public void Add(string sName, double dValue) { this.Add(sName, dValue.ToString()) ; }
public void Add(string sName,int iValue) { this.Add(sName, iValue.ToString() ) ; } public void Add(string sName,string sValue) { Inputs.Add(sName,sValue) ; }
public string Url { get { return _sUrl ; } set { _sUrl = value ; } } public string Send() { string sPostData = "" ;
Uri sUri = new Uri(this.Url) ; //Wird für evtl. SSL - Verbindungen benötigt System.Net.ServicePointManager.CertificatePolicy = new trustedCertificatePolicy();
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(sUri);
//User-Agent request.UserAgent = "GaliNeo OnlineStore" ;
request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; //Zusammensetzen der Daten für die Übermittlung sPostData = GetContentToPost() ; request.ContentLength = sPostData.Length ;
Stream writeStream = request.GetRequestStream(); //Encoding im UTF-8 Format um kompatible zu sein //UTF8Encoding encoding = new UTF8Encoding(); //UFT-8 funktioniert unter ASP.NET nicht :( System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding() ; byte[] bytes = encoding.GetBytes(sPostData); writeStream.Write(bytes, 0, bytes.Length); writeStream.Close();
HttpWebResponse response = (HttpWebResponse) request.GetResponse(); //Überprüft den HTTP-Statuscode if (response.StatusCode != HttpStatusCode.OK) { return "N,-1, HTTPStatuscode:" +response.StatusCode.ToString() ; }
Stream responseStream = response.GetResponseStream(); StreamReader readStream = new StreamReader (responseStream, Encoding.UTF8); return readStream.ReadToEnd(); }
public string GetContentToPost() { string sReturn = "" ;
for(int i=0; i< Inputs.Keys.Count;i++) { if (sReturn.Length == 0) { sReturn = Inputs.Keys[i] + "=" + Inputs[Inputs.Keys[i]] ; } else { sReturn += "&" + Inputs.Keys[i] + "=" + Inputs[Inputs.Keys[i]] ; }// if (sPostData.Length == 0)
}//for(int i=0; i< Inputs.Keys.Count;i++)
return sReturn ;
}
#endregion
} }
Soeben habe ich eine sehr coole Komponente entdeckt, die es einem relativ einfach ermöglicht bei einer ASP.NET WebForm einen "Bitte warte Sie"-Dialog / BusyBox einzubelnden. Dieses ASP.NET Komponenten ist sehr anpassbar und scheint auf dem ersten Blick sogar schon in der Version 0.2.1 sehr brauchen zu sein.
Das Opensource Projekt heißt "BusyBoxDotNet" und ist hier zu finden.
Mal schauen wie sich die Komponente unter DotNetNuke verhält und ob man diese für eine Modulentwicklung einsetzen kann.
Die Komponente ist nur für ASP.NET 2.0 verfügbar ...
Die letzten Monate war es um DotNetNuke ja doch recht ruhig geworden. Mittlerweile kommt aber wieder Leben in den Releasezyklus :) Vor wenigen Tagen wurde die Version 3.3.3 veröffentlich, die als Final Release bezeichnet wird.
Hier ist ein Auszug aus den Änderungen (die Orginalfassung findet man unter dotnenuke.com):
Membership Management CAPTCHA - eine bekannte Technologie, die mehr Sicherheit bei der Anmeldung verspricht. In einem Bild werden Zahlen und oder Buchstaben angezeigt, die der Benutzer bei der Anmeldung eingegben muss. Durch die Darstellung als Bild es es dann nicht mehr so ohne weiteres möglich eine brute force Attacke durchzuführen. Login Redirect - Nach der Anmeldung kann man die User zu einer definierten Seite weiterleiten Password Generation - Als Administrator kann man nun automatisier ein Passwort für einen Anwender verwegeben. Public Registration - Beim diesem Anmeldetyp wird nun auch eine E-Mail an den Benutzer geschickt - damit kann man den Mißbrauch von Daten vorbeugen. Profile Change Notification - Anwender werde über Änderungen in Ihrem Profil per E-Mail informiert. User Account Creation Notification - Wenn ein Administrator ein Benutzerkonto anlegt, dann können nun die Daten an den entsprechenden Benutzer verschickt werden. Force Profile Update - Der Benutzer kann dazu "gezwungen" werden, nach einem Login sein Profil u verändern. Force Password Change - Bietet die Möglichkeit das der Anwender sein Passwort erneuern muss. Password Complexity - Der Aufbau und die Komplexität der Passwörter lassen sich definieren. Preserve Login Parameters - Wenn ein Benutzer zur Anmeldung geleitet wird, dann wird nun die Herkunftsurl ausgelesen und gespeichert, damit anschließend dahin zurück geleitet werden kann. Automated Verified Registration URL - Der Benutzer bekommt per E-Mail eine URL und sobald er diese aufruft wird das Konto freigeschaltet. User Lockout Notification - Wen ein Benutzerkonto gesperrt wird (durch die falsche Eingabe vom Passwort), bekommt der Administrator darüber eine E-Mail.
Role Management Effective Date - Ab wann soll eine Rolle gültig sein. Role Groups - Rollen können nun zu Gruppen zusammengefasst werden.
Usability Rich Text Editor - Nun können Verlinkungen zu Seiten im Poral direkt angelegt werden. Newsletter - Die Abenderadresse kann definiert werden.
Framework AccessDeniedURL - Hierfür kan nu eine Seite definiert werden.
Performance Module Settings - Seiten und Moduleinstellungen werden nun gecached :).
Module Definitions/Installer Module Definitions - Die Moduledefinition kann nun direkt durch eine *.dnn Datei angelegt werden, ohne eine Zip-Datei hochzuladen.
Other SMTP Settings - Die Portnumber kann angepasst werden New Modules - Store, Forums, Blogs, Gallery sind nun Bestandteil der aktuellen Version.
In der Mittagspause noch mit einem Bekannte über DNN diskutiert und das es in der letzten Zeit doch recht still geworden ist .... musste ich gerade feststellen das das Core-Team von DotNetNuke heute das neue Release 3.3.0 zum Download frei gegeben hat.
Leider komme ich in den nächsten nicht dazu diese Version zu testen aber in den Foren gibt es dazu bestimmt jede Menge zu lesen - oder auch nicht, wenn alles ohne Probleme verläuft. Es bleibt aber dabei: Zuerst die Installationsanweisung von DotNetNuke lesen und auf jeden Fall eine komplette Datensicherung von Datenbank und Dateisystem durchführen.
Die deutsche Übersetzung gibt es wie immer unter Sebastian Leupold.
Gerade habe ich einen interessanten Artikel gefunden der zeigt, wie man einen Parameter aus der Querystringauflistung entfernen kann.
Public Sub RemoveQueryString(ByVal Req As HttpRequest, ByVal strKeyToDel As String, ByVal http_Context As HttpContext) Dim nvc As System.Collections.Specialized.NameValueCollection = New System.Collections.Specialized.NameValueCollection(Req.QueryString) Dim sPage As String = Req.ServerVariables("SERVER_NAME") & Req.ServerVariables("SCRIPT_NAME") nvc.Remove(strKeyToDel)
Dim strNewURL As String = Req.Path Dim sSeparator As String = "?" Dim sKey As String For Each sKey In nvc If sKey <> Nothing Then Dim sValues As String() = nvc.GetValues(sKey) Dim sValue As String For Each sValue In sValues If sValue <> Nothing Then strNewURL &= sSeparator strNewURL &= sKey & "=" & sValue sSeparator = "&" End If Next End If Next If nvc.Keys.Count < 1 Then strNewURL &= "?" End If http_Context.Current.RewritePath(strNewURL) End Sub
Gefunden habe ich den Code unter http://www.codeproject.com
Eines meiner größten Probleme - oder sagen wir lieber Sorgen - war bisher, dass DNN keine Transaktionen unterstützt. Es gibt doch immer wieder mal Business-Cases wo man transaktionsicher Daten verarbeiten möchte. Ein typischens Beispiel wäre z.B. eine Online-Shop, wenn es um die Bestellung und gleichzeitige Zahlungsabwicklung geht.
Da DotNetNuke in vermutlich in vielen Fällen auf einem Windows 2003 Server betrieben wird, ist die komplette Umgebung schon vorhanden um die Abläufe transaktionssicher zu gestalten. Das Stichwort ist hier "Services without Components". Der Begriff "Services without Components" steht für die Nutzung von COM+-Dienste in .NET-Anwendung ohne Registrierung als Serviced Component (COM+-Anwendung).
Klassen: .ServiceConfig and System.EnterpriseServices.ServiceDomain
Das folgende Beispiel zeigt eine ganz einfache Implementierung innerhalb einer Methode:
//Hier wird mit einem Transaktionskontext gearbeitet, //damit das Handling einfacher wird ServiceConfig sc = new ServiceConfig(); sc.Transaction = TransactionOption.RequiresNew ; ServiceDomain.Enter ( sc ) ;
try { //mach was .... ContextUtil.SetComplete() ;
} catch (Exception exc) { ContextUtil.SetAbort() ; throw exc ; } finally { ServiceDomain.Leave(); }
Seit einigen Tagen existiert ein eigenes Informations-Portal für IT-Architekten (www.architectsconnection.de). Dort stehen sehr viele architekturrelevanten Ressourcen – Fachartikel, Kommentare, Web- und PodCasts, Vorträge, Event-Termine u.v.m zur Verfügung und soll bei den täglichen Aufgaben helfen und unterstützen.
Der Gedanke des Portals ist "Service von Architekten für Architekten". Schauen wir mal wie sich dieses Microsoft Portal entwickelt.
Bevor ich aber mit .NET 2.0 so richtig loslege hier noch ein kleiner Codebeitrag. Man hat ja immer wieder die Anforderung Daten in CSV zu exportieren (im Zeitalter von XML für mich unverständlich aber gut ). Dafür veröffentliche ich jetzt das fünftausenste Code-Beispiel.
Zunächst habe ich eine allgemeine Basisklasse geschrieben, die auch für weiter Exportformate genutzt werden kann:
using System; using System.Data; using System.Web; using System.Text; using System.IO;
namespace GaliNeo.Framework { /// <summary> /// Zusammenfassung für BaseConverter. /// </summary> public abstract class BaseConverter { public BaseConverter() { }
/// <summary> /// Export DataTable to http stream /// </summary> /// <param name="oDataTable"></param> /// <param name="response"></param> /// <param name="sFileName"></param> public void ExportToResponseStream(DataTable oDataTable, System.Web.HttpResponse response, string sFileName) { response.Clear() ; response.ClearHeaders(); response.ClearContent(); response.Buffer = true; //Very importent because "ü", "ä", usw....
response.ContentEncoding = System.Text.Encoding.Default ; response.ContentType = GetHTTPContentType() ; response.AppendHeader("content-disposition", "attachment; filename=" + HttpUtility.UrlEncode(sFileName, System.Text.Encoding.UTF8)) ;
response.Write( ConvertDataTable(oDataTable).ToString() ) ; //close http response response.End();
}
/// <summary> /// Export datatable to file /// </summary> /// <param name="oDataTable"></param> /// <param name="sFileName"></param> /// <returns></returns> public bool ExportToFile(DataTable oDataTable, string sFileName ) { try { string fullpath = System.IO.Path.GetFullPath( sFileName ) ; if (System.IO.Directory.Exists ( fullpath )) { System.IO.Directory.CreateDirectory( fullpath ) ; }
StreamWriter SW; SW=File.CreateText(sFileName); SW.WriteLine(ConvertDataTable(oDataTable).ToString()); SW.Close(); return true ; } catch { return false ; }
}
protected abstract StringBuilder ConvertDataTable(DataTable oDataTable) ; protected abstract string GetHTTPContentType() ;
} }
Die Klasse ermöglicht es eine Stream entweder auf Festplatte zu schreiben oder aber in den HTTP Responsestream.
Die passende CSV-Klasse ist relativ simple und kein wirkliches Geheimnis:
using System; using System.Data; using System.IO; using System.Text;
namespace GaliNeo.Framework { /// <summary> /// Summary description for CSVConvertor. /// </summary> public class CSVConverter : BaseConverter {
protected override string GetHTTPContentType() { return "text/csv"; }
/// <summary> /// To generate CSV file. /// </summary> /// <param name="oDataTable"></param> /// <param name="directoryPath"></param> /// <param name="fileName"></param> /// <returns></returns> protected override StringBuilder ConvertDataTable(DataTable oDataTable) {
StringBuilder oStringBuilder = new StringBuilder();
// Start, Creating column header
foreach(DataColumn oDataColumn in oDataTable.Columns) { oStringBuilder.Append(oDataColumn.ColumnName + ","); oStringBuilder.Append( System.Environment.NewLine ) ; }
//End, Creating column header //Start, Creating rows
foreach(DataRow oDataRow in oDataTable.Rows) { foreach(DataColumn oDataColumn in oDataTable.Columns) { oStringBuilder.Append(oDataRow[oDataColumn.ColumnName] + ","); }
oStringBuilder.Append( System.Environment.NewLine ) ; }
//End, Creating rows
return oStringBuilder;
} } }
Fertig!
So, nach ein wenig google etwas Arbeit habe ich jetzt ein Lösung gefunden, die sogar recht performant aussieht.
Zunächst noch ein paar Basics:
Die Tabelle ASPNET_PROFILE beinhaltet folgende Felder:
- UserId
- PropertyNames
- PropertyValuesString
- PropertyValuesBinary
- LastUpdatedDate
Das Feld PropertyNames beinhaltet eine mit ":" separierte Zeichenfolge, die definiert welche Benutzereigenschaften / -properties gespeichert werden. Zusätzlich wird die Position dort angegeben. Ein Beispiel:
FirstName:S:39:7
Dieser (Teil-)Eintrage besagt das die Property den Namen "FirstName" hat, vom Typ String "S" ist, an der Postion 39 anfängt und 7 Zeichen lang ist.
Um nun einzelene Daten zur extrahieren, habe ich mir eine die Funktionalität der UDF (User Defined Functions) vom Microsoft SQL Server bzw. der MSDE zu nutze gemacht. Diese Funktionen können direkt aus SQL-Statements aufgerufen werden.
CREATE FUNCTION dbo.GaliNeo_UDF_GetElement ( @ord AS INT, @strToParse AS VARCHAR(8000), @seperator AS VARCHAR(1) ) RETURNS INT AS BEGIN
-- Wenn die Eingabeparatemer null sind, wird auch null zurück gegeben IF @strToParse IS NULL OR LEN(@strToParse) = 0 OR @ord IS NULL OR @ord < 1 OR @ord > LEN(@strToParse) - LEN(REPLACE(@strToParse, @seperator, '')) + 1 RETURN NULL
DECLARE @ipos AS INT, @curord AS INT
SELECT @ipos = 1, @curord = 1
-- nächsts Element suchen WHILE @curord < @ord SELECT @ipos = CHARINDEX(@seperator, @strToParse, @ipos) + 1, @curord = @curord + 1 RETURN CAST(SUBSTRING(@strToParse, @ipos, CHARINDEX(@seperator, @strToParse + @seperator, @ipos) - @ipos) AS INT) END
Die GaliNeo_UDF_GetElement ist eine sehr allgemein Funktion um mit separierte Strings in SQL-Queries zu arbeiten. Sehr hilfreich war dabei ein Artikel auf der Seite WindowsItPro.
Diese Funktion können wir nun in der eigentlichen UDF "GaliNeo_UDF_GetProfileElement" nutzen:
CREATE FUNCTION dbo.GaliNeo_UDF_GetProfileElement ( @fieldName AS NVARCHAR(100), @fields AS NVARCHAR(4000), @values AS NVARCHAR(4000) ) RETURNS NVARCHAR(4000) AS BEGIN
IF @fieldName IS NULL OR LEN(@fieldName) = 0 OR @fields IS NULL OR LEN(@fields) = 0 OR @values IS NULL OR LEN(@values) = 0 RETURN NULL
DECLARE @fieldNameToken AS NVARCHAR(20) DECLARE @fieldNameStart AS INTEGER, @valueStart AS INTEGER, @valueLength AS INTEGER
SET @fieldNameStart = CHARINDEX(@fieldName + ':S',@Fields,0)
IF @fieldNameStart = 0 RETURN NULL SET @fieldNameStart = @fieldNameStart + LEN(@fieldName) + 3
SET @fieldNameToken = SUBSTRING(@Fields,@fieldNameStart,LEN(@Fields)-@fieldNameStart)
SET @valueStart = dbo.GaliNeo_UDF_GetElement(1,@fieldNameToken,':') SET @valueLength = dbo.GaliNeo_UDF_GetElement(2,@fieldNameToken,':')
IF @valueLength = 0 RETURN ''
RETURN SUBSTRING(@values, @valueStart+1, @valueLength) END
Aus einer SQL Query kann jetzt diese Funktion wie folgt genutzt werden: SELECT dbo.GaliNeo_UDF_GetProfileElement('PostalCode',PropertyNames,PropertyValuesString) FROM aspnet_Profile
So, das tat doch mal wieder fast nicht weh und bei 10.000 Profilen ist es von der Performance noch in Ordnung. Mal schauen wie es sich verhält, wenn man etwas mehr Daten in er DB hat.
|