Office 365: Shared calendar (no licence)

Vandaag een leuke vraag gekregen waar ik nog niet direct het antwoord op had. Dit ging om het opzetten van een gedeelde agenda zonder echte ‘gebruiker’ dus bijvoorbeeld een algemene agenda voor een kantoor of iets. Dacht eerst dat ik het moest zoeken in de richting van distribution- of securitygroups maar dit was te diep gedacht. Al snel realiseerde ik me dat dit gewoon eenvoudig te realiseren was met een gedeelde mailbox.

Als eerste maak je via de powershell een gedeelde mailbox aan (in dit geval noemen we hem kantooragenda@bedrijf.nl):


New-Mailbox -Name "kantooragenda@bedrijf.nl" -Alias "kantooragenda" -Shared -primarysmtpaddress "kantooragenda@bedrijf.nl"

Daarna geef je de gewenste gebruikers maximale rechten op deze mailbox. Stel dat de gebruikers met de aliasen ‘henk’ en ‘annie’ toegang moeten krijgen tot deze agenda dan configureer je dit met:


Add-MailboxPermission "kantooragenda" -User "henk" -AccessRights FullAccess
Add-MailboxPermission "kantooragenda" -User "annie" -AccessRights FullAccess

Netbeans 7.1.1 & Subversion 1.19.* import werkt niet

Netbeans en subversion gaan soms niet echt vloeiend samen en zo nu en dan kost het je best wat tijd om dingen weer ‘recht’ te trekken maar vandaag maakten ze het wel heel bont. Het begon met een project waar de SVN dusdanig verziekt was dat ik het verstandiger vond om deze even opnieuw te builden. Simpel; oude repo weggegooid, .svn folders opgeruimd en Netbeans opnieuw gestart om te wennen aan de nieuwe situatie. Maar om onduidelijke redenen wilde hij gewoon niet meer opnieuw het project importeren in SVN. Wat ik ook deed, na Team > Subversion > Import into repository bleef alles stil, geen errors, geen meldingen, helemaal niets… Halve internet doorzocht, logs doorgespit maar gewoon null.

Had al bijna besloten dat dit project op zich geen SVN nodig had maar wilde gewoon niet toegeven aan een overwinning van Netbeans & Subversion dus toen toch nogmaals alles rustig bekeken. Enige wat ik kon bedenken was dat hij toch ergens in een soort cache herinneringen had die hem dwars zaten. Wat was geen oplossing;

  1. NBproject data van project verwijderen
  2. Netbeans 100x opnieuw opstarten
  3. PC opnieuw opstarten
  4. Netbeans updates installeren (stonden er nog paar klaar)
  5. Log files lezen

Uiteindelijk besloten om alle SVN cache van Netbeans te vernietigen (windows 7 te vinden in C:\Users\[username]\.netbeans\7.1\var\cache\svncache) en dit loste het probleem op wonderbaarlijke wijze op. Moet me er voor nu bij neerleggen dat niet snappen maar gewoon erop inhakken door cache te legen de enige oplossing was die werkte. Had nog wel wat mensen met soortgelijke problemen gezien maar toch nét niet hetzelfde.

Zend Framework Documentation: Constants for General Date Functions (reformatted)

Oké, was er klaar mee. De documentatie van Zend Framework is prima maar de pagina met Zend_Date constants is gewoon onmogelijk. Misschien dat ze hem in de toekomst wat mooier maken maar voor nu hier de ‘quick & dirty’ hackoplossing: zend_date_reformatted.html

Niets spannends. Gewoon wat breder, hier en daar wat paddings en wat zebrastreepjes voor de leesbaarheid. Enjoy!

Office 365: Aliases, Outlook Web App & Outlook 2010

Veel mensen maken gebruik van e-mail aliassen welke geen aparte mailboxen dienen te krijgen maar wel mails moeten verzenden. Een voorbeeld hiervan is bijvoorbeeld een kleinere organisatie waarbij de eigenaar vanaf zowel info@ als vanaf zijn persoonlijke adres wil mailen maar wel alles centraal in één mailbox wil ontvangen. Dit is in Office 365 te configureren middels distributiegroepen en wat configuratie via de powershell. Voor dit voorbeeld gaan we even uit van de situatie van een info@ mailbox en een henk@ voor de eigenaar.

Office 365 panel

Allereerst richten we een normale mailbox in voor henk@domeinnaam.nl. Ik ga er even vanuit dat dit geen probleem vormt verder. Daarna maken we via het panel een distributiegroep aan voor de gewenste alias. In dit geval dus info@domeinnaam.nl. Als identiteit geven we deze groep de naam ‘info_domeinnaam_nl’ zodat we hem makkelijk kunnen herkennen in de Powershell (mocht dat nog nodig zijn in de toekomst). We maken gebruiker ‘henk’ eigenaar en lid van deze groep zodat alle mails verzonden aan dit adres naar zijn mailbox toe worden gezonden. Wel een belangrijk punt van aandacht: Vergeet niet aan te vinken dat deze distributiegroep vanaf ‘onbekende’ adressen gemaild mag worden, standaard staat hij namelijk geconfigureerd als een box voor intern gebruik waarbij alleen bekende mailadressen welkom zijn.

Powershell

Via de powershell gaan we nu gebruiker ‘henk’ rechten geven om te verzenden namens de distributiegroep. Dit doe je met het volgende commando:


Add-RecipientPermission "info_domeinnaam_nl" -Trustee "henk" -AccessRights SendAs


Outlook Web App & Outlook

De outlook Web App is nu in principe klaar voor de nieuwe situatie. Bij het opstellen van een nieuw bericht kun je bij het kopje ‘opties’ een vinkje zetten voor het veld ‘van weergeven’ zodat je kunt kiezen namens wie je wilt mailen. Als je wilt wisselen tussen de verschillende afzenders (in dit geval dus henk@domeinnaam.nl en info@domeinnaam.nl) dan klik je op het ‘van’ veld en krijg je een lijstje met verschillende afzenderopties. Dubbelklik en bevestigen en je verzend nu namens een andere naam.

Outlook op de PC/Mac is wat lastiger op dit vlak. Normaliter is het niet mogelijk mail te versturen via een mailbox die niet is aangemaakt als mailaccount. Omdat het een distributiegroep betreft is het erg lelijk hem altijd te tonen. Voor nu zijn er echter geen echte opties voor, alleen ‘workarrounds’. Enkele mogelijkheden zijn;

  1. De distributiegroep ook als POP3 account aanmaken naast je normale exchange identiteit in Outlook zodat je nu ook kunt verzenden namens deze mailbox
  2. Het niet erg vinden dat er ‘verzending namens’ komt te staan in je mails (dit krijg je als je in Outlook het ‘van’ veld activeert en een afzender gebruikt die niet als account is aangemaakt)
  3. Toch ervoor kiezen losse gebruikers ervan te maken

Firefox freezes

De afgelopen weken had ik een diepgaand conflict met mijn Firefox. Om onduidelijke redenen bleef hij soms tientallen keren per dag om onheldere redenen ‘hangen’. Bijvoorbeeld als je klikte ergens op een pagina of ‘view source’ deed. Mijn eerste veronderstelling was dat er misschien extensies of plugins waren die de boel verstoorden. Als fervent Firefox liefhebber en als webdeveloper heb je vaak toch al snel aardig wat onmisbare tooltjes (Firebug, Web developer, Delicious bookmarks en ShowIP om maar iets te noemen). Mijn eerste poging op weg naar herstel van een gezonde werkomgeving was het uitschakelen van alles wat ik maar kon missen en meer. Ik was overtuigd dat dit de oplossing was maar dit bleek niet het geval. Zelfs zonder enige plugin of extensie deed hij vervelend.

En toen?

Aangezien er nooit tijd is voor dit soort ‘problemen’, ik in enkele weken mijn pc wil herinstalleren en ik Opera ook het beste gun dacht ik ‘ik kan best een paar weken met Opera gaan werken en wie weet bevalt het wel’. Opera geupdate, alternatieven voor mijn favoriete Firefox plugins gezocht en de meest belangrijke tabjes toegevoegd als ‘app’ (sinds ik dit ontdekt heb in Firefox is dit echt een primaire levensbehoefte geworden). Voor de meeste zaken waren prima alternatieven. Zo heeft Opera een geweldige, ingebakken, developer omgeving (Dragonfly, niet gezien? Zeker eens testen). Met mijn visie dat iets wat standaard in software zit ingebouwd altijd beter is dan iets wat je los installeert leek me dit het testen waard (en beviel me prima trouwens). Maar hoe sexy Opera ook was, hoe prettig Dragonfly ook was, ik miste mijn vertrouwde Firefox. Keer op keer opende ik toch automatisch weer Firefox om iets te testen en voor mijn geliefde Delicious extensie kon ik geen waardig alternatief vinden. Daar ik een trouw Delicious gebruik ben (ca 350 links) miste ik deze heel erg. Alles wat ik tegenkom tijdens onderzoek en belangrijk vindt wil ik in mijn Delicious stoppen. Kort samengevat; Opera is fantastisch maar ik mis Firefox…..

Op zoek naar een oplossing

Een beetje googlen bracht mij bij een weblog wat me hoop gaf. Ik was sowieso niet de enige en er was misschien een oplossing. Het zou namelijk kunnen komen door een verstoring van de database van Firefox die soms in een soort loop zou schieten. Vol goede moed de instructies gevolgd en vol verwachting mijn browser herstart maar helaas, de problemen waren niet weg.

Firefox 8 beta

Bij gebrek aan verdere opties, en bij het vinden van veel meer mensen met soortgelijke ‘vage’ klachten, toch maar even getest of het draaien van Firefox 8 beta dit op zou lossen. Dit bleek in mijn geval de oplossing. Ben er niet heel gelukkig mee dat ik op mijn primaire werkomgeving met een beta versie werk maar het werkt 100x beter dan Firefox 7 deed dus ben er blij mee. Mocht jij soortgelijke problemen hebben dan zou ik adviseren om eerst de poging te doen met het weblog. Beter een herstelde versie 7 dan een 8 beta lijkt me zo. Als het bij jou ook niet werkt dan is Firefox 8 beta dus een mogelijk goede oplossing.

CSS3 box shadow en Internet explorer <9

Nu CSS3 steeds meer onderdeel wordt van het dagelijks leven blijf je altijd weer met Internet explorer zitten. Alle meestgebruikte versies ondersteunen een hoop leuke CSS3 zaken niet. Vandaag was mijn irritatiepuntje de box shadow. Ik wilde deze met zo min mogelijk moeite werkend krijgen in IE zonder allerlei ingrepen. Nu zijn op zich de meeste zaken wel mogelijk zoals positieve, of negatieve offsets door middel van Microsoft filters. Alleen ik wilde een drop shadow langs alle kanten. Dit leek me zo voor de hand liggend maar het heeft me toch even gekost voor ik enige zinnige implementaties en voorbeelden hiervan had gevonden.

Ik heb voor het gemak even 3 heldere voorbeelden van drop shadows uiteen gezet met CSS code erbij (-webkit, -moz, CSS3 tag zelf en IE alternatief).

Resultaat in Internet explorer 8

Het resultaat in Firefox

Klik hier om de demo te bekijken

Een stap verder

Er zijn nog leukere mogelijkheden maar die vergen iets meer verdieping. Zo kun je bijvoorbeeld met een extra div voor de shadow om je primaire div heen ook nog gekkere dingen doen. Op css-tricks.com staat dit heel helder uitgelegd.

Office 365: Shared mailbox license required

Als Office 365 fans hebben wij de laatste tijd al een hoop gespeeld met de configuratie. Een probleem wat terug blijft komen (en ook erg veel terug te vinden is op het internet) is de vreemde werking van shared mailboxes in combinatie met licenties. De documentatie van Microsoft geeft op meerdere plaatsen zeer helder aan dat een shared mailbox geen licentie nodig heeft. Alleen gebruikers die deze mailbox willen openen moeten een licentie hebben (en mogen geen kiosk-account hebben).

Wat gaat er mis?

Stel je beschikt over een standaard P1 account en je maakt via de powershell een shared mailbox aan. Het lijkt een redelijke tijd goed te gaan maar ergens gaat het bij een hoop gebruikers fout als ze via de OWA proberen de mailbox te benaderen. Je krijgt dan een error ivm. te weinig toegekende licenties. Het rare is sowieso dat hij wel werkt via bijvoorbeeld Outlook 2010 en mails komen er ook gewoon in.

Na gisteren een ruim uur met een zeer vriendelijke medewerker van Microsoft dit probleem te onderzoeken heb ik zojuist als test een gedeelde mailbox opnieuw aangemaakt. Eerder had ik namelijk een beetje soep gemaakt van de mailboxen door ze ook als een user via het Office 365 admin panel aan te maken.

Mogelijke oorzaken

Omdat het via Outlook wel werkt maar via de OWA niet krijg je al vraagtekens. Wie heeft er gelijk en via welke route gaat er gewoon iets mis in het bepalen van licenties. Mijn eerste idee zou zijn dat de OWA een onjuiste manier gebruikt om de licenties te controleren. Waarom anders zouden er zoveel mensen zijn met dit probleem en er geen concrete oplossingen zijn nog. Het lijkt meer een bug dan een echt licentieprobleem te zijn. Daar de Microsoft medewerkers al enkele keren tussen neus en lippen hebben aangegeven dat zij denken dat Office 365 te snel gelanceerd is en nog best wat kinderziektes bevat ga je nog meer in deze richting neigen. Maar tot Microsoft een oplossing heeft wil je natuurlijk nadenken hoe dit probleem mogelijk kan worden omzeild. Enkele theorieen over waarom deze licentieproblemen getriggerd worden zijn:

  • P1 licenties hebben geen security groups (zoals in de E licenties wel het geval is)
    Als hier de oorzaak zit kan het nog wel eens lastig worden dit ooit werkend te krijgen

  • Standaard krijgt een shared mailbox normale quotas mee terwijl hij licentievrij maar 5 GB mag zijn
    Zou eenvoudig oplosbaar moeten zijn maar denk niet dat dit het probleem geeft.

  • Standaard wordt er met het maken van een mailbox ook een useraccount gemaakt in het Office365 panel
    Wat lastiger verhaal maar zou wel logischer zijn inzake het probleem via OWA. OWA loopt over de “office365 lagen” terwijl je lokale Outlook direct op Exchange rechten niveau werkt

Oplossingen?

Nog niet echt al denk ik ook dat dit meer een applicatie probleem is dan een configuratieprobleem aan de gebruikerskant. Ik zal de komende tijd in iedere geval alles kritisch in de gaten houden en nog wat tests uitvoeren en mocht het probleem zich niet meer voordoen bij specifieke configuraties dan zal ik hier zeker een nieuwe blogpost aan wijden.

 

WordPress style save confirmation

WordPress heeft bij het bewerken of aanmaken van een nieuwe blogpost een melding die je om bevestiging vraagt als je probeert de pagina te verlaten zonder iets op te slaan. Nooit echt bewust over nagedacht maar leek me een leuke toevoeging qua user experience in een nieuw project. Even een beetje nagedacht hoe ik dit in mijn Jquery wereld kon “opbergen” zonder dat het in de weg zou zitten en kwam met de volgende oplossing;

// Set the default state to false
jQuery.data(document.body, 'confirmationPrompt', false);

// On a keypress in any form field set confirmation to true
$('input,textarea').keypress(function() {
   jQuery.data(document.body, 'confirmationPrompt', true);
});

// On change of a select or any other form element (like radio buttons etc)
// Set confirmation to true
$('input,select').change(function() {
    jQuery.data(document.body, 'confirmationPrompt', true);
});

// On any form submit set confirmation to false
$('form').submit(function() {
    jQuery.data(document.body, 'confirmationPrompt', false);
});

// Check if confirmation is needed before unloading
window.onbeforeunload = function(){
    if(jQuery.data(document.body, 'confirmationPrompt')){
        return 'Are you sure?';
    }
};

Dit dekt op zich aardig de lading zo. De reden dat ik jquery.data() gebruik en niet gewoon een losse variabel is dat ik op deze manier ook vanuit andere functies de confirmationPrompt var op false kan zetten. Bijvoorbeeld als je sommige forms middels een AJAX call opslaat waardoor er geen formele form submit voorbij komt.

Zend Form security (basics)

Ook bij een “gesloten” applicatie kun je nooit teveel doen aan de beveiliging van je formulieren. Voor een “standaard” omgeving wil je in iedere geval basisbescherming opzetten tegen;

  • Formulier spam
  • Fake/overmatige form posts
  • Ongewenste gegevens

Met Zend Framework heb je op zich al best wat leuke mogelijkheden tot je beschikking voor het valideren en filteren van inputvelden en een stukje bescherming tegen CSRF.

Formulier spam

Voor een eenvoudige site zal een spammer niet veel moeite doen. Enige wat je wilt is een drempel die het voor “standaardscripts” onmogelijk maakt je systeem te vervuilen en tegelijkertijd niet teveel werk is om te implementeren. Een mogelijkheid is het toevoegen van een veld wat je met CSS (of JavaScript) verbergt voor normale gebruikers en wat “false” is als deze een waarde bevat. Een normale gebruiker die geen CSS en JavaScript heeft aan staan is zeldzaam maar mocht die toch voorkomen dan kun je die eventueel met een label nog informeren over het feit dat dit een veld is speciaal om spambots uit te sluiten.

Fake/overmatige form posts

Zend_Form_Element_Hash biedt hiervoor op zich in basis een goede bescherming tegen. Bij het aanmaken van je form neem je een veld op met het type “hash”. Dit veld kun je van parameters voorzien zoals een timeout etc. Bij het aanmaken van het form maakt Zend een session aan met deze hash erin. Bij de post van het form wordt deze gevalideerd tegen de hash. Als hij verlopen is geeft de form isValid false terug. Hier zit wel gelijk een “let op!” momentje in; als je in je structuur ergens de ongelukkige setup hebt waarbij het form meerdere keren gevalideerd wordt zal deze bij de 2e ronde false geven op deze hash.

Hieronder een simpele setup voor dit element in je form:

$this->addElement('hash', 'csrf_hash', array(
    'salt' => 'justSomeRandomSalt',
    'timeout' => 600,
));

ps. de documentatie op de Zend Framework website is niet erg diepgaand op dit vlak. Als je in de broncode van de Form/Element/Hash.php kijkt zie je dat er meer parameters zijn die je mee kunt geven.

  • setSalt($salt)
    String met salt. Geef je niets mee dan gebruikt hij standaard de string “salt”
  • setSession($session)
    Zend_Session_Namespace $session als je zelf een session wilt meegeven.
  • setTimeout($ttl)
    Int met timeout welke standaard op 300 staat.

Ongewenste gegevens

Hoe ver je hiermee gaat hangt een beetje van de aard van de data af, de plek waar het form zit (publiek of besloten) en of de data bijvoorbeeld naar een database gaat of alleen gemaild wordt naar een persoon. Tevens laat ik hierin de beveiliging van de opslag, verzending of wat dan ook van de data buiten wegen. Het gaat me nu even puur om de formulier-afhandeling.

Met Zend_Filter en Zend_Validator kun je krachtige combinaties maken welke de data strippen op alleen het nodige na en het resultaat hiervan valideren. Stel je hebt een select veld:


$this->addElement('select', 'is_lovingchocolate', array(
    'label' => 'Do you love chocolate?',
    'multiOptions' => array(
        1 => 'Yes',
        0 => 'No',
    ),
    'filters' => array('int'),
));

Wat je hiermee doet is in iedere geval gelijk de formulierwaarde die terugkomt met de POST typecasten naar INT.  Met de verschillende Zend_Filter classes kun je op die manier al heel veel ellende tegenhouden. Als je ingewikkeldere data-patronen hebt kun je ervoor kiezen met eenvoudige Zend_Filters alle ongewenste data eruit te halen om daarna met Zend_Validate_Regex de data tegen een hele strict patroon te matchen.

Een voorbeeld hiervan:


$this->addElement('text', 'zipcode', array(
'label' => 'Zipcode (Dutch notation)',
'required' => true,
'maxlength' => 7,
'validators' => array(
    array('NotEmpty', true),
    array(
        'regex', false, array('/^[0-9]{4} [A-Z]{2}$/',
        'messages' => array('regexNotMatch' => 'Incorrect zipcode notation. Should look like 2222 AA.'))
    ),
),
'filters' => array('StripTags', 'stringToUpper'),
));

Wat we doen met de filters is alles eruit halen wat we nu even niet willen en gelijk de letter naar hoofdletters converteren voor een uniforme dataopslag later. Na deze stap valideren we het resultaat met een reguliere expressie voor de postcode. Even los van het feit of deze expressie aansluit bij jouw idee van een goede postcode maar denk dat het idee wel helder is zo.

Office 365: Retention policy wijzigen

Retention policy is een hele coole feature in Exchange waarmee je e-mails en complete mappen een retentie periode kan geven. Stel je hebt een project wat nu loopt tot eind deze maand dan zou je er bijvoorbeeld voor kunnen kiezen om de documentatie van dit project over 2 jaar na nu automatisch te laten verwijderen.

Soorten retention tags

Standaard krijgt iedere mail de “Default Policy Tag”, daarnaast zijn er “Retention Policy Tags” die standaard op mappen zoals je inbox, prullenbank etc van toepassing zijn, als laatste zijn er de “Personal Tags” die een gebruiker zelf kan maken. Via de Powershell kun je van diverse tags de eigenschappen instellen. De onderstaande diagram van de Microsoft site geeft wel een aardig beeld van de verschillende tags en hun rol in het gehele plaatje:

Bron: http://technet.microsoft.com/en-us/library/dd297955.aspx

De tags tweaken

Standaard staat bijvoorbeeld de tag voor de prullenbak op iets van 30 dagen. Superleuk voor je hotmail account maar niet voor een productieomgeving in een bedrijf (of in ons bedrijf in iedere geval). Wil je dit wijzigen dan moet je even inloggen op de powershell. Met het volgende commando haal je de huidige tag informatie op:

get-RetentionPolicyTag "deleted items" | fl name,type,AgelimitForRetention,RetentionAction

Als je deze nu bijvoorbeeld wilt wijzigen in 5 jaar dan kun je dit doen met het volgende commando:

set-RetentionPolicyTag "deleted items" -AgeLimitForRetention 1825

De wijzigingen zullen in de loop van de komende uren bij alle gebruikers zichtbaar worden via de Outlook Web Access en de eventuele lokale Office installatie. Dit komt omdat het hele retention policy tag deel in handen is van de “Managed Folder Assistant”. Dit is een mailbox-hulpje die om de zoveel tijd de mailboxen naloopt en alle retention policies naloopt, bijstelt etc.

Op vakantie en nu?

Stel je hebt een policy die redelijk strict is (bijvoorbeeld mails van organisatie X of box Y max 14 dagen bewaren) en je bent een maand op vakantie dan wil je natuurlijk dat deze fanatieke mailboxhulp even rustig blijft en niet mails gaat lopen opruimen voor je. Hierin is gelukkig voorzien al is dit op dit moment niet mogelijk om door de gebruiker zelf in te stellen. Je zult dus als admin zelf via de commandline dit moeten plaatsen.

Set-Mailbox <name> -RetentionHoldEnabled $true

En om hem weer uit te schakelen:

Set-Mailbox <name> -RetentionHoldEnabled $false

Om het nog informatiever voor de gebruiker (en jezelf te houden) kun je ook nog een comment toevoegen aan de retention hold:

Set-Mailbox <name> -RetentionHoldEnabled $true -RetentionComment <comment>

Jquery fadeIn loop (supersimpel)

Wilde net even twee blokken met tekst in elkaar laten overlopen. Leek me overkill hiervoor een cycle plugin te gebruiken dus ging even kijken wat voor leuks ik nog niet had toegepast/ontdekt in Jquery. Nu blijkt dat je met delay() functie in combi met fadeIn/out erg leuke dingen kunt doen.

HTML:


<div id="blockHolder">
    <div id="block1">Tekst 1</div>
    <div id="block2">Tekst 2</div>
</div>

CSS:


#blockHolder {
    width: 100px;
    height: 200px;
    border: 1px solid red;
    position: relative;
}

#block1,
#block2 {
    background-color: white;
    position: absolute;
    left: 0;
    top: 0;
}

Jquery code:


function fadeLoop(){
    $('#block1').fadeIn().delay(3000).fadeOut('slow', fadeLoop).delay(3000);
}

$(document).ready(function() {
    fadeLoop();
});

Op zich redelijk basic maar werkt wel leuk zo. Wat de fadeLoop() nu doet is eigenlijk #block1 transparant maken, 3 seconde wachten, weer zichtbaar maken, 3 seconde wachten en weer zichzelf aanroepen voor een herhaling. Dit werkt prima met blokken die over elkaar gepositioneerd staan via CSS en waarbij de voorste transparant/niet-transparant wordt waardoor de achterste zichtbaar wordt.

BMI calculator met Jquery

Voor een project wat nog in ontwikkeling is moest ik een BMI calculator maken. Ik had er ooit wel eens één gemaakt maar die werkte via een POST en ‘berekende’ alles met PHP. Het leek me leuk dit keer Jquery te gebruiken.

De oude BMI calculator

Zoals je ziet op zich prima maar een beetje statisch. Tevens niet echt superduidelijk. Er zit een 1 px wit lijntje wat je huidige BMI aanduidt maar je moet wel goed opletten om die te zien.

De nieuwe BMI calculator

Zoals je ziet wel wat overeenkomsten (zelfde opdrachtgever dus moest zelfs alles een beetje gelijk houden) maar toch net even wat frisser. Een duidelijkere pointer voor je BMI en in plaats van invulvelden en een submitbutton gewoon twee Jquery UI sliders.

De code

Wat heb je in iedere geval nodig?

  1. Jquery
  2. Jquery UI
  3. Een BMI afbeelding voor de achtergrond
  4. Eventueel een leuke afbeelding voor de pointer

Voor het gemak kun je het hele gebruikte pakketje voor deze implementatie hier downloaden.

De te includen bestanden in de header

  1. reset.css
  2. screen.css
  3. jquery-ui-1.8.16.custom.css
  4. jquery.1.6.1.min.js
  5. jquery-ui-1.8.16.custom.min.js
  6. bmi.js

De HTML

Dit is de kale HTML voor de slider met tabel etc.


<div id="lengthSlider"></div>
<label for="length">Uw lengte is <input type="text" id="length" /> centimeter</label>

<div id="weightSlider"></div>
<label for="weight">Uw gewicht is <input type="text" id="weight" /> kilogram</label>

<div id="bmiScale">
    Uw Body Mass Index is
    <div id="bmiScalePointer"></div>
</div>

<div id="bmiInformation">
  <h4>Wat is een gezonde BMI?</h4>
  <table border="0" cellpadding="0" cellspacing="0" width="500">
    <tbody>
      <tr>
        <td width="10"><img src="</assets/images/bmi-yellow.gif" alt="ondergewicht" height="10" width="10"></td>
        <td width="130">Tussen 16 - 20</td>
        <td width="240">Uw BMI is iets te laag</td>
      </tr>
      <tr>
        <td><img src="/assets/images/bmi-green.gif" alt="gezond gewicht" height="10" width="10"></td>
        <td>Tussen 20 - 25</td>
        <td>gezond gewicht</td>
      </tr>
      <tr>
        <td><img src="/assets/images/bmi-yellow.gif'" alt="normaal - licht overgewicht" height="10" width="10"></td>
        <td>Tussen 25 – 27</td>
        <td>normaal - licht overgewicht</td>
      </tr>
      <tr>
        <td><img src="/assets/images/bmi-orange.gif" alt="normaal - overgewicht" height="10" width="10"></td>
        <td>Tussen 27 -29</td>
        <td>normaal - overgewicht</td>
      </tr>
      <tr>
        <td><img src="/assets/images/bmi-red-orange.gif'" alt="overgewicht" height="10" width="10"></td>
        <td>Boven de 30</td>
        <td>overgewicht</td>
      </tr>
      <tr>
        <td><img src="/assets/images/bmi-red.gif" alt="ernstig overgewicht" height="10" width="10"></td>
        <td>Boven de 40</td>
        <td>ernstig overgewicht</td>
      </tr>
    </tbody>
</table>
</div>

De Jquery code

De volgende code berekend de BMI, verplaatst de pointer etc.
Zie ook de opmerkingen ertussen.

// Function to calculate the BMI using lenght in centimeters and weight in kilograms
function calculateBMI(lenght, weight)
{
  // Calculate the BMI and round at 1 decimal
  var result = Math.round(weight / (lenght/100 * lenght/100)*10)/10;

  // Determine the left position of the bmiScalePointer (and subtract 50% of it's width)
  var leftPos = (result*10)-22;

  // If the leftpos is > 450 it would run out of the scale so stop it here
  if(leftPos > 450) {
  leftPos = 450;
  result = Math.round(result);
}

// Update the bmiScalePointer value
$('#bmiScalePointer').html("BMI<br/><br/>" + result);

// Move the bmiScalePointer to it's new position
$('#bmiScalePointer').css('left', leftPos);
}

$(document).ready(function() {
  // Just plain Jquery UI slider functionalitiy
  $("#lengthSlider").slider({
    value: 175,
    min: 130,
    max: 220,
    step: 1,
    slide: function( event, ui ) {
      // Initiate the calculation function on sliding
      calculateBMI(ui.value, $('#weight').val());
      $("#length").val(ui.value);
    }
  });

  $("#length").val($("#lengthSlider").slider("value"));

  $("#weightSlider").slider({
    value: 75,
    min: 24,
    max: 250,
    step: 1,
    slide: function( event, ui ) {
      // Initiate the calculation function on sliding
      calculateBMI($('#length').val(), ui.value);
      $("#weight").val(ui.value);
    }
  });
  $("#weight").val($("#weightSlider").slider("value"));

  // Do a initial calculation
  calculateBMI($('#length').val(), $('#weight').val());
});

Nogmaals voor het gemakt: Het hele gebruikte pakketje voor deze implementatie kun je hier downloaden.

Google maps API geocoding met Zend_Http_Client

Voor een leukheidje in een CMS wilde ik de geo-coordinaten van een adres ophalen. Ik had al wel iets liggen maar dat was wat oudere code en wilde de output als JSON hebben. Hier de meest recht-toe-recht-aan oplossing om verder te ontwikkelen naar wens:

// Address to lookup. Don't overlook the + instead of spaces
$address = 'Stationsplein+45,3013+AK,ROTTERDAM';

$client = new Zend_Http_Client();
$client->setUri('https://maps.googleapis.com/maps/geo');
$client->setConfig(array('maxredirects' => 0, 'timeout' => 30));
$client->setParameterGet('output', 'json');
$client->setParameterGet('q', $address);
$response = $client->request('GET');

$body = $response->getBody();
$response = Zend_Json::decode($body, Zend_Json::TYPE_OBJECT);

if($response->Status->code == 200) {
    // Jeej, success
    Zend_Debug::dump($response);
} else {
    // Trouble!
    Zend_Debug::dump($response);
}

Zoals ik al zei, geen übercode die klaar is voor gebruik maar een leuke basis voor implementatie in een Google maps service of class.

Jquery alle hyperlinks naar afbeeldingen koppelen aan Fancybox

Een tijdje terug heb ik voor een klant als service een microstukje javascript gemaakt met Jquery om alle hyperlinks in de content te hooken aan Fancybox (of welk ander script dan ook voor de weergave van foto’s). Wat hij doet is alle a elementen in de dom (of een selectie daarvan) opzoeken, kijken of ze een href hebben en of die linkt naar een afbeelding en zo ja, Fancybox eraan hangen. Normaal doe je dit gewoon door een class of iets aan de afbeelding te hangen die je gebruikt als selector maar ik wilde dat alle via het CMS ingevoerde links naar afbeeldingen ook meegepakt werden. Om de klant niet te hoeven uitleggen wat hij moest doen om afbeeldingen te linken kwam ik hierop uit.


$(document).ready(function() {
    $("a").filter(function(){
        if($(this).attr('href').match(/\.(jpe?g|png|gif)/i)) {
            $(this).fancybox();
        }
    });
});

ps. Als je sommige afbeeldingen eruit wilt houden kun je altijd nog iets toevoegen als een not()conditie (waarbij je in dit geval dus aan de hyperlink de class .noFancy meegeeft:

$(document).ready(function() {
    $("a:not(.noFancy)").filter(function(){
        if($(this).attr('href').match(/\.(jpe?g|png|gif)/i)) {
            $(this).fancybox();
        }
    });
});

PHP xmlWriter resultaten naar log schrijven

Een bestaande applicatie die een XML export doet met XMLWriter moest voorzien worden van een mogelijkheid om de exacte XML output weg te schrijven in een log. Het script maakte in eerste instantie gebruik van directe output naar de browser:

$xml->openURI('php://output');

Om het totale resultaat van de XML output in een logfile te schrijven was dit niet ideaal. Het zou op zich wel kunnen maar omzetten naar gebruik van het geheugen voor opslag van de XML was iets handiger (zeker omdat het om relatief weinig data per run ging).

Hieronder de code voor een basis XML die zowel naar de browser gaat als naar een logfile:

// Set correct header
header('Content-Type: application/xml, charset=utf-8');

// Initiate XMLWriter
$xml = new XMLWriter();

// Create new xmlwriter using memory for string output
$xml->openMemory();

// Create document tag
$xml->startDocument('1.0', 'UTF-8');

// Create start element tag
$xml->startElement('test');

//  Write full element tag
$xml->writeElement('value', 'testwaarde');

// End current element
$xml->endElement();

// End current document
$xml->endDocument();

// Define logfile name
$filename = '/logs/' . date('Ymd_hm') . '.xml';

// Open file
$fp = fopen($filename, "w");

// Write memory output to file. The false boolean is to
// prevent XMLWriter from flushing the current buffer
fwrite($fp, $xml->outputMemory(false));

// Close the file
fclose($fp);

// Return the current buffer
echo $xml->outputMemory();

Dit is een redelijk kort samengevat voorbeeld en het kan altijd beter, netter en leuker maar het idee is duidelijk.