Merge lp:~deryck/launchpad/better-html-handling-description-editing into lp:launchpad

Proposed by Deryck Hodge
Status: Merged
Approved by: Deryck Hodge
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~deryck/launchpad/better-html-handling-description-editing
Merge into: lp:launchpad
Diff against target: 134 lines
3 files modified
lib/canonical/launchpad/javascript/client/client.js (+37/-13)
lib/canonical/widgets/lazrjs.py (+3/-23)
lib/lp/bugs/browser/bug.py (+1/-1)
To merge this branch: bzr merge lp:~deryck/launchpad/better-html-handling-description-editing
Reviewer Review Type Date Requested Status
Leonard Richardson (community) Approve
Review via email: mp+13963@code.launchpad.net

Commit message

Do not use formatter config option to handle html in TextAreaEditor Widget. JS API client can do a patch on individual fields now.

To post a comment you must log in.
Revision history for this message
Deryck Hodge (deryck) wrote :

= Summary =

Tim had some troubles getting an xhtml representation of an object
displaying properly with the ajax multi-line editor used in bug
description editing. A launchpad-dev mailing list discussion around
this uncovered that the API will allow us to patch a field on an object
and get back just the field's representation, rather than patching an
object and getting back a representation of the object.

Bug #460725 records the need to update the widget used in multi-line
editing to not rely on formatter config option hacks to parse the object
xhtml. This means the widget should just patch the field.

== Proposed fix ==

This branch updates TextAreaEditorWidget to remove the formatter option.
 client.js also had to be patched to allow for submitting a single field
object patch request.

== Pre-implementation notes ==

There was discussion on the launchpad-dev mailing list with Francis that
led to private discussions with Leonard about how to correctly do this.
 He's pretty much looked at this diff in stages by this point.

== Implementation details ==

While the API is capable of doing this single field patch request, the
JavaScript client was not enabled for it and had to be updated.
Otherwise, it's a pretty simple branch removing the formatter option and
properly encoding the response.

(The first few lines of the diff are cleanup from lint warnings
unrelated to this change.)

There is a related lazr-js branch that must land when this one does.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/canonical/widgets/lazrjs.py
  lib/canonical/launchpad/javascript/client/client.js
  lib/lp/bugs/browser/bug.py

== JSLint notices ==
jslint: No problem found in
'/home/deryck/canonical/lp-branches/better-html-handling-description-editing/lib/canonical/launchpad/javascript/client/client.js'.

jslint: 1 file to lint.

== Pylint notices ==

lib/canonical/widgets/lazrjs.py
    23: [F0401] Unable to import 'lazr.restful.interfaces' (No module
named restful)

lib/lp/bugs/browser/bug.py
    44: [F0401] Unable to import 'lazr.enum' (No module named enum)
    45: [F0401] Unable to import 'lazr.lifecycle.event' (No module named
lifecycle)
    46: [F0401] Unable to import 'lazr.lifecycle.snapshot' (No module
named lifecycle)
    47: [F0401] Unable to import 'lazr.restful.interfaces' (No module
named restful)

Revision history for this message
Leonard Richardson (leonardr) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/javascript/client/client.js'
2--- lib/canonical/launchpad/javascript/client/client.js 2009-09-17 16:27:19 +0000
3+++ lib/canonical/launchpad/javascript/client/client.js 2009-10-27 17:44:13 +0000
4@@ -43,20 +43,24 @@
5 var keys = [];
6 var key;
7 for (key in collection) {
8- keys.push(key);
9+ if (collection.hasOwnProperty(key)) {
10+ keys.push(key);
11+ }
12 }
13 keys.sort();
14 for (var index in keys) {
15- key = keys[index];
16- var value;
17- try {
18- value = format(collection[key]);
19- } catch (e) {
20- // This is necessary to handle attributes which
21- // will throw a permission denied error.
22- value = e.message;
23+ if (keys.hasOwnProperty(index)) {
24+ key = keys[index];
25+ var value;
26+ try {
27+ value = format(collection[key]);
28+ } catch (e) {
29+ // This is necessary to handle attributes which
30+ // will throw a permission denied error.
31+ value = e.message;
32+ }
33+ items.push(key + '=' + value);
34 }
35- items.push(key + '=' + value);
36 }
37 return items.join(',\n ');
38 };
39@@ -574,6 +578,15 @@
40 resource: {},
41
42 /**
43+ * Is this a patch for only a field,
44+ * not the entire resource object?
45+ *
46+ * @attribute patch_field
47+ * @type Boolean
48+ */
49+ patch_field: false,
50+
51+ /**
52 * The function to use to format the returned result into a form that
53 * can be inserted into the page DOM.
54 *
55@@ -727,9 +740,16 @@
56
57 var client = new LP.client.Launchpad();
58 var formatter = Y.bind(this.get('formatter'), this);
59- var patch_payload = {};
60 var attribute = this.get('patch');
61- patch_payload[attribute] = owner.getInput();
62+
63+ var patch_payload;
64+ var val = owner.getInput();
65+ if (this.get('patch_field')) {
66+ patch_payload = val;
67+ } else {
68+ patch_payload = {};
69+ patch_payload[attribute] = val;
70+ }
71
72 var callbacks = {
73 on: {
74@@ -766,7 +786,11 @@
75 * the DOM.
76 */
77 _defaultFormatter: function(result, attribute) {
78- return result.get(attribute);
79+ if (Y.Lang.isString(result)) {
80+ return result;
81+ } else {
82+ return result.get(attribute);
83+ }
84 }
85 });
86
87
88=== modified file 'lib/canonical/widgets/lazrjs.py'
89--- lib/canonical/widgets/lazrjs.py 2009-09-17 19:09:29 +0000
90+++ lib/canonical/widgets/lazrjs.py 2009-10-27 17:44:13 +0000
91@@ -207,29 +207,9 @@
92 widget.editor.plug({
93 fn: Y.lp.client.plugins.PATCHPlugin, cfg: {
94 patch: '%(attribute)s',
95- resource: '%(context_url)s',
96- accept: 'application/xhtml+xml',
97- formatter: function(result, attribute) {
98- var dl = Y.DOM.create(result)[1]
99- var dl_nodes = dl.childNodes;
100- var i;
101- // <dd> in XHR responses breaks description formatting.
102- for (i=0; i<dl_nodes.length; i++) {
103- var child = dl_nodes[i];
104- // Ignore text nodes when looking for the attribute.
105- // 3 is the text nodeType.
106- if (child.nodeType != 3 &&
107- child.firstChild.textContent == attribute) {
108- var ptags = dl_nodes[i+2].childNodes;
109- var span = Y.Node.create('<span></span>');
110- var n;
111- for (n=0; n<ptags.length; n++) {
112- span.appendChild(ptags[n]);
113- }
114- return span;
115- }
116- }
117- }
118+ resource: '%(context_url)s/%(attribute)s',
119+ patch_field: true,
120+ accept: 'application/xhtml+xml'
121 }});
122 if (!Y.UA.opera) {
123 widget.render();
124
125=== modified file 'lib/lp/bugs/browser/bug.py'
126--- lib/lp/bugs/browser/bug.py 2009-10-23 18:07:36 +0000
127+++ lib/lp/bugs/browser/bug.py 2009-10-27 17:44:13 +0000
128@@ -933,5 +933,5 @@
129 def renderer(value):
130 nomail = formatter(value).obfuscate_email()
131 html = formatter(nomail).text_to_html()
132- return html
133+ return html.encode('utf-8')
134 return renderer