Karakter sets in Flash

In een vorige post hadden we het over custom fonts en speciale karakters in Flash. Maar welke karakters embedden we nu best?

Bij het ontwikkelen van onze projecten houden we zoveel mogelijk rekening met de gebruiksvriendelijkheid. Het is dus niet meer dan logisch dat de eindgebruiker een website of applicatie voorgeschoteld krijgt in zijn of haar eigen taal, of tenminste een begrijpbare taal.
Voor sommige projecten vereist dit het ondersteunen van een ander alfabet, zoals bvb. het Cyrillisch of het Grieks.

cs4-charsets-01
Om karakters in een movie te embedden, hebben we in de Flash IDE (in dit geval de CS4 versie), een knop bij de eigenschappen van een tekstvak. Deze opent een venster waarbij we 1 of meerdere karaktersets kunnen aanduiden, of zelfs aparte karakters kunnen intikken die we mee willen embedden in onze file.
In dit venster kunnen we duidelijk zien welke karakters een bepaalde set omvat.

Als onze tekst echter in meerdere, vreemde talen moet getoond worden, is het soms onduidelijk welke karakter sets we juist moeten embedden. Om dit op te lossen kunnen we op deze website een XML bestand laten genereren, aan de hand van een tekstbestand waarin alle benodigde karakters staan.
Dit XML bestand kan gebruikt worden als UnicodeTable.xml, of kan de bestaande UnicodeTable.xml aanvullen (aan te raden). UnicodeTable.xml is een bestand dat door de Flash IDE gebruikt wordt, en dat alle karakter sets definieert die dan in het embed-venster komen (zie bovenstaande screenshot).

De door Flash gebruikte UnicodeTable.xml kun je op deze locatie terugvinden:

  • Windows Vista: <boot drive>\Users\<gebruiker>\AppData\Local\Adobe\Flash <versie>\<taal>\Configuration\FontEmbedding\
  • Windows XP: <boot drive>\Documents and Settings\<gebruiker>\Local Settings\Application Data\Adobe\Flash <versie>\<taal>\Configuration\FontEmbedding\
  • Mac: <boot drive>/<gebruiker>/Library/Application Support/Adobe/Flash <versie>/<taal>/Configuration/FontEmbedding/

Voor je UnicodeTable.xml wijzigt, neem je best een backup.

Zoals je kan zien staat elke karakterset in deze XML gedefinieerd, d.m.v. een node glyphRange. Deze node heeft 2 attributen, namelijk name en id. De id node wordt niet visueel getoond in de IDE, maar moet wel een unieke waarde bevatten. De waarde van de name node is wat we te zien krijgen in ons embed-venster, geef je eigen karakterset dus best een logische naam.

Welke tekens juist bij een bepaalde set horen kiezen we aan de hand van range nodes. Elke glyphRange node kan één of meerdere van die range nodes omvatten. Een range node definieert een tekenreeks, aan de hand van de <a href=”http://en.wikipedia.org/wiki/Unicode” title=”Unicode op Wikipedia”>unicode</a> waarde van het 1e en laatste karakter in die reeks. Als voorbeeld gebruiken we de voorgedefinieerde karakter set ‘Numerals [0..9]‘

<glyphRange name="Numerals [0..9] " id="3" >
    <range min="0x0030" max ="0x0039" />
    <range min="0x002E" max ="0x002E" />
</glyphRange>

Als we één van de vele unicode tabellen bekijken, kunnen we aflezen dat de unicode waarde 0x0030 het cijfer 0 voorstelt, 0x0039 staat voor het cijfer 9, en omdat we een range definieren zullen alle karakters tussen deze 2 ook binnen deze reeks vallen. Zoals we zien is het ook mogelijk om een enkel karakter als reeks te definiëren, dit gebeurt hier met de punt (.) die 0x002E als unicode waarde heeft.

Bij wijze van voorbeeld heb ik een karakterset gemaakt die ons Latijns alfabet met alle speciale tekens omvat, alsook het Cyrillisch en Grieks alfabet. De regels in commentaar zijn dubbele regels.

<glyphRange name="Latin Greek Cyrillic " id="27">
    <!-- Uppercase [A..Z] -->
    <range min="0x0020" max ="0x0020" />
    <range min="0x0041" max ="0x005A" />
    <!-- Lowercase [a..z] -->
    <!-- <range min="0x0020" max ="0x0020" /> -->
    <range min="0x0061" max ="0x007A" />
    <!-- Numerals [0..9] -->
    <range min="0x0030" max ="0x0039" />
    <range min="0x002E" max ="0x002E" />
    <!-- Punctuation [!@#%...] -->
    <range min="0x0020" max ="0x002F" />
    <range min="0x003A" max ="0x0040" />
    <range min="0x005B" max ="0x0060" />
    <range min="0x007B" max ="0x007E" />
    <range min="0x02c6" max ="0x02c6" />
    <range min="0x02dc" max ="0x02dc" />
    <range min="0x2013" max ="0x2014" />
    <range min="0x2018" max ="0x201a" />
    <range min="0x201c" max ="0x201e" />
    <range min="0x2020" max ="0x2022" />
    <range min="0x2026" max ="0x2026" />
    <range min="0x2030" max ="0x2030" />
    <range min="0x2039" max ="0x203a" />
    <range min="0x20ac" max ="0x20ac" />
    <range min="0x2122" max ="0x2122" />
    <!-- Basic Latin -->
    <range min="0x0020" max ="0x002F" />
    <range min="0x0030" max ="0x0039" />
    <range min="0x003A" max ="0x0040" />
    <range min="0x0041" max ="0x005A" />
    <range min="0x005B" max ="0x0060" />
    <range min="0x0061" max ="0x007A" />
    <range min="0x007B" max ="0x007E" />
    <!-- Latin I -->
    <!-- <range min="0x0020" max ="0x0020" /> -->
    <range min="0x00A1" max ="0x00FF" />
    <range min="0x2000" max ="0x206F" />
    <range min="0x20A0" max ="0x20CF" />
    <range min="0x2100" max ="0x2183" />
    <!-- Latin Extended A -->
    <range min="0x0100" max ="0x01FF" />
    <!-- <range min="0x2000" max ="0x206F" /> -->
    <range min="0x20A0" max ="0x20CF" />
    <range min="0x2100" max ="0x2183" />
    <!-- Greek -->
    <range min="0x0374" max ="0x03F2" />
    <range min="0x1F00" max ="0x1FFE" />
    <range min="0x2000" max ="0x206f" />
    <range min="0x20A0" max ="0x20CF" />
    <range min="0x2100" max ="0x2183" />
    <!-- Cyrillic -->
    <range min="0x0400" max ="0x04CE" />
    <!-- <range min="0x2000" max ="0x206f" /> -->
    <range min="0x20A0" max ="0x20CF" />
    <range min="0x2100" max ="0x2183" />
</glyphRange>

Monsterlijke case study

Eind januari lanceerden we de nieuwe webshop voor de babyspeciaalzaak Monsters With An Attitude. Deze webshop is, net zoals alle andere webshops & projecten die we afleveren, “tailor made” naar de noden en wensen van de klant.

Zowel de look&feel als de functionaliteit werden vastgelegd na enkele rondes feedback en aanpassingen samen met de klant. Een goed inlevingsvermogen in de business en werkwijze van de klant laat toe om het aantal iteraties tot een minimum te houden terwijl we naar een maximum aan klantentevredenheid streven.

Wie de vorige posts op deze blog gelezen heeft weet dat we bepaalde ontwikkelings- en functionaliteitsprincipes hanteren. We kunnen ze gerust “web industry best practices” noemen. Wat nu volgt is een mix van een case study en whitepaper voor een webshop.

Analyse: analyse this

PrototypingEerst en vooral hebben we de analysefase. De eerste sessies met de klant zijn eerder brainstormsessies. Alle mogelijke functionaliteiten, inclusief de toeters en bellen, die relevant kunnen zijn voor dit type project worden uitvoerig besproken. Op het einde van de analysefase leggen we samen met de klant vast wat er zal worden uitgewerkt, hoeveel dat zal kosten en hoe lang dat zal duren. Vaak wordt er op dit ogenblik beslist om bepaalde opties al dan niet meteen te nemen of te verschuiven naar een volgende fase. Dankzij deze gefaseerde aanpak kan een project geleidelijk groeien.

Heel belangrijk is dat, naast de functionaliteit, ook de business processes van de klant mee onder de loupe worden genomen.  In overleg worden scenario’s opgesteld voor de verschillende types van gebruikers (doelpubliek) die de website gaan gebruiken. Een gebruikerscenario identificeert handelingen met stappen en acties die moeten gebeuren om bepaalde doelen te kunnen bereiken.
Tijdens de analysefase staan deze gebruikerscenario’s centraal. Enerzijds bepalen ze de functionaliteit en logica. Anderzijds kunnen ze worden gebruikt ter controle of alle afgesproken functionaliteiten zijn verwerkt, maar ook of de website gebruiksvriendelijk en logisch is opgebouwd.
Uit de hele analyse vloeit uiteindelijke een prototype. Vincent had enkele posts terug over dit punt al een interessante uiteenzetting online geplaatst.

Design: het uithangbord

Tijdens de analysefase kan ook al aandacht worden besteed aan de look&feel van de webshop. Onderschat het belang hiervan niet. Een shop moet niet alleen functioneel goed werken, het moet er ook professioneel uitzien en vertrouwen wekken. Bovendien wordt de webshop het uithangbord voor de winkel. Een reden te meer om een knap en origineel design af te leveren. De klant weet op dit vlak meestal wel heel duidelijk waar hij naartoe wil. Onze designer Gert wist, zoals altijd, enkele verbluffende, toepasselijke designs uit zijn mouw te schudden.

mwaa-design-2mwaa-design-3
mwaa-design-4

Ontwikkeling: goede afspraken

Na deze eerste fases, die meestal wel parallel verlopen, start de technische ontwikkeling van de webshop. Tijdens de interne, technische kick-off meeting worden heel wat zaken besproken. We spreken goed af hoe de zaken technisch geïmplementeerd zullen worden. Doorgaans volgen nu 3 grote brokken in de ontwikkeling:

  • Back-end development: dit houdt in dat de functionaliteit en bedrijfsprocessen die eigen zijn aan de webshop in code worden vertaald. Daarbij moet er ook communicatie naar een databank of externe systemen zijn.
  • Front-end development: de design templates worden opgeknipt en vertaald naar XHTML, CSS en Javascript. Onderschat het belang van deze taak niet want hier leggen we de basis voor de Search Engine Optimalization (SEO).
  • De laatste brok is de integratie van de back-end-ontwikkeling met de front-end-ontwikkeling.

Fasering, iteratie en klantfeedback

De ontwikkeling wordt opgedeeld in fases op basis van functionaliteit. Iedere fase wordt ontwikkeld en getest voor men naar de volgende fase overstapt. Veelal steunt een latere fase op eerder ontwikkelde fases dus is het van extreem belang dat men iedere fase goed test. Unit testing is hierbij een grote hulp.

Een belangrijk voordeel van fasering is dat men op het einde van iedere fase feedback kan vragen van de klant. Zo vermijdt je eventuele “verrassingen” op het einde van de complete ontwikkelingscyclus. Betrek je klant zo snel mogelijk bij de ontwikkeling. Iedere fase waarbij men binnen een korte periode een bepaalde functionaliteit ontwikkelt, test en ter goedkeuring voorlegt aan de klant noemt men een sprint.

Best practices

Er zijn een aantal best-practices die we in de kijker willen zetten:

  • CSS sprites: we voegen een aantal afbeeldingen samen tot één afbeelding. (voorbeeld)
  • Custom fonts door gebruik te maken van Adobe Flash.
  • Search Engine Optimized (SEO) url’s & content.
  • Gebruik van Ajaxvoor het winkelmandje met als doel een betere user experience.
  • Meertaligheid. Je kunt argumenteren dat dit een functionaliteit is en geen “best practice”. Je kunt je echter geen Europese webshop inbeelden zonder. Meertaligheid is een “must”. Momenteel is de webshop van Monsters With An Attitude beschikbaar in één taal. De shop is zo ontwikkeld dat het toevoegen van extra talen slechts een administratieve kwestie is. Het leuke aan de meertaligheidsfunctie is dat wanneer men van taal verandert men op dezelfde pagina in de andere taal terecht komt. De meeste sites gaan bij het switchen van taal naar de hoofdpagina waardoor men de gebruiker verplicht om opnieuw een product of pagina te zoeken. Bij good practices hoeft dit niet. “It’s what separates the men from the boys”

Beheermodule: geen dubbel werk

Als laatste willen we je ook even laten meekijken achter de schermen want de publieke webshop is slecht de helft van de webapplicatie.

Deze beheersmodule laat toe de gegevens van klanten, producten, bestellingen en stock te beheren. Daarnaast is er ook nog volledige synchronisatie voorzien tussen de database voor de webapplicatie en de Filemaker-software in de winkel zodat de klant geen dubbel beheer moet doen voor de fysische winkel en de webshop.

De beheersmodule laat o.a. de volgende zaken toe:

  • Beheer klanten, producten, bestellingen en stockindicatie;
  • Printen BTW-listings, verkooplijsten;
  • Printen van facturen, inhoudsdocumenten, plakbonnen & transportdocumenten;
  • Printbare facturen en creditnota’s zijn PDF-documenten die dynamisch worden gegenereerd.
  • Verzenden van betalingsherinneringen.

Beheersmodule

Oh ja, de ontwikkeling gebeurde in ASP.NET. Maar dit is slechts een triviaal gegeven.

Verdere informatie over dit project kan je terugvinden op de projectpagina

AS3, fonts & speciale karakters

Flash & custom fonts, het blijft een speciale combinatie. Op zich is een custom font gebruiken niet moeilijk, maar wat als je extended ASCII karakters wil gebruiken?

Bij de uitwerking van een Flash ActionScript 3 project voor Toyota Europe werd ons de vraag gesteld om ondersteuning te bieden voor andere talen, met hun eigen tekenset, zoals o.a. een Cyrillische tekenset. Zo gingen wij op zoek naar de meest efficiënte manier om een custom font te embedden, met ondersteuning voor de meer exotische karakters.

Fonts in de library

Een eerste, logische stap zou zijn om het gewenste font in onze Flash library op te slaan, en daaruit te exporteren.

AS3 Font Embedding: Font exporteren Met het font in onze library kunnen we nu in de code duiken. We creëren een TextFormat object, waarin we de .name property invullen met de naam van ons font (de naam zoals je die ziet in elke tekst editor). Een goede manier om de naam van het font op te halen is via de Font.enumerateFonts() methode.

var fonts:Array = Font.enumerateFonts(false);
fonts.sortOn("fontName", Array.CASEINSENSITIVE);
var fmt:TextFormat = new TextFormat();
fmt.font = fonts[0].fontName; // Naam van het font als String
fmt.color = 0x4E3E16;
fmt.size = 20;
fmt.bold = false;

Om tekst te tonen hebben we natuurlijk een TextField nodig. Een overzicht van de belangrijkste properties.

var normalField:TextField = new TextField();
normalField.defaultTextFormat = fmt;
normalField.embedFonts = true;
normalField.text = "Custom font\n.text property\n&éèà# Ъ ē Ψ";

Als resultaat krijgen we volgende Flash movie. Het linkse veld is normale tekst, het rechtse html tekst.

Zoals je ziet worden de ‘gewone’ ASCII tekens en accenten getoond, de Cyrillische en Griekse tekens zijn echter in geen van beide gevallen zichtbaar.

[kml_flashembed fversion="9.0.0" movie="/wp-content/uploads/2009/01/as3-fontembedding-swf-01.swf" targetclass="flashmovie" publishmethod="dynamic" width="471" height="140"]Get Adobe Flash player

[/kml_flashembed]

Een font embedden in een tekstveld

Als we even terug de Flash IDE induiken, kunnen we een nieuw Flash AS3 bestand aanmaken. In dit bestand creëren we via de IDE een dynamisch tekstveld. In dit veld embedden we ons font via de ‘embed’-knop.
AS3 Font Embedding: Font embedden
In ons geval embedden we de volgende karakter sets:

  • Uppercase [A..Z]
  • Lowercase [a..z]
  • Numerals [0..9]
  • Punctuation [!@#%...]
  • Basic Latin
  • Latin I
  • Latin Extended A
  • Greek
  • Cyrillic

Als we tekst in dit tekstveld stoppen, zien we dat onze tekst getoond wordt, het font zit dus mee in onze movie.

Het zou natuurlijk nogal omslachtig zijn om per blok tekst een veld manueel op de stage te plaatsen. Een oplossing hiervoor is dat we ons tekstveld in een MovieClip stoppen. We hebben nu dus een MovieClip op onze stage, met daarin een TextField waarin ons font zit.

Deze MovieClip kunnen we nu van onze stage verwijderen, daar die toch nog in de library zit. Als we nu de MovieClip een klasse naam geven (bij de linkage eigenschappen) kunnen we deze exporteren.
AS3 Font Embedding: MovieClip exporteren
Merk op dat ik de Sprite klasse als base class definieer, dit omdat we in deze ‘MovieClip’ geen gebruik maken van de timeline mogelijkheden.

Nu kunnen we ons font binnen onze applicatie gebruiken, mits we deze MovieClip 1x instantieren (zodat het font mee gecompileerd wordt).

new EmbeddedFont(); // Eénmalig instantieren
var field:TextField = new TextField();
field.embedFonts = true;
field.htmlText = "<font face=\"My Font\" size=\"20\" color=\"#4e3e16\">Custom font<br>.htmlText property<br>&éèà# Ъ ē Ψ</font>";
addChild(field);

Dit is echter niet praktisch, als we bijvoorbeeld meerdere SWF bestanden gebruiken zouden we telkens deze MovieClip moeten kopiëren naar elk FLA bestand. En wat dan als we helemaal geen FLA bestanden gebruiken om te compileren (bvb. via het Flex SDK)?

Een tekstveld in een SWC

Hoe kunnen we er nu voor zorgen dat we gemakkelijk aan onze geëxporteerde Sprite kunnen, zodat deze kan geinstantieerd worden? Via een centraal SWC bestand natuurlijk.

Bij de publicatie instellingen van onze FLA kunnen we er voor kiezen om deze Flash movie ook als SWC bestand exporteren.

AS3 Font Embedding: SWC exporteren

Dit SWC bestand bevat dus onze Sprite met daarin het tekstveld en kunnen we voortaan gebruiken doorheen onze applicaties.

In ons voorbeeld hebben we de Sprite geëxporteerd als klasse EmbeddedFont. Nu kunnen we in een willekeurige Flash movie deze SWC integreren (via FDT kun je deze bijvoorbeeld aan je ’source folder’ toevoegen). De geëxporteerde klasse kan nu gebruikt worden in ons nieuw project.

Stel dat we in onze Flash applicatie een tekstveld willen, met daarin terug enkele speciale karakters, het enige wat we nu moeten doen is eenmalig een instantie aanmaken van onze geëxporteerde Sprite, zodat de compiler weet dat deze mee in de SWF moet.
Dan kunnen we doorheen alle klasses van de applicatie tekstvelden aanmaken, en ons font gebruiken.

new EmbeddedFont(); // Eénmalig instantieren
var field:TextField = new TextField();
field.embedFonts = true;
field.htmlText = "<font face=\"My Font\" size=\"20\" color=\"#4e3e16\">Custom font<br>.htmlText property<br>&éèà# Ъ ē Ψ</font>";
addChild(field);

Omdat we deze keer gebruik maken van de .htmlText property hoeven we hier geen TextFormat object aan te maken. Wil je gewoon de .text property gebruiken, dan werk je natuurlijk best wel met een TextFormat object.
Let erop dat we terug de naam van het font letterlijk moeten gebruiken, zoals je deze bijvoorbeeld in de Flash IDE ziet.

Links hebben we terug een tekstveld opgevuld via de .text property, links via de .htmlText property.

[kml_flashembed fversion="9.0.0" movie="/wp-content/uploads/2009/01/as3-fontembedding-swf-02.swf" targetclass="flashmovie" publishmethod="dynamic" width="471" height="140"]Get Adobe Flash player

[/kml_flashembed]

Meerdere SWF-files

Alles in orde zou je denken, maar wat dan als een applicatie meerdere SWF bestanden bevat?

Links hebben we hier een tekstveld binnen de SWF waarin we onze geëxporteerde EmbeddedFont klasse instantieren, het rechtse tekstveld zit in een SWF die bij runtime ingeladen wordt.

[kml_flashembed fversion="9.0.0" movie="/wp-content/uploads/2009/01/as3-fontembedding-swf-03.swf" targetclass="flashmovie" publishmethod="dynamic" width="471" height="140"]Get Adobe Flash player

[/kml_flashembed]

Ons font wordt dus niet getoond doorheen alle SWF bestanden.

Doordat we met een SWC bestand werken waarin ons font vervat zit, kunnen we dit gemakkelijk oplossen. Een gemakkelijke manier is om gewoon binnen elk SWF bestand, onze geëxporteerde klasse één maal te importeren.

In onderstaand voorbeeld hebben we terug een SWF bestand, met links een tekstveld, en rechts een ingeladen SWF bestand met ook een tekstveld. In beide SWF bestanden wordt onze klasse 1x geinstantieerd, en nu worden alle tekens getoond.

[kml_flashembed fversion="9.0.0" movie="/wp-content/uploads/2009/01/as3-fontembedding-swf-04.swf" targetclass="flashmovie" publishmethod="dynamic" width="471" height="140"]Get Adobe Flash player

[/kml_flashembed]

Conclusie

Er zijn natuurlijk meerdere manieren om fonts te gebruiken binnen Flash, en elke manier heeft zijn voor- en nadelen. Uit ervaring merken wij echter dat deze manier weinig omslachtig en zeer flexibel is. Ook vooral de ondersteuning van allerhande tekensets naar wens zorgt ervoor dat deze implementatie gemakkelijk werkt.

What makes a frame work?

Alle recente projecten van Marlon maken gebruik van open source frameworks. Zowel voor .NET als PHP zijn er heel wat alternatieven beschikbaar. Voor PHP zijn er de frameworks als Symfony, Zend Framework en eZComponents maar ook minder algemeen gekende parels als Spoon Library.

Een framework kan je zien als een bouwdoos waarmee je je applicatie gaat bouwen. Een extra laag tussen de programmeertaal (vb. PHP) en je eigen geïmplementeerde functionaliteit. Een goed framework voorziet in een basis voor applicaties en zorgt ervoor dat je als ontwikkelaar niet steeds opnieuw het spreekwoordelijke wiel moet gaan uitvinden.

Ieder framework of library heeft ongetwijfeld zijn voor- en nadelen, net zoals iedere programmeertaal dat heeft. Google is uw vriend als je voor het slapengaan voor jezelf wilt uitmaken welk framework nu eigenlijk wel de beste is.

Naast snelheid en betrouwbaarheid stelden wij één uitermate belangrijk aandachtspunt voorop bij de keuze van een framework: uitbreidbaarheid. Hoe uitgebreid een specifiek framework ook mag zijn, wanneer je websites op maat ontwikkelt zal er altijd wel één of andere component zijn dat je niet zomaar uit de bibliotheek kunt pikken. Daarom is het van het allergrootste belang dat een framework uitbreidbaar is. Een ontwikkelaar moet eenvoudig (en dus ook snel) bestaande functionaliteit kunnen uitbreiden om zo aan projectspecifieke eisen te kunnen voldoen. (denk “extends”, “implements”, …)

Voor enkele recente projecten maakten we gebruik van het Zend Framework. Naast de verschillende mogelijkheden om het bestaande framework uit te breiden kunnen we hier ook rekenen op een erg uitgebreide documentatie en een levendige community.

Iedere webontwikkelaar met een beetje eergevoel wil alles altijd zelf programmeren. Onze ervaring leert echter dat het voor beide partijen de beste keuze is om zoveel mogelijk tijd en energie te steken in projectspecifieke functionaliteit en gebruik te maken van de basisfunctionaliteit van een framework.

Dit betekent echter niet dat je het framework van je keuze als een zwarte doos moet gaan beschouwen. Zoals bij alle mogelijke problemen die je tijdens het verloop van een project tegen het lijf loopt geldt ook hier dat je moet blijven nadenken. Neem nooit iets “zomaar” aan. Ga na en controleer hoe het komt dat iets op een bepaalde manier werkt. Ga je daarmee als team akkoord, doe het dan op die manier. Doe je dat niet, argumenteer waarom en los het probleem op je eigen manier op. Met een beetje geluk kan je de reeds bestaande functionaliteit alsnog uitbreiden en met minimale inspanning en tijd het gewenste resultaat bereiken!

3 systemen die je webapplicatie een pak flexibeler maken

De website van de Vlaamse Diabetes Vereniging, bijna een jaar geleden gelanceerd, is een website én extranet met een zeer uitgebreide functionaliteit. De website zelf bestaat uit content (nogal wiedes), een shop, en websites voor de lokale afdelingen. Daarnaast is alles beheerbaar door de vereniging zelf, en kunnen de vereniging en bepaalde bestuursleden van plaatselijke afdelingen aan ledenbeheer doen.

Een beslissing die we zeer vroeg bij de ontwikkeling hebben genomen, is om vooraf op 3 manieren flexibiliteit in te bouwen: 3 plug-in-systemen, die binnen de kern van de applicatie draaien, en die er voor zorgen dat we snel extra systemen, tools of aanpassingen kunnen inpassen, zonder aan de kern te raken:

  • jobs: een job is een stuk functionaliteit dat op een vast tijdstip of om de zoveel tijd wordt uitgevoerd;
  • rendering helpers: plug-ins die de HTML van alle pagina’s of een bepaald type pagina herwerken, vlak voordat die naar de browser wordt gestuurd;
  • listeners: plug-ins die luisteren naar en reageren op welbepaalde acties binnen de website en het extranet, zoals het aanmaken van een pagina, het plaatsen van een bestelling, een lid dat zich aanmeldt.

Jobs

Binnen de webapplicatie hebben we een ‘job’-systeem gebouwd: een systeem dat ons toelaat extra functionaliteit toe te voegen, die op een welbepaald tijdstip (bijvoorbeeld elke dag om 8u30) of om de zoveel tijd (bijvoorbeeld om de 3 minuten) wordt uitgevoerd.

De kerntaak van het systeem is: de plug-ins die jobs bevatten inladen en deze uitvoeren, volgens de bepaalde instellingen (aantal keren per dag of om de zoveel minuten, in een apart proces of niet).

Eenmaal dat de motor van dit systeem er was, konden we achteraf eenvoudigweg jobs toevoegen. Op dit ogenblik zijn er 6 jobs actief op de website:

E-mail queu job

Her en der in de website en het extranet zijn er pagina’s of formulieren die e-mails uitsturen: het formulier om lid te worden, de tell-a-friend, de webshop, enz.

Om de applicatie onafhankelijk te maken van het proces dat de e-mails effectief uitstuurt (de SMTP-server) maken we gebruik van een e-mail queu. Als bijvoorbeeld een lid een bestelling plaatst, wordt de e-mail niet onmiddelijk uitgestuurd door het systeem dat de bestelling afhandelt, maar wordt dat bericht in een wachtrij geplaatst.

Laten we de e-mail namelijk rechtstreeks vertrekken en is op dat moment de SMTP-server onbeschikbaar, zou in het geval van de webshop de bestelling niet kunnen worden geplaatst.

De e-mail queu job gaat met een vast bepaald interval in de wachtrij kijken of er e-mails moeten worden verstuurd  en zorgt voor de volledige afhandeling: SMTP-server contacteren, e-mail uitsturen, eventueel optreden als de SMTP-server niet beschikbaar is (we bepalen het aantal mogelijke retries per e-mail), de applicatie verwittigen als de e-mail effectief is uitgestuurd, eventueel extra functionaliteit uitvoeren wanneer de e-mail wel of niet verstuurd is, enz.

E-mail Nieuwsbrief-job

Deze job kijkt of er campagnes voor de e-mailnieuwsbrief beschikbaar zijn. Indien een campagne mag worden verstuurd, zorgt deze job voor het aanvullen van de e-mail queu, die op zich dan weer het uitsturen verzorgt, met de nodige afhandelingen: namelijk het systeem van de e-mailnieuwsbrieven feedback geven over elke verstuurd e-mail: verstuurd, niet verstuurd, fout in het adres van de ontvanger, …

Blog feed job

Voor de jongeren binnen de vereniging werd een blog opgezet, in WordPress. De titels van de blogartikels worden op de homepagina van de eigenlijke website getoond. Hier bestaat weer het gevaar dat de blog niet beschikbaar is en dus de nodige links naar artikels niet op de website kunnen geladen worden. Vandaar het loskoppelen en het gebruik van een job die om de zoveel minuten de RSS feed van de blog inleest en de nodige aanpassingen doet op de website.

Search engine indexer en spell check indexer job

De search engine van de website werkt op basis van Lucene.NET, een .NET port van de Java text search engine.

De website op zich bestaat uit heel wat dynamische pagina’s (deze worden regelmatig aangepast), maar ook statische bestanden (.ascx files, of anders gesteld: .NET controls) waarin teksten staan die nooit of amper moeten aangepast worden. Bij het aanmaken, aanpassen of verwijderen van alle dynamische pagina’s, wordt de search engine verwittigd dat de index moet worden aangepast (zie het Listeners systeem). Wijzigingen in de statische bestanden kan de search engine niet detecteren.

Daarom hebben we een eigen crawler gebouwd die vertrekt vanop de homepagina en zo van link tot link, door de website loopt en de nodige pagina’s indexeert en toevoegt of aanpast in de index van de search engine.

Daarnaast draait er een tweede job die nodig is om de search engine nog beter te maken: een spell checker job. Dit laat ons toe suggesties te geven bij fout getypte zoektermen. De index van de search engine, wat eigenlijk een lijst is van alle woorden die op de website staan, wordt daarom door deze job om de zoveel uur overlopen en alle unieke woorden in de index worden opgelijst en bewaard. Zo komen we tot de ‘Bedoelde je xxx’-suggesties.

Externe applicatie check job

De website en het extranet communiceren continu met een extern systeem dat door de VZW wordt gebruikt om haar ledenbestand te beheren. Om problemen met de communicatie met dit systeem snel te onderkennen, controlleren we door middel van een job de beschikbaarheid van die externe applicatie.

Rendering helpers

De VZW heeft meer dan 20 plaatselijke afdelingen die elk beschikken over een klein onderdeel binnen de website dat ze zelf kunnen beheren. Het centrale secretariaat is niet in staat om voortdurend te controlleren of de pagina’s correct zijn opgemaakt, en of ingebrachte inhoud consistent is met de layout van de rest van de website. Daarom hebben we de Rendering helpers gebouwd.

De Rendering Helpers is een systeem dat, ook weer door middel van plug-ins, de output die naar de browser wordt gestuurd, aanpast indien nodig. Dus ook dit is een systeem dat vandaag een aantal modules bevat en dat makkelijk kan worden uitgebreid.

De Rendering Helpers die vandaag actief zijn op de site zorgen er bijvoorbeeld voor dat:

Te grote beelden

Beelden die worden ingevoegd door iemand van een plaatselijke afdeling, en die te groot zijn om binnen de layout te passen, worden aangepast en on the fly verschaald. De mensen die die pagina’s beheren hebben meestal niet de nodige tools om foto’s zelf te bewerken. Daarom stoppen ze vaak digitale foto’s met hun originele breedte en hoogte in een pagina. Het effect is dat het beeld, kleiner gemaakt om in de layout te passen, niet mooi is, en het beeld veel te zwaar is (in kilobytes) om in een website goed te werken. De Rendering Helpers zorgen dat het beeld dynamisch kleiner wordt gemaakt, zodat de kwaliteit hoger ligt en de persoon die de pagina bezoekt geen te groot bestand moet inladen.

Tabellen voor layout omzetten naar <div/> elementen

De website bevat geen tabellen om de layout mee te bepalen (met layout bedoelen we hier welke elementen waar op de pagina staan). Maar voor de mensen die de inhoud ingeven, is het gebruik van tabellen makkelijk om verder te gaan dan de standaard vloeiende bladspiegel: tekst die van boven naar onder loopt. Daarom hebben we Table Layout Helper gebouwd. Deze zet, bij het opbouwen van de pagina, en net voor deze naar de browser wordt gestuurd, de layout die in een tabel zit om naar HTML-code die wel de webstandaarden respecteert.

HTML-optimalisatie

Naast bovenstaande interventies worden o.a. lege paragrafen verwijderd, <font/> tags vervangen, enz.

Dergelijke optimalisatie is echter nooit afgerond. Steeds ondervinden we opnieuw dat pagina’s gevuld zijn met incorrecte HTML-elementen of -structuren. We bouwen dus steeds extra Rendering Helpers die dergelijke fouten gracieus oplossen.

Listeners

Listeners zijn een concept dat bij de meeste programeertalen al lang bestaat. Voor deze applicatie hebben we binnen de basisfunctionaliteit een gelijkaardig principe ingevoerd: nieuwe systemen (plug-ins) die worden toegevoegd, luisteren naar bepaalde acties binnen de applicatie. Wordt die actie uitgevoerd, dan worden alle luisteraars verwittigd en krijgen ze de nodige gegevens.

Zo vuren we verschillende plug-ins aan als een lid zich aanmeldt, als een pagina wordt aangemaakt of aangepast of verwijderd, als een bestelling wordt geplaatst, enz.

Een dergelijk systeem zorgt er opnieuw voor dat er  aan de basiswerking niets moet worden gewijzigd. De applicatie houdt zich bezig met de essentie. Andere systemen schakelen zich in en luisteren dus naar wat binnen de applicatie gebeurt.

Content Indexing Listeners

Een van de listeners die actief is binnen de applicatie ‘luistert’ naar nieuw bewaarde inhoud, inhoud die gewijzigd wordt en inhoud die verwijderd wordt. Zo wordt de Index die de search engine gebruikt steeds up to date gehouden.

Conclusie

Kortom, 3 eenvoudige systemen met één gezamelijk doel: flexibiliteit. De website en webapplicatie, zoals die vandaag is gebouwd, is klaar voor uitbreidingen van morgen.