~webapps/webapps-applications/precise

« back to all changes in this revision

Viewing changes to doc/walkthrough.txt

  • Committer: Ken VanDine
  • Date: 2012-07-17 16:26:24 UTC
  • mfrom: (104.1.2 webapps-applications)
  • Revision ID: ken.vandine@canonical.com-20120717162624-g1u7y5ja9awb1hh3
Tags: 1.7.0-0precise1
releasing version 1.7.0-0precise1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
Who is this for
 
2
---------------
 
3
So you've got a website, and want to integrate your site with the Ubuntu using the webapps API. This is great. You should review the API docs, https://chinstrap.canonical.com/~racarr/unity-web-api-docs/unity-web-api-reference.html. This guide is for people who want to write a userscript to integrate your page without putting the code right into a site. I'll be using firefox as my browser for this tutorial. Feel free to use chromium but you might have to do some translation of the bits where I'm doing debuggging. This is meant to be an introduction to the webapps API, not a full api reference.
 
4
 
 
5
 
 
6
What integration points are you going to use?
 
7
---------------------------------------------
 
8
Unity webapps offer a solid list of integration points for your web app.
 
9
 
 
10
  * Launcher icon
 
11
  * Switcher icon
 
12
  * Launcher quicklist actions
 
13
  * Launcher icon count emblem
 
14
  * Launcher icon progress bar
 
15
  * HUD Actions
 
16
  * Messaging menu
 
17
  * Sound Menu
 
18
 
 
19
 
 
20
Writing our userscript
 
21
-----------------------
 
22
For this tutorial I'm going to write an integration script for tumblr.com. Both the dashboard and tumblr blogs. Tumblr is pretty cool example as it's a simple site that offers lots of chances for integration. Just off the top of my head I can see tumblr using:
 
23
  * HUD Actions for making a new post, liking, reblogging, and following
 
24
  * Launcher icon count emblems for unread new posts
 
25
  * Quicklist action for returning to the Dashboard
 
26
  * Messaging menu integration for new questions and fanmail
 
27
  * Soundmenu integration for sound posts and videos.
 
28
A pretty rockin' list if you ask me.
 
29
 
 
30
 
 
31
Step zero. Getting the code and the bits you need to write your script.
 
32
----------------------------------------------------------------------
 
33
This isn't a real step, this is prerequisite and numbering from 0 is always a fun programmer joke. You laughed right?
 
34
 
 
35
A great way of exploring this API is in the developer console. Ctrl+Shift+K in Firefox will open it, and allow you to test these code snippets without having to reinstall the script and restart your browser.
 
36
 
 
37
 
 
38
Step one half. Getting an Icon ready.
 
39
-------------------------------------
 
40
You'll need a 128px, a 64px and a 52px icon. Run the build-icon-list script to add your icons to the iconlist.mk file so that they'll be installed. This script only finds svg icons. If you've got .png files (svg preferred!) Add them to the list manually. You can also use a remote url if there's a suitable icon on the site you're trying to integrate.
 
41
 
 
42
 
 
43
Step one. Integrate. Get a Launcher Icon and Switcher Icon going.
 
44
------------------------------------------------------------------------
 
45
// ==UserScript==
 
46
// @name          tumblr-unity-integration
 
47
// @include       https://tumblr.com/dashboard
 
48
// @version       @VERSION@
 
49
// @author        WebApps Team
 
50
// @require       utils.js
 
51
// ==/UserScript==
 
52
 
 
53
window.Unity = external.getUnityObject(1.0);
 
54
 
 
55
Unity.init({ name: "Tumblr",
 
56
             iconUrl: "icon://Tumblr",
 
57
             onInit: null });
 
58
 
 
59
That's it. The // bits are boilerplate for the manifest file. The important bit that does the integration are getting the unity object into your DOM. window.Unity = external.getUnityObject and Unity.init. Here you establish the name of your webapp (the name parameter), the icon (the iconUrl parameter), and what to do once the page is loaded (the onInit parameter).
 
60
 
 
61
Want to test this? Open up tumblr in firefox, and then open the developer console and enter the bits from above. You can leave out the parts that are preceeded by //.
 
62
 
 
63
BAM. Should now have an icon in your launcher and switcher.
 
64
In our scripts it's commonplace to add some code like this to filter out pages that don't have the DOM elements we need.
 
65
 
 
66
function isCorrectPage() {
 
67
    var i, ids = ['content', 'tabs_outter_container'];;
 
68
 
 
69
    for (i = 0; i < ids.length; i++) {
 
70
        if (!document.getElementById(ids[i])) {
 
71
            return false;
 
72
        }
 
73
    }
 
74
 
 
75
    return true;
 
76
}
 
77
 
 
78
if (isCorrectPage()) {
 
79
        Unity.init({ name: "Tumblr",
 
80
                 iconUrl: "icon://Tumblr",
 
81
                 onInit: null });
 
82
}
 
83
 
 
84
NICE. Now let's start getting integrating tumblr deeper into our desktop.
 
85
First thing we're going to do is change our onInit to a callback that will setup our integration.
 
86
 
 
87
function setupTumblr()
 
88
{
 
89
}
 
90
 
 
91
if (isCorrectPage()) {
 
92
        Unity.init({ name: "Tumblr",
 
93
                 iconUrl: "icon://Tumblr",
 
94
                 onInit: wrapCallback(setupTumblr) });
 
95
}
 
96
 
 
97
A little note: there are some convenience functions in utils.js.in that we use in these scripts. wrapCallback is one of them. You'll see others used. Refer to utils.js.in for more information.
 
98
 
 
99
 
 
100
Posting via HUD
 
101
---------------
 
102
Tumblr lets us post Text, Photos, Quotes, Videos, Links, Chat, and Audio.
 
103
These are all really easy, they are just links.
 
104
 
 
105
function setupTumblr()
 
106
{
 
107
        Unity.addAction('/Post/Text', makeRedirector('http://tumblr.com/new/text'));
 
108
        Unity.addAction('/Post/Link', makeRedirector('http://tumblr.com/new/link'));
 
109
        Unity.addAction('/Post/Chat', makeRedirector('http://tumblr.com/new/chat'));
 
110
        Unity.addAction('/Post/Photo', makeRedirector('http://tumblr.com/new/photo'));
 
111
        Unity.addAction('/Post/Quote', makeRedirector('http://tumblr.com/new/quote'));
 
112
        Unity.addAction('/Post/Audio', makeRedirector('http://tumblr.com/new/audio'));
 
113
        Unity.addAction('/Post/Video', makeRedirector('http://tumblr.com/new/video'));
 
114
}
 
115
 
 
116
Voila! Now we've got HUD integration for posting any type. Simply summon the HUD and type 'post photo' and you'll be taken to a new photo post page. Rockin'.
 
117
 
 
118
 
 
119
 
 
120
Getting a count on the launcher icon
 
121
------------------------------------
 
122
Very simple. We just need to parse the page for the count, and then set the count. The API for this is very simple.
 
123
 
 
124
function setupTumblr()
 
125
{
 
126
        function setLauncherCount() {
 
127
    // we want the total count for new posts and messages on the launcher, so we sum the two.
 
128
    var i, total =0 , elements = document.getElementsByClassName("tab_notice_value");
 
129
    for (i = 0; i < elements.length; i++) {
 
130
      var count = elements[i].innerHTML;
 
131
      if (count > 0) {
 
132
        total += parseInt(count);
 
133
      }
 
134
    }
 
135
    if (total > 0) {
 
136
      Unity.Launcher.setLauncherCount(total);
 
137
    } else {
 
138
      Unity.Launcher.clearCount();
 
139
    }
 
140
  }
 
141
 
 
142
        ...
 
143
        ...
 
144
 
 
145
        setInterval(wrapCallback(checkLauncherCount), 5000);
 
146
}
 
147
 
 
148
Note that we do this on a timer. every 5000 ms (5 seconds) we check the count, and reset it when it's empty.
 
149
 
 
150
 
 
151
Putting questions and fanmail into the messaging menu
 
152
------------------------------------------------------
 
153
Tumblr alerts you when you've got a new question, similar to the unread post count.
 
154
 
 
155
function checkNewMessageCount() {
 
156
    var ib = document.getElementById("inbox_button");
 
157
    var count = document.evaluate('a//span', ib, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
 
158
    if (count == null) {
 
159
      count = 0;
 
160
    } else {
 
161
      count = parseInt(count.innerHTML);
 
162
    }
 
163
    Unity.MessagingIndicator.showIndicator(_("Inbox"), 
 
164
      { count: count,
 
165
        onIndicatorActivated: makeRedirector("http://tumblr.com/inbox");
 
166
      }
 
167
    );
 
168
  }
 
169
 
 
170
This will make an indicator under the tumblr subheading with a count for new messages that when clicked, will take our user to the inbox page.
 
171
 
 
172
 
 
173
Integrating with tumblr blogs
 
174
------------------------------
 
175
We're going to do a little bit of refactoring. Tumblr has two components, the dashboard and blogs. Our integration this far has been with the dashboard, but there are some nice things we can do on blogs.
 
176
So let's start by differentiating the two. Tumblr blogs have tumblr controls on the top right hand corner.
 
177
 
 
178
function isDashboard() {
 
179
    var i, ids = ['content', 'tabs_outter_container'];;
 
180
 
 
181
    for (i = 0; i < ids.length; i++) {
 
182
        if (!document.getElementById(ids[i])) {
 
183
            return false;
 
184
        }
 
185
    }
 
186
 
 
187
    return true;
 
188
}
 
189
 
 
190
function isTumblrBlog() {
 
191
  return document.getElementById("tumblr_controls");
 
192
}
 
193
 
 
194
if (isDashboard() || isTumblrBlog()) {
 
195
  Unity.init({ name: "Tumblr",
 
196
               iconUrl: "icon://Tumblr",
 
197
               onInit: wrapCallback(setupTumblr),
 
198
               domain: "tumblr.com" });
 
199
}
 
200
 
 
201
Next we'll need to rework our setupTumblr function to take the two cases into account. Let's move what used to be setupTumblr into a new function, integrateDashboard and rework setupTumblr to look like this,
 
202
 
 
203
function setupTumblr()
 
204
{
 
205
  if (isDashboard()) {
 
206
    integrateDashboard();
 
207
  } else if (isTumblrBlog()) {
 
208
    integrateTumblrblog();
 
209
  }
 
210
}
 
211
 
 
212
And what's left is to integrate the blogs.
 
213
 
 
214
 
 
215
Adding quicklist actions to the launcher icon
 
216
----------------------------------------------
 
217
 
 
218
function integrateTumblrblog()
 
219
{
 
220
  Unity.Launcher.addAction(_("Dashboard"), makeRedirector("http://tumblr.com/dashboard"));
 
221
}
 
222
 
 
223
 
 
224
Testing your script
 
225
--------------------
 
226
As always proper automated tested is imperative! Most of the test is setup boiler plate. Check the source !!!LINK!!! to see the full test.
 
227
 
 
228
Tumblr requires authorization so we need some code to log in. The username and password get stored in a password file. in the scripts/ folder, where the test runner is. You'll see passwords.example. Copy it to a file names passwords, then modify it with valid usernames and passwords for the scripts you want to test, like so,
 
229
 
 
230
{
 
231
        "Tumblr.user.js.in": {
 
232
                "login": "cooltumblrdude@coolemail.xxx",
 
233
                "password": "webappsrcool4u"
 
234
        }
 
235
}
 
236
 
 
237
we then need to modify our test script to insert the values into the login form
 
238
 
 
239
    onLoad: function () {
 
240
        function authorize(login, pass) {
 
241
            var name = document.getElementById("signup_email");
 
242
            var password = document.getElementById("signup_password");
 
243
            name.value = login;
 
244
            password.value = pass;
 
245
            password.form.submit();
 
246
        }
 
247
 
 
248
...
 
249
...
 
250
 
 
251
Next we validate our call log. This is how we test. TODO: How to get the call log for matching up log indices.
 
252
 
 
253
    validateCallLog: function (log) {
 
254
        assertEquals(log[0].func, "Unity.init");
 
255
        assertEquals(log[0].args[0].name, "Tumblr");
 
256
        assertEquals(log[3].func, "Unity.MessagingIndicator.showIndicator");
 
257
 
 
258
        var i, actionsCount = 0;
 
259
        for (i = 0; i < log.length; i++) {
 
260
            if (log[i].func === 'Unity.addAction') {
 
261
                actionsCount++;
 
262
            }
 
263
        }
 
264
        assertEquals(actionsCount, 7);
 
265
    },
 
266
 
 
267
    scriptName: 'Tumblr.user.js.in'
 
268
};