4.9.2008

Wilman pikaviestien vastaanottajien valitsemistyökalu

Tuotos

Kyseessä on siis bookmarkletiksi tarkoitettu JavaScript-pätkä, joka oleellisesti nopeuttaa vastaanottajien valitsemista Wilma-pikaviestiin. Bookmarklet on tarkoitus sijoittaa selaimen osoiterivin alapuolella olevaan työkaluriviin. Bookmarkletin käyttäminen luonnollisesti edellyttää, että on kirjautunut sisään Wilmaan. Tällä hetkellä koodi ei osaa luoda listoja valituista vastaanottajista, mikä olisi positiivinen lisäys. Mikäli joku lukija sellaisen tekee, pyytäisin laittamaan kommenttiin maininnan :)

Itse bookmarklet

Raahaa alla oleva linkki esim. selaimesi työkaluriviin niin saat itsellesi Wilma-pikaviestityökalun.

Wilma - lisää pikaviestin vastaanottajia

Wilma 2.04 - lisää pikaviestin vastaanottajia

Versiohistoria ja muutokset
2011-05-25
Toimivuus testattu Wilma 2.10c:ssä Firefox 3.6.16, Firefox 4.0.1, Google Chrome 11.0, Opera 11.10 ja IE8 (tosin IE8:ssa ei toimi, sillä se näköjään rajoittaa osoitekentän pituuden alle kahteen kilotavuun...aiemmin IE:ssä osoitekentän pituuden rajana on ollut noin 4kt, mikä muissakin selaimissa...)
2008-08-17
Korjattu virhe, joka aiheutti, että lisättävät nimet tulivat Firefoxissa sivun alareunaan kahteen kertaan.
2008-06-30
Lisätty bookmarkletista erillinen versio Wilman versiota 2.06 varten (tai ainakin 2.06c, en tiedä toimiiko 2.06a:ssa ja/tai 2.06b:ssä).
2008-05-08
  • Lisätty tuki opettajien lisäämiseksi vastaanottajaksi
  • Lisätty yläreunaan napit "Lisää opiskelijoita" ja "Lisää opettajia"
  • Huom! Ryhmiä voi lisätä vastaanottajiksi lisäämällä ensin halutut opiskelijat/opettajat, klikkaamalla "Aloita viestin kirjoittaminen", tämän jälkeen "Lisää ryhmiä" ja lopuksi klikkaa haluamasi ryhmän "Viesti"-linkkiä.
2008-02-25
Ensimmäinen julkaistu versio. Tuki opiskelijoiden nopeaa lisäämistä vastaanottajiksi.
Tuetut Wilman versiot

Kaikki ainakin versioon 2.10c asti (testattu 25.5.2011).

Tuetut selaimet (Windows-alustalla)
Firefox 4: toimii
Firefox 3: toimii
Firefox 2: toimii
Opera 11: toimii
Opera 10: ei testattu
Opera 9:   ei toimi
Google Chrome: toimii
IE8: ei toimi (IE8 rajoittaa osoitekentän alle 2kt!)
IE7:       toimii, tosin hiukan puutteellisesti
IE6:       ei toimi
Safari:    ei testattu

Taustaa

Marraskuussa kirjoitin Wilman pikaviestien poistamistyökalusta, jonka kirjoitin tuskastuttuani viestien poistamisen työläyteen. Wilma on StarSoft Oy:n toteuttama selainkäyttöliittymä samaisen firman Primus-koulutietojärjestelmän tietokantaan. Wilman avulla opettajat voivat esim. antaa opiskelijoille arvosanat, merkitä poissaoloja, lähettää opiskelijoille viestejä jne. Wilma-Primus-yhdistelmä on on käytössä suuressa osassa Suomen peruskouluja ja lukioita.

Ongelma, jonka halusin ratkaista

Wilman pikaviestien poistamistakin tuskallisempaa ainakin Wilman versiossa 2.04 on pikaviestien lähettäminen suure(hko)lle määrälle oppilaita/opiskelijoita, jotka eivät ole minkään senhetkisen opetusryhmän jäseniä. Jokaisen vastaanottajan joutuu valitsemaan erikseen ja jokaista valittua henkilö varten tarvitaan neljä klikkausta ja vastaava määrä sivulatauksia...juuri tekemäni pikatestauksen mukaan tähän menee n. 15-20 sek / lisätty vastaanottaja. Kun vastaanottajia on vaikkapa 20 niin pelkästään viestin vastaanottajien valitsemiseen kuluu arviolta viidestä minuutista hiukan yli kuuteen ja puoleen.

Viime viikolla minun tarvitsi tehdä juuri tällainen operaatio, tosin viesti piti lähettää 60 opiskelijalle 20 sijaan. Tovin jos toisenkin lisäilin vastaanottajia. Ajattelin, että ei tätä näin vaikeasti voi tehdä jatkossa.

Motivaatio ja ratkaisun kehittely

Edellä esitetyn motivaation lisäksi minulla oli myös toinen motivaatio. Tiesin nimittäin, että kyseessä olisi hyvä projekti jQuery:n harjoittelemiseen, jota en ollut vielä koskaan kunnolla käyttänyt. Niin sitten visualjquery.com tuli pelkän aiemman, mielenkiinnosta selaamisen, lisäksi myös käytännön koodauskäytössä hyväksi todetuksi.

Projektiin lähtiessäni tiesin yhden monimutkaisimmista taklattavaksi tulevista yksityiskohdista olevan CSRF:ää vastaan Wilmassa käytössä olevan action token -systeemin emuloimisen AJAX:ia käyttämällä. Kuitenkin jQuery:n avulla AJAX:n GET- ja POST-sivupyyntöjen tekeminen oli sinänsä kivutonta.

Teknisestä puolesta kiinnostuneille

Koodi riveihin purettuna...tosin ilman sen enempiä selityksiä

Lopuksi uudemman version koodi nätimmin riveille jaettuna niille, jotka mahdollisesti haluavat tutkia bookmarkettini sisuskaluja tarkemmin. Kommentit ja kehitysideat ovat tervetulleita. Valitettavasti koodi menee vähän tonne oikean reunan valikon alle. Kopioi koodi koodieditoriisi tai vaikka Notepadiin niin näkyy kokonaisuudessaan. Bookmarklet on kooltaan hiukan yli 2kt.

Huomautuksena sen verran, että vanhemman 2.04-version koodin saat kopioimalla kyseisen bookmarkletin koodin (eli linkin osoitteen hiiren oikealla napilla), liittämällä haluamaasi tekstieditoriin ja pilkkomalla riveihin; vanhemmassa versiossa actionTokenin hakeminen on tehty vähemmän fiksusti kuin tässä uudemmassa versiossa.


javascript:d=document;
d.open();
d.write('
  <html>
  <head>
  <title>Lisää vastaanottajia Wilma-viestiin</title>
  </head>
  <frameset rows='37, *, 50'>
    <frame src='about:blank' scrolling='no' />
    <frame src='' +location+ '' />
    <frame src='about:blank' />
    </frameset>
  </html>
');
d.close();
tf0d=top.frames[0].document;
tf0d.open();
tf0d.write('
  <html>
  <head>
  <title>top</title>
  <script type='text/javascript' src='http://jquery.com/src/jquery-latest.pack.js'></script>
  <script type='text/javascript'>
    var linkAlterEnabled=false;
    
    function reAlterLinks(){
      setTimeout(alterLinks,700);
    }

    function init(){
      var actionToken = $($.ajax( {url: '/messages/compose', async:false} ).responseText).find('input[name=\'formkey\']').val();
      $.post('/messages/compose', {formkey:actionToken,
                                       add_student:'',
                                       Subject:'',
                                       BodyText:'',
                                       HideRecipients:'false',
                                       CollatedReplies:'true'
                                  }
      );
      alterLinks();
      top.frames[1].location='/profiles/students';
      alert('Voit aloittaa vastaanottajien valitsemisen klikkaamalla nimiä.\\nKun
             olet valinnut vastaanottajat, paina \'Aloita viestin kirjoittaminen\'
             -nappia.\\n\\n(Huomaa, että aloitettuasi viestin kirjoittamisen, voit
             tämän jälkeen halutessasi lisätä vastaanottajaksi ryhmiä \'tavalliseen
             tapaan\')');
    }

    function alterLinks(){
      $('a.opp',top.frames[1].document).click(
        function(){ addRecipientAndHide(this,'student'); }
      );
      $('a.ope',top.frames[1].document).click(
        function(){ addRecipientAndHide(this,'teacher'); }
      );
      top.frames[1].window.onunload=top.frames[0].reAlterLinks;
    }

    function addRecipientAndHide(elem,recipientType){
      $.get('/messages/compose?r_'+recipientType+'='+elem.href.substring(elem.href.lastIndexOf('/')+1,elem.href.length));
      $('body',top.frames[2].document).append(elem.text+\', \');
      $(elem).unbind().removeAttr('href').css('visibility', 'hidden');
    }
  </script>
  </head>
  <body onload='javascript:init();'>
  
  <form>
    <input type='button' value='Lisää opiskelijoita (aakkoslista)'
      onclick='javascript:top.frames[1].location=\'/profiles/students\';' />
    <input type='button' value='Lisää opiskelijoita (ryhmälista)'
      onclick='javascript:top.frames[1].location=\'/groups\';' />
    <input type='button' value='Lisää opettajia'
      onclick='javascript:top.frames[1].location=\'/profiles/teachers\';'
      style='margin-right: 3em;' />
    <input type='button' value='Aloita viestin kirjoittaminen'
      onclick='javascript:top.location=\'/messages/compose\';' />
    <input type='button' value='Peruuta'
      onclick='javascript:top.location=top.frames[1].location' />
  </form>
  </body>
  </html>
');
tf0d.close();
tf2d=top.frames[2].document;
tf2d.open();
tf2d.write('<strong>Vastaanottajat:</strong> ');
tf2d.close();

Parannettavaa ja muita huomioita

Koodissa on paljon kehitettävää ja asioita voisi jaotella enemmän funktioiksi. Oliolähtöiseen toteutukseen en tässä ehkä kuitenkaan lähtisi johtuen bookmarklettien noin 4kt kokorajasta, joka johtuu selainten osoitekenttään (tai kirjanmerkkiin) maksimissaan sallitun sisällön pituudesta.

Jos vastaanottajien hallintaan tekisi listatoiminnot, olisi oliolähestymistavan käyttäminen listojen hanskaamiseen erittäin hyvä, mutta mutufiilis on, että 4kt rajaan listatoiminnot eivät oliototeutuksena kuitenkaan mahtuisi. Voin tietty olla väärässä, kun en ole kokeillut.

Tuon massiivisen massiivisen document.write()-lähestymistavan voisi muuttaa niin, että ottaisi suoraan kiinni DOM:iin heti koodin päätasolta alkaen eikä document.write():n läpi HTML:ää ja JavaScriptiä JavaScriptillä generoiden...tosin DOM:n käyttäminen taitaisi vähän pidentää koodia ja tuo osoitekentän 4kt raja rupeaisi ehkä tulemaan vastaan.

Koodilla tuottamani HTML ei myöskään validoidu XHTML1.0 Strictiä vastaan niinkuin yleensä yritän tehdä. En nähnyt sitä tässä tarpeelliseksi koska se lähinnä lisäisi bookmarkletin koodin pituutta tuomatta mitään oleellista hyötyä tässä sovelluskohteessa.

Tuon framejen HTML-koodin tuottamisen voisi mahdollisesti tehdä myös jQueryllä, mutta päätin käyttää tuttua ja turvallista document.write():a sen suoraviivaisuudesta johtuen.

Palautetta, kysyttävää, kommentoitavaa?

Mikäli on palautetta, kehitysideoita yms. voi ne laittaa kommenttina tähän kirjoituksen yhteyteen; olisin erittäin kiitollinen :)

Lähteet

Projektia tehdessäni käytin mm. seuraavia lähteitä, joskaan lista ei ole täydellinen:

jQuery.com
John Resigin, jQuery:n kehittäjän, aiheeseen liittyvä sivusto. Sivustolla on myös keskustelufoorumi ja paljon jQuery-pluginejä.
visualjquery.com
Erittäin hyvä jQuery-dokumentaatiokäyttöliittymä
Wikipedia: XmlHttpRequest
Lueskelin XmlHttpRequest-objektin, eli AJAX:n/AHAH:n kulmakiven dokumentaatiota, jota tarvitsin tuon responseText:n löytämiseen
IBM: Simplify Ajax development with jQuery
IBM:n artikkeli jQuery:n käyttämisestä. Sisältää paljon koodiesimerkkejä.
$.frameReady()
Tätä en vielä käyttänyt, mutta näyttäisi siltä, että voisin hyötyä tästä jQuery-pluginistä: frameReady lets you run jQuery commands in a target frame as if it were in the local document. It works much like the $(document).ready() function, waiting until the DOM is ready in the target frame before attempting to run your code.

(Mikä ihmeen bookmarklet?)

Bookmarklet on JavaScript-ohjelmointikielellä kirjoitettu pieni selaimessa toimiva ohjelma, jolla voidaan automatisoida jotain selaimeen, tiettyyn nettisivuun tai kaikkiin nettisivuihin liittyviä toimintoja. Lisää aiheesta englanniksi.

Tämän artikkelin sisällön lisenssi: Creative Commons License