pühapäev, 25. oktoober 2009

Open source CMS Google App Engine platvormile

Olen praktiliselt kogu oma hostingu kolinud Google App Engine peale, kuid sinna saab saata ainult aplikatsioone - vahel aga on vajadus lihtsalt kodulehe järele. Ükski olemasolev CMS sinna peale aga ei sobi (kasutusel on Python+BigTable vs. standardne PHP+MySQL). Seega otsustasin luua ise lihtsa CMS-i ja olen nüüd paar päeva sellega vaeva näinud. Tulemuseks on TurbineCMS.

Hetkel ei tasu koledat disaini väga tähele panna - ma pole ise eriline disainimees, aga Heimar lubas mind selle koha pealt veidi aidata. Pealegi igaüks saab ise oma disaini luua (ja seda saab muuseas teha ka CMS-i adminni liidese kaudu).

Tegu on MIT litsentsiga välja antud avatud lähtekoodiga rakendusega, kasutan küll ka LGPL komponente (TinyMCE), kuid loodan et need litsentsid ei lähe omavahel konflikti. Samuti kasutan hetkel ühte vabavaralist ikooni failide näitamiseks, kuid kuna ma ei leidnud mis litsentsi alt see välja antud on, siis ma ei osanud sellega arvestada. Kui tuleb välja, et ei sobi, siis otsin uue ikooni.

Platvorm:
  • Hostinguna on kasutusel Google App Engine konto (id: turbinecms), millele viitab domeen turbinecms.com
  • Serveripoolne kood on kirjutatud Pythonis (1 fail)
  • Andmebaasiks on GAE poolt pakutav BigTable
  • Üleslaetud failid (sh. ka kasutaja loodud kujundusfail) hoitakse andmebaasis
  • Kõik andmebaasist tulevad andmed on puhverdatud memcache puhvrisse
  • Templiidimootoriks on Django 0.96 (GAE default)
  • JavaScripti teekidena on kasutusel Prototype + Scriptaculous
Võimalused:
  • CMS võimaldab luua WYSIWYG tekstitoimetiga (TinyMCE) uusi lehti
  • Samuti saab luua alamlehti, mida näidatakse põhilehe järel. Näiteks Uudiste põhilehele saab lisada alamlehtedena üksikuid uudiseid.
  • Lehed võivad olla mustandid või avaldatud. Juhul kui on mustandid, siis neid avalikult vaadata ei saa.
  • Saab laadida üles pilte ja faile, et neid lehe sees kasutada. Kahjuks on faili suuruse piirang hetkel 1 MB, seega tuleks pildid eelnevalt arvutis väiksemaks teha, kui need juhtuvad liiga suured olema.
  • Lehe kujundust saad muuta on-line administreerimise lehelt. Kujundus moodustub ühest HTML failist (Django 0.96 templiidiformaat)
Probleemid:
  • Hetkel on peamiseks probleemiks kujundus, mis on suhteliselt lihtsakoeline
  • Internet Exploreri kasutajatel ei toimi failide upload, failinimede asemel saab skript failinimed koos kataloogiteega - C:/Documents and settings/.../pilt.jpg" ja seega genereeritud lingid ei tööta, kuna sisaldavad endas ka failinime. Avasin ka vastava issue endale meeldetuletuseks.
Tõmmata saab (ja lähtekoodi sirvida) Google Code lehel - http://code.google.com/p/turbinecms/

Kasutamiseks peab olema Google App Engine konto, kuid testida saab ka GAE SDK-ga kaasa tuleva localhost serveri peal.

Kui keegi tunneb projekti vastu huvi ja tahab mingil viisil kaasa aidata (kas siis koodi või disaini arenduses), siis kõik on oodatud (Y)


PS. Selles blogis oli probleem kommenteerimisega - määrasin adminniliidesest, et kommenteerimine käib otse blogi lehelt, aga hetkel kasutuses olev templiit ei oska seda teha (katsetasin vana templiidiga), seega kommenteerida ei saanudki. Nüüd saab kommenteerimiseks kasutada Bloggeri vaikimisi kommentaarivormi eraldi lehel.

esmaspäev, 19. oktoober 2009

iPhone veebirakendused vol. 3

Eelmistest iPhone veebirakenduste postitusest kirjutasin, kuidas kasutada ära erinevaid iPhone brauseri poolt pakutud võimalusi, loomaks rakendustelaadseid veebilehti. Kuid siiani on nendelt rakendustel olnud üks häda - need töötavad ainult võrgus olles, offline annab rakendus veateate.

Õnneks on ka siin lahendus olemas, tuleb kasutada offline application caching API't, mille toetus on olemas nii uuemates Firefoxides kui ka Safarites ning seega ka iPhone brauseris.

Lehe puhverdamiseks on esiteks tarvis luua puhvri manifest fail - selles kirjeldame ära, millised ressursid peaks brauser ära tõmbama. Kuna demorakenduses on kõik vajalik ainult HTML failis endas, siis midagi muud puhverdada polegi vaja.

myapp.manifest faili sisu:
CACHE MANIFEST
app.php

Selle jaoks, et manifesti fail oleks brauserile üheselt arusaadav, tuleks paika seada ka vastav mime-tüüp. Kui serveris on võimalik kasutada .htaccess faile, saab seda teha lisades manifesti faili kataloogis asuvasse .htaccess faili rea

AddType text/cache-manifest manifest

Ja viimasena oleks tarvis veebilehe koodis ära märkida, et puhverdamine on kasutusel. Kuna tegu on HTML5 featuuriga, peaks ka doctype olema vastav.

<!DOCTYPE HTML>
<html manifest="myapp.manifest">

Kui nüüd avada selline rakendus iPhone brauseriga ning lisada see telefoni töölauale, siis võib rakendust avada ka täiesti offline olekus, mis teeb rakendusest praktiliselt samaväärse kui AppStore'i kaudu hangitud rakendused on. Ainsa vahega, et installida on suhteliselt mugav - seda saab teha otse veebilehelt ning ilma igasuguse itunes kontota.

Selle jaoks, et kontrollida, kas veebileht on avatud otse brauserist või töölaualt (et vahet teha, kas näidata installimise juhendit või mitte), saab kasutada window.navigator omadust standalone.

if (window.navigator.standalone){
    // tee vajalikke asju
}else{
    // näita installimise juhendit
}

Näites kasutatud puhverdamine on suhteliselt primitiivne - võibolla tuleks vahepeal mingeid asju uuesti laadida või tuleks andmete sünkroniseerimiseks kontrollida, kas parasjagu ollakse online või offline jms.  Kõik see on võimalik, tasub lugeda täpsemat spetsifikatsiooni, mille leiad siit.

laupäev, 17. oktoober 2009

Cross domain JavaScript

Ülidselt on cross-domain JavaScript brauserites rangelt keelatud. See tähendab, et ei ole võimalik teha AJAX päringuid võõrasse domeeni (st. domeeni, mis on erinev lehe domeenist, kust päring esitati, näiteks www.neti.ee ei saa teha AJAX päringut aadressile www.delfi.ee) ning samuti ei ole võimalik JavaScriptil ligi pääseda võõrast domeenist tulevale teise freimi või akna sisule.

Juhul kui domeenid on samad, pole freimide (sh. ka iframe ning brauseri teised aknad) puhul mingit probleemi - teise freimi sisu on kättesaadav sama lihtsalt nagu window.document DOM puu. window.document asemel tuleb kasutada lihtsalt vastavale freimile viitavale objektile. Iframe saab põhifreimi kätte objektist parent ning aken, mis on avatud teise akna poolt, saab avaja kätte objektist window.opener.

Juhul kui põhidomeen on sama, aga alamdomeen on erinev, näiteks ühes freimis on avatud leht aadressilt www1.neti.ee ja teises freimis on avatud www2.neti.ee, saavad freimid omavahel suhelda, kui mõlemad muudavad ära document.domain väärtuse põhidomeeni vastu.

Põhifreim (www1.neti.ee):
document.domain = 'neti.ee';
abc = 123;

Iframe (www2.neti.ee):
document.domain = 'neti.ee';
alert(parent.abc); //123

Keerulisem aga on lugu juhul, kui erinevad freimid asuvad täiesti eri domeenidel, näiteks aadressidel teenus1.com ning teenus2.com. Siiani praktiline lahendus puudus, kuid uutes HTML5 toega brauserites (näiteks alates Firefox 3.0 ning Internet Explorer 8) on probleemi lahendamiseks võimalik kasutada messaging API't.

Töötab asi järgmiselt - freim, kes tahab saata teisele freimile mingit infot, käivitab selle jaoks vastava freimi objektis postMessage meetodi, mis edastab info adressaadini. Adressaat ise aga peab sõnumite kättesaamiseks seadma üles vastava sündmusehalduri (window.onmessage).

Juhul kui sündmuse haldajale laekub teisest freimist sõnum peaks see kontrollima, kas info tuleb usaldusväärsest allikast ning seejärel saab teha saadud infoga, mida vaja.

Sisuliselt võib öelda, et turvakontroll on lükatud brauserist skripti poolele. Skript ise vastutab, et see info (mis tuleb sisse alati stringi kujul) kõlbab töötlemiseks või mitte.

Sõnumi saatmiseks tuleb kasutada postMessage meetodit:

parent.postMessage(message, targetOrigin);

message on saadetav sõnum stringi kujul.
targetOrigin - string, mis määrab ära lubatud vastuvõtja. Lubatud kuju on protokoll://domeen[:port]. Port on vaja määrata vaid juhul, kui see ei ole sama mis protokolli vaikimis port (http:80, https:443). Võimalik on kasutada ka kuju "*",  sellisel juhul saavad sõnumi kõik aknad.

Sõnumi kättesaamiseks adressaadi poolt tuleb seada üles vastav sündmus - onmessage.

window.onmessage = function(e){
    e.source; // objekt, mis viitab sõnumit saatvale freimile
    e.origin; // string, mis sisaldab endas saatja domeeni kujul protokoll://domeen[:port]
    e.data; // vastuvõetud sõnum stringi kujul
}

Kuigi sõnumi saatja kontroll pole kohustuslik, samuti võib sõnumit saata "broadcast'ina", kasutades vastuvõtjana stringi "*", on soovitatav kontrollida nii sõnumi saajat, kui saatjat. Ja seda seetõttu, et kolmandad osapooled ei saaks midagi kurja korda saata.

Näiteks eeldame, et stringi kujul transporditakse JSON koodi, mis käivitatakse vastuvõtja poolt eval käsuga.

window.onmessage = function(e){
    data = eval(e.data);
}

Juhul kui saatjaks on pahalane, võib e.data sisaldada endas näiteks mõnda funktsiooni, mis tõstab kõrvale kasutaja hetke sessiooni või veebipõhise e-posti kliendi puhul näiteks suunab kasutaja e-posti aadressi pahalase aadressile. Sellest, aga mida kõike paha sarnasel viisil teha annab (meenutagem näiteks Orkuti viiruseid), kirjutan juba mõni teine kord.

Lingid:
window.postMessage Mozilla Developers saidil
window.postMessage dokumentatsioon MSDN saidil