Disclaimer: Welkom bij onze TechBlog!
Dit is een artikel in ons TechBlog. Ons Techblog bevat artikelen, geschreven door onze developers, over dingen die ze zijn tegengekomen tijdens hun werkzaamheden. Deze artikelen gaan (meestal) over een technisch onderwerp, en zijn met name bedoeld om te vermaken en (soms) te informeren. Waarom deze disclaimer? Zie het als een vorm van voorsorteren.

vrijdag 12 december
Auteurs: Lars Waage, Jeroen Mimpen

Sorteren

Ons verhaal begon, zoals het dat wel vaker doet, met een opmerking van Casper. Tijdens onze vaste meeting op dinsdag benoemde hij dat het best veel werk was om ons lijstje taken op volgorde van datum te houden, want dit moest handmatig. De reactie was ook gebruikelijk: Henk beaamde het bedachtzaam en Jeroen zat zoals gewoonlijk niet te luisteren.

Het is ook een lastig probleem. In tegenstelling tot onze eigen ontwikkelde tool Streamline, waarin we zelf eenvoudig dit soort wijzigingen kunnen aanbrengen, kan dat hier niet. Want de software die we gebruiken voor onze tickets en todo-lijsten is een commerciële tool (een webapplicatie), waarvan we geen toegang hebben tot de broncode. We kunnen immers niet al onze tools zelf ontwikkelen, want er moet ook nog wat tijd over blijven om te werken voor onze klanten. Weinig aan te doen dus, hooguit kunnen we erover mopperen.

De week erna vond onze vaste meeting weer plaats. Maar dit keer verliep het anders. Op het moment dat Casper begon met handmatig sorteren, sprong Henk op en stopte hem.

Muis nog half in de hand, al anticiperend op de woorden van Henk, vroeg Casper: "Is het wat ik denk dat het is? Hebben ze eindelijk een sorterings-feature toegevoegd!?". "Nee." Zei Henk, terwijl hij liet zien hoe we nu de lijst automatisch kunnen sorteren. "Zij hebben die feature niet toegevoegd. Wij hebben die feature toegevoegd. Of Lars, om specifiek te zijn." Casper juichte van vreugde: hij hoeft vanaf nu nooit meer handmatig te sorteren! Ik geloof dat er op een gegeven moment zelfs confetti en champagne bij gehaald werd. Het was een van de mooiste dagen van zijn leven.

Ok, het bovenstaande verhaal is misschien een licht overdreven dramatisering van hoe ik het ervaarde (waarschijnlijk omdat ik weer niet zat op te letten). En het automatisch sorteren was niet meteen mogelijk, want in werkelijkheid moest Casper eerst wat installatie-stappen doorlopen, voordat de knop hiervoor in de user interface verscheen. Echter klopt de essentie van mijn (licht gedramatiseerde) verhaal verder wel: de interface van een tool die wij niet zelf ontwikkelen hebben wij uitgebreid met deze functionaliteit. Maar, dit is toch een commerciële webapplicatie, waar wij de broncode niet van hebben? Hoe heeft Lars dit dan in kunnen bouwen? In dit artikel laat ik Lars aan het woord om dat uit te leggen.

Sorteren in Basecamp

Bij Webenable gebruiken we Basecamp als ticket systeem voor alle werkzaamheden. Het sluit heel goed aan op onze werkzaamheden, maar het kan altijd beter. Een functionaliteit die we gebruiken is het zetten en controleren van een due date op een ticket. Maar als je een lijst hebt met veel tickets, dan zie je tussen de bomen het bos niet meer. Je kan wel tickets slepen qua volgorde, maar dan blijf je aan de gang. En er is geeneens een sorteer functie! Normaal gesproken dien je dan wijziging verzoeken in en wacht je een tijdje terwijl je work-arounds aan het verzinnen bent....tenzij je vermoed dat je het zelf kan en dat een mooie uitdaging vindt.

Google Chrome extensions

We gebruiken standaard Google Chrome als browser en daarbinnen kan je een extensies maken, waarmee je websites kan manipuleren. Een Chrome extension bestaat uit een manifest en javascript bestanden voor de uitvoering. Het manifest is een json documentje dat beschrijft wat de extensie doet, welke javascript bestanden iets moeten doen, wat deze javascript bestanden mogen doen en waar (welke urls) ze dat moeten doen.

Background worker en tabbladen

Er is altijd een javascript bestand voor een background worker, die centraal binnen Chrome iets kan doen op basis van events die afgaan op de verschillende tabbladen. Chrome kent een library met events die afgaan als er iets gebeurt op een tabblad. Zo hebben wij chrome.webRequest.onCompleted geïmplementeerd. Bij het onCompleted event kan je vanuit de background worker met chroms.tabs.sendMessage een bericht versturen naar het tabblad waar het om gaat. En daar kan je dan een tweede javascript bestand injecteren die het chrome.runtime.onMessage event oppakt. Met die constructie kunnen we code aftrappen om op dat tabblad daadwerkelijk iets te gaan doen. Zo kunnen we de DOM van de betreffende pagina manipuleren (hij is immers 'Completed'). We kunnen op zoek gaan naar de juiste css classes om ergens te komen waar je een optie wil toevoegen, in ons geval een sorteer optie in het menu. Bij die knop implementeren we dan het onclick event voor het sorteren.

Sorteren

Maar hoe gaan we automatisch een ticket verplaatsen? Als je in Basecamp via Developer Tools gaat kijken wat er gebeurt als je tickets sleept, dan zie je dat er een functie in een javascript library wordt aangeroepen met als parameters het ID van het ticket, ID van de parent lijst en de nieuwe positie in die lijst. Die zorgt voor de verwerking daarvan bij Basecamp, dus dat zouden we prima kunnen gebruiken.

Het totaalplaatje om te doen wordt dan:

  1. Loop door de DOM op zoek naar tickets met een due date.
  2. Onthoudt de ticket ID's en de due date.
  3. Er blijken ook meerdere lijsten te zijn, dus onthoudt ook de parent IDs, zodat we de tickets binnen de lijsten in 1 keer zouden kunnen sorteren.
  4. Sorteer de lijst.
  5. Loop door de lijst en roep voor elk ticket de library functie aan met de parent en de nieuwe positie.
  6. Ververs het scherm als alles klaar is.

Dus, mooi achter de onclick bouwen en gaan? Helaas.

Security

Omdat Chrome extensions wel erg kunnen ingrijpen op de werking van een arme onwetende website, is het ook een security risico. Daarom heeft Google toegang tot javascript op de pagina geblokkeerd. Je kan er nog wel omheen, door in het manifest voor het javascript bestand de WORLD (een soort security scope) in te stellen op MAIN in plaats van ISOLATED. Dan kan je wel overal bij op de pagina, maar dan kan je weer niet de chrome events gebruiken. Het is dus het een of het ander.

Maar wat nou als we twee javascript bestanden gebruiken, 1 met scope MAIN en de ander met scope ISOLATED? ISOLATED ontvangt nog steeds het chrome event, past de DOM aan met ons menu item, en bij de onclick van dat item doen we window.postMessage naar MAIN.
MAIN heeft window.addEventListener en krijg daarmee het seintje door dat hij zijn gang kan gaan. Hij die doet al het bovenstaande werk, inclusief het aanroepen van de nu wel beschikbare javascript library call.

En dat werkt! Het resultaat is een naadloze integratie. Iedereen die het ziet zou denken dat het standaard Basecamp functionaliteit is, maar dat is het niet.

Conclusies

  • Met Chrome extensions kan je ver gaan om extra gedrag in een bestaande applicatie te injecteren.
  • De constructies zijn best complex, maar de beveiliging was goed te omzeilen.
  • Installeer alleen third party Chrome extensions die je echt vertrouwd.
  • Als uitdagingen slagen, dan vinden we dat gaaf en worden we blij.