Merge lp:~thumper/launchpad/client-cache-sync into lp:launchpad

Proposed by Tim Penhey
Status: Merged
Approved by: Tim Penhey
Approved revision: no longer in the source branch.
Merged at revision: 12478
Proposed branch: lp:~thumper/launchpad/client-cache-sync
Merge into: lp:launchpad
Prerequisite: lp:~thumper/launchpad/lp-client-yui-module
Diff against target: 703 lines (+302/-102)
14 files modified
lib/lp/app/javascript/client.js (+101/-22)
lib/lp/app/javascript/lp.ui.js (+20/-0)
lib/lp/app/javascript/picker.js (+9/-39)
lib/lp/app/javascript/tests/test_lp_client.html (+1/-2)
lib/lp/app/javascript/tests/test_lp_client.js (+129/-4)
lib/lp/app/templates/base-layout-macros.pt (+11/-8)
lib/lp/app/templates/text-area-editor.pt (+2/-3)
lib/lp/app/templates/text-line-editor.pt (+1/-2)
lib/lp/bugs/javascript/tests/test_bug_subscription_widget.html (+1/-1)
lib/lp/bugs/javascript/tests/test_me_too.html (+1/-1)
lib/lp/bugs/javascript/tests/test_me_too.js (+5/-3)
lib/lp/bugs/javascript/tests/test_subscriber.html (+1/-1)
lib/lp/code/interfaces/sourcepackagerecipe.py (+9/-11)
lib/lp/code/templates/sourcepackagerecipe-index.pt (+11/-5)
To merge this branch: bzr merge lp:~thumper/launchpad/client-cache-sync
Reviewer Review Type Date Requested Status
j.c.sackett (community) Approve
Review via email: mp+51049@code.launchpad.net

Commit message

[r=jcsackett][bug=490826,721064,724004] Keep LP.cache up to date with launchpadlib PATCH changes, and raise update events.

Description of the change

The primary focus of this branch is to keep the Javascript
cache of the context object up to date with API changes done
using the PATCH method. As much as possible this has been
pushed as low into the library code as possible to make sure
it just works.

As we are now updating the cache object, we know when fields
have changed. Now when fields change, we send YUI events.
One change is to implement a standard listener for when the
web_link attribute changes on the context object. If this
changes, it means that the object that we are editing or viewing
right now is now at a different location. We change the window's
location when this happens to take the user to the new page.
This happens now independently of how the web_link got changed,
whether that be a picker to change the owner, or editing a
different part of the URL (like a name).

The standard widgets were updated to support the changes to
the PATCHPlugin. The PATCHPlugin now always requests JSON
with the additional html representations. This change also
made the picker simpler, as it no longer had to work out
itself if the context object was being changed.

The branch also updates the source package recipe so the
base_branch and deb_version_template are exported.

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :

Tim--

This looks really excellent.

You might want to get one other heavier duty js person to take a look at this (and possibly your other branch, but that was mostly mechanical changes), but I think it looks solid.

review: Approve
Revision history for this message
Francis J. Lacoste (flacoste) wrote :

It would be nice to have some test coverage for these new facilities.

Revision history for this message
Francis J. Lacoste (flacoste) wrote :

> lib/lp/code/javascript/sourcepackagerecipe.index.js

Given that this only contains 2 lines of hook-up and is used only on one page, is it worth defining a YUI module for that? I'd suggest inlining the hook-up in the page itself.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed directory 'lib/canonical/launchpad/javascript/client'
2=== renamed file 'lib/canonical/launchpad/javascript/client/client.js' => 'lib/lp/app/javascript/client.js'
3--- lib/canonical/launchpad/javascript/client/client.js 2011-02-24 22:10:32 +0000
4+++ lib/lp/app/javascript/client.js 2011-02-28 01:12:51 +0000
5@@ -1,11 +1,10 @@
6-
7-// An AJAX client that runs against Launchpad's web service.
8-var LP = {
9- cache: {},
10- links: {}
11-};
12-
13-
14+/* Copyright 2009-2011 Canonical Ltd. This software is licensed under the
15+ * GNU Affero General Public License version 3 (see the file LICENSE).
16+ *
17+ * Utility methods and classes to deal with the Launchpad API using Javascript.
18+ *
19+ * @module Y.lp.client
20+ */
21 YUI.add('lp.client', function(Y) {
22
23 var module = Y.namespace('lp.client');
24@@ -179,17 +178,76 @@
25 return data;
26 };
27
28+function update_cached_object(cache_name, cache, entry)
29+{
30+ var fields_changed = new Array();
31+ for (var name in cache) {
32+ var old_value = cache[name];
33+ var new_value = entry.get(name);
34+ if (name != 'lp_html') {
35+ if (old_value != new_value) {
36+ fields_changed.push(name);
37+ cache[name] = new_value;
38+ var field_updated_event_name = 'lp:' + cache_name + ':' + name + ':changed';
39+ var event = {
40+ 'name': name,
41+ 'old_value': old_value,
42+ 'new_value': new_value,
43+ 'entry': entry
44+ };
45+ Y.fire(field_updated_event_name, event);
46+ }
47+ }
48+ else {
49+ // Since we don't care here about the content, we aren't using the
50+ // values here to determine if the field has changed, so we can just
51+ // update the cache.
52+ for (var html_name in old_value) {
53+ old_value[html_name] = new_value[html_name];
54+ }
55+ }
56+ }
57+
58+ if (fields_changed.length > 0) {
59+ var event_name = 'lp:' + cache_name + ':changed';
60+ var event = {
61+ 'fields_changed': fields_changed,
62+ 'entry': entry
63+ };
64+ Y.fire(event_name, event);
65+ }
66+}
67+
68+
69+module.update_cache = function(entry) {
70+ if (!entry) return;
71+ var original_uri = entry.lp_original_uri;
72+ var full_uri = module.get_absolute_uri(original_uri);
73+ for (var name in LP.cache) {
74+ var cached_object = LP.cache[name];
75+ if (cached_object['self_link'] == full_uri) {
76+ Y.log(name + ' cached object has been updated.');
77+ update_cached_object(name, cached_object, entry);
78+ }
79+ }
80+}
81+
82 module.wrap_resource_on_success = function(ignore, response, args) {
83 var client = args[0];
84 var uri = args[1];
85 var old_on_success = args[2];
86+ var update_cache = args[3];
87 var representation, wrapped;
88 if (old_on_success) {
89 var media_type = response.getResponseHeader('Content-Type');
90 if (media_type.substring(0,16) == 'application/json') {
91 representation = Y.JSON.parse(response.responseText);
92 wrapped = client.wrap_resource(uri, representation);
93- return old_on_success(wrapped);
94+ result = old_on_success(wrapped);
95+ if (update_cache) {
96+ module.update_cache(wrapped);
97+ }
98+ return result;
99 } else {
100 return old_on_success(response.responseText);
101 }
102@@ -390,6 +448,23 @@
103 return this.get(key);
104 };
105
106+Entry.prototype.getHTML = function(key) {
107+ var lp_html = this.get('lp_html');
108+ if (lp_html) {
109+ // First look for the key.
110+ var value = lp_html[key];
111+ if (value !== undefined) {
112+ return value;
113+ }
114+ // now look for key_link
115+ value = lp_html[key + '_link'];
116+ if (value !== undefined) {
117+ return value;
118+ }
119+ }
120+ return null;
121+};
122+
123 module.Entry = Entry;
124
125 // The Launchpad client itself.
126@@ -415,10 +490,11 @@
127 }
128
129 var old_on_success = on.success;
130+ var update_cache = false;
131 on.success = module.wrap_resource_on_success;
132 var client = this;
133 var y_config = { on: on,
134- 'arguments': [client, uri, old_on_success],
135+ 'arguments': [client, uri, old_on_success, update_cache],
136 'headers': headers,
137 data: data};
138 Y.io(uri, y_config);
139@@ -463,9 +539,10 @@
140 return module.wrap_resource_on_success(undefined, response, args);
141 };
142 var client = this;
143+ var update_cache = false;
144 var y_config = { method: "POST",
145 on: on,
146- 'arguments': [client, uri, old_on_success],
147+ 'arguments': [client, uri, old_on_success, update_cache],
148 data: data};
149 Y.io(uri, y_config);
150 },
151@@ -476,8 +553,9 @@
152 uri = module.normalize_uri(uri);
153
154 var old_on_success = on.success;
155+ var update_cache = true;
156 on.success = module.wrap_resource_on_success;
157- args = [this, uri, old_on_success];
158+ args = [this, uri, old_on_success, update_cache];
159
160 var extra_headers = {
161 "X-HTTP-Method-Override": "PATCH",
162@@ -674,13 +752,13 @@
163 resource: {},
164
165 /**
166- * Is this a patch for only a field,
167- * not the entire resource object?
168+ * Should the resulting field get the value from the lp_html
169+ * attribute?
170 *
171- * @attribute patch_field
172+ * @attribute use_html
173 * @type Boolean
174 */
175- patch_field: false,
176+ use_html: false,
177
178 /**
179 * The function to use to format the returned result into a form that
180@@ -728,6 +806,7 @@
181 // Save the config object that the user passed in so that we can pass
182 // any extra parameters through to the lp.client constructor.
183 this.extra_config = config || {};
184+ this.extra_config['accept'] = 'application/json;include=lp_html';
185
186 // Save a reference to the original _saveData()
187 //method before wrapping it.
188@@ -773,12 +852,8 @@
189
190 var patch_payload;
191 var val = owner.getInput();
192- if (this.get('patch_field')) {
193- patch_payload = val;
194- } else {
195- patch_payload = {};
196- patch_payload[attribute] = val;
197- }
198+ patch_payload = {};
199+ patch_payload[attribute] = val;
200
201 var callbacks = {
202 on: {
203@@ -818,7 +893,11 @@
204 if (Y.Lang.isString(result)) {
205 return result;
206 } else {
207+ if (this.get('use_html')) {
208+ return result.getHTML(attribute);
209+ } else {
210 return result.get(attribute);
211+ }
212 }
213 }
214 });
215
216=== added file 'lib/lp/app/javascript/lp.ui.js'
217--- lib/lp/app/javascript/lp.ui.js 1970-01-01 00:00:00 +0000
218+++ lib/lp/app/javascript/lp.ui.js 2011-02-28 01:12:51 +0000
219@@ -0,0 +1,20 @@
220+/* Copyright 2011 Canonical Ltd. This software is licensed under the
221+ * GNU Affero General Public License version 3 (see the file LICENSE).
222+ *
223+ * Launchpad helper methods for generic UI stuff.
224+ *
225+ * @module Y.lp.ui
226+ */
227+YUI.add('lp.ui', function(Y) {
228+
229+ var module = Y.namespace('lp.ui');
230+
231+ module.update_field = function(selector, content)
232+ {
233+ var element = Y.one(selector);
234+ element.set('innerHTML', content);
235+ Y.lazr.anim.green_flash({node:element}).run();
236+ }
237+
238+ }, "0.1", {"requires": ["node", "lazr.anim"]}
239+);
240
241=== modified file 'lib/lp/app/javascript/picker.js'
242--- lib/lp/app/javascript/picker.js 2011-02-23 23:46:43 +0000
243+++ lib/lp/app/javascript/picker.js 2011-02-28 01:12:51 +0000
244@@ -40,9 +40,6 @@
245 var null_display_value = 'None';
246 var show_search_box = true;
247 resource_uri = Y.lp.client.normalize_uri(resource_uri)
248- var full_resource_uri = Y.lp.client.get_absolute_uri(resource_uri);
249- var current_context_uri = LP.cache['context']['self_link'];
250- var editing_main_context = (full_resource_uri == current_context_uri);
251
252 if (config !== undefined) {
253 if (config.remove_button_text !== undefined) {
254@@ -102,41 +99,13 @@
255 var save = function (picker_result) {
256 activator.renderProcessing();
257 var success_handler = function (entry) {
258- // XXX mars 2009-12-1
259- // The approach we use here is deprecated. Instead of requesting
260- // the entire entity we should only request the fields we need.
261- // Then most of this code can go away. See bug #490826.
262- var success_message_node = null;
263- var xhtml = Y.Node.create(entry);
264- var current_field = null;
265- var content_uri_has_changed = false;
266- // The entry is an XHTML document with a <dl> node at the root. We
267- // want to process each <dt><dd> tag pair under that root.
268- var page_uri = null;
269- xhtml.all('dl *').each(function(element) {
270- if (element.get('tagName') == 'DT') {
271- current_field = element.get('innerHTML');
272- } else if (element.get('tagName') == 'DD') {
273- if (current_field == attribute_name) {
274- // The field value is found
275- success_message_node = Y.Node.create('<span></span>');
276- rendered_content = element.get('innerHTML');
277- success_message_node.set('innerHTML', rendered_content);
278- } else if (current_field == 'web_link') {
279- page_uri = element.get('innerHTML');
280- } else if (current_field == 'self_link') {
281- picker._resource_uri = element.get('innerHTML');
282- content_uri_has_changed = (
283- full_resource_uri != picker._resource_uri);
284- }
285- }
286- });
287- activator.renderSuccess(success_message_node);
288- show_hide_buttons();
289- if (editing_main_context && content_uri_has_changed
290- && page_uri !== null) {
291- window.location = page_uri;
292- }
293+
294+ var xhtml = entry.getHTML(attribute_name);
295+ var success_message_node = Y.Node.create('<span></span>');
296+ success_message_node.set('innerHTML', xhtml);
297+ activator.renderSuccess(success_message_node);
298+ show_hide_buttons();
299+ return;
300 };
301
302 var patch_payload = {};
303@@ -145,7 +114,7 @@
304
305 var client = new Y.lp.client.Launchpad();
306 client.patch(picker._resource_uri, patch_payload, {
307- accept: 'application/xhtml+xml',
308+ accept: 'application/json;include=lp_html',
309 on: {
310 success: success_handler,
311 failure: failure_handler
312@@ -177,6 +146,7 @@
313 // Use picker._resource_uri, since it might have been changed
314 // from the outside after the widget has already been initialized.
315 client.patch(picker._resource_uri, patch_payload, {
316+ accept: 'application/json;include=lp_html',
317 on: {
318 success: success_handler,
319 failure: failure_handler
320
321=== modified file 'lib/lp/app/javascript/tests/test_lp_client.html'
322--- lib/lp/app/javascript/tests/test_lp_client.html 2011-02-24 01:54:21 +0000
323+++ lib/lp/app/javascript/tests/test_lp_client.html 2011-02-28 01:12:51 +0000
324@@ -12,8 +12,7 @@
325 <link rel="stylesheet" href="../../../../canonical/launchpad/javascript/test.css" />
326
327 <!-- The module under test -->
328- <script type="text/javascript"
329- src="../../../../canonical/launchpad/javascript/client/client.js"></script>
330+ <script type="text/javascript" src="../client.js"></script>
331
332 <!-- The test suite -->
333 <script type="text/javascript" src="test_lp_client.js"></script>
334
335=== modified file 'lib/lp/app/javascript/tests/test_lp_client.js'
336--- lib/lp/app/javascript/tests/test_lp_client.js 2011-02-24 01:54:21 +0000
337+++ lib/lp/app/javascript/tests/test_lp_client.js 2011-02-28 01:12:51 +0000
338@@ -42,10 +42,135 @@
339 Assert.areEqual(get_field_uri("/has/slash/", "field"),
340 "/api/devel/has/slash/field");
341 }
342-
343-
344-
345-}));
346+}));
347+
348+suite.add(new Y.Test.Case({
349+ name: "update cache",
350+
351+ setUp: function() {
352+ window.LP = {
353+ cache: {
354+ context: {
355+ 'first': "Hello",
356+ 'second': true,
357+ 'third': 42,
358+ 'fourth': "Unaltered",
359+ 'self_link': Y.lp.client.get_absolute_uri("a_self_link")
360+ }
361+ }};
362+ },
363+
364+ tearDown: function() {
365+ delete window.LP;
366+ },
367+
368+ test_update_cache: function() {
369+ // Make sure that the cached objects are in fact updated.
370+ var entry_repr = {
371+ 'first': "World",
372+ 'second': false,
373+ 'third': 24,
374+ 'fourth': "Unaltered",
375+ 'self_link': Y.lp.client.get_absolute_uri("a_self_link")
376+ };
377+ var entry = new Y.lp.client.Entry(null, entry_repr, "a_self_link");
378+ Y.lp.client.update_cache(entry);
379+ Assert.areEqual("World", LP.cache.context.first);
380+ Assert.areEqual(false, LP.cache.context.second);
381+ Assert.areEqual(24, LP.cache.context.third);
382+ Assert.areEqual("Unaltered", LP.cache.context.fourth);
383+ },
384+
385+ test_update_cache_raises_events: function() {
386+ // Check that the object changed event is raised.
387+ var raised_event = null;
388+ var handle = Y.on('lp:context:changed', function(e) {
389+ raised_event = e;
390+ });
391+ var entry_repr = {
392+ 'first': "World",
393+ 'second': false,
394+ 'third': 24,
395+ 'fourth': "Unaltered",
396+ 'self_link': Y.lp.client.get_absolute_uri("a_self_link")
397+ };
398+ var entry = new Y.lp.client.Entry(null, entry_repr, "a_self_link");
399+ Y.lp.client.update_cache(entry);
400+ handle.detach();
401+ Y.ArrayAssert.itemsAreEqual(
402+ ['first','second','third'], raised_event.fields_changed);
403+ Assert.areEqual(entry, raised_event.entry);
404+ },
405+
406+ test_update_cache_raises_attribute_events: function() {
407+ // Check that the object attribute changed events are raised.
408+ var first_event = null;
409+ var second_event = null;
410+ var third_event = null;
411+ var fourth_event = null;
412+ var first_handle = Y.on('lp:context:first:changed', function(e) {
413+ first_event = e;
414+ });
415+ var second_handle = Y.on('lp:context:second:changed', function(e) {
416+ second_event = e;
417+ });
418+ var third_handle = Y.on('lp:context:third:changed', function(e) {
419+ third_event = e;
420+ });
421+ var fourth_handle = Y.on('lp:context:fourth:changed', function(e) {
422+ fourth_event = e;
423+ });
424+ var entry_repr = {
425+ 'first': "World",
426+ 'second': false,
427+ 'third': 24,
428+ 'fourth': "Unaltered",
429+ 'self_link': Y.lp.client.get_absolute_uri("a_self_link")
430+ };
431+ var entry = new Y.lp.client.Entry(null, entry_repr, "a_self_link");
432+ Y.lp.client.update_cache(entry);
433+ first_handle.detach();
434+ second_handle.detach();
435+ third_handle.detach();
436+ fourth_handle.detach();
437+
438+ Assert.areEqual('first', first_event.name);
439+ Assert.areEqual('Hello', first_event.old_value);
440+ Assert.areEqual('World', first_event.new_value);
441+ Assert.areEqual(entry, first_event.entry);
442+
443+ Assert.areEqual('second', second_event.name);
444+ Assert.areEqual(true, second_event.old_value);
445+ Assert.areEqual(false, second_event.new_value);
446+ Assert.areEqual(entry, second_event.entry);
447+
448+ Assert.areEqual('third', third_event.name);
449+ Assert.areEqual(42, third_event.old_value);
450+ Assert.areEqual(24, third_event.new_value);
451+ Assert.areEqual(entry, third_event.entry);
452+
453+ Assert.isNull(fourth_event);
454+ },
455+
456+ test_update_cache_different_object: function() {
457+ // Check that the object is not modified if the entry has a different
458+ // link.
459+ var entry_repr = {
460+ 'first': "World",
461+ 'second': false,
462+ 'third': 24,
463+ 'fourth': "Unaltered",
464+ 'self_link': Y.lp.client.get_absolute_uri("different_link")
465+ };
466+ var entry = new Y.lp.client.Entry(null, entry_repr, "different_link");
467+ Y.lp.client.update_cache(entry);
468+ Assert.areEqual("Hello", LP.cache.context.first);
469+ Assert.areEqual(true, LP.cache.context.second);
470+ Assert.areEqual(42, LP.cache.context.third);
471+ Assert.areEqual("Unaltered", LP.cache.context.fourth);
472+ }
473+}));
474+
475
476 // Lock, stock, and two smoking barrels.
477 var handle_complete = function(data) {
478
479=== modified file 'lib/lp/app/templates/base-layout-macros.pt'
480--- lib/lp/app/templates/base-layout-macros.pt 2011-02-24 03:27:21 +0000
481+++ lib/lp/app/templates/base-layout-macros.pt 2011-02-28 01:12:51 +0000
482@@ -62,6 +62,12 @@
483 somewhere in the automated test system that will prevent Windmill from
484 executing.
485 </tal:comment>
486+ <script type="text/javascript">
487+ var LP = {
488+ cache: {},
489+ links: {}
490+ };
491+ </script>
492 <script type="text/javascript"
493 tal:attributes="src string:${icingroot}/MochiKit.js"></script>
494
495@@ -95,7 +101,6 @@
496 <tal:comment replace="nothing">
497 Load and initialize the common script used by all pages.
498 </tal:comment>
499-
500 <tal:needs_json tal:condition="request/needs_json">
501 <script type="text/javascript"
502 tal:attributes="src string:${icingroot_contrib}/json2.js"></script>
503@@ -106,25 +111,23 @@
504 <metal:load-lavascript use-macro="context/@@+base-layout-macros/load-javascript" />
505
506 <script id="base-layout-load-scripts" type="text/javascript">
507- LPS.use('node', 'lp', function(Y) {
508+ LPS.use('node', 'lp', 'lp.app.links', function(Y) {
509 Y.on('load', function(e) {
510 sortables_init();
511 initInlineHelp();
512 Y.lp.activate_collapsibles();
513 activateFoldables();
514 activateConstrainBugExpiration();
515+ Y.lp.app.links.check_valid_lp_links();
516 }, window);
517
518 // Hook up the function that dismisses the help window if we click
519 // anywhere outside of it.
520 Y.on('click', handleClickOnPage, window);
521- });
522
523- LPS.use('lp.app.links',
524- function(Y) {
525- Y.on('load', function(e) {
526- Y.lp.app.links.check_valid_lp_links();
527- }, window);
528+ Y.on('lp:context:web_link:changed', function(e) {
529+ window.location = e.new_value;
530+ });
531 });
532 </script>
533 </metal:page-javascript>
534
535=== modified file 'lib/lp/app/templates/text-area-editor.pt'
536--- lib/lp/app/templates/text-area-editor.pt 2011-01-27 20:25:05 +0000
537+++ lib/lp/app/templates/text-area-editor.pt 2011-02-28 01:12:51 +0000
538@@ -23,9 +23,8 @@
539 widget.editor.plug({
540 fn: Y.lp.client.plugins.PATCHPlugin, cfg: {
541 patch: ${view/json_attribute},
542- resource: ${view/json_attribute_uri},
543- patch_field: true,
544- accept: 'application/xhtml+xml'
545+ resource: ${view/json_resource_uri},
546+ use_html: true
547 }});
548 if (!Y.UA.opera) {
549 widget.render();
550
551=== modified file 'lib/lp/app/templates/text-line-editor.pt'
552--- lib/lp/app/templates/text-line-editor.pt 2011-01-27 20:25:05 +0000
553+++ lib/lp/app/templates/text-line-editor.pt 2011-02-28 01:12:51 +0000
554@@ -17,8 +17,7 @@
555 widget.editor.plug({
556 fn: Y.lp.client.plugins.PATCHPlugin, cfg: {
557 patch: ${view/json_attribute},
558- resource: ${view/json_attribute_uri},
559- patch_field: true
560+ resource: ${view/json_resource_uri}
561 }});
562 widget.render();
563 });
564
565=== modified file 'lib/lp/bugs/javascript/tests/test_bug_subscription_widget.html'
566--- lib/lp/bugs/javascript/tests/test_bug_subscription_widget.html 2010-12-13 15:30:16 +0000
567+++ lib/lp/bugs/javascript/tests/test_bug_subscription_widget.html 2011-02-28 01:12:51 +0000
568@@ -28,7 +28,7 @@
569 src="../../../../canonical/launchpad/icing/lazr/build/choiceedit/choiceedit.js">
570 </script>
571 <script type="text/javascript"
572- src="../../../../canonical/launchpad/javascript/client/client.js"></script>
573+ src="../../../app/javascript/client.js"></script>
574
575 <!-- The module under test -->
576 <script type="text/javascript" src="../bug_subscription_widget.js"></script>
577
578=== modified file 'lib/lp/bugs/javascript/tests/test_me_too.html'
579--- lib/lp/bugs/javascript/tests/test_me_too.html 2011-02-07 15:21:02 +0000
580+++ lib/lp/bugs/javascript/tests/test_me_too.html 2011-02-28 01:12:51 +0000
581@@ -28,7 +28,7 @@
582 src="../../../../canonical/launchpad/icing/lazr/build/choiceedit/choiceedit.js">
583 </script>
584 <script type="text/javascript"
585- src="../../../../canonical/launchpad/javascript/client/client.js"></script>
586+ src="../../../app/javascript/client.js"></script>
587 <script type="text/javascript" src="../subscriber.js"></script>
588
589 <!-- The module under test -->
590
591=== modified file 'lib/lp/bugs/javascript/tests/test_me_too.js'
592--- lib/lp/bugs/javascript/tests/test_me_too.js 2011-02-24 00:01:31 +0000
593+++ lib/lp/bugs/javascript/tests/test_me_too.js 2011-02-28 01:12:51 +0000
594@@ -34,9 +34,11 @@
595 function(url, func, config) {
596 config.on.success();
597 };
598- LP.cache.bug = {
599- self_link: "http://bugs.example.com/bugs/1234"
600- };
601+ LP = {
602+ 'cache': {
603+ 'bug': {
604+ self_link: "http://bugs.example.com/bugs/1234"
605+ }}};
606 // add the in-page HTML
607 var inpage = Y.Node.create([
608 '<span id="affectsmetoo">',
609
610=== modified file 'lib/lp/bugs/javascript/tests/test_subscriber.html'
611--- lib/lp/bugs/javascript/tests/test_subscriber.html 2011-02-25 03:54:13 +0000
612+++ lib/lp/bugs/javascript/tests/test_subscriber.html 2011-02-28 01:12:51 +0000
613@@ -17,7 +17,7 @@
614 href="../../../../canonical/launchpad/javascript/test.css" />
615
616 <script type="text/javascript"
617- src="../../../../canonical/launchpad/javascript/client/client.js"></script>
618+ src="../../../app/javascript/client.js"></script>
619
620 <!-- The module under test -->
621 <script type="text/javascript"
622
623=== modified file 'lib/lp/code/interfaces/sourcepackagerecipe.py'
624--- lib/lp/code/interfaces/sourcepackagerecipe.py 2011-02-23 23:25:01 +0000
625+++ lib/lp/code/interfaces/sourcepackagerecipe.py 2011-02-28 01:12:51 +0000
626@@ -46,7 +46,6 @@
627 Choice,
628 Datetime,
629 Int,
630- Object,
631 Text,
632 TextLine,
633 )
634@@ -77,14 +76,16 @@
635 class ISourcePackageRecipeData(Interface):
636 """A recipe as database data, not text."""
637
638- base_branch = Object(
639- schema=IBranch, title=_("Base branch"), description=_(
640- "The base branch to use when building the recipe."))
641+ base_branch = exported(
642+ Reference(
643+ IBranch, title=_("The base branch used by this recipe."),
644+ required=True, readonly=True))
645
646- deb_version_template = TextLine(
647- title=_('deb-version template'),
648- description = _(
649- 'The template that will be used to generate a deb version.'),)
650+ deb_version_template = exported(
651+ TextLine(
652+ title=_('deb-version template'), readonly=True,
653+ description = _(
654+ 'The template that will be used to generate a deb version.')))
655
656 def getReferencedBranches():
657 """An iterator of the branches referenced by this recipe."""
658@@ -238,9 +239,6 @@
659 debianized source tree.
660 """
661 export_as_webservice_entry()
662- base_branch = Reference(
663- IBranch, title=_("The base branch used by this recipe."),
664- required=True, readonly=True)
665
666
667 class ISourcePackageRecipeSource(Interface):
668
669=== modified file 'lib/lp/code/templates/sourcepackagerecipe-index.pt'
670--- lib/lp/code/templates/sourcepackagerecipe-index.pt 2011-02-25 07:20:24 +0000
671+++ lib/lp/code/templates/sourcepackagerecipe-index.pt 2011-02-28 01:12:51 +0000
672@@ -139,12 +139,19 @@
673 </div>
674 <div class='portlet'>
675 <h2>Recipe contents</h2>
676- <tal:widget replace="structure view/recipe_text_widget"/>
677+ <tal:widget replace="structure view/recipe_text_widget"/>
678 </div>
679
680- <tal:script>
681- <script id='requestbuild-script' type="text/javascript" tal:content="string:
682- LPS.use('io-base', 'lp.code.requestbuild_overlay', function(Y) {
683+ <script type="text/javascript">
684+ LPS.use('io-base', 'lp.ui', 'lp.code.requestbuild_overlay', function(Y) {
685+
686+ Y.on('lp:context:deb_version_template:changed', function(e) {
687+ Y.lp.ui.update_field('#debian-version dd', e.new_value);
688+ });
689+ Y.on('lp:context:base_branch_link:changed', function(e) {
690+ Y.lp.ui.update_field('#base-branch dd', e.entry.getHTML(e.name));
691+ });
692+
693 if(Y.UA.ie) {
694 return;
695 }
696@@ -163,7 +170,6 @@
697 });"
698 >
699 </script>
700- </tal:script>
701 </div>
702 </body>
703 </html>