/** * Copyright 2008 Google Inc. * Copyright 2008 IGN Entertainment Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @fileoverview This file implements a google gears backed container. It's a port of * in memory mock container provided by google. This container is useful for * development, prototyping and testing. The advantage of using google gears backed * container over mock container is its ability to show shared data between two browser * windows which is not possible in case of mock container. * */ var happyhour = function() {}; /** * Implements the opensocial.Container apis by using google gears as persistence store * * @param appId application id. * @param instanceId application id. * @param viewerId Person object that corresponds to the viewer. * @param ownerId Person object that corresponds to the owner. * @constructor */ //assuming google gears opensocial.HappyHourContainer = function(appId, instanceId, viewerId, ownerId, bootstrap) { this.viewerId = viewerId; this.ownerId = ownerId; this.instanceId = instanceId; this.appId = appId; var db = google.gears.factory.create('beta.database', '1.0'); db.open('HappyHour-0.7'); this.db = db; db.execute('begin'); try { this.initSchema(db); if (bootstrap) { bootstrap(db); } this.loadOwner(); this.loadViewer(); db.execute('commit'); } catch(ex) { db.execute('rollback'); throw ex; } //TODO uncomment this opensocial.Container.call(this); }; opensocial.HappyHourContainer.inherits(opensocial.Container); /** * @private */ happyhour.Query = function(db, q, params) { this.db = db; this.q = q; this.params = params; }; /** * @private */ happyhour.Query.Constants = {BREAK: 1}; /** * @private */ happyhour.Query.prototype.scalar = function(defaultValue) { var rs = this.db.execute(this.q, this.params); try { return rs.isValidRow() ? rs.field(0) : defaultValue; } finally { rs.close(); } }; /** * @private */ happyhour.Query.prototype.singleton = function(cursorCallback, defaultValue) { var rs = this.db.execute(this.q, this.params); try { return rs.isValidRow() ? cursorCallback(rs) : defaultValue; } finally { rs.close(); } }; /** * @private */ happyhour.Query.prototype.map = function(cursorCallback) { var rs = this.db.execute(this.q, this.params); try { while(rs.isValidRow()) { if(cursorCallback(rs) == happyhour.Query.Constants.BREAK) { break; } rs.next(); } } finally { rs.close(); } }; /** * @private */ happyhour.query = function(db, q, params) { return new happyhour.Query(db, q, params); }; /** * @private */ opensocial.HappyHourContainer.prototype.initSchema = function(db) { db.execute( "CREATE TABLE if not exists [person] (\n" + "[id] integer not null, \n" + "[name] text, \n" + "[thumbnail_url] text, \n" + "[profile_url] text,\n" + "[dob] timestamp,\n" + "[gender] text, \n" + "[email] text, \n" + "[extended_properties] text,\n" + "[last_updated_on] timestamp,\n" + "[created_on] timestamp DEFAULT CURRENT_TIMESTAMP not null,\n" + "primary key([id])\n" + " check ([gender] in ('M', 'F'))\n" + ")" ); db.execute( "CREATE TABLE if not exists [activity] (\n" + " [id] integer not null, \n" + " [app_id] integer NOT NULL,\n" + " [user_id] integer not null,\n" + " [stream_title] text not null,\n" + " [stream_favicon_url] text,\n" + " [stream_source_url] text,\n" + " [stream_url] text,\n" + " [title] text not null,\n" + " [summary] text,\n" + " [body] text,\n" + " [external_id] text,\n" + " [url] text,\n" + " [posted_time] timestamp,\n" + " [extended_properties] text,\n" + " [created_on] timestamp DEFAULT CURRENT_TIMESTAMP not null,\n" + " [priority] text,\n" + " primary key([id])\n" + " check ([priority] in ('HIGH', 'LOW'))\n" + ")" ); db.execute( "CREATE INDEX IF NOT EXISTS [activity_stream_index] on [activity]([app_id], [user_id], [stream_title])" ); db.execute( "CREATE TABLE if not exists [activity_media_items] (\n" + " [id] integer not null,\n" + " [activity_id] integer not null,\n" + " [mime_type] text not null, \n" + " [type] text not null, \n" + " [url] text url not null,\n" + " [extended_properties] text,\n" + " primary key([id]),\n" + " check ([type] in ('audio', 'image', 'video')) \n" + ")" ); db.execute( "CREATE TABLE if not exists [friends] (\n" + "[person_id] integer NOT NULL,\n" + "[friend_id] integer NOT NULL,\n" + "PRIMARY KEY ([person_id],[friend_id])\n" + ")" ); db.execute( "CREATE TABLE if not exists [global_app_data] (\n" + "[app_id] integer NOT NULL,\n" + "[key_name] text NOT NULL,\n" + "[value] NULL,\n" + "PRIMARY KEY ([app_id],[key_name])\n" + ")" ); db.execute( "CREATE TABLE if not exists [instance_app_data] (\n" + "[app_id] integer NOT NULL,\n" + "[instance_id] integer NOT NULL,\n" + "[key_name] text NOT NULL,\n" + "[value] NULL,\n" + "PRIMARY KEY ([app_id],[instance_id],[key_name])\n" + ")" ); db.execute( "CREATE TABLE if not exists [person_app_data] (\n" + "[app_id] integer NOT NULL,\n" + "[person_id] integer NOT NULL,\n" + "[key_name] text NOT NULL,\n" + "[value] NULL,\n" + "PRIMARY KEY ([app_id],[person_id],[key_name])\n" + ")" ); db.execute( "CREATE TRIGGER if not exists [friends_del_trg] \n" + "AFTER DELETE ON [friends] \n" + "FOR EACH ROW \n" + "BEGIN \n" + "delete from friends where\n" + "person_id = old.friend_id\n" + "and\n" + "friend_id = old.person_id;\n" + "END" ); db.execute( "CREATE TRIGGER if not exists [friends_ins_trg] \n" + "AFTER INSERT ON [friends] \n" + "FOR EACH ROW \n" + "BEGIN \n" + "insert or ignore into friends (person_id, friend_id)\n" + " values (new.friend_id, new.person_id);\n" + "END" ); db.execute( "CREATE TRIGGER if not exists [friends_upd_trg] \n" + "AFTER UPDATE ON [friends] \n" + "FOR EACH ROW WHEN OLD.PERSON_ID <> NEW.PERSON_ID OR OLD.FRIEND_ID <> NEW.FRIEND_ID \n" + "BEGIN \n" + "update friends set\n" + "person_id = new.friend_id,\n" + "friend_id = new.person_id\n" + "where\n" + "person_id = old.friend_id\n" + "and\n" + "friend_id = old.person_id;\n" + "END" ); }; /** * @private */ opensocial.HappyHourContainer.prototype.loadOwner = function() { this.owner = this.getPerson(this.ownerId) || this.createOwner(); }; /** * @private */ opensocial.HappyHourContainer.prototype.loadViewer = function() { this.viewer = this.getPerson(this.viewerId) || this.createViewer(); }; /** * @private */ opensocial.HappyHourContainer.prototype.createOwner = function() { var db = this.db; db.execute("insert or ignore into person (id, name, email) values (?, ?, ?)", [this.ownerId || null, 'Friendly Host' , 'friendly.host@example.com']); this.ownerId = db.lastInsertRowId; return this.getPerson(this.ownerId); }; /** * @private */ opensocial.HappyHourContainer.prototype.createViewer = function() { var db = this.db; db.execute("insert or ignore into person (id, name, email) values (?, ?, ?)", [this.viewerId || null, 'Honerable Guest' , 'honerable.guest@example.com']); this.viewerId = db.lastInsertRowId; return this.getPerson(this.viewerId); }; /** * @private */ opensocial.HappyHourContainer.PERSON_COLUMNS = " id, name, thumbnail_url, profile_url, dob, gender, email, extended_properties "; /** * @private */ opensocial.HappyHourContainer.prototype.getPerson = function(personId) { return happyhour.query(this.db, "select "+ opensocial.HappyHourContainer.PERSON_COLUMNS + " from person where id = ?", [personId]).singleton(this.materializePerson); }; /** * @private */ opensocial.HappyHourContainer.prototype.getFriends = function(personId) { var db = this.db; //unfortunately bind parameter doesn't work with nullif //var rs = db.execute("select id, name, email, thumbnail_url, profile_url from person where id in(select ifnull(nullif(person_id, "+personId+"), friend_id) from friends where person_id = ? or friend_id = ?)", [personId, personId]); //but works with case /* create unique index friends_index on friends( min(person_id, friend_id), max(person_id, friend_id) ); we would like to enforce constraints on friends to avoid symmetric relationship since sqlite doesn't support function based indexes we can only do (person_id, friend_id) as pk and triggers make sure that pair exits. so to find friends we don't run this query anymore (select (case person_id when ? then friend_id else person_id end) from friends where person_id = ? or friend_id = ?) */ var q = "select "+ opensocial.HappyHourContainer.PERSON_COLUMNS +" from person where id in" + "(select friend_id from friends where person_id = ?)"; var params = [personId]; var me = this; var a = new Array(); happyhour.query(this.db, q, params).map(function(rs) { a.push(me.materializePerson(rs)); }); return a; }; /** * */ opensocial.HappyHourContainer.prototype.requestCreateActivity = function(activity, priority, opt_callback) { var db = this.db; // Permissioning is not being handled in the sample container. All real // containers should check for user permission before posting activities. var userId = this.viewer.getId(); var appId = this.appId; db.execute('begin'); try { activity.setField(opensocial.Activity.Field.USER_ID, userId); activity.setField(opensocial.Activity.Field.APP_ID, this.appId); if (activity.getField(opensocial.Activity.Field.POSTED_TIME) == null) { activity.setField(opensocial.Activity.Field.POSTED_TIME, new Date()); } var postedTime = this.toISO8601String(activity.getField(opensocial.Activity.Field.POSTED_TIME)); db.execute("INSERT into activity (id, app_id, user_id, stream_title, stream_favicon_url, stream_source_url, stream_url, \n"+ " title, summary, body, external_id, url, posted_time, extended_properties, priority " + ") VALUES (null, ?, ?, ?, ?, ?, ?,\n"+ " ?, ?, ?, ?, ?, ?, ?, ?)", [appId, userId, activity.getField(opensocial.Activity.Field.STREAM_TITLE) || null, activity.getField(opensocial.Activity.Field.STREAM_FAVICON_URL) || null, activity.getField(opensocial.Activity.Field.STREAM_SOURCE_URL) || null, activity.getField(opensocial.Activity.Field.STREAM_URL) || null, activity.getField(opensocial.Activity.Field.TITLE) || null, activity.getField(opensocial.Activity.Field.SUMMARY) || null, activity.getField(opensocial.Activity.Field.BODY) || null, activity.getField(opensocial.Activity.Field.EXTERNAL_ID) || null, activity.getField(opensocial.Activity.Field.URL) || null, postedTime, null, priority ]); var activityId = db.lastInsertRowId; activity.setField(opensocial.Activity.Field.ID, activityId); var mediaItems = activity.getField(opensocial.Activity.Field.MEDIA_ITEMS); if (mediaItems) { for (var i = 0; i < mediaItems.length; i++) { var mediaItem = mediaItems[i]; db.execute("INSERT into activity_media_items (id, activity_id, mime_type, type, url, extended_properties" + ") VALUES (null, ?, ?, ?, ?, ?)", [activityId, mediaItem.getField(opensocial.Activity.MediaItem.Field.MIME_TYPE) || null, mediaItem.getField(opensocial.Activity.MediaItem.Field.TYPE) || null, mediaItem.getField(opensocial.Activity.MediaItem.Field.URL) || null, null ]); } } db.execute('commit'); } catch(ex) { alert("Error while creating activity "+ex.message); db.execute('rollback'); throw ex; } if (opt_callback) { opt_callback(); } }; /** * */ opensocial.HappyHourContainer.prototype.newActivityMediaItem = function(mimeType, url, opt_params) { if(opt_params && opt_params.type) { return new opensocial.Activity.MediaItem(mimeType, url, opt_params); } else { var m = new opensocial.Activity.MediaItem(mimeType, url, opt_params); m.setField(opensocial.Activity.MediaItem.Field.TYPE, mimeType); return m; } }; /** * @private */ opensocial.HappyHourContainer.isArray = function(object) { return object && object.constructor === Array; }; /** * @private */ opensocial.HappyHourContainer.isString = function(object) { return object && object.constructor === String; }; /** * @private */ opensocial.HappyHourContainer.keyNameClause = function(keys) { var keyClause = null; if (opensocial.HappyHourContainer.isArray(keys)) { if (keys.length == 1 && "*" != keys[0]) { keyClause = {sql: " key_name = ? ", params: keys}; } else { keyClause = {sql: " key_name in (" + keys.join("?") + ") ", params: keys}; } } if (opensocial.HappyHourContainer.isString(keys) && "*" != keys) { keyClause = {sql: " key_name = ? ", params: [keys]}; } return keyClause || {sql: " ", params: []}; }; /** * This method returns the data requested about the viewer and his/her friends. * * To keep this simple (for now), the PeopleRequestFields values such as sort * order, filter, pagination, etc. specified in the DataRequest are ignored and * all requested data is returned in a single call back. * * @param {Object} dataRequest The object that specifies the data requested. * @param {Function} callback The callback method on completion. */ opensocial.HappyHourContainer.prototype.requestData = function(dataRequest, callback) { var requestObjects = dataRequest.getRequestObjects(); var dataResponseValues = {}; var globalError = false; for (var requestNum = 0; requestNum < requestObjects.length; requestNum++) { var request = requestObjects[requestNum].request; var requestName = requestObjects[requestNum].key; var requestedValue; var hadError = false; switch (request.type) { case 'FETCH_PERSON' : var personId = request.id; if (personId == opensocial.DataRequest.PersonId.VIEWER) { requestedValue = this.viewer; } else if (personId == opensocial.DataRequest.PersonId.OWNER) { requestedValue = this.owner; } else { requestedValue = this.getPerson(personId); } break; case 'FETCH_PEOPLE' : var idSpec = request.idSpec; var persons = []; if (idSpec == opensocial.DataRequest.Group.VIEWER_FRIENDS) { persons = this.getFriends(this.viewer.getId()); } else if (idSpec == opensocial.DataRequest.Group.OWNER_FRIENDS) { persons = this.getFriends(this.owner.getId()); } else { if (!opensocial.Container.isArray(idSpec)) { idSpec = [idSpec]; } for (var i = 0; i < idSpec.length; i++) { var person = this.getPerson(idSpec[i]); if (person != null) { persons.push(person); } } } requestedValue = new opensocial.Collection(persons); break; case 'FETCH_GLOBAL_APP_DATA' : var keyClause = opensocial.HappyHourContainer.keyNameClause(request.keys); var q = "select key_name, value from global_app_data where " + keyClause.sql + " and app_id = ? "; var values = {}; happyhour.query(this.db, q, keyClause.params.concat([this.appId])).map(function(rs) { values[rs.field(0)] = rs.field(1); }); requestedValue = values; break; case 'FETCH_INSTANCE_APP_DATA' : var keyClause = opensocial.HappyHourContainer.keyNameClause(request.keys); var q = "select key_name, value from instance_app_data where " + keyClause.sql + " and app_id = ? and instance_id = ?"; var values = {}; happyhour.query(this.db, q, keyClause.params.concat([this.appId, this.instanceId])).map( function(rs) { values[rs.field(0)] = rs.field(1); } ); break; case 'UPDATE_INSTANCE_APP_DATA' : this.db.execute("insert or replace into instance_app_data (app_id, instance_id, key_name, value) values (?, ?, ?,?)", [this.appId, this.instanceId, request.key, request.value]); break; case 'FETCH_PERSON_APP_DATA' : var keyClause = opensocial.HappyHourContainer.keyNameClause(request.keys); var q = "select person_id, key_name, value from person_app_data where " + keyClause.sql + " and app_id = ?"; var rs = null; switch (request.idSpec) { case opensocial.DataRequest.PersonId.VIEWER: q += " and person_id = ?" rs = this.db.execute(q, keyClause.params.concat([this.appId, this.viewerId])); break; case opensocial.DataRequest.PersonId.OWNER: q += " and person_id = ?" rs = this.db.execute(q, keyClause.params.concat([this.appId, this.ownerId])); break; case opensocial.DataRequest.Group.VIEWER_FRIENDS: q += " and person_id in " + "(select friend_id from friends where person_id = ?)"; rs = this.db.execute(q, keyClause.params.concat([this.appId, this.viewerId])); break; case opensocial.DataRequest.Group.OWNER_FRIENDS: q += " and person_id in " + "(select friend_id from friends where person_id = ?)"; rs = this.db.execute(q, keyClause.params.concat([this.appId, this.ownerId])); break; } if (rs) { try { var values = {}; while (rs.isValidRow()) { var id = rs.field(0); if (values[id]) { values[id][rs.field(1)] = rs.field(2); } else { values[id] = {}; values[id][rs.field(1)] = rs.field(2); } rs.next(); } requestedValue = values; } finally { rs.close(); } } else { requestedValue = {}; } break; case 'UPDATE_PERSON_APP_DATA': var userId = request.id; // Gadgets can only edit viewer data if (userId == opensocial.DataRequest.PersonId.VIEWER || userId == this.viewer.getId()) { userId = this.viewer.getId(); this.db.execute("insert or replace into person_app_data (app_id, person_id, key_name, value) values (?, ?, ?,?)", [this.appId, userId, request.key, request.value]); } else { hadError = true; } break; case 'FETCH_ACTIVITIES' : var q = "select\n" + "a.app_id, a.user_id, a.stream_title,\n" + "a.stream_favicon_url, a.stream_source_url, a.stream_url,\n" + "a.id activity_id, a.title activity_title, a.summary activity_summary, \n" + "a.body activity_body, a.external_id activity_external_id,\n" + "a.url activity_url, ((strftime('%s', a.posted_time) - strftime('%S', a.posted_time)) *1000 + strftime('%f', a.posted_time)*1000) activity_posted_time, a.extended_properties activity_extended_properties,\n" + "m.id media_id, m.mime_type media_mime_type, m.type media_type, m.url media_url, m.extended_properties media_extended_properties\n" + "from \n" + "activity a \n" + "left outer join activity_media_items m on m.activity_id = a.id\n" + "where a.app_id = ? "; var rs = null; switch (request.idSpec) { case opensocial.DataRequest.PersonId.VIEWER: q += " and a.user_id = ? order by a.id, m.id" rs = this.db.execute(q, [this.appId, this.viewerId]); break; case opensocial.DataRequest.PersonId.OWNER: q += " and a.user_id = ? order by a.id, m.id" rs = this.db.execute(q, [this.appId, this.ownerId]); break; case opensocial.DataRequest.Group.VIEWER_FRIENDS: q += " and a.user_id in " + "(select friend_id from friends where person_id = ?) order by a.id, m.id"; rs = this.db.execute(q, [this.appId, this.viewerId]); break; case opensocial.DataRequest.Group.OWNER_FRIENDS: q += " and a.user_id in " + "(select friend_id from friends where person_id = ?) order by a.id, m.id"; rs = this.db.execute(q, [this.appId, this.ownerId]); break; } if (rs) { try { var activity = null; var mediaItems = null; var values = {}; var requestedActivities = []; while (rs.isValidRow()) { if (activity == null || activity.getId() != rs.fieldByName("activity_id")) { if (activity != null && mediaItems != null) { activity.setField(opensocial.Activity.Field.MEDIA_ITEMS, mediaItems); mediaItems = null; } activity = this.materializeActivity(rs); requestedActivities.push(activity); } if (rs.fieldByName("media_id")) { var media = this.materializeMedia(rs); if (mediaItems == null) { mediaItems = [media]; } else { mediaItems.push(media); } } rs.next(); } if (activity != null && mediaItems != null) { activity.setField(opensocial.Activity.Field.MEDIA_ITEMS, mediaItems); mediaItems = null; } requestedValue = { // Real containers should set the requested stream here 'activities' : new opensocial.Collection(requestedActivities)}; } finally { rs.close(); } } else { requestedValue = {}; } break; default: break; } dataResponseValues[requestName] = new opensocial.ResponseItem(request, requestedValue, hadError); globalError = globalError || hadError; } callback(new opensocial.DataResponse(dataResponseValues, globalError)); }; /** *@private */ opensocial.HappyHourContainer.prototype.materializePerson = function(rs) { var personId = rs.field(0); var nameParms = {}; nameParms[opensocial.Name.Field.UNSTRUCTURED] = rs.field(1); var name = new opensocial.Name(nameParms); var fields = { 'id' : personId, 'name' : name, 'thumbnailUrl' : rs.field(2), 'profileUrl' : rs.field(3), 'dateOfBirth' : rs.field(4), 'gender' : rs.field(5), 'email' : rs.field(6) }; var xps = rs.field(7); if(xps) { var extendedProperties = eval(xps); for(var key in extendedProperties) { fields[key] = extendedProperties[key]; } } return new opensocial.Person(fields, personId == this.viewerId, personId == this.ownerId); }; /** *@private */ opensocial.HappyHourContainer.prototype.materializeActivity = function(rs) { var activity = opensocial.newActivity({}); activity.setField(opensocial.Activity.Field.TITLE, rs.fieldByName("activity_title")); activity.setField(opensocial.Activity.Field.ID, rs.fieldByName("activity_id")); activity.setField(opensocial.Activity.Field.APP_ID, rs.fieldByName("app_id")); activity.setField(opensocial.Activity.Field.USER_ID, rs.fieldByName("user_id")); activity.setField(opensocial.Activity.Field.SUMMARY, rs.fieldByName("activity_summary")); activity.setField(opensocial.Activity.Field.BODY, rs.fieldByName("activity_body")); activity.setField(opensocial.Activity.Field.EXTERNAL_ID, rs.fieldByName("activity_external_id")); activity.setField(opensocial.Activity.Field.URL, rs.fieldByName("activity_url")); activity.setField(opensocial.Activity.Field.POSTED_TIME, new Date(rs.fieldByName("activity_posted_time"))); activity.setField(opensocial.Activity.Field.STREAM_TITLE, rs.fieldByName("stream_title")); activity.setField(opensocial.Activity.Field.STREAM_FAVICON_URL, rs.fieldByName("stream_favicon_url")); activity.setField(opensocial.Activity.Field.STREAM_SOURCE_URL, rs.fieldByName("stream_source_url")); activity.setField(opensocial.Activity.Field.STREAM_URL, rs.fieldByName("stream_source_url")); return activity; }; /** * @private */ opensocial.HappyHourContainer.prototype.materializeMedia = function(rs) { var opt_params = { 'id' : rs.fieldByName("media_id"), 'type' : rs.fieldByName("media_type") }; var item = opensocial.newActivityMediaItem(rs.fieldByName("media_mime_type"), rs.fieldByName("media_url"), opt_params) return(item); }; /** * @private */ opensocial.HappyHourContainer.prototype.toISO8601String = function (date) { var zeropad = function (num) { return ((num < 10) ? '0' : '') + num; } var str = ""; str += date.getUTCFullYear(); str += "-" + zeropad(date.getUTCMonth() + 1); str += "-" + zeropad(date.getUTCDate()); str += "T" + zeropad(date.getUTCHours()) + ":" + zeropad(date.getUTCMinutes())+ ":" + zeropad(date.getUTCSeconds()); var ms = date.getUTCMilliseconds(); str += "."+(ms < 10 ? "00"+ms : (ms < 100 ? "0"+ms : ms)); return str; }; /** * Request a profile for the specified person id. * When processed, returns a Person object. * * @param {String} id The id of the person to fetch. Can also be standard * person IDs of VIEWER and OWNER. * @param {Map<opensocial.DataRequest.PeopleRequestFields, Object>} * opt_params Additional params to pass to the request. This request supports * PROFILE_DETAILS. * @return {Object} a request object */ opensocial.HappyHourContainer.prototype.newFetchPersonRequest = function(id, opt_params) { return {'type' : 'FETCH_PERSON', 'id' : id}; }; /** * Used to request friends from the server, optionally joined with app data * and activity stream data. * When processed, returns a Collection<Person> object. * * @param {Array<String> or String} idSpec An id, array of ids, or a group * reference used to specify which people to fetch * @param {Map<opensocial.DataRequest.PeopleRequestFields, Object>} * opt_params Additional params to pass to the request. This request supports * PROFILE_DETAILS, SORT_ORDER, FILTER, FIRST, and MAX. * @return {Object} a request object */ opensocial.HappyHourContainer.prototype.newFetchPeopleRequest = function(idSpec, opt_params) { return {'type' : 'FETCH_PEOPLE', 'idSpec' : idSpec}; }; /** * Used to request global app data. * When processed, returns a Map<String, String> object. * * @param {Array<String>|String} keys The keys you want data for. This * can be an array of key names, a single key name, or "*" to mean * "all keys". * @return {Object} a request object */ opensocial.HappyHourContainer.prototype.newFetchGlobalAppDataRequest = function(keys) { return { 'type' : 'FETCH_GLOBAL_APP_DATA', 'keys' : keys }; }; /** * Used to request instance app data. * When processed, returns a Map<String, String> object. * * @param {Array<String>|String} keys The keys you want data for. This * can be an array of key names, a single key name, or "*" to mean * "all keys". * @return {Object} a request object */ opensocial.HappyHourContainer.prototype.newFetchInstanceAppDataRequest = function(keys) { return { 'type' : 'FETCH_INSTANCE_APP_DATA', 'keys' : keys }; }; /** * Used to request an update of an app instance field from the server. * When processed, does not return any data. * * @param {String} key The name of the key * @param {String} value The value * @return {Object} a request object */ opensocial.HappyHourContainer.prototype.newUpdateInstanceAppDataRequest = function(key, value) { return { 'type' : 'UPDATE_INSTANCE_APP_DATA', 'key' : key, 'value' : value }; }; /** * Used to request app data for the given people. * When processed, returns a Map<person id, Map<String, String>> * object. * * @param {Array<String> or String} idSpec An id, array of ids, or a group * reference. (Right now the supported keys are VIEWER, OWNER, * OWNER_FRIENDS, or a single id within one of those groups) * @param {Array<String>|String} keys The keys you want data for. This * can be an array of key names, a single key name, or "*" to mean * "all keys". * @return {Object} a request object */ opensocial.HappyHourContainer.prototype.newFetchPersonAppDataRequest = function(idSpec, keys) { return { 'type' : 'FETCH_PERSON_APP_DATA', 'idSpec' : idSpec, 'keys' : keys }; }; /** * Used to request an update of an app field for the given person. * When processed, does not return any data. * * @param {String} id The id of the person to update. (Right now only the * special VIEWER id is allowed.) * @param {String} key The name of the key * @param {String} value The value * @return {Object} a request object */ opensocial.HappyHourContainer.prototype.newUpdatePersonAppDataRequest = function(id, key, value) { return { 'type' : 'UPDATE_PERSON_APP_DATA', 'id' : id, 'key' : key, 'value' : value }; }; /** * Used to request an activity stream from the server. * Note: Although both app and folder are optional, you can not just provide a * folder. * When processed, returns an object where "activities" is a * Collection<Activity> object and "requestedStream" is a Stream object * representing the stream you fetched. (Note: this may or may not be different * that the streams that each activity belongs to) * * @param {Array<String> or String} idSpec An id, array of ids, or a group * reference to fetch activities for * @param {Map<opensocial.DataRequest.ActivityRequestFields, Object>} * opt_params Additional params to pass to the request. * @return {Object} a request object */ opensocial.HappyHourContainer.prototype.newFetchActivitiesRequest = function(idSpec, opt_params) { return {'type' : 'FETCH_ACTIVITIES', 'idSpec' : idSpec}; }; /** * @return {opensocial.Environment} returns the current container's Environment */ opensocial.HappyHourContainer.prototype.getEnvironment = function() { var supportedFields = { 'person' : { 'id' : true, 'name' : true, 'thumbnailUrl' : true, 'profileUrl' : true, 'gender' : true, 'dateOfBirth' : true, 'email' : true }, 'activity' : { 'id' : true, 'externalId' : true, 'userId' : true, 'appId' : true, 'streamTitle' : true, 'streamUrl' : true, 'streamSourceUrl' : true, 'streamFaviconUrl' : true, 'title' : true, 'body' : true, 'url' : true, 'mediaItems' : true, 'postedTime' : true, 'customValues' : false }, 'activityMediaItem' : { 'type' : true, 'mimeType' : true, 'url' : true } }; var host = 'google.com'; try { host = window.location.host; } catch (e){} var environment = this.newEnvironment(host, supportedFields); // In a real container this environment will probably be static return (environment); }; /** * @return {boolean} true, if the current gadget has access to the specified permission. */ opensocial.HappyHourContainer.prototype.hasPermission = function(permission) { // The sample container always grants access to the viewer object return permission == opensocial.Permission.VIEWER; }; /** * */ opensocial.HappyHourContainer.prototype.requestPermission = function(permissions, reason, opt_callback) { // TODO(doll): We should probably add a permission restriction to the sample // container just for the sake of providing a reference implementation. if (opt_callback) { opt_callback(); } }; /** * */ opensocial.HappyHourContainer.prototype.requestShareApp = function(recipients, reason, opt_callback) { var resp = new opensocial.ResponseItem(); if (opt_callback) { opt_callback(resp); } };