Alternative für Deutschland

Transcrição

Alternative für Deutschland
Core
Das Umbraco-Add-in uCommerce
Alternative für Deutschland
International ist das CMS-Framework Umbraco längst etabliert, hierzulande hapert es noch an der Bekanntheit.
Dabei bauen inzwischen sogar interessante Shop-Systeme auf dieser Plattform auf.
Auf einen Blick
Christian Wendler startete
1996 mit seinem ersten Softwareunternehmen, das seinerzeit
noch mit Power Basic, Visual
Basic und ASP arbeitete. Später
verlagerte sich der Schwerpunkt
auf C#, ASP.NET und Unity. Seit
zwei Jahren hat sich sein Unternehmen exklusiv auf Umbraco
spezialisiert. Sein Motiv: „Beschäftige dich möglichst nur mit
Dingen, die Leidenschaft in dir
wecken“. Er ist unter cwendler@
byte5.de zu erreichen.
Inhalt
▸ A
ufbau eines Umbraco/
uCommerce-Shops.
▸ Einen Seitenaufruf zurück­
verfolgen.
▸ Eigenschaften eines ProductObjekts.
▸ Das Pipeline-System.
▸ Stärkere Anpassung des ShopSystems.
dnpCode
A1309Umbraco
F
alls Sie als .NET-Entwickler in die Verlegenheit kommen, einen Webshop aufzubauen, bieten sich die üblichen Verdächtigen aus dem PHP-Umfeld an wie beispielsweise Magento oder Oxid. Wollen Sie jedoch der
.NET-Plattform treu bleiben, dünnt das Feld der
möglichen Kandidaten schlagartig aus. Dann
fällt die Wahl vielleicht auf nopCommerce oder
eine selbst entwickelte Lösung, wenn der Aufwand den Projektrahmen nicht sprengt.
An dieser Stelle lohnt sich ein Blick auf das
offene CMS Umbraco [1], über das dotnetpro
schon einige Male berichtete [2, 3, 4]. Über die
vergangenen Jahre hinweg hat die UmbracoCommunity recht fleißig Zusatzsoftware, sogenannte Packages, für das System erstellt. Einige davon sind, wie bei Open-Source-Projekten
üblich, von zweifelhafter Qualität, doch finden
sich darunter auch viele Perlen, die professionellen Ansprüchen genügen. In einigen Fällen
haben die Autoren dieser Packages die Weiterentwicklung inzwischen sogar recht erfolgreich
zu ihrem Hauptberuf erklärt, was – abseits der
Diskussion um Kommerz – für die Qualität dieser Softwareprodukte spricht und somit als gutes Zeichen verstanden werden kann.
Umbraco ist von Natur aus ein ContentManagement-System (CMS), das sich primär
an Entwickler richtet und daher gern auch als
CMS-Framework verstanden wird: Es erweitert
ASP.NET um ein CMS-API und bietet ein erweiterbares Backend an. Ein Designer, der es gewohnt ist, „out of the box“ eine bunte Website
zu erhalten, die dann mit etwas Oberflächen­
arbeit und ein paar Mausklicks an die eigenen
Bedürfnisse anzupassen ist, sollte Umbraco
besser meiden.
Entwickler jedoch, die Wert darauf legen, ein
flexibles und erweiterbares System zu nutzen,
das zudem auf ASP.NET aufbaut, werden nach
der ersten Einarbeitung und Eingewöhnung die
vielen Möglichkeiten zu schätzen wissen.
unter anderem Kundendaten an die registrierten
Nutzer der Website gekoppelt werden und die
Shops klinken sich in das Umbraco-Backend ein.
Einziger Wermutstropfen ist die Tatsache,
dass alle drei Systeme derzeit nicht quelloffen
sind. Jedoch verbergen sich dahinter keine gesichtslosen Softwareunternehmen, sondern
Entwickler, die in der Community sehr aktiv
und daher auch leicht zu erreichen sind.
Die Systeme unterscheiden sich sowohl
im Lizenzmodell als auch in der Frage, wo sie
Stammdaten ablegen. Tea Commerce nutzt
hierfür den Content-Baum von Umbraco (Produkte sind also originäre Content-Objekte im
CMS), uCommerce setzt dagegen auf eine eigene Datenstruktur in separaten Tabellen.
Welcher Ansatz der bessere ist, muss jeder
für sich entscheiden. Diese Einführung arbeitet
mit uCommerce, da hier die Wahl zwischen zwei
kostenlosen Versionen besteht. Die DeveloperEdition enthält alle Funktionen, ist jedoch nur
für den Einsatz mit localhost-Domänen gedacht.
Andernfalls erscheint ein Lizenzhinweis im
Frontend. Die kostenlose Edition ist etwas eingeschränkt im Umfang, dafür jedoch auch produktiv ohne Gebühr oder Ähnliches nutzbar und
für die meisten Webshops ausreichend.
Bevor Sie sich der Programmierung eines
eigenen Shops widmen können, benötigen Sie
eine funktionstüchtige CMS-Instanz. Wie Sie
Umbraco einrichten, erklärt der Kasten So setzen Sie eine Umbraco-Instanz auf. Die folgenden
Erläuterungen gehen davon aus, dass das lokale
Web unter dev.local eingerichtet ist.
Ist die Installation erfolgreich absolviert,
können Sie jederzeit auf das Umbraco-Backend
über den URL http://dev.local/umbraco zugreifen. Loggen Sie sich ein und machen Sie
sich mit der Navigation vertraut, die Sie in Abbildung 1 sehen: In der linken Spalte der Um-
Die Alternativen
Aus Community-Projekten sind inzwischen drei
professionelle Shop-Systeme hervorgegangen,
die sich allesamt nahtlos in Umbraco einfügen:
uCommerce [5], Tea Commerce [6] und uWebshop [7]. Vorteil bei allen ist, dass es zwischen
CMS und Shop keinen Bruch gibt; so können
52
[Abb. 1] Das Hauptmenü einer Umbraco-Website.
9.2013 www.dotnetpro.de
Core
braco-Oberfläche befindet sich stets eine
Baumansicht, die je nach aktivem Bereich
die Inhaltsstruktur oder einen Menübaum
darstellt. Im großen Bereich rechts findet
die eigentliche Bearbeitung von Einstellungen und Inhalten statt. Links unten
stellt Umbraco die sogenannten Sektionen (Sections) bereit; der Kasten UmbracoSektionen im Überblick erläutert die Sektio­
nen-Struktur.
Begeben Sie sich zunächst in die Developer-Sektion, um dort im Baum den Eintrag Packages aufzuklappen. Wählen Sie
die Funktion Install local package, markieren Sie das Kontrollkästchen I understand the security risk und wählen Sie über
Durchsuchen das heruntergeladene und
nicht entpackte uCommerce-Archiv aus.
Nach einem Klick auf Load Package gelangen Sie auf die nächste Seite, auf der
Sie die Lizenzbedingungen akzeptieren
müssen, um fortzufahren.
Das Package wird der Umbraco-Instanz
hinzugefügt. Nach einem Neuladen des
Backends durch einen erneuten Login
erscheint eine neue Sektion mit der Bezeichnung Commerce, die das Bearbeiten
aller Shop-Einstellungen sowie der Produkte und Aufträge erlaubt.
Installieren Sie zum Schluss noch auf
die gleiche Art und Weise den uCommerce
Razor Store [8] über Install local package.
Rückverfolgen eines Aufrufs
Öffnen Sie nun mit Ihrem Browser das
Frontend der Website über die Webadresse http://dev.local, um sich mit dem
Demo-Shop, dem „uCommerce Razor
Store“, vertraut zu machen, den Sie in Abbildung 2 sehen. Er dient als Ausgangsbasis für die ersten Anpassungen und Erweiterungen, die Sie programmieren werden.
uCommerce selbst versteht sich, wie
in der Umbraco-Welt üblich, als eine Art
Shop-Foundation. Der Fokus liegt also
auf der Bereitstellung von API [9] und
Backend. Das API lässt sich innerhalb
Umbracos über XSLT und Razor steuern,
außerdem stellt uCommerce über das
Framework ServiceStack verschiedene
Webdienst-Schnittstellen zum Zugriff auf
die wichtigsten Funktionen zur Verfügung, beispielsweise JSON [10]. Der Demo-Shop macht hiervon Gebrauch, um
Änderungen am Warenkorb und Produktbewertungen auch ohne vollständiges
Neuladen der Seite zu verarbeiten.
Schauen Sie sich einmal die Produktseite an, um sich die Zusammenhänge in
uCommerce und der vorliegenden Im-
So setzen Sie eine Umbraco-Instanz auf
Installieren Sie zunächst, falls noch nicht geschehen, die Internet Information Services (IIS), den Microsoft SQL Server (die Express-Version genügt) und natürlich das .NET Framework in der Version 4.
Sie können aber auch WebMatrix benutzen; hier wird jedoch die klassische manuelle Installation
beschrieben.
Legen Sie im Management Studio des SQL Servers eine neue Datenbank und anschließend einen
Nutzer an. Achten Sie darauf, dass dieser User die Datenbankrollen db_datareader, db_datawriter,
db_ddladmin, db_owner und public erhält.
Im nächsten Schritt erzeugen Sie eine neue Website im IIS-Manager. Hier empfiehlt es sich,
.local-Domänen zu verwenden, also zum Beispiel umbraco.local oder dev.local, um lokal mehrere
Instanzen gleichzeitig hosten zu können. Diese .local-Domänen müssen lediglich in die hosts-Datei
unter C:\Windows\System32\Drivers\etc\ eingetragen werden.
Weisen Sie nun der neuen Website einen vorhandenen oder neuen Application-Pool zu, achten
Sie jedoch darauf, dass dieser für .NET 4 konfiguriert ist. Notieren Sie sich den Namen des Pools, Sie
benötigen ihn beim Setzen der Zugriffsrechte [13].
Nun ist es an der Zeit, Umbraco und uCommerce herunterzuladen [14, 15]. Laden Sie auf der
uCommerce-Downloadseite unbedingt auch noch den „uCommerce Razor Store“ herunter, der als
Ausgangsbasis für den ersten Shop dient.
Der Code des Demo-Shops ist offen, interessierte Entwickler finden ihn auf Bitbucket [8].
Die nötigen Dateien befinden sich nun auf dem System. Bevor Sie fortfahren, müssen Sie bei beiden Archiven im Eigenschaftendialog auf Zulassen klicken, andernfalls entpackt Windows die Archive
nicht vollständig.
Beginnen Sie mit dem Umbraco-Archiv und extrahieren Sie den Inhalt in das neue Web. Geben Sie
nun im Sicherheitsdialog des Web-Verzeichnisses dem Benutzer IIS AppPool\MeinAppPool vollständigen Zugriff, wobei Sie MeinAppPool durch den Namen des von Ihnen ausgewählten AnwendungsPools ersetzen.
Nun ist alles bereit für die Installation; diese aktivieren Sie durch das Öffnen Ihrer lokalen Domain,
zum Beispiel http://dev.local. Machen Sie die notwendigen Angaben zu Ihrer Datenbankverbindung
und folgen Sie den weiteren Installationsschritten.
Umbraco-Sektionen im Überblick
Im Bereich Sections stellt Umbraco die Bereiche der Site zur Verfügung, er dient sozusagen als Hauptmenü. Folgende Sektionen werden angeboten:
◼ Content dient dem Verwalten und Bearbeiten der eigentlichen Inhalte der Website. Hier arbeiten
später die Redakteure.
◼ Media ähnelt Content, ist jedoch auf die Verwaltung von Mediendaten spezialisiert. Darunter fallen
zum Beispiel Bilder, PDF-Dateien und Videos.
◼ Settings enthält unter anderem die HTML-Vorlagen, CSS-Dateien, das Wörterbuch für mehrsprachige
Websites und die Dokumenttypen.
◼ Developer erlaubt das Bearbeiten von Datentypen und Makros, außerdem lassen sich hier Packages
installieren.
◼ Users offeriert eine Verwaltung der Backend-Benutzer.
◼ Members kommt zum Einsatz, wenn sich Nutzer der Website registrieren können, sei es für einen
Newsletter, ein Forum oder einen Webshop.
plementierung im Demo-Shop klarzumachen. Sie sind aber bei der Entwicklung
eines eigenen Webshops keineswegs auf
dieses Beispiel festgelegt. Generell lassen
Umbraco und uCommerce große Freiheiten, wie Sie zum Ziel gelangen.
Die Betrachtung einer Produktseite beginnt zunächst im Frontend: Wählen Sie
in einer beliebigen Kategorie ein beliebiges Produkt. Achten Sie nun auf den URL
dieser Produktseite, der etwa so aussieht:
http://ucommerce.local/demo-store/
shoes/paraboot-chambord-texmarron-lis-cafe-brown-shoes/
c-24/c-72/p-186
uCommerce generiert von Haus aus
suchmaschinentaugliche URLs, die stets
den Kategorie- und Produktnamen enthalten. Um dennoch eine eindeutige Zuordnung zu erlauben, werden den Webadressen je nach Kontext verschiedene IDs
www.dotnetpro.de 9.201353
Core Das Umbraco-Add-in uCommerce
[Abb. 2] Das Frontend des uCommerce-Demo-Shops Razor.
angehängt, in diesem Fall c-24, c-72 und
p-186. Die erste ID referenziert den aktiven Katalog, die zweite die Kategorie und
die dritte das gewählte Produkt.
Da dieser URL eine virtuelle Adresse
darstellt und Umbraco damit wenig anfangen kann, hinterlegt uCommerce bei
der Installation einige Rewriting-Regeln,
die Sie im Webverzeichnis in der Textdatei
\Config\UrlRewriting.config finden.
Dazu noch ein Tipp: Wenn Sie die web.
config-Datei und die Umbraco-Konfigurationsdateien direkt im CMS bearbeiten
wollen, sollten Sie sich einmal mit dem Pa-
ckage ConfigTree beschäftigen. Sie können
es in der Developer-Sektion unter Packages
mit dem Menüpunkt Umbraco package
Repository aus dem Sortiment der Pakete
auswählen und direkt installieren.
Die Rewriting-Regel, die den Aufruf auf
eine Produktseite umformt, sieht wie in
Listing 1 aus. Sie legt fest, dass der Aufruf
in Umbraco auf einen Knoten mit dem
URL /Catalog/Product.aspx umgeleitet
wird; die IDs werden als GET-Parameter
angehängt. Sie finden diesen Knoten in
der Content-Sektion, wenn Sie die Knoten
Shop und Catalog öffnen. Klicken Sie den
Listing 1
Die Rewriting-Regel für den Aufruf einer Produktseite.
<add name="DefaultCategoryProductRewrite" virtualUrl="(.*)/c-([0-9]+)/c-([0-9]+)/
p-([0-9]+)" rewriteUrlParameter="ExcludeFromClientQueryString"
destinationUrl="~/catalog/product.aspx?catalog=$2&amp;category=$3&amp;product=$4"
ignoreCase="true" xmlns="" />
Listing 2
Die versteckten Formularfelder, die Metainformationen zum Produkt
transportieren.
<input name="product-sku" id="product-sku" type="hidden" value="@product.Sku" />
<input name="quantity-to-add" id="quantity-to-add" type="hidden" value="1" />
<input name="catalog-id" id="catalog-id" type="hidden"
value="@SiteContext.Current.CatalogContext.CurrentCatalog.ProductCatalogId" />
<!-- We have to default the button to "success" for those customers who don't have
JavaScript enabled -in which case it will still be a clear call to action -->
<input name="add-to-basket" id="add-to-basket" class="btn btn-block btn-success"
type="submit" value="Add to basket" />
54
Product-Node an, um ihn zu bearbeiten.
Umbraco wird nun im Hauptbereich zwei
Registerkarten anzeigen: ein Content-Register mit einem großen Rich-Text-Editor
und ein Properties-Register. Letzteres gibt
es grundsätzlich für jeden Knoten in Umbraco und es enthält die Stammdaten wie
Name, Erstellungsdatum, Autor, zugrunde
liegender Dokumenttyp und die Vorlage,
die für die Darstellung der Ausgabe herangezogen wird.
Der Dokumenttyp ist in Umbraco so etwas wie eine abstrakte Klasse und definiert,
welche Datenfelder ein Knoten enthält und
welches Format, und damit auch, welcher
Editor für jedes Feld infrage kommt. Weiterhin gibt ein Dokumenttyp vor, welche
anderen Typen im Inhaltebaum unter ihm
angelegt werden dürfen. Ein klassisches
Beispiel wäre, dass unterhalb eines Knotens
vom Typ Blog nur der Dokumenttyp Blogpost erlaubt ist.
Um dem Pfad des Aufrufs auf die Produktseite weiter zu folgen, gerät nun die
Vorlage in den Fokus: Auf der PropertiesRegisterkarte erhalten Sie die Auskunft,
dass der Product-Knoten eine Vorlage
namens RazorStoreProduct verwendet.
Diese können Sie in der Settings-Sektion
im Bereich Templates finden: Dort werden die Vorlagen, die technisch gesehen
ASP.NET-Masterpages darstellen, verwaltet. Im Demo-Shop sind drei Ebenen von
Masterpages implementiert: RazorStore
enthält das HTML-Grundgerüst der Website, das untergeordnete Template RazorStoreContent schleift ContentPlaceHolder durch und dient der Abgrenzung zur
Homepage-Vorlage RazorStoreFrontPage.
Öffnen Sie nun die Vorlage RazorStoreProduct, die vom Product-Knoten verwendet wird. Es mag überraschen, dass an dieser Stelle nur ein Makro in den Platzhalter
cphMain und ein JavaScript-Skript in den
Platzhalter cphScripts eingefügt werden.
Der Hintergrund ist, dass die Produktseite
naturgemäß hauptsächlich aus varia­blen
Ausgaben besteht wie Produktnamen,
Bezeichnungen, Preisen und dergleichen
mehr. Hinzu kommt, dass Schaltflächen
Einfluss auf die Anzeige nehmen, zum
Beispiel die Produktbewertung oder der
Kaufen-Button. Es liegt daher nahe, die
gesamte Produktdarstellung in ein einziges großes Makro auszulagern. Beachten
Sie, dass bei diesem Ansatz der jeweilige
Designer etwaige Änderungen am HTML
im Makro vornehmen muss. Es empfiehlt
sich daher, sich frühzeitig auf ein solides
HTML-Gerüst festzulegen, sodass gestal-
9.2013 www.dotnetpro.de
Core
terische Anpassungen ausschließlich über
CSS vorgenommen werden können.
Nehmen Sie sich als Nächstes das Produktmakro vor, das für die Anzeige der
Produktdaten im Browser zuständig ist.
Hierzu wechseln Sie in die Developer-Sektion und werfen zunächst einen Blick in
den Menüpunkt Macros, der die Makrodefinitionen enthält. Öffnen Sie den Eintrag
uCommerce Product, um Auskunft über
das verknüpfte Control oder die entsprechende Skriptdatei und die konfigurierten
Makroparameter zu erhalten. In diesem
Fall verweist die Makrodefinition auf ein
Razor-Skript namens uCommerce/Product.
cshtml. Dieses finden Sie ebenfalls in der
Developer-Sektion, jedoch im Menüpunkt
Scripting Files.
Hinter den Kulissen
Wenn Sie die Datei Product.cshtml öffnen,
sehen Sie Code und HTML der Produkt­
anzeige wie in Abbildung 3. Am Anfang
des Skripts wird unter anderem das aktive
Produkt geladen; als dieser dotnetproArtikel entstand, befand sich in Zeile 9 die
folgende Anweisung :
var product = SiteContext.Current.
CatalogContext.CurrentProduct;
uCommerce lädt bei diesem Aufruf automatisch das Produkt, das über die GETParameter in der Rewriting-Regel spezifiziert wurde.
Folgende Eigenschaften sind über das
Product-Objekt verfügbar:
◼.AllowOrdering
◼.CategoryProductRelations
◼.CreatedBy
◼.CreatedOn
◼.DisplayOnSite
◼.Id
◼.IsVariant
◼.Item[String]
◼.ModifiedBy
◼.ModifiedOn
◼.Name
◼.ParentProduct
◼.ParentProductId
◼.PriceGroupPrices
◼.PrimaryImageMediaId
◼.ProductDefinition
◼.ProductDefinitionId
◼.ProductDescriptions
◼.ProductId
◼.ProductProperties
◼.ProductRelations
◼.ProductReviews
◼.Rating
◼.Sku
[Abb. 3] Ein Razor-Skript in C# im Umbraco-Backend steuert die Ausgabe der Produktseite.
◼.ThumbnailImageMediaId
◼.Variants
◼.VariantSku
◼.Weight
Wird zudem UCommerce.Extensions
über using eingebunden, stehen noch folgende Hilfsfunktionen im Product-Objekt
bereit, die lokalisierte Attribute anhand
des aktiven Kulturcodes zurückliefern:
◼.DisplayName()
◼.ShortDescription()
◼.LongDescription()
Mit diesen Eigenschaften und Funktio­
nen lässt sich nun bereits eine einfache
Produktseite aufbauen. Die Ausgabe einzelner Produktattribute, beispielsweise
des Namens, können Sie in Zeile 80 nachvollziehen:
<h1 itemprop="name">@product.DisplayName()
</h1>
Ein wichtiges Detail fehlt allerdings im
Produktobjekt: der Preis. Da dieser von
verschiedenen Faktoren abhängt, muss er
individuell im jeweiligen Zusammenhang
berechnet werden. Das API bietet dazu eine Funktion:
CatalogLibrary.CalculatePrice(product);
Die reine Anzeige von Produktattributen wäre nun wohl etwas wenig, immerhin zeichnet sich ein Webshop durch
zahlreiche Funktionen aus, die Sie als
Entwickler mithilfe von Shop-Systemen
implementieren. Die wichtigste Funktion ist sicherlich das Hinzufügen eines
Produkts zum Warenkorb. Diese ist im
Demo-Shop auf zwei Arten realisiert. Die
eine arbeitet klassisch als Postback und
zeitgemäß mittels JavaScript-API, und um
diese geht es im Folgenden. Ab Zeile 115
legt Products.cshtml einige versteckte Formularfelder an, die beim Postback Infor-
Listing 3
Der Code, der ein Produkt in den Warenkorb legt.
if (request.HttpMethod == "POST")
{
@* View /App_Code/uCommerce/Functions/Product.cshtml for this function *@
var variant = uCommerce.Functions.Product.GetVariantFromPostData(product, "variation-");
@* View /App_Code/uCommerce/Functions/Basket.cshtml for this function *@
itemAddedToCart = uCommerce.Functions.Basket.AddToBasket("add-to-basket",
"quantity-to-add", variant);
@* View /App_Code/uCommerce/Functions/Review.cshtml for this function *@
uCommerce.Functions.Review.LeaveReview(product, "review-name", "review-email",
"review-rating", "review-headline", "review-text");
}
www.dotnetpro.de 9.201355
Core Das Umbraco-Add-in uCommerce
Listing 4
Die Funktion, die ein Produkt zum Warenkorb hinzufügt.
addToBasket: function(options, onSuccess, onError) {
var defaults = {
quantity: 1,
sku: '',
variantSku: '',
addToExistingLine: true
};
var extendedOptions = $.extend(defaults, options);
callServiceStack({ AddToBasket: extendedOptions }, onSuccess, onError);
}
Listing 5
Die Klasse HappyHour.
using
using
using
using
System;
System.Web;
UCommerce.EntitiesV2;
UCommerce.Pipelines;
namespace dotnetpro {
public class HappyHour : IPipelineTask<PurchaseOrder> {
public PipelineExecutionResult Execute(PurchaseOrder purchaseOrder) {
// Auftragspositionen einzeln durchlaufen
foreach(OrderLine item in purchaseOrder.OrderLines) {
// Preis auf 1 EUR setzen
item.Price = 1;
// Änderungsdatum und Uhrzeit in der Position speichern
item["Test"] = DateTime.Now.ToString();
}
// Erfolgreiche Verarbeitung
return PipelineExecutionResult.Success;
}
}
}
Listing 6
Die Deklaration des neuen Tasks.
<component id="Basket.HappyHour"
service="UCommerce.Pipelines.IPipelineTask'1[[UCommerce.EntitiesV2.PurchaseOrder,
UCommerce]], UCommerce"
type="dotnetpro.HappyHour, dotnetpro" />
mationen zum Produkt, zum Katalog und
zur Menge übergeben und die in Listing 2
zu sehen sind.
Klickt der Nutzer auf die Kaufen-Schaltfläche, wird ein Postback ausgelöst, der
die Seite neu lädt und das Razor-Skript
erneut ausführt. An dieser Stelle greifen
56
nun die Zeilen ab Zeile 12, siehe Listing 3.
Das Skript prüft unter Verwendung von
ein paar Hilfsfunktionen (zu finden im
Webverzeichnis unter \App_Code\uCommerce\Functions) auf einen erfolgten
Postback, ermittelt die Produktvariante,
fügt den Artikel in der gewünschten Vari-
ante dem Warenkorb hinzu und speichert
ein Review. Letzteres mutet zunächst fehlerhaft an, ist aber korrekt, da sowohl die
Schaltfläche für das Kaufen als auch die
Review-Funktion schlicht einen Postback
auslösen. Da jedoch unterschiedliche Formulare im HTML definiert sind, werden
nur die jeweiligen Parameter gesendet,
deren Vorhandensein die Hilfsfunktionen
prüfen, bevor ein Produkt dem Warenkorb
hinzugefügt oder eine Produktbewertung
gespeichert wird.
Sollte im Browser JavaScript aktiviert
sein, wird der Postback jedoch abgefangen. Dies findet in der Datei uCommerce.
demostore.productpage.js statt, die Sie in
der Settings-Sektion im Menüpunkt Scripts
öffnen können. Dieses Skript ist auf der
Clientseite Teil des Frontends und daher
nicht in der Developer-Sektion zu finden.
Ab Zeile 75 ist eine JavaScript-Funktion
mit dem Namen wireupAddToCartButton() zu finden, die das Klickereignis abfängt und den Artikel durch folgenden
Aufruf dem Warenkorb hinzufügt:
$.uCommerce.addToBasket(
{
catalogId: catalogIdInput.val(),
sku: variant.Sku,
variantSku: variant.VariantSku,
quantity: qty
},
Die Funktion uCommerce.addToBasket() selbst ist in der JavaScript-Datei
uCommerce.jQuery.js ab Zeile 27 definiert
und in Listing 4 zu sehen.
An dieser Stelle wird die Kontrolle an das
JavaScript-API von ServiceStack abgegeben; im Hintergrund wird gleichzeitig das
uCommerce-JavaScript-API aufgerufen.
Einen Überblick über die mittels Ser­
vice­
S tack zur Verfügung stehenden
uCom­merce-Funktionen verschaffen Sie
sich, indem Sie den URL http://dev.local/
ucommerceapi im Browser aufrufen.
Das Pipeline-System
Ob Sie nun einen Shop auf Grundlage des
Demo-Shops oder von Grund auf selbst
entwickeln, bleibt Ihnen mit Umbraco und
uCommerce völlig freigestellt. Auch gibt es
kaum Dinge, die sich nicht völlig anders
umsetzen ließen – was sich besonders bei
der Zusammenarbeit mit Agenturen auszahlt. Falls einmal die Bordmittel nicht
weiterhelfen, lassen sich die meisten
Dinge in uCommerce durch ein paar in
Visual Studio geschriebene Klassen leicht
anpassen. Möglicherweise benötigen Sie
9.2013 www.dotnetpro.de
Core
zusätzliche Schritte in einem Workflow,
wie beispielsweise die Aktualisierung des
Warenkorbs; oder Sie wollen in Echtzeit
die Verfügbarkeit eines Artikels prüfen.
Für diese Fälle bietet uCommerce sogenannte Pipelines, die über XML-Dateien
zu konfigurieren sind. Die Pipeline, die bei
Warenkorbänderungen ausgeführt wird,
ist in \Umbraco\ucommerce\Pipelines\
Basket.config festgelegt. Im oberen Abschnitt ist die Ausführungsreihenfolge der
einzelnen Tasks definiert, im unteren Bereich werden diese über Component-Einträge deklariert. Jedes Element entspricht
dabei einer Klasse, die das Interface IPipelineTask implementiert.
Legen Sie für einen ersten Versuch
in Visual Studio ein neues Projekt vom
Typ Klassenbibliothek an und nennen
Sie es dotnetpro. Als Nächstes fügen Sie
Verweise auf System.Web, UCommerce.
dll, UCommerce.Infrastructure.dll und
UCommerce.Pipelines.dll hinzu; anschließend erstellen Sie eine neue Klasse namens HappyHour mit dem Quellcode, der
in Listing 5 und in Abbildung 4 im Editor
von Visual Studio zu sehen ist.
Kompilieren Sie dieses Projekt und kopieren Sie die Datei dotnetpro.dll in das
bin-Verzeichnis Ihrer Umbraco-Website.
Damit uCommerce diesen Pipeline-Task
aufruft, muss er noch in die Basket.config
eingetragen werden. Dazu fügen Sie die
folgende Zeile nach Basket.CalculateShippingTotalForShipments und vor Basket.
CalculateOrderLineTax ein:
<value>${Basket.HappyHour}</value>
Nun fehlt noch die Deklaration des
neuen Tasks. Direkt unter dem Kommentar <!-- Pipeline Tasks --> ist hierfür der
Eintrag nötig, der in Listing 6 zu sehen ist.
id stellt eine frei wählbare Bezeichnung
dar, auf die oben die eigentliche Pipeline
Bezug nimmt. type besteht aus Ihrem Namensraum und dem Klassennamen, nach
dem Komma folgt dann noch der Assembly-Name, also der Dateiname ohne die
Erweiterung .dll.
Beachten Sie, dass Änderungen an diesen Konfigurationsdateien einen Neustart des Webs erforderlich machen, um
wirksam zu werden. Der einfachste Weg
besteht darin, die Datei web.config zu verändern (und sei es auch nur durch neues
Speichern).
Wechseln Sie nun in das UmbracoBack­end und öffnen Sie in der DeveloperSektion unter Scripting Files die Datei
Cart.cshtml. Da die neue HappyHour-
[Abb. 4] Auch mit Visual Studio kann das Shop-System um eigene Funktionen erweitert werden.
Klasse nicht nur den Preis auf einen Euro
setzt, sondern zu Demonstrationszwecken auch noch einen Zeitstempel in der
benutzerdefinierten Eigenschaft Test ablegt, können Sie den Inhalt dieser Eigenschaft im Warenkorb abrufen und anzeigen. Suchen Sie dazu die folgende Zeile:
<td><a href="@CatalogLibrary.
GetNiceUrlForProduct(product)">
@lineItem.ProductName</a></td>
Verändern Sie diese Zeile zu:
<td><a href="@CatalogLibrary.
GetNiceUrlForProduct(product)">
@lineItem.ProductName</a><br/>
@lineItem["Test"]</td>
Wenn Sie nun den Shop besuchen und
ein neues Produkt in den Warenkorb legen, wird es mit einem Euro ausgewiesen,
zudem erscheint wie gewünscht der Zeitstempel. Benutzerdefinierte Eigenschaften
können für unterschiedliche Zwecke nützlich sein, so zum Beispiel um – basierend
auf einem Zeitstempel – zyklisch Verfügbarkeiten von bereits im Warenkorb befindlichen Produkten zu prüfen oder um Bestellpositionen zusammenzufassen. Allein mit
den Pipelines, die nicht nur für den Warenkorb, sondern beispielsweise auch für den
Checkout-Prozess vorliegen, lassen sich
umfangreiche Anpassungen vornehmen.
Weitergehende Anpassungen
des Shop-Systems
Auch abseits der Pipelines können Sie
Einfluss auf uCommerce nehmen, beispielsweise eigene Zahlungs-Provider entwickeln und einbinden, falls die mitgelie-
ferten Provider den gewünschten Anbieter
nicht abdecken. Über Windows-Applika­
tionen können Sie sogar außerhalb des
Webkontexts auf uCommerce zugreifen,
um im Hintergrund Stammdaten abzugleichen, auszuwerten oder zu exportieren.
Sehr interessant ist auch die Möglichkeit, URLs selbst zu erzeugen. Bei größeren Shops mit besonderen Anforderungen
kann dies sehr hilfreich sein. Folgendes
Beispiel zeigt, wie Sie auch vermeintliche
Kernfunktionen des Systems leicht durch
eigene Klassen ersetzen können.
Fügen Sie dem Projekt eine neue Datei
namens UrlService.cs hinzu und leiten Sie
Ihre Klasse von IUrlService ab. Als Ausgangspunkt kann dabei der Quellcode aus
Listing 7 dienen.
Um den eingebauten UrlService gegen
den selbst entwickelten auszutauschen,
wird der Code kompiliert, die DLL-Datei
in das bin-Verzeichnis kopiert und die Datei \Umbraco\ucommerce\Configuration\
Core.xml bearbeitet. Dazu suchen Sie nach
folgendem Eintrag:
<component id="UrlService"
service="UCommerce.Catalog.IUrlService,
UCommerce"
type="UCommerce.Catalog.UrlService,
UCommerce"/>
Hier muss lediglich das type-Attribut
angepasst werden, damit die neue Klasse
statt des Originals referenziert wird:
<component id="UrlService"
service="UCommerce.Catalog.IUrlService,
UCommerce"
type="dotnetpro.UrlService,
dotnetpro"/>
www.dotnetpro.de 9.201357
Core Das Umbraco-Add-in uCommerce
Listing 7
Vergessen Sie nicht, das Web danach
neu zu starten, damit die Änderungen,
die Sie angebracht haben, auch wirksam
werden.
Zudem sollten Sie noch beachten, dass
Anpassungen an UrlService Auswirkungen
haben: Vor allem die Rewriting-Regeln im
Shop müssen zur veränderten URL-Syntax kompatibel sein und müssen entsprechend angepasst werden.
uCommerce-Kernfunktionen ersetzen.
using
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Web;
UCommerce.Catalog;
UCommerce.EntitiesV2;
UCommerce.Infrastructure.Globalization;
Fazit
namespace dotnetpro {
public class UrlService : IUrlService {
private ILocalizationContext LocalizationContext { get; set; }
public UrlService(ILocalizationContext localizationContext) {
LocalizationContext = localizationContext;
}
public virtual string GetUrl(ProductCatalog catalog) {
ProductCatalogDescription catalogDescription =
GetCatalogDescriptionWithFallback(catalog);
return string.Format("/{0}/c-{1}", UrlEncode(catalogDescription.DisplayName),
catalog.ProductCatalogId);
}
public virtual string GetUrl(ProductCatalog catalog, Product product) {
var catalogDescription = GetCatalogDescriptionWithFallback(catalog);
ProductDescription productDescription = GetProductDescription(product);
string productUrl;
if(product.IsVariant) {
var parentDescription = GetProductDescription(product.ParentProduct);
productUrl = string.Format("/{0}/{1}/{2}/c-{3}/p-{4}/v-{5}",
UrlEncode(catalogDescription.DisplayName),
UrlEncode(parentDescription.DisplayName),
UrlEncode(productDescription.DisplayName),
catalog.ProductCatalogId, product.ParentProduct.Id, product.ProductId);
} else {
productUrl = string.Format("/{0}/{1}/c-{2}/p-{3}",
UrlEncode(catalogDescription.DisplayName),
UrlEncode(productDescription.DisplayName),
catalog.ProductCatalogId, product.ProductId);
}
return productUrl;
}
public virtual string GetUrl(ProductCatalog catalog, Category category, Product product) {
var catalogDescription = GetCatalogDescriptionWithFallback(catalog);
var currentCategory = category;
var concattedCategoryDisplyName = "";
while(currentCategory != null) {
concattedCategoryDisplyName = UrlEncode(GetCategoryDescriptionWithFallback(
currentCategory).DisplayName) + (string.IsNullOrEmpty(
concattedCategoryDisplyName) ? "" : "/") + concattedCategoryDisplyName;
currentCategory = currentCategory.ParentCategory;
}
var productDescription = GetProductDescription(product);
string productUrl;
if(product.IsVariant) {
var parentDescription = GetProductDescription(product.ParentProduct);
productUrl = string.Format("/{0}/{1}/{2}/{3}/c-{4}/c-{5}/p-{6}/v-{7}",
UrlEncode(catalogDescription.DisplayName), concattedCategoryDisplyName,
UrlEncode(parentDescription.DisplayName),
UrlEncode(productDescription.DisplayName), catalog.ProductCatalogId,
category.CategoryId, product.ParentProduct.Id, product.ProductId);
} else {
productUrl = string.Format("/{0}/{1}/{2}/c-{3}/c-{4}/p-{5}",
UrlEncode(catalogDescription.DisplayName), concattedCategoryDisplyName,
UrlEncode(productDescription.DisplayName), catalog.ProductCatalogId,
▶(Fortsetzung S.59)
58
Dieser Artikel war nur ein kurzer Rundflug über uCommerce und die Anpassungsmöglichkeiten, die das System dem
Entwickler bietet und auf die er Zugriff
hat. Für eine umfassende Beschäftigung
sei, neben den eigenen Erkundungen des
API, auf das Support-Forum [11] und das
lesenswerte Weblog des Herstellers verwiesen. Auch der Kontakt zur Usergroup
kann weiterhelfen [12].
Ein weites Betätigungsfeld bieten auch
die eingangs erwähnten anderen ShopSysteme, die Sie für die Umsetzung Ihrer Projekte einsetzen können. Umbraco
dient in jedem Fall als solide technische
Grundlage und verbindendes Element aller drei Alternativen.
[jp]
[1] Umbraco, http://umbraco.com
[2] T homas Höhler, Freundliches Content Management System, Einführung in Umbraco 2.1,
dotnetpro 4/2007, Seite 66 ff.,
www.dotnetpro.de/A0704Umbraco
[3] Thomas Höhler, Gutes besser machen, Umbraco 2.1: Entwickeln mit und für Umbraco,
dotnetpro 5/2007, Seite 54 ff.,
www.dotnetpro.de/A0705Umbraco
[4] Thomas Höhler, U wie Umbraco, Umbraco
unter der Lupe, dotnetpro 7/2011, Seite 74 ff.,
www.dotnetpro.de/A1107Umbraco
[5] uCommerce, www.ucommerce.net
[6] Tea Commerce, www.teacommerce.net
[7] uWebshop, http://uwebshop.com
[8] uCommerce Razor Store,
www.dotnetpro.de/SL1309Umbraco1
[9] uCommerce Reference,
www.dotnetpro.de/SL1309Umbraco2
[10] Service Stack, www.servicestack.net
[11] uCommerce Support,
www.dotnetpro.de/SL1309Umbraco3
[12] U
mbraco User Group Deutschland,
www.uugd.de
[13] Application Pool Identities,
www.dotnetpro.de/SL1309Umbraco4
[14] U
mbraco, Releases,
www.dotnetpro.de/SL1309Umbraco5
[15] E -commerce Feauture Comparison for uCommerce, www.dotnetpro.de/SL1309Umbraco6
9.2013 www.dotnetpro.de
D
Listing 7 (Fortsetzung)
uCommerce-Kernfunktionen ersetzen.
category.CategoryId, product.ProductId);
}
return productUrl;
}
public virtual string GetUrl(ProductCatalog catalog, Category category) {
var catalogDescription = GetCatalogDescriptionWithFallback(catalog);
var currentCategory = category; var categoryDisplyNameConcatted = "";
while(currentCategory != null) {
categoryDisplyNameConcatted = UrlEncode(GetCategoryDescriptionWithFallback(
currentCategory).DisplayName) + (string.IsNullOrEmpty(
categoryDisplyNameConcatted) ? "" : "/") + categoryDisplyNameConcatted;
currentCategory = currentCategory.ParentCategory; }
return string.Format("/{0}/{1}/c-{2}/c-{3}",
UrlEncode(catalogDescription.DisplayName), categoryDisplyNameConcatted,
catalog.ProductCatalogId, category.CategoryId);
}
D
e v b ook
s
Core
Upgrades
für Ihr
Know-How
Expertenwissen für Web,
Mobile- und .NET-Entwickler
protected virtual string UrlEncode(string urlPart) {
urlPart = urlPart.ToLower();
foreach(var replacement in GetReplacements())
urlPart = urlPart.Replace(replacement.Key, replacement.Value);
return HttpUtility.UrlEncode(urlPart);
}
protected virtual IDictionary<string, string> GetReplacements() {
IDictionary<string, string> replacementParts = new Dictionary<string, string>();
replacementParts.Add("?", "oe"); replacementParts.Add("?", "ae");
replacementParts.Add("?", "aa"); replacementParts.Add("ö", "oe");
replacementParts.Add("ä", "ae"); replacementParts.Add("ü", "ue");
replacementParts.Add("ß", "ss"); replacementParts.Add(" ", "-");
replacementParts.Add("\"", "");
return replacementParts; }
private CategoryDescription GetCategoryDescriptionWithFallback(Category category) {
return category.CategoryDescriptions.Where(x => x.CultureCode ==
LocalizationContext.CurrentCultureCode && !string.IsNullOrEmpty(
x.DisplayName)).SingleOrDefault()
?? category.CategoryDescriptions.Where(x => x.CultureCode ==
LocalizationContext.DefaultCulture.ToString() && !string.IsNullOrEmpty(
x.DisplayName)).SingleOrDefault()
?? new CategoryDescription { DisplayName = category.Name };
}
je nur
€ 5,90
private ProductCatalogDescription GetCatalogDescriptionWithFallback(
ProductCatalog catalog) {
return catalog.ProductCatalogDescriptions.Where(x => x.CultureCode ==
LocalizationContext.CurrentCultureCode && !string.IsNullOrEmpty(x.DisplayName)).
SingleOrDefault()
?? catalog.ProductCatalogDescriptions.Where(x => x.CultureCode ==
LocalizationContext.DefaultCulture.ToString() && !string.IsNullOrEmpty(
x.DisplayName)).SingleOrDefault()
?? new ProductCatalogDescription { DisplayName = catalog.Name };
}
private ProductDescription GetProductDescription(Product product) {
return product.ProductDescriptions.Where(x => x.CultureCode ==
LocalizationContext.CurrentCultureCode && !string.IsNullOrEmpty(
x.DisplayName)).SingleOrDefault()
?? product.ProductDescriptions.Where(x => x.CultureCode == LocalizationContext.
DefaultCulture.ToString() && !string.IsNullOrEmpty(x.DisplayName)).
SingleOrDefault()
?? new ProductDescription { DisplayName = product.Name };
}
}
}
www.dotnetpro.de 9.2013
Zu beziehen über
www.developer-media.de/devbooks