Merge lp:~rharding/launchpad/new_banner_stage1 into lp:launchpad

Proposed by Richard Harding
Status: Work in progress
Proposed branch: lp:~rharding/launchpad/new_banner_stage1
Merge into: lp:launchpad
Diff against target: 4858 lines (+495/-2431)
43 files modified
buildout-templates/bin/combine-css.in (+1/-0)
lib/canonical/launchpad/icing/css/components/beta_banner.css (+0/-48)
lib/lp/app/browser/launchpad.py (+29/-23)
lib/lp/app/browser/tales.py (+4/-3)
lib/lp/app/browser/tests/test_launchpad.py (+13/-0)
lib/lp/app/javascript/banners/banner.js (+0/-156)
lib/lp/app/javascript/banners/beta-notification.js (+0/-104)
lib/lp/app/javascript/banners/privacy.js (+0/-119)
lib/lp/app/javascript/banners/tests/test_banner.html (+0/-46)
lib/lp/app/javascript/banners/tests/test_banner.js (+0/-110)
lib/lp/app/javascript/banners/tests/test_beta_notification.html (+0/-51)
lib/lp/app/javascript/banners/tests/test_beta_notification.js (+0/-140)
lib/lp/app/javascript/banners/tests/test_privacy.html (+0/-47)
lib/lp/app/javascript/banners/tests/test_privacy.js (+0/-117)
lib/lp/app/javascript/information_type.js (+47/-28)
lib/lp/app/javascript/tests/test_information_type.js (+0/-61)
lib/lp/app/javascript/ui/assets/skins/sam/banner.css (+0/-109)
lib/lp/app/javascript/ui/banner.js (+0/-315)
lib/lp/app/javascript/ui/tests/test_banner.html (+0/-49)
lib/lp/app/javascript/ui/tests/test_banner.js (+0/-287)
lib/lp/app/javascript/views/global.js (+0/-142)
lib/lp/app/javascript/views/tests/test_global.html (+0/-62)
lib/lp/app/javascript/views/tests/test_global.js (+0/-164)
lib/lp/app/templates/base-layout-macros.pt (+5/-7)
lib/lp/app/templates/base-layout.pt (+8/-4)
lib/lp/archivepublisher/scripts/generate_contents_files.py (+13/-3)
lib/lp/archivepublisher/scripts/generate_extra_overrides.py (+1/-1)
lib/lp/archivepublisher/tests/test_generate_contents_files.py (+35/-7)
lib/lp/archivepublisher/tests/test_generate_extra_overrides.py (+0/-16)
lib/lp/blueprints/javascript/addspec.js (+15/-2)
lib/lp/bugs/browser/tests/test_bug_views.py (+23/-0)
lib/lp/bugs/javascript/bugtask_index.js (+0/-1)
lib/lp/bugs/javascript/filebug.js (+66/-45)
lib/lp/bugs/javascript/tests/test_filebug.js (+118/-61)
lib/lp/code/javascript/branch.information_type_choice.js (+8/-5)
lib/lp/code/javascript/tests/test_information_type_choice.js (+14/-41)
lib/lp/registry/browser/product.py (+1/-0)
lib/lp/registry/configure.zcml (+31/-1)
lib/lp/registry/interfaces/product.py (+40/-41)
lib/lp/registry/javascript/product_views.js (+6/-0)
lib/lp/registry/model/product.py (+2/-1)
lib/lp/registry/tests/test_product.py (+14/-13)
lib/lp/services/features/flags.py (+1/-1)
To merge this branch: bzr merge lp:~rharding/launchpad/new_banner_stage1
Reviewer Review Type Date Requested Status
Launchpad code reviewers Pending
Review via email: mp+132957@code.launchpad.net

Description of the change

TBD

To post a comment you must log in.
16203. By Richard Harding

Lots of updates in the style/setup for multiple beta features

16204. By Richard Harding

Garden

16205. By Richard Harding

Work on updating the banner to work via css vs manual style

16206. By Richard Harding

Add docs strings yay

16207. By Richard Harding

Bwuhahahaha two banners on one page

16208. By Richard Harding

Clean debug

16209. By Richard Harding

Fix the css on the private banner

16210. By Richard Harding

Update to override the show/hide method and provide a brief pause to get the css animation to kick in

16211. By Richard Harding

Get rid of the timeout and use load to show the banners

16212. By Richard Harding

Add more tests to the banner code

16213. By Richard Harding

Add docs and XXX for body class crap

16214. By Richard Harding

Add XXX to tales.py that it affects

16215. By Richard Harding

Global tests added

16216. By Richard Harding

Broken the dippy things

16217. By Richard Harding

Fix animation and full width issue

16218. By Richard Harding

Update with trunk and resolve conflicts

16219. By Richard Harding

Make sure the edit fires the information type change event

16220. By Richard Harding

Shorten the animation time since you get it on every page load

16221. By Richard Harding

get the banners working on the blueprints UI

16222. By Richard Harding

fix the filebug use case and handle security as private

16223. By Richard Harding

Add the content watch event to update the banner when the content is changed

16224. By Richard Harding

Fix information type tests

16225. By Richard Harding

Fix the filebug tests

16226. By Richard Harding

Update tests for the information type choice

16227. By Richard Harding

Update from trunk

16228. By Richard Harding

Add some docs

16229. By Richard Harding

Remove the old banners code

16230. By Richard Harding

Remove the old banner css

16231. By Richard Harding

Update upstream

16232. By Richard Harding

Lint

Unmerged revisions

16232. By Richard Harding

Lint

16231. By Richard Harding

Update upstream

16230. By Richard Harding

Remove the old banner css

16229. By Richard Harding

Remove the old banners code

16228. By Richard Harding

Add some docs

16227. By Richard Harding

Update from trunk

16226. By Richard Harding

Update tests for the information type choice

16225. By Richard Harding

Fix the filebug tests

16224. By Richard Harding

Fix information type tests

16223. By Richard Harding

Add the content watch event to update the banner when the content is changed

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'buildout-templates/bin/combine-css.in'
2--- buildout-templates/bin/combine-css.in 2012-08-23 19:56:15 +0000
3+++ buildout-templates/bin/combine-css.in 2012-11-12 13:21:28 +0000
4@@ -27,6 +27,7 @@
5 #'lazr/build/yui/cssgrids/grids.css',
6 'cssgrids/grids.css',
7 'build/ui/assets/skins/sam/lazr.css',
8+ 'build/ui/assets/skins/sam/banner.css',
9 'build/inlineedit/assets/skins/sam/editor.css',
10 'build/autocomplete/assets/skins/sam/autocomplete.css',
11 'build/overlay/assets/skins/sam/pretty-overlay.css',
12
13=== removed file 'lib/canonical/launchpad/icing/css/components/beta_banner.css'
14--- lib/canonical/launchpad/icing/css/components/beta_banner.css 2012-05-16 20:32:47 +0000
15+++ lib/canonical/launchpad/icing/css/components/beta_banner.css 1970-01-01 00:00:00 +0000
16@@ -1,48 +0,0 @@
17-/* ===========
18- Beta banner
19-*/
20-.yui3-betabanner-content .global-notification {
21- position: fixed;
22- z-index: 9;
23- top: 0;
24- left: 0;
25- right: 0;
26- padding: 7px 20px;
27- /* Define colour for browsers that don't support transparency */
28- background-color: #606060;
29- /* Set transparent background for browsers that support it */
30- background-color: rgba(64, 64, 64, 0.9);
31- box-shadow: 0 0 5px #333;
32- color: #fff;
33- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5);
34- font-size: 14px;
35- line-height: 21px;
36- text-align: left;
37- }
38-.beta-banner .info-link {
39- color: #4884ef;
40-}
41-.beta-warning {
42- padding: 3px 6px 4px 6px;
43- margin-right: 12px;
44- background-color: #be0000;
45- font-weight: bold;
46- font-size: 12px;
47- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
48- border-radius: 5px;
49- border-top: 1px solid #e20000;
50- background-color: #c10000;
51- background: linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
52- background: -o-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
53- background: -moz-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
54- background: -webkit-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
55- background: -ms-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
56- }
57-.beta-feature {
58- font-weight: bold;
59- }
60-.yui3-betabanner-content .global-notification-close,
61-.yui3-betabanner-content .global-notification-close:active,
62-.yui3-betabanner-content .global-notification-close:visited {
63- color: #fff;
64- }
65
66=== modified file 'lib/lp/app/browser/launchpad.py'
67--- lib/lp/app/browser/launchpad.py 2012-11-11 23:39:34 +0000
68+++ lib/lp/app/browser/launchpad.py 2012-11-12 13:21:28 +0000
69@@ -766,29 +766,35 @@
70 pillar = getUtility(IPillarNameSet).getByName(
71 name, ignore_inactive=False)
72
73- if (pillar is not None and IProduct.providedBy(pillar) and
74- not pillar.active):
75- # Emergency brake for public but inactive products:
76- # These products should not be shown to ordinary users.
77- # The root problem is that many views iterate over products,
78- # inactive products included, and access attributes like
79- # name, displayname or call canonical_url(product) --
80- # and finally throw the data away, if the product is
81- # inactive. So we cannot make these attributes inaccessible
82- # for inactive public products. On the other hand, we
83- # require the permission launchpad.View to protect private
84- # products.
85- # This means that we cannot simply check if the current
86- # user has the permission launchpad.View for an inactive
87- # product.
88- user = getUtility(ILaunchBag).user
89- if user is None:
90- return None
91- user = IPersonRoles(user)
92- if (not user.in_commercial_admin and not user.in_admin and
93- not user.in_registry_experts):
94- return None
95- if pillar is not None and check_permission('launchpad.View', pillar):
96+ if pillar is None:
97+ return None
98+
99+ if IProduct.providedBy(pillar):
100+ if not pillar.active:
101+ # Emergency brake for public but inactive products:
102+ # These products should not be shown to ordinary users.
103+ # The root problem is that many views iterate over products,
104+ # inactive products included, and access attributes like
105+ # name, displayname or call canonical_url(product) --
106+ # and finally throw the data away, if the product is
107+ # inactive. So we cannot make these attributes inaccessible
108+ # for inactive public products. On the other hand, we
109+ # require the permission launchpad.View to protect private
110+ # products.
111+ # This means that we cannot simply check if the current
112+ # user has the permission launchpad.View for an inactive
113+ # product.
114+ user = getUtility(ILaunchBag).user
115+ if user is None:
116+ return None
117+ user = IPersonRoles(user)
118+ if (not user.in_commercial_admin and not user.in_admin and
119+ not user.in_registry_experts):
120+ return None
121+ permitted = check_permission('launchpad.LimitedView', pillar)
122+ else:
123+ permitted = check_permission('launchpad.View', pillar)
124+ if permitted:
125 if pillar.name != name:
126 # This pillar was accessed through one of its aliases, so we
127 # must redirect to its canonical URL.
128
129=== modified file 'lib/lp/app/browser/tales.py'
130--- lib/lp/app/browser/tales.py 2012-09-28 06:15:58 +0000
131+++ lib/lp/app/browser/tales.py 2012-11-12 13:21:28 +0000
132@@ -663,15 +663,16 @@
133 def global_css(self):
134 css_classes = set([])
135 view = self._context
136+
137+ # XXX: Bug #1076074
138 private = getattr(view, 'private', False)
139 if private:
140 css_classes.add('private')
141- css_classes.add('global-notification-visible')
142 else:
143 css_classes.add('public')
144 beta = getattr(view, 'beta_features', [])
145- if beta != []:
146- css_classes.add('global-notification-visible')
147+ if beta:
148+ css_classes.add('beta')
149 return ' '.join(list(css_classes))
150
151 def _getSaneBreadcrumbDetail(self, breadcrumb):
152
153=== modified file 'lib/lp/app/browser/tests/test_launchpad.py'
154--- lib/lp/app/browser/tests/test_launchpad.py 2012-11-11 23:39:34 +0000
155+++ lib/lp/app/browser/tests/test_launchpad.py 2012-11-12 13:21:28 +0000
156@@ -554,6 +554,19 @@
157 self.assertRaises(
158 NotFound, self.traverse_to_inactive_proprietary_product)
159
160+ def test_access_for_persons_with_artifact_grant(self):
161+ # Persons with an artifact grant related to a private product
162+ # can traverse the product.
163+ user = self.factory.makePerson()
164+ with person_logged_in(self.proprietary_product_owner):
165+ bug = self.factory.makeBug(
166+ target=self.active_proprietary_product,
167+ information_type=InformationType.PROPRIETARY)
168+ getUtility(IService, 'sharing').ensureAccessGrants(
169+ [user], self.proprietary_product_owner, bugs=[bug])
170+ with person_logged_in(user):
171+ self.traverse_to_active_proprietary_product()
172+
173 def check_admin_access(self):
174 self.traverse_to_active_public_product()
175 self.traverse_to_inactive_public_product()
176
177=== removed directory 'lib/lp/app/javascript/banners'
178=== removed file 'lib/lp/app/javascript/banners/banner.js'
179--- lib/lp/app/javascript/banners/banner.js 2012-10-04 14:17:23 +0000
180+++ lib/lp/app/javascript/banners/banner.js 1970-01-01 00:00:00 +0000
181@@ -1,156 +0,0 @@
182-/*
183- * Copyright 2012 Canonical Ltd. This software is licensed under the
184- * GNU Affero General Public License version 3 (see the file LICENSE).
185- *
186- * Notification banner widget
187- *
188- * @module lp.app.banner
189- */
190-
191-YUI.add('lp.app.banner', function (Y) {
192-var ns = Y.namespace('lp.app.banner');
193-
194-ns.Banner = Y.Base.create('banner', Y.Widget, [], {
195-
196- _getAnimTimes: function() {
197- var anim_times;
198- if (this.get('skip_animation')) {
199- anim_times = {
200- fade: 0.0,
201- slide_out: 0.0
202- };
203- } else {
204- anim_times = {
205- fade: 0.3,
206- slide_out: 0.2
207- };
208- }
209- return anim_times;
210- },
211-
212- _showBanner: function () {
213- var body = Y.one('body');
214- var global_notification = Y.one('.global-notification');
215- var anim_times = this._getAnimTimes();
216-
217- body.addClass('global-notification-visible');
218- global_notification.removeClass('hidden');
219-
220- var fade_in = new Y.Anim({
221- node: global_notification,
222- to: {opacity: 1},
223- duration: anim_times.fade
224- });
225- var body_space = new Y.Anim({
226- node: body,
227- to: {'paddingTop': '40px'},
228- duration: anim_times.slide_out,
229- easing: Y.Easing.easeOut
230- });
231- var login_space = new Y.Anim({
232- node: '.login-logout',
233- to: {'top': '45px'},
234- duration: anim_times.slide_out,
235- easing: Y.Easing.easeOut
236- });
237- // For testing, we don't do the animations or else the tests will fail.
238- if (anim_times.fade > 0) {
239- fade_in.run();
240- }
241- if (anim_times.slide_out > 0) {
242- body_space.run();
243- login_space.run();
244- }
245- },
246-
247- _hideBanner: function () {
248- var body = Y.one('body');
249- var global_notification = Y.one('.global-notification');
250- var anim_times = this._getAnimTimes();
251-
252- global_notification.addClass('transparent');
253-
254- var fade_out = new Y.Anim({
255- node: global_notification,
256- to: {opacity: 0},
257- duration: anim_times.fade
258- });
259- var body_space = new Y.Anim({
260- node: body,
261- to: {'paddingTop': 0},
262- duration: anim_times.slide_out,
263- easing: Y.Easing.easeOut
264- });
265- var login_space = new Y.Anim({
266- node: '.login-logout',
267- to: {'top': '6px'},
268- duration: anim_times.slide_out,
269- easing: Y.Easing.easeOut
270- });
271- fade_out.on('end', function() {
272- global_notification.addClass('hidden');
273- });
274- body_space.on('end', function() {
275- body.removeClass('global-notification-visible');
276- });
277-
278- fade_out.run();
279- body_space.run();
280- login_space.run();
281- },
282-
283- bindUI: function() {
284- this.after('visibleChange', function() {
285- if (this.get('visible')) {
286- this._showBanner();
287- } else {
288- this._hideBanner();
289- }
290- });
291- },
292-
293- renderUI: function () {
294- var banner_data = {
295- badge: this.get('banner_icon'),
296- text: this.get('notification_text')
297- };
298- var banner_html = Y.lp.mustache.to_html(
299- this.get('banner_template'),
300- banner_data);
301- this.get('contentBox').append(banner_html);
302- },
303-
304- updateText: function (new_text) {
305- var text_node = this.get('contentBox').one('.banner-text');
306- if (!Y.Lang.isValue(new_text)) {
307- new_text = this.get('notification_text');
308- }
309-
310- if (text_node) {
311- text_node.set('text', new_text);
312- } else {
313- Y.log('No text node to update banner text.', 'error');
314- }
315- }
316-
317-}, {
318- ATTRS: {
319- banner_icon: { value: "<span></span>" },
320- banner_template: {
321- valueFn: function() {
322- return [
323- '<div class="global-notification transparent hidden">',
324- '{{{ badge }}}',
325- '<span class="banner-text">{{ text }}</span>',
326- "</div>"].join('');
327- }
328- },
329- notification_text: { value: "" },
330- skip_animation: { value: false },
331- visible: { value: false }
332- }
333-});
334-
335-}, '0.1', {
336- requires: ['base', 'node', 'anim', 'widget', 'lp.mustache', 'yui-log']
337-});
338
339=== removed file 'lib/lp/app/javascript/banners/beta-notification.js'
340--- lib/lp/app/javascript/banners/beta-notification.js 2012-09-25 18:47:19 +0000
341+++ lib/lp/app/javascript/banners/beta-notification.js 1970-01-01 00:00:00 +0000
342@@ -1,104 +0,0 @@
343-/**
344- * Add a BetaBanner widget for use.
345- *
346- * @namespace lp.app.banner
347- * @module beta
348- *
349- */
350-YUI.add('lp.app.banner.beta', function(Y) {
351-
352-var ns = Y.namespace('lp.app.banner.beta');
353-var Banner = Y.lp.app.banner.Banner;
354-
355-// For the beta banner to work, it needs to have one instance, and one
356-// instance only.
357-window._singleton_beta_banner = null;
358-
359-ns.show_beta_if_needed = function () {
360- if (window._singleton_beta_banner === null) {
361- var src = Y.one('.yui3-betabanner');
362- window._singleton_beta_banner = new ns.BetaBanner({ srcNode: src });
363- }
364- if (window._singleton_beta_banner.get('features').length !== 0) {
365- window._singleton_beta_banner.render();
366- window._singleton_beta_banner.show();
367- }
368-};
369-
370-
371-/**
372- * Banner to display for beta features.
373- *
374- * @class BetaBanner
375- * @extends Banner
376- *
377- */
378-ns.BetaBanner = Y.Base.create('betaBanner', Banner, [], {
379-
380- bindUI: function () {
381- Banner.prototype.bindUI.apply(this, arguments);
382- var close_box = Y.one('.global-notification-close');
383- var that = this;
384- close_box.on('click', function(e) {
385- e.halt();
386- that.hide();
387- });
388- },
389-
390- renderUI: function () {
391- var banner_data = {
392- badge: this.get('banner_icon'),
393- text: this.get('notification_text'),
394- features: this.get('features'),
395- };
396- var banner_html = Y.lp.mustache.to_html(
397- this.get('banner_template'),
398- banner_data);
399- this.get('contentBox').append(banner_html);
400- var beta_node = Y.one('.global-notification');
401- var close_box = Y.Node.create(
402- '<a href="#" class="global-notification-close">Hide' +
403- '<span class="notification-close sprite" /></a>');
404- beta_node.appendChild(close_box);
405- }
406-
407-}, {
408- ATTRS: {
409- banner_icon: { value: '<span class="beta-warning">BETA!</span>' },
410-
411- banner_template: {
412- valueFn: function() {
413- return [
414- '<div class="global-notification transparent hidden">',
415- '{{{ badge }}}',
416- '<span class="banner-text">',
417- '{{ text }}{{{ features }}}',
418- '</span>',
419- "</div>"].join('');
420- }
421- },
422-
423- features: {
424- valueFn: function () {
425- var features_template = [
426- '{{#features}}{{#is_beta}}',
427- '<span class="beta-feature"> {{title}}',
428- '{{#url}}',
429- ' (<a href="{{url}}" class="info-link">read more</a>)',
430- '{{/url}}',
431- '</span>',
432- '{{/is_beta}}{{/features}}'].join('');
433- var feature_data = {
434- features: Y.Object.values(LP.cache.related_features)
435- };
436- return Y.lp.mustache.to_html(features_template, feature_data);
437- }
438- },
439-
440- notification_text: { value: "Some parts of this page are in beta: " }
441- }
442-});
443-
444-
445-}, '0.1', {'requires': ['base', 'node', 'anim', 'lp.mustache',
446- 'lp.app.banner']});
447
448=== removed file 'lib/lp/app/javascript/banners/privacy.js'
449--- lib/lp/app/javascript/banners/privacy.js 2012-10-04 14:17:23 +0000
450+++ lib/lp/app/javascript/banners/privacy.js 1970-01-01 00:00:00 +0000
451@@ -1,119 +0,0 @@
452-/**
453- * Add a PrivacyBanner widget for use.
454- *
455- * @namespace lp.app.banner
456- * @module privacy
457- *
458- */
459-YUI.add('lp.app.banner.privacy', function(Y) {
460-var ns = Y.namespace('lp.app.banner.privacy');
461-var Banner = Y.lp.app.banner.Banner;
462-
463-ns.EV_SHOW = 'privacy_banner:show';
464-ns.EV_HIDE = 'privacy_banner:hide';
465-
466-/**
467- * Allow for adjusting the global instance of the Privacy Banner via events.
468- *
469- * @event privacy_banner:show
470- * @param text The message to show
471- */
472-Y.publish(ns.EV_SHOW, {
473- emitFacade: true
474-});
475-
476-/**
477- * Hide the global instance of the banner via events.
478- *
479- * @event privacy_banner:hide
480- */
481-Y.publish(ns.EV_HIDE, {
482- emitFacade: true
483-});
484-
485-
486-// For the privacy banner to work, it needs to have one instance, and one
487-// instance only.
488-window._singleton_privacy_banner = null;
489-ns.getPrivacyBanner = function (banner_text, skip_animation) {
490- if (window._singleton_privacy_banner === null) {
491- var src = Y.one('.yui3-privacybanner');
492- window._singleton_privacy_banner = new ns.PrivacyBanner(
493- { srcNode: src, skip_animation: skip_animation });
494- window._singleton_privacy_banner.render();
495- }
496- if (Y.Lang.isValue(banner_text)) {
497- window._singleton_privacy_banner.updateText(banner_text);
498- }
499- return window._singleton_privacy_banner;
500-};
501-
502-
503-/**
504- * Banner to display when page contains private information.
505- *
506- * @class PrivacyBanner
507- * @extends Banner
508- *
509- */
510-ns.PrivacyBanner = Y.Base.create('privacyBanner', Banner, [], {
511- _custom_message: function (ev) {
512- var body = Y.one('body');
513- body.replaceClass('public', 'private');
514- if (!ev.text) {
515- throw('A custom privacy banner must have a text attribute');
516- }
517- this.updateText(ev.text);
518- this.show();
519- },
520-
521- _make_public: function (ev) {
522- var body = Y.one('body');
523- body.replaceClass('private', 'public');
524- this.hide();
525- },
526-
527- _make_private: function (ev) {
528- // Update the text in the banner before we show it.
529- var body = Y.one('body');
530- body.replaceClass('public', 'private');
531-
532- if (!ev.text) {
533- throw('Showing a privacy banner must supply text for the banner');
534- }
535- this.updateText(ev.text);
536- this.show();
537- },
538-
539- bindUI: function () {
540- var that = this;
541- var info_type = Y.lp.app.information_type;
542- Banner.prototype.bindUI.apply(this, arguments);
543-
544- // We care about changes to Information Type in the UI.
545- Y.on(info_type.EV_ISPUBLIC, that._make_public, that);
546- Y.on(info_type.EV_ISPRIVATE, that._make_private, that);
547-
548- // And provide our own manual events for the Security banner usage.
549- Y.on(ns.EV_SHOW, that._custom_message, that);
550- Y.on(ns.EV_HIDE, function (ev) {
551- that.hide();
552- }, that);
553- }
554-
555-}, {
556- ATTRS: {
557- banner_icon: {
558- value: '<span class="sprite notification-private"></span>'
559- },
560- notification_text: {
561- value: "The information on this page is private."
562- }
563- }
564-});
565-
566-}, "0.1", {
567- requires: [
568- "base", "node", "anim", "lp.app.banner",
569- "lp.app.information_type"]
570-});
571
572=== removed directory 'lib/lp/app/javascript/banners/tests'
573=== removed file 'lib/lp/app/javascript/banners/tests/test_banner.html'
574--- lib/lp/app/javascript/banners/tests/test_banner.html 2012-10-26 09:54:28 +0000
575+++ lib/lp/app/javascript/banners/tests/test_banner.html 1970-01-01 00:00:00 +0000
576@@ -1,46 +0,0 @@
577-<!DOCTYPE html>
578-<!--
579-Copyright 2012 Canonical Ltd. This software is licensed under the
580-GNU Affero General Public License version 3 (see the file LICENSE).
581--->
582-
583-<html>
584- <head>
585- <title>lp.app.banner Tests</title>
586-
587- <!-- YUI and test setup -->
588- <script type="text/javascript"
589- src="../../../../../../build/js/yui/yui/yui.js">
590- </script>
591- <link rel="stylesheet"
592- href="../../../../../../build/js/yui/console/assets/console-core.css" />
593- <link rel="stylesheet"
594- href="../../../../../../build/js/yui/test-console/assets/skins/sam/test-console.css" />
595- <link rel="stylesheet"
596- href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
597-
598- <script type="text/javascript"
599- src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
600-
601- <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
602-
603- <!-- Dependencies -->
604- <script type="text/javascript"
605- src="../../../../../../build/js/lp/app/mustache.js"></script>
606-
607- <!-- The module under test. -->
608- <script type="text/javascript" src="../banner.js"></script>
609-
610- <!-- Placeholder for any css asset for this module. -->
611- <!-- <link rel="stylesheet" href="../assets/${LIBRARY}-core.css" /> -->
612-
613- <!-- The test suite -->
614- <script type="text/javascript" src="test_banner.js"></script>
615-
616- </head>
617- <body class="yui3-skin-sam">
618- <ul id="suites">
619- <li>lp.app.banner.test</li>
620- </ul>
621- </body>
622-</html>
623
624=== removed file 'lib/lp/app/javascript/banners/tests/test_banner.js'
625--- lib/lp/app/javascript/banners/tests/test_banner.js 2012-10-26 10:00:20 +0000
626+++ lib/lp/app/javascript/banners/tests/test_banner.js 1970-01-01 00:00:00 +0000
627@@ -1,110 +0,0 @@
628-/* Copyright (c) 2012, Canonical Ltd. All rights reserved. */
629-
630-YUI.add('lp.app.banner.test', function (Y) {
631-
632- var tests = Y.namespace('lp.app.banner.test');
633- tests.suite = new Y.Test.Suite('lp.app.banner Tests');
634-
635- tests.suite.add(new Y.Test.Case({
636- name: 'banner_tests',
637-
638- setUp: function () {
639- var main = Y.Node.create('<div id="maincontent"></div>');
640- var login_logout = Y.Node.create('<div></div>')
641- .addClass('login-logout');
642- main.appendChild(login_logout);
643- Y.one('body').appendChild(main);
644- },
645-
646- tearDown: function () {
647- Y.one('#maincontent').remove(true);
648- Y.all('.yui3-banner').remove(true);
649- },
650-
651- test_library_exists: function () {
652- Y.Assert.isObject(Y.lp.app.banner,
653- "Could not locate the lp.app.banner module");
654- },
655-
656- test_init_without_config: function () {
657- var banner = new Y.lp.app.banner.Banner();
658- Y.Assert.areEqual("", banner.get('notification_text'));
659- Y.Assert.areEqual("<span></span>", banner.get('banner_icon'));
660- },
661-
662- test_init_with_config: function () {
663- var cfg = {
664- notification_text: "Some text.",
665- banner_icon: '<span class="sprite"></span>'
666- };
667- var banner = new Y.lp.app.banner.Banner(cfg);
668- Y.Assert.areEqual(
669- cfg.notification_text,
670- banner.get('notification_text'));
671- Y.Assert.areEqual(cfg.banner_icon, banner.get('banner_icon'));
672- },
673-
674- test_render_no_config: function () {
675- var banner = new Y.lp.app.banner.Banner({ skip_animation: true });
676- banner.render();
677-
678- var banner_node = Y.one(".global-notification");
679- Y.Assert.isObject(banner_node);
680- Y.Assert.isTrue(banner_node.hasClass('hidden'));
681- },
682-
683- test_render_with_config: function () {
684- var cfg = {
685- notification_text: "Some text.",
686- banner_icon: '<span class="sprite"></span>',
687- skip_animation: true
688- };
689- var banner = new Y.lp.app.banner.Banner(cfg);
690- banner.render();
691-
692- var banner_node = Y.one(".global-notification");
693- var badge = banner_node.one('.sprite');
694- Y.Assert.isObject(banner_node);
695- Y.Assert.areEqual(cfg.notification_text, banner_node.get('text'));
696- Y.Assert.isObject(badge);
697- },
698-
699- test_show: function() {
700- var banner = new Y.lp.app.banner.Banner({ skip_animation: true });
701- banner.render();
702- banner.show();
703- var banner_node = Y.one(".global-notification");
704- Y.Assert.isFalse(banner_node.hasClass('hidden'));
705- },
706-
707- test_hide: function() {
708- var banner = new Y.lp.app.banner.Banner({ skip_animation: true });
709- banner.render();
710- banner.show();
711-
712- // Even with animation times set to 0, this test needs a slight
713- // delay in order for the animation end events to fire.
714- var banner_node = Y.one(".global-notification");
715- var wait_for_anim = 20;
716- var check = function () {
717- Y.Assert.isTrue(banner_node.hasClass('hidden'));
718- };
719- banner.hide();
720- this.wait(check, wait_for_anim);
721- },
722-
723- test_updateText: function() {
724- var banner = new Y.lp.app.banner.Banner({ skip_animation: true });
725- banner.render();
726- var new_text = 'some new text';
727- banner.updateText(new_text);
728- var banner_node = Y.one(".global-notification");
729- Y.Assert.areEqual(new_text, banner_node.get('text'));
730-
731- banner.updateText();
732- banner_node = Y.one(".global-notification");
733- Y.Assert.areEqual("", banner_node.get('text'));
734- }
735- }));
736-
737-}, '0.1', {'requires': ['test', 'test-console', 'lp.app.banner']});
738
739=== removed file 'lib/lp/app/javascript/banners/tests/test_beta_notification.html'
740--- lib/lp/app/javascript/banners/tests/test_beta_notification.html 2012-10-26 09:54:28 +0000
741+++ lib/lp/app/javascript/banners/tests/test_beta_notification.html 1970-01-01 00:00:00 +0000
742@@ -1,51 +0,0 @@
743-<!DOCTYPE html>
744-<!--
745-Copyright 2012 Canonical Ltd. This software is licensed under the
746-GNU Affero General Public License version 3 (see the file LICENSE).
747--->
748-
749-<html>
750- <head>
751- <title>Test beta-notification</title>
752-
753- <!-- YUI and test setup -->
754- <script type="text/javascript"
755- src="../../../../../../build/js/yui/yui/yui.js">
756- </script>
757- <link rel="stylesheet"
758- href="../../../../../../build/js/yui/console/assets/console-core.css" />
759- <link rel="stylesheet"
760- href="../../../../../../build/js/yui/test-console/assets/skins/sam/test-console.css" />
761- <link rel="stylesheet"
762- href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
763-
764- <script type="text/javascript"
765- src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
766-
767- <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
768-
769- <!-- Dependencies -->
770- <script type="text/javascript"
771- src="../../../../../../build/js/lp/app/banners/banner.js"></script>
772- <script type="text/javascript"
773- src="../../../../../../build/js/lp/app/mustache.js"></script>
774-
775- <!-- The module under test. -->
776- <script type="text/javascript" src="../beta-notification.js"></script>
777-
778- <!-- Placeholder for any css asset for this module. -->
779- <!-- <link rel="stylesheet" href="../assets/beta-notification-core.css" /> -->
780-
781- <!-- The test suite. -->
782- <script type="text/javascript" src="test_beta_notification.js"></script>
783-
784- </head>
785- <body class="yui3-skin-sam">
786- <ul id="suites">
787- <!-- <li>lp.large_indicator.test</li> -->
788- <li>lp.app.banner.beta.test</li>
789- </ul>
790- <!-- The example markup required by the script to run. -->
791- <div id="maincontent"></div>
792- </body>
793-</html>
794
795=== removed file 'lib/lp/app/javascript/banners/tests/test_beta_notification.js'
796--- lib/lp/app/javascript/banners/tests/test_beta_notification.js 2012-10-26 10:00:20 +0000
797+++ lib/lp/app/javascript/banners/tests/test_beta_notification.js 1970-01-01 00:00:00 +0000
798@@ -1,140 +0,0 @@
799-/* Copyright (c) 2012, Canonical Ltd. All rights reserved. */
800-
801-// Set the "enabled" variable, normally set by base-layout-macros.
802-// This must be a global variable for the code being tested to work.
803-var privacy_notification_enabled = true;
804-
805-YUI.add('lp.app.banner.beta.test', function (Y) {
806-
807- var tests = Y.namespace('lp.app.banner.beta.test');
808- tests.suite = new Y.Test.Suite('lp.app.banner.beta Tests');
809-
810- tests.suite.add(new Y.Test.Case({
811- name: 'beta-notification',
812-
813- setUp: function () {
814- var main = Y.Node.create('<div id="maincontent"></div>');
815- var login_logout = Y.Node.create('<div></div>')
816- .addClass('login-logout');
817- main.appendChild(login_logout);
818- Y.one('body').append(main);
819- window.LP = {
820- cache: {}
821- };
822- },
823-
824- tearDown: function () {
825- Y.one('#maincontent').remove(true);
826- Y.all('.yui3-banner').remove(true);
827- },
828-
829- test_library_exists: function () {
830- Y.Assert.isObject(Y.lp.app.banner.beta,
831- "Could not locate the lp.app.banner.beta module");
832- },
833-
834- test_beta_banner_one_beta_feature: function() {
835- LP.cache.related_features = {
836- '': {
837- is_beta: true,
838- title: 'A beta feature',
839- url: 'http://lp.dev/LEP/one'
840- }};
841- var betabanner = new Y.lp.app.banner.beta.BetaBanner(
842- {skip_animation: true});
843- betabanner.render();
844- betabanner.show();
845-
846- var body = Y.one('body');
847- // The <body> node has the class global-notification-visible,
848- // so that the element has enough padding for the beta banner.
849- Y.Assert.isTrue(body.hasClass('global-notification-visible'));
850-
851- feature_info = Y.one('.beta-feature');
852- // The message about a beta feature consists of the feature
853- // title and a link to a page with more information about
854- // the feature.
855- Y.Assert.areEqual(
856- ' A beta feature (read more)', feature_info.get('text'));
857- info_link = feature_info.get('children').item(0);
858- Y.Assert.areEqual('http://lp.dev/LEP/one', info_link.get('href'));
859- },
860-
861- test_beta_banner_two_beta_features: function() {
862- LP.cache.related_features = {
863- '1': {
864- is_beta: true,
865- title: 'Beta feature 1',
866- url: 'http://lp.dev/LEP/one'
867- },
868- '2': {
869- is_beta: true,
870- title: 'Beta feature 2',
871- url: ''
872- }};
873- var betabanner = new Y.lp.app.banner.beta.BetaBanner(
874- {skip_animation: true});
875- betabanner.render();
876- betabanner.show();
877-
878- var body = Y.one('body');
879- Y.Assert.isTrue(body.hasClass('global-notification-visible'));
880-
881- // Notifications about several features can be displayed.
882- feature_info = Y.all('.beta-feature').item(0);
883- Y.Assert.areEqual(
884- ' Beta feature 1 (read more)', feature_info.get('text'));
885- info_link = feature_info.get('children').item(0);
886- Y.Assert.areEqual('http://lp.dev/LEP/one', info_link.get('href'));
887-
888- // If an entry in LP.cache.related_features does not provide a
889- // "read more" link, the corrsponding node is not added.
890- feature_info = Y.all('.beta-feature').item(1);
891- Y.Assert.areEqual(
892- ' Beta feature 2', feature_info.get('text'));
893- Y.Assert.isNull(feature_info.get('children').item(0));
894- },
895-
896- test_beta_banner_no_beta_features_defined: function() {
897- LP.cache.related_features = {
898- foo_feature: {
899- is_beta: false,
900- title: 'Non-beta feature',
901- url: 'http://example.org'
902- }};
903- Y.lp.app.banner.beta.show_beta_if_needed();
904- Y.Assert.isNull(Y.one('.global-notification'));
905- },
906-
907- test_hide_beta_banner: function() {
908- LP.cache.related_features = {
909- '': {
910- is_beta: true,
911- title: 'A beta feature',
912- url: 'http://lp.dev/LEP/one'
913- }};
914- var betabanner = new Y.lp.app.banner.beta.BetaBanner(
915- {skip_animation: true});
916- betabanner.render();
917- betabanner.show();
918- var body = Y.one('body');
919- var banner = Y.one('.global-notification');
920- Y.Assert.isFalse(banner.hasClass('hidden'));
921-
922- // Even with animation times set to 0, this test needs a slight
923- // delay in order for the animation end events to fire.
924- var check = function() {
925- Y.Assert.isTrue(banner.hasClass('hidden'));
926- Y.Assert.isFalse(
927- body.hasClass('global-notification-visible'));
928- };
929- close_link = Y.one('.global-notification-close');
930- close_link.simulate('click');
931- var wait_time = 20;
932- this.wait(check, wait_time);
933- }
934- }));
935-
936-}, '0.1', { 'requires': ['test', 'test-console', 'node', 'lp.app.banner.beta',
937- 'node-event-simulate']
938-});
939
940=== removed file 'lib/lp/app/javascript/banners/tests/test_privacy.html'
941--- lib/lp/app/javascript/banners/tests/test_privacy.html 2012-10-26 09:54:28 +0000
942+++ lib/lp/app/javascript/banners/tests/test_privacy.html 1970-01-01 00:00:00 +0000
943@@ -1,47 +0,0 @@
944-<!DOCTYPE html>
945-<!--
946-Copyright 2012 Canonical Ltd. This software is licensed under the
947-GNU Affero General Public License version 3 (see the file LICENSE).
948--->
949-
950-<html>
951- <head>
952- <title>lp.app.banner.privacy Tests</title>
953-
954- <!-- YUI and test setup -->
955- <script type="text/javascript"
956- src="../../../../../../build/js/yui/yui/yui.js">
957- </script>
958- <link rel="stylesheet"
959- href="../../../../../../build/js/yui/console/assets/console-core.css" />
960- <link rel="stylesheet"
961- href="../../../../../../build/js/yui/test-console/assets/skins/sam/test-console.css" />
962- <link rel="stylesheet"
963- href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
964-
965- <script type="text/javascript"
966- src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
967-
968- <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
969-
970- <!-- Dependencies -->
971- <script type="text/javascript"
972- src="../../../../../../build/js/lp/app/mustache.js"></script>
973- <script type="text/javascript"
974- src="../../../../../../build/js/lp/app/banners/banner.js"></script>
975- <script type="text/javascript"
976- src="../../../../../../build/js/lp/app/information_type.js"></script>
977-
978- <!-- The module under test. -->
979- <script type="text/javascript" src="../privacy.js"></script>
980-
981- <!-- The test suite. -->
982- <script type="text/javascript" src="test_privacy.js"></script>
983-
984- </head>
985- <body class="yui3-skin-sam">
986- <ul id="suites">
987- <li>lp.app.banner.privacy.test</li>
988- </ul>
989- </body>
990-</html>
991
992=== removed file 'lib/lp/app/javascript/banners/tests/test_privacy.js'
993--- lib/lp/app/javascript/banners/tests/test_privacy.js 2012-10-26 10:00:20 +0000
994+++ lib/lp/app/javascript/banners/tests/test_privacy.js 1970-01-01 00:00:00 +0000
995@@ -1,117 +0,0 @@
996-/* Copyright 2011-2012 Canonical Ltd. This software is licensed under the
997- * GNU Affero General Public License version 3 (see the file LICENSE).
998- */
999-
1000-YUI.add('lp.app.banner.privacy.test', function (Y) {
1001-
1002- var tests = Y.namespace('lp.app.banner.privacy.test');
1003- tests.suite = new Y.Test.Suite('lp.app.banner.privacy Tests');
1004-
1005- tests.suite.add(new Y.Test.Case({
1006- name: 'privacy_tests',
1007-
1008- setUp: function () {
1009- var main = Y.Node.create('<div id="maincontent"></div>');
1010- var login_logout = Y.Node.create('<div></div>')
1011- .addClass('login-logout');
1012- main.appendChild(login_logout);
1013-
1014- var banner_node = Y.Node.create('<div></div>')
1015- .addClass('yui3-privacybanner');
1016- main.appendChild(banner_node);
1017- Y.one('body').appendChild(main);
1018- },
1019-
1020- tearDown: function () {
1021- Y.one('#maincontent').remove(true);
1022- window._singleton_privacy_banner = null;
1023- delete window.LP;
1024- },
1025-
1026- test_library_exists: function () {
1027- Y.Assert.isObject(Y.lp.app.banner.privacy,
1028- "Could not locate the lp.app.banner.privacy module");
1029- },
1030- test_init: function () {
1031- var banner = new Y.lp.app.banner.privacy.PrivacyBanner();
1032- Y.Assert.areEqual(
1033- "The information on this page is private.",
1034- banner.get('notification_text'));
1035- Y.Assert.areEqual(
1036- '<span class="sprite notification-private"></span>',
1037- banner.get('banner_icon'));
1038- },
1039-
1040- test_only_one_banner: function () {
1041- // getPrivacyBanner only returns one banner.
1042- var banner = Y.lp.app.banner.privacy.getPrivacyBanner();
1043- Y.Assert.areEqual(1, Y.all('.global-notification').size());
1044-
1045- var new_text = 'This is new text';
1046- banner = Y.lp.app.banner.privacy.getPrivacyBanner(new_text);
1047- Y.Assert.areEqual(1, Y.all('.global-notification').size());
1048- var banner_node = Y.one('.global-notification');
1049- Y.Assert.areEqual(
1050- new_text,
1051- Y.one('.global-notification').get('text'));
1052- },
1053-
1054- test_banner_with_custom_text: function () {
1055- var banner = Y.lp.app.banner.privacy.getPrivacyBanner();
1056- var new_text = 'New custom text';
1057-
1058- Y.fire('privacy_banner:show', {
1059- text: new_text
1060- });
1061- Y.Assert.areEqual(
1062- new_text,
1063- Y.one('.global-notification').get('text'));
1064- Y.Assert.isTrue(banner.get('visible'),
1065- 'Banner should be visible.');
1066- },
1067-
1068- test_banner_hide_event: function () {
1069- var banner = Y.lp.app.banner.privacy.getPrivacyBanner();
1070- var new_text = 'New custom text';
1071-
1072- Y.fire('privacy_banner:show', {
1073- text: new_text
1074- });
1075- Y.fire('privacy_banner:hide');
1076- Y.Assert.isFalse(banner.get('visible'),
1077- 'Banner should not be visible.');
1078- },
1079-
1080- test_info_type_private_event: function () {
1081- var banner = Y.lp.app.banner.privacy.getPrivacyBanner();
1082- var body = Y.one('body');
1083- var msg = 'Some private message';
1084- Y.fire('information_type:is_private', {
1085- text: msg,
1086- value: 'PROPRIETARY'
1087- });
1088- Y.Assert.areEqual(
1089- msg,
1090- Y.one('.global-notification').get('text'));
1091- Y.Assert.isTrue(body.hasClass('private'),
1092- 'Body should be private');
1093- Y.Assert.isTrue(banner.get('visible'),
1094- 'Banner should be visible.');
1095- },
1096-
1097- test_info_type_public_event: function () {
1098- var banner = Y.lp.app.banner.privacy.getPrivacyBanner();
1099- var new_text = 'New custom text';
1100-
1101- Y.fire('privacy_banner:show', {
1102- text: new_text
1103- });
1104- Y.fire('information_type:is_public');
1105- var body = Y.one('body');
1106- Y.Assert.isTrue(body.hasClass('public'), 'Body should be public');
1107- Y.Assert.isFalse(banner.get('visible'),
1108- 'Banner should not be visible.');
1109- }
1110- }));
1111-
1112-}, '0.1', {'requires': ['test', 'test-console', 'lp.app.banner.privacy']});
1113
1114=== modified file 'lib/lp/app/javascript/information_type.js'
1115--- lib/lp/app/javascript/information_type.js 2012-10-04 14:17:23 +0000
1116+++ lib/lp/app/javascript/information_type.js 2012-11-12 13:21:28 +0000
1117@@ -44,6 +44,29 @@
1118 emitFacade: true
1119 });
1120
1121+
1122+/**
1123+ * Provide some logic to check if this is a private inducing event. This could
1124+ * be due to a non-public information type or as security related information
1125+ * type/value.
1126+ */
1127+var is_private_event = function (value) {
1128+ var is_private = false;
1129+ if (ns.get_cache_data_from_key(value,
1130+ 'value',
1131+ 'is_private')) {
1132+
1133+ is_private = true;
1134+ }
1135+
1136+ if (value.indexOf('SECURITY') !== -1) {
1137+ is_private = true;
1138+ }
1139+
1140+ return is_private;
1141+};
1142+
1143+
1144 /**
1145 * Wire up a helper event so that if someone changes the event, we take care
1146 * of also firing any is_private/is_public event shortcuts others want to
1147@@ -53,14 +76,13 @@
1148 * instead of having each module needing to know the location in the LP.cache
1149 * and such.
1150 */
1151+
1152 Y.on(ns.EV_CHANGE, function (ev) {
1153 if (!ev.value) {
1154 throw('Information type change event without new value');
1155 }
1156
1157- if (ns.get_cache_data_from_key(ev.value,
1158- 'value',
1159- 'is_private')) {
1160+ if (is_private_event(ev.value)) {
1161 Y.fire(ns.EV_ISPRIVATE, {
1162 text: ns.get_banner_text(ev.value),
1163 value: ev.value
1164@@ -155,16 +177,33 @@
1165 };
1166
1167 ns.get_banner_text = function(value) {
1168- var text_template = "This page contains {info_type} information.";
1169- var info_type = ns.get_cache_data_from_key(value, 'value', 'name');
1170- return Y.Lang.sub(text_template, {'info_type': info_type});
1171+ // Construct a different message for security related banner content.
1172+ var text;
1173+ if (value.indexOf('SECURITY') !== -1) {
1174+ var security_text = "This report will be private " +
1175+ "because it is a security " +
1176+ "vulnerability. You can " +
1177+ "disclose it later.";
1178+ text = security_text;
1179+ } else {
1180+ var text_template = "This page contains {info_type} information.";
1181+ var info_type = ns.get_cache_data_from_key(value, 'value', 'name');
1182+ text = Y.Lang.sub(text_template, {'info_type': info_type});
1183+ }
1184+ return text;
1185 };
1186
1187 ns.save_success = function(widget, context, value, subscribers_list,
1188 result_data) {
1189 context.information_type =
1190 ns.get_cache_data_from_key(value, 'value', 'name');
1191- ns.update_privacy_banner(value);
1192+
1193+ // Let the world know the information type has been updated. Allows
1194+ // banners to update.
1195+ Y.fire(ns.EV_CHANGE, {
1196+ value: value
1197+ });
1198+
1199 widget._showSucceeded();
1200 if (Y.Lang.isObject(result_data)) {
1201 var subscribers = result_data.subscription_data;
1202@@ -344,27 +383,7 @@
1203 }
1204 };
1205
1206-/**
1207- * Update the privacy banner to display the specified information type value.
1208- *
1209- * @param value
1210- */
1211-ns.update_privacy_banner = function(value) {
1212- var body = Y.one('body');
1213- var privacy_banner = Y.lp.app.banner.privacy.getPrivacyBanner(
1214- undefined, skip_animation);
1215- var private_type = LP.cache.information_type_data[value].is_private;
1216- if (private_type) {
1217- body.replaceClass('public', 'private');
1218- var banner_text = ns.get_banner_text(value);
1219- privacy_banner.updateText(banner_text);
1220- privacy_banner.show();
1221- } else {
1222- body.replaceClass('private', 'public');
1223- privacy_banner.hide();
1224- }
1225-};
1226
1227 }, "0.1", {"requires": [
1228 "base", "oop", "node", "event", "io-base", "lp.mustache", "lp.app.choice",
1229- "lp.bugs.bugtask_index", "lp.app.banner.privacy", "lp.ui.choiceedit"]});
1230+ "lp.bugs.bugtask_index", "lp.ui.choiceedit"]});
1231
1232=== modified file 'lib/lp/app/javascript/tests/test_information_type.js'
1233--- lib/lp/app/javascript/tests/test_information_type.js 2012-10-26 10:00:20 +0000
1234+++ lib/lp/app/javascript/tests/test_information_type.js 2012-11-12 13:21:28 +0000
1235@@ -84,22 +84,6 @@
1236 privacy_link, this.lp_client, LP.cache.bug, null, true);
1237 },
1238
1239- _shim_privacy_banner: function () {
1240- var old_func = Y.lp.app.banner.privacy.getPrivacyBanner;
1241- Y.lp.app.banner.privacy.getPrivacyBanner = function () {
1242- return {
1243- show: function () { Y.fire('test:banner:show'); },
1244- hide: function () { Y.fire('test:banner:hide'); },
1245- updateText: function () { Y.fire('test:banner:update'); }
1246- };
1247- };
1248- return old_func;
1249- },
1250-
1251- _unshim_privacy_banner: function (old_func) {
1252- Y.lp.app.banner.privacy.getPrivacyBanner = old_func;
1253- },
1254-
1255 test_library_exists: function () {
1256 Y.Assert.isObject(Y.lp.app.information_type,
1257 "Cannot locate the lp.app.information_type module");
1258@@ -142,53 +126,9 @@
1259 ns.save_success = orig_save_success;
1260 },
1261
1262- // Setting a private type shows the privacy banner.
1263- test_save_success_private: function() {
1264- this.makeWidget();
1265- var old_func = this._shim_privacy_banner();
1266- var hide_flag = false;
1267- var update_flag = false;
1268- Y.on('test:banner:show', function() {
1269- hide_flag = true;
1270- });
1271- Y.on('test:banner:update', function() {
1272- update_flag = true;
1273- });
1274-
1275- ns.save_success(this.widget, LP.cache.bug,
1276- 'PROPRIETARY');
1277- var body = Y.one('body');
1278- Y.Assert.isTrue(body.hasClass('private'));
1279- Y.Assert.isTrue(hide_flag);
1280- Y.Assert.isTrue(update_flag);
1281- Y.Assert.areEqual(
1282- 'Proprietary', LP.cache.bug.information_type);
1283- this._unshim_privacy_banner(old_func);
1284- },
1285-
1286- // Setting a public type hides the privacy banner.
1287- test_save_success_public: function() {
1288- this.makeWidget();
1289- var old_func = this._shim_privacy_banner();
1290- var flag = false;
1291- Y.on('test:banner:hide', function() {
1292- flag = true;
1293- });
1294- var summary = Y.one('#information-type-summary');
1295- summary.replaceClass('public', 'private');
1296-
1297- ns.save_success(this.widget, 'PUBLIC', 'PUBLIC');
1298- var body = Y.one('body');
1299- Y.Assert.isTrue(body.hasClass('public'));
1300- Y.Assert.isTrue(flag);
1301- Y.Assert.areEqual('Public', LP.cache.bug.information_type);
1302- this._unshim_privacy_banner(old_func);
1303- },
1304-
1305 // A successful save updates the subscribers portlet.
1306 test_save_success_with_subscribers_data: function() {
1307 this.makeWidget();
1308- var old_func = this._shim_privacy_banner();
1309 var flag = false;
1310 Y.on('test:banner:hide', function() {
1311 flag = true;
1312@@ -216,7 +156,6 @@
1313 Y.Assert.isTrue(load_subscribers_called);
1314 Y.Assert.areEqual('value1', window.LP.cache.item1);
1315 Y.Assert.areEqual('value2', window.LP.cache.item2);
1316- this._unshim_privacy_banner(old_func);
1317 },
1318
1319 // A successful save updates the task actions.
1320
1321=== added file 'lib/lp/app/javascript/ui/assets/skins/sam/banner.css'
1322--- lib/lp/app/javascript/ui/assets/skins/sam/banner.css 1970-01-01 00:00:00 +0000
1323+++ lib/lp/app/javascript/ui/assets/skins/sam/banner.css 2012-11-12 13:21:28 +0000
1324@@ -0,0 +1,109 @@
1325+/* JS Banner styling */
1326+.yui3-banner {
1327+ /* Default to not visible so we can fade in */
1328+ opacity: 1;
1329+
1330+ /* Animations for fade-in/out */
1331+ -webkit-transition: opacity 0.3s ease-in;
1332+ -moz-transition: opacity 0.3s ease-in;
1333+ transition: opacity 0.3s ease-in;
1334+
1335+ width: 100%;
1336+}
1337+
1338+.yui3-banner.yui3-banner-hidden {
1339+ opacity: 0;
1340+}
1341+
1342+/* Nasty hack to get the bar moved since it's absolutely positioned
1343+ * This also needs to be updated
1344+ * */
1345+body.beta #locationbar, body.private #locationbar {
1346+ top: 47px;
1347+}
1348+
1349+/* If we have both classes make room for two banners height */
1350+body.beta.private #locationbar {
1351+ top: 94px;
1352+}
1353+
1354+/* If the container exists make sure we start out with the rest of the page
1355+ * bumped down the starting distance to reduce flash effect
1356+ */
1357+.beta_banner_container, .private_banner_container {
1358+ min-height: 45px;
1359+}
1360+
1361+.yui3-banner-content {
1362+ box-shadow: 0 0 5px #333;
1363+ background-color: #666;
1364+ color: #fff;
1365+ display: block;
1366+ font-size: 14px;
1367+ font-weight: bold;
1368+ line-height: 21px;
1369+ padding: 8px 20px;
1370+ text-align: left;
1371+ text-shadow: 0 -1px 0 #631616;
1372+ z-index: 10;
1373+}
1374+
1375+.yui3-banner-content .badge {
1376+ display: inline-block;
1377+ height: 21px;
1378+ margin-right: 10px;
1379+ padding: 0;
1380+ vertical-align: middle;
1381+ width: 20px;
1382+}
1383+.yui3-banner-content .banner-content {}
1384+
1385+.yui3-banner-content.beta {
1386+ /* Some of these are required to override .beta CSS */
1387+
1388+ /* Defined for browsers that don't support transparency */
1389+ background-color: #606060;
1390+ /* Transparent background for browsers that support it */
1391+ background-color: rgba(64, 64, 64, 0.9);
1392+ height: auto;
1393+ margin-top: 0px;
1394+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5);
1395+ width: auto;
1396+}
1397+.yui3-banner-content.beta .yui3-banner-content-content {}
1398+.yui3-banner-content.beta .badge {
1399+ /* sprite-ref: icon-sprites */
1400+ background-color: #c10000;
1401+ background: linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
1402+ background: -moz-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
1403+ background: -ms-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
1404+ background: -o-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
1405+ background: -webkit-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
1406+ border-radius: 5px;
1407+ border-top: 1px solid #e20000;
1408+ font-size: 12px;
1409+ font-weight: bold;
1410+ margin-right: 12px;
1411+ padding: 3px 6px 4px 6px;
1412+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
1413+ width: auto;
1414+}
1415+
1416+.yui3-banner-content.beta .beta-feature {
1417+ font-weight: bold;
1418+}
1419+.yui3-banner-content.beta .info-link {
1420+ color: #4884ef;
1421+}
1422+
1423+.yui3-banner-content.private {
1424+ /* Define colour for browsers that don't support transparency */
1425+ background: #8d1f1f;
1426+ /* Set transparent background for browsers that support it */
1427+ background: rgba(125,0,0,0.9);
1428+}
1429+.yui3-banner-content.private .banner-content {}
1430+.yui3-banner-content.private .badge {
1431+ background: url(/@@/notification-private.png); /* sprite-ref: icon-sprites */
1432+ background-repeat: no-repeat;
1433+}
1434
1435=== removed file 'lib/lp/app/javascript/ui/assets/skins/sam/banner.css'
1436--- lib/lp/app/javascript/ui/assets/skins/sam/banner.css 2012-11-09 19:06:16 +0000
1437+++ lib/lp/app/javascript/ui/assets/skins/sam/banner.css 1970-01-01 00:00:00 +0000
1438@@ -1,109 +0,0 @@
1439-/* JS Banner styling */
1440-.yui3-banner {
1441- /* Default to not visible so we can fade in */
1442- opacity: 1;
1443-
1444- /* Animations for fade-in/out */
1445- -webkit-transition: opacity 0.3s ease-in;
1446- -moz-transition: opacity 0.3s ease-in;
1447- transition: opacity 0.3s ease-in;
1448-
1449- width: 100%;
1450-}
1451-
1452-.yui3-banner.yui3-banner-hidden {
1453- opacity: 0;
1454-}
1455-
1456-/* Nasty hack to get the bar moved since it's absolutely positioned
1457- * This also needs to be updated
1458- */
1459-body.beta #locationbar, body.private #locationbar {
1460- top: 47px;
1461-}
1462-
1463-/* If we have both classes make room for two banners height */
1464-body.beta.private #locationbar {
1465- top: 94px;
1466-}
1467-
1468-/* If the container exists make sure we start out with the rest of the page
1469- * bumped down the starting distance to reduce flash effect
1470- */
1471-.beta_banner_container, .private_banner_container {
1472- min-height: 45px;
1473-}
1474-
1475-.yui3-banner-content {
1476- box-shadow: 0 0 5px #333;
1477- background-color: #666;
1478- color: #fff;
1479- display: block;
1480- font-size: 14px;
1481- font-weight: bold;
1482- line-height: 21px;
1483- padding: 8px 20px;
1484- text-align: left;
1485- text-shadow: 0 -1px 0 #631616;
1486- z-index: 10;
1487-}
1488-
1489-.yui3-banner-content .badge {
1490- display: inline-block;
1491- height: 21px;
1492- margin-right: 10px;
1493- padding: 0;
1494- vertical-align: middle;
1495- width: 20px;
1496-}
1497-.yui3-banner-content .banner-content {}
1498-
1499-.yui3-banner-content.beta {
1500- /* Some of these are required to override .beta CSS */
1501-
1502- /* Defined for browsers that don't support transparency */
1503- background-color: #606060;
1504- /* Transparent background for browsers that support it */
1505- background-color: rgba(64, 64, 64, 0.9);
1506- height: auto;
1507- margin-top: 0px;
1508- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5);
1509- width: auto;
1510-}
1511-.yui3-banner-content.beta .yui3-banner-content-content {}
1512-.yui3-banner-content.beta .badge {
1513- /* sprite-ref: icon-sprites */
1514- background-color: #c10000;
1515- background: linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
1516- background: -moz-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
1517- background: -ms-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
1518- background: -o-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
1519- background: -webkit-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
1520- border-radius: 5px;
1521- border-top: 1px solid #e20000;
1522- font-size: 12px;
1523- font-weight: bold;
1524- margin-right: 12px;
1525- padding: 3px 6px 4px 6px;
1526- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
1527- width: auto;
1528-}
1529-
1530-.yui3-banner-content.beta .beta-feature {
1531- font-weight: bold;
1532-}
1533-.yui3-banner-content.beta .info-link {
1534- color: #4884ef;
1535-}
1536-
1537-.yui3-banner-content.private {
1538- /* Define colour for browsers that don't support transparency */
1539- background: #8d1f1f;
1540- /* Set transparent background for browsers that support it */
1541- background: rgba(125,0,0,0.9);
1542-}
1543-.yui3-banner-content.private .banner-content {}
1544-.yui3-banner-content.private .badge {
1545- background: url(/@@/notification-private.png);
1546- background-repeat: no-repeat;
1547-}
1548
1549=== added file 'lib/lp/app/javascript/ui/banner.js'
1550--- lib/lp/app/javascript/ui/banner.js 1970-01-01 00:00:00 +0000
1551+++ lib/lp/app/javascript/ui/banner.js 2012-11-12 13:21:28 +0000
1552@@ -0,0 +1,316 @@
1553+/*
1554+ * Copyright 2012 Canonical Ltd. This software is licensed under the
1555+ * GNU Affero General Public License version 3 (see the file LICENSE).
1556+ *
1557+ * Notification banner widget
1558+ *
1559+ * @module lp.ui.banner
1560+ * @namespace lp.ui
1561+ * @module banner
1562+ */
1563+YUI.add('lp.ui.banner', function (Y) {
1564+
1565+ var ns = Y.namespace('lp.ui.banner');
1566+
1567+ // GLOBALS
1568+ ns.PRIVATE = 'private';
1569+ ns.BETA = 'beta';
1570+
1571+ /**
1572+ * Banner widget base class
1573+ *
1574+ * This is the base Banner, you're supposed to supply some message data to
1575+ * generate the banner in the proper method.
1576+ *
1577+ * This banner provides all shared functionality between the Privacy and
1578+ * Beta banners.
1579+ *
1580+ * @class Banner
1581+ * @extends Y.Widget
1582+ *
1583+ */
1584+ ns.Banner = Y.Base.create('banner', Y.Widget, [], {
1585+ template: [
1586+ '<div class="banner">',
1587+ '<span class="badge {{ banner_type }}">{{ badge_text }}</span>',
1588+ '<span class="banner-content">{{{ content }}}</span>',
1589+ '</div>'
1590+ ].join(''),
1591+
1592+
1593+ /**
1594+ * Bind events that our widget supports such as closing the banner.
1595+ *
1596+ * We also watch the destroy event to clean up side effect css we
1597+ * created.
1598+ *
1599+ * @method bindUI
1600+ */
1601+ bindUI: function () {
1602+ this.on('destroy', function (ev) {
1603+ // XXX: Bug #1076074
1604+ var body = Y.one('body');
1605+ var banner_type = this.get('banner_type');
1606+ body.removeClass(banner_type);
1607+
1608+ // Remove any container the page might have provided for us to
1609+ // start out with.
1610+ var container_class = '.' + banner_type + '_banner_container';
1611+ var container = Y.one(container_class);
1612+ if (container) {
1613+ Y.one(container_class).remove();
1614+ }
1615+ });
1616+
1617+ this.after('contentChange', function () {
1618+ this.renderUI();
1619+ });
1620+ },
1621+
1622+ /**
1623+ * Default initialize method.
1624+ *
1625+ * @method initialize
1626+ * @param {Object} cfg
1627+ */
1628+ initialize: function (cfg) {
1629+ },
1630+
1631+ /**
1632+ * Widget render method to generate the html of the widget.
1633+ *
1634+ * @method renderUI
1635+ */
1636+ renderUI: function () {
1637+ var contentBox = this.get('contentBox');
1638+ contentBox.addClass(this.get('banner_type'));
1639+ var html = Y.lp.mustache.to_html(this.template, this.getAttrs());
1640+ contentBox.setHTML(html);
1641+
1642+ // XXX: Bug #1076074
1643+ // Needs to get cleaned up. Only applies to the global
1644+ // banners and not to other ones which we're working to allow.
1645+ // This is currently required because the #locationbar is
1646+ // absolutely located and needs to be moved as banners change.
1647+ var body = Y.one('body');
1648+ body.addClass(this.get('banner_type'));
1649+
1650+ if (this.get('visible')) {
1651+ this.show();
1652+ }
1653+ },
1654+
1655+ /**
1656+ * We need to override show so that we force a browser repaint which
1657+ * allows our CSS3 animation to run. Otherwise the browser sees we
1658+ * added new DOM elements and jumps straight to the finished animation
1659+ * point.
1660+ *
1661+ * @method show
1662+ */
1663+ show: function () {
1664+ var _node = this.get('boundingBox')._node;
1665+ var getComputedStyle = document.defaultView.getComputedStyle;
1666+ _node.style.display = getComputedStyle(_node).display;
1667+ return this.set('visible', true);
1668+ }
1669+
1670+ }, {
1671+ ATTRS: {
1672+ /**
1673+ * Instead of a sprite we might have text such as the Beta banner.
1674+ *
1675+ * @attribute badge_text
1676+ * @default undefined
1677+ * @type {String}
1678+ */
1679+ badge_text: {},
1680+
1681+ /**
1682+ * The Banner is meant to house some message to the user provided
1683+ * by this content. It can be html and is not escaped for that
1684+ * reason.
1685+ *
1686+ * @attribute content
1687+ * @default undefined
1688+ * @type {String}
1689+ */
1690+ content: {},
1691+
1692+ /**
1693+ * This is listed to help aid in discovery of how the container
1694+ * node for the widget is determined. It's passed into the
1695+ * render() method and the Widget constructs itself inside of
1696+ * there.
1697+ *
1698+ * @attribute boundingBox
1699+ * @default undefined
1700+ * @type {Node}
1701+ */
1702+ boundingBox: {
1703+
1704+ },
1705+
1706+ /**
1707+ * Much of the Widget is determined by the type of banner it is.
1708+ * See the constants defined PRIVATE and BETA for two known types.
1709+ * If you set this manually you'll be able to provide custom
1710+ * styling as required because the type is used as a css class
1711+ * property.
1712+ *
1713+ * @attribute banner_type
1714+ * @default undefined
1715+ * @type {String}
1716+ */
1717+ banner_type: {},
1718+
1719+ /**
1720+ * Start out as not visible which should render as opacity 0, then
1721+ * we update it and it animates due to our css3.
1722+ *
1723+ * @attribute visible
1724+ * @default false
1725+ * @type {Bool}
1726+ */
1727+ visible: {
1728+ value: false
1729+ }
1730+ }
1731+ });
1732+
1733+ /**
1734+ * Beta Banner widget
1735+ *
1736+ * This is the Beta feature banner which needs to know about the title and
1737+ * url of the feature to construct the content correctly. Features are
1738+ * meant to be matched to the current LP.cache.related_features data
1739+ * available.
1740+ *
1741+ * @class BetaBanner
1742+ * @extends Banner
1743+ *
1744+ */
1745+ ns.BetaBanner = Y.Base.create('banner', ns.Banner, [], {
1746+
1747+ }, {
1748+ ATTRS: {
1749+ /**
1750+ * @attribute badge_text
1751+ * @default "BETA!"
1752+ * @type {String}
1753+ */
1754+ badge_text: {
1755+ value: 'BETA!'
1756+ },
1757+
1758+ /**
1759+ * The content for the beta banner is constructed from hard coded
1760+ * content and the list of enabled beta features currently
1761+ * relevant to the page.
1762+ *
1763+ * @attribute content
1764+ * @default {generated}
1765+ * @type {String}
1766+ */
1767+ content: {
1768+ getter: function () {
1769+ var content = "Some parts of this page are in beta:&nbsp;";
1770+ var key;
1771+ // We need to process the features to build the features
1772+ // that apply.
1773+ var features = this.get('features');
1774+ for (key in features) {
1775+ if (features.hasOwnProperty(key)) {
1776+ var obj = features[key];
1777+ if (obj.is_beta) {
1778+ content = content + [
1779+ '<span class="beta-feature">',
1780+ obj.title,
1781+ '&nbsp;<a class="info-link" href="',
1782+ obj.url + '">(read more)</a>',
1783+ '</span>'
1784+ ].join('');
1785+ }
1786+ }
1787+ }
1788+ return content;
1789+ }
1790+ },
1791+
1792+ /**
1793+ * features is a nested object of the beta features going. See
1794+ * LP.cache.related_features for the list of features. We only
1795+ * want those related features that are in beta.
1796+ * Ex: {
1797+ * disclosure.private_projects.enabled: {
1798+ * is_beta: true,
1799+ * title: "",
1800+ * url: "http://blog.ld.net/general/private-projects-beta",
1801+ * value: "true"
1802+ * }
1803+ * }
1804+ * @attribute features
1805+ * @default {}
1806+ * @type {Object}
1807+ */
1808+ features: {},
1809+
1810+ /**
1811+ * Manually force the banner type so users don't need to set it.
1812+ * This is a beta banner class.
1813+ *
1814+ * @attribute banner_type
1815+ * @default BETA
1816+ * @type {String}
1817+ */
1818+ banner_type: {
1819+ value: ns.BETA
1820+ }
1821+
1822+ }
1823+ });
1824+
1825+ /**
1826+ * Private Banner widget
1827+ *
1828+ * This is the Private feature banner which is pretty basic.
1829+ *
1830+ * Note that this doesn't automatically follow the information type code.
1831+ * Nor does it listen to the choice widgets and try to update. It's purely
1832+ * meant to function as told to do so. Most of the work around making sure
1833+ * the banner shows and works properly is in the View code in global.js.
1834+ *
1835+ * @class PrivateBanner
1836+ * @extends Banner
1837+ *
1838+ */
1839+ ns.PrivateBanner = Y.Base.create('banner', ns.Banner, [], {
1840+
1841+ }, {
1842+ ATTRS: {
1843+ badge_text: {
1844+ value: ''
1845+ },
1846+
1847+ content: {
1848+ value: 'The information on this page is private.'
1849+ },
1850+
1851+ /**
1852+ * Manually force the banner type so users don't need to set it.
1853+ * This is a beta banner class.
1854+ *
1855+ * @attribute banner_type
1856+ * @default BETA
1857+ * @type {String}
1858+ */
1859+ banner_type: {
1860+ value: ns.PRIVATE
1861+ },
1862+
1863+ }
1864+ });
1865+
1866+}, '0.1', {
1867+ requires: ['base', 'node', 'anim', 'widget', 'lp.mustache', 'yui-log']
1868+});
1869
1870=== removed file 'lib/lp/app/javascript/ui/banner.js'
1871--- lib/lp/app/javascript/ui/banner.js 2012-11-09 18:55:02 +0000
1872+++ lib/lp/app/javascript/ui/banner.js 1970-01-01 00:00:00 +0000
1873@@ -1,315 +0,0 @@
1874-/*
1875- * Copyright 2012 Canonical Ltd. This software is licensed under the
1876- * GNU Affero General Public License version 3 (see the file LICENSE).
1877- *
1878- * Notification banner widget
1879- *
1880- * @module lp.ui.banner
1881- * @namespace lp.ui
1882- * @module banner
1883- */
1884-YUI.add('lp.ui.banner', function (Y) {
1885-
1886- var ns = Y.namespace('lp.ui.banner');
1887-
1888- // GLOBALS
1889- ns.PRIVATE = 'private';
1890- ns.BETA = 'beta';
1891-
1892- /**
1893- * Banner widget base class
1894- *
1895- * This is the base Banner, you're supposed to supply some message data to
1896- * generate the banner in the proper method.
1897- *
1898- * This banner provides all shared functionality between the Privacy and
1899- * Beta banners.
1900- *
1901- * @class Banner
1902- * @extends Y.Widget
1903- *
1904- */
1905- ns.Banner = Y.Base.create('banner', Y.Widget, [], {
1906- template: [
1907- '<div class="banner">',
1908- '<span class="badge {{ banner_type }}">{{ badge_text }}</span>',
1909- '<span class="banner-content">{{{ content }}}</span>',
1910- '</div>'
1911- ].join(''),
1912-
1913-
1914- /**
1915- * Bind events that our widget supports such as closing the banner.
1916- *
1917- * We also watch the destroy event to clean up side effect css we
1918- * created.
1919- *
1920- * @method bindUI
1921- */
1922- bindUI: function () {
1923- this.on('destroy', function (ev) {
1924- // XXX: Bug #1076074
1925- var body = Y.one('body');
1926- var banner_type = this.get('banner_type');
1927- body.removeClass(banner_type);
1928-
1929- // Remove any container the page might have provided for us to
1930- // start out with.
1931- var container_class = '.' + banner_type + '_banner_container';
1932- var container = Y.one(container_class);
1933- if (container) {
1934- Y.one(container_class).remove();
1935- }
1936- });
1937-
1938- this.after('contentChange', function () {
1939- this.renderUI();
1940- });
1941- },
1942-
1943- /**
1944- * Default initialize method.
1945- *
1946- * @method initialize
1947- * @param {Object} cfg
1948- */
1949- initialize: function (cfg) {
1950- },
1951-
1952- /**
1953- * Widget render method to generate the html of the widget.
1954- *
1955- * @method renderUI
1956- */
1957- renderUI: function () {
1958- var contentBox = this.get('contentBox');
1959- contentBox.addClass(this.get('banner_type'));
1960- var html = Y.lp.mustache.to_html(this.template, this.getAttrs());
1961- contentBox.setHTML(html);
1962-
1963- // XXX: Bug #1076074
1964- // Needs to get cleaned up. Only applies to the global
1965- // banners and not to other ones which we're working to allow.
1966- // This is currently required because the #locationbar is
1967- // absolutely located and needs to be moved as banners change.
1968- var body = Y.one('body');
1969- body.addClass(this.get('banner_type'));
1970-
1971- if (this.get('visible')) {
1972- this.show();
1973- }
1974- },
1975-
1976- /**
1977- * We need to override show so that we force a browser repaint which
1978- * allows our CSS3 animation to run. Otherwise the browser sees we
1979- * added new DOM elements and jumps straight to the finished animation
1980- * point.
1981- *
1982- * @method show
1983- */
1984- show: function () {
1985- var _node = this.get('boundingBox')._node;
1986- var getComputedStyle = document.defaultView.getComputedStyle;
1987- _node.style.display = getComputedStyle(_node).display;
1988- return this.set('visible', true);
1989- }
1990-
1991- }, {
1992- ATTRS: {
1993- /**
1994- * Instead of a sprite we might have text such as the Beta banner.
1995- *
1996- * @attribute badge_text
1997- * @default undefined
1998- * @type {String}
1999- */
2000- badge_text: {},
2001-
2002- /**
2003- * The Banner is meant to house some message to the user provided
2004- * by this content. It can be html and is not escaped for that
2005- * reason.
2006- *
2007- * @attribute content
2008- * @default undefined
2009- * @type {String}
2010- */
2011- content: {},
2012-
2013- /**
2014- * This is listed to help aid in discovery of how the container
2015- * node for the widget is determined. It's passed into the
2016- * render() method and the Widget constructs itself inside of
2017- * there.
2018- *
2019- * @attribute boundingBox
2020- * @default undefined
2021- * @type {Node}
2022- */
2023- boundingBox: {
2024-
2025- },
2026-
2027- /**
2028- * Much of the Widget is determined by the type of banner it is.
2029- * See the constants defined PRIVATE and BETA for two known types.
2030- * If you set this manually you'll be able to provide custom
2031- * styling as required because the type is used as a css class
2032- * property.
2033- *
2034- * @attribute banner_type
2035- * @default undefined
2036- * @type {String}
2037- */
2038- banner_type: {},
2039-
2040- /**
2041- * Start out as not visible which should render as opacity 0, then
2042- * we update it and it animates due to our css3.
2043- *
2044- * @attribute visible
2045- * @default false
2046- * @type {Bool}
2047- */
2048- visible: {
2049- value: false
2050- }
2051- }
2052- });
2053-
2054- /**
2055- * Beta Banner widget
2056- *
2057- * This is the Beta feature banner which needs to know about the title and
2058- * url of the feature to construct the content correctly. Features are
2059- * meant to be matched to the current LP.cache.related_features data
2060- * available.
2061- *
2062- * @class BetaBanner
2063- * @extends Banner
2064- *
2065- */
2066- ns.BetaBanner = Y.Base.create('banner', ns.Banner, [], {
2067-
2068- }, {
2069- ATTRS: {
2070- /**
2071- * @attribute badge_text
2072- * @default "BETA!"
2073- * @type {String}
2074- */
2075- badge_text: {
2076- value: 'BETA!'
2077- },
2078-
2079- /**
2080- * The content for the beta banner is constructed from hard coded
2081- * content and the list of enabled beta features currently
2082- * relevant to the page.
2083- *
2084- * @attribute content
2085- * @default {generated}
2086- * @type {String}
2087- */
2088- content: {
2089- getter: function () {
2090- var content = "Some parts of this page are in beta:&nbsp;";
2091- var key;
2092- // We need to process the features to build the features
2093- // that apply.
2094- var features = this.get('features');
2095- for (key in features) {
2096- if (features.hasOwnProperty(key)) {
2097- var obj = features[key];
2098- if (obj.is_beta) {
2099- content = content + [
2100- '<span class="beta-feature">',
2101- obj.title,
2102- '&nbsp;<a class="info-link" href="',
2103- obj.url + '">(read more)</a>',
2104- '</span>'
2105- ].join('');
2106- }
2107- }
2108- }
2109- return content;
2110- }
2111- },
2112-
2113- /**
2114- * features is a nested object of the beta features going. See
2115- * LP.cache.related_features for the list of features. We only
2116- * want those related features that are in beta.
2117- * Ex: {
2118- * disclosure.private_projects.enabled: {
2119- * is_beta: true,
2120- * title: "",
2121- * url: "http://blog.ld.net/general/private-projects-beta",
2122- * value: "true"
2123- * }
2124- * }
2125- * @attribute features
2126- * @default {}
2127- * @type {Object}
2128- */
2129- features: {},
2130-
2131- /**
2132- * Manually force the banner type so users don't need to set it.
2133- * This is a beta banner class.
2134- *
2135- * @attribute banner_type
2136- * @default BETA
2137- * @type {String}
2138- */
2139- banner_type: {
2140- value: ns.BETA
2141- }
2142-
2143- }
2144- });
2145-
2146- /**
2147- * Private Banner widget
2148- *
2149- * This is the Private feature banner which is pretty basic.
2150- *
2151- * Note that this doesn't automatically follow the information type code.
2152- * Nor does it listen to the choice widgets and try to update. It's purely
2153- * meant to function as told to do so. Most of the work around making sure
2154- * the banner shows and works properly is in the View code in global.js.
2155- *
2156- * @class PrivateBanner
2157- * @extends Banner
2158- *
2159- */
2160- ns.PrivateBanner = Y.Base.create('banner', ns.Banner, [], {
2161-
2162- }, {
2163- ATTRS: {
2164- badge_text: {
2165- value: ''
2166- },
2167-
2168- content: {
2169- value: 'The information on this page is private.'
2170- },
2171-
2172- /**
2173- * Manually force the banner type so users don't need to set it.
2174- * This is a beta banner class.
2175- *
2176- * @attribute banner_type
2177- * @default BETA
2178- * @type {String}
2179- */
2180- banner_type: {
2181- value: ns.PRIVATE
2182- }
2183- }
2184- });
2185-
2186-}, '0.1', {
2187- requires: ['base', 'node', 'anim', 'widget', 'lp.mustache', 'yui-log']
2188-});
2189
2190=== added directory 'lib/lp/app/javascript/ui/tests'
2191=== removed directory 'lib/lp/app/javascript/ui/tests'
2192=== added file 'lib/lp/app/javascript/ui/tests/test_banner.html'
2193--- lib/lp/app/javascript/ui/tests/test_banner.html 1970-01-01 00:00:00 +0000
2194+++ lib/lp/app/javascript/ui/tests/test_banner.html 2012-11-12 13:21:28 +0000
2195@@ -0,0 +1,49 @@
2196+<!DOCTYPE html>
2197+<!--
2198+Copyright 2012 Canonical Ltd. This software is licensed under the
2199+GNU Affero General Public License version 3 (see the file LICENSE).
2200+-->
2201+
2202+<html>
2203+ <head>
2204+ <title>lp.ui.banner Tests</title>
2205+
2206+ <!-- YUI and test setup -->
2207+ <script type="text/javascript"
2208+ src="../../../../../../build/js/yui/yui/yui.js">
2209+ </script>
2210+ <link rel="stylesheet"
2211+ href="../../../../../../build/js/yui/console/assets/console-core.css" />
2212+ <link rel="stylesheet"
2213+ href="../../../../../../build/js/yui/test-console/assets/skins/sam/test-console.css" />
2214+ <link rel="stylesheet"
2215+ href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
2216+
2217+ <script type="text/javascript"
2218+ src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
2219+ <script type="text/javascript"
2220+ src="../../../../../build/js/lp/app/testing/helpers.js"></script>
2221+
2222+ <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
2223+
2224+ <!-- Dependencies -->
2225+ <script type="text/javascript"
2226+ src="../../../../../../build/js/lp/app/mustache.js"></script>
2227+
2228+ <!-- The module under test. -->
2229+ <script type="text/javascript" src="../banner.js"></script>
2230+
2231+ <!-- Placeholder for any css asset for this module. -->
2232+ <link rel="stylesheet" href="../assets/skins/sam/banner.css" />
2233+
2234+ <!-- The test suite -->
2235+ <script type="text/javascript" src="test_banner.js"></script>
2236+
2237+ </head>
2238+ <body class="yui3-skin-sam">
2239+ <ul id="suites">
2240+ <li>lp.ui.banner.test</li>
2241+ </ul>
2242+ <div id="fixture"></div>
2243+ </body>
2244+</html>
2245
2246=== removed file 'lib/lp/app/javascript/ui/tests/test_banner.html'
2247--- lib/lp/app/javascript/ui/tests/test_banner.html 2012-11-09 18:46:31 +0000
2248+++ lib/lp/app/javascript/ui/tests/test_banner.html 1970-01-01 00:00:00 +0000
2249@@ -1,49 +0,0 @@
2250-<!DOCTYPE html>
2251-<!--
2252-Copyright 2012 Canonical Ltd. This software is licensed under the
2253-GNU Affero General Public License version 3 (see the file LICENSE).
2254--->
2255-
2256-<html>
2257- <head>
2258- <title>lp.ui.banner Tests</title>
2259-
2260- <!-- YUI and test setup -->
2261- <script type="text/javascript"
2262- src="../../../../../../build/js/yui/yui/yui.js">
2263- </script>
2264- <link rel="stylesheet"
2265- href="../../../../../../build/js/yui/console/assets/console-core.css" />
2266- <link rel="stylesheet"
2267- href="../../../../../../build/js/yui/test-console/assets/skins/sam/test-console.css" />
2268- <link rel="stylesheet"
2269- href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
2270-
2271- <script type="text/javascript"
2272- src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
2273- <script type="text/javascript"
2274- src="../../../../../build/js/lp/app/testing/helpers.js"></script>
2275-
2276- <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
2277-
2278- <!-- Dependencies -->
2279- <script type="text/javascript"
2280- src="../../../../../../build/js/lp/app/mustache.js"></script>
2281-
2282- <!-- The module under test. -->
2283- <script type="text/javascript" src="../banner.js"></script>
2284-
2285- <!-- Placeholder for any css asset for this module. -->
2286- <link rel="stylesheet" href="../assets/skins/sam/banner.css" />
2287-
2288- <!-- The test suite -->
2289- <script type="text/javascript" src="test_banner.js"></script>
2290-
2291- </head>
2292- <body class="yui3-skin-sam">
2293- <ul id="suites">
2294- <li>lp.ui.banner.test</li>
2295- </ul>
2296- <div id="fixture"></div>
2297- </body>
2298-</html>
2299
2300=== added file 'lib/lp/app/javascript/ui/tests/test_banner.js'
2301--- lib/lp/app/javascript/ui/tests/test_banner.js 1970-01-01 00:00:00 +0000
2302+++ lib/lp/app/javascript/ui/tests/test_banner.js 2012-11-12 13:21:28 +0000
2303@@ -0,0 +1,287 @@
2304+/* Copyright (c) 2012 Canonical Ltd. All rights reserved. */
2305+
2306+YUI.add('lp.ui.banner.test', function (Y) {
2307+
2308+ var tests = Y.namespace('lp.ui.banner.test');
2309+ tests.suite = new Y.Test.Suite('ui.banner Tests');
2310+
2311+ var ns = Y.lp.ui.banner;
2312+
2313+ tests.suite.add(new Y.Test.Case({
2314+ name: 'ui.banner_tests',
2315+
2316+ setUp: function () {
2317+ this.container = Y.one('#fixture');
2318+ },
2319+
2320+ tearDown: function () {
2321+ this.container.empty();
2322+ },
2323+
2324+ test_library_exists: function () {
2325+ Y.Assert.isObject(Y.lp.ui.banner,
2326+ "Could not locate the lp.ui.banner module");
2327+ },
2328+
2329+ test_render: function () {
2330+ var b = new ns.Banner();
2331+ b.render(this.container);
2332+
2333+ var banners = Y.all('.banner');
2334+ Y.Assert.areEqual(
2335+ 1,
2336+ banners._nodes.length,
2337+ 'We have one banner node');
2338+
2339+ // The banner should make sure it's in the container as well.
2340+ var contained_banners = Y.all('#fixture .banner');
2341+ Y.Assert.areEqual(
2342+ 1,
2343+ contained_banners._nodes.length,
2344+ 'Banner node is placed.');
2345+ },
2346+
2347+ test_render_content: function () {
2348+ var msg = 'This is a banner message. Fear me.',
2349+ b = new ns.Banner({
2350+ content: msg
2351+ });
2352+
2353+ b.render(this.container);
2354+
2355+ var banner = Y.one('.banner');
2356+ Y.Assert.areEqual(
2357+ msg,
2358+ banner.one('.banner-content').get('text')
2359+ );
2360+ },
2361+
2362+ test_render_private_type: function () {
2363+ var msg = 'Private!',
2364+ b = new ns.Banner({
2365+ content: msg,
2366+ banner_type: ns.PRIVATE
2367+ });
2368+
2369+ b.render(this.container);
2370+
2371+ var banner = Y.one('.banner');
2372+ Y.Assert.areEqual(
2373+ msg,
2374+ banner.one('.banner-content').get('text'),
2375+ 'The banner should have the private message.'
2376+ );
2377+
2378+ var badge = banner.one('.badge');
2379+
2380+ Y.Assert.isTrue(
2381+ badge.hasClass('private'),
2382+ 'The badge should have a private class on it.');
2383+
2384+ },
2385+
2386+ test_render_beta_type: function () {
2387+ var msg = 'BETA!',
2388+ b = new ns.Banner({
2389+ content: msg,
2390+ banner_type: ns.BETA
2391+ });
2392+
2393+ b.render(this.container);
2394+
2395+ var banner = Y.one('.banner');
2396+ Y.Assert.areEqual(
2397+ msg,
2398+ banner.one('.banner-content').get('text'),
2399+ 'The banner should have the beta message.'
2400+ );
2401+
2402+ var badge = banner.one('.badge');
2403+
2404+ Y.Assert.isTrue(
2405+ badge.hasClass('beta'),
2406+ 'The badge should have a beta class on it.');
2407+ },
2408+
2409+ test_render_badge_text: function () {
2410+ // We can set the badge to contain text.
2411+ var badge = 'BETA!',
2412+ b = new ns.Banner({
2413+ badge_text: badge,
2414+ banner_type: ns.BETA
2415+ });
2416+
2417+ b.render(this.container);
2418+ var banner = Y.one('.banner');
2419+ Y.Assert.areEqual(
2420+ badge,
2421+ banner.one('.badge').get('text'),
2422+ 'The badge should have the beta message.'
2423+ );
2424+ },
2425+
2426+ test_banner_text_update: function () {
2427+ // The banner should update the rendered text when the content
2428+ // ATTR is changed.
2429+ var msg = 'This is a banner message. Fear me.',
2430+ b = new ns.Banner({
2431+ content: msg
2432+ });
2433+
2434+ b.render(this.container);
2435+
2436+ var banner = Y.one('.banner');
2437+ Y.Assert.areEqual(
2438+ msg,
2439+ banner.one('.banner-content').get('text')
2440+ );
2441+
2442+ // Now change the content on the widget and check again.
2443+ var new_msg = 'Updated me!';
2444+ b.set('content', new_msg);
2445+ banner = Y.one('.banner');
2446+ Y.Assert.areEqual(
2447+ new_msg,
2448+ banner.one('.banner-content').get('text')
2449+ );
2450+ }
2451+ }));
2452+
2453+
2454+ tests.suite.add(new Y.Test.Case({
2455+ name: 'ui.beta_banner_tests',
2456+
2457+ setUp: function () {
2458+ this.container = Y.one('#fixture');
2459+ },
2460+
2461+ tearDown: function () {
2462+ this.container.empty();
2463+ },
2464+
2465+ test_base_beta_banner: function () {
2466+ // The beta banner is auto set to the right type, has the right
2467+ // badge text.
2468+ var badge = 'BETA!',
2469+ msg = 'are in beta:',
2470+ b = new ns.BetaBanner({
2471+ });
2472+
2473+ b.render(this.container);
2474+ var banner = Y.one('.banner');
2475+ Y.Assert.areEqual(
2476+ badge,
2477+ banner.one('.badge').get('text'),
2478+ 'The badge should have the beta message.'
2479+ );
2480+
2481+ Y.Assert.areEqual(
2482+ ns.BETA,
2483+ b.get('banner_type'),
2484+ 'The banner should be the right type.'
2485+ );
2486+
2487+ Y.Assert.areNotEqual(
2488+ -1,
2489+ banner.one('.banner-content').get('text').indexOf(msg),
2490+ 'The badge should have beta content.'
2491+ );
2492+ },
2493+
2494+ test_beta_features: function () {
2495+ // The features fed to the banner effect display of the messages.
2496+ var features = {
2497+ private_projects: {
2498+ is_beta: true,
2499+ title: "Private Projects",
2500+ url: "http://blog.ld.net/general/private-projects-beta",
2501+ value: "true"
2502+ },
2503+ test_projects: {
2504+ is_beta: true,
2505+ title: "Test Projects",
2506+ url: "http://blog.ld.net/general/private-projects-beta",
2507+ value: "true"
2508+ },
2509+ no_beta: {
2510+ is_beta: false,
2511+ title: "Better not see me",
2512+ url: "http://blog.ld.net/general/private-projects-beta",
2513+ value: "true"
2514+ }
2515+ };
2516+
2517+ var b = new ns.BetaBanner({
2518+ features: features
2519+ });
2520+
2521+ b.render(this.container);
2522+
2523+ var banner = Y.one('.banner'),
2524+ banner_content = banner.one('.banner-content').get('text');
2525+
2526+ Y.Assert.areNotEqual(
2527+ -1,
2528+ banner_content.indexOf(features.private_projects.title),
2529+ 'The private projects feature should be displayed.'
2530+ );
2531+
2532+ Y.Assert.areNotEqual(
2533+ -1,
2534+ banner_content.indexOf(features.test_projects.title),
2535+ 'Also test projects since we support multiple features.'
2536+ );
2537+
2538+ Y.Assert.areEqual(
2539+ -1,
2540+ banner_content.indexOf(features.no_beta.title),
2541+ 'But not no beta since we only support beta features.'
2542+ );
2543+ }
2544+ }));
2545+
2546+
2547+ tests.suite.add(new Y.Test.Case({
2548+ name: 'ui.private_banner_tests',
2549+
2550+ setUp: function () {
2551+ this.container = Y.one('#fixture');
2552+ },
2553+
2554+ tearDown: function () {
2555+ this.container.empty();
2556+ },
2557+
2558+ test_base_private_banner: function () {
2559+ // The private banner is auto set to the right type, has the right
2560+ // badge text.
2561+ var badge = '',
2562+ msg = 'page is private',
2563+ b = new ns.PrivateBanner({
2564+ });
2565+
2566+ b.render(this.container);
2567+ var banner = Y.one('.banner');
2568+ Y.Assert.areEqual(
2569+ badge,
2570+ banner.one('.badge').get('text'),
2571+ 'The badge should be empty'
2572+ );
2573+
2574+ Y.Assert.areEqual(
2575+ ns.PRIVATE,
2576+ b.get('banner_type'),
2577+ 'The banner should be the right type.'
2578+ );
2579+
2580+ Y.Assert.areNotEqual(
2581+ -1,
2582+ banner.one('.banner-content').get('text').indexOf(msg),
2583+ 'The badge should have a private warning.'
2584+ );
2585+ }
2586+ }));
2587+
2588+}, '0.1', {
2589+ requires: ['test', 'lp.testing.helpers', 'lp.ui.banner']
2590+});
2591
2592=== removed file 'lib/lp/app/javascript/ui/tests/test_banner.js'
2593--- lib/lp/app/javascript/ui/tests/test_banner.js 2012-11-09 18:55:02 +0000
2594+++ lib/lp/app/javascript/ui/tests/test_banner.js 1970-01-01 00:00:00 +0000
2595@@ -1,287 +0,0 @@
2596-/* Copyright (c) 2012 Canonical Ltd. All rights reserved. */
2597-
2598-YUI.add('lp.ui.banner.test', function (Y) {
2599-
2600- var tests = Y.namespace('lp.ui.banner.test');
2601- tests.suite = new Y.Test.Suite('ui.banner Tests');
2602-
2603- var ns = Y.lp.ui.banner;
2604-
2605- tests.suite.add(new Y.Test.Case({
2606- name: 'ui.banner_tests',
2607-
2608- setUp: function () {
2609- this.container = Y.one('#fixture');
2610- },
2611-
2612- tearDown: function () {
2613- this.container.empty();
2614- },
2615-
2616- test_library_exists: function () {
2617- Y.Assert.isObject(Y.lp.ui.banner,
2618- "Could not locate the lp.ui.banner module");
2619- },
2620-
2621- test_render: function () {
2622- var b = new ns.Banner();
2623- b.render(this.container);
2624-
2625- var banners = Y.all('.banner');
2626- Y.Assert.areEqual(
2627- 1,
2628- banners._nodes.length,
2629- 'We have one banner node');
2630-
2631- // The banner should make sure it's in the container as well.
2632- var contained_banners = Y.all('#fixture .banner');
2633- Y.Assert.areEqual(
2634- 1,
2635- contained_banners._nodes.length,
2636- 'Banner node is placed.');
2637- },
2638-
2639- test_render_content: function () {
2640- var msg = 'This is a banner message. Fear me.',
2641- b = new ns.Banner({
2642- content: msg
2643- });
2644-
2645- b.render(this.container);
2646-
2647- var banner = Y.one('.banner');
2648- Y.Assert.areEqual(
2649- msg,
2650- banner.one('.banner-content').get('text')
2651- );
2652- },
2653-
2654- test_render_private_type: function () {
2655- var msg = 'Private!',
2656- b = new ns.Banner({
2657- content: msg,
2658- banner_type: ns.PRIVATE
2659- });
2660-
2661- b.render(this.container);
2662-
2663- var banner = Y.one('.banner');
2664- Y.Assert.areEqual(
2665- msg,
2666- banner.one('.banner-content').get('text'),
2667- 'The banner should have the private message.'
2668- );
2669-
2670- var badge = banner.one('.badge');
2671-
2672- Y.Assert.isTrue(
2673- badge.hasClass('private'),
2674- 'The badge should have a private class on it.');
2675-
2676- },
2677-
2678- test_render_beta_type: function () {
2679- var msg = 'BETA!',
2680- b = new ns.Banner({
2681- content: msg,
2682- banner_type: ns.BETA
2683- });
2684-
2685- b.render(this.container);
2686-
2687- var banner = Y.one('.banner');
2688- Y.Assert.areEqual(
2689- msg,
2690- banner.one('.banner-content').get('text'),
2691- 'The banner should have the beta message.'
2692- );
2693-
2694- var badge = banner.one('.badge');
2695-
2696- Y.Assert.isTrue(
2697- badge.hasClass('beta'),
2698- 'The badge should have a beta class on it.');
2699- },
2700-
2701- test_render_badge_text: function () {
2702- // We can set the badge to contain text.
2703- var badge = 'BETA!',
2704- b = new ns.Banner({
2705- badge_text: badge,
2706- banner_type: ns.BETA
2707- });
2708-
2709- b.render(this.container);
2710- var banner = Y.one('.banner');
2711- Y.Assert.areEqual(
2712- badge,
2713- banner.one('.badge').get('text'),
2714- 'The badge should have the beta message.'
2715- );
2716- },
2717-
2718- test_banner_text_update: function () {
2719- // The banner should update the rendered text when the content
2720- // ATTR is changed.
2721- var msg = 'This is a banner message. Fear me.',
2722- b = new ns.Banner({
2723- content: msg
2724- });
2725-
2726- b.render(this.container);
2727-
2728- var banner = Y.one('.banner');
2729- Y.Assert.areEqual(
2730- msg,
2731- banner.one('.banner-content').get('text')
2732- );
2733-
2734- // Now change the content on the widget and check again.
2735- var new_msg = 'Updated me!';
2736- b.set('content', new_msg);
2737- banner = Y.one('.banner');
2738- Y.Assert.areEqual(
2739- new_msg,
2740- banner.one('.banner-content').get('text')
2741- );
2742- }
2743- }));
2744-
2745-
2746- tests.suite.add(new Y.Test.Case({
2747- name: 'ui.beta_banner_tests',
2748-
2749- setUp: function () {
2750- this.container = Y.one('#fixture');
2751- },
2752-
2753- tearDown: function () {
2754- this.container.empty();
2755- },
2756-
2757- test_base_beta_banner: function () {
2758- // The beta banner is auto set to the right type, has the right
2759- // badge text.
2760- var badge = 'BETA!',
2761- msg = 'are in beta:',
2762- b = new ns.BetaBanner({
2763- });
2764-
2765- b.render(this.container);
2766- var banner = Y.one('.banner');
2767- Y.Assert.areEqual(
2768- badge,
2769- banner.one('.badge').get('text'),
2770- 'The badge should have the beta message.'
2771- );
2772-
2773- Y.Assert.areEqual(
2774- ns.BETA,
2775- b.get('banner_type'),
2776- 'The banner should be the right type.'
2777- );
2778-
2779- Y.Assert.areNotEqual(
2780- -1,
2781- banner.one('.banner-content').get('text').indexOf(msg),
2782- 'The badge should have beta content.'
2783- );
2784- },
2785-
2786- test_beta_features: function () {
2787- // The features fed to the banner effect display of the messages.
2788- var features = {
2789- private_projects: {
2790- is_beta: true,
2791- title: "Private Projects",
2792- url: "http://blog.ld.net/general/private-projects-beta",
2793- value: "true"
2794- },
2795- test_projects: {
2796- is_beta: true,
2797- title: "Test Projects",
2798- url: "http://blog.ld.net/general/private-projects-beta",
2799- value: "true"
2800- },
2801- no_beta: {
2802- is_beta: false,
2803- title: "Better not see me",
2804- url: "http://blog.ld.net/general/private-projects-beta",
2805- value: "true"
2806- }
2807- };
2808-
2809- var b = new ns.BetaBanner({
2810- features: features
2811- });
2812-
2813- b.render(this.container);
2814-
2815- var banner = Y.one('.banner'),
2816- banner_content = banner.one('.banner-content').get('text');
2817-
2818- Y.Assert.areNotEqual(
2819- -1,
2820- banner_content.indexOf(features.private_projects.title),
2821- 'The private projects feature should be displayed.'
2822- );
2823-
2824- Y.Assert.areNotEqual(
2825- -1,
2826- banner_content.indexOf(features.test_projects.title),
2827- 'Also test projects since we support multiple features.'
2828- );
2829-
2830- Y.Assert.areEqual(
2831- -1,
2832- banner_content.indexOf(features.no_beta.title),
2833- 'But not no beta since we only support beta features.'
2834- );
2835- }
2836- }));
2837-
2838-
2839- tests.suite.add(new Y.Test.Case({
2840- name: 'ui.private_banner_tests',
2841-
2842- setUp: function () {
2843- this.container = Y.one('#fixture');
2844- },
2845-
2846- tearDown: function () {
2847- this.container.empty();
2848- },
2849-
2850- test_base_private_banner: function () {
2851- // The private banner is auto set to the right type, has the right
2852- // badge text.
2853- var badge = '',
2854- msg = 'page is private',
2855- b = new ns.PrivateBanner({
2856- });
2857-
2858- b.render(this.container);
2859- var banner = Y.one('.banner');
2860- Y.Assert.areEqual(
2861- badge,
2862- banner.one('.badge').get('text'),
2863- 'The badge should be empty'
2864- );
2865-
2866- Y.Assert.areEqual(
2867- ns.PRIVATE,
2868- b.get('banner_type'),
2869- 'The banner should be the right type.'
2870- );
2871-
2872- Y.Assert.areNotEqual(
2873- -1,
2874- banner.one('.banner-content').get('text').indexOf(msg),
2875- 'The badge should have a private warning.'
2876- );
2877- }
2878- }));
2879-
2880-}, '0.1', {
2881- requires: ['test', 'lp.testing.helpers', 'lp.ui.banner']
2882-});
2883
2884=== added directory 'lib/lp/app/javascript/views'
2885=== removed directory 'lib/lp/app/javascript/views'
2886=== added file 'lib/lp/app/javascript/views/global.js'
2887--- lib/lp/app/javascript/views/global.js 1970-01-01 00:00:00 +0000
2888+++ lib/lp/app/javascript/views/global.js 2012-11-12 13:21:28 +0000
2889@@ -0,0 +1,142 @@
2890+/*
2891+ * Copyright 2012 Canonical Ltd. This software is licensed under the
2892+ * GNU Affero General Public License version 3 (see the file LICENSE).
2893+ *
2894+ * Global View handler for Launchpad
2895+ *
2896+ * @module lp.views.Global
2897+ * @namespace lp.views
2898+ * @module global
2899+ */
2900+YUI.add('lp.views.global', function (Y) {
2901+
2902+ var ns = Y.namespace('lp.views'),
2903+ ui = Y.namespace('lp.ui'),
2904+ info_type = Y.namespace('lp.app.information_type');
2905+
2906+ /**
2907+ * Provides a Y.View that controls all of the things that need handling on
2908+ * every single request. All code currently in the base-layout-macros
2909+ * should eventually moved into here to be loaded via the render() method.
2910+ * Events bound as required, etc.
2911+ *
2912+ * @class Global
2913+ * @extends Y.View
2914+ */
2915+ ns.Global = Y.Base.create('lp-views-global', Y.View, [], {
2916+ _events: [],
2917+
2918+ /**
2919+ * Watch for page level events in all pages.
2920+ *
2921+ * @method _bind_events
2922+ */
2923+ _bind_events: function () {
2924+ var that = this;
2925+
2926+ // Watch for any changes in information type.
2927+ this._events.push(Y.on(info_type.EV_ISPUBLIC, function (ev) {
2928+ // Remove the banner if there is one.
2929+ if (that._private_banner) {
2930+ that._private_banner.hide();
2931+ that._private_banner.destroy(true);
2932+ that._private_banner = null;
2933+ // XXX: Bug #1076074
2934+ var body = Y.one('body');
2935+ body.addClass('public');
2936+ }
2937+ }));
2938+
2939+ // If the information type is changed to private, and we don't
2940+ // currently have a privacy banner, then create a new one and set
2941+ // it up.
2942+ this._events.push(Y.on(info_type.EV_ISPRIVATE, function (ev) {
2943+ // Create a Private banner if there is not currently one.
2944+ if (!that._private_banner) {
2945+ that._private_banner = new ui.banner.PrivateBanner({
2946+ content: ev.text
2947+ });
2948+
2949+ // There is no current container for the banner since
2950+ // we're creating it on the fly.
2951+ var container = Y.Node.create('<div>');
2952+
2953+ // XXX: Bug #1076074
2954+ var body = Y.one('body');
2955+ body.removeClass('public');
2956+ that._private_banner.render(container);
2957+ // Only append the content to the DOM after the rest is
2958+ // one to avoid any repaints on the browser end.
2959+ body.prepend(container);
2960+ that._private_banner.show();
2961+ } else {
2962+ // The banner is there but we need to update text.
2963+ that._private_banner.set('content', ev.text);
2964+ }
2965+ }));
2966+ },
2967+
2968+ /**
2969+ * Clean up the view and its event bindings when destroyed.
2970+ *
2971+ * @method _destroy
2972+ * @param {Event} ev
2973+ * @private
2974+ */
2975+ _destroy: function (ev) {
2976+ var index;
2977+ for (index in this._events) {
2978+ event = this._events[index];
2979+ event.detach();
2980+ }
2981+ },
2982+
2983+ _init_banners: function () {
2984+ var that = this;
2985+
2986+ // On page load the banner container already exists. This is so
2987+ // that the height of the page is already determined.
2988+ var is_beta = Y.one('.beta_banner_container');
2989+ if (is_beta) {
2990+ that._beta_banner = new ui.banner.BetaBanner({
2991+ features: LP.cache.related_features
2992+ });
2993+ that._beta_banner.render(is_beta);
2994+ // We delay the show until the page is ready so we get our
2995+ // pretty css3 animation that distracts the user a bit.
2996+ Y.after('load', function (ev) {
2997+ that._beta_banner.show();
2998+ });
2999+ }
3000+
3001+ // On page load the banner container already exists. This is so
3002+ // that the height of the page is already determined.
3003+ var is_private = Y.one('.private_banner_container');
3004+ if (is_private) {
3005+ that._private_banner = new ui.banner.PrivateBanner();
3006+ that._private_banner.render(is_private);
3007+ // We delay the show until the page is ready so we get our
3008+ // pretty css3 animation that distracts the user a bit.
3009+ Y.on('load', function (ev) {
3010+ that._private_banner.show();
3011+ });
3012+ }
3013+ },
3014+
3015+ initialize: function (cfg) {},
3016+
3017+ render: function () {
3018+ this._bind_events();
3019+ this.on('destroy', this._destroy, this);
3020+ this._init_banners();
3021+ }
3022+
3023+ }, {
3024+ ATTRS: {
3025+
3026+ }
3027+ });
3028+
3029+}, '0.1', {
3030+ requires: ['base', 'view', 'lp.ui.banner', 'lp.app.information_type']
3031+});
3032
3033=== removed file 'lib/lp/app/javascript/views/global.js'
3034--- lib/lp/app/javascript/views/global.js 2012-11-09 18:46:31 +0000
3035+++ lib/lp/app/javascript/views/global.js 1970-01-01 00:00:00 +0000
3036@@ -1,142 +0,0 @@
3037-/*
3038- * Copyright 2012 Canonical Ltd. This software is licensed under the
3039- * GNU Affero General Public License version 3 (see the file LICENSE).
3040- *
3041- * Global View handler for Launchpad
3042- *
3043- * @module lp.views.Global
3044- * @namespace lp.views
3045- * @module global
3046- */
3047-YUI.add('lp.views.global', function (Y) {
3048-
3049- var ns = Y.namespace('lp.views'),
3050- ui = Y.namespace('lp.ui'),
3051- info_type = Y.namespace('lp.app.information_type');
3052-
3053- /**
3054- * Provides a Y.View that controls all of the things that need handling on
3055- * every single request. All code currently in the base-layout-macros
3056- * should eventually moved into here to be loaded via the render() method.
3057- * Events bound as required, etc.
3058- *
3059- * @class Global
3060- * @extends Y.View
3061- */
3062- ns.Global = Y.Base.create('lp-views-global', Y.View, [], {
3063- _events: [],
3064-
3065- /**
3066- * Watch for page level events in all pages.
3067- *
3068- * @method _bind_events
3069- */
3070- _bind_events: function () {
3071- var that = this;
3072-
3073- // Watch for any changes in information type.
3074- this._events.push(Y.on(info_type.EV_ISPUBLIC, function (ev) {
3075- // Remove the banner if there is one.
3076- if (that._private_banner) {
3077- that._private_banner.hide();
3078- that._private_banner.destroy(true);
3079- that._private_banner = null;
3080- // XXX: Bug #1076074
3081- var body = Y.one('body');
3082- body.addClass('public');
3083- }
3084- }));
3085-
3086- // If the information type is changed to private, and we don't
3087- // currently have a privacy banner, then create a new one and set
3088- // it up.
3089- this._events.push(Y.on(info_type.EV_ISPRIVATE, function (ev) {
3090- // Create a Private banner if there is not currently one.
3091- if (!that._private_banner) {
3092- that._private_banner = new ui.banner.PrivateBanner({
3093- content: ev.text
3094- });
3095-
3096- // There is no current container for the banner since
3097- // we're creating it on the fly.
3098- var container = Y.Node.create('<div>');
3099-
3100- // XXX: Bug #1076074
3101- var body = Y.one('body');
3102- body.removeClass('public');
3103- that._private_banner.render(container);
3104- // Only append the content to the DOM after the rest is
3105- // one to avoid any repaints on the browser end.
3106- body.prepend(container);
3107- that._private_banner.show();
3108- } else {
3109- // The banner is there but we need to update text.
3110- that._private_banner.set('content', ev.text);
3111- }
3112- }));
3113- },
3114-
3115- /**
3116- * Clean up the view and its event bindings when destroyed.
3117- *
3118- * @method _destroy
3119- * @param {Event} ev
3120- * @private
3121- */
3122- _destroy: function (ev) {
3123- var index;
3124- for (index in this._events) {
3125- event = this._events[index];
3126- event.detach();
3127- }
3128- },
3129-
3130- _init_banners: function () {
3131- var that = this;
3132-
3133- // On page load the banner container already exists. This is so
3134- // that the height of the page is already determined.
3135- var is_beta = Y.one('.beta_banner_container');
3136- if (is_beta) {
3137- that._beta_banner = new ui.banner.BetaBanner({
3138- features: LP.cache.related_features
3139- });
3140- that._beta_banner.render(is_beta);
3141- // We delay the show until the page is ready so we get our
3142- // pretty css3 animation that distracts the user a bit.
3143- Y.after('load', function (ev) {
3144- that._beta_banner.show();
3145- });
3146- }
3147-
3148- // On page load the banner container already exists. This is so
3149- // that the height of the page is already determined.
3150- var is_private = Y.one('.private_banner_container');
3151- if (is_private) {
3152- that._private_banner = new ui.banner.PrivateBanner();
3153- that._private_banner.render(is_private);
3154- // We delay the show until the page is ready so we get our
3155- // pretty css3 animation that distracts the user a bit.
3156- Y.on('load', function (ev) {
3157- that._private_banner.show();
3158- });
3159- }
3160- },
3161-
3162- initialize: function (cfg) {},
3163-
3164- render: function () {
3165- this._bind_events();
3166- this.on('destroy', this._destroy, this);
3167- this._init_banners();
3168- }
3169-
3170- }, {
3171- ATTRS: {
3172-
3173- }
3174- });
3175-
3176-}, '0.1', {
3177- requires: ['base', 'view', 'lp.ui.banner', 'lp.app.information_type']
3178-});
3179
3180=== added directory 'lib/lp/app/javascript/views/tests'
3181=== removed directory 'lib/lp/app/javascript/views/tests'
3182=== added file 'lib/lp/app/javascript/views/tests/test_global.html'
3183--- lib/lp/app/javascript/views/tests/test_global.html 1970-01-01 00:00:00 +0000
3184+++ lib/lp/app/javascript/views/tests/test_global.html 2012-11-12 13:21:28 +0000
3185@@ -0,0 +1,62 @@
3186+<!DOCTYPE html>
3187+
3188+<!--
3189+Copyright 2012 Canonical Ltd. This software is licensed under the
3190+GNU Affero General Public License version 3 (see the file LICENSE).
3191+-->
3192+
3193+<html>
3194+ <head>
3195+ <title>Product New Tests</title>
3196+
3197+ <!-- YUI and test setup -->
3198+ <script type="text/javascript"
3199+ src="../../../../../../build/js/yui/yui/yui.js">
3200+ </script>
3201+ <link rel="stylesheet"
3202+ href="../../../../../../build/js/yui/console/assets/console-core.css" />
3203+ <link rel="stylesheet"
3204+ href="../../../../../../build/js/yui/test-console/assets/skins/sam/test-console.css" />
3205+ <link rel="stylesheet"
3206+ href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
3207+
3208+ <script type="text/javascript"
3209+ src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
3210+
3211+ <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
3212+
3213+ <!-- Dependencies -->
3214+ <script type="text/javascript" src="../../../../../../build/js/lp/app/client.js"></script>
3215+ <script type="text/javascript" src="../../../../../../build/js/lp/app/choice.js"></script>
3216+ <script type="text/javascript" src="../../../../../../build/js/lp/app/ellipsis.js"></script>
3217+ <script type="text/javascript" src="../../../../../../build/js/lp/app/expander.js"></script>
3218+ <script type="text/javascript" src="../../../../../../build/js/lp/app/errors.js"></script>
3219+ <script type="text/javascript" src="../../../../../../build/js/lp/app/information_type.js"></script>
3220+ <script type="text/javascript" src="../../../../../../build/js/lp/app/lp.js"></script>
3221+ <script type="text/javascript" src="../../../../../../build/js/lp/app/mustache.js"></script>
3222+ <script type="text/javascript" src="../../../../../../build/js/lp/app/anim/anim.js"></script>
3223+ <script type="text/javascript" src="../../../../../../build/js/lp/app/extras/extras.js"></script>
3224+ <script type="text/javascript" src="../../../../../../build/js/lp/app/choiceedit/choiceedit.js"></script>
3225+ <script type="text/javascript" src="../../../../../../build/js/lp/app/effects/effects.js"></script>
3226+ <script type="text/javascript" src="../../../../../../build/js/lp/app/formoverlay/formoverlay.js"></script>
3227+ <script type="text/javascript" src="../../../../../../build/js/lp/app/formwidgets/resizing_textarea.js"></script>
3228+ <script type="text/javascript" src="../../../../../../build/js/lp/app/inlineedit/editor.js"></script>
3229+ <script type="text/javascript" src="../../../../../../build/js/lp/app/testing/helpers.js"></script>
3230+ <script type="text/javascript" src="../../../../../../build/js/lp/app/overlay/overlay.js"></script>
3231+ <script type="text/javascript" src="../../../../../../build/js/lp/app/ui/ui.js"></script>
3232+ <script type="text/javascript" src="../../../../../../build/js/lp/app/ui/banner.js"></script>
3233+
3234+ <!-- The module under test. -->
3235+ <script type="text/javascript" src="../global.js"></script>
3236+
3237+ <!-- The test suite -->
3238+ <script type="text/javascript" src="test_global.js"></script>
3239+
3240+ </head>
3241+ <body class="yui3-skin-sam">
3242+ <ul id="suites">
3243+ <li>lp.views.global.test</li>
3244+ </ul>
3245+ <div id="fixture"></div>
3246+ </body>
3247+</html>
3248
3249=== removed file 'lib/lp/app/javascript/views/tests/test_global.html'
3250--- lib/lp/app/javascript/views/tests/test_global.html 2012-11-09 18:46:31 +0000
3251+++ lib/lp/app/javascript/views/tests/test_global.html 1970-01-01 00:00:00 +0000
3252@@ -1,62 +0,0 @@
3253-<!DOCTYPE html>
3254-
3255-<!--
3256-Copyright 2012 Canonical Ltd. This software is licensed under the
3257-GNU Affero General Public License version 3 (see the file LICENSE).
3258--->
3259-
3260-<html>
3261- <head>
3262- <title>Product New Tests</title>
3263-
3264- <!-- YUI and test setup -->
3265- <script type="text/javascript"
3266- src="../../../../../../build/js/yui/yui/yui.js">
3267- </script>
3268- <link rel="stylesheet"
3269- href="../../../../../../build/js/yui/console/assets/console-core.css" />
3270- <link rel="stylesheet"
3271- href="../../../../../../build/js/yui/test-console/assets/skins/sam/test-console.css" />
3272- <link rel="stylesheet"
3273- href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
3274-
3275- <script type="text/javascript"
3276- src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
3277-
3278- <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
3279-
3280- <!-- Dependencies -->
3281- <script type="text/javascript" src="../../../../../../build/js/lp/app/client.js"></script>
3282- <script type="text/javascript" src="../../../../../../build/js/lp/app/choice.js"></script>
3283- <script type="text/javascript" src="../../../../../../build/js/lp/app/ellipsis.js"></script>
3284- <script type="text/javascript" src="../../../../../../build/js/lp/app/expander.js"></script>
3285- <script type="text/javascript" src="../../../../../../build/js/lp/app/errors.js"></script>
3286- <script type="text/javascript" src="../../../../../../build/js/lp/app/information_type.js"></script>
3287- <script type="text/javascript" src="../../../../../../build/js/lp/app/lp.js"></script>
3288- <script type="text/javascript" src="../../../../../../build/js/lp/app/mustache.js"></script>
3289- <script type="text/javascript" src="../../../../../../build/js/lp/app/anim/anim.js"></script>
3290- <script type="text/javascript" src="../../../../../../build/js/lp/app/extras/extras.js"></script>
3291- <script type="text/javascript" src="../../../../../../build/js/lp/app/choiceedit/choiceedit.js"></script>
3292- <script type="text/javascript" src="../../../../../../build/js/lp/app/effects/effects.js"></script>
3293- <script type="text/javascript" src="../../../../../../build/js/lp/app/formoverlay/formoverlay.js"></script>
3294- <script type="text/javascript" src="../../../../../../build/js/lp/app/formwidgets/resizing_textarea.js"></script>
3295- <script type="text/javascript" src="../../../../../../build/js/lp/app/inlineedit/editor.js"></script>
3296- <script type="text/javascript" src="../../../../../../build/js/lp/app/testing/helpers.js"></script>
3297- <script type="text/javascript" src="../../../../../../build/js/lp/app/overlay/overlay.js"></script>
3298- <script type="text/javascript" src="../../../../../../build/js/lp/app/ui/ui.js"></script>
3299- <script type="text/javascript" src="../../../../../../build/js/lp/app/ui/banner.js"></script>
3300-
3301- <!-- The module under test. -->
3302- <script type="text/javascript" src="../global.js"></script>
3303-
3304- <!-- The test suite -->
3305- <script type="text/javascript" src="test_global.js"></script>
3306-
3307- </head>
3308- <body class="yui3-skin-sam">
3309- <ul id="suites">
3310- <li>lp.views.global.test</li>
3311- </ul>
3312- <div id="fixture"></div>
3313- </body>
3314-</html>
3315
3316=== added file 'lib/lp/app/javascript/views/tests/test_global.js'
3317--- lib/lp/app/javascript/views/tests/test_global.js 1970-01-01 00:00:00 +0000
3318+++ lib/lp/app/javascript/views/tests/test_global.js 2012-11-12 13:21:28 +0000
3319@@ -0,0 +1,164 @@
3320+/* Copyright (c) 2012 Canonical Ltd. All rights reserved. */
3321+
3322+YUI.add('lp.views.global.test', function (Y) {
3323+ var tests = Y.namespace('lp.views.global.test');
3324+ var info_type = Y.namespace('lp.app.information_type');
3325+ var ns = Y.lp.views;
3326+
3327+ tests.suite = new Y.Test.Suite('lp.views.global test');
3328+ tests.suite.add(new Y.Test.Case({
3329+ name: 'lp.views.global',
3330+
3331+ setUp: function () {
3332+ this.container = Y.one('#fixture');
3333+ window.LP = {
3334+ cache: {
3335+ related_features: {
3336+ private_projects: {
3337+ is_beta: true,
3338+ title: "Private Projects",
3339+ url: "http://blog.ld.net/private-projects-beta",
3340+ value: "true"
3341+ }
3342+ },
3343+ information_type_data: {
3344+ PUBLIC: {
3345+ value: 'PUBLIC', name: 'Public',
3346+ is_private: false, order: 1,
3347+ description: 'Public Description'
3348+ },
3349+ EMBARGOED: {
3350+ value: 'EMBARGOED', name: 'Embargoed',
3351+ is_private: true, order: 2,
3352+ description: 'Something embargoed'
3353+ },
3354+ PROPRIETARY: {
3355+ value: 'PROPRIETARY', name: 'Proprietary',
3356+ is_private: true, order: 3,
3357+ description: 'Private Description'
3358+ }
3359+ }
3360+ }
3361+ };
3362+ },
3363+
3364+ tearDown: function () {
3365+ this.container.empty();
3366+ },
3367+
3368+ test_library_exists: function () {
3369+ Y.Assert.isObject(ns.Global,
3370+ "Could not locate the lp.views.global module");
3371+ },
3372+
3373+ test_basic_render: function () {
3374+ // Nothing is currently rendered out by default.
3375+ var view = new ns.Global();
3376+ view.render();
3377+
3378+ Y.Assert.areEqual(
3379+ '',
3380+ this.container.get('innerHTML'),
3381+ 'The container is still empty.');
3382+ view.destroy();
3383+ },
3384+
3385+ test_beta_banner: function () {
3386+ // If we've prepped on load a beta banner will auto appear.
3387+ var banner_container = Y.Node.create('<div/>');
3388+ banner_container.addClass('beta_banner_container');
3389+ this.container.append(banner_container);
3390+ var view = new ns.Global();
3391+ view.render();
3392+
3393+ // We have to wait until after page load event fires to test
3394+ // things out.
3395+ var banner_node = Y.one('.banner');
3396+ Y.Assert.isObject(
3397+ banner_node,
3398+ 'The container has a new banner node in there.');
3399+
3400+ view.destroy();
3401+ },
3402+
3403+ test_privacy: function () {
3404+ var beta_container = Y.Node.create('<div/>');
3405+ var private_container = Y.Node.create('<div/>');
3406+
3407+ beta_container.addClass('beta_banner_container');
3408+ private_container.addClass('private_banner_container');
3409+
3410+ this.container.append(beta_container);
3411+ this.container.append(private_container);
3412+ var view = new ns.Global();
3413+ view.render();
3414+
3415+ // We have to wait until after page load event fires to test
3416+ // things out.
3417+ var banner_nodes = Y.all('.banner');
3418+ Y.Assert.areEqual(
3419+ 2,
3420+ banner_nodes._nodes.length,
3421+ 'We should have two banners rendered.');
3422+
3423+ view.destroy();
3424+ },
3425+
3426+ test_privacy_banner_from_event: function () {
3427+ // We can also get a privacy banner via a fired event.
3428+ // This is hard coded to the <body> tag so we have to do some
3429+ // manual clean up here.
3430+ var view = new ns.Global();
3431+ view.render();
3432+
3433+ var msg = 'Testing Global';
3434+ Y.fire(info_type.EV_ISPRIVATE, {
3435+ text: msg
3436+ });
3437+
3438+ var banner = Y.one('.banner');
3439+ var banner_text = banner.one('.banner-content').get('text');
3440+ Y.Assert.areNotEqual(
3441+ -1,
3442+ banner_text.indexOf(msg),
3443+ 'The event text is turned into the banner content');
3444+
3445+ // Manually clean up.
3446+ Y.one('.yui3-banner').remove(true);
3447+ view.destroy();
3448+ },
3449+
3450+ test_banner_updates_content: function () {
3451+ // If we change our privacy information type the banner needs to
3452+ // update the content to the new text value from the event.
3453+ var view = new ns.Global();
3454+ view.render();
3455+
3456+ var msg = 'Testing Global';
3457+ Y.fire(info_type.EV_ISPRIVATE, {
3458+ text: msg
3459+ });
3460+
3461+ var updated_msg = 'Updated content';
3462+ Y.fire(info_type.EV_ISPRIVATE, {
3463+ text: updated_msg
3464+ });
3465+
3466+ var banner = Y.one('.banner');
3467+ var banner_text = banner.one('.banner-content').get('text');
3468+ Y.Assert.areNotEqual(
3469+ -1,
3470+ banner_text.indexOf(updated_msg),
3471+ 'The banner updated content to the second event message.');
3472+
3473+ // Manually clean up.
3474+ Y.one('.yui3-banner').remove(true);
3475+ view.destroy();
3476+
3477+ }
3478+ }));
3479+
3480+}, '0.1', {
3481+ requires: ['test', 'event-simulate', 'node-event-simulate',
3482+ 'lp.app.information_type', 'lp.views.global']
3483+});
3484
3485=== removed file 'lib/lp/app/javascript/views/tests/test_global.js'
3486--- lib/lp/app/javascript/views/tests/test_global.js 2012-11-09 18:55:02 +0000
3487+++ lib/lp/app/javascript/views/tests/test_global.js 1970-01-01 00:00:00 +0000
3488@@ -1,164 +0,0 @@
3489-/* Copyright (c) 2012 Canonical Ltd. All rights reserved. */
3490-
3491-YUI.add('lp.views.global.test', function (Y) {
3492- var tests = Y.namespace('lp.views.global.test');
3493- var info_type = Y.namespace('lp.app.information_type');
3494- var ns = Y.lp.views;
3495-
3496- tests.suite = new Y.Test.Suite('lp.views.global test');
3497- tests.suite.add(new Y.Test.Case({
3498- name: 'lp.views.global',
3499-
3500- setUp: function () {
3501- this.container = Y.one('#fixture');
3502- window.LP = {
3503- cache: {
3504- related_features: {
3505- private_projects: {
3506- is_beta: true,
3507- title: "Private Projects",
3508- url: "http://blog.ld.net/general/beta",
3509- value: "true"
3510- }
3511- },
3512- information_type_data: {
3513- PUBLIC: {
3514- value: 'PUBLIC', name: 'Public',
3515- is_private: false, order: 1,
3516- description: 'Public Description'
3517- },
3518- EMBARGOED: {
3519- value: 'EMBARGOED', name: 'Embargoed',
3520- is_private: true, order: 2,
3521- description: 'Something embargoed'
3522- },
3523- PROPRIETARY: {
3524- value: 'PROPRIETARY', name: 'Proprietary',
3525- is_private: true, order: 3,
3526- description: 'Private Description'
3527- }
3528- }
3529- }
3530- };
3531- },
3532-
3533- tearDown: function () {
3534- this.container.empty();
3535- },
3536-
3537- test_library_exists: function () {
3538- Y.Assert.isObject(ns.Global,
3539- "Could not locate the lp.views.global module");
3540- },
3541-
3542- test_basic_render: function () {
3543- // Nothing is currently rendered out by default.
3544- var view = new ns.Global();
3545- view.render();
3546-
3547- Y.Assert.areEqual(
3548- '',
3549- this.container.get('innerHTML'),
3550- 'The container is still empty.');
3551- view.destroy();
3552- },
3553-
3554- test_beta_banner: function () {
3555- // If we've prepped on load a beta banner will auto appear.
3556- var banner_container = Y.Node.create('<div/>');
3557- banner_container.addClass('beta_banner_container');
3558- this.container.append(banner_container);
3559- var view = new ns.Global();
3560- view.render();
3561-
3562- // We have to wait until after page load event fires to test
3563- // things out.
3564- var banner_node = Y.one('.banner');
3565- Y.Assert.isObject(
3566- banner_node,
3567- 'The container has a new banner node in there.');
3568-
3569- view.destroy();
3570- },
3571-
3572- test_privacy: function () {
3573- var beta_container = Y.Node.create('<div/>');
3574- var private_container = Y.Node.create('<div/>');
3575-
3576- beta_container.addClass('beta_banner_container');
3577- private_container.addClass('private_banner_container');
3578-
3579- this.container.append(beta_container);
3580- this.container.append(private_container);
3581- var view = new ns.Global();
3582- view.render();
3583-
3584- // We have to wait until after page load event fires to test
3585- // things out.
3586- var banner_nodes = Y.all('.banner');
3587- Y.Assert.areEqual(
3588- 2,
3589- banner_nodes._nodes.length,
3590- 'We should have two banners rendered.');
3591-
3592- view.destroy();
3593- },
3594-
3595- test_privacy_banner_from_event: function () {
3596- // We can also get a privacy banner via a fired event.
3597- // This is hard coded to the <body> tag so we have to do some
3598- // manual clean up here.
3599- var view = new ns.Global();
3600- view.render();
3601-
3602- var msg = 'Testing Global';
3603- Y.fire(info_type.EV_ISPRIVATE, {
3604- text: msg
3605- });
3606-
3607- var banner = Y.one('.banner');
3608- var banner_text = banner.one('.banner-content').get('text');
3609- Y.Assert.areNotEqual(
3610- -1,
3611- banner_text.indexOf(msg),
3612- 'The event text is turned into the banner content');
3613-
3614- // Manually clean up.
3615- Y.one('.yui3-banner').remove(true);
3616- view.destroy();
3617- },
3618-
3619- test_banner_updates_content: function () {
3620- // If we change our privacy information type the banner needs to
3621- // update the content to the new text value from the event.
3622- var view = new ns.Global();
3623- view.render();
3624-
3625- var msg = 'Testing Global';
3626- Y.fire(info_type.EV_ISPRIVATE, {
3627- text: msg
3628- });
3629-
3630- var updated_msg = 'Updated content';
3631- Y.fire(info_type.EV_ISPRIVATE, {
3632- text: updated_msg
3633- });
3634-
3635- var banner = Y.one('.banner');
3636- var banner_text = banner.one('.banner-content').get('text');
3637- Y.Assert.areNotEqual(
3638- -1,
3639- banner_text.indexOf(updated_msg),
3640- 'The banner updated content to the second event message.');
3641-
3642- // Manually clean up.
3643- Y.one('.yui3-banner').remove(true);
3644- view.destroy();
3645-
3646- }
3647- }));
3648-
3649-}, '0.1', {
3650- requires: ['test', 'event-simulate', 'node-event-simulate',
3651- 'lp.app.information_type', 'lp.views.global']
3652-});
3653
3654=== modified file 'lib/lp/app/templates/base-layout-macros.pt'
3655--- lib/lp/app/templates/base-layout-macros.pt 2012-08-13 17:49:31 +0000
3656+++ lib/lp/app/templates/base-layout-macros.pt 2012-11-12 13:21:28 +0000
3657@@ -163,19 +163,17 @@
3658 <script id="base-layout-load-scripts" type="text/javascript">
3659 //<![CDATA[
3660 LPJS.use('base', 'node', 'console', 'event',
3661- 'oop', 'lp', 'lp.app.banner.privacy',
3662- 'lp.app.banner.beta', 'lp.app.foldables','lp.app.sorttable',
3663+ 'oop', 'lp', 'lp.app.foldables','lp.app.sorttable',
3664 'lp.app.inlinehelp', 'lp.app.links', 'lp.app.longpoll',
3665 'lp.bugs.bugtask_index', 'lp.bugs.subscribers',
3666 'lp.app.ellipsis', 'lp.code.branchmergeproposal.diff',
3667+ 'lp.views.global',
3668 function(Y) {
3669
3670 Y.on("domready", function () {
3671- if (Y.one(document.body).hasClass('private')) {
3672- banner = Y.lp.app.banner.privacy.getPrivacyBanner();
3673- banner.show();
3674- }
3675- Y.lp.app.banner.beta.show_beta_if_needed();
3676+ var global_view = new Y.lp.views.Global();
3677+ global_view.render();
3678+
3679 Y.lp.app.sorttable.SortTable.init();
3680 Y.lp.app.inlinehelp.init_help();
3681 Y.lp.activate_collapsibles();
3682
3683=== modified file 'lib/lp/app/templates/base-layout.pt'
3684--- lib/lp/app/templates/base-layout.pt 2012-05-18 22:04:13 +0000
3685+++ lib/lp/app/templates/base-layout.pt 2012-11-12 13:21:28 +0000
3686@@ -64,6 +64,12 @@
3687 ${view/macro:pagetype}
3688 ${view/fmt:global-css}
3689 yui3-skin-sam">
3690+ <tal:beta-banner condition="view/beta_features">
3691+ <div class="beta_banner_container"></div>
3692+ </tal:beta-banner>
3693+ <tal:private-banner condition="view/private">
3694+ <div class="private_banner_container"></div>
3695+ </tal:private-banner>
3696 <script type="text/javascript"
3697 tal:condition="python: is_lpnet">
3698 var _gaq = _gaq || [];
3699@@ -97,10 +103,8 @@
3700 <div class="yui-t4"
3701 tal:omit-tag="not: view/macro:pagehas/portlets">
3702 <div id="maincontent" class="yui-main">
3703- <metal:privacy-banner
3704- use-macro="view/@@+banner-macros/beta-banner"/>
3705- <metal:privacy-banner
3706- use-macro="view/@@+banner-macros/privacy-banner"/>
3707+ <!-- <metal:privacy-banner -->
3708+ <!-- use-macro="view/@@+banner-macros/privacy-banner"/> -->
3709 <div class="yui-b"
3710 tal:attributes="
3711 lang view/lang|default_language|default;
3712
3713=== modified file 'lib/lp/archivepublisher/scripts/generate_contents_files.py'
3714--- lib/lp/archivepublisher/scripts/generate_contents_files.py 2012-11-10 02:25:07 +0000
3715+++ lib/lp/archivepublisher/scripts/generate_contents_files.py 2012-11-12 13:21:28 +0000
3716@@ -94,6 +94,16 @@
3717 "Failure while running command: %s" % description)
3718
3719
3720+def move_file(old_path, new_path):
3721+ """Rename file `old_path` to `new_path`.
3722+
3723+ Mercilessly delete any file that may already exist at `new_path`.
3724+ """
3725+ if file_exists(new_path):
3726+ os.remove(new_path)
3727+ os.rename(old_path, new_path)
3728+
3729+
3730 class GenerateContentsFiles(LaunchpadCronScript):
3731
3732 distribution = None
3733@@ -154,7 +164,7 @@
3734 def getArchs(self, suite):
3735 """Query architectures supported by the suite."""
3736 series, _ = self.distribution.getDistroSeriesAndPocket(suite)
3737- return [arch.architecturetag for arch in series.enabled_architectures]
3738+ return [arch.architecturetag for arch in series.architectures]
3739
3740 def getDirs(self, archs):
3741 """Subdirectories needed for each component."""
3742@@ -276,8 +286,8 @@
3743 contents_dest = os.path.join(
3744 self.config.distsroot, suite, "%s.gz" % contents_filename)
3745
3746- os.rename(current_contents, last_contents)
3747- os.rename(new_contents, contents_dest)
3748+ move_file(current_contents, last_contents)
3749+ move_file(new_contents, contents_dest)
3750 os.chmod(contents_dest, 0664)
3751 else:
3752 self.logger.debug(
3753
3754=== modified file 'lib/lp/archivepublisher/scripts/generate_extra_overrides.py'
3755--- lib/lp/archivepublisher/scripts/generate_extra_overrides.py 2012-11-10 02:21:31 +0000
3756+++ lib/lp/archivepublisher/scripts/generate_extra_overrides.py 2012-11-12 13:21:28 +0000
3757@@ -367,7 +367,7 @@
3758 series_name = series.name
3759 components = self.getComponents(series)
3760 architectures = sorted(
3761- arch.architecturetag for arch in series.enabled_architectures)
3762+ [arch.architecturetag for arch in series.architectures])
3763
3764 # This takes a while. Ensure that we do it without keeping a
3765 # database transaction open.
3766
3767=== modified file 'lib/lp/archivepublisher/tests/test_generate_contents_files.py'
3768--- lib/lp/archivepublisher/tests/test_generate_contents_files.py 2012-11-10 02:25:07 +0000
3769+++ lib/lp/archivepublisher/tests/test_generate_contents_files.py 2012-11-12 13:21:28 +0000
3770@@ -14,10 +14,10 @@
3771 differ_in_content,
3772 execute,
3773 GenerateContentsFiles,
3774+ move_file,
3775 )
3776 from lp.registry.interfaces.pocket import PackagePublishingPocket
3777 from lp.services.log.logger import DevNullLogger
3778-from lp.services.osutils import write_file
3779 from lp.services.scripts.base import LaunchpadScriptFailure
3780 from lp.services.scripts.tests import run_script
3781 from lp.services.utils import file_exists
3782@@ -29,24 +29,33 @@
3783 )
3784
3785
3786+def write_file(filename, content=""):
3787+ """Write `content` to `filename`, and flush."""
3788+ output_file = file(filename, 'w')
3789+ output_file.write(content)
3790+ output_file.close()
3791+
3792+
3793 def fake_overrides(script, distroseries):
3794 """Fake overrides files so `script` can run `apt-ftparchive`."""
3795+ os.makedirs(script.config.overrideroot)
3796+
3797 components = ['main', 'restricted', 'universe', 'multiverse']
3798 architectures = script.getArchs(distroseries.name)
3799 suffixes = components + ['extra.' + component for component in components]
3800 for suffix in suffixes:
3801 write_file(os.path.join(
3802 script.config.overrideroot,
3803- "override.%s.%s" % (distroseries.name, suffix)), "")
3804+ "override.%s.%s" % (distroseries.name, suffix)))
3805
3806 for component in components:
3807 write_file(os.path.join(
3808 script.config.overrideroot,
3809- "%s_%s_source" % (distroseries.name, component)), "")
3810+ "%s_%s_source" % (distroseries.name, component)))
3811 for arch in architectures:
3812 write_file(os.path.join(
3813 script.config.overrideroot,
3814- "%s_%s_binary-%s" % (distroseries.name, component, arch)), "")
3815+ "%s_%s_binary-%s" % (distroseries.name, component, arch)))
3816
3817
3818 class TestHelpers(TestCaseWithFactory):
3819@@ -96,6 +105,24 @@
3820 execute(logger, "touch", [filename])
3821 self.assertTrue(file_exists(filename))
3822
3823+ def test_move_file_renames_file(self):
3824+ # move_file renames a file from its old name to its new name.
3825+ self.useTempDir()
3826+ text = self.factory.getUniqueString()
3827+ write_file("old_name", text)
3828+ move_file("old_name", "new_name")
3829+ self.assertEqual(text, file("new_name").read())
3830+
3831+ def test_move_file_overwrites_old_file(self):
3832+ # If move_file finds another file in the way, that file gets
3833+ # deleted.
3834+ self.useTempDir()
3835+ write_file("new_name", self.factory.getUniqueString())
3836+ new_text = self.factory.getUniqueString()
3837+ write_file("old_name", new_text)
3838+ move_file("old_name", "new_name")
3839+ self.assertEqual(new_text, file("new_name").read())
3840+
3841
3842 class TestGenerateContentsFiles(TestCaseWithFactory):
3843 """Tests for the actual `GenerateContentsFiles` script."""
3844@@ -135,6 +162,9 @@
3845 :return: The arbitrary string that is also in the file.
3846 """
3847 marker_contents = self.factory.getUniqueString()
3848+ dir_name = os.path.dirname(file_path)
3849+ if not file_exists(dir_name):
3850+ os.makedirs(dir_name)
3851 write_file(file_path, marker_contents)
3852 return marker_contents
3853
3854@@ -173,12 +203,10 @@
3855 self.assertEqual(distro, script.distribution)
3856
3857 def test_getArchs(self):
3858- # getArchs returns a list of enabled architectures in the distroseries.
3859+ # getArchs returns a list of architectures in the distroseries.
3860 distro = self.makeDistro()
3861 distroseries = self.factory.makeDistroSeries(distro)
3862 das = self.factory.makeDistroArchSeries(distroseries=distroseries)
3863- self.factory.makeDistroArchSeries(
3864- distroseries=distroseries, enabled=False)
3865 script = self.makeScript(das.distroseries.distribution)
3866 self.assertEqual(
3867 [das.architecturetag], script.getArchs(distroseries.name))
3868
3869=== modified file 'lib/lp/archivepublisher/tests/test_generate_extra_overrides.py'
3870--- lib/lp/archivepublisher/tests/test_generate_extra_overrides.py 2012-11-10 02:21:31 +0000
3871+++ lib/lp/archivepublisher/tests/test_generate_extra_overrides.py 2012-11-12 13:21:28 +0000
3872@@ -36,7 +36,6 @@
3873 from lp.services.utils import file_exists
3874 from lp.soyuz.enums import PackagePublishingStatus
3875 from lp.testing import TestCaseWithFactory
3876-from lp.testing.fakemethod import FakeMethod
3877 from lp.testing.faketransaction import FakeTransaction
3878 from lp.testing.layers import (
3879 LaunchpadZopelessLayer,
3880@@ -614,21 +613,6 @@
3881 self.assertTrue(os.path.exists(output(seed_new)))
3882 self.assertFalse(os.path.exists(output(seed_old)))
3883
3884- def test_process_skips_disabled_distroarchseries(self):
3885- # The script does not generate overrides for disabled DistroArchSeries.
3886- flavour = self.factory.getUniqueString()
3887- self.setUpDistroAndScript(extra_args=[flavour])
3888- das = self.factory.makeDistroArchSeries(
3889- distroseries=self.distroseries[0])
3890- self.factory.makeDistroArchSeries(
3891- distroseries=self.distroseries[0], enabled=False)
3892- self.script.generateExtraOverrides = FakeMethod()
3893- self.script.process()
3894- self.assertEqual(1, self.script.generateExtraOverrides.call_count)
3895- self.assertEqual(
3896- [das.architecturetag],
3897- self.script.generateExtraOverrides.calls[0][0][2])
3898-
3899 def test_main(self):
3900 # If run end-to-end, the script generates override files containing
3901 # output for all architectures, and sends germinate's log output to
3902
3903=== modified file 'lib/lp/blueprints/javascript/addspec.js'
3904--- lib/lp/blueprints/javascript/addspec.js 2012-10-15 16:01:38 +0000
3905+++ lib/lp/blueprints/javascript/addspec.js 2012-11-12 13:21:28 +0000
3906@@ -10,11 +10,24 @@
3907
3908 var namespace = Y.namespace('lp.blueprints.addspec');
3909 var to_choice = Y.lp.app.information_type.cache_to_choicesource;
3910+var info_type = Y.lp.app.information_type;
3911
3912 namespace.set_up = function () {
3913 var choice_data = to_choice(LP.cache.information_type_data);
3914- Y.lp.app.choice.addPopupChoiceForRadioButtons('information_type',
3915- choice_data);
3916+ var widget = Y.lp.app.choice.addPopupChoiceForRadioButtons(
3917+ 'information_type',
3918+ choice_data);
3919+
3920+ // We are not doing ajax saves of the information type so we need to
3921+ // disable the flash on save for the widget.
3922+ widget.set('flashEnabled', false);
3923+
3924+ // Make sure we catch changes to the information type.
3925+ widget.on('save', function (ev) {
3926+ Y.fire(info_type.EV_CHANGE, {
3927+ value: ev.target.get('value')
3928+ });
3929+ });
3930 };
3931
3932 }, "0.1", {"requires": ['lp.app.information_type', 'lp.app.choice']});
3933
3934=== modified file 'lib/lp/bugs/browser/tests/test_bug_views.py'
3935--- lib/lp/bugs/browser/tests/test_bug_views.py 2012-11-11 23:39:34 +0000
3936+++ lib/lp/bugs/browser/tests/test_bug_views.py 2012-11-12 13:21:28 +0000
3937@@ -28,6 +28,7 @@
3938 from zope.security.proxy import removeSecurityProxy
3939
3940 from lp.app.enums import InformationType
3941+from lp.app.interfaces.services import IService
3942 from lp.bugs.adapters.bugchange import BugAttachmentChange
3943 from lp.registry.enums import BugSharingPolicy
3944 from lp.registry.interfaces.accesspolicy import (
3945@@ -499,6 +500,28 @@
3946 self.assertEqual(
3947 u'Private', soup.find('label', text="Private"))
3948
3949+ def test_bugtask_view_user_with_grant_on_bug_for_private_product(self):
3950+ # The regular bug view is properly rendered even if the user
3951+ # does not have permissions to view every detail of a product.
3952+ owner = self.factory.makePerson()
3953+ product = self.factory.makeProduct(
3954+ owner=owner,
3955+ information_type=InformationType.PROPRIETARY)
3956+ user = self.factory.makePerson()
3957+ with person_logged_in(owner):
3958+ bug = self.factory.makeBug(
3959+ target=product, information_type=InformationType.PROPRIETARY)
3960+ getUtility(IService, 'sharing').ensureAccessGrants(
3961+ [user], owner, bugs=[bug])
3962+ launchbag = getUtility(IOpenLaunchBag)
3963+ launchbag.add(bug)
3964+ launchbag.add(bug.default_bugtask)
3965+ with person_logged_in(user):
3966+ view = create_initialized_view(
3967+ bug.default_bugtask, name=u'+index', principal=user)
3968+ contents = view.render()
3969+ self.assertTrue(bug.title in contents)
3970+
3971
3972 class TestBugTextViewPrivateTeams(TestCaseWithFactory):
3973 """ Test for rendering BugTextView with private team artifacts.
3974
3975=== modified file 'lib/lp/bugs/javascript/bugtask_index.js'
3976--- lib/lp/bugs/javascript/bugtask_index.js 2012-10-11 04:57:59 +0000
3977+++ lib/lp/bugs/javascript/bugtask_index.js 2012-11-12 13:21:28 +0000
3978@@ -1131,6 +1131,5 @@
3979 "lp.app.information_type",
3980 "lp.app.widgets.expander", "lp.client", "escape",
3981 "lp.client.plugins", "lp.app.errors",
3982- "lp.app.banner.privacy",
3983 "lp.app.confirmationoverlay",
3984 "lp.bugs.duplicates"]});
3985
3986=== modified file 'lib/lp/bugs/javascript/filebug.js'
3987--- lib/lp/bugs/javascript/filebug.js 2012-09-25 19:15:04 +0000
3988+++ lib/lp/bugs/javascript/filebug.js 2012-11-12 13:21:28 +0000
3989@@ -9,6 +9,7 @@
3990 YUI.add('lp.bugs.filebug', function(Y) {
3991
3992 var namespace = Y.namespace('lp.bugs.filebug');
3993+var info_type = Y.lp.app.information_type;
3994
3995 // For tests.
3996 var skip_animation;
3997@@ -28,13 +29,40 @@
3998 if (Y.Lang.isValue(search_button )) {
3999 search_button.set('value', 'Check again');
4000 }
4001+ setup_plain_inputs();
4002 set_default_privacy_banner();
4003- setup_information_type();
4004 setup_security_related();
4005 setupChoiceWidgets();
4006 }
4007 };
4008
4009+
4010+/**
4011+ * If there are not choice widgets, but radio buttons, then we should watch
4012+ * those radio buttons for change to make sure we fire the information type
4013+ * change events.
4014+ */
4015+var setup_plain_inputs = function () {
4016+ var itypes_table = Y.one('.radio-button-widget');
4017+
4018+ if (itypes_table) {
4019+ itypes_table.delegate('change', function(ev) {
4020+ Y.fire(info_type.EV_CHANGE, {
4021+ value: this.get('value')
4022+ });
4023+ }, "input[name='field.information_type']");
4024+ }
4025+};
4026+
4027+/**
4028+ * Due to the privacy setting of the project/bugs we might only allow a
4029+ * non-public information type. In this case we need to go ahead and let the
4030+ * user know it's going to be private.
4031+ *
4032+ * This is used by the security checks. If the issue is unmade/not security
4033+ * related then we need to check the normal default behavior to make sure we
4034+ * show the correct banner.
4035+ */
4036 var set_default_privacy_banner = function() {
4037 var itypes_table = Y.one('.radio-button-widget');
4038 var val = null;
4039@@ -48,47 +76,12 @@
4040 if (LP.cache.bug_private_by_default) {
4041 var filebug_privacy_text = "This report will be private. " +
4042 "You can disclose it later.";
4043- update_privacy_banner(true, filebug_privacy_text);
4044- } else {
4045- update_banner_from_information_type(val);
4046- }
4047-};
4048-
4049-var update_privacy_banner = function(show, banner_text) {
4050- var banner = Y.lp.app.banner.privacy.getPrivacyBanner(
4051- banner_text, skip_animation);
4052- if (show) {
4053- banner.show();
4054- } else {
4055- banner.hide();
4056- }
4057-};
4058-
4059-var get_new_banner_text = function(value) {
4060- var cache = LP.cache.information_type_data;
4061- var text_template = "This report contains {info_type} information." +
4062- " You can change the information type later.";
4063- return Y.Lang.substitute(text_template, {
4064- 'info_type': cache[value].name
4065- });
4066-};
4067-
4068-var update_banner_from_information_type = function(value) {
4069- var banner_text = get_new_banner_text(value);
4070- var is_private = LP.cache.information_type_data[value].is_private;
4071- update_privacy_banner(is_private, banner_text);
4072-};
4073-
4074-var setup_information_type = function() {
4075- var itypes_table = Y.one('.radio-button-widget');
4076- if (!Y.Lang.isValue(itypes_table)) {
4077- return;
4078- }
4079-
4080-
4081- itypes_table.delegate('change', function() {
4082- update_banner_from_information_type(this.get('value'));
4083- }, "input[name='field.information_type']");
4084+
4085+ Y.fire(info_type.EV_ISPRIVATE, {
4086+ text: filebug_privacy_text,
4087+ value: val
4088+ });
4089+ }
4090 };
4091
4092
4093@@ -100,8 +93,25 @@
4094 var information_helpers = Y.lp.app.information_type;
4095 var choices = information_helpers.cache_to_choicesource(cache);
4096
4097- Y.lp.app.choice.addPopupChoiceForRadioButtons(
4098+ var information_type = Y.lp.app.choice.addPopupChoiceForRadioButtons(
4099 'information_type', choices, true);
4100+
4101+ // When dealing with legacy inputs the information type could have not
4102+ // been created and we get back a null value.
4103+ if (information_type) {
4104+ // We are not doing ajax saves of the information type so we need to
4105+ // disable the flash on save for the widget.
4106+ information_type.set('flashEnabled', false);
4107+
4108+ // When the information type widget changes we need to let the
4109+ // information type module know so it can process the change and
4110+ // update things like banners displayed.
4111+ information_type.on('save', function (ev) {
4112+ Y.fire(info_type.EV_CHANGE, {
4113+ value: ev.target.get('value')
4114+ });
4115+ });
4116+ }
4117 };
4118
4119 var setup_security_related = function() {
4120@@ -116,8 +126,19 @@
4121 security_related.on('change', function() {
4122 var checked = security_related.get('checked');
4123 if (checked) {
4124- update_privacy_banner(true, notification_text);
4125+ // XXX: This should use the correct information type based on the
4126+ // project default. We use PRIVATESECURITY because it'll get the
4127+ // right banner shown.
4128+ Y.fire(info_type.EV_ISPRIVATE, {
4129+ text: notification_text,
4130+ value: 'PRIVATESECURITY'
4131+ });
4132 } else {
4133+ Y.fire(info_type.EV_ISPUBLIC, {
4134+ value: 'PUBLIC'
4135+ });
4136+ // Check with the default settings if we should add the privacy
4137+ // banner back because it's the default of the current project.
4138 set_default_privacy_banner();
4139 }
4140 });
4141@@ -127,5 +148,5 @@
4142
4143 }, "0.1", {"requires": [
4144 "base", "node", "event", "node-event-delegate", "lp.ui.choiceedit",
4145- "lp.app.banner.privacy", "lp.app.choice", "lp.app.information_type",
4146+ "lp.ui.banner", "lp.app.choice", "lp.app.information_type",
4147 "lp.bugs.filebug_dupefinder"]});
4148
4149=== modified file 'lib/lp/bugs/javascript/tests/test_filebug.js'
4150--- lib/lp/bugs/javascript/tests/test_filebug.js 2012-10-26 10:00:20 +0000
4151+++ lib/lp/bugs/javascript/tests/test_filebug.js 2012-11-12 13:21:28 +0000
4152@@ -3,6 +3,8 @@
4153 YUI.add('lp.bugs.filebug.test', function (Y) {
4154
4155 var tests = Y.namespace('lp.bugs.filebug.test');
4156+ var info_type = Y.lp.app.information_type;
4157+
4158 tests.suite = new Y.Test.Suite(
4159 'lp.bugs.filebug Tests');
4160
4161@@ -73,96 +75,136 @@
4162
4163 // Filing a public bug does not show the privacy banner.
4164 test_setup_filebug_public: function () {
4165+ // Watch out for the event that showing a privacy banner would
4166+ // fire.
4167+ var fired = false;
4168+ var ev = Y.on(info_type.EV_ISPRIVATE, function (ev) {
4169+ fired = true;
4170+ });
4171 this.setupForm(true);
4172- var banner_hidden = Y.one('.yui3-privacybanner-hidden');
4173- Y.Assert.isNotNull(banner_hidden);
4174+ Y.Assert.isFalse(fired);
4175+ ev.detach();
4176 },
4177
4178 // Filing a bug for a project with private bugs shows the privacy
4179 // banner.
4180 test_setup_filebug_private: function () {
4181+ var fired = false;
4182+ var ev = Y.on(info_type.EV_ISPRIVATE, function (ev) {
4183+ fired = true;
4184+ Y.Assert.areEqual(
4185+ 'This report will be private. ' +
4186+ 'You can disclose it later.', ev.text);
4187+ });
4188+
4189 window.LP.cache.bug_private_by_default = true;
4190 this.setupForm(true);
4191- var banner_hidden = Y.one('.yui3-privacybanner-hidden');
4192- Y.Assert.isNull(banner_hidden);
4193- var banner_text = Y.one('.banner-text').get('text');
4194- Y.Assert.areEqual(
4195- 'This report will be private. ' +
4196- 'You can disclose it later.', banner_text);
4197+ Y.Assert.isTrue(fired);
4198+ ev.detach();
4199 },
4200
4201 // Selecting a private info type using the legacy radio buttons
4202 // turns on the privacy banner.
4203 test_legacy_select_private_info_type: function () {
4204+ var fired = false;
4205+ var ev = Y.on(info_type.EV_ISPRIVATE, function (ev) {
4206+ fired = true;
4207+ Y.Assert.areEqual(
4208+ 'This report will be private because it is a security ' +
4209+ 'vulnerability. You can disclose it later.', ev.text);
4210+ });
4211+
4212 this.setupForm(true);
4213- var banner_hidden = Y.one('.yui3-privacybanner-hidden');
4214- Y.Assert.isNotNull(banner_hidden);
4215+ Y.Assert.isFalse(fired);
4216+
4217 Y.one('[id="field.information_type.2"]').simulate('click');
4218- banner_hidden = Y.one('.yui3-privacybanner-hidden');
4219- Y.Assert.isNull(banner_hidden);
4220- var banner_text = Y.one('.banner-text').get('text');
4221- Y.Assert.areEqual(
4222- 'This report contains Private Security information. ' +
4223- 'You can change the information type later.', banner_text);
4224+ // And the event should be fired and caught.
4225+ Y.Assert.isTrue(fired);
4226+ ev.detach();
4227 },
4228
4229 // Selecting a public info type using the legacy radio buttons
4230 // turns off the privacy banner.
4231 test_legacy_select_public_info_type: function () {
4232+ var fired = false;
4233+ var ev = Y.on(info_type.EV_ISPUBLIC, function (ev) {
4234+ fired = true;
4235+ });
4236+
4237 window.LP.cache.bug_private_by_default = true;
4238 this.setupForm(true);
4239+
4240+ // This will fire ISPRIVATE and a banner shows.
4241 Y.one('[id="field.information_type.2"]').simulate('click');
4242- var banner_hidden = Y.one('.yui3-privacybanner-hidden');
4243- Y.Assert.isNull(banner_hidden);
4244+
4245+ // The second one will fire ISPUBLIC and the banner disappears.
4246 Y.one('[id="field.information_type.0"]').simulate('click');
4247- banner_hidden = Y.one('.yui3-privacybanner-hidden');
4248- Y.Assert.isNotNull(banner_hidden);
4249+ Y.Assert.isTrue(fired);
4250 },
4251
4252 // When non bug supervisors select a security related bug the privacy
4253 // banner is turned on.
4254 test_select_security_related: function () {
4255+ var fired = false;
4256+ var ev = Y.on(info_type.EV_ISPRIVATE, function (ev) {
4257+ fired = true;
4258+ Y.Assert.areEqual(
4259+ 'This report will be private because it is a security ' +
4260+ 'vulnerability. You can disclose it later.', ev.text);
4261+ });
4262+
4263 this.setupForm(false);
4264- var banner_hidden = Y.one('.yui3-privacybanner-hidden');
4265- Y.Assert.isNotNull(banner_hidden);
4266+ Y.Assert.isFalse(fired);
4267+
4268 Y.one('[id="field.security_related"]').simulate('click');
4269- banner_hidden = Y.one('.yui3-privacybanner-hidden');
4270- Y.Assert.isNull(banner_hidden);
4271- var banner_text = Y.one('.banner-text').get('text');
4272- Y.Assert.areEqual(
4273- 'This report will be private because it is a ' +
4274- 'security vulnerability. You can disclose it later.',
4275- banner_text);
4276+ // And the event should be fired and caught.
4277+ Y.Assert.isTrue(fired);
4278+ ev.detach();
4279 },
4280
4281 // When non bug supervisors unselect a security related bug the privacy
4282 // banner is turned off.
4283 test_unselect_security_related: function () {
4284+ var fired = false;
4285+ var ev = Y.on(info_type.EV_ISPUBLIC, function (ev) {
4286+ fired = true;
4287+ });
4288+
4289 this.setupForm(false);
4290- Y.one('[id="field.security_related"]').simulate('click');
4291- var banner_hidden = Y.one('.yui3-privacybanner-hidden');
4292- Y.Assert.isNull(banner_hidden);
4293- Y.one('[id="field.security_related"]').simulate('click');
4294- banner_hidden = Y.one('.yui3-privacybanner-hidden');
4295- Y.Assert.isNotNull(banner_hidden);
4296+
4297+ Y.one('[id="field.security_related"]').simulate('click');
4298+ Y.Assert.isFalse(fired);
4299+
4300+ Y.one('[id="field.security_related"]').simulate('click');
4301+ Y.Assert.isTrue(fired);
4302 },
4303
4304 // When non bug supervisors unselect a security related bug the privacy
4305 // banner remains on for private_by_default bugs.
4306 test_unselect_security_related_default_private: function () {
4307 window.LP.cache.bug_private_by_default = true;
4308+ var public = 0;
4309+ var private = 0;
4310+ var ev = Y.on(info_type.EV_ISPUBLIC, function (ev) {
4311+ public = public + 1;
4312+ });
4313+
4314+ var ev = Y.on(info_type.EV_ISPRIVATE, function (ev) {
4315+ private = private + 1;
4316+ });
4317+
4318+
4319 this.setupForm(false);
4320- Y.one('[id="field.security_related"]').simulate('click');
4321- var banner_hidden = Y.one('.yui3-privacybanner-hidden');
4322- Y.Assert.isNull(banner_hidden);
4323- Y.one('[id="field.security_related"]').simulate('click');
4324- banner_hidden = Y.one('.yui3-privacybanner-hidden');
4325- Y.Assert.isNull(banner_hidden);
4326- var banner_text = Y.one('.banner-text').get('text');
4327+ Y.Assert.areEqual(1, private, 'Private is the default state.');
4328+
4329+ Y.one('[id="field.security_related"]').simulate('click');
4330+ Y.Assert.areEqual(2, private, 'The first click fires ISPRIVATE');
4331+
4332+ Y.one('[id="field.security_related"]').simulate('click');
4333+ Y.Assert.areEqual(1, public, 'The second click fires ISPUBLIC');
4334 Y.Assert.areEqual(
4335- 'This report will be private. ' +
4336- 'You can disclose it later.',
4337- banner_text);
4338+ 3, private,
4339+ "It also fires an ISPRIVATE since that's the default state.");
4340 },
4341
4342 // The dupe finder functionality is setup.
4343@@ -280,44 +322,59 @@
4344 // Selecting a private info type using the popup choice widget
4345 // turns on the privacy banner.
4346 test_select_private_info_type: function () {
4347+ var fired = false;
4348+ var ev = Y.on(info_type.EV_ISPRIVATE, function (ev) {
4349+ fired = true;
4350+ Y.Assert.areEqual(
4351+ 'This report contains Private information. ' +
4352+ 'You can change the information type later.', ev.text);
4353+ });
4354+
4355 this.setupForm(true);
4356- var banner_hidden = Y.one('.yui3-privacybanner-hidden');
4357- Y.Assert.isNotNull(banner_hidden);
4358+ Y.Assert.isFalse(fired);
4359+
4360 var information_type_popup = Y.one('.information_type-content a');
4361 information_type_popup.simulate('click');
4362 var information_type_choice = Y.one(
4363 '.yui3-ichoicelist-content a[href="#USERDATA"]');
4364 information_type_choice.simulate('click');
4365- banner_hidden = Y.one('.yui3-privacybanner-hidden');
4366- Y.Assert.isNull(banner_hidden);
4367- var banner_text = Y.one('.banner-text').get('text');
4368- Y.Assert.areEqual(
4369- 'This report contains Private information. ' +
4370- 'You can change the information type later.', banner_text);
4371+
4372+ // And the event should be fired and caught.
4373+ Y.Assert.isTrue(fired);
4374+ ev.detach();
4375 },
4376
4377 // Selecting a public info type using the popup choice widget
4378 // turns off the privacy banner.
4379 test_select_public_info_type: function () {
4380+ var fired = false;
4381+ var ev = Y.on(info_type.EV_ISPUBLIC, function (ev) {
4382+ fired = true;
4383+ });
4384+
4385 window.LP.cache.bug_private_by_default = true;
4386 this.setupForm(true);
4387+
4388+ // This will fire ISPRIVATE and a banner shows.
4389 var information_type_popup = Y.one('.information_type-content a');
4390 information_type_popup.simulate('click');
4391 var information_type_choice = Y.one(
4392 '.yui3-ichoicelist-content a[href="#USERDATA"]');
4393 information_type_choice.simulate('click');
4394- var banner_hidden = Y.one('.yui3-privacybanner-hidden');
4395- Y.Assert.isNull(banner_hidden);
4396+
4397+ // The second one will fire ISPUBLIC and the banner disappears.
4398 information_type_popup.simulate('click');
4399 information_type_choice = Y.one(
4400 '.yui3-ichoicelist-content a[href="#PUBLIC"]');
4401 information_type_choice.simulate('click');
4402- banner_hidden = Y.one('.yui3-privacybanner-hidden');
4403- Y.Assert.isNotNull(banner_hidden);
4404+
4405+ Y.Assert.isTrue(fired);
4406 }
4407 }));
4408
4409-}, '0.1', {'requires': ['test', 'test-console', 'event', 'node-event-simulate',
4410- 'lp.app.banner.privacy', 'lp.app.choice',
4411- 'lp.bugs.filebug_dupefinder', 'lp.bugs.filebug'
4412- ]});
4413+}, '0.1', {
4414+ requires: [
4415+ 'test', 'test-console', 'event', 'node-event-simulate',
4416+ 'lp.app.banner.privacy', 'lp.app.choice', 'lp.app.information_type',
4417+ 'lp.bugs.filebug_dupefinder', 'lp.bugs.filebug' ]
4418+});
4419
4420=== modified file 'lib/lp/code/javascript/branch.information_type_choice.js'
4421--- lib/lp/code/javascript/branch.information_type_choice.js 2012-09-21 15:39:22 +0000
4422+++ lib/lp/code/javascript/branch.information_type_choice.js 2012-11-12 13:21:28 +0000
4423@@ -105,7 +105,9 @@
4424 LP.cache.context.information_type =
4425 information_type.get_cache_data_from_key(
4426 value, 'value', 'name');
4427- information_type.update_privacy_banner(value);
4428+ Y.fire(information_type.EV_CHANGE, {
4429+ value: value
4430+ });
4431 if (this.get('use_animation')) {
4432 this.information_type_edit._showSucceeded();
4433 }
4434@@ -119,7 +121,8 @@
4435 }
4436 });
4437
4438-}, "0.1", {"requires": ["base", "oop", "node", "event", "io-base",
4439- "lp.ui.choiceedit", "lp.app.banner.privacy",
4440- "lp.app.errors", "lp.app.choice",
4441- "lp.app.information_type"]});
4442+}, "0.1", {
4443+ requires: [
4444+ "base", "oop", "node", "event", "io-base", "lp.ui.choiceedit",
4445+ "lp.app.errors", "lp.app.choice", "lp.app.information_type"]
4446+});
4447
4448=== modified file 'lib/lp/code/javascript/tests/test_information_type_choice.js'
4449--- lib/lp/code/javascript/tests/test_information_type_choice.js 2012-10-26 10:00:20 +0000
4450+++ lib/lp/code/javascript/tests/test_information_type_choice.js 2012-11-12 13:21:28 +0000
4451@@ -4,6 +4,8 @@
4452
4453 var tests = Y.namespace('lp.code.branch.information_type_choice.test');
4454 var ns = Y.lp.code.branch.information_type_choice;
4455+ var info_type = Y.lp.app.information_type;
4456+
4457 tests.suite = new Y.Test.Suite(
4458 'lp.code.branch.information_type_choice Tests');
4459
4460@@ -62,22 +64,6 @@
4461 this.widget.render();
4462 },
4463
4464- _shim_privacy_banner: function () {
4465- var old_func = Y.lp.app.banner.privacy.getPrivacyBanner;
4466- Y.lp.app.banner.privacy.getPrivacyBanner = function () {
4467- return {
4468- show: function () { Y.fire('test:banner:show'); },
4469- hide: function () { Y.fire('test:banner:hide'); },
4470- updateText: function () { Y.fire('test:banner:update'); }
4471- };
4472- };
4473- return old_func;
4474- },
4475-
4476- _unshim_privacy_banner: function (old_func) {
4477- Y.lp.app.banner.privacy.getPrivacyBanner = old_func;
4478- },
4479-
4480 test_library_exists: function () {
4481 Y.Assert.isObject(Y.lp.code.branch.information_type_choice,
4482 "Cannot locate the " +
4483@@ -118,44 +104,31 @@
4484
4485 // Setting a private type shows the privacy banner.
4486 test_information_type_save_success_private: function() {
4487+ var fired = false;
4488+ Y.on(info_type.EV_ISPRIVATE, function (ev) {
4489+ fired = true;
4490+ });
4491+
4492 this.makeWidget();
4493- var old_func = this._shim_privacy_banner();
4494- var hide_flag = false;
4495- var update_flag = false;
4496- Y.on('test:banner:show', function() {
4497- hide_flag = true;
4498- });
4499- Y.on('test:banner:update', function() {
4500- update_flag = true;
4501- });
4502
4503 this.widget._information_type_save_success('PROPRIETARY');
4504- var body = Y.one('body');
4505- Y.Assert.isTrue(body.hasClass('private'));
4506- Y.Assert.isTrue(hide_flag);
4507- Y.Assert.isTrue(update_flag);
4508+ Y.Assert.isTrue(fired);
4509 Y.Assert.areEqual(
4510 'Proprietary', LP.cache.context.information_type);
4511- this._unshim_privacy_banner(old_func);
4512 },
4513
4514 // Setting a private type hides the privacy banner.
4515 test_information_type_save_success_public: function() {
4516+ var fired = false;
4517+ Y.on(info_type.EV_ISPUBLIC, function (ev) {
4518+ fired = true;
4519+ });
4520+
4521 this.makeWidget();
4522- var old_func = this._shim_privacy_banner();
4523- var flag = false;
4524- Y.on('test:banner:hide', function() {
4525- flag = true;
4526- });
4527- var summary = Y.one('#information-type-summary');
4528- summary.replaceClass('public', 'private');
4529
4530 this.widget._information_type_save_success('PUBLIC');
4531- var body = Y.one('body');
4532- Y.Assert.isTrue(body.hasClass('public'));
4533- Y.Assert.isTrue(flag);
4534+ Y.Assert.isTrue(fired);
4535 Y.Assert.areEqual('Public', LP.cache.context.information_type);
4536- this._unshim_privacy_banner(old_func);
4537 },
4538
4539 // Test error handling when a save fails.
4540
4541=== modified file 'lib/lp/registry/browser/product.py'
4542--- lib/lp/registry/browser/product.py 2012-10-26 15:11:20 +0000
4543+++ lib/lp/registry/browser/product.py 2012-11-12 13:21:28 +0000
4544@@ -1905,6 +1905,7 @@
4545
4546 class ProjectAddStepTwo(StepView, ProductLicenseMixin, ReturnToReferrerMixin):
4547 """Step 2 (of 2) in the +new project add wizard."""
4548+ related_features = [PRIVATE_PROJECTS_FLAG]
4549
4550 _field_names = ['displayname', 'name', 'title', 'summary', 'description',
4551 'homepageurl', 'information_type', 'licenses',
4552
4553=== modified file 'lib/lp/registry/configure.zcml'
4554--- lib/lp/registry/configure.zcml 2012-11-11 23:39:34 +0000
4555+++ lib/lp/registry/configure.zcml 2012-11-12 13:21:28 +0000
4556@@ -1356,6 +1356,25 @@
4557 set_schema="lp.registry.interfaces.product.IProductModerateRestricted"
4558 set_attributes="active"/>
4559
4560+ <!-- IBugTarget -->
4561+
4562+ <require
4563+ permission="launchpad.LimitedView"
4564+ attributes="
4565+ bugtargetdisplayname"/>
4566+
4567+ <require
4568+ permission="launchpad.View"
4569+ attributes="
4570+ bug_reported_acknowledgement
4571+ bug_reporting_guidelines
4572+ bugtargetname
4573+ createBug
4574+ enable_bugfiling_duplicate_search
4575+ getBugTaskWeightFunction
4576+ pillar
4577+ searchTasks"/>
4578+
4579 <!-- IHasAliases -->
4580
4581 <require
4582@@ -1391,8 +1410,19 @@
4583 <!-- IStructuralSubscriptionTarget -->
4584
4585 <require
4586+ permission="launchpad.LimitedView"
4587+ attributes="
4588+ parent_subscription_target" />
4589+ <require
4590 permission="launchpad.View"
4591- interface="lp.bugs.interfaces.structuralsubscription.IStructuralSubscriptionTargetRead" />
4592+ attributes="
4593+ bug_subscriptions
4594+ getSubscription
4595+ getSubscriptions
4596+ target_type_display
4597+ userCanAlterBugSubscription
4598+ userCanAlterSubscription
4599+ userHasBugSubscriptions" />
4600 <require
4601 permission="launchpad.AnyAllowedPerson"
4602 interface="lp.bugs.interfaces.structuralsubscription.IStructuralSubscriptionTargetWrite" />
4603
4604=== modified file 'lib/lp/registry/interfaces/product.py'
4605--- lib/lp/registry/interfaces/product.py 2012-11-11 23:39:34 +0000
4606+++ lib/lp/registry/interfaces/product.py 2012-11-12 13:21:28 +0000
4607@@ -89,7 +89,6 @@
4608 from lp.blueprints.interfaces.sprint import IHasSprints
4609 from lp.bugs.interfaces.bugsupervisor import IHasBugSupervisor
4610 from lp.bugs.interfaces.bugtarget import (
4611- IBugTarget,
4612 IHasExpirableBugs,
4613 IOfficialBugTagTargetPublic,
4614 IOfficialBugTagTargetRestricted,
4615@@ -428,11 +427,27 @@
4616 """True if the given user has access to this product."""
4617
4618
4619-class IProductLimitedView(Interface):
4620+class IProductLimitedView(IHasLogo, IHasOwner, ILaunchpadUsage):
4621 """Attributes that must be visible for person with artifact grants
4622 on bugs, branches or specifications for the product.
4623 """
4624
4625+ displayname = exported(
4626+ TextLine(
4627+ title=_('Display Name'),
4628+ description=_("""The name of the project as it would appear in a
4629+ paragraph.""")),
4630+ exported_as='display_name')
4631+
4632+ logo = exported(
4633+ LogoImageUpload(
4634+ title=_("Logo"), required=False,
4635+ default_image_resource='/@@/product-logo',
4636+ description=_(
4637+ "An image of exactly 64x64 pixels that will be displayed in "
4638+ "the heading of all pages related to this project. It should "
4639+ "be no bigger than 50kb in size.")))
4640+
4641 name = exported(
4642 ProductNameField(
4643 title=_('Name'),
4644@@ -442,16 +457,14 @@
4645 "letters, numbers, dots, hyphens or pluses. "
4646 "Keep this name short; it is used in URLs as shown above.")))
4647
4648-
4649-class IProductView(
4650- IBugTarget, ICanGetMilestonesDirectly, IHasAppointedDriver, IHasBranches,
4651- IHasDrivers, IHasExternalBugTracker, IHasIcon,
4652- IHasLogo, IHasMergeProposals, IHasMilestones, IHasExpirableBugs,
4653- IHasMugshot, IHasOwner, IHasSprints, IHasTranslationImports,
4654- ITranslationPolicy, IKarmaContext, ILaunchpadUsage, IMakesAnnouncements,
4655- IOfficialBugTagTargetPublic, IHasOOPSReferences,
4656- ISpecificationTarget, IHasRecipes, IHasCodeImports, IServiceUsage):
4657- """Public IProduct properties."""
4658+ owner = exported(
4659+ PersonChoice(
4660+ title=_('Maintainer'),
4661+ required=True,
4662+ vocabulary='ValidPillarOwner',
4663+ description=_("The restricted team, moderated team, or person "
4664+ "who maintains the project information in "
4665+ "Launchpad.")))
4666
4667 project = exported(
4668 ReferenceChoice(
4669@@ -469,14 +482,21 @@
4670 'and security policy will apply to this project.')),
4671 exported_as='project_group')
4672
4673- owner = exported(
4674- PersonChoice(
4675- title=_('Maintainer'),
4676- required=True,
4677- vocabulary='ValidPillarOwner',
4678- description=_("The restricted team, moderated team, or person "
4679- "who maintains the project information in "
4680- "Launchpad.")))
4681+ title = exported(
4682+ Title(
4683+ title=_('Title'),
4684+ description=_("The project title. Should be just a few words.")))
4685+
4686+
4687+class IProductView(
4688+ ICanGetMilestonesDirectly, IHasAppointedDriver, IHasBranches,
4689+ IHasDrivers, IHasExternalBugTracker, IHasIcon,
4690+ IHasMergeProposals, IHasMilestones, IHasExpirableBugs,
4691+ IHasMugshot, IHasSprints, IHasTranslationImports,
4692+ ITranslationPolicy, IKarmaContext, IMakesAnnouncements,
4693+ IOfficialBugTagTargetPublic, IHasOOPSReferences,
4694+ ISpecificationTarget, IHasRecipes, IHasCodeImports, IServiceUsage):
4695+ """Public IProduct properties."""
4696
4697 registrant = exported(
4698 PublicPersonChoice(
4699@@ -503,18 +523,6 @@
4700 "required because there might be a project driver and also a "
4701 "driver appointed in the overarching project group.")
4702
4703- displayname = exported(
4704- TextLine(
4705- title=_('Display Name'),
4706- description=_("""The name of the project as it would appear in a
4707- paragraph.""")),
4708- exported_as='display_name')
4709-
4710- title = exported(
4711- Title(
4712- title=_('Title'),
4713- description=_("The project title. Should be just a few words.")))
4714-
4715 summary = exported(
4716 Summary(
4717 title=_('Summary'),
4718@@ -614,15 +622,6 @@
4719 "will be displayed next to the project name everywhere in "
4720 "Launchpad that we refer to the project and link to it.")))
4721
4722- logo = exported(
4723- LogoImageUpload(
4724- title=_("Logo"), required=False,
4725- default_image_resource='/@@/product-logo',
4726- description=_(
4727- "An image of exactly 64x64 pixels that will be displayed in "
4728- "the heading of all pages related to this project. It should "
4729- "be no bigger than 50kb in size.")))
4730-
4731 mugshot = exported(
4732 MugshotImageUpload(
4733 title=_("Brand"), required=False,
4734
4735=== modified file 'lib/lp/registry/javascript/product_views.js'
4736--- lib/lp/registry/javascript/product_views.js 2012-10-26 06:59:14 +0000
4737+++ lib/lp/registry/javascript/product_views.js 2012-11-12 13:21:28 +0000
4738@@ -95,6 +95,9 @@
4739 */
4740 information_type_change: function (value) {
4741 toggle_license_field(value);
4742+ Y.fire(info_type.EV_CHANGE, {
4743+ value: value
4744+ });
4745 },
4746
4747 /**
4748@@ -349,6 +352,9 @@
4749 */
4750 information_type_change: function (value) {
4751 toggle_license_field(value);
4752+ Y.fire(info_type.EV_CHANGE, {
4753+ value: value
4754+ });
4755 var driver_cont =
4756 Y.one('input[name="field.driver"]').ancestor('td');
4757 var bug_super_cont =
4758
4759=== modified file 'lib/lp/registry/model/product.py'
4760--- lib/lp/registry/model/product.py 2012-11-11 23:39:34 +0000
4761+++ lib/lp/registry/model/product.py 2012-11-12 13:21:28 +0000
4762@@ -109,6 +109,7 @@
4763 from lp.bugs.interfaces.bugtarget import (
4764 BUG_POLICY_ALLOWED_TYPES,
4765 BUG_POLICY_DEFAULT_TYPES,
4766+ IBugTarget,
4767 )
4768 from lp.bugs.interfaces.bugtaskfilter import OrderedBugTask
4769 from lp.bugs.model.bugtarget import (
4770@@ -345,7 +346,7 @@
4771 """A Product."""
4772
4773 implements(
4774- IBugSummaryDimension, IFAQTarget, IHasBugSupervisor,
4775+ IBugSummaryDimension, IBugTarget, IFAQTarget, IHasBugSupervisor,
4776 IHasCustomLanguageCodes, IHasIcon, IHasLogo, IHasMugshot,
4777 IHasOOPSReferences, ILaunchpadUsage, IProduct, IServiceUsage)
4778
4779
4780=== modified file 'lib/lp/registry/tests/test_product.py'
4781--- lib/lp/registry/tests/test_product.py 2012-11-11 23:39:34 +0000
4782+++ lib/lp/registry/tests/test_product.py 2012-11-12 13:21:28 +0000
4783@@ -551,7 +551,11 @@
4784 CheckerPublic: set((
4785 'active', 'id', 'information_type', 'pillar_category', 'private',
4786 'userCanView',)),
4787- 'launchpad.LimitedView': set(('name', )),
4788+ 'launchpad.LimitedView': set((
4789+ 'bugtargetdisplayname', 'displayname', 'enable_bug_expiration',
4790+ 'logo', 'name', 'official_answers', 'official_anything',
4791+ 'official_blueprints', 'official_codehosting', 'official_malone',
4792+ 'owner', 'parent_subscription_target', 'project', 'title', )),
4793 'launchpad.View': set((
4794 '_getOfficialTagClause', '_all_specifications',
4795 '_valid_specifications', 'active_or_packaged_series',
4796@@ -561,16 +565,15 @@
4797 'blueprints_usage', 'branch_sharing_policy',
4798 'bug_reported_acknowledgement', 'bug_reporting_guidelines',
4799 'bug_sharing_policy', 'bug_subscriptions', 'bug_supervisor',
4800- 'bug_tracking_usage', 'bugtargetdisplayname', 'bugtargetname',
4801- 'bugtracker', 'canUserAlterAnswerContact',
4802- 'codehosting_usage',
4803+ 'bug_tracking_usage', 'bugtargetname',
4804+ 'bugtracker', 'canUserAlterAnswerContact', 'codehosting_usage',
4805 'coming_sprints', 'commercial_subscription',
4806 'commercial_subscription_is_due', 'createBug',
4807 'createCustomLanguageCode', 'custom_language_codes',
4808 'date_next_suggest_packaging', 'datecreated', 'description',
4809 'development_focus', 'development_focusID',
4810- 'direct_answer_contacts', 'displayname', 'distrosourcepackages',
4811- 'downloadurl', 'driver', 'drivers', 'enable_bug_expiration',
4812+ 'direct_answer_contacts', 'distrosourcepackages',
4813+ 'downloadurl', 'driver', 'drivers',
4814 'enable_bugfiling_duplicate_search', 'findReferencedOOPS',
4815 'findSimilarFAQs', 'findSimilarQuestions', 'freshmeatproject',
4816 'getAllowedBugInformationTypes',
4817@@ -594,15 +597,13 @@
4818 'has_custom_language_codes', 'has_milestones', 'homepage_content',
4819 'homepageurl', 'icon', 'invitesTranslationEdits',
4820 'invitesTranslationSuggestions',
4821- 'license_info', 'license_status', 'licenses', 'logo', 'milestones',
4822+ 'license_info', 'license_status', 'licenses', 'milestones',
4823 'mugshot', 'name_with_project', 'newCodeImport',
4824- 'obsolete_translatable_series', 'official_answers',
4825- 'official_anything', 'official_blueprints', 'official_bug_tags',
4826- 'official_codehosting', 'official_malone', 'owner',
4827- 'parent_subscription_target', 'packagedInDistros', 'packagings',
4828+ 'obsolete_translatable_series', 'official_bug_tags',
4829+ 'packagedInDistros', 'packagings',
4830 'past_sprints', 'personHasDriverRights', 'pillar',
4831 'primary_translatable', 'private_bugs',
4832- 'programminglang', 'project', 'qualifies_for_free_hosting',
4833+ 'programminglang', 'qualifies_for_free_hosting',
4834 'recipes', 'redeemSubscriptionVoucher', 'registrant', 'releases',
4835 'remote_product', 'removeCustomLanguageCode',
4836 'screenshotsurl',
4837@@ -610,7 +611,7 @@
4838 'series',
4839 'sharesTranslationsWithOtherSide', 'sourceforgeproject',
4840 'sourcepackages', 'specification_sharing_policy', 'specifications',
4841- 'sprints', 'summary', 'target_type_display', 'title',
4842+ 'sprints', 'summary', 'target_type_display',
4843 'translatable_packages', 'translatable_series',
4844 'translation_focus', 'translationgroup', 'translationgroups',
4845 'translationpermission', 'translations_usage', 'ubuntu_packages',
4846
4847=== modified file 'lib/lp/services/features/flags.py'
4848--- lib/lp/services/features/flags.py 2012-11-08 06:06:22 +0000
4849+++ lib/lp/services/features/flags.py 2012-11-12 13:21:28 +0000
4850@@ -236,7 +236,7 @@
4851 'boolean',
4852 'If true, enabled access to private project registration features.',
4853 'disabled',
4854- '',
4855+ 'Allow registering a non-public project.',
4856 'http://blog.launchpad.net/general/private-projects-beta'),
4857 ('disclosure.private_project.traversal_override',
4858 'boolean',