OpenSocial Gyorstalpaló

A iWiW Fejlesztői Wiki wikiből

Tartalomjegyzék

Hogyan készítsük el első OpenSocial alkalmazásunkat?

Az alábbi írás egy példa mentén próbál bevezetni az OpenSocial alkalmazások fejlesztésébe. Az itt leírtak javarészt általános érvényűek, azaz minden OpenSocial konténerben működnek a példák (ahol nem, ott külön jelezzük). Ha az OpenSocial alkalmazások fejlesztésének iWiW specifikus módszereire vagy kíváncsi, olvasd el előbb ezt: !!TODO!!.

Feltételezzük, hogy megvannak az alapvető ismereteid az alábbi technológiákról:

  • XML
  • JavaScript
  • prototype.js
  • XHR
  • HTML
  • CSS

Ebben a bevezetőben egy egyszerű kis alkalmazást fogunk lépésről lépésre fejleszteni, aminek segítségével a felhsználók számolják hány órát dolgozhatnak, és ezt megoszthatják ismerőseikkel is.

A javascript kódolást megkönnyítendő használni fogjuk a prototype.js JavaScript libraryt, de ízlés szerint bármely más külső JavaScript is használható.

A példák kipróbálásához egy szövegszerkesztőre (pl. VIM, Notepad, TextMate, stb.) és egy webtárhelyre lesz szükség (erre a célra tökéletesen megfelel valamelyik ingyenes webtárhely-szolgáltató). Hasznos lehet még egy Firebug kiterjesztéssel ellátott Firefox is (JavaScript hibakeresés, kommunikáció követése és hibakeresése).

A példakódok elérhetőek a GitHubon, ahol ezekből készíthetsz saját branchet is, és közzéteheted a saját verziódat. Fontos! A példákat közvetlenül a GitHubról is fölveheted a DevPortálra, ez esetben a "raw" XML-re kell hivatkoznod, amelyre a linket a kód "ablak"jobb felső sarkában találod.

Első lépések: a gadget spec xml létrehozása

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Hello World!" author="Gipsz Jakab">
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      Hello world!
    ]]>
  </Content>
</Module>

A hello world alkalmazás teljes kódja

Második lépés: rögzítsük, mennyit dolgoztunk

A Content tagen belül létrehozunk egy űrlapot, hogy meg tudjuk adni, hány órát dolgoztunk (a továbbiakban minden a Content tag en belül CDATA-ba kerül, ha ezt külön nem jelezzük):

<form id="logWork">
	<p>
		<label>Eddig ennyit lapátoltál:</label>
		<strong id="sum" class="sum">semennyit</strong>
	</p>
	<p>
		<label>Ma ennyit:</label>
		<input type="text" size="2" id="hours"/> órát
		<button type="submit" id="saveHours">Mehet</button>
	</p>
</form>

..és megírjuk az űrlapot kezelő JavaScript kódot is (a kódokat a yuiblog.com-on ismertetett modul pattern alapján szervezem, természetesen a kódszervezés mikéntje tetszőleges):

 <script type="text/javascript">
 var sztahanov = function() {
 	var hours = 0;
 
 	var logWork = function (event) {
 		Event.stop(event);
 		var hoursLogged = + $F('hours');
 		if(hoursLogged > 0) {
 			$('hours').value = '';
 			hours += hoursLogged;
 			$('sum').update(hours + ' órát');
 		}
 	}
 
 	return {
 		init: function() {
 			$('logWork').observe('submit',logWork);
 		}
 
 	}
 }();
 
 gadgets.util.registerOnLoadHandler(sztahanov.init);
</script>

Az első lépés teljes kódja

Mi is történik itt?

  • gadgets.util.registerOnLoadHandler(sztahanov.init): a registerOnLoadHandler-nek átadott függvény-referencia fog meghívódni amikor a konténer kész az alkalmazás inicializálására.
  • $('logWork').observe('submit',logWork): a form elküldésekor meghívódik a logWork függvény, ami egyszerűen növeli a munkaóra-számlálót, amit szépen meg is jelenítünk.

Az eddig mind szép, de mi történik, ha újratöltjük az oldalt, vagy elnavigálunk és később visszatérünk: a kínkeservesen összelapátolt óráink elvesztek.

Második lépés: tároljuk az összelapátolt órákat

Szerencsére az OpenSocial API biztosítja a lehetőséget, hogy az alkalmazások felhasználóhoz kapcsolódóan egyszerűen adatokat tároljanak (az adattárolás ez esetben a konténer szolgáltatása, azaz az alkalmazás fejlesztőjének a háttérben történtekkel egyáltalán nem kell foglalkozni). Lássuk, hogyan is történik mindez:

A logWork függvényt kibővítjük:

var logWork = function (event) {
	Event.stop(event);
	var hoursLogged = + $F('hours');
	if(hoursLogged > 0) {
		$('hours').value = '';
		hours += hoursLogged;
		$('sum').update(hours + ' órát');
 
		var req = opensocial.newDataRequest();
		req.add(req.newUpdatePersonAppDataRequest("VIEWER", 'workHours', {hours:hours}));
		req.send();
	}
}

(Fontos, hogy ezen a ponton a gadget xml ModulePrefs részében be kell töltenünk az opensocial feature-t, hogy elérhessük a JavaScript APIt a kódunkból. Lásd a teljes xml-t.)

A varázslat ebben a sorban rejlik: req.add(req.newUpdatePersonAppDataRequest("VIEWER", 'workHours', {hours:hours}))

  • létrehozunk egy új requestet
  • a requestre rárakunk egy appData frissítés-kérést: a viewer 'workHours' adatának {hours:hours} értéket adunk
  • elküldjük a kérést

Ha van Firebugunk, a konzolon láthatjuk is, hogy amikor újabb lelapátolt órákat rögzítünk, történik egy XHR kérés a konténer felé, valami ilyesmi POST adatként:

[
    {
        "method": "appdata.update",
        "params": {
            "userId": [
                "@viewer"
            ],
            "groupId": "@self",
            "appId": "@app",
            "data": {
                "workHours": {
                    "hours": 11
                }
            },
            "fields": "workHours"
        }
    }
]

Rendben, megvan a mentés. Ha viszont újratöltjük az oldalt, az adatokat továbbra is elveszítjük. Írjuk hát meg az adatbetöltést is.

Az init függvény bővítjük:

init: function() {
	$('logWork').observe('submit',logWork);
 
	var viewer = opensocial.newIdSpec({ "userId" : "VIEWER" });
	var req = opensocial.newDataRequest();
	req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), 'viewer');
	req.add(req.newFetchPersonAppDataRequest(viewer, 'workHours'), 'viewerSztahanovData');
	req.send(load);
}
  • létrehozunk egy új kérést
  • lekérjük a viewer adatait 'viewer' kulccsal
  • a viewer 'workHours' adatát is lekérjük, 'viewerSztahanovData' kulccsal (ez utóbbinak a következőkben lesz jelentősége)
  • mivel az OpenSocial API a konténerrel aszinkron módon kommunikál, abban az esetben, ha egy kérés visszatérési értékével foglalkozni akarunk (márpedig itt éppen igen), egy callback függvényt kell átadni a kéréssel együtt, amely a sikeres válasz adataival fog mehívódni. Esetünkben ez a load névre hallgat:
var load = function(data) {
	var viewer = data.get('viewer').getData();
 
	var sztahanovData = data.get('viewerSztahanovData').getData();		
	if(sztahanovData[viewer.getId()]) {
		var workHours = gadgets.json.parse(gadgets.util.unescapeString(sztahanovData[viewer.getId()]['workHours']));	
		if(workHours.hours) {
			hours = workHours.hours;
			$('sum').update(hours + ' órát');
		}
	}
}

A második lépés teljes kódja

  • a callback a data változóban várja az adatokat
  • 'viewer' kulccsal megkapjuk a viewert
  • 'viewerSztahanovData' kulccsal az alkalmazásunkkal mentett adatokat (vegyük észre, hogy az itt használt kulcsok megegyeznek a kérés létrehozásakor használt kulcsokkal)
  • feldolgozzuk az adatokat (ha vannak)

Mivel a válaszban JSON stringet kapunk, ezt az OpenSocial API által biztosított parserrel először fel kell dolgozni, hogy az adatok kinyerhetőek legyenek. Firebug segítségével is megnézhetjük, a workHours változó tartalma ez lesz: {"hours": 11}, amiből már könnyen kinyerhető az elmentett lelapátolt órák száma.

Vegyük észre, hogy az adatokat felhasználó-azonosító kulccsal kell kinyerni (sztahanovData[viewer.getId()]['workHours']): ennek később nagyobb jelentősége lesz.

Harmadik lépés: szocializálódjunk

Izgalmas dolgokat csináltunk eddig is, de még izgalmasabb lenne, ha nem csak magunknak tudnánk mentegeni és megjeleníteni, mennyire vagyunk sztahanovisták, hanem ezt másoknak is meg tudnánk mutatni. Ha például egy valaki más által telepített alkalmazást látok (azaz owner != viewer), összehasonlíthatjuk, ki lapátolt többet. Sőt, azt is megtudhatjuk, hogy az owner ismerősei köszül ki mennyire sztahanovista. Lássuk!

Az init függvényben el kell kérnünk az ownert és a hozzá tartozó adatokat is:

init: function() {
	$('logWork').observe('submit',logWork);
	var viewer = opensocial.newIdSpec({ "userId" : "VIEWER" });
	var owner = opensocial.newIdSpec({ "userId" : "OWNER" });
	var req = opensocial.newDataRequest();
	req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), 'viewer');
	req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.OWNER), 'owner');
	req.add(req.newFetchPersonAppDataRequest(viewer, 'workHours'), 'viewerSztahanovData');
	req.add(req.newFetchPersonAppDataRequest(owner, 'workHours'), 'ownerSztahanovData');
	req.send(load);
}

A megjelenítéshez egy kicsit bővítettük a felületet, hogy abban az esetben, amikor van értelme, az owner adatokat külön megjeleníthessük:

<p id="ownerData">
	<label><span id="ownerName"></span> ennyit lapátolt:</label>
	<strong id="ownerHours">semennyit</strong>
</p>

A load bővül az owner feldolgozásával:

var load = function(data) {
	var viewer = data.get('viewer').getData();
	var owner = data.get('owner').getData();
 
	var viewerWorkHours = extractSztahanovData(data.get('viewerSztahanovData').getData(),viewer);
	if(viewerWorkHours) {
		hours = viewerWorkHours;
		$('sum').update(hours + ' órát');
	}
 
	if(! viewer.isOwner()) {
		//ha más alkalmazását nézem, megjelenítjük az owner összelapátolt óráit is
		var ownerWorkHours = extractSztahanovData(data.get('ownerSztahanovData').getData(),owner);
		if(ownerWorkHours) {
			$('ownerHours').update(ownerWorkHours + ' órát');
		}
		$('ownerName').update(owner.getDisplayName());
	} else {
		//ha a sajátomat nézem, az egész eltüntethető
		$('ownerData').hide();	
	}			
 
}

A harmadik lépés teljes kódja

(Az újrafelhasználhatóság és áttekinthetőség kedvéért a JSON adatok feldolgozása külön függvénybe került (lásd extractSztahanovData a teljes forrásban).

Ha most egy másik sztahanovista felhasználónak telepített alkalmazást nézek, valami ilyesmit láthatok:

Ábel Jakab ennyit lapátolt:  11 órát
Eddig ennyit lapátoltál: 46 órát 
Ma ennyit: [  ] órát [Mehet]

Negyedik lépés: sztahanovisták sztahanovista ismerősei

Az izgalmas dolgok itt kezdődnek: nézzük meg, kik az owner sztahanovista ismerősei (azaz, akiknek szintén telepítve van az alkalmazás), és ők mennyit lapátoltak.

Ehhez először az init függvényünket kell bővíteni, hogy lekérjük az owner ismerőseit, és a hozzájuk tartozó munkaóra adatokat:

 
init: function() {
 	$('logWork').observe('submit',logWork);
 	var viewer = opensocial.newIdSpec({ "userId" : "VIEWER" });
 	var owner = opensocial.newIdSpec({ "userId" : "OWNER" });
 	var req = opensocial.newDataRequest();
 	req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), 'viewer');
 	req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.OWNER), 'owner');
 	req.add(req.newFetchPersonAppDataRequest(viewer, 'workHours'), 'viewerSztahanovData');
 	req.add(req.newFetchPersonAppDataRequest(owner, 'workHours'), 'ownerSztahanovData');
 	var friends_params = {};
 	//legfeljebb 5 ismerőst kérünk
 	friends_params[opensocial.DataRequest.PeopleRequestFields.MAX] = 5;
 	//név szerint rendezve
 	friends_params[opensocial.DataRequest.PeopleRequestFields.SORT_ORDER] = opensocial.DataRequest.SortOrder.NAME;
 	//csak azokat, akiknek telepítve van az alkalmazás
 	friends_params[opensocial.DataRequest.PeopleRequestFields.FILTER] =	opensocial.DataRequest.FilterType.HAS_APP;
 	var ownerFriends = opensocial.newIdSpec({ "userId" : "OWNER", "groupId" : "FRIENDS" });
 	//ismerősök, es a hozzájuk tartozó alkalmazás-adatok
 	req.add(req.newFetchPeopleRequest(ownerFriends, friends_params), 'ownerFriends');
 	req.add(req.newFetchPersonAppDataRequest(ownerFriends, 'workHours', friends_params), 'ownerFriendsSztahanovData');</strong>
 	req.send(load);
 }

A loadfüggvényben feldolgozzuk és megjelenítjük az ismerős sztahanovistákat:

 var load = function(data) {
 	var viewer = data.get('viewer').getData();
 	var owner = data.get('owner').getData();
 
 	var viewerWorkHours = extractSztahanovData(data.get('viewerSztahanovData').getData(),viewer);
 	if(viewerWorkHours) {
 		hours = viewerWorkHours;
 		$('sum').update(hours + ' órát');
 	}
 
 	if(! viewer.isOwner()) {
 		//ha más alkalmazását nézem, megjelenítjük az owner összalepátolt óráit is
 		var ownerWorkHours = extractSztahanovData(data.get('ownerSztahanovData').getData(),owner);
 		if(ownerWorkHours) {
 			$('ownerHours').update(ownerWorkHours + ' órát');
 		}
 		$('ownerName').update(owner.getDisplayName());
 	} else {
 		//ha a sajátomat nézem, az egész eltüntethető
 		$('ownerData').hide();	
 	}		
 
 	//feldolgozzuk az ismerős listát
 	var friendsHTML = [];
 	var friends = data.get('ownerFriends').getData();
 	var friendsSztahanovData = data.get('ownerFriendsSztahanovData').getData()
 
 	friends.each(function(person) {
 		var workHours = extractSztahanovData(friendsSztahanovData, person);
 		friendsHTML.push(
 			'&lt;li&gt;'
 			+person.getDisplayName()
 			+' &lt;em&gt;' + (workHours ? workHours + ' órát lapátolt' : 'veszélyes munkakerülő')+'&lt;/em&gt;'
 			+'&lt;/li&gt;');
 	})
 
 	$('friends').update(friendsHTML.join(''));
 
 }

Egy apró bővítést kapott a HTML is:

<ul id="friends">
</ul>

A negyedik lépés teljes kódja

Ha mindent jól csináltunk, Sztahanov alkalmazásunk valami ilyesmi kimenettel örvendeztet meg (ha más alkalmazását nézem, jelen esetben ez Ábel Georgina):

Ábel Georgina ennyit lapátolt: 112 órát
Eddig ennyit lapátoltál: 34 órát
Ma ennyit: [  ] órát [Mehet]
    * András Zsolti 8 órát lapátolt
    * Aranyos Nándorné 34 órát lapátolt

Ötödik lépés: tudassuk a világgal, mennyit forgott a lapátunk

Ki hitte volna, az OpenSocial API még mindig tartogat számunkra izgalmakat. Már tudunk órákat rögzíteni és ismerősöknek mutogatni, de mi lenne, ha ismerőseink azonnal tudnának minden lapátforgatásunkról, és nem kéne rendszeresen látogatni az adatlapunkat, vajon ma dolgoztunk-e valamit. Erre szolgálnak a történések.

Azt szeretnénk, ha minden esetben, amikor órákat rögzít egy felhasználó, az ismerősei értesítést kapnának, valami ilyet: "Gipsz Jakab 8 órát lapátolt". Ehhez a logWork függvényt fogjuk bővíteni:

 var logWork = function (event) {
 	Event.stop(event);
 	var hoursLogged = + $F('hours');
 	if(hoursLogged > 0) {
 		$('hours').value = '';
 		hours += hoursLogged;
 		$('sum').update(hours + ' órát');
 
 		var req = opensocial.newDataRequest();
 		req.add(req.newUpdatePersonAppDataRequest("VIEWER", 'workHours', {hours:hours}));
 		req.send();
 
 		//történés rögzítése
 		var params = {};
 		params[opensocial.Activity.Field.TITLE] = hoursLogged + ' órát lapátolt';
 		var activity = opensocial.newActivity(params);
 		opensocial.requestCreateActivity(activity,opensocial.CreateActivityPriority.HIGH);
 
 	}
 }

Az ötödik lépés teljes kódja

Ha ezzel megvagyunk, ismerőseink minden rögzített lapátolás után értesülnek róla, mit alkottunk, valahogy így:

Balazs Zalán [Teszt011]	4 órát lapátolt
2 órája

Merre tovább?

Merre lehetne még továbbfejleszteni Sztahanovunkat?

  • aszinkron kérések hibakezelése
  • látványos, dizájnos felület
  • view-függő megjelenés
  • owner/viewer függő megjelenés
  • bővebb gadget-metaadatok
  • lapozható ismerős-lista

Ezeket ezen az oldalon nem tárgyaljuk, de majd' mindre található példa a Sztahanov alkalmazás teljes, vágatlan verziójában.

Személyes eszközök