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&category=$3&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 variablen 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 uCommerce-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 UmbracoBackend 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