Building OpenSocial Apps
Building OpenSocial Apps A Field Guide to Working with the MySpace Platform Chris Cole Chad Russell Jessica Whyte
Upper Saddle River, NJ • Boston • Indianapolis • San Francisco New York • Toronto • Montreal • London • Munich • Paris • Madrid Capetown • Sydney • Tokyo • Singapore • Mexico City
Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and the publisher was aware of a trademark claim, the designations have been printed with initial capital letters or in all capitals.
Editor-in-Chief Mark L. Taub
The screenshots and other depictions of myspace.com contained in this book may not accurately represent myspace.com as it exists today or in the future, including without limitation with respect to any policies, technical specs or product design.
Development Editor Songlin Qiu
The authors and publisher have taken care in the preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein.
Managing Editor John Fuller
The publisher offers excellent discounts on this book when ordered in quantity for bulk purchases or special sales, which may include electronic versions and/or custom covers and content particular to your business, training goals, marketing focus, and branding interests. For more information, please contact: U.S. Corporate and Government Sales (800) 382-3419
[email protected] Full-Service Production Manager Julie B. Nahil Project Management diacriTech LLC Copy Editor Barbara Wood Indexer Jack Lewis
For sales outside the United States, please contact: International Sales
[email protected] Proofreader George Seki
Visit us on the Web: informit.com/aw Library of Congress Cataloging-in-Publication Data Cole, Chris, 1974Building OpenSocial apps : a field guide to working with the MySpace platform/Chris Cole, Chad Russell, Jessica Whyte. p. cm. Includes bibliographical references and index. ISBN-13: 978-0-321-61906-8 (pbk. : alk. paper) ISBN-10: 0-321-61906-4 (pbk. : alk. paper) 1. Entertainment computing. 2. Internet programming. 3. MySpace.com. 4. OpenSocial. 5. Web site development. 6. Social networks—Computer network resources. 7. Application program interfaces (Computer software) I. Russell, Chad. II. Whyte, Jessica. III. Title. QA76.9.E57C65 2010 006.7'54—dc22 2009032342 Copyright © 2010 Pearson Education, Inc. All rights reserved. Printed in the United States of America. This publication is protected by copyright, and permission must be obtained from the publisher prior to any prohibited reproduction, storage in a retrieval system, or transmission in any form or by any means, electronic, mechanical, photocopying, recording, or likewise. For information regarding permissions, write to: Pearson Education, Inc Rights and Contracts Department 501 Boylston Street, Suite 900 Boston, MA 02116 Fax: (617) 671-3447 ISBN-13: 978-0-321-61906-8 ISBN-10: 0-321-61906-4 Text printed in the United States on recycled paper at RR Donnelley in Crawfordsville, Indiana. First printing, October 2009
Acquisitions Editor Trina MacDonald
Technical Reviewers Cassie Doll Bess Ho Benjamin Schupak Book Designer Gary Adair Compositor diacriTech LLC
❖
This book is dedicated to my ever-suffering wife, Kristen, and our two crazy and wonderful children, Darien and Reece.Thanks for working overtime with the kids, baby. —Chris Cole To the reader, we hope this book serves you well. —Chad Russell and Jessica Whyte ❖
Contents at a Glance Contents
viii
Foreword
xvi
Acknowledgments
xviii
About the Authors
xix
Introduction
xxi
I: Building Your First MySpace Application 1 Your First MySpace App
3
2 Getting Basic MySpace Data
9
3 Getting Additional MySpace Data 4 Persisting Information
29
47
5 Communication and Viral Features
67
6 Mashups and External Server Communications 91 7 Flushing and Fleshing: Expanding Your
App and Person-to-Person Game Play
117
II: Other Ways to Build Apps 8 OAuth and Phoning Home 9 External Iframe Apps
153
177
10 OSML, Gadgets, and the Data Pipeline
213
11 Advanced OSML: Templates, Internationalization, and View Navigation 239
III: Growth and How to Deal with It 12 App Life Cycle
265
13 Performance, Scaling, and Security
283
Contents at a Glance
14 Marketing and Monetizing
305
15 Porting Your App to OpenSocial 0.9
References Index
355
351
329
vii
Contents Foreword xvi Acknowledgments About the Authors Introduction xxi
xviii xix
I: Building Your First MySpace Application 1 Your First MySpace App
3
Creating the App—“Hello World”
3
Step 1: Sign Up for a Developer Account Step 2: Create an App
Step 3: Enter Your Source Code Installing and Running Your App Summary
3
4 4
7
7
2 Getting Basic MySpace Data
9
The Two Concepts That Every Developer Should Know 9 Basic Concepts: Owner and Viewer
9
Basic Concepts: Permissions for Accessing MySpace Data 10 Starting Our Tic-Tac-Toe App
10
Accessing MySpace User Data
11
Accessing Profile Information Using the opensocial.Person Object 15 Getting More than Just the Default Profile Data
18
opensocial.DataResponse and opensocial. ResponseItem (aka, Using MySpace User Data)
19
Error Handling Summary
24
27
3 Getting Additional MySpace Data
29
How to Fetch a Friend List and Make Use of the Data 29 Getting the Friend List Filters and Sorts
31
30
Contents
Paging
32
Using the Data Fetching Media Photos
37
39
39
Albums and Videos
41
Using opensocial.requestPermission and opensocial.hasPermission to Check a User’s Permission Settings 43 Summary
45
4 Persisting Information App Data Store
47
47
Saving and Retrieving Data
48
Refactoring to Build a Local App Data Store Cookies
56
Why You Shouldn’t Use Cookies
57
Building the Cookie Jacker App Third-Party Database Storage Summary
51
59
64
65
5 Communication and Viral Features
67
Using opensocial.requestShareApp to Spread Your App to Other Users 67 Defining requestShareApp
70
Writing the requestShareApp Code Calling requestShareApp
71
72
The requestShareApp Callback
72
Using opensocial.requestSendMessage to Send Messages and Communications 74 Defining requestSendMessage
75
Writing the requestSendMessage Code Callback in requestSendMessage
76
78
Getting Your App Listed on the Friend Updates with opensocial.requestCreateActivity Basics 79 Defining opensocial.requestCreateActivity
79
Using the Template System to Create Activities Data Types
80
Reserved Variable Names Aggregation
82
81
80
ix
x
Contents
Body and Media Items
82
Using the Template Editor to Create Templates Using opensocial.requestCreateActivity Sending Notifications Summary
88
90
6 Mashups and External Server Communications Communicating with External Servers Mashups
91
92
Adding a Feed Reader to Our App
93
Overview of gadgets.io.makeRequest Response Structure
94
96
Handling JSON Content
97
Handling Partial HTML Content Handling RSS Feed Content Handling XML Content
97
97
98
“User’s Pick” Feed Reader
98
Setup and Design of the Feed Reader FEED Content Type
98
104
XML Content Type with Parsing TEXT Content Type
105
107
Adding a Feed Refresh Option Feed Automation Candy
109
110
Secure Communication with Signed makeRequest 111 Adding an Image Search Overview of JSONP
112 112
Implementing the Image Search Posting Data with a Form Summary
83
85
113
114
114
7 Flushing and Fleshing: Expanding Your App and Person-to-Person Game Play 117 Turn-Based Games Design Overview
117 118
Adding FriendPicker App Data Game Store
119 125
Supporting Person-to-Person Game Play
133
91
Contents
Adding P2P Game Play Support in the Game Engine 133 Adding User Feedback
135
Fleshing Out P2P Game Logic
138
Finishing and Clearing a Game “Real-Time” Play
144
146
Advantages and Disadvantages of App Data P2P Play 148 Summary
148
II: Other Ways to Build Apps 8 OAuth and Phoning Home What Is OAuth?
153
153
OAuth Libraries
154
Setting Up Your Environment When Is OAuth Not OAuth? Secure Phone Home
154 157
157
Unsigned GET Request Signed POST Request
158 162
Testing Your OAuth Implementation Locally Making Real MySpace Requests
166
169
Spicing Up the Home and Profile Surfaces Using makeRequest 173 Summary
174
9 External Iframe Apps REST APIs
177
178
How a REST Web Service Is Addressed Setting Up an External Iframe App The Server Code REST API List The Client Code
181
183 197
Friends Web Service and Paging The Profile Endpoint
199
203
Sending Messages Using IFPC
208
Using the 0.7 Container for postTo
210
The Friends Response from the REST API Summary
212
178
179
211
xi
xii
Contents
10 OSML, Gadgets, and the Data Pipeline The Big Picture
213
Gadget XML
214
Data Pipeline OSML
213
214
214
Writing a Gadget
214
“Hello World” Gadget
214
Adding a Second Surface to the Gadget Declaring and Using Basic Data Data Pipelining
219
DataContext
220
Data Tags
217
218
220
In-Network versus Out-of-Network Data
221
Data Tags os:ViewerRequest and os:OwnerRequest 222 Data Tag os:PeopleRequest
222
Data Tag os:ActivitiesRequest Data Tag os:DataRequest
223
223
JavaScript Blocks in OSML Apps
225
OpenSocial Markup Language (OSML) Basic Display Tags
Remote Content Display Tags Control Flow Tags
226
226
Putting It Together: OSML Tic-Tac-Toe Setting Up the Gadget
230
235
Displaying Data Lists Summary
226
227
Reusing Common Content Working with Data
225
226
237
238
11 Advanced OSML: Templates, Internationalization, and View Navigation 239 Inline Tag Templates
239
Defining and Using a Tag Template Using Client-Side Templates Working with Subviews
242
245
Converting Tabs to Subviews
245
240
Contents
HTML Fragment Rendering
248
Adding Content with os:Get
248
Adding Targeted Content with myspace:RenderRequest 249 Data Listeners
250
Displaying JSON Results with a Data Listener Internationalization and Message Bundles Creating Our First Message Bundle
256
Creating Translations of the Message Bundle Including Translations in an App and Testing Future Directions Summary
260
261
III: Growth and How to Deal with It 12 App Life Cycle
265
Publishing Your App
265
What’s Allowed, or Why So Many Apps Get Rejected 266 Dealing with Rejection
267
Contesting a Rejection
267
Managing Your App
274
Hiding and Deleting an App
274
Making Changes to a Live App (Multiple Versions) 274 Republishing a Live App
275
Changing the App Profile/Landing Page Managing Developers Managing Testers
275
279 279
Event Handling—Installs and Uninstalls Suspension and Deletion of Your App Summary
279
280
281
13 Performance, Scaling, and Security Performance and Responsiveness
283
283
What Is Responsive Performance and What Is Scale Performance? 283 Design for Responsiveness
284
251
255 257 258
xiii
xiv
Contents
Responsive OpenSocial App Performance Guidelines 285 Design for Scale
292
App Guidelines for Internet-Scale Performance Stability and Fault Tolerance Rule 1: Validate Inputs
299 299
Rule 2: Test OpenSocial DataResponse Objects for Errors 300 Rule 3: Provide Time-Outs and Error Flow
300
Rule 4: Don’t Assume That Weird Error Was an Anomaly 300 User and Application Security User Data Security
301
Application Security
301
Hacking and Cracking Summary
300
302
303
14 Marketing and Monetizing
305
Using MySpace to Promote Your App The App Gallery
306
306
App Profile, or Bringing Out the Bling MySpace’s Own MyAds
308
User Base and Viral Spreading Listen to Your Customers Ads
308
309 311
311
Google AdSense Cubics
RockYou! Ads Micropayments PayPal Boku Others
311
313 314 316
316 317 318
Interviews with Successful App Developers Dave Westwood: BuddyPoke (www.myspace.com/buddypoke) Eugene Park: Flixster (www.myspace.com/flixstermovies)
318 321
318
293
Contents
Tom Kincaid: TK’s Apps (www.myspace.com/tomsapps) Dan Yue: Playdom (www.myspace.com/playdom) Summary
322 324
326
15 Porting Your App to OpenSocial 0.9 Media Item Support opensocial.Album Fetching Albums
330 333
Fetching Media Items
335
Updating Albums and Media Items Uploading Media Items
340
Simplification of App Data
341
REST APIs Summary
343 348
References Index
355
351
329
330
338
xv
Foreword The Internet is constantly evolving, with vast arrays of information on every topic growing at a remarkable pace. Google’s 1998 search index had only 26 million Web pages; a decade later it recognizes more than 1 trillion URLs.With so much information, we need a new set of tools to make sense of the Internet.The transition is from a strict focus on informational content, to being able to take advantage of our context, our relationships, and our activities. Enter the social Web, a relatively new twist to the Internet, which is being built up by social networks, portals, and even more traditional businesses.The “Open Stack” is a set of specifications being developed by grass-roots communities all over the world, enabling developers to create new products and services enhanced by user-specific data.The Open Stack includes specifications such as OAuth, which provides secure access to data; OpenID, a global identity standard; and OpenSocial, a common API for building applications.These specifications are becoming the underlying infrastructure for the social Web, weaving a social fabric throughout the Web. OpenSocial enables developers to learn a single core programming model that can be applied to all “OpenSocial containers,” those sites that support the OpenSocial specification.With standards-based tools, including a JavaScript-based gadget API, a REST-based data access API, lightweight storage capabilities, and access to common viral channels, developers can build inside those containers as well as create applications for mobile phones or other sites. In late 2009, less than two years after its introduction, more than 50 sites have implemented support for the OpenSocial specification. In aggregate, these sites provide developers with access to more than 750 million users all over the world. By taking advantage of the OpenSocial API and the information available in this book, you will have a great opportunity to reach a lot of users. For example, you’ll find the OpenSocial API supported by many major sites that span the globe:Yahoo!, iGoogle, Xiaonei and 51.com (China), Mixi (Japan), orkut (Brazil), Netlog (Europe), and, of course, MySpace. Beyond that, OpenSocial is also supported by more productivity-oriented sites like LinkedIn and by Fortune 100 companies as diverse as IBM and Lockheed Martin. With this book, you can quickly get up and running with OpenSocial on MySpace, and you’ll be poised to leverage that experience to reach users on other sites as well.The in-depth programming examples provide a good introduction to the design options available when building with the OpenSocial API, and the code is open source for ease of use and future reference. Additionally, the MySpace Platform tips sprinkled throughout will help you avoid common mistakes and understand the intricacies of their platform policies. Getting a feel for how social platforms operate will be valuable as you continue to explore the wide world of OpenSocial. OpenSocial is constantly evolving, just like the rest of the Internet.The OpenSocial specification is managed through an open process where anyone can contribute their ideas to influence the next version of the specification and help move the social Web forward.
Since its creation, there have been several significant revisions to the specification, introducing some new programming methodologies and improvements that make it easier for new developers to start using OpenSocial. As you’re getting into the OpenSocial API, be sure to contribute back to the OpenSocial.org community your ideas on how to improve the specification. It’s open. It’s social. It’s up to you. —Dan Peterson, president, OpenSocial Foundation San Francisco, California August 2009
Acknowledgments Chris Cole: I’d like to acknowledge the great team at MySpace that helped build the developer platform and all the people who’ve contributed to refining the OpenSocial specification. Compromise is hard, but a bad spec would have been even harder. Chad Russell: Thank you to Dan Peterson, for being a resource and agreeing to write our Foreword. And, of course, a big thanks to our technical editors, Bess Ho, Cassie Doll, and Ben Schupak, who all found a number of issues that would otherwise slipped through the cracks. Jessica Whyte: Thank you to Trina MacDonald and Olivia Basegio at Addison-Wesley for your support. I’d also like to acknowledge Tom Kincaid, Eugene Park, Dave Westwood, Dan Yue, Jon Nguyen, and Katie Simpkins for taking the time to answer all of my (many) questions about the platform.
About the Authors Chris Cole is a software architect at MySpace and is a major contributor to building the MySpace Open Platform and keeping it committed to OpenSocial. He’s been a core contributor to the OpenSocial 0.9 specification and is the primary architect and implementer of OSML (OpenSocial Markup Language) on the MySpace platform. Chris has been in software for fifteen years and has spent much of that time riding the various waves of the Internet and pushing the boundaries of the Web. Chad Russell is the lead developer on the MySpace OpenSocial team. He knows the OpenSocial spec front to back, in addition to every tip, trick, and nuance of the MySpace platform itself. Chad holds an engineering degree from the University of Toronto and currently resides in Seattle,Washington. Jessica Whyte has worked for several years as a journalist, most recently with Journalists for Human Rights, and is currently a graduate student at the University of Washington, studying Human-Centered Design and Engineering. She lives in Seattle with her husband and coauthor, Chad Russell.
This page intentionally left blank
Introduction Welcome to the wonderful world of social apps.You are about to enter—or have already entered—a fast-paced and treacherous landscape that can be exciting, frustrating, intellectually challenging—and yes, it can even be pretty profitable. We hope to be your guide through both the hidden pitfalls and the soaring peaks. There is much to gain from this exciting new market; social apps have been around for only a few years, after all. Many successful apps have yet to be created and then discovered by the millions upon millions of passionate MySpace users. In this book we’ll start from scratch and walk you through the entire process of building apps, from signing up as a developer all the way to building highly complex social apps that can scale out to thousands of users.We have extensive experience on the MySpace Development Platform (MDP), from building apps to helping build the platform itself.We’ll point out the many idiosyncrasies and quirks of the platform, as well as the many ways to squeeze out a little better performance by making a few tweaks to your existing apps. Throughout this book we’ll demonstrate best practices and show lots of sample code as we take a step-by-step approach to app development. Starting with a simple “Hello World”–style app, we’ll add functionality until we have a fully built, feature-rich app. We’ll fetch data on the current user, get the user’s friend list and photos, and parse all the data that comes back to display it on the screen. We’ll send app invitations and notifications to help spark viral growth and send requests back to Web services running on a third-party server in order to process and store data. Finally, and possibly most important, we’ll show you how to make money developing apps using ads and micropayments. When it comes to developing social apps on the MySpace platform, the sky is the limit. As of this writing, the most popular app on MySpace has more than 13 million installs. Maybe your app will be the next big thing. All it takes is a good idea and a little bit of knowledge. If you provide the former, we can provide the latter. Our hope is that this book is accessible both to experienced social app developers and to those developers who have only heard about it on the news.To that end, if you’re standing in the technology section of your local bookstore reading this, and you have a bit of computer programming knowledge mixed in with some free time and curiosity, this book is for you. We’ll start from scratch in this introduction to ramp you up on MySpace and OpenSocial. For those who already have apps up and running out in the wild, this book is also for you, as we’ll dig pretty deeply into the platform a little later on.You may be familiar with the ideas and terms we introduce here, so feel free to skip ahead a bit. (Check out the section on tools, though!)
xxii
Introduction
How to Read This Book There are a couple of ways to read this book. If you’re an experienced app developer, you can use this as a reference book.The index will direct you to specific topics, where you can consult the various tables, sample code, and tips. Or, if you need to learn a new aspect of the platform, you can skip directly to that chapter and dive right in. If you’re not an experienced app developer, we suggest you start from the beginning. We’ll start off with a “Hello World”–style app in Chapter 1 and add to it in each subsequent chapter. By the time the app is completed, you’ll have a broad knowledge base of the entire platform.We then branch out into some advanced topics to take you a step further.
Assumptions Made by the Authors This is not a book about how to code in JavaScript or Python or any other language.We will try to follow good practice in our sample code throughout the book, and we’ll point out a few interesting tidbits along the way, but that’s all.This is a book on how to write OpenSocial apps on MySpace. To that end, we assume the following: n
n
You have basic knowledge of computer programming in general. Maybe you work at a software company and write PHP every day, or you’ve taken a few programming classes, or you’ve taught yourself by constructing your own Web page. You have basic knowledge of JavaScript or are willing to pick up the basics.We’re not talking advanced stuff here, just the basics like calling functions, giving values to variables, and that kind of thing.
With all that said, let’s get started!
What Is MySpace? Ah, MySpace. Land of lost friends now found, hours wasted, and every band on the planet, large and small. Oh, and spam. Lots of spam. You can find the site here: www.myspace.com. MySpace is a social network. You sign up for free and you’re given a “profile”; the Profile page is a Web page that others can view and you can edit. On your Profile you can add information about yourself, pick a display name, and upload a picture. Others who view your Profile can leave comments for all to see.The cascading style sheets (CSS) of Profile pages can also be edited, so individuals can alter the styles of the page to “bling” out their Profiles in ways both beautiful and stunningly, shockingly bad. In addition to a Profile page, you are given a Home page (shown in Figure I.1), which only you can see. It contains your notifications, “You have new mail!” and the like, along with various links to other parts of the site and updates from your friends. It is the friend aspect that truly makes a social network.The idea is simple; you sign up and are assigned a single solitary friend:Tom, one of the cofounders of MySpace.
Introduction
Figure I.1
A MySpace Home page.
Through various means, such as searching, browsing, or having a robot guess “People You May Know,” you can find new friends and add them to your list. Having other users as friends grants you certain privileges to their information. You may have access to their Profiles even if they’re set to “Private”; you see their updates on your feed; and, most important to us, friends can interact inside apps. That’s MySpace in a nutshell. You sign up, bling out your Profile, add some friends, upload some pictures, install some apps, and send some messages.
What Is OpenSocial? OpenSocial is a specification that defines how Web sites allow third-party apps to run on their sites.That means many things. For example, OpenSocial defines what information a Web site can and must expose for each of its users. It defines a client-side JavaScript runtime environment with a set of APIs that describe how to fetch and update that user data.This environment is commonly called an “OpenSocial container.” Likewise, it defines a set of server-side APIs that manipulate the same data but can be called from a server running your language of choice, such as PHP. It also defines a markup language called OSML that can be used to draw your app and fetch data. This specification was started by the folks at Google, along with the help of MySpace, Hi5, and other initial early adopters. Maintenance of the spec has since been passed to the OpenSocial Foundation and the community.That means any implementers, such as MySpace, Orkut, and LinkedIn, app developers, or really anyone at all can suggest modifications to the spec and participate in setting the direction.Those modifications are then voted on in a public forum and either brought into the spec (found at http:// opensocial.org) or rejected.
xxiii
xxiv
Introduction
Any site on the Web is free to implement the OpenSocial spec.The idea is that when a site becomes OpenSocial-compliant, it is able to run almost any of the thousands of apps already created using OpenSocial. If a site has correctly implemented all the various APIs, an app running on MySpace should run just as well anywhere else.That’s the theory, anyway. In practice there are differences, some small, some large, between the various OpenSocial implementers, which can make it tricky to maintain an app on multiple sites.
What Is the MySpace Open Platform? The MySpace Open Platform (sometimes called MySpace Developer Platform or MDP) is simply MySpace’s implementation of the OpenSocial spec. MDP is a blanket term that covers how to sign up as a developer, how to update your app, and how to actually run your app. MySpace tries its best (we really do) to fully implement the OpenSocial spec, but there are bound to be some inconsistencies. Most of these inconsistencies are small, but we’ll cover them as they come up throughout this book. MySpace has also implemented some features that aren’t in the spec; we’ll cover these as well. More than on any other OpenSocial-enabled site, developing apps on the MySpace platform gives you access to a huge number of users and potential customers. As of this writing, MySpace has more than 130 million monthly active users and is growing every day. Forty-five million of those users have installed apps.To give you an idea of the demographics, 53% of app users are female, with an average age of 18 to 24.That is a very marketable demographic to have at your fingertips.
What Tools Will You Need? Here’s where we finally start to get a little technical. After many long nights spent debugging why that callback wasn’t being fired or where that stupid 401 unauthorized error was coming from, we’ve found some really useful tools that will make your life easier. Let’s repeat that for effect:These tools will make your life easier.
Browsers First, a quick note on browsers. Install Firefox right now. Go to www.firefox.com. In our opinion it’s best to develop on Firefox, get your app running the way you like it, then worry about running it in Internet Explorer (IE) as well. Developing on the Web is still an “IE and everyone else” process. You get an app working in Firefox, and that means it will probably work just fine in Safari, Chrome, and Opera.Then you can bang your head for a few hours (days?) fighting floating divs and onclick handlers in IE.
Firebug Firebug is the main reason Firefox is the Web developer’s browser of choice. Firebug is an incredible tool for all Web developers, and every single one of us should install this
Introduction
Firefox extension. It’s even more useful because all JavaScript “alert” and “confirm” functions are blocked by the OpenSocial container, so it really is the best way to debug. Get it at http://getfirebug.com. Firebug has many useful functions.We won’t go into every last one here, but there are a few that we use every single day. To follow along at home, install the Firebug extension, restart Firefox, navigate to your Web page of choice, then click Tools → Firebug → Open Firebug.The Firebug window will open at the bottom of the browser. Inspect The Inspect feature allows you to view and edit CSS and inline style on any HTML element currently on the page. Click Inspect at the top left of the Firebug window and hover your cursor over a Web page. You’ll see that the various HTML elements become highlighted. Clicking on one will show you the HTML markup of that element in the left pane and its current style in the right pane. You can then add a style or modify the existing style on the fly. This is very useful for building up user interface (UI) elements quickly. Instead of making a change, saving a file, uploading it, and reloading the page in a browser, you can just edit the style very quickly and try different values.When you find what works in Firebug, you can make one edit to your page and it should be good to go. Console The Console tab shows you a couple of useful things. The first useful bit of information is that any and all JavaScript errors are displayed here, with a description of the error along with its file name and line number. The second is that any outgoing Web service requests are shown here. You’ll be able to view the exact outgoing URL, any parameters and headers sent along, and the response from the server. As most MySpace requests are sent as outgoing Web service requests, this is an invaluable debugging tool. You’ll be able to see if the correct parameters were appended to the request, and you won’t have to output the response onto a div on the UI to see if you got the correct response or some sort of error. Script The Script tab is your debugger. You can load any HTML or JavaScript file into the left pane by selecting the file in the drop-down above that pane. You can then set breakpoints at any line with a green line number. In the right pane you can set watches on variables, view the current stack, and manipulate the breakpoints you’ve set. With the Watch window you can interrogate any object by simply typing the variable name into the New Watch Expression bar. Doing so displays the object’s current state and value. But you can also directly manipulate objects. Say you wanted to see what would happen if a Boolean variable were true instead of false; you can set that directly by entering something like varName = true into the Watch Expression bar. Need to figure out what’s causing that JavaScript error? Set a breakpoint on that line and inspect the objects and variables that are causing the issue. Need to figure out exactly what the response looks like from a request to the MySpace servers? Set a
xxv
xxvi
Introduction
Figure I.2
Interrogating the response from MySpace’s servers using Firebug.
breakpoint in your callback function and add the response object to your Watch list (see this in action in Figure I.2). There are countless other things you can do with the Script tab, and Firebug in general, but we can’t discuss them all here.We hope we’ve sold you on its usefulness and that you’ll discover the many great features it has to offer.
Fiddler Fiddler is a free tool that watches all the traffic coming into and out of your computer. With respect to building apps, it is useful mainly if you decide to use the server APIs as opposed to the JavaScript APIs.We’ll discuss what that means later on in the book, but keep this tool in mind when you get there.To give you a little bit of a preview, you’ll be sending requests for data to MySpace, and those requests need to be sent using an authentication scheme. Using the authentication scheme is tricky, but Fiddler will help you see exactly what you’re sending to MySpace and exactly what MySpace is sending back to you. Get it here: www.fiddler2.com. One other thing to note is that Fiddler works natively in Internet Explorer but not Firefox. If you do a little bit of Googling, you can get it up and running in both browsers.
I Building Your First MySpace Application
1
Your First MySpace App
2
Getting Basic MySpace Data
3
Getting Additional MySpace Data
4
Persisting Information
5
Communication and Viral Features
6
Mashups and External Server Communications
7
Flushing and Fleshing: Expanding Your App and Person-to-Person Game Play
This page intentionally left blank
1 Your First MySpace App Tarehiscommonly book is primarily a guide to writing OpenSocial applications (or “apps” as they known and as we’ll refer to them throughout this book), but with a focus on the MySpace platform. This first chapter is designed to give you an overview of what to expect from developing with OpenSocial for MySpace.To do that, we’ll start with a “Hello World” app, something simple to introduce you to MySpace and OpenSocial. As we make our way through Chapter 1, we’ll refer to any other chapters that may delve deeper into a particular topic. Feel free to skip to the more detailed chapters if something is not clear. Now, let’s get started.
Creating the App—“Hello World” No programming book is complete without the requisite “Hello World” program. And who are we to flout tradition? We’ll create our version of “Hello World” with the small addition of personalizing it for the current MySpace user—also known as the “Viewer” in OpenSocial parlance. In a few easy steps we’ll sign up for a developer account and build our first app.
Step 1: Sign Up for a Developer Account Before you can do anything, you’ll need a basic MySpace user account. Once you have that, the next step is to sign yourself up as a developer. MySpace recently changed this procedure from a manual approval process that could take a few days to a self-serve one.You simply need to have a valid e-mail account associated with your MySpace account. 1. Log on and go to the MySpace developer site at http://developer.myspace.com. 2. Click the MyApps link.This sends you to the Signup page. 3. Fill out the form and accept the terms of service. Click Next.
4
Chapter 1 Your First MySpace App
4. Solve the “CAPTCHA” puzzle and click Finish.This sends a confirmation message to your e-mail account. 5. When you get the verification e-mail, click the embedded link.You must be currently signed in to MySpace under the user account requesting developer status for the verification to be successful.
Step 2: Create an App Before you can create a “Hello World” app, you must first create your app record.This defines the app Profile, a contact e-mail address, and some meta-information about your app. Here’s how to do it: 1. Log on and go to the MySpace developer site at http://developer.myspace.com. 2. Click the MyApps link. 3. Click the Create New App button. 4. Click the Create On-Site App button. A general information form will appear. 5. Fill in the form with some values for your sample app: Application Title: Hello World E-mail:
[email protected] Password: <something_you_will_remember> Terms: The e-mail address used for each app must be unique—a sometimes frustrating requirement that’s covered in more detail in Chapter 12, App Life Cycle. 6. Solve the nigh-impossible word puzzle by entering the correct “CAPTCHA” letters. 7. On the Upload Application XML screen, click Skip This Step since we’ll be doing a simple, direct edit of the app’s details. 8. Complete the form on the next screen.You must set values for all the required fields, but it’s not critical what the values are. Application Description: “Hello World” app Primary Category: 9. Click Save. 10. Click the Edit App Source tab. You are now ready to write code. Go to the next section and collect $200.
Step 3: Enter Your Source Code The first thing we need to do is pick a “surface” on which to build our app. Canvas is typically the easiest surface to work with, so we’ll use that. There are two basic formats for entering app source code: as gadget XML or in one of the surface source editors. Since this is just an introduction, we’re sticking with the
Creating the App—“Hello World”
What are Surfaces? Surfaces—sometimes also referred to as “views”—are a notion OpenSocial inherits from the Google Gadgets spec. A surface denotes a drawing surface on which an app can live. Currently there are three surfaces for MySpace OpenSocial apps: Home: The user’s private Home portal page. This is what is displayed to the user when he or she first logs in to MySpace. In the future, you may want to use this space to show vital app stats, news, or specific user details. Profile: The user’s public Profile page. This is what is displayed on the user’s public page, meaning it’s visible to the world. This surface is a great way to promote and advertise your app. See Chapter 14, Marketing and Monetizing, for more information on this topic. Canvas: A full page usable by the app. This is typically where the user plays and interacts with the app.
surface source editor. Authoring your app as gadget XML brings you a little more power but a lot more complexity. Now, let’s enter the following “Hello World” code and run it: OpenSocial Hello World <script type = "text/javascript"> var viewer = null; /** * Initial data request to load Viewer */ function getViewerData(){ var req = opensocial.newDataRequest(); req.add(req.newFetchPersonRequest( opensocial.IdSpec.PersonId.VIEWER), "viewer"); req.send(getDataCallback); } /** * Callback handler to process the response * @param {Object} data */ function getDataCallback(data){ if (data.hadError()) { var err = "Processing Error: " + data.get("viewer").getErrorMessage(); writeMessage(err + "
You should install the app"); return; }
5
6
Chapter 1 Your First MySpace App
viewer = data.get("viewer").getData(); if(viewer){ writeMessage("Hello, " + viewer.getDisplayName()); } else{ writeMessage("Wow, you don't exist"); } } /** * Convenience method to write our message to the page * @param {Object} msg */ function writeMessage(msg){ var elem = document.getElementById("message"); elem.innerHTML = msg; } //Register to look for initial data gadgets.util.registerOnLoadHandler(getViewerData); ... <script type="text/javascript"> // <script type="text/os-template">
Putting It Together: OSML Tic-Tac-Toe
<script type="text/javascript"> //
Working with Data Having shared display code among the surfaces is great. Having shared data is even more awesome.The next step is to start migrating some of our data calls and our display to make use of the Data Pipeline. We’ll start by changing the Viewer request to an os:ViewerRequest and use the expression language to display the Viewer info. Getting Viewer Information with os:ViewerRequest 1. Edit the canvas Content block to add a new os-data section above the os-template section.The modified code section will look like this: <script type="text/os-data"> <script type="text/os-template">
2. Add a new os:ViewerRequest tag to this os-data section with the key viewer. This tag will retrieve the basic Viewer fields by default. Since our code uses several extended fields, we will specify the full field set by adding the value @all to the fields attribute.
3. Edit the contents of the myinfo div to pull the Viewer image and name from our new pipelined data stored under the viewer key in the DataContext. An expression language statement is used to set the name and image URL: ... ... ${viewer.DisplayName}
4. Search for the call to printPerson in the JavaScript. It is in the getDataCallback function. Comment out this call: // printPerson(document.getElementById("myinfo"));
235
236
Chapter 10 OSML, Gadgets, and the Data Pipeline
5. Save and view your results in the Sandbox Editor.The app will now render with the Viewer information prepopulated on the rendered surface. That’s pretty exciting.We got access to all of that information without a single line of JavaScript. Now, let’s fix the More button to show the player bio again instead of allowing the app to get stuck in lightbox mode. Updating the Player Bio Lightbox to Use Client-Side DataContext In this section we make some minor refactors to the existing JavaScript function that controls the display of the opponent box. At this point we only swap out some of the data calls with calls to the DataContext and leave most of the code intact. Moving to using a template will be introduced in the next chapter: Chapter 11, Advanced OSML. 1. Add a new div below the myinfo div with the ID of playerBioWrapper and set the style equal to display:none. 2. Search for the printPerson function. Notice all of the HTML being built later in the function. In the original version of this function the div element for the lightbox is created dynamically.We’re going to code the lightbox content container directly on the page. 3. Copy out the wrapping divs and close button divs. Add these to the div created in step 1.Your markup should appear like this: Player Bio close...
4. Add a new JavaScript function called showViewerBio.This holds the logic for triggering the display of the Viewer’s bio in the lightbox. 5. Add a line to this function to get the Viewer from the client DataContext: var vwr = opensocial.data.getDataContext().getDataSet("viewer");
6. Build out the contents of the lightbox using the vwr object. Client DataContext objects are in JSON format and have their properties directly accessible as properties. var str str str
str = ""; += "" += "" += "\n"; var tryAppendLine = function(val){ if(val && val != ""){ return val + "
\n"; } } str += tryAppendLine (vwr.DisplayName); str += tryAppendLine (vwr.DateOfBirth);
7. Assign the contents of your string to the innerHTML of the element internal to the player bio div and assign the entire bio contents to the lightbox: var bioElem = document.getElementById("playerBioWrapper"); var bioContents = document.getElementById("bio_contents"); bioContents.innerHTML = str; //Add to lightbox TTT.LightBox.setContent(bioElem.innerHTML); //Now show TTT.LightBox.show();
8. Update the More link to fire the new showViewerBio function instead of directly invoking the lightbox.Your code should properly show the lightbox again.
Displaying Data Lists The next operation is to display a list of friends.This is done by combining the results of an os:PeopleRequest tag with an os:Repeat control.We’re going to update the Invite tab to use OSML and the Data Pipeline instead of client XHR requests. Displaying Friends with a Repeater A repeater allows your app to apply the same display content to multiple items in a list. The most common use case is to display a list of friends. In this section we’ll use a repeater to display the friends in the Invite tab of our app instead of the previously designed JavaScript function for manually outputting this code. 1. Add an os:PeopleRequest tag to the Data Pipeline script (text/os-data) for our Canvas view.We’ll place it under the key "friends" and get the Viewer’s friends with it.The following tag states that “I’m going to get a group of friends”—denoted by the special value "@friends" in the groupId attribute—”and these friends will be friends of the Viewer”—denoted by the special value "@viewer" in the userId attribute:
237
238
Chapter 10 OSML, Gadgets, and the Data Pipeline
2. Find the invite_container div.We’re going to modify this div to add a repeat tag to iterate over friends. Ahead of the last closing div on this element, add the following: ${Cur.DisplayName}
3. Save and view.The Invite tab should now show all of the Viewer’s friends prepopulated.We leave it to the reader to rewire the client script call to rsaInviteClicked.
Summary OSML is a powerful, convenient, and simple way to build apps. Operations that are tedious and repetitive with JavaScript are a snap with OSML and Data Pipelining.We’ve only just introduced some common elements, though. In the next chapter we’ll discuss some more advanced topics, such as tag templates and internationalization. The astute reader will also notice that as we ported over our Tic-Tac-Toe app to use OSML, we were not cleaning out the retired JavaScript.With the exception of commenting out some clashing client JavaScript calls, the now unused script code is still in place. A partial listing of the retired JavaScript functions includes n
getInitialData
n
getDataCallback
n
printPerson
Since proper refactoring and cleanup of old code can be an arduous process, we will leave the code in place for now.We leave it to the reader to fully identify superseded code blocks and clean them out of the app. What we’ve seen so far are some of the most common operations in OSML. Remember, OSML is a late addition to the OpenSocial spec and supported starting only in version 0.9. At the time of this writing, OSML and Data Pipelining are not even fully released and aren’t recognized by previous versions, whereas the OpenSocial JavaScript API has more than a year under its belt and is fairly stable.We encourage you to push the limits with OSML, though, and give your feedback both to MySpace and to the OpenSocial community so that we may continue to improve things. Note Code listings and/or code examples for this chapter can be found on our Google Code page under http://opensocialtictactoe.googlecode.com.
11 Advanced OSML: Templates, Internationalization, and View Navigation Tyouhiswhen chapter will introduce you to some of the more advanced techniques available to using OSML. After reading it, you should be able to define your own custom tags in your apps and use them on both the client and the server.We’ll explore directly rendering HTML fragment content from external servers and interacting with data through data listeners. And to wrap it all up, we’ll internationalize our app and localize it into different languages. OSML provides significant features beyond simple markup reuse and tag-based data declaration. In this chapter we’re going to explore some of the more advanced features of OSML.These features include defining custom tags, translating your app to different cultures and languages, and direct rendering from your external server. In the preceding chapter we covered some different ways of solving the same problems we’ve previously solved, but using OSML. In this chapter we’ll do a little more of that, plus introduce some new features that open even more doors to our app. Remember, though, OSML is a very late addition to the OpenSocial spec, and it is supported beginning only with version 0.9 of the OpenSocial specification; previous versions do not recognize OSML.
Inline Tag Templates Inline tag templates are reusable components that are declared directly inside the main gadget XML.They look very similar to standard inline templates with one exception: the presence of a tag attribute in the declaring template script tag.Templates defined in this manner are not rendered in place but are instead registered as custom tags for use later in the app.
240
Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
Figure 11.1
Player Info box.
Defining and Using a Tag Template Tag templates are an easy and convenient way to create reusable display elements. For the Tic-Tac-Toe app, we’re going to convert the Player Info box (shown in Figure 11.1) to a custom tag.This element is used for the current Viewer as well as an opponent. Our custom tag will display based on the person object passed in as a parameter. Creating the Custom Tag Template Definition The first step is to create our custom tag definition. In the following steps we’ll convert the existing Player Info box into a custom tag template for use in our app.We’ll generalize it so that it can be used for both the Viewer and the opponent. 1. Create a new template script element in the Content block of your Canvas code. This element should appear at the top of the block, before any other template scripts. In addition to the normal template script tags, it has an additional attribute of tag="my:PlayerInfo". <script type="text/os-template" tag="my:PlayerInfo">
2. Find the Player Info box markup in our existing app gadget. It can be found by searching for the div with an ID of myinfo. Copy and paste the contents of the myinfo div into our new tag: <script type="text/os-template" tag="my:PlayerInfo"> ${viewer.displayName} More...
Inline Tag Templates
3. Now we’re going to clean out the hard-coded data and change the tag template to use parameters. Parameters are local values passed in from a tag instance.They are accessible through the reserved variable ${My}. Our tag requires that a person object be passed into the tag instance in the Player element. Replace all the ${viewer.X} statements with ${My.Player.X} statements: <script type="text/os-template" tag="my:PlayerInfo"> ${My.Player.displayName} More...
4. Add an additional borderColor parameter to specify the border color.This parameter value will be placed in an inline style in the containing div.
5. Remove the id="myinfo" attribute from the main div. Each instance tag has an individual ID, so this should not be in the template. ID values from the tag instance are carried through to the resolved tag. 6. Modify the styles to accommodate the blue/pink (male/female) background colors on this element. It would be a little tedious to do an "if" test within this markup for male/female on the My.Player object, so we’ll accommodate this feature by using style class names instead.The player_info style definition will have a blue background (default), and an additional player_info_FEMALE style will be defined to override the background to pink: .player_info { border:solid 2px black; padding:5px; background-color:#09f; } .player_info_FEMALE { background-color:#fcf; }
241
242
Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
7. Add an expression statement as a final CSS class name in the main div so that the gender is included in the style class name. In this way the style class name evaluates to "player_info_MALE" or "player_info_FEMALE", removing the need for any "if" checks.
Using the Custom Tag Now that we’ve defined our template, the next step is to use it.We’re going to add instances of the custom tag for both the current Viewer and for the opponent, if present. 1. Search again in the code for the div with an ID of myinfo. Delete this entire div and all contained elements. 2. Add in place of the myinfo div an instance of our my:PlayerInfo tag with the id="myinfo": <my:PlayerInfo id="myinfo" >
3. Add in parameter elements for Player and borderColor.The Player value should be ${viewer} and the borderColor can be any valid CSS color string or color hex.We chose the color green. <my:PlayerInfo id="myinfo" > ${viewer} green
4. Save and view your updated app in the Sandbox Editor.The Player Info box should display as before. You can download this code and a full code listing for this chapter from our Google Code repository at http://code.google.com/p/opensocialtictactoe/source/browse/#svn/ trunk/chapter11.
Using Client-Side Templates Custom templates are also available for use in client-side code. All your custom templates are available for use on the client side via the new opensocial.template namespace. This allows your app to create and render new template instances on the client.The call sequence looks something like this: var templateInst = opensocial.template.getTemplate("my:CustomTag"); var tdata = {} tdata["background"] = "red";
Inline Tag Templates
tdata["otherParam"] = 1; templateInst.renderInto("target_element_id", tdata);
This code creates a new instance of the tag template defined for my:CustomTag. It then builds some data to pass in to the template as local parameters and renders the instance to a target div element target_element_id.
Warning Client-side templates are processed by the browser’s DOM engine. As such, they are sometimes subject to the various “features” of each DOM implementation. Some browsers attempt to perform tag balancing or add in new elements to get the markup to conform to their idea of what a proper DOM structure should look like. The net result is that templates that render perfectly on the server may not render correctly on the client. To avoid client-side template-rendering issues, use only complete DOM element blocks as a template—for example, div elements, fully enclosed tables, and the like. Avoid starting your templates with elements that are typically nested in other elements, such as list items (LI) and table rows (TR). Tables in particular are problematic because many browsers introduce a multitude of new elements, such as TBODY, THEAD, and TFOOT, without telling you. Favor styled div blocks in these instances. The same problem also exists with client-side repeaters and tables. If you wish to repeat a table row (TR) element, you must make use of attribute-based repeaters, not the os:Repeat element in your template.
We’re going to use client templates to render the opponent’s display block using our my:PlayerInfo custom tag template.
Client-Side Rendering with Custom Tags Because apps are dynamic, the information that is needed doesn’t always exist at server render time. User interactions affect the app. In our case, the opponent is selected after the initial app load.To address this, we use the templating JavaScript methods to dynamically render our custom tag after the opponent has been selected. In the following steps we will convert the opponentinfo div element from markup that required tedious editing in JavaScript code to a second implementation of our custom my:PlayerInfo template. 1. Edit the div element with an ID of opponentinfo and delete all the contents. Also remove the style class attribute so that we are left with an empty div element.
2. Add an os:PeopleSelector tag element immediately above the opponentinfo div defined in step 1. It should specify the group as the contents of our friends data item and a var="selectedOpponent" to have the selected friend placed in
243
244
Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
the DataContext under that key.We’ll also tie its select behavior into our existing selectOpponent method.This replaces the client-side FriendPicker we added in Chapter 7, Flushing and Fleshing.
3. Remove the div with an ID of opponentPicker and comment out the call to loadFriendPicker since this is now being handled by the os:PeopleSelector. You may skip this step if you did not implement app data person-to-person play (discussed and outlined in Chapter 7). 4. Define a new function to trigger the client-side rendering of a new template instance in the opponentinfo div named updateOpponentTemplate.This function gets a template instance using the opensocial.template.getTemplate function and then calls renderInto to render the template into our opponentinfo div.The function looks like this: function updateOpponentTemplate(player){ var elem = document.getElementById("opponentinfo"); if(!player){ elem.innerHTML = ""; return; } //Get a new instance of the template var tplate = opensocial.template.getTemplate("my:PlayerInfo"); //Construct the data to pass into the template var data = {}; data["Player"] = player; data["borderColor"] = "red"; //Render the template into our target div tplate.renderInto(elem, data); }
5. Update the function selectOpponent to invoke this new method: function selectOpponent(player){ updateOpponentTemplate(player); ... }
Now, in addition to setting up a game challenge, the display will update to show the opponent’s information using our template.
Working with Subviews
Working with Subviews Subviews are a new concept introduced with OSML. So, yes, that means they work only starting with version 0.9 of the OpenSocial spec. If you’re writing or editing an app for a lower version, you won’t be able to use subviews. In addition to the main surface views (Home, Profile, and Canvas), subviews allow for multiple views or view parts to be defined on a particular surface.The app can then “navigate” to these subviews without forcing a page reload. Using subviews is quite simple. All you have to do is name the subview in a Content block’s view attribute, then use the following call: gadgets.views.requestNavigateTo();
This call causes the target subview to show, and all other subviews are hidden. Subviews are useful, but they do have some design and behavior characteristics you should be aware of: n n
n n
Subview Content blocks must consist of coherent, well-formed XHTML. Adjacent non-subview Content blocks must also be well formed (this means no partial nesting of elements). By default, activating one subview turns off all other subviews. On initial load, all subviews are hidden.
Converting Tabs to Subviews We’re going to reimplement our tab interface (shown in Figure 11.2) by making use of subviews. Changing the interface in this way allows us to make use of the built-in view navigation call gadgets.views.requestNavigateTo instead of manually manipulating CSS classes or styles on the elements. It gives us the added benefit of not having to write loops to manage turning off other elements that should be hidden; it’s all handled by the subview processing.
Figure 11.2
Tab interface in app.
245
246
Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
Editing the Markup to Use Subviews for Tab Content Tabs are currently defined as div blocks.We need to modify the markup so that the header is a Content block, the footer is a Content block, and each subview is a Content block.This allows our app to control the display of the tabs as subviews but still display the header and footer in a consistent manner. 1. Find the location of the first tab content container by searching for the div with an ID of play_container. 2. Close out the Content block above this by adding the closing template script and Content block tags.The code will look like this: ...
4. Add similar wrappers around the other tab content div elements. Name the new subviews as defined in Table 11.1 (the subview name goes into the view attribute of the Content block element). 5. Edit each tab content element’s CSS class attribute to remove the hide class: ...
Table 11.1
New Subview Names
Tab Div
Subview Name
invite_container
canvas.invite
challenge_container
canvas.challenge
set_background_container
canvas.pickBackground
Working with Subviews
6. Enclose the markup after the tab content container elements in a standard Content block with the view attribute set to canvas. Modifying the Script to Use Subviews The code currently has a TTT.Tabs object.We will modify this object to use subviews instead of direct DOM manipulation to do tab management. 1. Edit the TTT.Tab object to simplify its representation. It will now hold the subview name instead of a reference to the tab container DOM element. TTT.Tab = function(tab_dom, view, action){ this.tab_dom = tab_dom; this.view = view; this.action = action; };
2. Modify the initTabs function in TTT.Tabs to pass in the view name instead of the DOM reference when creating each TTT.Tab object: _initTabs:function(){ this._tabs[0] = new TTT.Tab(document.getElementById("tab0"), "canvas.play", playClicked); this._tabs[1] = new TTT.Tab(document.getElementById("tab1"), "canvas.invite", inviteClicked); this._tabs[2] = new TTT.Tab(document.getElementById("tab2"), "canvas.challenge", challengeClicked); this._tabs[3] = new TTT.Tab(document.getElementById("tab3"), "canvas.setBackground", setBackgroundClicked); }
3. Modify the selectTab function in TTT.Tabs to use the requestNavigateTo call. As part of this modification, you must also remove the calls to container.show(); and container.hide();. selectTab:function(index){ if(0 === this._tabs.length){ this._initTabs(); } var tab = this._tabs[index]; if(tab){ gadgets.views.requestNavigateTo(tab.view); } else{ return; } ... }
247
248
Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
4. Add a call in the loadInitializer to activate the canvas.play subview: function loadInitializer(){ ... gadgets.views.requestNavigateTo("canvas.play"); ... }
We haven’t actually saved much code by making this change. Primarily, this is because the code still has to modify the display of the tab. The OpenSocial spec group is considering proposals to add activate/deactivate event handlers to subviews, but at the time of this writing this functionality has not been settled in the OSML specification. The well-formedness constraint can be a problem in some interfaces with tag nesting. As a result, subviews tend to lend themselves best to absolutely positioned elements (for example, xxx) or root children DOM elements of the view. In this way the subviews do not interfere with layout flow when being enabled or disabled.
HTML Fragment Rendering One of the most common paradigms we have found with OpenSocial apps is that of content rewriting. Many apps perform an initial load of a Bootstrap page, then call back to their external servers for the actual app content and perform a wholesale innerHTML rewrite of the app.While this is very flexible, it is also a very slow user experience. As we previously mentioned, OSML brings a number of new options to the table for finegrained content rewriting. In our app we’re going to add a tab to view one of our favorite sites and then render a banner ad from a pretend ad server URL (actually, a historic Web site).
Adding Content with os:Get In this section we’ll create a new tab that displays content from an external site.We’ll use the os:Get tag to pull content directly from the external site and display it inline on a subview. 1. Add a new tab to our series of tabs. Search for the div with an ID of tab3. Create a copy of this immediately after as tab4 and adjust the text to be “Laugh.” Also adjust the CSS class definitions as shown here: Set Background Laugh
HTML Fragment Rendering
2. Create a new content subview for the tab surface and identify it as canvas.laugh: <script type="text/os-template"> Am I a comedian? Do I make you laugh?
3. Modify the _initTabs function in the TTT.Tabs object to include this new subview tab: this._tabs[4] = new TTT.Tab( document.getElementById("tab4"), "canvas.laugh");
4. Add a div container and an os:Get tag within this Content block.The div container constrains the display size and the os:Get retrieves the content.The finished tab contents subview will look like this: <script type="text/os-template"> Am I a comedian? Do I make you laugh?
Adding Targeted Content with myspace:RenderRequest The os:Get tag is useful for simple fragment rendering. Sometimes you need more power, though.The myspace:RenderRequest allows for any HTTP verbs supported by makeRequest in addition to just GET. It also supports targeting output to a specific element instead of just inline rendering. We’re going to add a target element, presized for the ad banner, and a render request to fetch the ad. 1. Above the title heading for the app, add a div for the banner ad with an ID of bannerAd. Style it to be 80 pixels high. OpenSocial Tic-Tac-Toe
249
250
Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
2. Below the ad, add a myspace:RenderRequest tag. For our fictitious ad we’ll use something from a bit of Internet history: an article from Suck Daily. <myspace:RenderRequest target="bannerAd" href="http://www.suck.com/daily/2000/12/26/" />
A Note on Internet History For those of you too young to remember, Suck was one of the first, best satire sites where all of us dot-commies got to “nudge-nudge, wink-wink” each other about how ridiculous the notion was that we’d all somehow become overnight millionaires. Then we’d get back to our 60-hour-a-week jobs and labor under the ridiculous notion that we’d all somehow become overnight millionaires.
Data Listeners In the traditional JavaScript OpenSocial API, there is no need for a data listener. All data requests are processed as XHR requests. Any actions your app may need to execute are placed in the callback function.With Data Pipelining it’s a different case. Since the data is simply declared via a tag language, there isn’t a traditional callback function.To make up for this, any number of JavaScript functions may subscribe to a data key, or “listen” for when the data becomes available. A data listener is registered by calling the registerListener function on the clientside DataContext and passing in the data key and the function to trigger.The function receives the key(s), causing it to trigger when the data is ready for processing. Any handler has to then retrieve the data from the DataContext using the key.The benefit of this design is that generalized handlers can be written to handle multiple data keys. var dataContext = opensocial.data.getDataContext(); dataContext.registerListener("key", handler_function);
Exactly when and how data listeners trigger depend a little on the underlying data to which they’re being attached.The trigger is supposed to fire as soon as the data is available for use.This might be as soon as the app renders in the browser, at some arbitrary point after the initial rendering, or never. You’ll recall that any calls to the built-in OpenSocial data endpoints (for example, people,Viewer, activities, etc.) are resolved server-side prior to sending the processed app down to the browser client.This means the listener function triggers on initial load of the app. Server-processed data keys are added at the very bottom of the processed app markup, so listeners trigger as part of the load sequence.They may, however, fire before any functions registered with the gadgets.util.registerOnLoadHandler call. Data keys that are resolved by calling external servers do not fire until the callback trigger is fired and succeeds in loading the data.This means these listeners behave in a
Data Listeners
manner more akin to what we’re used to with the JavaScript OpenSocial API data calls coupled with callback functions.
Displaying JSON Results with a Data Listener For our app, we’re going to add a new tab that shows image search results for our favorite game,Tic-Tac-Toe.We’ll make use of the Yahoo! image search API to get the search results in JSON format, then render them using a listener and a custom template. The results will appear as in Figure 11.3. Warning Remember to use proper escaping of ampersands and other characters when placing URLs in attributes.
Set Up the Data Pipeline Request and the UI Tab The first step in handling display via data listeners is to create the initial data request. In this case we’re going to use an os:HttpRequest tag that will search for tic-tac-toe image results.
Figure 11.3
Rendered search results from the data listener.
251
252
Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
1. Create a new os:HttpRequest in the Data Pipeline script to invoke the Yahoo! image search API and search for “tic-tac-toe.” Key: tttImages URL: http://search.yahooapis.com/ImageSearchService/V1/ imageSearch?appid=YahooDemo&query=tic+tac+toe&output=json <script type="text/os-data"> ... ...
2. Add a new tab to the interface to hold the image results. Modify the CSS classes so the display remains consistent. Laugh Tic-Tac-Toe Images
3. Create a new content subview for the image results and identify it as canvas.tttImages. On the subview, add a div to hold the results and use the ID tttImageDisplay. Within this div we can place any content to be displayed while waiting for the results. In this manner your app can display loading messages or images until the data arrives with almost no additional coding. <script type="text/os-template"> Look at Tic-Tac-Toe Still Loading Search Results
4. Modify the _initTabs function in the TTT.Tabs object to include this new subview tab: this._tabs[5] = new TTT.Tab( document.getElementById("tab5"), "canvas.tttImages");
Data Listeners
Creating the Display Template for the Results You didn’t think we’d be manually parsing the search results, did you? Given all the powerful new templating tools at our disposal, we’d be crazy to do that. Instead, we’re going to build a template to display these results. First, we need to understand the format of the JSON search results.The following is a much-truncated result from this search: {"ResultSet": {"totalResultsAvailable":"38979", "totalResultsReturned":10, "firstResultPosition":1, "Result":[{"Title":"tic tac toe jpg", "Summary":"Tic Tac Toe - A game that never goes out of fashion Play", "Url":"http:\/\/www.something.com\/blog\/tic_tac_toe.jpg", "ClickUrl":"http:\/\/www.something.com\/blog\/tic_tac_toe.jpg", "RefererUrl":"http:\/\/www.something.com\/classic-tic-tac-toe", "FileSize":6041, "FileFormat":"jpeg", "Height":"182", "Width":"200", "Thumbnail":{ "Url":"http:\/\/thm-a01.yimg.com\/image\/0c7837e21c1b1598", "Height":"113", "Width":"125" } } , ... ] } }
The results include some aggregate information about the results, then an array of results as objects. Most of this information we don’t care about, so we’ll just show the total result count and the first page of thumbnails with the summary text. Our template makes use of the expression language tokens to pull values from the data passed into the template.We’ll pass the entire result set into the parameter searchResults.Then we can reference any property with the expression language like this: ${My.searchResults.totalResultsAvailable}
Create the template shown below and add it to the Content block containing all the other custom template definitions.This defines the tag my:YImageSearchResults. <script type="text/os-template" tag="my:YImageSearchResults"> Search Returned ${My.searchResults.totalResultsAvailable} Results
253
254
Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
${Cur.Summary}
Attaching the Data Listener to Render the Results The final step is to create and attach the listener function that triggers the template processing. Add the following function to your JavaScript code. It turns the tab red when the data returns and performs a client-side rendering of the results using the my:YImageSearchResults template we just created. function tttImagesLoaded(key){ var tab = document.getElementById("tab5"); tab.style.background="red"; var tplate = opensocial.template.getTemplate( "my:YImageSearchResults"); var dataContext = opensocial.data.getDataContext(); var d = dataContext.getDataSet(key[0]); var tdata = {} tdata["searchResults"] = d.ResultSet; tplate.renderInto("tttImageDisplay", tdata); }
The actual registerListener call should be added in the loadInitializer function.This notifies the DataContext to trigger the tttImagesLoaded function as soon as the data key tttImages is loaded. function loadInitializer(){ ... var dataContext = opensocial.data.getDataContext(); dataContext.registerListener("tttImages", tttImagesLoaded); ... }
Internationalization and Message Bundles
Internationalization and Message Bundles The gadget XML structure recognizes the need to support users from around the world.To that end, localization is a key component of the gadget design.We’re going to translate our Tic-Tac-Toe app into three different languages: English, German, and Japanese. To those readers unfamiliar with the concept of internationalization, it’s the process of designing software so that it can easily be adapted to other cultures and languages without in-depth code changes. Localization is generally referred to as the actual translation work of adapting a program to a particular language or locale. The Gadgets specification on message bundles is designed to work in a manner similar to Windows resource files or Java internationalization support.The application must be stripped of all text, which is replaced with message keys (resource keys). Message bundles are composed for each locale (language and possibly culture) that will be supported, and the original message is translated and saved under the corresponding message key. The app makes use of fallback logic in the process of resolving message keys to get their translated text.The translation starts with the most specific language and culture for the current Viewer and attempts to find the message bundle and value. If a bundle for the specific culture is not found, the translation moves up to the general culture for the current language. If this is not found, the translation falls back to the invariant culture (global) message bundle. To illustrate, let’s look at the example of French Canada. The culture code for French Canada is fr-CA.That’s pretty specific. If an app does not have a specific translation for French Canada, it attempts to resolve the message by looking at the global French translation. If that fails, the invariant message bundle is used. If that fails, the message is probably not defined. Table 11.2 shows the different culture codes that are examined.
Table 11.2
Culture Code Processing Order
Culture
Culture Code
Notes
French Canada
fr-CA
This is the most specific culture in the table. It is examined first for viewers in French Canada.
French global
fr-FR
Also may be interpreted as “French in France.” In the Locale tag, the country code is omitted for the global language translation. When external MessageBundle files are used, the file name convention is fr_ALL.xml.
Invariant
The invariant (global) culture is the final fallback. It is specified by omitting both language and country in the Locale tag. If external MessageBundle files are used, the file name would be ALL_ALL.xml.
255
256
Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
This provision allows you to truly tailor your app to the audience. An example of when to use specific cultures in English illustrates this point. In the United States (code: en-US), one would call a semi-truck simply a “truck.” In the UK (code: en-GB), the same thing would be called a “lorry.” Culture and Country Codes Culture codes are four-letter codes with a dash in the middle. They follow this format: -
Most conventions use lowercase for the language code and uppercase for the country code. For global translations, most internationalization implementations use the recognized originating country of the language. The Gadgets spec breaks from this convention by instead using the text ALL in the country code position. The Gadgets specification recommends using the name ALL_ALL for the invariant message bundle instead of leaving the culture code off entirely, which is what many conventions do.
In our Tic-Tac-Toe app, we will start by creating the invariant culture (global) message bundle and tokenizing all strings.Then we’ll translate this file into our target cultures. Finally, we’ll inline the translations in our main gadget file.
Creating Our First Message Bundle In this section we’ll create our first external message bundle file to hold translations.The same message bundle XML format will be used for each of the languages we translate. 1. Create an XML file named ticStrings.xml.This contains the invariant translations. In our case, the invariant fallback is to English, since that is the language in which the app is being written. 2. Open the file in an editor and add the following structure: <messageBundle>
3. Open our Tic-Tac-Toe gadget source file in an editor as well. 4. Find the first string on the Profile surface, which should be the app title: Tic-Tac-Toe. 5. Copy this string and add the following line in our message bundle strings file between the <messagebundle> open and close tags: <msg name="apptitle">Tic-Tac-Toe
6. Go back to the app gadget source and replace the title “Tic-Tac-Toe” with the expression ${msg.apptitle}.The finished line of code should look like this: <span>${msg.apptitle}
Internationalization and Message Bundles
7. Copy the next visible string, starting with the text “So...You think you can beat me?” into our strings file in a <msg> element with the name challengetaunt. 8. Replace the text in the gadget source with the expression ${msg.challengetaunt}. 9. Continue until you’ve extracted all visible strings and replaced them with message expressions. You have now internationalized your app. Even though the only translation we have is to the original strings, our app is ready for translation and set to become a global phenomenon. We should probably test things before going much further.To test in the Sandbox Editor, do the following: 1. Edit your app gadget source to add a tag in the ModulePrefs section: <ModulePrefs title="Tic-Tac-Toe"> ...
2. Copy the markup from your message bundle file and paste it inside the tag, excluding the starting tag: <ModulePrefs title="Tic-Tac-Toe"> ... <msg name="apptitle">Tic-Tac-Toe <msg name="challengetaunt">So... you think you can beat me!? Check out my stats below...
3. Copy your newly modified app code into the Sandbox Editor and view it. If we’ve done everything correctly, you should see no difference between this version and the previous version. Now it’s time to translate our message bundle into the other target languages: German and Japanese.
Creating Translations of the Message Bundle Now that we have created a baseline message bundle in the preceding section, “Creating Our First Message Bundle,” we will duplicate and translate it into our two target language localizations: German (de) and Japanese (ja).
257
258
Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
1. Create two copies of our message bundle XML file and name them ticStrings_de_ALL.xml and ticStrings_ja_ALL.xml. 2. Open your Web browser and go to a translation site.Two sites are Yahoo! Babelfish (http://babelfish.yahoo.com) and Google Translate (http://translate.google.com/ translate), though there are many others. For our translations we used the iGoogle translation gadget. 3. Open the German translation file (ticStrings_de_ALL.xml) and set the translation tool to be English to German. 4. Copy and translate each message in turn using the translation tool. Replace each original message with the translated result, keeping the name attribute the same. 5. Repeat steps 3 and 4 for the Japanese file (ticStrings_ja_ALL.xml), using English to Japanese as the translation. If the pasted Japanese characters come out as a series of question marks, it means your editor cannot handle this character set. You may be able to change the file encoding to UTF-8, or you may need to find a different code editor. Aptana (http://aptana.com), a Web development environment, handles Unicode characters well. Warning App gadgets (and OpenSocial apps by extension) always use UTF-8 encoding. Make sure you save your app gadget source with UTF-8 encoding. If it is submitted using a different encoding (Unicode or ANSI, for example), you may experience unusual and unpredictable results. The app review process may even flag your app as being an attempt to hack MySpace and summarily reject it.
Unfortunately, this technique does not end up with the best-quality translations. Sometimes the context is lost in translation and the text can come out like gobbledygook—think something along the lines of “I can has cheeseburger.” The only way to get good-quality translations is to have a native speaker translate the results in the context of your app or hire a professional translator. However, the translation technique we just described is adequate for our purposes (Tic-Tac-Toe is a pretty basic game). Our app is now ready to have the translations linked with the main app.
Including Translations in an App and Testing The final step is to include all our new translations in the app’s gadget code. In the following steps we will define appropriate Locale sections for each language translation and save our translated messages. 1. Define a new tag for each of our translations—Japanese (language code: ja) and German (language code: de), plus the invariant locale:
Internationalization and Message Bundles
2. Copy the associated message bundle contents, excluding the tag, and paste them inside the associated Locale element: <messagebundle> <msg name="apptitle">Tic-Tac-Toe <msg name="challengetaunt"> So... you think you can beat me!? Check out my stats below... <messagebundle> <msg name="apptitle"> <msg name="challangetaunt"> <messagebundle> <msg name="apptitle">Tic-Tac-Toe <msg name="challangetaunt">So ... Sie denken, Sie können mich!? Check out my stats unter ...
3. Open the Sandbox Editor and paste our gadget code into it. Render the results, which should be in English. 4. Now go back to the Editor tab and select German from the Culture drop-down (de-DE). Render the results. Our German translations should appear. 5. Repeat step 4, selecting Japanese (ja-JP) from the Culture drop-down.The rendered results should show in Japanese. 6. Test the invariant culture by going to the Editor tab and selecting a culture not translated, such as Spanish (es-ES).The results should appear in our invariant language, English.
259
260
Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
The technique described in this chapter inlines the translations with the main app gadget source.This is convenient for testing with the Sandbox Editor but can be difficult from a code maintenance standpoint.The specification allows for external message bundle files to be used as well.This is accomplished by specifying a message attribute in the tag that points to an external URL where the translated message bundle is located:
External message bundles can significantly reduce the amount of code you have to look at since all the strings are externalized. Using external message bundles has some downsides as well: n n
n
Your translations must be hosted on a public Web server. It’s more difficult to test your translations since you will be unable to use the Sandbox Editor; your app must be saved and have the message strings fetched.This takes time. MySpace does not currently update translations separately from app source code. They are fetched and saved on the MySpace servers when you update your gadget source. So, updating your translations requires you to republish your app.
It’s currently the recommendation of the authors that you manage your message bundles in separate XML files but use a script to inline the translations prior to saving your app source to the MySpace developer site.
Future Directions The Google Gadgets specification is only one of many gadget or widget specifications out in the wild today.Three of the most commonly known widget formats are the Google Gadgets specification, the Windows Sidebar gadget format, and the Apple Dashboard widget format. On top of that are a multitude of other formats with a much smaller reach.The underlying commonality of all these specifications is the use of HTML as a primary programming language. The W3C has now waded into the mix as well with a Widgets specification.They have even rolled the W3C Widgets specification into the general HTML 5 specification.While the only implementations today are on mobile phones and the Opera Web browser, look to see this format grow in importance. It may even be that OSML, or even the whole OpenSocial specification, will be merged with W3C Widgets at some future date.
Summary We have explored some of the more advanced techniques available when using OSML. Templates, data listeners, and fragment rendering can save you a tremendous amount of
Summary
work when building your app. Localization support takes what used to be a hideous hack everyone had to reinvent and makes everyone able to give apps a global reach. Even with these techniques, we’re only scratching the surface. Nested templates, proxied content, and data reprocessing have only been hinted at. New techniques are sure to evolve as developers become familiar with what OSML can do for their apps. Maybe you’ll be the one to invent the next great set of techniques. Note Code listings and/or code examples for this chapter can be found on our Google Code page under http://opensocialtictactoe.googlecode.com.
261
This page intentionally left blank
III Growth and How to Deal with It
12
App Life Cycle
13
Performance, Scaling, and Security
14
Marketing and Monetizing
15
Porting Your App to OpenSocial 0.9
This page intentionally left blank
12 App Life Cycle Tpublishing his chapter is about advanced app management and the sometimes difficult app process.To learn how to create a developer account and application from scratch, please review Chapter 1,Your First MySpace App.There you’ll find everything you need to know about applying for developer status and creating your first app. Of course, once your first app is created, then what? How do you take it live? How do you make changes to it? How do you add other developers or alter your app’s public Profile? This chapter will show you.
Publishing Your App Getting your app published involves more than simply hitting the Publish button on your My Applications screen (as shown in Figure 12.1).
Figure 12.1
My Applications screen on the developer platform.
266
Chapter 12 App Life Cycle
When you hit Publish, your app won’t immediately be published.The app’s status changes to “Pending” while it’s sent to a team of reviewers who check the app to make sure it actually works and abides by the MySpace Terms of Use and Developer Addendum and the MySpace Agreement. When your app is Pending, you can opt to “Unpublish” it. Unpublishing an app simply removes it from the approval queue; it does not delete the app.
What’s Allowed, or Why So Many Apps Get Rejected There are two major reasons why apps are not approved for publication: n n
The app doesn’t work. The app breaks the rules.
If an app doesn’t function correctly or it violates the Terms of Use, the MySpace Agreement, or the subsequent Developer Addendum to the Terms of Use, it will be rejected. Our own Tic-Tac-Toe app was rejected numerous times for various reasons, which we’ll detail a little later.You only need to visit the App Denial and Status Clarification section of the MySpace Developers’ Forum (http://developer.myspace.com/Community/forums/44.aspx) to quickly realize that app approval can often be a tricky, frustrating, and very human process. First and foremost, though, your app has to work—that means no glaring bugs—and it has to actually do something.Then it has to follow the rules.To read the Terms of Use, the MySpace Agreement, and the Developer Addendum, visit the Application Guidelines page at http://wiki.developer.myspace.com/index.php?title=MySpace_Apps_Developer_ Addendum_to_MySpace.com_Terms_of_Use_Agreement. Here are some of the most common reasons for rejection (culled from the App Denial and Status Clarification forum’s frustrated posts): n
n
n n
n
n
n
Requiring the user to install something (the only exception to this is installing Flash or Silverlight) Anything that hinders the functionality of the site or browsers for the user; for example, an app that doesn’t allow a user to navigate away from the app Canvas surface by clicking the browser’s Back button JavaScript errors (this falls under the “The app has to work” rule) Improperly labeling an application; for example, describing an app as a Chess app when it’s actually an app designed to advertise and sell your Widgets Including an “incentive” to get people to invite friends to the app (points, rewards, etc.) Being incorrectly labeled as “for all ages”; for example, designating a drink-making app (must be 21+) or a dating app (must be 18+) as “all ages” Using either the MySpace name or brand or the name Tom (don’t call your app “MySpace Tic-Tac-Toe” or “Tom’s Favorite Games,” for instance)
Publishing Your App
Dealing with Rejection If your app is rejected, you will be given a reason, as shown in Figure 12.2.You can then acknowledge the feedback (if you’re the app’s creator) by clicking the Acknowledge Feedback button, fix the problem, and attempt to publish the app again. But what if you feel your app was incorrectly denied? What if you don’t understand the feedback? There is recourse:You can contest the rejection.
Contesting a Rejection If you don’t understand the feedback or you think the feedback is incorrect, you can contest the rejection by going to the App Denial and Status Clarification forum (http://developer.myspace.com/Community/forums/44.aspx) and stating your case.To contest the rejection, simply make a post. Note To make sure your post is answered as quickly as possible, follow these posting guidelines: Post Subject Heading [app name], [app ID] - [brief description of the issue/question] Body [app name] [app ID] [denial reason] [your question about the denial]
Figure 12.2
Example of feedback on a rejected app.
267
268
Chapter 12 App Life Cycle
The folks at MySpace are generally pretty fast at replying to posts on the App Denial and Status Clarification forum. Going through the process may take a while, but in the end things usually work out, as they did for the developer in the following case study. A Case Study in Successful Rejection Negotiation Sometimes the app reviewers make mistakes, but they can be appealed and rectified. We show you how to do just that in the following steps and accompanying figures. By following up, you can get those mistakes reversed. Remember that the app reviewers don’t reject apps out of malice or spite, but they do make mistakes. When negotiating app rejections, as in life, you can catch more flies with honey than with vinegar, so try to be nice! 1. The developer makes a post arguing that his app was incorrectly rejected (see Figure 12.3). 2. The review team representative responds, acknowledging the mistake and requesting that the developer attempt to publish the app again (Figure 12.4). 3. The developer resubmits the app, and it is rejected again for the same reasons.The developer posts again. (See Figure 12.5.) 4. Here’s where staying with it and showing perseverance pays off—the review team representative flags the app for approval (Figure 12.6). Yes, it must have been frustrating and yes, the process took four days, but the app was eventually approved. So, stay with it, and if your app is incorrectly rejected, it should get fixed.
Figure 12.3
The original poster contests the app’s rejection and states his case.
Publishing Your App
Figure 12.4
The MySpace review team member responds.
Figure 12.5
The original poster explains that the app was rejected again.
269
270
Chapter 12 App Life Cycle
Figure 12.6
The MySpace review team member resolves the issue and approves the app.
Even We Aren’t Immune Just to show what a black art getting apps approved can be, here are some examples of the rejection e-mails we received while writing this book.We have been intimately involved with the MySpace OpenSocial platform since its inception, yet it still took four tries to get an app live. Our intention was to create one master Tic-Tac-Toe app that showed the final result, along with apps for each chapter of the book, to show the progress of the app up to that point. Take 1: Multiple Submissions From: Developer <
[email protected]> Date: Mon, Jan 26, 2009 at 12:43 PM Subject: MDP Submission Denied: Multiple Submissions To:
[email protected] Thank you for your submission to the MySpace Developer Platform. We review all applications to ensure that they are compliant with our Terms of Use and Application Guidelines. Unfortunately, your current applications do not meet our criteria for the following reason(s): 119328 120983 125557 124920
TTT TTT TTT TTT
Chapter Chapter Chapter Chapter
1 2 5 3
1. Templated Apps. If an application is "templated," it must (i) contain substantial differences between each instance of the application and (ii) allow
Publishing Your App
for a User to switch between the various templates within the application itself. Please combine all your templated applications into a single app. If your application is still going through development, please complete the application before submission as this area is not intended for testing. Please update your app and republish. Note, fixing the issues above will not guarantee approval as we were unable to perform a full review and may uncover additional problems. If you have questions, please refer to our forums or documentation. MDP Team
Our first rejection happened because we submitted the apps for Chapters 1, 2, 3, and 5 all at once.The reviewers felt we were trying to submit “templated” apps, because there were only slight differences between them.This was, of course, true. Each chapter built slightly on the one before it.To get around this, we decided to publish just one at a time. Take 2: User Experience/Functionality and Loading Issues From: Developer <
[email protected]> Date: Tue, Jan 27, 2009 at 5:20 AM Subject: MDP Submission Denied: [Tic-Tac-Toe Chapter 5, app ID: 125557] To:
[email protected] Thank you for your submission to the MySpace Developer Platform. We review all applications to ensure that they are compliant with our Terms of Use and Application Guidelines. Unfortunately, your current application does not meet our criteria for the following reason(s): Issue/Request: 1. User Experience / Functionality: The app is not functioning properly. While playing the game, user is allowed to make multiple moves without having to wait for the opponent's turn. 2. User Experience / Functionality: Loading Issues. On Canvas Surface, the "Challenge" tab fails to load and appears blank. Please update your application and republish. Note, fixing the issues above will not guarantee approval as we were unable to perform a full review and may uncover additional problems. If you have questions, please refer to our forums or documentation. MDP Team
271
272
Chapter 12 App Life Cycle
Next we published just one app, and it was rejected for two reasons, both valid. First, you were initially able to cheat the computer player in the Tic-Tac-Toe game by making all three moves before the computer made any.This was a legitimate bug that we fixed. Second, when the reviewers clicked the Challenge tab, they just got a blank screen.This is because we were showing only friends who had the app installed, and none of the reviewers’ friends had installed the app.To fix this, we simply checked for a result of 0 friends and displayed a message to the user. As you can see, the reviewers actually dive pretty deep into the app’s functionality; it’s not just a cursory glance. Once those issues were fixed, we republished and the app went live! We then thought, Well, we’re on our way! We’ll submit the apps one at a time and they’ll all be approved. Hmm, not so fast ... Take 3: Duplicate Applications From: Developer <
[email protected]> Date: Thu, Jan 29, 2009 at 6:53 AM Subject: MDP Submission Denied: [Tic-Tac-Toe Chapter 3, app ID: 124920] To:
[email protected] Thank you for your submission to the MySpace Developer Platform. We review all applications to ensure that they are compliant with our Terms of Use and Application Guidelines. Unfortunately, your current application does not meet our criteria for the following reason(s): Issue/Request: Duplicate applications (124920, 125557). If you have any questions please refer to the developer forums (http://developer.myspace.com/Community/forums/44.aspx). Please update your application and republish. Note, fixing the issues above will not guarantee approval as we were unable to perform a full review and may uncover additional problems. If you have questions, please refer to our forums or documentation. MDP Team
We submitted the next app, and it got rejected for being a duplicate of our previously submitted chapters.This is where things got a little hairy.The reviewer could see our other live app along with the app we had just published.The reviewer then loaded the live app, compared it to the app we were trying to publish, and felt they were similar enough to be considered duplicates. So how did we work around this? At first it seemed as though we’d need to 1. Create a bunch of e-mail accounts. 2. Make a spreadsheet with all the e-mail account logins and passwords.
Publishing Your App
3. 4. 5. 6. 7.
Sign each one up for MySpace. Apply for developer status on each account. Have each account create one app. Publish each app. Once all the apps were live, add our master account as a developer to each app.
And, well, that’s essentially what we had to do. But we did use one small trick that you may find useful. Our master account e-mail address is
[email protected]. Gmail allows you to create throwaway e-mail addresses by using the plus (+) character in the address. Because of this, we didn’t actually have to create e-mail addresses for each app or developer account. For our Chapter 1 developer account, we signed up for a MySpace account with the e-mail address
[email protected]. At the end, we had a bunch of MySpace accounts with developer status, and all the e-mails sent to any of those accounts actually got delivered to our master account.This allowed us to sign in to a single e-mail account and administer several developer accounts at once. So, that took care of creating the developer accounts, but each app needs its own e-mail address, too.Well, we thought we could just follow the same trick and create apps using an e-mail address like
[email protected]. But no, that didn’t work. The Create App page doesn’t accept e-mail addresses with the plus character. So, we could just create a bunch of e-mail addresses for each app, right? You’re wrong again, dear reader. Each app gets its own MySpace Profile like any other MySpace account, so you can log in as an app.This means you can create an app with a dummy e-mail address, log in to MySpace as the app with that e-mail address, and then change your e-mail address.When you first log in, you get a warning box at the top middle of your Home page saying your e-mail address isn’t yet verified; at the bottom of this box is a “Wrong e-mail address?” link. Clicking this link allows you to change your e-mail address, and there it will accept pluses in the address. The final result was that we had a bunch of e-mail addresses like this:
[email protected] [email protected] [email protected] [email protected] [email protected] [email protected] These all went to the same master e-mail address,
[email protected], and allowed us to manage everything from one central location. Phew! Of course, there are no guarantees that this bug or work-around feature won’t be fixed by the time you read this, but it’s worth a shot.
273
274
Chapter 12 App Life Cycle
Managing Your App Once your app is live, you can hide it, publish changes to the code, or delete it.
Hiding and Deleting an App When you hide an app, you change the status of the application to “Hidden.”This only stops your app from showing up in search results in the apps gallery (http://apps.myspace.com). It does not hide the app from users who already have the app installed. Warning The only way to hide an app from users who already have the app installed or force an uninstall is to delete the app. Hiding the app does not hide it from existing users.
If you want to delete an app, click Delete.You will be prompted with a confirmation. Click Yes and the app will be deleted.
Making Changes to a Live App (Multiple Versions) Once your app has gone live, there are two versions of it: n n
Development version Live version
When you click Edit App Source on a live app, you see two links: Dev App and Live App at the bottom of the screen (as shown in Figure 12.7).
Figure 12.7
Dev App and Live App selection options.
Managing Your App
Figure 12.8
Note the &appvers=dev at the end of the URL in the address bar.
Your Live version can’t be edited directly.You must edit your source code on the Development version. Any changes are immediately reflected in the Development version and don’t appear in the Live version until the app is republished.When you’re testing or viewing your Development version, you should see &appvers=dev at the end of the app’s URL, as shown in Figure 12.8. So, if you ever want to see the Development version of your app, just append &appvers=dev to the end of the query string for the surface you’re currently viewing.
Republishing a Live App You’ve changed your code, tested it, made some improvements, and you’re ready to release version 1.1; now what? Click Publish Changes to start the review process over again.
Changing the App Profile/Landing Page When users discover your app in the app gallery or through a friend’s page, they’ll click on your app link and be taken to your app Profile page.This is like a landing page, and it contains that magical button that you want everyone to click: Add This App. To see what your app’s Profile looks like, go to the Canvas page and click More Info at the top of the page. Now, do you see a boring default surface with little more than the basic details? Or something that looks more like the landing page shown in Figure 12.9? This is one area that developers often overlook, but it’s very important.The app’s Profile is the first introduction of a potential user to your app, so you should try to sell your app here in order to turn a potential user into a user.You’ll notice that the large professional app development companies have polished-looking app Profile pages.
275
276
Chapter 12 App Life Cycle
Figure 12.9
App Profile page for iLike (http:/myspace.com/ilike), a popular application.
Common tweaks include making the Add This App button larger, adding branding, putting up screen shots of the app in action, and just generally making the page more appealing to prospective installs. Editing your app Profile page is similar to the way users “bling out” their MySpace Profile pages, and you’re under the same restrictions.That means no JavaScript, but you can modify CSS and add image, anchor, and various style tags, like bold. To edit the app Profile page, you must first log in to MySpace as the app (not as the developer), click Edit Profile, then click the CSS tab. Inside the CSS box you can put any valid CSS.You can hook your CSS styles into existing elements on the page using their IDs or element types. For example, the Add This App button has an ID of profile_appinstall_button, so you can style it using the # CSS selector. Some other useful element IDs are found in Table 12.1, and you can use Firebug or view the source of the page to see all the markup on the page. Another option is to add custom HTML elements to the About section. Inside the Edit Profile page click the About tab; inside the box you can add HTML elements including and tags. If any of the tags have a class attribute, you can add the class definition into the CSS markup under the CSS tab. Here’s an example of how we spiffed up the Tic-Tac-Toe app Profile. First, let’s look at the About section:
Managing Your App
Table 12.1
CSS Selectors in MySpace
CSS Selector
HTML Element
#profile_appinstall_button{}
Add This App button
#mainLayout{}
Main container for the contents of the Profile; doesn’t include the header or footer of the page or the gray (by default) background
#profile_appdetail{}
Container for the top left portion of the Profile, including the app’s icon, developers, number of installs, and categories
#profile_appinstall{}
Container for the top right portion of the Profile; includes the Add This App button
#profile_aboutMe{}
Container for the bottom portion of the Profile, from “About [app name]” to above the forums
#profile_forums{}
Container for the forum links
body{}
The body element for the page, useful for modifying the background color and image
The game that’s sweeping the world!
Features:
- Custom backgrounds!
- Challenge friends!
- Play the computer!
Click the Add this App button above to play now!
Then the CSS section: body{ background-image:url(http://c1.acimages.myspacecdn.com/images02/31/l_e98f74e73ace46f2939c2de7322fcce4.png); } #profile_appinstall_button{ background-color:#1E4C9F; border:solid 5px #0cf; color:White; font-family:Verdana; font-size:28px;
277
278
Chapter 12 App Life Cycle
padding:10px; } .ttt_img{ float:left; } .ttt_text{ font-family:Verdana; font-size:18px; } .ttt_text ul{ list-style-position: inside; text-indent: 20px; } .clear{ clear:both; }
The final product is shown in Figure 12.10. Tip You’ll notice that in both our CSS and About sections we include images. To save on hosting and bandwidth costs, both images are hosted by MySpace. While logged in as the app to edit the Profile, we uploaded the photos through the Upload link on the Home page, just as normal users would upload their photos. Once the photos are uploaded, you can view the source of the image by right-clicking it and selecting Properties. Voilà! Free image hosting for MySpace apps!
Figure 12.10
Updated Tic-Tac-Toe app Profile page.
Managing Developers
Managing Developers We already know how to edit the source code, details, and templates, but you can also use the developer platform to manage multiple developers. By clicking the Developers & Testers option under an app on the My Apps page, you can add and delete codevelopers. Once you click on Developers & Testers, you will be shown your friend list.To be added to your app, the developer must first be your MySpace friend. He or she must also be an approved developer; all of the approved developers in your friend list will have a gear icon under their pictures.To add a friend as a developer, click and drag his or her picture box into the Developers column.To remove the friend as a developer, simply click on the red circular icon in the Developers column next to the friend’s name. Before you grant developer status to anyone, it’s recommended that you back up your source code with every version of the app. Note You can’t set specific permissions for each codeveloper. Once someone is added as a developer to an app, the person can install the app in development status, make changes to the code, and even submit the app for publishing. However, the person can’t delete the app, add other developers, or acknowledge feedback. For example, if your app is denied publication because of an error, only you, the original developer, may acknowledge feedback and revert it to a ready-to-publish status.
There’s one last kick in the pants. As long as you’re someone’s friend and an approved developer, that person can add you as a developer to his or her app and you can’t say no or remove yourself as a developer.The app creator must remove you. So, if someone adds you as a developer on a questionable app, such as a Battlestar Galactica Sucks app, your name will be attached to it publicly unless you ask the original creator very nicely to please remove you as a developer.
Managing Testers You may want to have a few people test your app before you make it live, or test it yourself on different accounts.That’s smart. So, how do you do it? From the My Apps section of the developer platform, click the Developers & Testers link.You’ll be shown a list of your friends alongside a Developer column and a Test Users column. Simply drag and drop your friends over to the Test Users column to add them as testers. Once added, they’ll have instant access to your app in progress.
Event Handling—Installs and Uninstalls The concept of signed data calls was covered extensively in Chapter 8, OAuth and Phoning Home, and it really comes into play here with an app’s Install Callback URL and Uninstall Callback URL.These two settings fields can be found under the Edit Details tab of your app and are shown in Figure 12.11. Basically, whenever a user installs or uninstalls your app, MySpace makes a signed request to these URLs and passes the user’s ID. So, other than the OAuth signature, it
279
280
Chapter 12 App Life Cycle
Figure 12.11
Callback URL settings.
sends only one piece of information, the user’s ID.What you then choose to do with that piece of information is up to you. Some apps use the user IDs to simply track who has the app installed. Others may use them to create profiles for all the users and add them to an off-site database. Again, how you use the ID that’s passed in to your callback URLs is your decision.
Suspension and Deletion of Your App Suspension is pretty rare in the world of MySpace apps. If an app violates the Terms of Use and gets past the reviewers, the app developer usually receives a warning before the app is suspended or shut down.The developer then has 24 hours (or however long is specified in the violation notice) to fix the problem.This is not set in stone, and if an app grossly violates the Terms of Use, or the law, it can be suspended immediately. Any suspension or violation notices are sent to the e-mail of the app’s creator (not the app’s e-mail address or the e-mail address of any codevelopers), so make sure you monitor your app developer e-mail address. Warning If your MySpace account is deleted for violating the MySpace Terms of Use, you will not be able to log in and manage your application. Even getting your now-orphaned application deleted can be a major headache. There is a work-around for this, though—don’t violate the Terms of Use.
If your app is suspended, you need to fix the problem before the app is reinstated. So, while you can appeal on the forums, there is no quick fix if your app breaks the rules.
Summary
Summary If your app works and doesn’t violate the Terms of Use, you should have no problem getting it published and up and running. However, in the rare instance that an app is incorrectly flagged, we hope this chapter will help you navigate the ins and outs of app publishing. Remember, though, that it’s easier to catch flies with honey, so be courteous when dealing with the developer relations team. Note Code listings and/or code examples for this chapter can be found on our Google Code page under http://opensocialtictactoe.googlecode.com.
281
This page intentionally left blank
13 Performance, Scaling, and Security W
e’re going to delve into some of the more esoteric aspects of building your app empire. It’s one thing to build a “Hello World” app, or even put together a small mashup. It’s something else entirely to build an app that becomes an everyday part of life for over a million people. Consistent performance and user trust become some of the most important aspects of your app. This chapter is not an exhaustive study of Web app performance. If you want to study Web app performance, there are many resources online.Yahoo! Developer Network has done some excellent research into performance and publishes the information online. You could also pick up any number of books on performance, such as High Performance Web Sites by Steve Souders. It is a large topic worthy of several books.We are, however, going to cover some of the basics, such as the difference between performance and scaling of an application and some design hints to improve your app’s ability to grow.
Performance and Responsiveness To start this discussion we’re going to explore different aspects of performance. Performance is an overloaded term. Cars are often said to have good performance when they can reach top speed quickly and handle tight turns without sliding off the road. Another less common but equally correct statement is that a car has good performance if it gets good gas mileage and can travel long distances on a limited amount of fuel. People are often said to perform well on the job if they are able to handle multiple competing priorities simultaneously. It’s not that anything is getting done fast, but that lots of things are getting done.
What Is Responsive Performance and What Is Scale Performance? Performance is generally discussed in terms of how responsive a system is: “When I click the button, does something happen immediately?”To borrow from the auto industry, responsive performance is your “zero-to-60” measure. If your app is like my boss’s
284
Chapter 13 Performance, Scaling, and Security
Ferrari, it’s doing the equivalent of zero to 60 in 3.5 seconds. If your app is more like my jalopy (a 1985 Toyota Tercel, engine factory-rated at 65 HP when it rolled off the assembly line), it’s doing zero to 60 in 35.0 seconds. A secondary, and less common, measure of performance is in terms of scale: “If everyone in the world pushes the button at the same time, does it still work?”This is equivalent to a hot summer day when everyone in the city turns on the air conditioning. If the power grid scales, everyone’s air conditioner continues to function and homes and apartments cool off. If the power grid doesn’t scale, as in California, everyone’s lights flicker or brown out.This behavior is not to be confused with the situation in the mid-nineties when a rat chewed through a main feeder line, which in turn blew some circuits and took down the Western power grid (a failure of robustness). Nor is it to be confused with events of the late nineties into the early twenty-first century when Enron was running its “Death Star” and “Fat Boy” projects to create artificial scarcity and brownouts so it could jack up the price and “relieve the congestion” (a failure of public policy).
Design for Responsiveness Responsive performance is generally well understood. For years the common wisdom in many computing circles, particularly with Internet applications, was to ignore responsive performance and focus on feature delivery.The underlying assumption was that because computing power and network connection speeds were constantly improving, by the time a given product had reached market penetration, the underlying hardware and network speeds would have improved enough to account for poor performance designs in the early stages of development. An object example of this idea that hardware would fix all issues with software responsiveness can be witnessed in the early growth and attitudes of the Java community. Programming in Java was very powerful because of the rich and clean abstraction layer it provided.The promise of a cross-operating-system language that one could “write once, run anywhere” was very compelling. Unfortunately for Sun, the hardware performance envelope failed to progress quickly enough to account for the initial nonperforming virtual machine design. Over the course of a decade the JVM ( Java Virtual Machine) did improve and hardware did catch up, but the damage was done.Today, because of this and a number of other reasons, Java is almost never found in user-facing applications and user interfaces, for which it was originally designed. Instead, it is mostly relegated to back-end servers and some embedded applications. While CPU power continues its relentless Moore’s march, overall computer performance has hit a bit of a plateau. Much, if not most, of the excess computing power is being used for visual effects and to support more background processes and higherlevel programming tools. Another major driver in this performance plateau, in our opinion, is the rise of Web applications and the underlying constraints of network latencies and Web browser rendering engines and JavaScript interpretation.
Performance and Responsiveness
Broadband connection speeds, at least in the United States, have been increasing more linearly (actually, probably more like cubically, but we’re not math majors), not exponentially. Back in 1993, when NCSA Mosaic first launched and brought the Web out of the doldrums of Telnet and IRC, most people who had private Internet access were connecting via 9600Bps or 14.4kBps modems. For a short while, connection speeds almost doubled every two years (following a path similar to Moore’s law) but have moved more slowly in recent years as broadband providers seek to wring profits from their networks by creating artificial product differentiation.Two recent studies put the average connection speed in the United States somewhere between 1.9 and 4.8MBps.A U.S. Department of Agriculture census found that in 2007 only 33% of all farms had broadband connections.That means the rest were still on 56K modems or had no connectivity at all.This is in contrast to Japan, which has an average broadband speed of 61MBps (about ten times what would be considered the upper half of U.S. connection speeds). Responsive performance has seen a bit of a renaissance in the last few years. New research into user interaction and how great the effect of response times is on user behavior has highlighted the importance of responsive design.A responsive product is now seen as a strategic differentiator in a world of me-too products.That and new research into what leads to a responsive design have given rise to a number of rules for writing Web apps. Basic Rules Here are some basic rules that every developer concerned with performance and responsiveness should know: n n n
Place CSS styling at the top. Place JavaScript, particularly includes, at the bottom. Simplify your markup.
At this point in time these rules are bordering on common wisdom, much as “Size your images” and “Make pages smaller” were in the late dot-com days.
Responsive OpenSocial App Performance Guidelines There are also some less obvious guidelines that are specific to writing OpenSocial apps. These rules will help your app be more responsive when dealing with fetching and working with OpenSocial data. Following these guidelines will also help your app perform consistently and keep your users coming back rather than being annoyed by quirks and stutters when using your app. 1. 2. 3. 4.
Don’t re-request the same piece of data. Prefetch record lists for paging. Batch requests. Handle your errors.
We’ll cover each rule in greater detail in the following sections.
285
286
Chapter 13 Performance, Scaling, and Security
Rule 1: Don’t Re-Request the Same Piece of Data Many times when we review code, we see this mistake being made. Recently we were reviewing some code written in jQuery that was reselecting the same node each time it wanted to change a property: $("#navhead").addClass("enabled"); $("#navhead").text = "Welcome";
This business went on for another five lines. Because it was so convenient to reselect the node, the author mistook it for being free. In actuality each call went through a seven-layer call stack to select the node and add the jQuery extensions.The simple tune-up was to assign the result to a variable and reuse it: var navHead = $("#navhead"); navHead.addClass("enabled"); navHead.text = "Welcome";
This is fairly basic stuff.We know you jQuery people out there are saying, “Well, the dev should have just chained things.” True, but that is a jQuery-specific feature. Stick with us here for the example. The same rules apply for any piece of data being requested via the OpenSocial framework.The difference is that the performance ramifications are much more noticeable.Take the case of requesting the Viewer: function getViewer(myCallback){ var req = opensocial.newDataRequest(); var id = opensocial.IdSpec.PersonId.VIEWER; req.add(req.newFetchPersonRequest(id), "viewer"); req.send(function(data){ myCallback(data.get("viewer").getData()); }); }
This call is fine—once.What if this code were to be called every single time the Viewer data was needed? Not only is that execution clunky with extra callbacks, but it’s unnecessarily slow. A better solution is to execute this call once, place the result in a variable, such as myAppStuff.Viewer, and read it from this variable every time it is needed. In the early versions of the MySpace OpenSocial container, the container code would actually make an XHR request to the REST endpoint to refetch the Viewer data every time this code was called. Over time, MySpace has made some good optimizations on the container side. Now, many data call results are either cached and re-served client-side or prefetched. Even with those optimizations, the code still executes a 20-function code call each time the data is re-requested. The MySpace optimizations for fetched data do not extend to the results of makeRequest calls. If your code is calling out to your servers or some other external server, only re-execute the call if you think the data might have changed.
Performance and Responsiveness
Rule 2: Prefetch Record Lists for Paging This performance enhancer is about both efficiency and responsiveness. Paging through a list of friends is a very common operation.There are two ways to do this: our way and the wrong way. ( Just kidding, but only a little.) We’ll start with a simple example.Your app wants to page through the Viewer’s list of friends, four at a time.The simple and obvious solution is to place code handlers under the Prev and Next links to create a newFetchPeopleRequest, passing in the start record and number of records to retrieve.The guts of the code appear something like this: var pageSize = 4; var curPage = 0; function loadFriendsPage(){ var dataReqObj = opensocial.newDataRequest(); var viewerReq; var viewerFriendsReq; var startNum = (curPage * pageSize) + 1; var idspec = opensocial.newIdSpec( {userId : 'VIEWER', groupId : 'FRIENDS'}); var requestOpts = {}; var fldEnum = opensocial.DataRequest.PeopleRequestFields; requestOpts[fldEnum.MAX] = pageSize; requestOpts[fldEnum.FIRST] = startNum; viewerFriendsReq = dataReqObj.newFetchPeopleRequest( idspec, requestOpts); dataReqObj.add(viewerFriendsReq, "viewerFriendsReq"); dataReqObj.send(loadFriendsPageCallback); var prev = document.getElementById("prevLink"); if(curPage == 0){ prev.className = "pageaction disabled"; } else{ prev.className = "pageaction enabled"; } } function loadFriendsPageCallback(data){ var elem = document.getElementById("currentFriends"); var label = document.getElementById("pageHeader"); var fData = data.get("viewerFriendsReq").getData();
287
288
Chapter 13 Performance, Scaling, and Security
if(data.hadError()){ var err = "error: " + fData.getErrorMessage(); label.innerHTML = "error: "; return; } var startNum = (curPage * pageSize) + 1; var endNum = startNum + pageSize; label.innerHTML = "Friends " + startNum + " to " + endNum; var items = ""; fData.each(function(person){ var src = person.getField(opensocial.Person.Field.THUMBNAIL_URL); items += ""; items += person.getField(opensocial.Person.Field.NICKNAME); items += "
"; }) elem.innerHTML = items; } function getPreviousFriends(){ if(curPage < 1){ return; } curPage--; loadFriendsPage(); } function getNextFriends(){ curPage++; loadFriendsPage(); }
The code works fine. If you test it in a simple scenario, the performance is probably okay as well, provided it’s not during peak hours. Looking at the network traffic (see Figure 13.1) tells a more interesting story. Each request shown in the figure is approximately 150ms.That’s not much time in and of itself, but that time adds up. If the user goes through six pages, a full second of time is used waiting for the network.What’s more, if the user pages backward, the same data your app just had is re-requested.This violates Rule 1: Don’t re-request the same piece of data. If your app is simultaneously attempting to retrieve album information or communicate with another server, this design would add noticeable and unnecessary overhead. A better design, and one used by the FriendPicker widget, is to ask to preload all the data in the background, then page by moving through the local data list. In order to make this happen, we use most of the same code with two additional code elements: an
Performance and Responsiveness
Figure 13.1
Network traffic on a wasteful pager.
array to hold all the friend information and a new display function. Here is a look at the modified method of retrieving the friend list: var pageSize = 4; var curPage = 0; var allMyFriends = []; function loadFriendsInfo(){ var dataReqObj = opensocial.newDataRequest(); var viewerReq; var viewerFriendsReq; var idspec = opensocial.newIdSpec( {userId : 'VIEWER', groupId : 'FRIENDS'}); var requestOpts = {}; var fldEnum = opensocial.DataRequest.PeopleRequestFields; requestOpts[fldEnum.MAX] = 100; requestOpts[fldEnum.FIRST] = 1; viewerFriendsReq = dataReqObj.newFetchPeopleRequest( idspec, requestOpts); dataReqObj.add(viewerFriendsReq, "viewerFriendsReq"); dataReqObj.send(loadFriendsPageCallback); } function loadFriendsPageCallback(data){ var fData = data.get("viewerFriendsReq").getData();
289
290
Chapter 13 Performance, Scaling, and Security
if(data.hadError()){ var err = "error: " + fData.getErrorMessage() label.innerHTML = "error: "; return; } fData.each(function(person){ var src = person.getField(opensocial.Person.Field.THUMBNAIL_URL); var item = ""; item += person.getField(opensocial.Person.Field.NICKNAME); allMyFriends.push(item); }); showCurrentPage(); }
You may notice that the code for these two methods is actually smaller. It’s also cleaner because the two methods simply deal with retrieving and managing the data.The basic information of each friend (name and image) is pushed into our new data array, allMyFriends. Display of the data is now external to the data retrieval methods and in the showCurrentPage method.This separation of concerns leads to more efficient and maintainable code. function showCurrentPage(){ var elem = document.getElementById("currentFriends") var label = document.getElementById("pageHeader") var startNum = (curPage * pageSize) + 1; var endNum = startNum + pageSize; label.innerHTML = "Friends " + startNum + " to " + endNum; var items = ""; if(startNum < allMyFriends.length){ for(var i=0; i < pageSize; i++){ if((startNum + i) >= allMyFriends.length ){ break; } items += allMyFriends[startNum+i] + "
"; } } elem.innerHTML = items; var prev = document.getElementById("prevLink"); if(curPage == 0){ prev.className = "pageaction disabled"; }
Performance and Responsiveness
else{ prev.className = "pageaction enabled"; } }
function getPreviousFriends(){ if(curPage < 1){ return; } curPage--; showCurrentPage(); } function getNextFriends(){ curPage++; showCurrentPage(); }
Now each time the user clicks the Prev/Next link, the page is repainted with data coming from a local array variable, not across the network.The network traffic sniffer results shown in Figure 13.2 also tell the story. The initial request now takes 400ms. It happens only once, though.This image also displays all the traffic after scrolling back and forth through the list several times.These measurements were taken on an account with 52 friends.The step-by-step pager would have taken 1950ms to retrieve the same number of records—almost five times as long! Not only that, but it would have continued to waste network requests each time the user scrolled.To really see the difference, the user can click the Next link in quick succession in both implementations.The second displays smoothly and quickly.The first will occasionally stutter, then catch up. Sometimes it will skip pages and can even get out sync if the XHR responses are returned out of order.
Figure 13.2
Network traffic on a good pager.
291
292
Chapter 13 Performance, Scaling, and Security
Rule 3: Batch Requests The OpenSocial spec has a built-in mechanism for batching requests. Use it. At the time of this writing, MySpace sends these requests individually, but if you write your code to batch things, when the batching performance optimization is made on the underlying MySpace container, your code will be able to take advantage of it immediately. For servers under your control, design your endpoints to deliver all the data your app needs to execute a given task in one shot.This may feel as if it’s violating the integrity of your code design, where each type of data gets its own endpoint, but it is a worthwhile optimization. As an example, say your app allows virtual flowers to be sent to a friend. The user has an allotment of credits for buying flowers. One design is to have one call to purchase the flowers, then a second call to request the user’s remaining credits and update the “available credit” display. A better design is to make a single request that purchases the flowers and returns the remaining available credits in the response.You just cut your server request load in half! Rule 4: Handle Errors Stability and robustness will be covered in more depth later in this chapter, but they’re worth touching on now. Often when an unhandled error occurs in program execution, the program is left in an indeterminate state—it doesn’t understand where it is. As it attempts to continue execution, it may make calls with insufficient or invalid inputs, generating even more errors. More often than one might think, a program descends into a state of thrash— alternately requesting one piece of data incorrectly, getting a bad response, requesting a different piece of data incorrectly, getting a bad response, and ultimately descending into an infinite loop, thrashing back and forth between requests until a stack overflow happens or the browser pegs the CPU and has to be forcefully killed. If this sort of error happens often enough with your app, it may come to the attention of MySpace.When that occurs, you are typically given a few hours to rectify things. If the errors are bad enough that your app is adversely affecting other apps by tying up connections or causing user Home pages to crash, it may even be summarily suspended. Handle your errors.
Design for Scale An application is said to “scale” if it maintains its performance characteristics while the number of users increases dramatically. MySpace scales. At the time of this writing, MySpace has in excess of 150 million unique users. Friendster did not scale. It predated MySpace and had more users initially, but once it hit some level of critical mass, the system froze and became nonresponsive. As a result, everyone left and moved to MySpace.
Design for Scale
Application Scale Definitions Applications are typically designed to perform and scale within one of these definitions: 1. Small/Workgroup Typified by a user base of fewer than 100 users/month. Mom-and-pop Web sites, niche e-commerce sites, and internal IT projects designed to address the needs of a single workgroup fall into this range. Deployments are usually single-server or partial-server (a server hosting several applications). 2. Enterprise Enterprise-scale applications are designed to satisfy the needs of an entire organization or a large part of an organization, servicing anywhere from a few hundred to tens of thousands of users. They tend to be industry-specific, like a medical records management system or a payroll system. Deployments may be to a single dedicated server or a small cluster (three to five computers). Classic relational databases and reporting systems are very common. Enterprise applications are almost never consumer-oriented. 3. Internet Internet-scale applications may service from several hundred thousand to several hundred million users. Internet-scale applications are almost always consumeroriented, although some also provide business-oriented features. These applications have unique designs to reach their performance needs. The architecture appears foreign to anyone trained solely in classic computer science and data modeling and seems to break many core rules. Deployments are across tens, hundreds, or even thousands of servers and must be able to handle individual server failures.
There are several applicable guidelines to keep in mind when designing for scale.The sidebar “Application Scale Definitions” identifies different scaling terms to consider. At MySpace, the developers think only in Internet scale. As an app developer who could reach millions of users, you should also be thinking in Internet scale.
App Guidelines for Internet-Scale Performance Here are some simple guidelines to consider when designing for scale. Follow these rules and you won’t get tripped up by some of the most common scaling mistakes. 1. 2. 3. 4. 5.
Know your scaling point. Identify scaling bottlenecks. Remember what you know. Scale horizontally. Move beyond relational databases.
293
294
Chapter 13 Performance, Scaling, and Security
6. Push work to the nodes. 7. Consider utility computing. 8. Load-test your system. Your app must also scale.What works for a few hundred or even a few thousand users may not work for a few hundred thousand users. If you do hit the magic tipping point and your app takes off, there will be little time to redesign things. Take the example of Own Your Friends (OYF), the first big-hit app on MySpace. When Own Your Friends launched, the install rate was just a few hundred a day. Something happened at around 30 days and the app reached the magic tipping point (somewhere between 20,000 and 60,000 installs), and the app install rate jumped by an order of magnitude. At 60 days OYF was growing by over 100,000 installs a day.The developers (moonlighting at the time) were staying up all night and working weekends to try to bring more servers online and eliminate bottlenecks.Yes, it may have been exciting and frantic, but it was also a time that very nearly cost them the business. Users are fickle, and if you fail to execute when your app becomes the big hit, you rarely get a second chance to make a first impression. Now, let’s delve just a little deeper into our scaling rules. Rule 1: Know Your Scaling Point The “scaling point” is the point in a system’s utilization where it is no longer able to accommodate additional usage. If this scaling point is outside the system’s expected usage parameters, the current architecture should be adequate. If this scaling point is inside the system’s usage parameters, you need to adjust your architecture to accommodate a larger scaling point. In terms of pure numbers, we can calculate how much data will overflow the available RAM with some simple math. Let’s say you have a user table with the structure shown in Table 13.1. Every row of data is 304 bytes. If 2MB are available to hold this database table, your table could hold 6578 records and still fit completely in memory.That’s not very many records. All this begs the question of how much data is too much for a single server. That’s not an easy question to answer because it depends so much on how your app is using the data and the overall load of the system. Why don’t more database-backed software applications cause their servers to tip over? It has to do with partial usage, the principle of locality, and load. Irrespective of the number of users of a system, not all of them are actively pushing buttons at the same
Table 13.1
Sample User Table
Column
Field Data Type
Size
ID
32-bit integer
4 bytes
Name
100 Unicode character
200 bytes
FavoriteColor
50 Unicode character
100 bytes
Design for Scale
time. Also, only some of the data is used at a given time, and within a time period it tends to be the same data, like a nurse updating the same ten medical records. An enterprise-class decision support system might have five or six data tables with a few hundred thousand records in them, plus a multitude of other tables having 20 to 100 records in them.The application is quite often required to go to hard disk in order to look up the data to perform some operation, such as running a report. Only a fraction of the people who have access to a system use it at one time, though. In a company of 10,000 people, only 10% may ever access the system. Of that number, perhaps only 10% use it at a given time.That works out to 100 concurrent users. If the average time between clicks is 5 seconds, the application must be able to process 20 requests/second for this system to operate. Even with an architecture not specifically designed to scale and the database reading from disk on every request, the system should continue to perform handily (the hard disk latency allows for over 100 requests/second).To reach the scaling tipping point of this system, usage would have to increase by a factor of 5 (go from 10,000 employees to 50,000 employees or increase usage from 10% to 50%). Rule 2: Identify Scaling Bottlenecks The first step in designing for scale is to identify potential bottlenecks. Bottlenecks occur wherever there is a limited resource that many or all of the clients need to access.This could be a REST endpoint on a single server, a routing method, a shared database table, or even a single shared log file. The first scaling bottleneck most applications hit is the database.This tends to be the classic point of scaling failure. If you can design your system to not use a database, do it. If your needs are simple enough, the app data store may be enough.Then you will be building on the battle-hardened MySpace infrastructure. Unfortunately, apps of any complexity can rarely get around using a database. Other sorts of back-end data stores have been experimented with, such as simply writing to a flat file, but a database almost always outperforms them under load.
Dissecting How Hardware Affects the Database There are several reasons why databases tend to be the primary scaling bottleneck: the physical limitations of reading from a hard drive, CPU saturation from processing queries, and buffer cache running into the wall of available physical memory. If a system is using a misconfigured database, it may also run out of available concurrent database connections. To illustrate, let’s examine the read performance of various components in a computer, from the CPU down to the magnetic hard disk drive. Performance tends to change by orders of magnitude. In-CPU registers and L1 cache read at a speed of 2 CPU cycles. The L3 cache, if present, has a latency of 24 CPU cycles. This is incredibly fast. Hertz is a measure of cycles per second. A 1GHz CPU could conceivably find over 1 billion pieces of data from its cache in 1 second.
295
296
Chapter 13 Performance, Scaling, and Security
Not much data can live in these caches, however. The L1 cache in modern CPUs is around 128kB, depending on the processor. The L2 cache is commonly 256kB or 512kB. Higher-end chips have added an L3 cache as large as 8MB. This is still a fairly small amount of data in the grand scheme of things. A small database with 5000 data rows could easily fill that cache. And the reality is that CPU cache is going to be filled with numbers and instructions and bits the computer needs to execute its immediate responsibility. There’s no room for your data there, other than what is being actively worked on. Moving from cache memory that is directly on the CPU to RAM memory, the performance jumps by another order of magnitude. The L3 cache access of 24 CPU cycles is now closer to 200 CPU cycles on 6ns RAM. That translates to theoretically finding 5 million pieces of data in a second using a 1GHz CPU. This is still nothing to scoff at, but it’s quite a difference from data on the CPU cache. A hard disk, where most of your data lives, is not one, not two, not three, but several orders of magnitude slower. This calculation is not completely accurate since it depends on CPU frequency and bus speed, but it is about 1 million times slower to read data off a hard drive than it is to read it out of memory. A 7200RPM drive has a latency of approximately 4ms and a seek time of 9ms. This correlates to finding somewhere between 100 and 250 distinct pieces of data from hard disk in 1 second. 1ms = 1,000,000 ns On average, in the time it takes to find one piece of data on hard disk, a computer could find 20,000 pieces of data in RAM. Of course, these numbers are somewhat deceptive since they are calculated based solely on latency and seek times. In reality, the data should be stored contiguously and read from the seek point. Then the constraints become hard disk platter rotation speed and bus transfer rate. The numbers we’ve given are enough to illustrate the point, however. This performance spread is why most databases attempt to load all available data into RAM memory. There was a tremendous performance boost to SQL Server once it started loading entire tables of data off hard disk and completely into memory. In this manner an entire database can be loaded into RAM and processed at a rate several orders of magnitude faster than if the physical hard drive had to be accessed continuously.
Modern databases use several tricks to speed up performance. One of those tricks is to load the entire database into RAM for processing.This leads to a tremendous improvement in access speeds—of several orders of magnitude. So if the database is in RAM, why is this a scaling performance issue instead of a responsiveness performance issue? It has to do with database size and how much RAM is available. If the database is small enough to fit entirely in memory, the performance will be off the charts. As soon as the database outgrows the size of physical memory available (about 4GB on a 32-bit machine, minus the space needed by the operating system), it must store part of its information on the hard disk. If the database has to read from the hard drive into memory too many times, the computer starts “thrashing.”Therefore, as soon as your database
Design for Scale
crosses that line in terms of the amount of data stored, the performance of your app will plummet. Code that previously could execute immediately will become blocked waiting for the database to respond.This in turn ties up HTTP and database connections.The connections will begin to queue up until the queue is full. Any new connections get turned away, and the Web server returns 503 “Server too busy” errors. At MySpace, we refer to a server exhibiting this behavior as “tipping over.” Rule 3: Remember What You Know This rule is a variation of “Don’t re-request the same piece of data” from the responsive performance section. Let’s say we are going to see the kind of growth outlined previously and expect usage to jump from 10% to 80%—well above our parameters.The first step is to move often accessed and seldom changing data into a location where it is guaranteed to be in RAM. For example, let’s assume the app in question allows users to pick their favorite color.The 20 or so available colors and their hex definitions should be loaded from the database once and placed into a static hash table object.This avoids any question of whether the data will be read from memory or disk.The same holds true for all the domain reference tables—lookup data defined in the system that rarely or never changes. Much of the data being accessed is read-only or limited to a certain session. Often this can be placed in a caching layer, like a MySQL MEMORY table or a memcached system distributed across a cluster of computers.These systems start to get fairly complicated and are a little beyond the scope of what this book covers, but be aware that they exist. In reality, a reasonably intelligent database, like SQL Server, does a fairly good job of automatically keeping the most frequently accessed records in memory, provided the number of “most recently used” records can fit in a single system’s memory. Rule 4: Scale Horizontally Once an app hits a point where it can no longer live on a single computer, it must be split to scale across multiple computers.There are two ways to do this: vertically and horizontally.Vertical scaling is scaling by specialty: One computer is the Web server and another is the database. Another might serve static files, like a content delivery network (CDN). Horizontal scaling is distributing the same work across many computers.The URL www.example.com could resolve to a Web farm of hundreds of machines.The database can be federated across many, many computers. Each computer has the same kind of data but is responsible for only a small segment of the total data, say, 500,000 records. Vertical scaling is typically used to improve responsive performance. Long-running or expensive processes, like running reports, could easily bog down a Web server while servicing only a few users. Unfortunately, there is a limit to how much specialized processing can be segmented out, so vertically scaled systems often top out at distributing the load across two to five machines (Web server, CDN, transactional database server, reporting database server, data warehouse). Some things simply can’t be vertically scaled,
297
298
Chapter 13 Performance, Scaling, and Security
such as a system containing 100 million user records. Horizontal scaling can distribute load across a much larger set of machines. Hundreds of machines are sometimes used to scale out different feature areas of MySpace. Designing for horizontal scale means not requiring all of your data to exist on the same machine or to come from any other single machine. Classic ASP Session handling was notorious for causing performance problems and hampering horizontal scaling. The problems were actually twofold: It dropped the Web server into single-threaded execution per user, and it couldn’t be distributed across different computers. In using classic ASP Session, an application essentially condemned itself to not using a Web farm and not being able to scale. Rule 5: Move Beyond Relational Databases Relational databases are hard to beat within their domain space—small to enterpriseclass transactional systems. Joining data tables starts to lose its efficiency once a system has to scale across multiple servers. Large federated database systems often perform better when laid out in a dimensional model, like a data warehouse.There are data redundancy issues, but the performance gains of avoiding multiple joins can be worth it. Rule 6: Push Work Out to the Nodes In the same way that using a Web farm distributes request load across many computers, some work can be pushed all the way down to the app running in a client browser. By using the JavaScript engine in client browsers, your app can create what amounts to a giant grid computer.Whenever practical, perform calculations in the client script prior to sending the information back to the server. Consider an app that needs to draw a pie chart for each user based on the user’s data. It could be done efficiently for a few users on the server, then pushed to the client as an image.Thousands of requests would make this operation a bottleneck.This work would scale better if the data points were pushed to the client and the browser drew the pie chart using Canvas, Flash, or any of a number of JavaScript graphing solutions, such as PlotKit. Rule 7: Consider Utility Computing Utility computing may have sounded like crazy talk a few years ago, but today it makes sense. Amazon Web Services, Google App Engine, and Microsoft Azure are the three big players in this space. Rather than paying your own network engineers and leasing rack space, it can be much more cost-effective to build on one of these services and leverage the expertise of a multibillion-dollar corporation. Utility computing, by its very nature, is designed for Internet-scale businesses. One of the reasons utility computing sounded like snake oil a few years ago was that few applications needed Internet scale; most were only enterprise scale. Existing applications were also not designed to take advantage of a utility computing architecture, and an existing application is unlikely to port easily to a utility computing model.
Stability and Fault Tolerance
New applications, such as your “Next Big Thing” app, can be built from the ground up on a utility model. Rule 8: Load-Test Your System You can never fully predict how your system will behave in the wild based on class diagrams or whiteboard architectures.You can, however, make a reasonable simulation to find the most egregious bottlenecks and cross-server interaction issues.This is done by load-testing your system. There are numerous load-testing tools available, including numerous free ones. A load-test tool for a Web application starts with one basic premise: It must simulate numerous simultaneous connections to the Web application. From there, the complexity grows; the tools are able to record and play back requests, remember cookies, or perform full activities with your app. A fairly competent tool is the Microsoft Web Application Stress Tool, available from this URL or by searching the Web: http://support.microsoft.com/kb/231282.You can also search for “free load-testing tool” to find numerous results or sites that review available tools, like OpenSource testing (www.opensourcetesting.org/performance.php). If you desire to fully simulate a user experience, tools like Watir or WatiN allow programmatically driving a browser. Sometimes a combination of multiple tools is required to satisfy your needs.
Stability and Fault Tolerance While stability and fault tolerance do not look integral to security and performance at first blush, they are actually one of the most integral aspects of both items. If your app is so unstable that it falls flat on its face twice an hour, it won’t have very good responsive performance, will it? Sometimes these failures even puke up error messages detailing some internal aspect of your system, like database table structure, that can later be used to mount an attack. These rules will help you avoid simply “forgetting” to handle stability issues until it’s too late. Ingrain these simple rules in how you code instead of making them a checklist to run after the fact, and you will avoid many firefights as your app grows. 1. Validate inputs. 2. Test OpenSocial
objects for errors. 3. Provide time-outs and error flow. 4. Don’t assume that weird error was an anomaly. DataResponse
Rule 1: Validate Inputs One of the first things you should do is validate all the inputs to your system. Even user-entered data that is coming from a constrained drop-down should be validated. If you don’t, it may be possible for a nefarious user to inject string data into an integer parameter and bring your carefully built house of cards tumbling down.Validating inputs winds up being both a stability and a security concern.
299
300
Chapter 13 Performance, Scaling, and Security
Rule 2: Test OpenSocial DataResponse Objects for Errors Too often developers consider a single pass of success a sign that they are finished coding. Just because you finally got your app to read the Viewer’s photos does not mean you are finished. Far from it.There are many, many ways an OpenSocial endpoint may fail. Here are a few examples: n n n n n n
App not installed Viewer not defined Network congestion causing a time-out User has no valid data defined for given endpoint User has not granted app needed permissions Underlying server endpoint has a failure
In any of these circumstances your app may go into an indeterminate state. Sometimes the user flow can break because of insufficient data, forcing the user to reload the Web page (or uninstall your app). A little care and planning can keep things humming or get the user through these situations gracefully. At the very least you need to code general error paths. For an extra bonus, try to code recovery paths so the user doesn’t have to refresh the page.
Rule 3: Provide Time-Outs and Error Flow In addition to errors with OpenSocial endpoints, you may have errors with your servers or third-party servers.Try not to just fail. In the course of writing this book, we discovered a silent failure in the main makeRequest method.This was very bad form, because the developer could never write code to compensate for the issue. Databases may time out when overloaded. Rather than doing nothing and allowing all your impatient users to click refresh 20 more times in frustration, further overloading your database, you should display a friendly error with a helpful message, like “Don’t panic.” Not only will it help your scaling and recovery, it’s a common courtesy.
Rule 4: Don’t Assume That Weird Error Was an Anomaly If you saw it once, it will happen again. One of the unexpected rules of working at Internet scale is that there are no edge cases. An error that occurs in 0.5% of cases will happen 5000 times if you have one million users.You just have to decide if those 5000 occurrences justify the cost of tracking down the issue and fixing it.
User and Application Security Security has become a much more prominent topic recently. Identity theft and credit fraud are simply some of the best-known issues with security, and compromised account logins are often how identity theft and credit fraud happen. Add to that malware
User and Application Security
installation, having a compromised machine become part of a botnet for spammers, and phishing.Then there’s simple in-game cheating to think about, too. If you ignore security, your app might be the gateway to compromise hundreds or thousands of people’s personal information. The two basic types of security we’ll discuss are user data security and application security.
User Data Security There is only one main rule in user data security: Respect the rights of your users. Follow it and you’ll already have the answer to most of the other questions that may arise. Often this becomes a matter of policy, so you need to be willing to read (or have someone read for you) the various terms of service.You will even need to consider your own terms of service. More than a few companies have gotten themselves into hot water by thinking that as soon as someone used their services, they owned the right to resell every bit of data that user had provided. At best they ended up with some upset users. Many ended up in court, the target of class-action lawsuits.The first step in avoiding problems is to settle on, write down, and publish your privacy policy. If your app will be pulling user data off of MySpace and using it on your servers, you must be aware of MySpace policies regarding data availability and off-site caching of data. Read the MySpaceID Off-Site Service Developer Addendum if you plan to hold data on your servers.The basic rules are that you can only temporarily cache data off of MySpace.Your app must periodically refresh that data. Feeds, like the friend feed, must be refreshed every 4 hours. User data may be kept for only 24 hours; then it must be purged. It may be refreshed at that point, if required. About the only thing from MySpace your app can store indefinitely is the user’s ID. Information your users supply directly to your app is a different story, though.That is typically governed by your terms of service, applicable laws, and your own good sense. In general, don’t store more data than you have to. Don’t, for instance, store credit card numbers without good reason. And for goodness’ sake, don’t store them in an unencrypted, publicly accessible database. Don’t store independent logins without need—you can rely on the MySpace platform to provide accurate user ID information. An exception to this would be any sort of e-commerce operation. In this case have your security team evaluate the best path. A third-party payment gateway, like PayPal, is a good starting point. Unless there is good reason, don’t provide access points to bulk-download user data. In all likelihood, someone will hack you.
Application Security An application is only as secure as its weakest link. In the case of most apps, this is the interface between your server and the MySpace servers.The best way to address this issue is by specifying all requests as signed. Specific instructions on using signed makeRequest calls are found in Chapter 8, OAuth and Phoning Home.
301
302
Chapter 13 Performance, Scaling, and Security
In addition to locking down the MySpace-to-your-server access points, you need to confirm that other access points to your app are locked down as well. Consider the case that you are writing a space battle MMOG.Your users have to play scenarios and use the game to earn credits. Credits are then used to buy better ships. Users can also earn credits by watching advertisements or registering to donate plasma.To facilitate plasma donation, you set up a REST endpoint so that the blood bank can call back to your app with a user’s ID after that user finishes making an appointment to donate. Because the blood bank is not technically astute, you opt to leave this unsecured. Now Hannah Hacker just loves your game. She stays up nights playing it, hoping to get enough credits to buy that fleet of dreadnoughts. One night she gets so excited she decides to go ahead and donate plasma. Being a good hacker, she’s got Fiddler fired up and is watching all the network traffic while she’s playing.What’s this? It’s an unencrypted redirect to your server from the blood bank with her user ID and a confirmation number? So that’s how plasma donation credits are counted.Well, after about 15 minutes of script work Hannah just signed up to donate plasma 359 times. Don’t roll your eyes.This scenario has already played out more than a few times.
Hacking and Cracking While this does not seek to be a security book, it is worth describing a few of the more common exploits used on the Web.We hope you have at least a passing awareness of these attacks and that this will serve only as a short refresher. If not, you’d better get cracking (no pun intended) and do some real research. The scenario in the previous section describes a simple method to discover and hack an open endpoint. If you are not using signed requests, you might as well add a bunch of buttons titled “Hack This” with a text field to facilitate easier data entry. Earlier, in the chapter about data persistence (Chapter 4, Persisting Information), we wrote a Cookie Jacker application. Everyone can read your cookies (including your competitors), so only the most transitory data, like temporary session data, should be placed there. And really, not even that. The app data store is not a secure store. Data is stored on a per-app/per-user basis, but your app can read the Viewer’s friends’ app data. Even if your app does not explicitly read other users’ data, it is quite easy to hack. Here’s how:You are very foolishly storing users’ private login information to your e-commerce site in the app data store. I, being a curious and slightly evil person, know this. I fire up your app with my credentials. Next I re-edit the HTML of your app using Firebug to include my hacking script, which fires a newFetchPersonAppData request. Once I confirm that my script works, I start sending out friend requests to everyone I see who has your app installed so I can read their app data stores.Violà! You have allowed your app to be used to hack yourself. Unvalidated input fields can be a real problem, as mentioned in “Stability and Fault Tolerance.” SQL injection remains a very dangerous attack. As an example, let’s say that code in your app takes a name field and constructs the following SQL statement: INSERT INTO MyAppUsers(id, name) VALUES(nnn, '$UserName');
Summary
The user enters Bob');
DELETE * FROM MyAppUsers; --
Your SQL statement then becomes INSERT INTO MyAppUsers(id, name) VALUES(nnn, 'Bob'); DELETE * FROM MyAppUsers –-');
Many frameworks have built-in testing for SQL injection, but you should do your own testing. Cross-site scripting (XSS) attacks remain a very popular attack vector.The MySpace message channels (requestSendMessage, notifications) do a fairly good job of stopping XSS attacks. If your app has an independent user-to-user communication channel, you will have to write your own security mechanism to stop these. There are many other attack vectors to which Web apps are susceptible, and new issues are being discovered all the time. Neglect security at your peril.
Summary Performance and security are major factors in app design.With a little care and forethought, you can save yourself some messy rewriting under fire. Responsive performance in apps is most commonly improved by optimizing the front-end client code. Minor responsiveness improvements on the server pale in comparison to the overall wire time to push information back and forth across the Internet. Designing for scale is one of the most common and difficult problems in Internet computing. Scaling performance often involves limiting the number of requests from the client and designing the database to scale horizontally.These and the other guidelines presented in this chapter will help you avoid some of the most common errors. Load testing will help as well. At the end of the day, though, you cannot completely predict how an app will behave in the wild.
303
This page intentionally left blank
14 Marketing and Monetizing Tdo here are two types of app developers: those who write apps for profit, and those who it for fun. At first glance this chapter may seem to be directed toward the former group, but we think everyone can get something out of reading it. For those looking for a secondary revenue stream from writing apps, we don’t need to convince you of the need to monetize. In this chapter we’ll look at the different monetization techniques, including ads and micropayments, and see what’s involved in setting them up.There are a slew of services out there, each promising the best CPM, or cost per 1000-page impressions. We’ll compare and contrast some of the more popular services in order to help you determine which best suits you and your app’s needs. For those who are just writing apps for a bit of fun, there are a few things here for you to consider. You may just be curious about OpenSocial and the MySpace platform and want to tinker around. Or perhaps you want to learn some new coding techniques, and writing a Web service and JavaScript-based social Web app appeals to you. Or maybe you just want an excuse to try out Google App Engine. If that’s the case, and you don’t plan to publish your apps, this chapter may not be for you. On the other hand, maybe you’re not in it for the money, but you want to make and share some of your creative ideas for apps.Well, who wants to spend hours making a great app, only to have 14 people install it? The sections on the app gallery and app Profile may help you get a few extra installs, at which point the viral features of your app can further spread the word. But who can’t use a few extra bucks from time to time? Maybe you could use that money to beef up your app’s infrastructure to give your users a better experience, or maybe just to buy yourself something nice. You deserve it! It’s a really great app! It can be quick and easy to hook up an ad network to your app, and it’s always rewarding to get paid for your hard work. And finally, why should you take our word for it? Well, in this chapter you won’t have to. We’ve tracked down the developers of some of the more popular apps on MySpace so they can share their success stories. How did they get so many users? How are they monetizing? What suggestions do they have for up-and-coming app developers? Look for their insights and tidbits at the end of the chapter.
306
Chapter 14 Marketing and Monetizing
Using MySpace to Promote Your App MySpace provides a few ways to showcase your app, along with a fairly reasonable advertising platform. Let’s take a look at some of the methods MySpace provides that you can employ to attract new users.
The App Gallery The main gateway that connects users to apps is the app gallery. It’s a simple list of all the apps on MySpace. The list can be sorted, filtered, and searched.This is where the link in the My Apps module on every user’s Home page will lead, as well as any other apprelated links provided by MySpace. Know it and learn to love it, for it is the nexus of the MySpace app universe. Take a look for yourself at http://apps.myspace.com/. This is where the usual “How do I get to the top of the list?” search engine gaming starts. Similar to the way a higher search engine listing can dramatically increase traffic to a Web site, making it easier for users to find your app in the app gallery can quickly increase your number of installs. There are three main ways for the app list to be filtered. Along the left-hand side of the page are the various categories, such as Communicating, Fashion, Games, and Sports, as well as a language filter. The filters along the top of the page are fairly selfexplanatory; they are Alphabetical, Most Users, Newest, and Recently Popular. All of these filters can be active at the same time; so, for example, a user can view the newest apps, in Japanese, under the Events category, as we have shown in Figure 14.1.
Figure 14.1 The app gallery filtered to show the newest Events-based apps in Japanese.
Using MySpace to Promote Your App
The page defaults to the categories All Applications, All Languages, and Recently Popular, so it should be your goal to get your app as high as possible on this list for as long as possible. Landing on this list can give your app a nice jump start; we’ve seen anywhere from several hundred to several thousand installs generated within a 24-hour period. At that point the awesomeness of your app, plus various viral channels, could push your app over the tipping point where you start to see exponential growth. So how do you get on the list? There are some key points to look at, and we’ll address them each in turn: 1. Icon, name, and description 2. The Try App link 3. Categories It can help to think of your app as a book.When browsing in the local bookstore, what do you look for? What makes you buy one book instead of another? Icon, Name, and Description The first thing users see is your app’s icon and name. If they like what they see, they’ll read the description.This would be like picking up a book and looking at the cover. You look at the graphics on the front, the title of the book, and then maybe read the one or two sentences that are always there—you know, like “From the bestselling author of …” or “ ‘Mesmerizing!’ —Joe Fake, New York Times Book Review.” The same idea applies here; you need to catch the potential user’s attention. All of the apps with the highest installs have colorful, polished icons with interesting graphics. It may be easy to overlook, but a little Photoshop wizardry can go a long way. If the icon looks cheap, well, the app probably has similar qualities. Also, by having a name, description, and icon that give the user a good idea of what the app actually does, you can better find your target audience. If users like what they see, they can do one of three things. First, they can click Add App to install it on the spot, and your work is done, plus one install. Second, they can click More Info, which takes them to the app’s Profile page. (More on that a little later.) Third, they can click Try App. Try App To further our book analogy, clicking Try App is like reading the inner flap of the dust jacket. It gives the reader a quick and easy sample of what the book is like. When users click Try App, they are taken to the Canvas page of your app. In this context, very few, if any, permissions are granted to an app when a user has yet to install it. That means that the data to which the app has access is limited. You won’t be able to fetch friend lists, but under some circumstances you may be able to get the Viewer’s basic information (see Chapter 2, Getting Basic MySpace Data, for details on permissions). For apps that rely heavily on social features, this can be severely limiting. For this reason a lot of developers simply decide to blank out the Canvas and show an “Add App!” graphic with a big arrow that points to the Add App button.
307
308
Chapter 14 Marketing and Monetizing
Well, that’s not the greatest user experience because you can’t exactly try the app in that scenario. But if your app is crippled without a friend list, it may be your only recourse. However, if you can enable some functionality, but still strongly persuade the (potential) user to install, it may be the best of both worlds. Categories To stretch our book analogy even further, app categories are similar to book genres. A book might be one of a small number in a section at a bookstore. Let’s say, for example, an OpenSocial book is in the Computer section. It’s not the most popular topic in the world, so the masses won’t be clamoring for it, but the few who do seek it out won’t have endless choices. Conversely, a book in a very popular genre, like a mystery, has a very large target audience, but there are shelves upon shelves of choices. In MySpace app land, the Games and Fun Stuff categories are the most popular; the others lag behind. So, depending on your app and how you categorize it, you could be a big fish in a little pond or a little fish in a big pond. One other thing to keep in mind is that you can apply up to two categories to an app. Adding that second category, even if it is a bit of a stretch, is yet another way for a user to find your app, so it really can’t hurt.
App Profile, or Bringing Out the Bling The app Profile is like the back cover of a book; it should have a further description of the app along with some graphics or screen shots to show more about the app. A user who navigates to the app Profile from the gallery is obviously interested in the app but is probably looking for more information. You need to convince that user to take the plunge by selling your app. We discussed how to bling out our app Profile in Chapter 12, App Life Cycle, so check out that chapter for the technical details.
MySpace’s Own MyAds MySpace offers an ad platform called MyAds that can be found at http://advertise. myspace.com. MyAds allows you to create a banner ad or upload an existing ad. You can then pinpoint your target audience by specifying criteria such as gender, education, and interests. You then bid on the per-click cost of the ad, how much per day to spend (with a $5 minimum), and set the end date for the ad campaign. See Figure 14.2 for a screen shot of MyAds in action. The nice part about MyAds is that you know the target audience is already using MySpace, so half the battle is already won.This can be a pretty good way to drive traffic to your Canvas or app Profile pages to help maintain or increase growth. The app advertisement at the top of Figure 14.3 illustrates how one app is taking advantage of the leaderboard banner space to advertise.
User Base and Viral Spreading
Figure 14.2 Setting up a MyAds campaign.
Figure 14.3 One of the more popular apps making use of the leaderboard-style ad on a MySpace Home page.
User Base and Viral Spreading Ultimately, the spread of apps will live and die with the users themselves. Unless you’re sitting on a pile of money for advertisements, your app will have to grow organically. That means a user will have to install your app, use it, and either enjoy it enough to want to share it, or get some other benefit from installing it. Making an app great
309
Chapter 14 Marketing and Monetizing
enough that people want to share it is easier said than done, but that’s your job, dear reader—you provide the great idea and we’ll show you how to execute it. On the other hand, you may want your users to gain something from installing your app. Now, this is a fine line to walk, as the MySpace Terms of Service forbid incentivized app installs. For example, you couldn’t say, “Invite a friend to receive 100 free points.” That incentive would break the terms you agree to when creating your app. Many of the popular games on the platform have found interesting ways around this. The most common way is to add the number of friends you’ve invited into the game rules. In many games you perform various missions or quests or whatnot and the early missions are available to everyone. As you progress through the game, the harder and more valuable missions are available only if you’ve invited a certain number of users.This is rationalized in the game’s world by the idea that you need to build up a team to complete the harder missions, and the team consists of the friends you’ve successfully invited to the game. This technique has proved very successful for many of the top apps, and the viral nature can lead to exponential growth patterns. Once you hit a critical mass of users, the growth seems to take care of itself. Figure 14.4 shows the installation rate of the Own Your Friends app, the first MySpace app to hit the one-million-install mark. 160000
140000
120000 Number of Installs
310
100000
80000
60000
40000
20000
0 Mar. 22, 2008
Apr. 19, 2008
May 20, 2008
Timeline for the Own Your Friends app
Figure 14.4 The early growth pattern for Own Your Friends, the first big MySpace app.
Ads
There seems to be a tipping point at around several thousand users; once that mark is hit, the viral nature of the app increases the installs exponentially. On April 19, 2008, Own Your Friends had just 4920 installs; just 11 days later, on April 30, 2008, it had 65,594. Fifteen days after that it nearly doubled to 127,768.
Listen to Your Customers “The customer is always right” isn’t just for the mall. An easy way to retain users is to listen to them. Users send messages to the developers of an app, they post messages to the app’s forum (found on the app’s Profile), they report bugs, and they try to hack you. It’s important that you listen to your users and try to respond to them. It’s even more important that you fix bugs and especially any security vulnerabilities.This is doubly true if your app is a game; the integrity of the game is very important, and if it’s compromised, your users won’t find too much enjoyment in it. In return, you’ll find that you grow a base of “power users.” These users will spend hours using your app, finding and reporting bugs, and even letting you know about security exploits.They will also spread the game far and wide, inviting hundreds of friends. So, in return, you get a marketing department for free!
Ads You’ve now got a stylish icon and an interesting description for your app in the gallery.Your app Profile is fully blinged out with custom style and graphics. Maybe you’re even driving traffic to your app through the use of MyAds, and your users are organically growing the app as well. Now what? Let’s make some money! The most obvious way for you to cash in is to add advertisements to your app’s Canvas page. Let’s take a look at the various ad services available out there, as there is a huge number of choices. Note According to the MySpace Terms of Use, ads are allowed only on the Canvas view; they can’t be placed on the Home or Profile views.
Google AdSense Google AdSense (https://www.google.com/adsense) is the granddaddy of modern advertising on the Web. The AdSense engine attempts to contextualize the contents of the page and serve ads from its roster that are relevant to that content.This can be especially useful if your app involves a particular theme that can translate into real-life products. For example, if your app involves golf, ads for actual golfing products are a logical choice. You’ll need a Google account as well as a Web site URL in addition to all the other usual biographical information. Once you’ve signed up, your application will be submitted for approval, and apparently someone or something will “review” it.The site states
311
312
Chapter 14 Marketing and Monetizing
that this process will take one or two days, and the application page says “within a week.”We received our confirmation in a few days. Make sure the Web site URL you specify is an actual Web site with some content on it; the reviewers will reject sites that don’t exist or are under construction. Once your account is set up, it’s time to create some ads. AdSense offers various types of ads geared toward various types of Web sites; we chose to use plain old AdSense for Content. These ads try to guess the content of your page and ideally display something relevant. There are a few more customizations you can make to the ads that will be displayed, such as size, shape, color, and what to display if there are no relevant ads available. Once all the customizations are complete, and the ad looks and behaves just so, AdSense provides you with the necessary JavaScript to place inside your app. Here’s the code we were provided; yours will be slightly different depending on the options you chose (all personal information and identifiers have, of course, been changed): <script type="text/javascript"> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
We pasted this script into the top of our Tic-Tac-Toe app and got the result shown in Figure 14.5.
Figure 14.5 AdSense ads inside Tic-Tac-Toe.
Ads
We’re not too sure how relevant the ads are in this case (lemon laws?), but they’re there, ready to make money nonetheless.
Cubics Cubics (http://cubics.com) is a little different from AdSense in that it’s geared toward generating revenue with apps on social networks.This is nice, because, well, that’s exactly what we’re trying to do. Cubics started out serving ads for Facebook apps but has since branched out to other social networks, including MySpace, as those networks created app platforms. Signing up for Cubics is a little easier than for AdSense in that your application doesn’t need to be “reviewed,” so you don’t have to wait around for a couple of days to start placing ads in your app. Once you’ve provided your personal biography, you’ll need to select a social network.The rest of the flow is essentially this: 1. Add an application to administer by providing a few details of the app. 2. Select the type of ad, such as a 468⫻60 banner ad or a 120⫻600 skyscraper ad. 3. Pick some style elements of the ad, such as background color. 4. Copy and paste the provided code into your app’s code. For example, here’s the code we were provided, which we stuck at the top of our app: <script type="text/javascript"> var pid = 338280; var appId = 70194; var plid = 17263; var adSize = "468x60"; var linkColor = "%230033ff"; var textColor = "%23000000"; var bgColor = "%23ffffff"; var channel = ""; var frameSize = "468x60"; <script language="javascript" type="text/javascript" src="http://social.bidsystem.com/displayAd.js">
Figure 14.6 shows a sample ad being rendered into the Tic-Tac-Toe app and placed above the “OpenSocial Tic-Tac-Toe” title.
313
314
Chapter 14 Marketing and Monetizing
Figure 14.6 A sample ad rendered in the Tic-Tac-Toe app.
Warning Several times with Cubics we got a “Server too busy” error displayed where the ad should have been, which is a little disconcerting! It may have been an intermittent issue, and we couldn’t re-create it the next day, but it’s something to keep in mind when choosing an ad network. You can’t make money with ads you don’t show, so always test a network before you jump in. Also from time to time the ads were slow to load, say, on the order of 5 to 15 seconds. Because of this, the onload event of our app was delayed by the same amount of time, so everything essentially was put on hold until the ad rendered. This can lead to a pretty bad user experience; a user may even think the app is broken and never come back. But again, the next day we tried again and everything seemed fine.
RockYou! Ads RockYou! (https://www.rockyouads.com) is actually primarily an app company. It started way back when by providing Flash widgets for MySpace Profiles and branched out to social apps when the networks started opening up their APIs. Over time they developed an ad network geared toward social networking apps. Once you provide your details and verify your e-mail address, you can start placing ads in your apps.You’ll need to fill out some details of your app in addition to the size and position of the ad. For some reason it wants the URL of the app’s Profile page, even though you’re not allowed to show ads there, so it’s a little strange.To determine this
Ads
URL, click the More Info link on your app’s Canvas page; this takes you to your app’s Profile page, but the URL will be something like www.myspace.com/446431217.That last number, 446431217 in this case, is the user ID of your app’s Profile.Take that number and paste it into the field for Application URL on the RockYou! Ads page. It’ll look something like this: http://profile.myspace.com/index.cfm?fuseaction=user. viewprofile&friendid⫽446431217. Once that’s done, you’ll be taken to your list of apps. Click Embed Code to see the JavaScript and HTML markup you’ll need to paste into your app. Here’s an example of what ours looked like: <script type='text/javascript'> <script type='text/javascript' src='http://cdn.rockyou.com/apps/ams/tag_os.js'>
Pasting that into the top of our app gave us what you see in Figure 14.7. The RockYou! ads seem to be a very different style from both Google AdSense and Cubics ads.The previous ads were text-based, but RockYou! ads, for the most part, seem to be graphics-based.
Figure 14.7 RockYou! ad in the Tic-Tac-Toe app.
315
316
Chapter 14 Marketing and Monetizing
Warning We ran into a couple of problems when using RockYou! Ads. From time to time we would get no ads rendered to the page at all; there would just be a cryptic error message displayed. We got in contact with the engineers at RockYou! and they were quick to fix the problem. However, it would have resulted in a loss of revenue for a time.
Micropayments Micropayments are a very different way for an app to generate revenue compared to showing ads.The way it works is that you provide some kind of premium content for your users at the cost of a dollar or two. A few dollars doesn’t seem like a lot to the bottom line, but it’s a low enough price that a lot of users will be willing to pay it.Those few dollars can add up quickly to a nice flow of money. For a real-life example, let’s take a look at Mobsters, the app that currently sits atop all MySpace apps in terms of number of installs: http://profile.myspace.com/Modules/ Applications/Pages/Canvas.aspx?appId=104283. You’ll notice that there isn’t a single ad anywhere in the app; it generates revenue solely from micropayments. By paying real money, users of the app can gain certain advantages in the game, whether it’s beefing up certain stats or acquiring rare items. Let’s take a look at how to take advantage of this potential revenue stream.
PayPal PayPal (https://www.paypal.com/IntegrationCenter/ic_micropayments.html) is the leader in online payments, and it now offers micropayment support. As of this writing, the rate for micropayments, which means charges under $12, is 5% + $0.05 per transaction.To get started you’ll need to follow the link we’ve given and sign up for a Business Account. This signup process is a drawn out-affair; you’ll need to supply your own personal details, details of your company, such as a customer service line, and then finally bank account details (which will take several days to process). As always, be careful when providing bank or credit card details to anyone, especially over the Internet. If you’re not comfortable doing this, definitely don’t do it; we’ve all seen news reports of credit card numbers being stolen from online vendors. Assuming you’re willing to provide a bank account to PayPal, once it’s set up, click on the Merchant Services tab to add a payment button to your app. Fill out the details of your payment button, such as text, button type, price, and tax rate. Once you’re happy with everything, click Create Button.That takes you to a page that supplies the markup that you’ll need to copy and paste into your app. Here’s the markup that was generated to insert into our Tic-Tac-Toe app:
Micropayments
Number of Victories |
<select name="os0"> 1 Victory $1.00 5 Victories $4.00 10 Victories $7.00 |
Boku Boku (www.boku.com) is a micropayment platform that uses cell phones to make payments. You may know it as Mobillcash, which was the company that Boku recently bought out.To make payments, users select their cell phone carrier, enter their phone number, and are sent a text message.The users then confirm purchases by responding to the text message. The signup process is fairly straightforward, but you need to submit a request to become an ad publisher.That submission is then sent for a manual review (which can take a few days). Once you’ve successfully opened an account, you can fine-tune your settings before obtaining your code.We don’t include the markup sample here because at the time of this writing it hadn’t been updated to reflect the switch to Boku. However, like the other ad networks, it’s a relatively straightforward signup process and copy-and-paste job. Signing up for Boku and installing the code gives you the little Pay by Mobile button shown in Figure 14.8.
Figure 14.8 The Boku Pay by Mobile button.
317
318
Chapter 14 Marketing and Monetizing
Others A huge number of companies offer micropayments, and there are more on the way every day. We’ve touched on only a few, but the ones we’ve listed seem to work well and, even more important, work well with MySpace. Some other micropayment platforms of note are n n n n n
Social Gold: www.jambool.com Spare Change: www.sparechangeinc.com Zong: www.zong.com PayByCash: www.paybycash.com ClickandBuy: www.clickandbuy.com
It can be in your interest, and the interest of your users, to implement multiple micropayment platforms in your app. Nothing says you need to choose just one, and giving your users a choice is always nice. Also, if they’re familiar with one of the platforms you’ve implemented, they may be more likely to make use of it.You’ll notice that the aforementioned Mobsters app uses most of the platforms described here.
Interviews with Successful App Developers We’re developers, and though we may know a lot about the MySpace platform, we’ve never built a viral app that attracted millions of users. So, when it comes to marketing and monetizing, why should you listen to us? We thought the same thing, which is why we sat down with some successful app developers whom we admire, big and small, to talk to them about what they did that worked and didn’t work.We hope these interviews will provide some insight into how an app hits it “big.”
Dave Westwood: BuddyPoke (www.myspace.com/buddypoke) 1. Who are you? My name is Dave Westwood. My primary skills were initially in 3D renderers, and I created a few Java-based 3D engines at various companies, later moving on to Mobile renderers and now Flash. My partner, Randall Ho, is a 3D character artist/animator and scripter.We’ve worked together at five different companies over the past 12 years or so and decided a couple of years ago to strike it out on our own after seeing the advent of social networks and, in particular, social network widgets. 2. What OpenSocial apps have you created or helped to create? BuddyPoke, as the name of our company suggests, is our only OpenSocial application. Because of the complexity of our particular application as well as the room for growth in our design, we decided to focus on a single, rich
Interviews with Successful App Developers
application rather than a portfolio of simpler applications. Considering the sheer volume of applications that are available to users, it seemed like the best approach to stand out from the crowd. 3. Is there anything you wish you had known before you started developing your first app? We always wish to learn more about the users of each social networking site. Each site is unique in its own way, and having a better understanding of the demographics and interests of the users can have a huge influence on the direction and content that goes into an app. 4. How do you find the MySpace platform, in terms of both technology and growth opportunities? I find myself amazed at the success we’ve been able to achieve thus far considering the size of our company (tiny) and the amount of capital we’ve put in (none). It’s truly a testament to the openness of the MySpace development platform and the OpenSocial community that’s given us access to a market that really sprang from nothing and now gives us access to millions of users around the world. 5. And how do you find the MySpace platform in comparison to other platforms like Facebook? The primary reason that we find MySpace (and OpenSocial) to be a “better” platform is that our particular application is better suited to it. BuddyPoke’s primary strength relies on customizing a 3D avatar and seeing it animated. Having a Flash application viewable (and animated) from the Profile page is key to the attractiveness and virality of BuddyPoke. That’s not to say that one platform is better than the other—simply that MySpace is a perfect match for animated expressive content. 6. What has been your most successful app in terms of number of installs? How many installs does it have? As of April 2009, there are approximately 40 million installs of BuddyPoke worldwide across ten different containers. 7. What did you try that did and didn’t work to promote your app and increase installs? Honestly, we haven’t done a whole lot to promote the application aside from trying to make the best application that we could. Quite literally, we spent about a year on the technology and assets behind BuddyPoke, the minority of which was really OpenSocial per se. The virality of BuddyPoke is based on two things: Firstly, people like to play “dress-up.”They like to express themselves, and BuddyPoke seems to fit the bill insofar as the customization aspects as well as the universality of “cute” characters such as Peanuts, Mario, Mickey Mouse, etc. Secondly, people like to communicate
319
320
Chapter 14 Marketing and Monetizing
8.
9.
10.
11.
with each other, so they are more than happy to spread the word with their friends to find rich ways of interacting with each other. I think a lot of app developers do one (or both) of two things: Either they have “spammy” incentivized features to achieve large install numbers, or they heavily invest in advertising. As far as the spam is concerned, we’ve decided from Day 1 to take the “high road” in this regard and rely on the app’s strength rather than, shall we say, tricking users into installing the application. Our users must be passionate about the application for us to really be a success. Dormant installs, while great on paper, do us nothing. As far as advertising is concerned, we’d gladly have bought advertising, except that we didn’t have the money(!)—so that was an easy decision for us. How did you scale your app once growth took off? Were you prepared, or was it a surprise? We were very fortunate that Google released the preview of App Engine around the same time that we went live on MySpace. Its ease of use and fluid scaling is game changing and allows a two-person start-up like ours to support such a large user base without buying servers, worrying about scaling a database, etc. Have you tried to monetize your app? If so, how? Our primary form of revenue has been through ads, and we’re exploring branded pokes, which is the app equivalent of product placement. This has been very challenging for us, and even though there has been considerable interest from a variety of ad agencies, many of these things have not come to fruition. We just recently released our first branded poke for X-Men Origins: Wolverine. We hope to have many more products like this that really appeal to our demographic. Finally, we’ve just recently started to implement a virtual currency and offer add-on features for printing and “VIP” pokes. Have you taken advantage of the OpenSocial platform and imported your apps to other social networks like Orkut or Hi5? We’re on ten different containers, including Orkut, Hi5, Hyves, Friendster, NetLog, and others, though, of course, MySpace has a special place in our hearts since we’re based in the U.S. Every container is slightly different, but it’s really quite easy to port from one to the next.The main challenge is dealing with the localization, but this is a relatively minor task in the overall development of BuddyPoke. What advice, if any, would you offer a first-time app developer? Go ahead and get your feet wet! Maybe start out with a simpler application and take advantage of the many code examples available to you.Then move on to something more complex. Spend time thinking about your viral loop, iterate your ideas, closely track user actions and feedback, and localize your content.
Interviews with Successful App Developers
Eugene Park: Flixster (www.myspace.com/flixstermovies) 1. Who are you? Eugene Park, Director of Development, Flixster, Inc. 2. What OpenSocial apps have you created or helped to create? Flixster Movies, currently on Myspace, Orkut,Yahoo!, and soon iGoogle. 3. Is there anything you wish you had known before you started developing your first app? OpenSocial containers run our apps inside of iframes under a different domain than the host, so tools like Firebug are disabled by default for these unrecognized domains.This can make debugging JavaScript a bit of a pain.The work-around is simple: Load the iframe’s URL into a new browser window and enable this domain on Firebug.This should help you get the most out of Firebug. 4. How do you find the MySpace platform, in terms of both technology and growth opportunities? MySpace platform (and OpenSocial in general) requires only an understanding of HTML and JavaScript, so it’s easy to learn and get started.The platform also provides a clean REST API, one of the best implementations of REST on the Web. This gives us a pretty complete set of tools for building deep applications for our movie fans. MySpace also provides access to one of the world’s largest and most active social networks, so the growth opportunity is huge. 5. And how do you find the MySpace platform in comparison to other platforms like Facebook? MySpace and Facebook both provide complete platforms for application development, and each has its unique strengths.The thing that I love the most about the MySpace platform is that it gives us full access to our favorite JavaScript libraries (such as Prototype and Scriptaculous), so it’s a lot easier to develop rich, engaging user interfaces. 6. What has been your most successful app in terms of number of installs? How many installs does it have? Our OpenSocial app on MySpace (Movies) now has over 5 million installs—and growing! 7. What did you try that did and didn’t work to promote your app and increase installs? According to our product manager, Josh Gould, the most effective thing has been to create fun features that people want to share with their friends and provide easy ways to let them do so. For example, after taking a movie quiz (one of our most popular features), we allow people to easily share the quiz with their friends by sending a bulletin or comment message.This has helped introduce many new people to the app without much effort.
321
322
Chapter 14 Marketing and Monetizing
8. Do you think your app has been successful? Why or why not? Flixster Movies has been a huge success, thanks in large part to a vibrant social community and a platform that supports (rather than constrains) active interaction and sharing. 9. How did you scale your app once growth took off? Were you prepared, or was it a surprise? We have an amazing team of engineers, and they’ve built a solid infrastructure that supports all of our different product platforms—so we were well prepared for growth. One thing worth noting on the MySpace platform is that the Home surface gets exponentially more traffic than the other surfaces, so we spend a lot of time optimizing our content for caching there. Also, as an application dedicated to movies, we serve a lot of rich media (images and Flash), so we use content delivery networks to help us scale our bandwidth. 10. Have you tried to monetize your app? If so, how? Like all of our other products, we monetize primarily through advertisements. 11. Have you ever spent money advertising your app? If so, where and was it worth it? When we launched our application, we invested some advertising dollars on featured app placement in the MySpace apps directory—this helped us jump-start our first installations.We also exchanged ads with other popular OpenSocial applications; this is a pretty cost-effective way of reaching new users. 12. Have you taken advantage of the OpenSocial platform and imported your apps to other social networks like Orkut or Hi5? We’ve successfully imported our app into Orkut, and it was pretty easy. Most containers are pretty committed to OpenSocial standards, so the cost of porting an app from one OpenSocial container to the next has been pretty low. 13. What advice, if any, would you offer a first-time app developer? Take the time to learn JavaScript properly, and take advantage of any of the great JavaScript frameworks out there (Prototype, Dojo, etc.).
Tom Kincaid: TK’s Apps (www.myspace.com/tomsapps) 1. Who are you? My name is Tom Kincaid and I create TK’s Apps; on other sites they’re called Tom’s Apps, but this turned out to be an issue on MySpace. [Authors’ note: Apps are not allowed to use the name Tom or Tom references in their titles or descriptions because of Tom Anderson, the founder of MySpace and every member’s first friend.] I work in interactive media, but most of my apps are my own side projects.
Interviews with Successful App Developers
2. What OpenSocial apps have you created or helped to create? My apps include Ultimate Fan for Football, Baseball, Basketball, and Hockey teams, Astrology Badge and Match, Profile Poster Gifts, and TV Show Badges and Chat. 3. Is there anything you wish you had known before you started developing your first app? I regret making an app for every sports team. It’s too difficult to maintain so many. 4. How do you find the MySpace platform, in terms of both technology and growth opportunities? For whatever reason, MySpace users are more prone to click on ads, so per user it’s more profitable. On the technology side, it’s very controlled (for reasons I understand), so to update an app, it takes more work than other platforms and there are continual little bug fixes and improvements that need to be done. 5. And how do you find the MySpace platform in comparison to other platforms like Facebook? One thing that benefited the MySpace platform launching after Facebook was they understood that developers would abuse it and they have better technical restrictions in place to combat this. For example, every communication sent by a user must be actively confirmed, whereas on Facebook, notifications can be sent automatically and they try to address abuse through policy, which their staff cannot possibly keep up with across tens of thousands of apps. 6. What has been your most successful app in terms of number of installs? How many installs does it have? I’m not that big a developer. Posters and Astrology have over 15K each. 7. What did you try that did and didn’t work to promote your app and increase installs? I just try to cross-promote. I don’t use ads or anything. 8. Do you think your app(s) has been successful? Why or why not? For something one guy did alone, not bad, but it’s not enough to make a full-time living doing. 9. How did you scale your app once growth took off? Were you prepared, or was it a surprise? Didn’t have this problem yet. 10. Have you tried to monetize your app? If so, how? Mostly just banner ads. Get about $0.50 CPM. 11. Have you ever spent money advertising your app? If so, where and was it worth it? Have not done any advertising, just cross-app promotions.
323
324
Chapter 14 Marketing and Monetizing
12. Have you taken advantage of the OpenSocial platform and imported your apps to other social networks like Orkut or Hi5? I did at the start, but it became overwhelming to support multiple apps across multiple sites that are not 100% compatible both in technology and policies, so now I just focus on MySpace and Facebook. 13. What advice, if any, would you offer a first-time app developer? Don’t expect your “big idea” will make you rich overnight.
Dan Yue: Playdom (www.myspace.com/playdom) 1. Who are you? My name is Dan Yue and I’m the cofounder and CEO of Playdom, the largest game developer on MySpace. Prior to founding Playdom, I was employee number 1 at Adify, an ad network platform acquired by Cox Enterprises in 2008; founded several wildly unsuccessful technology start-ups; and served as technical consultant to Wynn Design and Development … As a lifelong gamer, I’ve played thousands of hours of Final Fantasy 7 and Baldur’s Gate 2 and spent 34 long days as a semiprofessional Blackjack player. My commitment to the player experience runs so deep that I wake up several times a night to check the performance of Playdom’s games. 2. What OpenSocial apps have you created or helped to create? Playdom has developed and successfully launched 12 apps on MySpace, including seven RPGs. Playdom has also launched Poker Palace and Bumper Stickers on Hi5. 3. Is there anything you wish you had known before you started developing your first app? I wish we realized that games are much more successful on social platforms than other types of social apps.We first launched Kiss Me on MySpace, which was (and is) a success, but it may have been wiser to launch a game first, then follow with a social app. In the perfect scenario, you’d attract a high volume of installs with a highly engaging game; then you can use the first game’s popularity to cross-promote new apps. 4. How do you find the MySpace platform, in terms of both technology and growth opportunities? MySpace is a great platform that provides a lot of possibilities for developers. The average length of visit is longer on MySpace, compared to other social networks, and the emphasis is on self-expression and fun. Social games— and Playdom’s games in particular—work really well in MySpace’s environment.
Interviews with Successful App Developers
5. And how do you find the MySpace platform in comparison to other platforms like Facebook? MySpace is very developer-focused, and we value our relationship with them. As one of many examples, their platform team recently conducted a survey to pinpoint developer pain points and solicit recommendations. (That said, we have a positive relationship with Facebook and find their team and policies by and large developer-friendly.) 6. What has been your most successful app in terms of number of installs? How many installs does it have? Mobsters is the number-1 app on MySpace with over 13.6 million installs. Playdom also has three of the top four apps on MySpace, including Own Your Friends, Kiss Me, and Bumper Stickers. 7. What did you try that did and didn’t work to promote your app and increase installs? We have generally enjoyed great success promoting our apps. Our two biggest strengths: cross-promotions (marketing a new game via a popular established game) and deeply integrating viral channels into our games (the best virals are a natural part of the game play). 8. Do you think your app(s) has been successful? Why or why not? A number of our apps have been successful. The most obvious indicator of success is in the number of installs: Mobsters is the obvious success, our largest MySpace app with over 13.6 installs; and Bumper Stickers is in second place with over 11 million installs to date. However, we really focus on the level of player engagement, and we’re very pleased with our DAU [Daily Active Users] and length-of-session metrics. 9. How did you scale your app once growth took off? Were you prepared, or was it a surprise? Playdom’s first app to hit it big was Own Your Friends, which rocketed to 5.7 million installs in the third month, only to grow to more than 7.5 [million] installs a month later.To be honest, we weren’t fully prepared:We didn’t have the server infrastructure to support the app’s massive growth. As a stopgap solution, the founding team got up several times a night to make sure the game was online. And we quickly focused on scaling the operation. Thanks to an angel investor, this was possible early in our history. 10. Have you tried to monetize your app? If so, how? Playdom has monetized various apps—and all our RPGs. Roughly 60% of our revenue comes from direct payments for virtual goods, including in-game advancement and limited edition items. Five percent of the people playing our games purchase virtual goods and the percentage goes up as players spend more time on our game.The other half of our revenue is generated via incentivized offers.
325
326
Chapter 14 Marketing and Monetizing
11. Have you ever spent money advertising your app? If so, where and was it worth it? We have purchased both sponsorships and cost-per-click banner ads on MySpace. Both have been worth the energy and money because of the return-per-user metrics they provide.With each user that clicks on the ad, that user brings in additional players through the app’s viral channels.These advertisements help seed virality—a critical driver of growth. 12. Have you taken advantage of the OpenSocial platform and imported your apps to other social networks like Orkut or Hi5? Poker Palace and Bumper Stickers have both been successfully launched on Hi5. 13. What advice, if any, would you offer a first-time app developer? New developers need to understand that in today’s market, it’s difficult to go massively viral by relying on old-school “spammy” methods.Viral channels need to be authentic to the game, and the game play needs to be fundamentally engaging. We’ve created a formula that speaks to a joint need to drive growth and reduce churn: G ⫺ cD ⬎ 0 or [Growth ⫺ % of daily churn ⫻ number of daily active users must be greater than 0] We’ve fueled growth by identifying the right in-game channels to deploy the virals.We’ve reduced churn by introducing in-game comments, compelling storylines, and mini-games.
Summary In this chapter we looked at ways to spread your app, and then how to cash in once the installs start coming in. One thing to take away is that there are individuals and companies out there that are making money by making apps. We heard from a few of them in the interviews section and got some tips on how to achieve some success. When it comes to generating income, there is a wide variety of options; this is an expanding market and one that is interesting for advertisers. In terms of getting the biggest bang for your buck with ads, there are a lot of variables to consider. Using one of the bigger and more established companies, like Google, is a superior technical solution. You won’t see a lot of downtime with AdSense, for example. But AdSense isn’t built to target social networking apps. On the other hand, RockYou! Ads and Cubics are designed to specifically target the social networking user. But we encountered technical problems with both ad networks, so there’s a give-and-take you’ll need to consider. This is one of the most important decisions when it comes to developing and growing your app; ultimately you can’t make successful apps if you can’t pay for the
Summary
bandwidth. So it may take time for you to try different solutions, or a combination of solutions. Maybe your app is suited for micropayments and you needn’t bug your users with ads. Maybe your golfing app pulls up some great targeted ads via Google AdSense and you get lots of click-throughs. As we’ve seen, it’s easier than ever before to simply drop an ad into your app and start making money; the sooner you start thinking in terms of profit margins, the better.
327
This page intentionally left blank
15 Porting Your App to OpenSocial 0.9 W e have some good news and some bad news. Let’s go with the bad news first.The bad news is that the OpenSocial spec is currently in constant flux while the spec group seeks the Holy Grail of version 1.0.You may think that this isn’t so bad. After all, you logically go from version 0.8 to 0.9, then finally 1.0, right? Well, there is currently some talk of going from 0.8 to 0.9 to 0.10.That’s right: zero dot eight, zero dot nine, zero dot ten. We hope that won’t be the case and that the 0.9 spec will be stable enough that it can be called version 1.0 without too many changes. If that’s what happens, the spec should stop churning for a while, excluding a few bug fixes here and there. The other piece of bad news is that MySpace will probably continue to support the latest and greatest versions of the OpenSocial spec as they are released.That means version 0.9 will have the full support of MySpace. Sounds great, right? Well, if your app is currently written in version 0.7, that means you’ll now be two spec versions behind.The more versions that are released and supported by MySpace, the more likely it is that the older versions will receive less and less support.We’re already seeing this phenomenon to some extent. Bug fixes and feature releases for 0.7 are fewer and farther between compared to those for 0.8. Fortunately, the good news more than makes up for the bad. For the foreseeable future, MySpace will continue to support older versions of the OpenSocial spec. It’s a lot of effort to deprecate older versions of the spec, and too many apps are running on old versions. It will happen eventually, but it will probably be a long and drawn-out process. A little-known fact is that the 0.7 container actually also supports OpenSocial 0.6, an early and buggy release of the spec. By the time you read this page, dear readers, version 0.6 will be about two years old (or more)—an eternity in Internet time. If version 0.6 can last that long, and probably longer, then version 0.8 will be around for a long time as well. Even better news is the fact that when it comes down to it, not a whole lot is changing from version 0.8 to version 0.9.The biggest change will be the inclusion of OSML to the spec. Since we covered OSML extensively in Chapters 10 and 11, we won’t
330
Chapter 15 Porting Your App to OpenSocial 0.9
rehash that topic here.The other big change is what is called “OS Lite,” or the “Lightweight JS APIs.” OS Lite is, for the most part, a rewrite of the OpenSocial JavaScript APIs and has two main goals: to unify the JavaScript and REST APIs, and to use JSON for both inputs and outputs.These are both worthy goals, but the truth of the matter is that the original APIs will work just fine in version 0.9. Moving forward, OS Lite will probably become the standard, but it’s probably best to wait for version 1.0, when it becomes a bit more baked-in. Other than that, there are a few bug fixes, a feature or two, and a bit of a cleanup. Now, let’s take a look at the big changes.
Media Item Support A slew of functions to support media items were added in 0.9; this includes fetches, updates, deletes, and (finally!) uploads. However, some of this functionality already existed on the MySpace platform as MySpace-specific extensions in 0.8. In the sections that follow, we’ll take a look at the new APIs and, if applicable, contrast them with the 0.8 APIs.Then we’ll show some sample code so you can see how to use the new features.
opensocial.Album The first thing we’ll look at is the new opensocial.Album object.The album object behaves exactly like the other OpenSocial objects, such as opensocial.Person; it has fields, and those fields are fetched with getField. Let’s take a look at the available fields that albums provide. OpenSocial 0.9* opensocial.Album.Field = { /** * String, unique identifier for the album. * May be used interchangeably with the string 'id'. * @member opensocial.Album.Field */ ID: 'id', /** * String, URL to a thumbnail cover of the album. * May be used interchangeably with the string 'thumbnailUrl'. * @member opensocial.Album.Field */ THUMBNAIL_URL: 'thumbnailUrl',
*Code
courtesy of OpenSocial.org: http://sites.google.com/site/opensocialdraft/Home/opensocialjavascript-api-reference/albums-js.
Media Item Support
/** * String, the title of the album. * May be used interchangeably with the string 'title'. * @member opensocial.Album.Field */ TITLE: 'title', /** * opensocial.Address, location corresponding to the album. * May be used interchangeably with the string 'location'. * @member opensocial.Album.Field */ LOCATION: 'location', /** * String, ID of the owner of the album. * May be used interchangeably with the string 'ownerId'. * @member opensocial.Album.Field */ OWNER_ID: 'ownerId', /** * Array of MediaItem.TYPE, types of MediaItems in the album. * May be used interchangeably with the string 'mediaType'. * @member opensocial.Album.Field */ MEDIA_TYPE: 'mediaType', /** * Array of strings identifying the mime-types of media items in the album. * May be used interchangeably with the string 'mediaMimeType'. * @member opensocial.Album.Field */ MEDIA_MIME_TYPE:'mediaMimeType', /** * Integer, number of items in the album. * May be used interchangeably with the string 'mediaItemCount'. * @member opensocial.Album.Field */ MEDIA_ITEM_COUNT:'mediaItemCount' };
OpenSocial 0.8 on MySpace MyOpenSpace.Album.Field = { /** * A number representing an album's unique identifier.
331
332
Chapter 15 Porting Your App to OpenSocial 0.9
* @memberOf MyOpenSpace.Album.Field */ ALBUM_ID:"ALBUM_ID", /** * The RESTFUL URI with which to access the album on the API. * @memberOf MyOpenSpace.Album.Field */ ALBUM_URI:"ALBUM_URI", /** * The album's title. * @memberOf MyOpenSpace.Album.Field */ TITLE:"TITLE", /** * The geographic location where the album's pictures were taken. * @memberOf MyOpenSpace.Album.Field */ LOCATION:"LOCATION", /** * A URL for the album's default image. * @memberOf MyOpenSpace.Album.Field */ DEFAULT_IMAGE:"DEFAULT_IMAGE", /** * A string representing the album's privacy setting, * such as "Public" or "Private" * @memberOf MyOpenSpace.Album.Field */ PRIVACY:"PRIVACY", /** * An integer representing the total number of photos in the album * (not the number of photos actually contained * within the current object). * @memberOf MyOpenSpace.Album.Field */ PHOTO_COUNT:"PHOTO_COUNT", /** * A RESTFUL URI with which to access the photos contained in the album.
Media Item Support
* @memberOf MyOpenSpace.Album.Field */ PHOTOS_URI:"PHOTOS_URI" };
The entities are basically the same—all the important stuff is there in both cases, such as IDs, album cover URL, and the number of media items in the album.
Fetching Albums Here’s the functionality for fetching an album. First, let’s look at OpenSocial 0.9. OpenSocial 0.9† /** * The newFetchAlbumsRequest() creates an object for * DataRequest to request albums. * * @param {opensocial.IdSpec} An IdSpec used to specify which * people/groups to fetch albums from. * * @param {Map.<string, string>} opt_params * opt_params can specify the following: * opensocial.Album.Field.ID - an array of album IDs to fetch * (fetch all albums if empty, subject to pagination) * opensocial.Album.Field.MEDIA_TYPE - an array of MediaItem.TYPE * values to specify the kind of albums to fetch. * opensocial.DataRequest.AlbumRequestFields.FIRST * The first item to fetch. * opensocial.DataRequest.AlbumRequestFields.MAX * The maximum number of items to fetch. * @return {Object} A request object */ opensocial.DataRequest.prototype.newFetchAlbumsRequest = function(idSpec, opt_params) {};
And now, the MySpace 0.8 extension. OpenSocial 0.8 on MySpace /** * Creates an object to be used when sending to the server * @param {String} id The ID (VIEWER or OWNER) * of the person who owns the albums
†Code
courtesy of OpenSocial.org: http://sites.google.com/site/opensocialdraft/Home/opensocialjavascript-api-reference/datarequest.
333
334
Chapter 15 Porting Your App to OpenSocial 0.9
* @param {Map} * opt_params Optional parameters specified when creating the albums. * @return {Object} A request object * @static * @memberOf MyOpenSpace.DataRequest */ MyOpenSpace.DataRequest.newFetchAlbumsRequest = function(id, opt_params) {};
There are no real functional differences between versions.The new version uses an IdSpec object instead of a plain old string ID. It can specify a particular album, whereas 0.8
had a separate endpoint for that, and the paging parameters are in a different namespace. Here’s an example function that takes in optional paging parameters, album ID, and callback function and makes a request for the Viewer’s photo albums: // Fetches the Viewer's photo albums function fetchViewerPhotoAlbums(first, max, album_id, callback){ // Create the IdSpec object var params = {}; params[opensocial.IdSpec.Field.USER_ID] = opensocial.IdSpec.PersonId.VIEWER; params[opensocial.IdSpec.Field.NETWORK_DISTANCE] = 0; var idspec = opensocial.newIdSpec(params); // Create the DataRequest object var request = opensocial.newDataRequest(); params = {}; // Set the paging parameters if(first){ params[opensocial.DataRequest.AlbumRequestFields.FIRST] = first; } if(max){ params[opensocial.DataRequest.AlbumRequestFields.MAX] = max; } // Pick an album; album_id is an array of album IDs if(album_id){ params[opensocial.Album.Field.ID] = album_id; } // Force it to images params[opensocial.Album.Field.MEDIA_TYPE] = opensocial.MediaItem.Type.IMAGE;
Media Item Support
// Add the request to the queue request.add(request.newFetchAlbumsRequest(idspec, params), "albums"); // Send it off request.send(callback); }
In the function we specified opensocial.MediaItem.Type.IMAGE.The other possibility is opensocial.MediaItem.Type.VIDEO.
Fetching Media Items Let’s look at the OpenSocial 0.9 method for fetching media items. OpenSocial 0.9‡ /** * The newFetchAlbumsRequest() creates an object for * DataRequest to request albums. * * @param {opensocial.IdSpec} An IdSpec used to specify which * people/groups to fetch media items from. * * @param {string} albumId * The ID of the album to fetch MediaItems from. * * @param {Map.<string, string>} opt_params * opt_params can specify the following: * opensocial.MediaItem.Field.ID - an array of media item IDs to * selectively fetch (fetch all items if empty, subject to pagination) * opensocial.MediaItem.Field.MEDIA_TYPE - an array of MediaItem.TYPE * values to specify the types of MediaItems to fetch * opensocial.DataRequest.MediaItemRequestFields.FIRST * The first item to fetch. * opensocial.DataRequest.MediaItemRequestFields.MAX * The maximum number of items to fetch. * * @return {Object} A request object */ opensocial.DataRequest.prototype.newFetchMediaItemsRequest = function(idSpec, albumId, opt_params){};
And now for version 0.8. ‡Code
courtesy of OpenSocial.org: http://sites.google.com/site/opensocialdraft/Home/opensocialjavascript-api-reference/datarequest.
335
336
Chapter 15 Porting Your App to OpenSocial 0.9
OpenSocial 0.8 on MySpace /** * Creates an object to be used when sending to the server * @param {String} id The ID (VIEWER or OWNER) of * the person who owns the albums * @param {Map} * opt_params Optional parameters specified when creating the videos. * @return {Object} A request object * @static * @memberOf MyOpenSpace.DataRequest */ MyOpenSpace.DataRequest.newFetchVideosRequest = function(id, opt_params) {}; /** * Creates an object to be used when sending to the server * @param {String} id The ID (VIEWER or OWNER) * of the person who owns the photos * @param {Map} * opt_params Optional parameters specified when creating the photos. * @return {Object} A request object * @static * @memberOf MyOpenSpace.DataRequest */ MyOpenSpace.DataRequest.newFetchPhotosRequest = function(id, opt_params) {};
The big difference here is that the 0.9 API combines two MySpace 0.8 APIs into one, and it again uses an IdSpec object and modifies the paging parameter namespaces. In our Tic-Tac-Toe app, we had a function that fetched the Viewer’s photos; we’ll reproduce it here so that you can see the differences between versions. Version 0.8 Function for Fetching Viewer’s Photos // Fetch the Viewer's photos function fetchPhotosList(first, max, callback){ // Set the paging parameters var params = {}; params[opensocial.DataRequest.PeopleRequestFields.FIRST] = first; params[opensocial.DataRequest.PeopleRequestFields.MAX] = max; // Send the request var request = opensocial.newDataRequest();
Media Item Support
var id = opensocial.IdSpec.PersonId.VIEWER; var req = MyOpenSpace.DataRequest.newFetchPhotosRequest(id, params); request.add(req, TTT.RequestKeys.VIEWER_PHOTOS); request.send(callback); }
Let’s now convert that code into 0.9. Version 0.9 Function for Fetching Viewer’s Photos // Fetches the Viewer's photos function fetchPhotosList(first, max, callback, media_id){ // Create the IdSpec object var params = {}; params[opensocial.IdSpec.Field.USER_ID] = opensocial.IdSpec.PersonId.VIEWER; params[opensocial.IdSpec.Field.NETWORK_DISTANCE] = 0; var idspec = opensocial.newIdSpec(params); // Create the DataRequest object var request = opensocial.newDataRequest(); params = {}; // Set the paging parameters if(first){ params[opensocial.DataRequest.MediaItemRequestFields.FIRST] = first; } if(max){ params[opensocial.DataRequest.MediaItemRequestFields.MAX] = max; } // Pick a media item; media_id is an array of media IDs if(media_id){ params[opensocial.MediaItem.Field.ID] = media_id; } // Force it to images params[opensocial.MediaItem.Field.MEDIA_TYPE] = opensocial.MediaItem.Type.IMAGE; // Add the request to the queue request.add(request.newFetchAlbumsRequest(idspec, params), TTT.RequestKeys.VIEWER_PHOTOS);
337
338
Chapter 15 Porting Your App to OpenSocial 0.9
// Send it off request.send(callback); }
Again we specified opensocial.MediaItem.Type.IMAGE, but opensocial. MediaItem.Type.VIDEO is supported as well.
Updating Albums and Media Items These are APIs that allow you to update the metadata of albums and media items, not actually upload new ones.There are no 0.8 substitutes for these endpoints:§ /** * Updates the fields specified in the params. The following * fields cannot be set: MEDIA_ITEM_COUNT, * OWNER_ID, ID. Containers implement restrictions. * * @param {opensocial.IdSpec} An IdSpec used to specify which * people/groups to own the album. * * @param {string} albumId * The album to update. * * @param {Map} fields * The Album Fields to update.The following fields * cannot be set: MEDIA_ITEM_COUNT, * OWNER_ID, ID. Containers implement restrictions. * * @return {Object} A request object */ opensocial.DataRequest.prototype.newUpdateAlbumRequest = function(idSpec, albumId, fields){}; /** * Updates the fields specified in the params. The following * fields cannot be set: * ID, CREATED, ALBUM_ID, FILE_SIZE, NUM_COMMENTS. * Containers implement restrictions. * * @param {opensocial.IdSpec} An IdSpec used to specify which * people/groups own the album/media item. * * @param {string} albumId * The album containing the media item to update. * §Code
courtesy of OpenSocial.org: http://sites.google.com/site/opensocialdraft/Home/opensocialjavascript-api-reference/datarequest.
Media Item Support
* @param {string} mediaItemId * The media item to update. * * @param {Map} fields * The Album Fields to update. The following fields cannot be set: * ID, CREATED, ALBUM_ID, FILE_SIZE, NUM_COMMENTS. * Containers implement restrictions. * * @return {Object} A request object */ opensocial.DataRequest.prototype.newUpdateMediaItemRequest = function(idSpec, albumId, mediaItemId, fields){};
Let’s take a look at a quick example.The following function can be used to update the title, thumbnail URL, and description of a particular media item in a particular album. Since the functionality to update an album is very similar, we’ll leave it as an exercise for the reader to create a function to do so. // Updates a media item for the Viewer function updateMediaItem(album_id, media_id, newTitle, newPic, newDesc, callback){ // Create the IdSpec object var params = {}; params[opensocial.IdSpec.Field.USER_ID] = opensocial.IdSpec.PersonId.VIEWER; params[opensocial.IdSpec.Field.NETWORK_DISTANCE] = 0; var idspec = opensocial.newIdSpec(params); // Create the DataRequest object var request = opensocial.newDataRequest(); var fields = {}; // Set the new fields fields[opensocial.MediaItem.Field.TITLE] = newTitle; fields[opensocial.MediaItem.Field.THUMBNAIL_URL] = newPic; fields[opensocial.MediaItem.Field.DESCRIPTION] = newDesc; // Add the request to the queue request.add(request.newUpdateMediaItemRequest(idspec, album_id, media_id, fields), "update_mi"); // Send it off request.send(callback); }
339
340
Chapter 15 Porting Your App to OpenSocial 0.9
Uploading Media Items One of the big new features added to the spec is the ability to upload media. Sending the actual contents of an image isn’t very easy in JavaScript, so because of this the upload functionality works similarly to opensocial.requestSendMessage and opensocial. requestShareApp.You invoke opensocial.requestUploadMediaItem and a pop-up modal appears.The pop-up itself takes care of the actual uploading process. On MySpace either a Flash or a Java-based widget is used, depending on the end user’s system. Initially, only images are available for upload. Once an image has been uploaded and the user closes the pop-up window, a callback function is executed.The one parameter passed to this callback is an opensocial. ResponseItem object.This object contains information on any error that may have occurred, or an array of opensocial.MediaItem objects if there were any successful uploads. An example is in order: // Uploads an image to the specified album function uploadMediaItem(album_id){ opensocial.requestUploadMediaItem(album_id, uploadMediaItemCallback); } // Handles the response function uploadMediaItemCallback(response){ if(!response || response.hadError()){ // Error code var error_code = response.getErrorCode(); // Comma-delimited list of failed files var error_message = response.getErrorMessage(); // Array of failed files var failed_files = error_message.split(","); // Retry request? Update UI? } else{ // Array of MediaItem objects var mi_array = response.getData(); var div = document.getElementById("messages"); var url = opensocial.MediaItem.Field.THUMBNAIL_URL; for(var i = 0; i < mi_array.length; i++){ div.innerHTML += "
"; } } }
There are a couple of functions in the code.The first just wraps opensocial. requestUploadMediaItem.The second is specified as the callback function and handles the result of the attempted image upload.The response is checked for an error, and if one is found, we parse the error field for data.The error code is set as normal and should give you a good idea of what went wrong.The error message is a comma-delimited list of the files that had an error. If there was no error, either the user canceled the pop-up window or some files were uploaded. If the upload was successful, the data portion of the response contains an array of opensocial.MediaItem objects, one for each successfully uploaded image. If the array has a length of zero, you can assume the user canceled the action; otherwise you’re free to parse the array as you see fit. In our case, we simply iterate through the list of images and output them to the UI.
Simplification of App Data The functionality of app data has remained constant between versions, but the APIs used to interact with it have been significantly simplified.The original API was deemed to be too complex for what was actually allowed. For example, it was possible to update the app data only for the Viewer, yet the function accepted an ID as a parameter even though its only valid value was VIEWER. To that end, the signatures for updating and deleting app data were modified.The following is what they used to look like. Version 0.8 (Updating and Deleting App Data) opensocial.DataRequest.prototype.newUpdatePersonAppDataRequest = function(id, key, value) {}; opensocial.DataRequest.prototype.newRemovePersonAppDataRequest = function(id, keys) {};
They have now been changed for 0.9. Version 0.9 (Updating and Deleting App Data) opensocial.DataRequest.prototype.newUpdatePersonAppDataRequest = function(key, value) {}; opensocial.DataRequest.prototype.newRemovePersonAppDataRequest = function(keys) {};
341
342
Chapter 15 Porting Your App to OpenSocial 0.9
It’s a very simple change; you’ll literally just have to go through and remove the ID you were passing in. Updating and deleting app data weren’t the only APIs to be simplified; fetching app data also got a once-over.The entire opensocial.DataRequest. newFetchPersonAppDataRequest function was deprecated. Instead, app data was attached to the opensocial.Person object. Let’s take a look at an example: // Fetches the app data for Owner or Viewer function fetchPersonAppData(id, callback){ // Create an empty object to use for passing in the parameters var params = {}; var keys = ["key1", "key2", "key3"]; // Add the list of fields to the parameters params[opensocial.DataRequest.PeopleRequestFields.APP_DATA] = keys; // ID will be either: // opensocial.IdSpec.PersonId.VIEWER // or: // opensocial.IdSpec.PersonId.OWNER var appdata_req = req.newFetchPersonRequest(id, params); // Add the request to the queue and give it a key req.add(appdata_req, "app_data"); // Send it off req.send(callback); }
This function fetches the app data for either the Viewer or the Owner. First an array of strings is created with all the app data keys we’d like to fetch—in this case we’d like to fetch three keys.Those keys are added to the parameter object via the opensocial. DataRequest.PeopleRequestFields.APP_DATA enum.The id variable can be either opensocial.IdSpec.PersonId.OWNER or opensocial.IdSpec.PersonId.VIEWER. The request is then sent as normal. Some small modifications are required to fetch the app data for the Owner’s or Viewer’s friends.The basic idea is to do everything as we did in the previous code sample but use opensocial.DataRequest.newFetchPeopleRequest instead. Note that when fetching friends, you need to provide an opensocial.IdSpec object instead of a string ID; see Chapter 3, Getting Additional MySpace Data, for details on that. To retrieve the app data in the callback, you must use a function that has been added to the opensocial.Person object:** **Code
courtesy of OpenSocial.org: http://sites.google.com/site/opensocialdraft/Home/opensocialjavascript-api-reference/person.
REST APIs
/** * Gets the app data for this person that * key. * * @param {String} key The key to get app * @return {String} The corresponding app */ opensocial.Person.prototype.getAppData =
is associated with the specified
data for. data. function(key) {};
You simply pass in the key of the app data you’re looking for, and the value is returned. Let’s look at an example: // Parse the app data function getAppData(response){ // First check for an error if(!response.hadError()){ // Person is now an opensocial.Person object var person = response.get("app_data").getData(); var val1 = person.getAppData("key1"); var val2 = person.getAppData("key2"); var val3 = person.getAppData("key3"); } else{ // Retry the request? } }
The values of val1, val2, and val3 are the JSON objects that were stored in app data for key1, key2, and key3 respectively or undefined if nothing was found for that key.
REST APIs For those of you using iframe apps and the REST APIs, there’s not too much change for you either.The SDKs will be updated to point to the new APIs, so the change should be invisible to you. Simply get the updated SDKs from the usual place: http://code.google. com/p/myspaceid-sdk/. Replace your existing source files with the new ones. If any of this sounds mysterious to you, check out Chapters 8 and 9 (where we cover OAuth and external iframe apps) for details.That should set you up with hitting the updated APIs. Unfortunately, it’s not quite that easy, since the responses of some of the APIs have been slightly modified.This difference in the response between versions exists because the MySpace 0.8 APIs weren’t quite to spec, but now in 0.9 they are. So it’s not that the spec changed; it’s just that MySpace is now compliant. Let’s take a look at the two most important APIs, person and friends, and take a look at how they’ve changed.
343
344
Chapter 15 Porting Your App to OpenSocial 0.9
This is a sample response from the 0.9 REST API for a friend list: { "isFiltered":"false", "itemsPerPage":2, "startIndex":1, "totalResults":123 "entry": [ {"person": { "displayName":"Tom", "hasApp":"false", "id":"myspace.com.person.6221", "profileUrl":"http:\/\/www.myspace.com\/tom", "thumbnailUrl":"http:\/\/path_to_img.png" } }, {"person": { "displayName":"Chad Russell", "hasApp":"false", "id":"myspace.com.person.123965750", "profileUrl":"http:\/\/www.myspace.com\/chad_at_myspace", "thumbnailUrl":"http:\/\/path_to_img.png" } } ] }
Meanwhile, a sample response from the 0.8 REST API looks like this: { "count": 2, "Friends": [ {"name": "Tom", "largeImage": "http:\/\/path_to_img.jpg", "image": "http:\/\/path_to_img.jpg", "userId": 6221, "uri": "http:\/\/api.myspace.com\/v1\/users\/6221", "webUri": "http:\/\/www.myspace.com\/tom", "userType": "RegularUser"}, {"name": "Chris Cole", "largeImage": "http:\/\/path_to_img.png", "image": "http:\/\/path_to_img.png",
REST APIs
"userId": 45070236, "uri": "http:\/\/api.myspace.com\/v1\/users\/45070236", "webUri": "http:\/\/www.myspace.com\/ccole_myspace", "userType": "RegularUser"} ], "user": {"name": "Chad Russell", "largeImage": "http:\/\/path_to_img.jpg", "image": "http:\/\/path_to_img.jpg", "userId": 183399670, "uri": "http:\/\/api.myspace.com\/v1\/users\/183399670", "webUri": "http:\/\/www.myspace.com\/cdsrussell", "userType": "RegularUser"}, "topFriends": "http:\/\/uri_to_topfriends", "next": "http:\/\/uri_to_next" }
So, there are some small differences there; you’ll have to make some modifications to your mapping code to compensate. For example, in Chapter 9 we pushed the results of the friend list call down onto the client in a JavaScript object called friends.We then used friends like this: TTT.Lists.getCurrentList().list = friends.Friends; TTT.Lists.getCurrentList().total = friends.count;
In 0.9 that code would become TTT.Lists.getCurrentList().list = friends.entry; TTT.Lists.getCurrentList().total = friends.totalResults;
Essentially Friends becomes entry and count becomes totalResults.You can see this difference in the two sample responses shown earlier. Also, some other code accessed the individual fields for each friend: id = friends[i].userId; name = friends[i].name; picture = friends[i].image;
That code would need to be converted to the following for 0.9: id = friends[i].person.id; name = friends[i].person.displayName; picture = friends[i].person.thumbnailUrl;
Notice that a user’s ID changed from just an integer, like 6221, to something a little more complex; it now looks like myspace.com.person.6221.
345
346
Chapter 15 Porting Your App to OpenSocial 0.9
As for the person endpoint, let’s take a look at a sample response from the 0.9 REST API when all the person fields are requested: { "itemsPerPage":1, "startIndex":0, "totalResults":1, "person":{ "aboutMe":"very interesting...", "age":"29", "bodyType":{"build":"Slim \/ Slender"}, "books":["Books"], "children":["Someday"], "currentLocation":{ "country":"US", "formatted":"SEATTLE, Washington, US", "latitude":"47.6344", "locality":"SEATTLE", "longitude":"-122.3422", "postalCode":"98109", "region":"Washington" }, "displayName":"Chad Russell", "drinker":{"displayValue":"Yes","value":"YES"}, "ethnicity":"White \/ Caucasian", "gender":"male", "hasApp":"true", "heroes":["Heroes"], "id":"myspace.com.person.183399670", "interests":["General"], "lookingFor":[ {"displayValue":"Friends","value":"FRIENDS"}, {"displayValue":"Networking","value":"NETWORKING"} ], "movies":["Movies"], "msLargeImage":"http:\/\/path_to_img.jpg", "msMediumImage":"http:\/\/path_to_img.jpg", "msMood":"(none)", "msMoodLastUpdated":"2009-05-20T16:24:19", "msUserType":"RegularUser", "msZodiacSign":"Gemini", "music":["Music"], "name":{"familyName":"Russell","givenName":"Chad"}, "networkPresence":{"displayValue":"Online","value":"ONLINE"}, "organizations":[{ "address":{ "country":"US",
REST APIs
"formatted":"Seattle, WA, US", "locality":"Seattle", "region":"WA" }, "field":"Engineering", "name":"MySpace.com", "title":"Developer", "type":"job" }], "photos":[ {"primary":"true","value":"http:\/\/path_to_img.jpg"}, {"value":"http:\/\/path_to_img.jpg"}, {"value":"http:\/\/path_to_img.jpg"} ], "profileUrl":"http:\/\/www.myspace.com\/cdsrussell", "relationshipStatus":"Married", "religion":"Taoist", "sexualOrientation":"Straight", "smoker":{"displayValue":"No","value":"NO"}, "status":"", "thumbnailUrl":"http:\/\/path_to_img.jpg", "tvShows":["Television"], "urls":[{ "linkText":"cdsrussell", "type":"accountProfile", "value":"http:\/\/www.myspace.com\/cdsrussell" }], "userAppData":{"appData":[], "userId":"myspace.com.person.183399670"}, "utcOffset":"-06:00" } }
And let’s compare that to the sample response generated by the older API in Chapter 9, External Iframe Apps: { "interests": "General", "fullprofile": { "city": "SEATTLE", "maritalstatus": "Married", "country": "US", "aboutme": "very interesting...", "hometown": "Winnipeg", "culture": "en-US", "basicprofile": { "name": "Chad Russell",
347
348
Chapter 15 Porting Your App to OpenSocial 0.9
"largeImage": "http:\/\/path_to_img.jpg", "image": "http:\/\/path_to_img.jpg", "userId": 183399670, "uri": "http:\/\/api.myspace.com\/v1\/users\/183399670", "webUri": "http:\/\/www.myspace.com\/cdsrussell", "lastUpdatedDate": "4\/8\/2009 7:11:00 PM" }, "gender": "Male", "postalcode": "98109", "region": "Washington", "age": 28 }, "television": "Television", "mood": "", "headline": "", "status": "", "movies": "Movies", "books": "Books", "desiretomeet": "Who I'd Like to Meet", "music": "Music", "heroes": "Heroes", "type": "extended", "zodiacsign": "Gemini", "occupation": "Software" }
Again, there are quite a few differences. Fortunately most of the same data is present in 0.9, so it’s basically the same data presented in a different way. For the friends endpoint earlier, we showed the client-side code modifications necessary to make use of the new JSON object that will be generated. Here we’ll leave it as an exercise for the reader. Well, okay … one example. In Chapter 9 we requested the Viewer’s data and output all the fields to the UI.To parse the Viewer’s display name from the response from the REST API, we did this: sb.append(viewer.fullprofile.basicprofile.name + "
");
In the updated 0.9 response, the display name would be accessed like so: sb.append(viewer.person.displayName + "
");
Summary There are a few more small tweaks between OpenSocial versions 0.8 and 0.9 coming down the OpenSocial pipeline, but the bulk of the changes that will directly affect porting your apps can be found in this chapter. However, this chapter has a couple of “sister” chapters that deal with the biggest change in 0.9: OSML.To learn more about OSML, see Chapter 10, OSML, Gadgets, and the Data Pipeline, and Chapter 11, Advanced OSML.
Summary
Overall, we think you’ll find that MySpace’s implementation of version 0.9 is cleaner and more spec-compliant; after all, it’s the third iteration, and we all (we hope) learn from our mistakes, even MySpace developers. Deciding when and why to port your app is a question you’ll have to answer for yourself.You’ll have to weigh the benefits and the costs since porting your apps isn’t free. There will be extra development and testing time, not to mention the research time that came from reading this chapter. One of the ways MySpace tries to get app developers to upgrade their apps is to release new features only on the latest and greatest version. So you may want to wait until some killer new feature is released before you port your app. Or you may just want to play around with the newest version of the spec.The choice is yours. Note Code listings and/or code examples for this chapter can be found on our Google Code page under http://opensocialtictactoe.googlecode.com.
349
This page intentionally left blank
References
This appendix is a list of references used in writing this book. References appear in alphabetical order. In addition to the cited references here, refer to the OpenSocial Foundation Web site, www.opensocial.org/, for the latest information, documentation, and announcements. Amazon Web Services LLC. “Amazon Web Services.” Accessed October 2008. http://aws.amazon.com/. Aptana, Inc. “Aptana Cloud Services.” Accessed November 2008. http://aptana.com/cloud. Bishop, Bill, and Tim Murphy. “Broadband Connection Highs and Lows Across Rural America.” Daily Yonder, February 11, 2009. Accessed April 2009. www.dailyyonder.com/broadband-connection-highs-and-lows-across-rural-america/ 2009/02/11/1921. Brain, Marshall. “How the Radio Spectrum Works.” HowStuff Works.com, April 1, 2000. Accessed May 2009. www.howstuffworks.com/radio-spectrum.htm. Caceres, Marcos. “Widgets 1.0: Packaging and Configuration.” W3C Working Draft, December 2008. Accessed May 2009. www.w3.org/TR/widgets/. ———. “Widgets 1.0: The Widget Landscape (Q1 2008),” April 14, 2008. Accessed May 2009. www.w3.org/TR/widgets-land/. Cheng, Jacqui. “CWA Survey: Average Broadband Speed in the US Is 1.9 Mbps.” ARS Technica, May 29, 2007. Accessed April 2009. http://arstechnica.com/tech-policy/ news/2007/05/survey-average-broadband-speed-in-us-is-1-9mbps.ars. Correa, Daniel K. “Accessing Broadband in America: OECD and IEIF Broadband Rankings,” April 24, 2007.The Information Technology and Innovation Foundation. Accessed April 2009. www.itif.org/index.php?id=57. Google Inc. “Gadgets and Internationalization (i18n).” Accessed May 2009. http://code.google.com/apis/gadgets/docs/i18n.html. ———. “Getting Started: gadgets.* API,” 2009. Accessed May 2009. http://code.google.com/apis/gadgets/docs/gs.html. ———. “Google App Engine.” Accessed November 2008. http://code.google.com/appengine/. Intel Corporation. “Instruction Latencies in Assembly Code for 64-Bit Intel® Architecture,” July 13, 2007. Accessed April 2009. http://software.intel.com/ en-us/articles/instruction-latencies-in-assembly-code-for-64-bit-intel-architecture/.
352
References
Karbo, Michael B. “About RAM.” KarbosGuide.com, 2005. Accessed April 2009. www.karbosguide.com/hardware/module2e1.htm. Koch, Peter Paul. “Introduction to W3C Widgets.” Quirksmode, April 2009. Accessed April 2009. www.quirksmode.org/blog/archives/2009/04/introduction_to.html. Lawyer, David S. “Modem-HOWTO: Appendix G: Antique Modems.” ModemHOWTO, January 2007. Accessed April 2009. http://tldp.org/HOWTO/ Modem-HOWTO-29.html. Microsoft Corporation. “SQL Data Services Blog,” October 2008. Accessed November 2008. http://blogs.msdn.com/ssds/. “Mosaic—The First Global Web Browser.” Living Internet. Accessed April 2009. www.livinginternet.com/w/wi_mosaic.htm. MySpace.com. “MySpaceID Developer Addendum to MySpace.com Terms of Use Agreement,” March 16, 2008. Accessed April 2009. http://wiki.developer.myspace. com/index.php?title=MySpaceID_Developer_Addendum_to_MySpace.com_Terms_ of_Use_Agreement. “New Documents Show Enron Traders Manipulated California Energy Costs.” The Wall Street Journal, May 7, 2002. Accessed April 2009. http://online.wsj.com/article/ SB1020718637382274400.html. OpenSocial Foundation. “OpenSocial Data Pipelining Specification v0.9,” April 2009. Accessed May 2009. http://opensocial-resources.googlecode.com/svn/spec/0.9/ OpenSocial-Data-Pipelining.xml. ———. “OpenSocial Javascript API Reference,” September 28, 2008. Accessed December 2008. http://wiki.opensocial.org/index.php?title=JavaScript_API_Reference. ———. “OpenSocial Templating Specification v0.9,” April 15, 2009. Accessed May 2009. www.opensocial.org/Technical-Resources/opensocial-spec-v09/ OpenSocial-Templating.html. “TCP Connections:The Three-Way Handshake.” InetDaemon. Accessed June 2009. www.inetdaemon.com/tutorials/internet/tcp/3-way_handshake.shtml. U.S. Department of Commerce, National Telecommunications and Information Administration Office of Spectrum Management. “United States Frequency Allocations—The Radio Spectrum,” October 2003. Wikimedia Foundation Inc. “Death Star (Business),” February 2009. Accessed April 2009. http://en.wikipedia.org/wiki/Death_Star_(Business). ———. “Hertz,” April 2009. Accessed April 2009. http://en.wikipedia.org/wiki/Hertz. ———. “HTTP Cookie,” October 3, 2008. Accessed October 2008. http://en.wikipedia.org/wiki/Http_cookie.
References
———. “Internationalization and Localization.” Accessed April 2009. http://en.wikipedia.org/wiki/Internationalization_and_localization. ———. “Transmission Control Protocol,” June 26, 2009. Accessed June 2009. http://en.wikipedia.org/wiki/Transmission_Control_Protocol. Yahoo! Inc. “Exceptional Performance,” 2009. Accessed April 2009. http://developer.yahoo.com/performance/.
353
This page intentionally left blank
Index
Symbols and Numbers ${sender} reserved variable, 81–82 0.7 container, 208–211
A About section, Profile page, 276–278 access points, application security, 302 Acknowledge button, 267 activities
creating from app’s Canvas surface, 79 get_activities_atom, 194–195 get_friends_activities_atom, 195 getting app listed on Friend Updates. See opensocial.requestCreate Activity os:ActivitiesRequest tag, Data Pipeline, 223–224 Add App button, 69, 308 add function, 17 Add This App button, 7, 276 adjustheight function, cross-domain access, 23 AdSense, 311–313 advertisements
BuddyPoke app and, 320 creating with Cubics, 313–314 creating with Google AdSense, 311–313 creating with RockYou! ads, 314–316 monetizing Flixster Movies app through, 322 monetizing Playdom apps through, 326 TK’s apps, 323
356
aggreggation, of activity feeds
aggregation, of activity feeds, 82 Ajax (Asynchronous JavaScript and XML), 94, 200–203 albums
creating with create_album, 195–196 fetching, 41–42 fetching with get_album, 184–185 fetching with get_albums, 183 albums, porting to OpenSocial 0.9
fetching, 333–335 updating, 338–339 using opensocial.Album, 330–333 ALL, not filtering friend list, 33 ALL_ALL, invariant message bundles, 256 Amazon
cloud storage products, 64 Web Service, 174, 298–299
managing developers, 279–280 publishing. See publishing app republishing live app, 275 suspension and deletion of app, 280 app Profile, 275–278, 308 AppDataPlay game object, 125–133 Apple Dashboard widget format, 260 application security, 301–302 &appvers=dev, 275 app.yaml
getting started with Google App Engine, 157–158 sending messages using IFPC, 209 server code for REST API, 181 testing OAuth implementation locally, 166 updating for friends Web service, 202
app categories, 308
Aptana, and Unicode, 65
app data store, 47–56
arrays, responseValues, 74
app data P2P play downsides, 147 to avoid scaling bottlenecks, 298 hacking, 302 limitations of, 153 overview of, 47–48 refactoring to build local, 51–56 saving and retrieving data, 48–51 setting up AppDataPlay game, 127–133
Asynchronous JavaScript and XML (Ajax), 94, 200–203 asynchronous JavaScript requests, 15–17 authentication. See OAuth AUTHORIZATION parameter, gadgets.io.makeRequest, 95 automation candy, adding feed, 110–111 AWS (Amazon Web Service), 174 Azure, Microsoft, 174–175, 298–299
App Denial and Status Clarification, MySpace Developer’s Forum, 266–268
B
App Engine, Google, 64
BAD_REQUEST error, 26
app gallery, promoting app with, 306–308
bandwidth, saving, 164
app life cycle, 265–281
basic Profile, 204
changing app Profile/landing page, 275–278 hiding and deleting app, 274 making changes to live app, 274–275
batch requests, performance optimization, 292 blogs, 75–76 body items, 82–83
comments
body, message, 75 Boku (Mobilcash), 317 Bootstrapper
adding FriendPicker widget, 120–121 initializing FriendPicker, 124–125 bottlenecks, identifying scaling, 295–297 branded pokes, 320
creating “Hello World” app on, 4–6 defined, 5 MySpace messaging policies, 70 permission model for accessing user data, 11 sending notifications, 89 spreading app to other users, 69
breakpoints, using Firebug as debugger, xxv–xxvi
case sensitivity, language and country codes, 256
broadband connection speeds, 285
CDATA tags
browsers, developing MySpace with, xxiv BuddyPoke app, 318–319 bulletins, 75–78 button text, customizing between views, 232–235
adding other surfaces to gadgets, 229–230 fixing parsing errors in gadget code, 228–229 JavaScript blocks using, 225 CDN (content delivery network), 92
C cache memory, scaling bottlenecks and, 295–296 callback function
accessing more than just default Profile data, 18 accessing Profile information, 15–17 checking user permissions to access media, 44 combating permission-denied errors in activities, 87–88 defined, 15 paging friend list, 200–202 in requestSendMessage, 78–79 in requestShareApp, 72–74 setting up AppDataPlay game, 130–132 using friends list data, 38–39 using user data, 20 Canvas page
accessing MySpace data on, 10 creating activities from, 79
ckeynsecret.py file, OAuth, 155
clearAllGames method, 145 clearGame method, 144–145 clearing, P2P game, 144–145 client-side code
Data Pipeline tags processed in, 221–222 implementing paging in, 200–203 REST APIs, 197–199 updating player bio lightbox, 236–237 using custom templates in, 242–244 clients, and scaling performance, 298 cloud services
Amazon Web Service, 174 Google App Engine, 155 overview of, 64 pitfalls of deep linking, 93 codeveloper permissions, 279 comments
adding to turn-based games, 135–138 as supported message type, 74–76
357
358
communication
communication
external server. See external server communications viral features and. See viral features and communication
external server security constraints for, 91 overview of, 56 reasons to not use, 57–59 uses for, 64
connection speeds, and performance, 285
country codes, 256
Console tab, Firebug, xxv
CPUs, scaling bottlenecks in databases and, 295–296
constraints
polling interval for real-time play, 146–147 static content on external servers, 92 consumer key, MySpace
defined, 153–154 OAuth settings, 155–156 server code for REST API, 183 container-generator keys, obtaining, 19 Content block, gadget XML
adding surfaces to gadgets, 217–218, 229–230 custom tag template definition, 240–242 customizing button text between views, 232–235 defined, 215 getting information with os:ViewerRequest tag, 235–236 merging Home and Profile with shared, 230–231 reusing common content, 230–231 shared style, 231–232 subviews, 245–248 using basic data, 218–219 content delivery network (CDN), 92 CONTENT_TYPE parameter, gadgets.io.makeRequest, 95, 97 control flow tags, OSML, 226–227 Cookie Jacker, 59–64 cookies, 56–64
building Cookie Jacker app, 59–63
crackers, 302
create_album function, 195–196 cross-app promotions, 323–325 cross-domain access, 23–24 cross-site scripting (XSS) attacks, 303 CSS styling
editing app Profile page, 276–278 responsive performance rules for, 285 Cubics ads, 313–314 culture codes, 255–256
currentGame, clearing, 144–145 custom values, passing into app’s Canvas surface, 86
D data, getting additional. See MySpace, getting additional data data, getting basic. See MySpace, getting basic data data listeners, OSML, 250–254 Data Pipeline, 219–225
coupling OSML with. See OSML (OpenSocial Markup Language) data tag os:ActivitiesRequest, 223–224 data tag os:DataRequest, 223–224 data tag os:PeopleRequest, 222–223 data tags, 220–221 data tags os:ViewerRequest and os:OwnerRequest, 222 DataContext, 220 defined, 214
Dewoestine, Eric Van
Data Pipeline, contd.
displaying JSON results with data listener, 251–252 in-network vs. out-of-network data, 221–222 JavaScript blocks in OSML apps, 225 overview of, 219–220 working with, 235–237 data security, 301 data tags, Data Pipeline
in-network vs. out-of-network data, 221–222 os:ActivitiesRequest, 223–224 os:DataRequest, 223–224 os:PeopleRequest, 222–223 os:ViewerRequest and os:OwnerRequest, 222 overview of, 220–221 data types, creating activities, 80–81 data warehouses, for Internet-scale apps, 298 databases
data warehouses vs., 298 as primary scaling bottleneck, 295–297 DataContext, Data Pipelining, 220, 236–237 DataRequest object
accessing more than just default Profile data, 18–19 accessing Profile information, 15–17 app data store, saving and retrieving data, 48–51 asynchronous JavaScript requests and, 15 fetching albums, 333–335 fetching friend list, 31 fetching media, 39–43 fetching media items, 335–336 fetching Viewer’s photos, 336–338 friend list filters and sorts, 32–33
os:DataRequest tag, Data Pipeline, 223–225 paging friend list, 32–37 updating albums and media items, 338–339 DataResponse object
error handling, 25 testing for errors, 300 using MySpace user data, 19–24 “Death Star” project, Enron, 284 debug flag, POST requests, 165 debugging, using Script tab of Firebug, xxv–xxvi deep linking, 93 deleting app data, OpenSocial 0.8 and 0.9, 341–343 deleting apps, 274, 280 description, app, 307 design
adding feed reader to app, 98–104 turn-based games, 118–119 detailtype parameter, 203 Developer Addendum, 266 developers
managing, 279–281 signing up for MySpace account, 3–4 Developers & Testers option, My Apps page, 279–281 developers, interviews with successful, 318–326
Dan Yue (Playdom), 324–326 Dave Westwood (BuddyPoke app), 318–319 Eugene Park (Flixster Movies), 321–322 Tom Kincaid (TK’s apps), 322–324 Development version, changing live app, 274–275 Dewoestine, Eric Van, 157
359
360
display content, gadgets
display content, gadgets, 215
Enron, 284
display, designing feed reader, 103–104
Enterprise-scale applications, 293
display value, opensocial.Enum, 22
enums
displayMode property, FriendPicker, 123 DOM (Document Object Model)
adding feed reader to app, 93, 95 customizing button text between views, 233 handling raw XML content in, 98 JSONP calls and, 112–113 modifying script to use subviews and, 247–248 processing client-side templates, issues with, 243 processing RSS feed with FEED content type, 104, 106–107 setting up feed reader, 101–102, 104 TTT.List object references to, 37 domains, cross-domain access, 23–24 DRY (Don’t Repeat Yourself) acronym, 230 duplicate applications, and app rejection, 271–272 dynamic content, creating, 92
E e-mail accounts
creating “Hello World” app, 4 dealing with duplicate applications, 273 signing up for MySpace developer account, 3–4 Edit App Information screen, 155, 216–217 Edit App Source, 274–275 Edit Profile, 276 endpoints
Profile, 203–204 supported by MySpace SDK. See REST API list testing for errors with OpenSocial, 300
defined, 19 parsing out, 21–22 error codes, common, 26 error flow, providing, 300 error handling
for fault tolerance and stability, 300 fixing parsing in gadget code, 228–229 from makeRequest call, 100 in on-site vs. off-site app, 200–202 OpenSocial DataResponse objects, 300 OpenSocial functions for, 24–27 for performance optimization, 292 event handling, installs and uninstalls, 279–280 extended Profile, 204 external Iframe apps, 177–212
cookie vulnerabilities not applicable to, 64 pros and cons of, 177–178 REST APIs. See REST (REpresentational State Transfer) APIs sending messages using IFPC, 208–212 talking to parent page, 23 external server communications
adding feed reader. See feed reader, adding to app adding image search, 111–114 mashups, 92–93 overview of, 91–92 pitfalls of deep linking, 93 posting data with form, 114
FriendPicker
external servers
defined, 91 using Data Pipeline tags to pull in data from, 221 using OAuth. See OAuth
F Facebook, MySpace platform vs., 319, 321, 323, 325 failure array, responseValues, 74 “Fat Boy” project, Enron, 284 fault tolerance, 299–300 FEED content type, 100, 104–105 feed reader, adding to app, 93–111
adding feed refresh option, 109–110 feed automation candy, 110–111 FEED content type, 104–105 gadgets.io.makeRequest overview, 94–96 handling JSON content, 97 handling partial HTML content, 97 handling RSS feed content, 97–98 handling XML content, 98 overview of, 93–94 response structure, 96–97 secure communication, 111 setup and design of, 98–104 TEXT content type, 107–108 “user’s pick” feed reader, 98 XML content type with parsing, 105–107 feedback, for turn-based game play, 135–138 FeedBurner, 105
sorting and filtering, 32 using data, 37–39 fetchPhotosList function, 336–338 Fiddler, xxvi fields
accessing more than just default Profile data, 18–19 MyOpenSpace.Album, 42 MyOpenSpace.NotificationButton, 89 MyOpenSpace.Photo, 40 MyOpenSpace.Video, 43 opensocial.Person, 11–17 Profile endpoint, 203–204 using MySpace user data, 19–24 filters
app gallery, 306–308 fetched lists, 31–33 finishing, P2P game, 144–145 Firebug, 48, xxiv–xxvi Firefox, xxiv–xxv Flixster Movies, 321–322 FORBIDDEN error, 26 form posts, communicating with external servers, 114 fr-CA (French Canada) culture code, 255 fr-FR (French global) culture code, 255 friend list
calling requestShareApp, 72 fetching, 30–31 using data, 37–39 using filters and sorts, 31–32 using paging, 32–37 Friend Updates, getting app listed on. See opensocial.requestCreate Activity
feedCallback function, 100
friendClickAction property, FriendPicker, 123
fetchFriendList( ) function
FriendPicker
fetching friend list, 31 paging, 33–34
adding, 119–121 operation modes, 122
361
362
friends
FriendPicker, contd.
using in turn-based games, 121–125 friends
adding as developers, 279 displaying with repeater, 237–238 get_friends function, 185–187 get_friends_activities_atom function, 195 get_friendship function, 187–188 interacting with on MySpace, xxii–xxiii prefetching record lists for paging, 287–291 Web service and paging, 200–203 friends object, 211–212 friendsCatalog property, FriendPicker, 123 friends_obj parameter, 198 friends.py script, 202–203 full Profile, 204 function signatures
defining requestSendMessage, 75 defining requestShareApp, 70–71 fetching albums, 42 fetching photos, 40–41 fetching videos, 42
G gadget XML, 214–219
adding other surfaces, 229–230 adding second surface, 217–218 basic structure, 215 creating “Hello World”, 214–217 creating initial file from existing code, 227–228 declaring and using basic data, 218–219 defined, 214 defining basic app meta-information, 216–217
including translations in app and testing, 259–260 internationalization and message bundles, 255–260 using UTF-8 encoding, 259 gadgets.io.makeRequest
application security and, 301 feed reader, adding to app, 93 feed reader, setting up and designing, 100, 102 feed refresh option, adding, 109–110 Google providing implementation code for, 105 making real MySpace requests, 170–173 making requests back to GAE server, 157 making signed POST request, 162–166 myspace:RenderRequest tag and, 226 option parameters to, 95–96 os:HttpRequest tag equivalent to, 221 overview of, 94–96 performance ramifications of, 286 requesting data with Data Pipeline tags using, 221 spicing up Home and Profile surfaces, 173–174 gadgets.log, 126 gadgets.util.escapeString, 108 gadgets.views.requestNavigateTo(view) function, 23–24, 245–248 GAE (Google App Engine)
Amazon Web Service vs., 174 getting started with, 157–158 making signed POST request using OAuth to, 162–166 making simple GET request to, 158–162 OAuth settings, 155–157
hardware
GAE (Google App Engine), contd.
supported data store properties, 158–159 testing OAuth implementation locally, 166–169 game engine, supporting P2P game play, 133–135 game play. See P2P (person-to-person) game play GameInfo storage object, 125, 138–139
get_photo function, 190 get_photos function, 188–190 get_profile function, 190–191 get_status function, 191 GET_SUMMARIES parameter, gadgets.io.makeRequest, 95, 97 Getter object, MySpace requests, 169–172 get_video function, 193 get_videos function, 192–193 global (invariant) culture, 255, 260
gameStatus function, 135
globally unique identifiers (GUIDs), 178
GET requests
Gmail accounts, 273
making to GAE server, 158–162 real-world implications of, 164 testing OAuth implementation locally, 166–168 get_activities_atom function, 194–195 get_album function, 184–185 get_albums function, 183–184 getCurrentGameObject function, 138–139, 141 getData function, 20, 72–74 getDataCallback function, 20
Google
AdSense, 311–313 App Engine. See GAE (Google App Engine) cloud storage products, 64 Gadgets specification, 260 gadgets.io.makeRequest code and, 105 Translate, 258 GQL (Graphical Query Language), 162 grid-computing, as storage solution, 64–65 GUIDs (globally unique identifiers), 178
getDisplayValue function, 22 getErrorCode function, 73 getErrorMessage function, 25 getField function, 22 getFriendGameData function, 147 get_friends function, 185–187 get_friends_activities_atom function, 195 get_friendship function, 187–188 getGameMovesString function, 143–144 getID function, 22 get_indicators function, 196
H hackers
avoiding game play, 166 security and, 302 hadError( ) function
checking user permissions to access media, 44 error handling, 25 requestShareApp callback, 73
getInitialData function, 51
hard disks, scaling bottlenecks in databases, 296
getMarkUp function, 36–37
hardware
get_mood function, 187–188 get_moods function, 188
performance issues, 284 scaling bottlenecks in databases, 295–297
363
364
HAS-APP
I
HAS-APP, 33 hasPermission function, 43–45 HEADERS parameter, gadgets.io.makeRequest, 95 “Hello World”
creating app, 3–4 entering app source code, 4–6 for gadgets, 214–217 installing and running app, 7 signing up for developer account, 3–4 Hi5, importing apps to, 326 hiding apps, 274 high priority, defining in OpenSocial, 80 Home page
creating shared style Content blocks, 231–232 customizing button text between views, 232–235 defined, 5, xxii–xxiii getting app listed on Friend Updates, see opensocial.requestCreateActivity indicating new notification on, 88 merging with Profile page, 230–231 not accessing MySpace data on, 10 spicing up surface, 173–174 spreading app to other users, 68–69 horizontal scaling, 297–298 href attribute, opensocial.requestShareApp, 85 HTML
adding custom elements to About section of Profile page, 276–278 adding feed reader to app, 97 building feed reader UI, 99–101 fragment rendering in OSML using, 248–250 Inspect feature of Firebug using, xxv learning in order to use MySpace, 321 widget formats using, 260
icon, app, 307 id string, 40, 42 IDs
calling requestShareApp, 72 defining requestSendMessage, 75 sorting friend list by, 33 IdSpec object
defined, 30–31 fetching friend list, 31 setting up AppDataPlay game, 131–132 IFPC (inter-frame procedure call)
cross-domain access, 23 sending messages, 208–212 Iframe apps. See external Iframe apps iLike, app Profile page for, 276 image search, adding to app, 112–114 importing apps, to other social networks, 322 in-network data, Data Pipelining, 221–222 incentivized app installs, 310 indicators, get_indicators function, 196 infrastructure, and app data P2P play, 147 initializeGame( ) function, 51 inline tag templates, OSML, 239–244
creating custom tag template definition, 240–242 defined, 239 using client-side, 242–244 using custom tags, 242 inputs, validating, 299, 302–303 Inspect feature, Firebug, xxv installing apps, event handling, 279–280 inter-frame procedure call (IFPC)
cross-domain access, 23 sending messages, 208–212
JVM (Java Virtual Machine)
INTERNAL_ERROR, common cause of, 25–26
invite.py, 181, 202
internationalization, and message bundles, 255–260
ISPs, database storage using, 64
creating first message bundle, 256–257 creating translations of message bundle, 257–258 culture code processing order, 255 including translations in app and testing, 258–260 limitations of message bundles, 260 overview of, 255 Internet history, 250 Internet-scale applications, defined, 293 Internet-scale applications, performance guidelines
data warehouses vs. relational databases, 298 identifying scaling bottlenecks, 295–297 knowing scaling point, 294–295 load-testing system, 299 overview of, 293–294 pushing work out to nodes, 298 remembering what you know, 297 scaling horizontally, 297–298 utility computing, 298–299 interviews with developers. See developers, interviews with successful invariant (global) culture, 255, 260 Invite page
client code for off-site app, 198–199 creating link to, 204 Invite tab
implementing, 180–182 sending messages to users, 203–208 updating to use OSML and Data Pipeline, 237–238
isADraw function, 133–135
J jail domain, 23, 91 Java, hardware performance issues, 284 Java Virtual Machine (JVM), 284 JavaScript
blocks in OSML apps, 225 error handling in, 24–27 information on using, 30 learning in order to use MySpace, 321 OSML vs., 219–220 responsive performance rules for, 285 sending messages using IFPC, 208–212 TTT namespace, 30 understanding asynchronous requests in, 15 JSLoader, 208–209 JSON (JavaScript Object Notation)
adding feed reader to app, 97 Ajax using, 94 app data game store using, 125 displaying results with data listener, 251–254 error handling in off-site app, 201 evaluating data in app data store, 48 handling content for feed reader, 97 makeRequest response object properties and, 96–97 processing RSS feed with FEED content type, 104–105 using simplejson file to manipulate strings, 160 JSP EL (JavaServer Pages Expression Language), 214 JVM (Java Virtual Machine), 284
365
366
keys
K keys
MySpace secret and consumer, 153–154 opensocial.Enum object, 22 setting up AppDataPlay game, 127 Kincaid, Tom, 322–324
L
logic flows
designing turn-based games, 118 for P2P game play, 138–144, 147 setting up AppDataPlay game object, 125–133 three-way handshakes as, 119 lookForWin function, P2P game play, 133–135 low priority, defining OpenSocial, 80
M
landing page, changing app, 275–276 latestGameInfo, 139 legal issues, deep linking, 93 libraries, OAuth, 154 lightbox, updating player bio, 236–237
Mail Center, notification folder, 88 makePlayerMove function, 134, 135–138
Lightweight JS APIs, 330
makeRequest calls. See gadgets.io.makeRequest
literal data type, 81
marketing and monetizing, 305–327
literals
defined, 19 parsing out, 21–22 live app, making changes to, 274–275 Live version, making changes to, 274–275 load-testing, 299 loadAppData function, 52–53 loadAppDataCallback function, 130–131 loadFeed function, 100 loadFriendPicker function, 124 loadGame function, 139–142 loading issues, example of app rejection, 271–272 localization
creating translations of message bundle, 257–258 defined, 255 localRelay parameter, IFPC, 209 logging, debugging app, 126
developer interviews. See developers, interviews with successful generating revenue with micropayments, 316–318 overview of, 305 promoting app on MySpace, 306–309 user base and viral spreading. See viral spreading markup
OSML tags, 226 simplifying for responsive performance, 285 mashups, 92–93, 98 MAX, paging friend list, 32–37 MDP (MySpace Developer Platform), xxiv media items
fetching albums, 41–42 fetching photos, 39–41 fetching videos, 42–43 including on message template, 82–83 using opensocial.requestShareApp, 86
MySpace, getting basic data
media items, OpenSocial 0.9
fetching albums, 333–335 fetching in 0.8 and, 335–336 fetching media items, 335–336 fetching Viewer’s photos, 336–338 opensocial.Album, 330–333 updating albums and media items, 338–339 uploading media items, 340–341 memory, scaling bottlenecks, 295–297
get_moods function, 188 get_status function, 191 set_mood function, 195 multiple submissions, example of app rejection, 270–271 My Application screen, 265 MyAds, MySpace, 308–309 mylocalAppData, 52–53 MyOpenSpace.Album object, 41–42 MyOpenSpace.NotificationButton object, 89
message bundles. See internationalization, and message bundles
MyOpenSpace.Photo object, 40
message parameter, requestSendMessage, 75
MyOpenSpace.requestCreateNotification, 88–90
messaging
MyOpenSpace.Video object, 42–43
MySpace policies for, 70 using requestSendMessage, 74–79 metadata, gadgets, 215 METHOD parameter, gadgets.io.makeRequest, 96 method stubs, for game play, 128–129 methods
opensocial.Person object, 11–14 opensocial.ResponseItem, 20
MySpace
Agreement, 266 creating “Hello World” app, 3–6 Developer’s Forum, 266 installing and running app, 7 Open Platform, xxiv promoting app on, 306–309 Terms of Service, 310 Terms of Use. See Terms of Use understanding, xxii
micropayments, generating revenue with, 316–318
MySpace Developer Platform (MDP), xxiv
Microsoft
MySpace, getting additional data, 29–46
Azure, 174–175, 298–299 cloud storage products, 64 Mobilcash (Boku), 317 Mobsters, 316, 325 modal dialogs, 69–70 ModulePrefs section, gadgets, 215, 216–217 modulo operations, 136 monetizing. See marketing and monetizing mood
get_mood function, 187–188
fetching albums, 41–42 fetching friend list, 30–31 fetching photos, 39–41 fetching videos, 42–43 using data, 37–39 using filters and sorts, 31–32 using paging, 32–37 MySpace, getting basic data, 9–27
accessing more than just default Profile data, 18–19 accessing Profile information, 15–17
367
368
myspace:RenderRequest
MySpace, getting basic data, contd.
accessing user data, 11–14 error handling, 24–27 Owner and Viewer concepts, 9–10 permissions concepts, 10 starting Tic-Tac-Toe app, 10–11 using MySpace user data, 19–24 myspace:RenderRequest, 249–250
NotificationButton object, 89 notifications
MySpace messaging policies, 70 sending, 88–90 send_notification function, 196–197 NOT_IMPLEMENTED error, cause of, 26 NUM_ENTRIES parameter, gadgets.io.makeRequest, 96
O
N OAuth, 153–175 name, app, 307 names
reserved variable, 81–82 sorting friend list by nicknames, 33 namespaces
MyOpenSpace, 40–41 TTT. See TTT namespace navigation
away from current page to specified view, 23–24 keystrokes used for, 122 to subviews, 245–248 using cookies for storage across surface, 64 using Pager object, 36
external server communication security with, 111 libraries, 154 MySpace incompatibilities with, 157 overview of, 153 secure phone home. See phoning home setting up environment, 154–157 spicing up Home and Profile surfaces, 173–174 testing implementation locally, 166–169 understanding, 153–154 objects
defined, 19 parsing out, 21–22
network distance, fetching friend list and, 31
off-site apps. See external Iframe apps
New App Invite, 68–69
online references
newFetchAlbumsRequest function, 333–335 newFetchMediaItemsRequest function, 335 newFetchPeopleRequest function
calling requestShareApp, 72 fetching friend list, 30–31 using data, 37–39 newFetchPersonRequest, 16 new_height function, for cross-domain access, 23 Next button, handling with paging, 36
Aptana, 259 basic, full and extended Profile data, 204 code examples for developing first app, 7 Cookie Jacker, 59 Cubics, 313–314 Fiddler, xxvi Firebug, xxv FriendPicker properties, 121
opensocial.requestShareApp
online references, contd.
GAE (Google App Engine), 157 Google AdSense, 311–313 micropayment companies, 316–318 MySpace SDK, 183 OAuth, 153 OpenSocial, xxiii PayPal, 316 Python download, 155 RockYou! ads, 314–316 SDKs for accessing MySpace APIs, 154 Tic-Tac-Toe installation, 10 TortoiseSVN, 155 ONLINE_FRIENDS, filtering friend list, 33 Open Canvas, 10–11 OpenSocial
app data store. See app data store basic request and response pattern for, 17 container, 40, xxiii porting app to 0.9, porting app to OpenSocial 0.9 Sandbox tool, 214–217, 227–228 understanding, xxiii–xxiv OpenSocial Markup Language. See OSML (OpenSocial Markup Language) opensocial.Activity, 221 opensocial.Album, 330–333 opensocial.DataResponse. See DataResponse object opensocial.hasPermission, 43–45 opensocial.IdSpec. See IdSpec object opensocial.Message
defining requestSendMessage, 75–76 defining requestShareApp, 70–71 writing requestSendMessage code, 76–78 writing requestShareApp code, 71
opensocial.newUpdatePersonAppDataRequest, 48 opensocial.Person
accessing more than just default Profile data, 18–19 accessing MySpace user data, 11–12 accessing Profile information, 15–17 Data Pipeline tags resulting in, 221 fetching friend list vs. fetching single, 30 request/response pattern, 19–24 opensocial.requestCreateActivity, 79–88
aggregation, 82 body and media items, 82–83 data types, 80–81 defining, 79–80 getting app listed on friend updates, 79–88 notifications patterned after, 88–89 overview of, 79 raising the event, 85–86 reserved variable names, 81–82 using activity callbacks to combat permission-denied errors, 86–87 using Template Editor to create templates, 83–94 using template system to create activities, 80 opensocial.requestCreateActivityPriority, 80 opensocial.requestPermission, 43–45, 87–88 opensocial.requestSendMessage, 74–79
callback for, 78–79 defining, 75–76 overview of, 74–75 writing code, 76–78 opensocial.requestShareApp, 67–74
callback for, 71–74 calling, 71
369
370
opensocial.requestUploadMediaItem
opensocial.requestShareApp, contd.
communication policies and rules, 70 defining, 70–71 requestSendMessage signature vs., 75 understanding, 67–69 writing code, 71 opensocial.requestUploadMediaItem, 340–341 opensocial.ResponseItem
error handling, 25–26 requestShareApp callback, 72–74 using MySpace user data, 19–24 opponent
always considering computer as, 143 creating custom tag template definition for, 240–242 designing logic flow for turn-based games, 118–119 implementing with P2P logic flow, 138–144 opponentPickedAction function, FriendPicker, 122–124 opt_callback parameter
defining requestCreateActivity, 79 defining requestSendMessage, 75 defining requestShareApp, 71 optional keys, 19 opt_params object
defined, 18–19 defining requestShareApp, 71 fetching albums, 42 fetching photos, 40 fetching videos, 42 POST request, 172–173 Orkut, importing app to, 322 os-data section, Content block, 235–236 OS Lite, 330
os:ActivitiesRequest tag, Data Pipeline, 221–224 os:DataRequest tag, Data Pipeline, 221–224 os:Get tag, Data Pipeline, 248–249 os:HttpRequest tag, Data Pipeline, 221–222, 251–252 os:If control flow tag, OSML, 226–227 OSML (OpenSocial Markup Language)
basic display tags, 226 control flow tags, 226–227 Data Pipeline, 219–225 defined, 214 gadget XML, 214–219 overview of, 213–214 remote content display tags, 226 understanding, 225–226 OSML (OpenSocial Markup Language), advanced, 239–261
data listeners, 250–254 future directions, 260 HTML fragment rendering, 248–250 inline tag templates, 239–244 internationalization and message bundles, 255–260 working with subviews, 245–248 OSML (OpenSocial Markup Language), applying to Tic-Tac-Toe app, 226–238
displaying data lists, 237–238 reusing common content, 230–235 setting up gadget, 227–230 working with data, 235–237 os:OwnerRequest tag, Data Pipeline, 221–222 os:PeopleRequest tag, Data Pipeline
defined, 221 displaying friends list with repeater, 237–238 overview of, 222–223 processed on server, 221–222
permissions
os:ViewerRequest tag, Data Pipeline
defined, 221 getting Viewer information with, 235–236 overview of, 222 processed on server, 221–222 osx:Else control flow tag, OSML, 227
paging
friend list, 199–203 prefetching record lists for performance optimization, 287–291 using, 32–37 parameters
custom tag template definitions, 241 gadgets.io.makeRequest, 95–96
out-of-network data, Data Pipelining, 221–222
Park, Eugene, 321–322
Own Your Friends app, 325
parsing
Owner
app data store. See app data store concept of, 9–10 fetching app data for, 342 fetching friend list for, 30–31 getting ID of current, 169–170, 172 getting more than just default profile data, 19 os:OwnerRequest tag, 221–222 permission model for accessing user data, 10–11 reading app data from, 47–48 signed POST requests for authenticating, 162–163
P P2P (person-to-person) game play, 117–149
adding user feedback, 135–138 advantages/disadvantages of, 148 finishing and clearing game, 144–145 fleshing out game logic, 138–144 “real-time” play, 146–148 supporting in game engine, 133–135 turn-based games. See turn-based gaming pageSize property, FriendPicker, 123
fixing errors in gadget code, 228–229 XML content type with, 105–106 Pay by Mobile button, Boku, 317 PayPal, 316–317 Pending status, publishing app, 266 performance, responsive
designing for, 284–285 designing for scale. See scaling performance OpenSocial app guidelines, 285–292 overview of, 283 stability and fault tolerance, 299–300 understanding, 283–284 understanding scale, 284 user and application security, 300–303 permission-denied errors, combating, 87–88 permissions
accessing more than just default Profile data, 18–19 checking user settings, 43–45 codeveloper, 279 error code causes, 26 fetching photos uploaded to Profile, 39–41 “Hello World” app, 6 MySpace model for, 9–11 MySpace supported, 44–45 notifications, 90
371
372
persisting information (between sessions)
persisting information (between sessions)
setting up AppDataPlay game, 129–132 using app data store. See app data store using cookies. See cookies using third-party database storage, 64–65 Person data type, 81 Person objects, see opensocial.Person object person-to-person game play. See P2P (person-to-person) game play phoning home, 157–173
making real MySpace requests, 169–173 overview of, 157 signed POST request, 162–166 testing OAuth implementation locally, 166–169 unsigned GET request, 158–162 photos
adding to Profile page, 276–278 checking user permissions to access, 43–45 fetching, 39–41 fetching for Viewer in OpenSocial 0.9 and 0.8, 336–338 fetching with get_photo function, 190 fetching with get_photos function, 188–190 Play page, 198 Play tab
porting to off-site app, 203–208 requiring user’s Profile data, 180 Playdom apps, 324–326 playerBioWrapper ID, 236–237 policies, MySpace communications or messaging, 70 pollForOpponentUpdatedMove function, 147 polling design, for “real time” play, 146–148
porting app to OpenSocial 0.9, 329–349
fetching albums, 333–335 fetching media items, 335–336 fetching Viewer’s photos, 336–338 opensocial.Album, 330–333 overview of, 329–330 REST APIs, 343–348 simplification of app data, 341–343 updating albums and media items, 338–339 uploading media items, 340–341 POST requests
making real MySpace requests, 172–173 making to GAE server using OAuth, 162–166 real-world implications of, 164 testing OAuth implementation locally, 166–168 POST_DATA parameter, gadgets.io.makeRequest, 96 Poster object, real MySpace requests, 169, 172–173 postTo function, 0.7, 210–211 power users, growing base of, 311 Prev button, 36 Preview Template button, Template Editor, 84 printPerson function
getting information with os:Viewer-Request tag, 235–236 updating player bio lightbox, 236–237 using MySpace user data, 21 priorities
defining in OpenSocial, 80 defining opensocial.requestCreateActivity, 79 privacy, cookies and, 57
relay files, using IFPC
Profile page
accessing information using opensocial.Person, 15–17 accessing more than just default data, 18–19 accessing user data, permission model for, 11 bulletins, 77 changing, 275–276 comments on, 74 creating shared style Content blocks, 231–232 customizing button text between views, 232–235 defined, 5, xxii defining for app, 4 defining requestShareApp on, 70–71 editing/updating user’s, 75 endpoint, 203–204 fetching photos uploaded to, 39–41 get_profile function, 190–191 installing and running app, 7 linking to, 7 merging with Home page using shared Content blocks, 230–231 not accessing MySpace data on, 10 reserved variable names and, 81 spicing up surface using makeRequest, 173–174 as supported message type, 75–76 profileDetail property, 19 promoting app. See marketing and monetizing properties
FriendPicker, 121–123 Google App Engine dat store, 158–159 makeRequest response object, 97 proto-mashups, 93
Prototype library, paging friend list, 200 proxy servers, makeRequest calls, 94 Publish button, 265–266 publishing app, 265–273
case study in successful rejection negotiation, 268–273 contesting rejection, 267–268 dealing with rejection, 267 overview of, 265–266 republishing live app, 275 why apps are not approved, 266 pushing work out to nodes, for Internet-scale apps, 298 Python editor, OAuth settings, 155
R RAM memory, database performance, 296–297 readers-writers problem, solving, 51–53 “real-time” play, P2P games, 146–148 reason parameter, 70–71 recipients parameter, 70, 75 refactoring, to build local app data store, 51–56 refresh option, feed, 109–110 REFRESH_INTERVAL parameter, gadgets.io.makeRequest, 96, 109 registerOnLoadHandler directive, feed refresh, 109 rejection, app
contesting app, 267–268 dealing with app, 266 examples of, 270–273 reasons for app, 266 successful negotiation case study, 268–270 relational databases, data warehouses vs., 298 relay files, using IFPC, 209
373
374
remote content display tags, OSML
remote content display tags, OSML, 226 removeGame method, 144–145 repeater, displaying friends list, 237–238 REpresentational State Transfer APIs. See REST (REpresentational State Transfer) APIs republishing live app, 275 requestCreateActivity function. See opensocial.requestCreateActivity requesting data, and performance, 286 requestPermission function, 43–45, 87–88 requestSendMessage function. See opensocial.requestSendMessage requestShareApp function. See opensocial.requestShareApp reserved variable names, 81–82 response structure, adding feed reader to app, 96–97 ResponseItem object. See opensocial.ResponseItem responseJSON property, error handling when paging, 202 responseValues, arrays, 74 responsive performance. See performance, responsive REST API list, 183–197
create_album function, 195–196 get_activities_atom function, 194–195 get_album function, 184–185 get_albums function, 183 get_friends function, 185–187 get_friends_activities_atom function, 195 get_friendship function, 187–188 get_indicators function, 196 get_mood function, 187–188 get_moods function, 188 get_photo function, 190 get_photos function, 188–190
get_profile function, 190–191 get_status function, 191 get_video function, 193 get_videos function, 191 send_notification function, 196–197 set_mood function, 195 set_status function, 195 test pages, 197 REST (REpresentational State Transfer) APIs, 178–208
client code, 197–199 friends response from, 211–212 friends Web service and paging, 199–203 how Web service is addressed, 178–179 os:DataRequest tag resulting in endpoints, 221 overview of, 178 porting app to OpenSocial 0.9, 343–348 Profile endpoint, 203–208 server code, 181–183 setting up external Iframe app, 179–180 supported endpoints. See endpoints, supported by MySpace SDK supporting XML and JSON formats, 94 retrieving, friend’s app data, 131–132 RockYou! ads, 314–316 RPC. See IFPC (inter-frame procedure call) RSS feeds
adding content to feed reader app, 97–98 building feed reader UI, 98–101 processing with FEED content type, 104–105 runOnLoad function, 198–199
source code
S Sandbox Editor
declaring and using basic data, 218–219 entering and executing code, 215–216 using os:ViewerRequest tag, 236 Sandbox tool, 214–217, 227–228 Save Template button, Template Editor, 84 scaling
advantages of app data P2P play, 147 BuddyPoke app, 320 Flixster Movies app, 322 using external cloud servers for storage, 64–65 scaling performance
application scale definitions, 293 defined, 284 Internet-scale app guidelines. See Internet-scale applications, performance guidelines scaling point, 294–295 Script tab, Firebug, xxv–xxvi scripts, 247–248 SDKs (software development kits)
accessing MySpace APIs, 154, 183 endpoints supported by MySpace, 183–197 secret key, MySpace, 153–154, 183 security
app data store limitations, 153 application, 301–302 external server communications, 111 fixing vulnerabilities, 311 hacking and cracking, 302–303 user data, 301 selectedFriend property, FriendPicker, 123 selectedOpponentDataCallback function
app data game store, 131–132
recognizing when new game has started, 145 selecting opponent, 138–140 semaphore (flag value), readers-writers problem, 52–53 send( ) function, 17 Send Message, 74–76 send_notification function, 196–197 servers
code for REST API, 181–183 Data Pipeline tags processed on, 221–222 external. See external server communications; external servers Set Lots of Cookies option, Cookie Jacker, 63 setBackgroundClicked function, 43–44 set_mood function, 195 set_status function, 195 setTimeout directive, 52–53, 109–110 shared style Content blocks, 231–232 showCurrentPage method, 290–291 showFeedResults function, 103–104 showViewerBio function, 236–237 signatures
function. See function signatures OAuth, 162–164 simplejson folder, 156–157, 160 Small/workgroup scale applications, 293 software development kits (SDKs)
accessing MySpace APIs, 154, 183 endpoints supported by MySpace, 183–197 sorting, fetched lists, 31–33 source code
backing up before granting developer status, 279 creating “Hello World” app, 4–6
375
376
stability, and responsive performance
stability, and responsive performance, 299–300 startGamePlayPolling function, 146–147 static content, and constraints, 92 status
get_status function, 191 set_status function, 195 storage
app data store. See app data store cookies. See cookies third-party database, 64–65 styles
custom tag, 241–242 shared style Content blocks, 231–232 Subversion, 155 subviews, OSML, 245–248 success array, responseValues, 74 surfaces
adding to gadget file, 229–230 creating “Hello World” app, 4–6 MySpace Open Social, 5 suspension of app, 280
T
creating with Template Editor, 83–84 defining requestShareApp using, 70–71 inline tag. See inline tag templates, OSML notification built-in, 89 using opensocial.requestShareApp, 85–86 Terms of Use
app suspension from violating, 280 example of app rejection, 270–273 overview of, 266 test pages, REST APIs, 197 testing
managing testers, 279 message bundles, 257 OAuth implementation locally, 166–169 OpenSocial endpoint errors, 300 translations, 259–260 TEXT content type, feed readers, 107–108 TEXT format, partial HTML content, 97 third-party database storage, 64–65 threads, solving readers-writers problem, 51–53 three-way handshakes, 119, 144
tabs, converting to subviews, 245–248
Tic-Tac-Toe app. See data, getting basic
tag templates. See inline tag templates, OSML
time-outs, providing, 300
tags
TK’s apps, 322–324
CDATA, 225 Data Pipeline, 219–223 OSML, 225–226 TCP (transmission control protocol), three-way handshakes, 119 Template Editor, 83–84 templated apps, 270–271 templates
creating activities using, 80–83
title, message, 75 tools, MySpace development, xxiv–xxvi TOP_FRIENDS, filtering friend list, 33 TortoiseSVN, 155 transferring data, three-way handshake, 119 translations
creating first message bundle for, 256–257 including and testing, 258–260 internationalization and, 255
utility computing
uninstalling apps, 279–280
translations, contd.
message bundle, 257–258 testing, 259–260 transmission control protocol (TCP), three-way handshakes, 119 Try App link, 307–308 TTT namespace
defined, 30 paging, 37 using data, 37–39 TTT.AppDataPlay game object
adding support in game engine, 133–135 adding user feedback, 135–138 clearing game, 144–145 implementing logic flow, 138–144 setting up, 125–133 TTT.Game (game engine), 133–135 TTT.Record.Getter object, 169–172 TTT.Record.Poster object, 169, 172–173 turn-based gaming, 117–133
adding FriendPicker, 119–121 app data game store, 125–133 design overview, 118–119 overview of, 117–118 using FriendPicker, 121–125
U UI (user interface)
adding FriendPicker to game play, 119–121 building feed reader, 98–101 building using Inspect feature of Firebug, xxv using FriendPicker, 121–125
universal resource identifiers (URIs), REST, 178–179 Updates module, Home page, 68–69 updating
albums and media items in OpenSocial 0.9, 338–339 app data in Open Social 0.8 and 0.9, 341–343 translations, 260 uploading media items, 340–341 URIs (universal resource identifiers), REST, 178–179 URLs
making real MySpace requests, 170–173 warning when placing in attributes, 251 user data
accessing, 11–14 accessing more than just default Profile data, 18–19 accessing Profile information, 15–17 using, 19–24 user interface. See UI (user interface) users
adding feedback to P2P game play, 135–138 data security for, 301 experience/functionality of app, 271–272, 285 implementing logic for selecting opponent, 140 retaining by listening to them, 311 viral spreading and. See viral spreading “user’s pick” feed reader, 98
UNAUTHORIZED error, cause of, 26
UTF-8 encoding, for app gadgets, 259
Unicode encoding, and app gadgets, 258
utility computing, 298–299
377
378
validating inputs
V validating inputs, 299 variables, setting up AppDataPlay game, 127–128 variables, template
creating templates with Template Editor, 84 data types, 80–81 notifications, 89 reserved names, 81 using opensocial.requestShareApp, 86 verify function, OAuth, 157 verify_request function, OAuth, 165–166 vertical scaling, 297–298 videos
fetching, 42–43 fetching with get_video function, 193 fetching with get_videos function, 192–193
viral features and communication, 67–90
getting app listed on friend updates, 79–88 sending messages and communications, 74–79 sending notifications, 88–90 spreading app to other users, 67–74 viral spreading, 309–316
of BuddyPoke app, 319–320 Cubics, 313–314 Google AdSense, 311–313 listening to customers, 311 overview of, 309–311 Playdom apps using, 325–326 RockYou! ads, 314–316 Visit Profile link, 7
W W3C Widgets specification, 260
View Development Version link, “Hello World” app, 6
Watch window, Script tab of Firebug, xxv–xxvi
Viewer, 236–237
Web applications, driving performance plateau, 284
accessing more than just default Profile data, 18–19 accessing Profile information, 15–17 app data store. See app data store concept of, 9–10 creating custom tag template definition for, 240–242 declaring data in gadget XML for, 218–219 fetching app data for, 342 fetching friend list for, 30–31 getting information with os:ViewerRequest tag, 235–236 os:ViewerRequest tag, 221–222 permission model for accessing user data, 10–11
Web references. See online references Web service, 202–203 Westwood, Dave, 318–319 widget formats, commonly known, 260 Widgets specification, W3C, 260 win/loss record
making MySpace requests, 169–173 making simple GET request to save, 158–162 testing OAuth locally, 166–169 updating with signed POST request, 162–166 Windows Sidebar gadget format, 260 Winloss class, 158–162, 164–166
Yue, Dan
“Word of the Day”, feed reader apps, 110–111 ws.py script, 158, 164–166
X XHR (XMLHtppRequest) object
asynchronous JavaScript requests, 15 external server security constraints for, 91 makeRequest as wrapper on top of, 94–95 overview of, 94
refactoring to build local app data store, 51–52 XML. See also gadget XML
adding feed reader to app, 98 content type with parsing, 105–107 handling content for feed reader, 98 XSS (cross-site scripting) attacks, 303
Y Yahoo! Babelfish, 258 Yue, Dan, 324–326
379
informIT.com
THE TRUSTED TECHNOLOGY LEARNING SOURCE
InformIT is a brand of Pearson and the online presence for the world’s leading technology publishers. It’s your source for reliable and qualified content and knowledge, providing access to the top brands, authors, and contributors from the tech community.
LearnIT at InformIT Looking for a book, eBook, or training video on a new technology? Seeking timely and relevant information and tutorials? Looking for expert opinions, advice, and tips? InformIT has the solution. • Learn about new releases and special promotions by subscribing to a wide variety of newsletters. Visit informit.com /newsletters. • Access FREE podcasts from experts at informit.com /podcasts. • Read the latest author articles and sample chapters at informit.com /articles. • Access thousands of books and videos in the Safari Books Online digital library at safari.informit.com. • Get tips from expert blogs at informit.com /blogs. Visit informit.com /learn to discover all the ways you can access the hottest technology content.
Are You Part of the IT Crowd? Connect with Pearson authors and editors via RSS feeds, Facebook, Twitter, YouTube, and more! Visit informit.com /socialconnect.
informIT.com
THE TRUSTED TECHNOLOGY LEARNING SOURCE
Developer’s Library Series
Visit developers-library.com for a complete list of available products
T
he Developer’s Library Series from Addison-Wesley provides practicing programmers with unique, high-quality references and
tutorials on the latest programming languages and technologies they use in their daily work. All books in the Developer’s Library are written by expert technology practitioners who are exceptionally skilled at organizing and presenting information in a way that’s useful for other programmers. Developer’s Library books cover a wide range of topics, from opensource programming languages and databases, Linux programming, Microsoft, and Java, to Web development, social networking platforms, Mac/iPhone programming, and Android programming.
Try Safari Books Online FREE Get online access to 5,000+ Books and Videos
FREE TRIAL—GET STARTED TODAY! www.informit.com/safaritrial Find trusted answers, fast Only Safari lets you search across thousands of best-selling books from the top technology publishers, including Addison-Wesley Professional, Cisco Press, O’Reilly, Prentice Hall, Que, and Sams.
Master the latest tools and techniques In addition to gaining access to an incredible inventory of technical books, Safari’s extensive collection of video tutorials lets you learn from the leading video training experts.
WAIT, THERE’S MORE! Keep your competitive edge With Rough Cuts, get access to the developing manuscript and be among the first to learn the newest technologies.
Stay current with emerging technologies Short Cuts and Quick Reference Sheets are short, concise, focused content created to get you up-to-speed quickly on new and cutting-edge technologies.
FREE Online Edition
Your purchase of Building OpenSocial Apps includes access to a free online edition for 45 days through the Safari Books Online subscription service. Nearly every AddisonWesley Professional book is available online through Safari Books Online, along with more than 5,000 other technical books and videos from publishers such as Cisco Press, Exam Cram, IBM Press, O’Reilly, Prentice Hall, Que, and Sams.
SAFARI BOOKS ONLINE allows you to search for a specific answer, cut and paste code, download chapters, and stay current with emerging technologies.
Activate your FREE Online Edition at www.informit.com/safarifree STEP 1: Enter the coupon code: SGBUPVH. STEP 2: New Safari users, complete the brief registration form. Safari subscribers, just log in.
If you have difficulty registering on Safari or accessing the online edition, please e-mail
[email protected]