Friday, January 27, 2012

EPiServer custom property settings and blowing up the database

So property settings was added to PTB 2.0, and i decided to play around a bit with it and not just change the tinymce settings due to that being boring as ****. When doing so i stumbled across a.. can we say interesting "feature" of EPiServer.

What you normally do is create a settings class that inherits EPiServer.Core.PropertySettings.PropertySettingsBase (or implement the interface) and add the attributes etc. In this class you have your property settings which are serialized and stored in the dds by episerver. Now this is all fine and dandy for primitives and lists of primitives.

Now what I was doing is making a type restricted page picker, so I needed to store a list of types (ptb page types to be more specific). So ofcourse I started by adding a property:
public List<type> AvailablePageTypes { get; set; }

This will make your entire site crash and the only way to fix it is to enter the database and set the "settingsID" value to NULL for the property you decorated with the settings attribute, and also remove a bunch of lines from the tblBigTable... tables.

Apparently EPiServer has no qualms whatsoever to add invalid data/configuration to the DDS, but when you try to do anything with it the entire site is held hostage by DDS exceptions, fun times.

So when making property settings: Make sure any property you place in the settings class has a public parameterless constructor, or you kill your site. You cant even decorate properties with anything to make them not get serialized by EPiServer, any such things are ignored apparently.

I'm not even gonna start on what you have to do if you want to change the type or remove a property on a setting class (hint: Site doesn't work anymore if you do).

Thursday, January 26, 2012

Enable logging for EPiServer Indexing Service

One of our customer's IT departments came to us with  the Windows application event log bloated with errors from EPiServiceIndexingService. The only information available was this:

"EPiServer Indexing Service has problems indexing some of the locations, please consult the log file for details."

But, from what I've understood the log isn't default activated, so go fish. After a quick support-session with EPiServer I was routed to this blog post:
A post that, amongst other things, explains how to activate the log4net-module for the Indexing service with the following steps:

  1. Put the log4net.dll into the EPiServerIndexingService-folder, under Shared services in your EPiServer 
  2. Create the most leight weight config-file possible, see below, and name it: log4net.config. And put it in the same folder. Parameters and configuration to the log-service can be changed at a later state.
  3. Restart the service


Example of log4net.config-file:

<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<appender name="ErrorAppender" type="log4net.Appender.FileAppender" >
<file value="C:\temp\LogServerError.log" />
<appendToFile value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d [%t] %l - %m%n" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="ErrorAppender" />
</root>
</log4net>



Wednesday, January 25, 2012

Rangy guidar dig i mobildjungeln

Idag lanserar Nansen sajten Rangy.se.

Rangy är både en jämförelsesajt och en återförsäljarportal för mobilabonnemang och mobiltelefoner.

Genom att du själv berättar för Rangy sina samtalsvanor kan sajten räkna fram exakt vilket abonnemang och tilläggspaket som skulle passa just dig. Men det slutar inte där. Rangy är även en återförsäljare av mobiltelefoner och mobilabonnemang.

Rangy är den första tjänsten i Sverige där kunder kan hitta och köpa den kombination av mobiltelefon och abonnemang som är billigast baserat på kundens egna samtalsvanor.

Den största utmaningen i sajten är helt klart beräkningsmotorn för abonnemangsberäkningar. Kombinationer av mobiltelefonerbjudanden, sms paket, surfpaket, rabattsatser mellan operatörer m.m gör beräkningen omfattande och komplex.

Sajten är byggd på Episerver CMS 6 R2 och E-handelsdelen är baserad på Epierver Commerce R2. För att hålla struktur, flexibilitet och överblick är hela beräkningsmotorn baserad på Microsoft Workflow Foundation.

För att få en så bra användarupplevelse som möjligt är hela sajten bygg med Ajax.

Team:

Klas Sabelström, Projektledare
Andreas Oldeskog, Systemutveckling, gränssnitt
Tomas Unestad, Systemarkitekt
Jonas Näslund, E-commerce
Viktor Gars, Systemutveckling
Sussi Zäll, Interaktionsdesign
Martin Lenngren, Gränssnitt

Söderhavet stod för digital identitet.

Friday, January 20, 2012

Visual studio compare and merge goodness



When doing a diff more complex than a == b using the standard diff/merge tool that comes with visual studio is an experience that no developer should need to go  through. I don't think that diff tool has changed since the black Visual SourceSafe days.

But there is a way to make your life so much nicer. I use WinMerge as the diff tool when in visual studio.
Follow this steps if you want to get rid of (some atleast) of the pains of merging code:

1. Download and install WinMerge
2. In Visual Studion go to Tools -> Options -> Source control ->Visual Studio Team Foundation Server and click on the Configure User Tools button.
3. If you don't have the two commands for compare and merge, then add one for each and set the file path to your WinMerge executable, mine is C:\Program Files (x86)\WinMerge\WinMergeU.exe. The extension should be .*
3a. The arguments for compare is /e /x /s /wl /dl %6 /dr %7 %1 %2
3b. and for merge /e /s /x /ub /dl %6 /dr %7 %1 %2 %4

Done!
Now next time you do a merge diff or compare Visual studio will use WinMerge instead.

Wednesday, January 18, 2012

ICT Expo - starkaste talare lineup'en någonsin?


Ett tåg och en spårvagn (!) senare anländer jag till ett regnigt Sverigemässan (blygsamt namn) där ICT Expo Internet flyttats till sal 2. 


En inte helt mäktig arena med de där 90-talstypiska, likformade, vita aluminiumkonstruktionerna med samma svarta företagslappar uppställda i rutnät. Utställarna är destå roligare. Med Azure-moln, säkerhetstanke i projekt och så vidare så känns det som vår del med webb/content/sociala media står närmare IT-secure och drift/lagring än någonsin. 


Och vilka talare, det var därför Nansen kastade sig på X2000 denna arla onsdagsmorgon:
  • Apple (talare från svenska Apple)
  • Microsoft (talare från svenska delen)
  • Facebook  (talare från svenska delen)
  • Google  (talare från svenska delen)
  • Spotify
  • iZettle
Dessutom VD:ar från stora börsföretag och diverse andra intressanta aktörer. SIME och Webbdagarna, släng er i väggen, typ. Varumärkesmässigt starkaste lineup'en någonsin i Sverige?

Pratade med IDG under lunchen, inte ens de kan förmå svenska Facebook skicka representanter till deras övertecknade och på bara timmar fullbokade Webbdagarna Facebook-seminarium i december. Trots fin adress i centrala Stockholm - men efter några på banan kirrar ICT alltså listan talare här ovanför. Till denna sorliga och dystra 90-talsmiljö!

För de stackars talarna tvingas köra on-floor presentationer, ni vet samma typ av föredrag som brukar hållas på TV-shopgolvet. Och det i en lokal som både brusar, sorlar och störs av mässans ombyggnation. Det känns helt ovärdigt. Nåväl, några framföranden där rösten kom fram till oss åhörare var:

 
MediaAnalys som presenterade en kul terminologi kring e-handelsavslut. Efter A/B test förespråkade de "trovärdighetsmarkörer" för bättre konvertering. Med termen menades dels video, (där klipp visat sig ha en enorm dragkraft och vunnen stannad tid) men även de klassiska e-handelscetifikaten, betrodda varumärkesbilderna/citaten etc. Dock blev det pannkaka då allt skulle maxas i ett checkoutflöde. Då sjönk de tidigare redan dåliga resultaten med ytterligare -1,29%. Men med ett A/B som dock visade ett helt rensat och vitt checkoutflöde ökade samma siffra istället med 23% bättre avslut!


Åt lunch med Bo Dahlbom, professor inom IT på Göteborgs universitet. Han var mycket intresserad av Nansens egna Rangy.se, ett ämne som togs upp då vi pratade om hans kommande föreläsningsämne. Det var "nästa stora sak inom it". Föredraget hölls senare på onsdagen. 

Svaret på Bos ämnesfråga? Det baserades på att vår BNP har gått upp med 500 000% under 800 år. Sverige har gått från det jordbrukssamhälle vi en gång var till en helt global aktör. Och vi måste bli ännu mer globala - det kommer ske genom ytterligare konsumtion. Dessutom rörde framtiden så klart allt fler eldrivna prylar och de ständigt annalkandes kineserna. De kommer snart vara överallt på jorden, inte bara i Afrika och på Mallorca. Dessutom kommer japanska robotar dyka upp lite här och var, M2M ger oss "smarta städer" där alla prylar internetkommunicerar med varandra och vi kommer få vänja oss att kontinenteras många skilda kulturer ständigt kommer mötas och mixas. Google Translate speak funktionaliteten (som finns för Android) kommer ge fungerande "Liftarens guide översättningar i realtid" via våra smartphones inom kort. Och just det, svaret på frågan då: Det var mångbottnat och rymde bl a att med allt listat ovan krävs, enligt Bo, bra överblickssysten. Detta då vi kommer konsumera så enormt mycket information och data. Bo slog även ett slag för videons fortsatta dragkraft (unga föredrar den) tillsammans med logiskt hantering av ET&IT (miljövänlig infrastruktur för ökad nätkonsumtion).


Ex-politikern Gabriel Sundqvist på 18 man starka sociala mediafokuserade Pronto berättade innan Bo om åtta drivkrafter för word-of-mouth (vilket de vurmade för). Se dessa i bilden ovan. 

Han tog även upp kul information om hur "kraften hos påverkare inom sociala medier" identifieras. Se bilden här intill.

CMS då, det fanns de som snackade EPiServer - men endast en, runt 60+ årig åhörare stannade vid deras dragning. Så intresset måste räknas som underkänt.

Och med tanke på platsen där vi var - så fanns så klart röda byxor på plats!

Tuesday, January 17, 2012

EPiServer Composer and Commerce, getting global functions to work

In a default installation of EPiServer with Composer and Commerce the global functions and clipboard of Composer does not work. If you debug Composer you will find the following stack trace in the log file,


2012-01-17 11:57:09,893 ERROR [38] Dropit.Extension.Common.Logger.Error - Dropit.Extension.UI.Edit.CreateContentFunction: Value cannot be null.
Parameter name: page
System.ArgumentNullException: Value cannot be null.
Parameter name: page
at EPiServer.Business.Commerce.BreadcrumbsFactory.IsProductListingPage(PageData page)
at EPiServer.Business.Commerce.HttpModules.CatalogNodeModule.RemoveCache(PageData pageData)
at EPiServer.PageEventHandler.Invoke(Object sender, PageEventArgs e)
at EPiServer.Core.PageStoreBase.RaisePageEvent(Object key, PageEventArgs eventArgs)
at EPiServer.DataFactory.Copy(PageReference pageLink, PageReference destinationLink, AccessLevel requiredSourceAccess, AccessLevel requiredDestinationAccess, Boolean publishOnDestination, Boolean allowThreading)
at Dropit.Extension.Controllers.PageDataManager.CopyContentFunction(ContentFunctionData sourceContentFunctionData, PageReference targetPageLink, PageReference targetShadowPageRef)
at Dropit.Extension.Core.ContentFunctionData.Copy(PageReference targetPageLink)
at Dropit.Extension.UI.Edit.CreateContentFunction.Page_Load(Object sender, EventArgs e)

I don’t know why but when Composer copies the page and the create page event is called the page link in the event argument is null and when the argument null the function IsProductListingPage will throw and exception and the creation of the global function will not happen. It’s the same error when you try to use a content function that is placed on the clipboard.


The idea of how to solve this problem was to just inherit CatalogNodeModule and add a null check before the call but as RemoveCache is a private function that did not work. The next approach was to just reimplement the module but then we noticed that the call within the RemoveCache function is internal so that didn’t work either.

After some more reflection of the code I found that the only calls to the BreadcrumbsFactory and its caching (that would be affected by the RemoveCache call) were the Breadcrumb control in the Commerce demo templates, and as we don’t use them in the current project we decided to just remove the RemoveCache call all together and just create a new module with the code that was left. This is the final result and the module we run with,

public class CatalogNodeModule : IHttpModule
{
public void Init(HttpApplication context)
{
InitializationModule.FirstBeginRequest += new EventHandler(this.InitializationModule_FirstBeginRequest);
context.BeginRequest += new EventHandler(this.BeginRequest);
}

private void BeginRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
HttpContext context = application.Context;
if (context.Request.Url.AbsolutePath.ToUpperInvariant().Contains("IMAGE.ASHX") && context.Request.QueryString.AllKeys.Contains("epslanguage"))
{
string[] values = context.Request.QueryString.GetValues("epslanguage");
CatalogContext.MetaDataContext.UseCurrentUICulture = false;
CatalogContext.MetaDataContext.Language = values[0];
}
}

private void InitializationModule_FirstBeginRequest(object sender, EventArgs e)
{
RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
RouteTable.Routes.IgnoreRoute("{resource}.aspx/{*pathInfo}");
RouteTable.Routes.MapRoute("ProductInCartOrWishList", "ProductInCartOrWishList/Edit/{id}", new { controller = "ProductInCartOrWishList", action = "Edit", id = "" });
RouteTable.Routes.MapRoute("CustomerProperties", "CustomerProperties/Edit/{id}", new { controller = "CustomerProperties", action = "Edit", id = "" });
RouteTable.Routes.MapRoute("OrdersFrequency", "OrdersFrequency/Edit/{id}", new { controller = "OrdersFrequency", action = "Edit", id = "" });
RouteTable.Routes.MapRoute("RecentSpent", "RecentSpent/Edit/{id}", new { controller = "RecentSpent", action = "Edit", id = "" });
RouteTable.Routes.MapRoute("RecentOrders", "RecentOrders/Edit/{id}", new { controller = "RecentOrders", action = "Edit", id = "" });
}

public void Dispose()
{
}
}

After removing the commerce http module and adding our own both the global functions and clipboard started working in Commerce.

Monday, January 16, 2012

Responsive design

Just nu sitter jag och anpassar en ny kunds webbplats så att det ska se lika fantastisk ut på både Smart Phone och iPad som den gör på bred skärm. Responsive design alltså, liknande det vi utvecklade för Radiotjänst i slutet av förra året.

Självklart använder vi oss utav Media Queries. Så här kan det se ut:

@media only screen
and (min-device-width : 768px)
and (max-device-width : 1024px) {
/* CSS här */
}

Jag känner mig mer och mer frustrerad när jag för femtioelfte gången förminskar webbläsarfönstret för att simulera ungefärlig iPad storlek eftersom designen vägrar omforma sig trots mina Media queries.

Jag vet ju att jag har gjort allting rätt, skolboksexempel, inga slarvfel någonstans…

Till slut kommer Lybeck förbi som en räddande ängel och påpekar att jag naturligtvis inte kan skriva min-device-width om jag inte testar på en faktisk device. Ska man testa provisoriskt som jag gör genom att dra i webbläsarfönstret måste man så klart vaska ordet device och endast skriva min-width och max-width för att det ska fungera.

Men ååååååååååh! Så självklart, så irriterande. Men tack Lybeck!

Tuesday, January 10, 2012

Responsive Design seminar at Devsum, Cornerstone Developer Summit 2012

Nansens own AC / AO, Andreas times two, will take their Web Breakfast session about Responsive web Design to this years Cornerstore Developer Summit. Where they're to be lined up next to prominent speakers such as Scott Allen, Tess Ferrandez, Fil Maj, Björn Eriksen, Alan Smith, Steve Sanderson, Eric Lawrence and Dag König.



Nansen is also a partner of Devsum 12, Cornerstone Developer Summit 2012. Don't miss it!

Monday, January 9, 2012

AJAX requests and authentication

Been scratching my head today over this, I have a site with windowsauthentication and a few ajax wcf service methods.

One method is supposed to be used by a specific user depending on what page its located on, so naturally I just added the following piece of code to my service method:

if(HttpContext.Current.User.Identity.Name == whatever)
{
// Do my stuff
}

This worked fine in IE, and sometimes in chrome, but firefox notoriously refused to send the authentication headers causing Identity.IsAuthenticated to be false all the time, making the user be anonymous for the request.

Turns out all that is needed is to is to set the response status to 401, and the browser will retry the request sending the credentials from the session, this is all obvious, what was difficult to figure out is how you achieve that.

Well you do it like so (note the difference from a regular browser page request where you would just set httpcontext.response.statuscode):

if(!HttpContext.Current.User.Identity.IsAuthenticated)
{
WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.Unauthorized;
return;
}

Now I can have both unsecured and secured ajax methods in the same service.

Also, im still not sure if this is the correct way of doing things, so if anyone know a better way to achieve the same thing (like some attribute I can decorate my methods with I don’t know about), please let me know!