← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~rharding/launchpad/wire_new_banner into lp:launchpad

 

Richard Harding has proposed merging lp:~rharding/launchpad/wire_new_banner into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~rharding/launchpad/wire_new_banner/+merge/133913

TBD
-- 
https://code.launchpad.net/~rharding/launchpad/wire_new_banner/+merge/133913
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rharding/launchpad/wire_new_banner into lp:launchpad.
=== modified file 'buildout-templates/bin/combine-css.in'
--- buildout-templates/bin/combine-css.in	2012-08-23 19:56:15 +0000
+++ buildout-templates/bin/combine-css.in	2012-11-12 13:05:28 +0000
@@ -27,6 +27,7 @@
     #'lazr/build/yui/cssgrids/grids.css',
     'cssgrids/grids.css',
     'build/ui/assets/skins/sam/lazr.css',
+    'build/ui/assets/skins/sam/banner.css',
     'build/inlineedit/assets/skins/sam/editor.css',
     'build/autocomplete/assets/skins/sam/autocomplete.css',
     'build/overlay/assets/skins/sam/pretty-overlay.css',

=== removed file 'lib/canonical/launchpad/icing/css/components/beta_banner.css'
--- lib/canonical/launchpad/icing/css/components/beta_banner.css	2012-05-16 20:32:47 +0000
+++ lib/canonical/launchpad/icing/css/components/beta_banner.css	1970-01-01 00:00:00 +0000
@@ -1,48 +0,0 @@
-/* ===========
-   Beta banner
-*/
-.yui3-betabanner-content .global-notification {
-    position: fixed;
-    z-index: 9;
-    top: 0;
-    left: 0;
-    right: 0;
-    padding: 7px 20px;
-    /* Define colour for browsers that don't support transparency */
-    background-color: #606060;
-    /* Set transparent background for browsers that support it */
-    background-color: rgba(64, 64, 64, 0.9);
-    box-shadow: 0 0 5px #333;
-    color: #fff;
-    text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5);
-    font-size: 14px;
-    line-height: 21px;
-    text-align: left;
-    }
-.beta-banner .info-link {
-    color: #4884ef;
-}
-.beta-warning {
-    padding: 3px 6px 4px 6px;
-    margin-right: 12px;
-    background-color: #be0000;
-    font-weight: bold;
-    font-size: 12px;
-    text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
-    border-radius: 5px;
-    border-top: 1px solid #e20000;
-    background-color: #c10000;
-    background: linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
-    background: -o-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
-    background: -moz-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
-    background: -webkit-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
-    background: -ms-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
-    }
-.beta-feature {
-    font-weight: bold;
-    }
-.yui3-betabanner-content .global-notification-close,
-.yui3-betabanner-content .global-notification-close:active,
-.yui3-betabanner-content .global-notification-close:visited {
-    color: #fff;
-    }

=== modified file 'lib/lp/app/browser/tales.py'
--- lib/lp/app/browser/tales.py	2012-09-28 06:15:58 +0000
+++ lib/lp/app/browser/tales.py	2012-11-12 13:05:28 +0000
@@ -663,15 +663,16 @@
     def global_css(self):
         css_classes = set([])
         view = self._context
+
+        # XXX: Bug #1076074
         private = getattr(view, 'private', False)
         if private:
             css_classes.add('private')
-            css_classes.add('global-notification-visible')
         else:
             css_classes.add('public')
         beta = getattr(view, 'beta_features', [])
-        if beta != []:
-            css_classes.add('global-notification-visible')
+        if beta:
+            css_classes.add('beta')
         return ' '.join(list(css_classes))
 
     def _getSaneBreadcrumbDetail(self, breadcrumb):

=== removed directory 'lib/lp/app/javascript/banners'
=== removed file 'lib/lp/app/javascript/banners/banner.js'
--- lib/lp/app/javascript/banners/banner.js	2012-10-04 14:17:23 +0000
+++ lib/lp/app/javascript/banners/banner.js	1970-01-01 00:00:00 +0000
@@ -1,156 +0,0 @@
-/*
- * Copyright 2012 Canonical Ltd.  This software is licensed under the
- * GNU Affero General Public License version 3 (see the file LICENSE).
- *
- * Notification banner widget
- *
- * @module lp.app.banner
- */
-
-YUI.add('lp.app.banner', function (Y) {
-var ns = Y.namespace('lp.app.banner');
-
-ns.Banner = Y.Base.create('banner', Y.Widget, [], {
-
-    _getAnimTimes: function() {
-        var anim_times;
-        if (this.get('skip_animation')) {
-            anim_times = {
-                fade: 0.0,
-                slide_out: 0.0
-            };
-        } else {
-            anim_times = {
-                fade: 0.3,
-                slide_out: 0.2
-            };
-        }
-        return anim_times;
-    },
-
-    _showBanner: function () {
-        var body = Y.one('body');
-        var global_notification = Y.one('.global-notification');
-        var anim_times = this._getAnimTimes();
-
-        body.addClass('global-notification-visible');
-        global_notification.removeClass('hidden');
-
-        var fade_in = new Y.Anim({
-            node: global_notification,
-            to: {opacity: 1},
-            duration: anim_times.fade
-        });
-        var body_space = new Y.Anim({
-            node: body,
-            to: {'paddingTop': '40px'},
-            duration: anim_times.slide_out,
-            easing: Y.Easing.easeOut
-        });
-        var login_space = new Y.Anim({
-            node: '.login-logout',
-            to: {'top': '45px'},
-            duration: anim_times.slide_out,
-            easing: Y.Easing.easeOut
-        });
-        // For testing, we don't do the animations or else the tests will fail.
-        if (anim_times.fade > 0) {
-            fade_in.run();
-        }
-        if (anim_times.slide_out > 0) {
-            body_space.run();
-            login_space.run();
-        }
-    },
-
-    _hideBanner: function () {
-        var body = Y.one('body');
-        var global_notification = Y.one('.global-notification');
-        var anim_times = this._getAnimTimes();
-
-        global_notification.addClass('transparent');
-
-        var fade_out = new Y.Anim({
-            node: global_notification,
-            to: {opacity: 0},
-            duration: anim_times.fade
-        });
-        var body_space = new Y.Anim({
-            node: body,
-            to: {'paddingTop': 0},
-            duration: anim_times.slide_out,
-            easing: Y.Easing.easeOut
-        });
-        var login_space = new Y.Anim({
-            node: '.login-logout',
-            to: {'top': '6px'},
-            duration: anim_times.slide_out,
-            easing: Y.Easing.easeOut
-        });
-        fade_out.on('end', function() {
-            global_notification.addClass('hidden');
-        });
-        body_space.on('end', function() {
-            body.removeClass('global-notification-visible');
-        });
-
-        fade_out.run();
-        body_space.run();
-        login_space.run();
-    },
-
-    bindUI: function() {
-        this.after('visibleChange', function() {
-            if (this.get('visible')) {
-                this._showBanner();
-            } else {
-                this._hideBanner();
-            }
-        });
-    },
-
-    renderUI: function () {
-        var banner_data = {
-            badge: this.get('banner_icon'),
-            text: this.get('notification_text')
-        };
-        var banner_html = Y.lp.mustache.to_html(
-            this.get('banner_template'),
-            banner_data);
-        this.get('contentBox').append(banner_html);
-    },
-
-    updateText: function (new_text) {
-        var text_node = this.get('contentBox').one('.banner-text');
-        if (!Y.Lang.isValue(new_text)) {
-            new_text = this.get('notification_text');
-        }
-
-        if (text_node) {
-            text_node.set('text', new_text);
-        } else {
-            Y.log('No text node to update banner text.', 'error');
-        }
-    }
-
-}, {
-    ATTRS: {
-        banner_icon: { value: "<span></span>" },
-        banner_template: {
-            valueFn: function() {
-                return [
-                    '<div class="global-notification transparent hidden">',
-                        '{{{ badge }}}',
-                        '<span class="banner-text">{{ text }}</span>',
-                    "</div>"].join('');
-            }
-        },
-        notification_text: { value: "" },
-        skip_animation: { value: false },
-        visible: { value: false }
-    }
-});
-
-}, '0.1', {
-    requires: ['base', 'node', 'anim', 'widget', 'lp.mustache', 'yui-log']
-});

=== removed file 'lib/lp/app/javascript/banners/beta-notification.js'
--- lib/lp/app/javascript/banners/beta-notification.js	2012-09-25 18:47:19 +0000
+++ lib/lp/app/javascript/banners/beta-notification.js	1970-01-01 00:00:00 +0000
@@ -1,104 +0,0 @@
-/**
- * Add a BetaBanner widget for use.
- *
- * @namespace lp.app.banner
- * @module beta
- *
- */
-YUI.add('lp.app.banner.beta', function(Y) {
-
-var ns = Y.namespace('lp.app.banner.beta');
-var Banner = Y.lp.app.banner.Banner;
-
-// For the beta banner to work, it needs to have one instance, and one
-// instance only.
-window._singleton_beta_banner = null;
-
-ns.show_beta_if_needed = function () {
-    if (window._singleton_beta_banner === null) {
-        var src = Y.one('.yui3-betabanner');
-        window._singleton_beta_banner = new ns.BetaBanner({ srcNode: src });
-    }
-    if (window._singleton_beta_banner.get('features').length !== 0) {
-        window._singleton_beta_banner.render();
-        window._singleton_beta_banner.show();
-    }
-};
-
-
-/**
- * Banner to display for beta features.
- *
- * @class BetaBanner
- * @extends Banner
- *
- */
-ns.BetaBanner = Y.Base.create('betaBanner', Banner, [], {
-
-    bindUI: function () {
-        Banner.prototype.bindUI.apply(this, arguments);
-        var close_box = Y.one('.global-notification-close');
-        var that = this;
-        close_box.on('click', function(e) {
-            e.halt();
-            that.hide();
-        });
-    },
-
-    renderUI: function () {
-        var banner_data = {
-            badge: this.get('banner_icon'),
-            text: this.get('notification_text'),
-            features: this.get('features'),
-        };
-        var banner_html = Y.lp.mustache.to_html(
-            this.get('banner_template'),
-            banner_data);
-        this.get('contentBox').append(banner_html);
-        var beta_node = Y.one('.global-notification');
-        var close_box = Y.Node.create(
-            '<a href="#" class="global-notification-close">Hide' +
-            '<span class="notification-close sprite" /></a>');
-        beta_node.appendChild(close_box);
-    }
-
-}, {
-    ATTRS: {
-        banner_icon: { value: '<span class="beta-warning">BETA!</span>' },
-
-        banner_template: {
-            valueFn: function() {
-                return [
-                    '<div class="global-notification transparent hidden">',
-                        '{{{ badge }}}',
-                        '<span class="banner-text">',
-                            '{{ text }}{{{ features }}}',
-                        '</span>',
-                    "</div>"].join('');
-            }
-        },
-
-        features: {
-            valueFn: function () {
-                var features_template = [
-                    '{{#features}}{{#is_beta}}',
-                    '<span class="beta-feature"> {{title}}',
-                    '{{#url}}',
-                    ' (<a href="{{url}}" class="info-link">read more</a>)',
-                    '{{/url}}',
-                    '</span>',
-                    '{{/is_beta}}{{/features}}'].join('');
-                var feature_data = {
-                    features: Y.Object.values(LP.cache.related_features)
-                };
-                return Y.lp.mustache.to_html(features_template, feature_data);
-            }
-        },
-
-        notification_text: { value: "Some parts of this page are in beta: " }
-    }
-});
-
-
-}, '0.1', {'requires': ['base', 'node', 'anim', 'lp.mustache',
-                        'lp.app.banner']});

=== removed file 'lib/lp/app/javascript/banners/privacy.js'
--- lib/lp/app/javascript/banners/privacy.js	2012-10-04 14:17:23 +0000
+++ lib/lp/app/javascript/banners/privacy.js	1970-01-01 00:00:00 +0000
@@ -1,119 +0,0 @@
-/**
- * Add a PrivacyBanner widget for use.
- *
- * @namespace lp.app.banner
- * @module privacy
- *
- */
-YUI.add('lp.app.banner.privacy', function(Y) {
-var ns = Y.namespace('lp.app.banner.privacy');
-var Banner = Y.lp.app.banner.Banner;
-
-ns.EV_SHOW = 'privacy_banner:show';
-ns.EV_HIDE = 'privacy_banner:hide';
-
-/**
- * Allow for adjusting the global instance of the Privacy Banner via events.
- *
- * @event privacy_banner:show
- * @param text The message to show
- */
-Y.publish(ns.EV_SHOW, {
-    emitFacade: true
-});
-
-/**
- * Hide the global instance of the banner via events.
- *
- * @event privacy_banner:hide
- */
-Y.publish(ns.EV_HIDE, {
-    emitFacade: true
-});
-
-
-// For the privacy banner to work, it needs to have one instance, and one
-// instance only.
-window._singleton_privacy_banner = null;
-ns.getPrivacyBanner = function (banner_text, skip_animation) {
-    if (window._singleton_privacy_banner === null) {
-        var src = Y.one('.yui3-privacybanner');
-        window._singleton_privacy_banner = new ns.PrivacyBanner(
-            { srcNode: src, skip_animation: skip_animation });
-        window._singleton_privacy_banner.render();
-    }
-    if (Y.Lang.isValue(banner_text)) {
-        window._singleton_privacy_banner.updateText(banner_text);
-    }
-    return window._singleton_privacy_banner;
-};
-
-
-/**
- * Banner to display when page contains private information.
- *
- * @class PrivacyBanner
- * @extends Banner
- *
- */
-ns.PrivacyBanner = Y.Base.create('privacyBanner', Banner, [], {
-    _custom_message: function (ev) {
-        var body = Y.one('body');
-        body.replaceClass('public', 'private');
-        if (!ev.text) {
-            throw('A custom privacy banner must have a text attribute');
-        }
-        this.updateText(ev.text);
-        this.show();
-    },
-
-    _make_public: function (ev) {
-        var body = Y.one('body');
-        body.replaceClass('private', 'public');
-        this.hide();
-    },
-
-    _make_private: function (ev) {
-        // Update the text in the banner before we show it.
-        var body = Y.one('body');
-        body.replaceClass('public', 'private');
-
-        if (!ev.text) {
-            throw('Showing a privacy banner must supply text for the banner');
-        }
-        this.updateText(ev.text);
-        this.show();
-    },
-
-    bindUI: function () {
-        var that = this;
-        var info_type = Y.lp.app.information_type;
-        Banner.prototype.bindUI.apply(this, arguments);
-
-        // We care about changes to Information Type in the UI.
-        Y.on(info_type.EV_ISPUBLIC, that._make_public, that);
-        Y.on(info_type.EV_ISPRIVATE, that._make_private, that);
-
-        // And provide our own manual events for the Security banner usage.
-        Y.on(ns.EV_SHOW, that._custom_message, that);
-        Y.on(ns.EV_HIDE, function (ev) {
-            that.hide();
-        }, that);
-    }
-
-}, {
-    ATTRS: {
-        banner_icon: {
-            value: '<span class="sprite notification-private"></span>'
-        },
-        notification_text: {
-            value: "The information on this page is private."
-        }
-    }
-});
-
-}, "0.1", {
-    requires: [
-        "base", "node", "anim", "lp.app.banner",
-        "lp.app.information_type"]
-});

=== removed directory 'lib/lp/app/javascript/banners/tests'
=== removed file 'lib/lp/app/javascript/banners/tests/test_banner.html'
--- lib/lp/app/javascript/banners/tests/test_banner.html	2012-10-26 09:54:28 +0000
+++ lib/lp/app/javascript/banners/tests/test_banner.html	1970-01-01 00:00:00 +0000
@@ -1,46 +0,0 @@
-<!DOCTYPE html>
-<!--
-Copyright 2012 Canonical Ltd.  This software is licensed under the
-GNU Affero General Public License version 3 (see the file LICENSE).
--->
-
-<html>
-  <head>
-      <title>lp.app.banner Tests</title>
-
-      <!-- YUI and test setup -->
-      <script type="text/javascript"
-              src="../../../../../../build/js/yui/yui/yui.js">
-      </script>
-      <link rel="stylesheet"
-      href="../../../../../../build/js/yui/console/assets/console-core.css" />
-      <link rel="stylesheet"
-      href="../../../../../../build/js/yui/test-console/assets/skins/sam/test-console.css" />
-      <link rel="stylesheet"
-      href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
-
-      <script type="text/javascript"
-              src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
-
-      <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
-
-      <!-- Dependencies -->
-      <script type="text/javascript"
-          src="../../../../../../build/js/lp/app/mustache.js"></script>
-
-      <!-- The module under test. -->
-      <script type="text/javascript" src="../banner.js"></script>
-
-      <!-- Placeholder for any css asset for this module. -->
-      <!-- <link rel="stylesheet" href="../assets/${LIBRARY}-core.css" /> -->
-
-      <!-- The test suite -->
-      <script type="text/javascript" src="test_banner.js"></script>
-
-    </head>
-    <body class="yui3-skin-sam">
-        <ul id="suites">
-            <li>lp.app.banner.test</li>
-        </ul>
-    </body>
-</html>

=== removed file 'lib/lp/app/javascript/banners/tests/test_banner.js'
--- lib/lp/app/javascript/banners/tests/test_banner.js	2012-10-26 10:00:20 +0000
+++ lib/lp/app/javascript/banners/tests/test_banner.js	1970-01-01 00:00:00 +0000
@@ -1,110 +0,0 @@
-/* Copyright (c) 2012, Canonical Ltd. All rights reserved. */
-
-YUI.add('lp.app.banner.test', function (Y) {
-
-    var tests = Y.namespace('lp.app.banner.test');
-    tests.suite = new Y.Test.Suite('lp.app.banner Tests');
-
-    tests.suite.add(new Y.Test.Case({
-        name: 'banner_tests',
-
-        setUp: function () {
-            var main = Y.Node.create('<div id="maincontent"></div>');
-            var login_logout = Y.Node.create('<div></div>')
-                .addClass('login-logout');
-            main.appendChild(login_logout);
-            Y.one('body').appendChild(main);
-        },
-
-        tearDown: function () {
-            Y.one('#maincontent').remove(true);
-            Y.all('.yui3-banner').remove(true);
-        },
-
-        test_library_exists: function () {
-            Y.Assert.isObject(Y.lp.app.banner,
-                "Could not locate the lp.app.banner module");
-        },
-
-        test_init_without_config: function () {
-            var banner = new Y.lp.app.banner.Banner();
-            Y.Assert.areEqual("", banner.get('notification_text'));
-            Y.Assert.areEqual("<span></span>", banner.get('banner_icon'));
-        },
-
-        test_init_with_config: function () {
-            var cfg = {
-                notification_text: "Some text.",
-                banner_icon: '<span class="sprite"></span>'
-            };
-            var banner = new Y.lp.app.banner.Banner(cfg);
-            Y.Assert.areEqual(
-                cfg.notification_text,
-                banner.get('notification_text'));
-            Y.Assert.areEqual(cfg.banner_icon, banner.get('banner_icon'));
-        },
-
-        test_render_no_config: function () {
-            var banner = new Y.lp.app.banner.Banner({ skip_animation: true });
-            banner.render();
-
-            var banner_node = Y.one(".global-notification");
-            Y.Assert.isObject(banner_node);
-            Y.Assert.isTrue(banner_node.hasClass('hidden'));
-        },
-
-        test_render_with_config: function () {
-            var cfg = {
-                notification_text: "Some text.",
-                banner_icon: '<span class="sprite"></span>',
-                skip_animation: true
-            };
-            var banner = new Y.lp.app.banner.Banner(cfg);
-            banner.render();
-
-            var banner_node = Y.one(".global-notification");
-            var badge = banner_node.one('.sprite');
-            Y.Assert.isObject(banner_node);
-            Y.Assert.areEqual(cfg.notification_text, banner_node.get('text'));
-            Y.Assert.isObject(badge);
-        },
-
-        test_show: function() {
-            var banner = new Y.lp.app.banner.Banner({ skip_animation: true });
-            banner.render();
-            banner.show();
-            var banner_node = Y.one(".global-notification");
-            Y.Assert.isFalse(banner_node.hasClass('hidden'));
-        },
-
-        test_hide: function() {
-            var banner = new Y.lp.app.banner.Banner({ skip_animation: true });
-            banner.render();
-            banner.show();
-
-            // Even with animation times set to 0, this test needs a slight
-            // delay in order for the animation end events to fire.
-            var banner_node = Y.one(".global-notification");
-            var wait_for_anim = 20;
-            var check = function () {
-                Y.Assert.isTrue(banner_node.hasClass('hidden'));
-            };
-            banner.hide();
-            this.wait(check, wait_for_anim);
-        },
-
-        test_updateText: function() {
-            var banner = new Y.lp.app.banner.Banner({ skip_animation: true });
-            banner.render();
-            var new_text = 'some new text';
-            banner.updateText(new_text);
-            var banner_node = Y.one(".global-notification");
-            Y.Assert.areEqual(new_text, banner_node.get('text'));
-
-            banner.updateText();
-            banner_node = Y.one(".global-notification");
-            Y.Assert.areEqual("", banner_node.get('text'));
-        }
-    }));
-
-}, '0.1', {'requires': ['test', 'test-console', 'lp.app.banner']});

=== removed file 'lib/lp/app/javascript/banners/tests/test_beta_notification.html'
--- lib/lp/app/javascript/banners/tests/test_beta_notification.html	2012-10-26 09:54:28 +0000
+++ lib/lp/app/javascript/banners/tests/test_beta_notification.html	1970-01-01 00:00:00 +0000
@@ -1,51 +0,0 @@
-<!DOCTYPE html>
-<!--
-Copyright 2012 Canonical Ltd.  This software is licensed under the
-GNU Affero General Public License version 3 (see the file LICENSE).
--->
-
-<html>
-  <head>
-      <title>Test beta-notification</title>
-
-      <!-- YUI and test setup -->
-      <script type="text/javascript"
-              src="../../../../../../build/js/yui/yui/yui.js">
-      </script>
-      <link rel="stylesheet"
-      href="../../../../../../build/js/yui/console/assets/console-core.css" />
-      <link rel="stylesheet"
-      href="../../../../../../build/js/yui/test-console/assets/skins/sam/test-console.css" />
-      <link rel="stylesheet"
-      href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
-
-      <script type="text/javascript"
-              src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
-
-      <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
-
-      <!-- Dependencies -->
-      <script type="text/javascript"
-          src="../../../../../../build/js/lp/app/banners/banner.js"></script>
-      <script type="text/javascript"
-          src="../../../../../../build/js/lp/app/mustache.js"></script>
-
-      <!-- The module under test. -->
-      <script type="text/javascript" src="../beta-notification.js"></script>
-
-      <!-- Placeholder for any css asset for this module. -->
-      <!-- <link rel="stylesheet" href="../assets/beta-notification-core.css" /> -->
-
-      <!-- The test suite. -->
-      <script type="text/javascript" src="test_beta_notification.js"></script>
-
-    </head>
-    <body class="yui3-skin-sam">
-        <ul id="suites">
-            <!-- <li>lp.large_indicator.test</li> -->
-            <li>lp.app.banner.beta.test</li>
-        </ul>
-        <!-- The example markup required by the script to run. -->
-        <div id="maincontent"></div>
-    </body>
-</html>

=== removed file 'lib/lp/app/javascript/banners/tests/test_beta_notification.js'
--- lib/lp/app/javascript/banners/tests/test_beta_notification.js	2012-10-26 10:00:20 +0000
+++ lib/lp/app/javascript/banners/tests/test_beta_notification.js	1970-01-01 00:00:00 +0000
@@ -1,140 +0,0 @@
-/* Copyright (c) 2012, Canonical Ltd. All rights reserved. */
-
-// Set the "enabled" variable, normally set by base-layout-macros.
-// This must be a global variable for the code being tested to work.
-var privacy_notification_enabled = true;
-
-YUI.add('lp.app.banner.beta.test', function (Y) {
-
-    var tests = Y.namespace('lp.app.banner.beta.test');
-    tests.suite = new Y.Test.Suite('lp.app.banner.beta Tests');
-
-    tests.suite.add(new Y.Test.Case({
-        name: 'beta-notification',
-
-        setUp: function () {
-            var main = Y.Node.create('<div id="maincontent"></div>');
-            var login_logout = Y.Node.create('<div></div>')
-                .addClass('login-logout');
-            main.appendChild(login_logout);
-            Y.one('body').append(main);
-            window.LP = {
-                cache: {}
-            };
-        },
-
-        tearDown: function () {
-            Y.one('#maincontent').remove(true);
-            Y.all('.yui3-banner').remove(true);
-        },
-
-        test_library_exists: function () {
-            Y.Assert.isObject(Y.lp.app.banner.beta,
-                "Could not locate the lp.app.banner.beta module");
-        },
-
-        test_beta_banner_one_beta_feature: function() {
-            LP.cache.related_features = {
-                '': {
-                    is_beta: true,
-                    title: 'A beta feature',
-                    url: 'http://lp.dev/LEP/one'
-            }};
-            var betabanner = new Y.lp.app.banner.beta.BetaBanner(
-                {skip_animation: true});
-            betabanner.render();
-            betabanner.show();
-
-            var body = Y.one('body');
-            // The <body> node has the class global-notification-visible,
-            // so that the element has enough padding for the beta banner.
-            Y.Assert.isTrue(body.hasClass('global-notification-visible'));
-
-            feature_info = Y.one('.beta-feature');
-            // The message about a beta feature consists of the feature
-            // title and a link to a page with more information about
-            // the feature.
-            Y.Assert.areEqual(
-                ' A beta feature (read more)', feature_info.get('text'));
-            info_link = feature_info.get('children').item(0);
-            Y.Assert.areEqual('http://lp.dev/LEP/one', info_link.get('href'));
-        },
-
-        test_beta_banner_two_beta_features: function() {
-            LP.cache.related_features = {
-                '1': {
-                    is_beta: true,
-                    title: 'Beta feature 1',
-                    url: 'http://lp.dev/LEP/one'
-                },
-                '2': {
-                    is_beta: true,
-                    title: 'Beta feature 2',
-                    url: ''
-                }};
-            var betabanner = new Y.lp.app.banner.beta.BetaBanner(
-                {skip_animation: true});
-            betabanner.render();
-            betabanner.show();
-
-            var body = Y.one('body');
-            Y.Assert.isTrue(body.hasClass('global-notification-visible'));
-
-            // Notifications about several features can be displayed.
-            feature_info = Y.all('.beta-feature').item(0);
-            Y.Assert.areEqual(
-                ' Beta feature 1 (read more)', feature_info.get('text'));
-            info_link = feature_info.get('children').item(0);
-            Y.Assert.areEqual('http://lp.dev/LEP/one', info_link.get('href'));
-
-            // If an entry in LP.cache.related_features does not provide a
-            // "read more" link, the corrsponding node is not added.
-            feature_info = Y.all('.beta-feature').item(1);
-            Y.Assert.areEqual(
-                ' Beta feature 2', feature_info.get('text'));
-            Y.Assert.isNull(feature_info.get('children').item(0));
-        },
-
-        test_beta_banner_no_beta_features_defined: function() {
-            LP.cache.related_features = {
-                foo_feature: {
-                    is_beta: false,
-                    title: 'Non-beta feature',
-                    url: 'http://example.org'
-                }};
-            Y.lp.app.banner.beta.show_beta_if_needed();
-            Y.Assert.isNull(Y.one('.global-notification'));
-        },
-
-        test_hide_beta_banner: function() {
-            LP.cache.related_features = {
-                '': {
-                    is_beta: true,
-                    title: 'A beta feature',
-                    url: 'http://lp.dev/LEP/one'
-            }};
-            var betabanner = new Y.lp.app.banner.beta.BetaBanner(
-                {skip_animation: true});
-            betabanner.render();
-            betabanner.show();
-            var body = Y.one('body');
-            var banner = Y.one('.global-notification');
-            Y.Assert.isFalse(banner.hasClass('hidden'));
-
-            // Even with animation times set to 0, this test needs a slight
-            // delay in order for the animation end events to fire.
-            var check = function() {
-                Y.Assert.isTrue(banner.hasClass('hidden'));
-                Y.Assert.isFalse(
-                    body.hasClass('global-notification-visible'));
-            };
-            close_link = Y.one('.global-notification-close');
-            close_link.simulate('click');
-            var wait_time = 20;
-            this.wait(check, wait_time);
-        }
-    }));
-
-}, '0.1', { 'requires': ['test', 'test-console', 'node', 'lp.app.banner.beta',
-                         'node-event-simulate']
-});

=== removed file 'lib/lp/app/javascript/banners/tests/test_privacy.html'
--- lib/lp/app/javascript/banners/tests/test_privacy.html	2012-10-26 09:54:28 +0000
+++ lib/lp/app/javascript/banners/tests/test_privacy.html	1970-01-01 00:00:00 +0000
@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<!--
-Copyright 2012 Canonical Ltd.  This software is licensed under the
-GNU Affero General Public License version 3 (see the file LICENSE).
--->
-
-<html>
-  <head>
-      <title>lp.app.banner.privacy Tests</title>
-
-      <!-- YUI and test setup -->
-      <script type="text/javascript"
-              src="../../../../../../build/js/yui/yui/yui.js">
-      </script>
-      <link rel="stylesheet"
-      href="../../../../../../build/js/yui/console/assets/console-core.css" />
-      <link rel="stylesheet"
-      href="../../../../../../build/js/yui/test-console/assets/skins/sam/test-console.css" />
-      <link rel="stylesheet"
-      href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
-
-      <script type="text/javascript"
-              src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
-
-      <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
-
-      <!-- Dependencies -->
-      <script type="text/javascript"
-          src="../../../../../../build/js/lp/app/mustache.js"></script>
-      <script type="text/javascript"
-          src="../../../../../../build/js/lp/app/banners/banner.js"></script>
-      <script type="text/javascript"
-          src="../../../../../../build/js/lp/app/information_type.js"></script>
-
-      <!-- The module under test. -->
-      <script type="text/javascript" src="../privacy.js"></script>
-
-      <!-- The test suite. -->
-      <script type="text/javascript" src="test_privacy.js"></script>
-
-    </head>
-    <body class="yui3-skin-sam">
-        <ul id="suites">
-            <li>lp.app.banner.privacy.test</li>
-        </ul>
-    </body>
-</html>

=== removed file 'lib/lp/app/javascript/banners/tests/test_privacy.js'
--- lib/lp/app/javascript/banners/tests/test_privacy.js	2012-10-26 10:00:20 +0000
+++ lib/lp/app/javascript/banners/tests/test_privacy.js	1970-01-01 00:00:00 +0000
@@ -1,117 +0,0 @@
-/* Copyright 2011-2012 Canonical Ltd.  This software is licensed under the
- * GNU Affero General Public License version 3 (see the file LICENSE).
- */
-
-YUI.add('lp.app.banner.privacy.test', function (Y) {
-
-    var tests = Y.namespace('lp.app.banner.privacy.test');
-    tests.suite = new Y.Test.Suite('lp.app.banner.privacy Tests');
-
-    tests.suite.add(new Y.Test.Case({
-        name: 'privacy_tests',
-
-        setUp: function () {
-            var main = Y.Node.create('<div id="maincontent"></div>');
-            var login_logout = Y.Node.create('<div></div>')
-                .addClass('login-logout');
-            main.appendChild(login_logout);
-
-            var banner_node = Y.Node.create('<div></div>')
-                .addClass('yui3-privacybanner');
-            main.appendChild(banner_node);
-            Y.one('body').appendChild(main);
-        },
-
-        tearDown: function () {
-            Y.one('#maincontent').remove(true);
-            window._singleton_privacy_banner = null;
-            delete window.LP;
-        },
-
-        test_library_exists: function () {
-            Y.Assert.isObject(Y.lp.app.banner.privacy,
-                "Could not locate the lp.app.banner.privacy module");
-        },
-        test_init: function () {
-            var banner = new Y.lp.app.banner.privacy.PrivacyBanner();
-            Y.Assert.areEqual(
-                "The information on this page is private.",
-                banner.get('notification_text'));
-            Y.Assert.areEqual(
-                '<span class="sprite notification-private"></span>',
-                banner.get('banner_icon'));
-        },
-
-        test_only_one_banner: function () {
-            // getPrivacyBanner only returns one banner.
-            var banner = Y.lp.app.banner.privacy.getPrivacyBanner();
-            Y.Assert.areEqual(1, Y.all('.global-notification').size());
-
-            var new_text = 'This is new text';
-            banner = Y.lp.app.banner.privacy.getPrivacyBanner(new_text);
-            Y.Assert.areEqual(1, Y.all('.global-notification').size());
-            var banner_node = Y.one('.global-notification');
-            Y.Assert.areEqual(
-                new_text,
-                Y.one('.global-notification').get('text'));
-        },
-
-        test_banner_with_custom_text: function () {
-            var banner = Y.lp.app.banner.privacy.getPrivacyBanner();
-            var new_text = 'New custom text';
-
-            Y.fire('privacy_banner:show', {
-                text: new_text
-            });
-            Y.Assert.areEqual(
-                new_text,
-                Y.one('.global-notification').get('text'));
-            Y.Assert.isTrue(banner.get('visible'),
-                            'Banner should be visible.');
-        },
-
-        test_banner_hide_event: function () {
-            var banner = Y.lp.app.banner.privacy.getPrivacyBanner();
-            var new_text = 'New custom text';
-
-            Y.fire('privacy_banner:show', {
-                text: new_text
-            });
-            Y.fire('privacy_banner:hide');
-            Y.Assert.isFalse(banner.get('visible'),
-                             'Banner should not be visible.');
-        },
-
-        test_info_type_private_event: function () {
-            var banner = Y.lp.app.banner.privacy.getPrivacyBanner();
-            var body = Y.one('body');
-            var msg = 'Some private message';
-            Y.fire('information_type:is_private', {
-                text: msg,
-                value: 'PROPRIETARY'
-            });
-            Y.Assert.areEqual(
-                msg,
-                Y.one('.global-notification').get('text'));
-            Y.Assert.isTrue(body.hasClass('private'),
-                            'Body should be private');
-            Y.Assert.isTrue(banner.get('visible'),
-                            'Banner should be visible.');
-        },
-
-        test_info_type_public_event: function () {
-            var banner = Y.lp.app.banner.privacy.getPrivacyBanner();
-            var new_text = 'New custom text';
-
-            Y.fire('privacy_banner:show', {
-                text: new_text
-            });
-            Y.fire('information_type:is_public');
-            var body = Y.one('body');
-            Y.Assert.isTrue(body.hasClass('public'), 'Body should be public');
-            Y.Assert.isFalse(banner.get('visible'),
-                             'Banner should not be visible.');
-        }
-    }));
-
-}, '0.1', {'requires': ['test', 'test-console', 'lp.app.banner.privacy']});

=== modified file 'lib/lp/app/javascript/information_type.js'
--- lib/lp/app/javascript/information_type.js	2012-10-04 14:17:23 +0000
+++ lib/lp/app/javascript/information_type.js	2012-11-12 13:05:28 +0000
@@ -44,6 +44,29 @@
     emitFacade: true
 });
 
+
+/**
+ * Provide some logic to check if this is a private inducing event. This could
+ * be due to a non-public information type or as security related information
+ * type/value.
+ */
+var is_private_event = function (value) {
+    var is_private = false;
+    if (ns.get_cache_data_from_key(value,
+                                  'value',
+                                  'is_private')) {
+
+        is_private = true;
+    }
+
+    if (value.indexOf('SECURITY') !== -1) {
+        is_private = true;
+    }
+
+    return is_private;
+};
+
+
 /**
  * Wire up a helper event so that if someone changes the event, we take care
  * of also firing any is_private/is_public event shortcuts others want to
@@ -53,14 +76,13 @@
  * instead of having each module needing to know the location in the LP.cache
  * and such.
  */
+
 Y.on(ns.EV_CHANGE, function (ev) {
     if (!ev.value) {
         throw('Information type change event without new value');
     }
 
-    if (ns.get_cache_data_from_key(ev.value,
-                                  'value',
-                                  'is_private')) {
+    if (is_private_event(ev.value)) {
         Y.fire(ns.EV_ISPRIVATE, {
             text: ns.get_banner_text(ev.value),
             value: ev.value
@@ -155,16 +177,33 @@
 };
 
 ns.get_banner_text = function(value) {
-    var text_template = "This page contains {info_type} information.";
-    var info_type = ns.get_cache_data_from_key(value, 'value', 'name');
-    return Y.Lang.sub(text_template, {'info_type': info_type});
+    // Construct a different message for security related banner content.
+    var text;
+    if (value.indexOf('SECURITY') !== -1) {
+        var security_text = "This report will be private " +
+                            "because it is a security " +
+                            "vulnerability. You can " +
+                            "disclose it later.";
+        text = security_text;
+    } else {
+        var text_template = "This page contains {info_type} information.";
+        var info_type = ns.get_cache_data_from_key(value, 'value', 'name');
+        text = Y.Lang.sub(text_template, {'info_type': info_type});
+    }
+    return text;
 };
 
 ns.save_success = function(widget, context, value, subscribers_list,
                            result_data) {
     context.information_type =
         ns.get_cache_data_from_key(value, 'value', 'name');
-    ns.update_privacy_banner(value);
+
+    // Let the world know the information type has been updated. Allows
+    // banners to update.
+    Y.fire(ns.EV_CHANGE, {
+        value: value
+    });
+
     widget._showSucceeded();
     if (Y.Lang.isObject(result_data)) {
         var subscribers = result_data.subscription_data;
@@ -344,27 +383,7 @@
     }
 };
 
-/**
- * Update the privacy banner to display the specified information type value.
- *
- * @param value
- */
-ns.update_privacy_banner = function(value) {
-    var body = Y.one('body');
-    var privacy_banner = Y.lp.app.banner.privacy.getPrivacyBanner(
-            undefined, skip_animation);
-    var private_type = LP.cache.information_type_data[value].is_private;
-    if (private_type) {
-        body.replaceClass('public', 'private');
-        var banner_text = ns.get_banner_text(value);
-        privacy_banner.updateText(banner_text);
-        privacy_banner.show();
-    } else {
-        body.replaceClass('private', 'public');
-        privacy_banner.hide();
-    }
-};
 
 }, "0.1", {"requires": [
     "base", "oop", "node", "event", "io-base", "lp.mustache", "lp.app.choice",
-    "lp.bugs.bugtask_index", "lp.app.banner.privacy", "lp.ui.choiceedit"]});
+    "lp.bugs.bugtask_index", "lp.ui.choiceedit"]});

=== modified file 'lib/lp/app/javascript/tests/test_information_type.js'
--- lib/lp/app/javascript/tests/test_information_type.js	2012-10-26 10:00:20 +0000
+++ lib/lp/app/javascript/tests/test_information_type.js	2012-11-12 13:05:28 +0000
@@ -84,22 +84,6 @@
                 privacy_link, this.lp_client, LP.cache.bug, null, true);
         },
 
-        _shim_privacy_banner: function () {
-            var old_func = Y.lp.app.banner.privacy.getPrivacyBanner;
-            Y.lp.app.banner.privacy.getPrivacyBanner = function () {
-                return {
-                    show: function () { Y.fire('test:banner:show'); },
-                    hide: function () { Y.fire('test:banner:hide'); },
-                    updateText: function () { Y.fire('test:banner:update'); }
-                };
-            };
-            return old_func;
-        },
-
-        _unshim_privacy_banner: function (old_func) {
-            Y.lp.app.banner.privacy.getPrivacyBanner = old_func;
-        },
-
         test_library_exists: function () {
             Y.Assert.isObject(Y.lp.app.information_type,
                 "Cannot locate the lp.app.information_type module");
@@ -142,53 +126,9 @@
             ns.save_success = orig_save_success;
         },
 
-        // Setting a private type shows the privacy banner.
-        test_save_success_private: function() {
-            this.makeWidget();
-            var old_func = this._shim_privacy_banner();
-            var hide_flag = false;
-            var update_flag = false;
-            Y.on('test:banner:show', function() {
-                hide_flag = true;
-            });
-            Y.on('test:banner:update', function() {
-                update_flag = true;
-            });
-
-            ns.save_success(this.widget, LP.cache.bug,
-                                             'PROPRIETARY');
-            var body = Y.one('body');
-            Y.Assert.isTrue(body.hasClass('private'));
-            Y.Assert.isTrue(hide_flag);
-            Y.Assert.isTrue(update_flag);
-            Y.Assert.areEqual(
-                'Proprietary', LP.cache.bug.information_type);
-            this._unshim_privacy_banner(old_func);
-        },
-
-        // Setting a public type hides the privacy banner.
-        test_save_success_public: function() {
-            this.makeWidget();
-            var old_func = this._shim_privacy_banner();
-            var flag = false;
-            Y.on('test:banner:hide', function() {
-                flag = true;
-            });
-            var summary = Y.one('#information-type-summary');
-            summary.replaceClass('public', 'private');
-
-            ns.save_success(this.widget, 'PUBLIC', 'PUBLIC');
-            var body = Y.one('body');
-            Y.Assert.isTrue(body.hasClass('public'));
-            Y.Assert.isTrue(flag);
-            Y.Assert.areEqual('Public', LP.cache.bug.information_type);
-            this._unshim_privacy_banner(old_func);
-        },
-
         // A successful save updates the subscribers portlet.
         test_save_success_with_subscribers_data: function() {
             this.makeWidget();
-            var old_func = this._shim_privacy_banner();
             var flag = false;
             Y.on('test:banner:hide', function() {
                 flag = true;
@@ -216,7 +156,6 @@
             Y.Assert.isTrue(load_subscribers_called);
             Y.Assert.areEqual('value1', window.LP.cache.item1);
             Y.Assert.areEqual('value2', window.LP.cache.item2);
-            this._unshim_privacy_banner(old_func);
         },
 
         // A successful save updates the task actions.

=== added file 'lib/lp/app/javascript/ui/assets/skins/sam/banner.css'
--- lib/lp/app/javascript/ui/assets/skins/sam/banner.css	1970-01-01 00:00:00 +0000
+++ lib/lp/app/javascript/ui/assets/skins/sam/banner.css	2012-11-12 13:05:28 +0000
@@ -0,0 +1,109 @@
+/* JS Banner styling */
+.yui3-banner {
+    /* Default to not visible so we can fade in */
+    opacity: 1;
+
+    /* Animations for fade-in/out */
+    -webkit-transition: opacity 0.3s ease-in;
+    -moz-transition: opacity 0.3s ease-in;
+    transition: opacity 0.3s ease-in;
+
+    width: 100%;
+}
+
+.yui3-banner.yui3-banner-hidden {
+    opacity: 0;
+}
+
+/* Nasty hack to get the bar moved since it's absolutely positioned
+ * This also needs to be updated
+ */
+body.beta #locationbar, body.private #locationbar {
+    top: 47px;
+}
+
+/* If we have both classes make room for two banners height */
+body.beta.private #locationbar {
+    top: 94px;
+}
+
+/* If the container exists make sure we start out with the rest of the page
+ * bumped down the starting distance to reduce flash effect
+ */
+.beta_banner_container, .private_banner_container {
+    min-height: 45px;
+}
+
+.yui3-banner-content {
+    box-shadow: 0 0 5px #333;
+    background-color: #666;
+    color: #fff;
+    display: block;
+    font-size: 14px;
+    font-weight: bold;
+    line-height: 21px;
+    padding: 8px 20px;
+    text-align: left;
+    text-shadow: 0 -1px 0 #631616;
+    z-index: 10;
+}
+
+.yui3-banner-content .badge {
+    display: inline-block;
+    height: 21px;
+    margin-right: 10px;
+    padding: 0;
+    vertical-align: middle;
+    width: 20px;
+}
+.yui3-banner-content .banner-content {}
+
+.yui3-banner-content.beta {
+    /* Some of these are required to override .beta CSS */
+
+    /* Defined for browsers that don't support transparency */
+    background-color: #606060;
+    /* Transparent background for browsers that support it */
+    background-color: rgba(64, 64, 64, 0.9);
+    height: auto;
+    margin-top: 0px;
+    text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5);
+    width: auto;
+}
+.yui3-banner-content.beta .yui3-banner-content-content {}
+.yui3-banner-content.beta .badge {
+    /* sprite-ref: icon-sprites */
+    background-color: #c10000;
+    background: linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
+    background: -moz-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
+    background: -ms-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
+    background: -o-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
+    background: -webkit-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
+    border-radius: 5px;
+    border-top: 1px solid #e20000;
+    font-size: 12px;
+    font-weight: bold;
+    margin-right: 12px;
+    padding: 3px 6px 4px 6px;
+    text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+    width: auto;
+}
+
+.yui3-banner-content.beta .beta-feature {
+    font-weight: bold;
+}
+.yui3-banner-content.beta .info-link {
+    color: #4884ef;
+}
+
+.yui3-banner-content.private {
+    /* Define colour for browsers that don't support transparency */
+    background: #8d1f1f;
+    /* Set transparent background for browsers that support it */
+    background: rgba(125,0,0,0.9);
+}
+.yui3-banner-content.private .banner-content {}
+.yui3-banner-content.private .badge {
+    background: url(/@@/notification-private.png);
+    background-repeat: no-repeat;
+}

=== removed file 'lib/lp/app/javascript/ui/assets/skins/sam/banner.css'
--- lib/lp/app/javascript/ui/assets/skins/sam/banner.css	2012-11-09 19:06:16 +0000
+++ lib/lp/app/javascript/ui/assets/skins/sam/banner.css	1970-01-01 00:00:00 +0000
@@ -1,109 +0,0 @@
-/* JS Banner styling */
-.yui3-banner {
-    /* Default to not visible so we can fade in */
-    opacity: 1;
-
-    /* Animations for fade-in/out */
-    -webkit-transition: opacity 0.3s ease-in;
-    -moz-transition: opacity 0.3s ease-in;
-    transition: opacity 0.3s ease-in;
-
-    width: 100%;
-}
-
-.yui3-banner.yui3-banner-hidden {
-    opacity: 0;
-}
-
-/* Nasty hack to get the bar moved since it's absolutely positioned
- * This also needs to be updated
- */
-body.beta #locationbar, body.private #locationbar {
-    top: 47px;
-}
-
-/* If we have both classes make room for two banners height */
-body.beta.private #locationbar {
-    top: 94px;
-}
-
-/* If the container exists make sure we start out with the rest of the page
- * bumped down the starting distance to reduce flash effect
- */
-.beta_banner_container, .private_banner_container {
-    min-height: 45px;
-}
-
-.yui3-banner-content {
-    box-shadow: 0 0 5px #333;
-    background-color: #666;
-    color: #fff;
-    display: block;
-    font-size: 14px;
-    font-weight: bold;
-    line-height: 21px;
-    padding: 8px 20px;
-    text-align: left;
-    text-shadow: 0 -1px 0 #631616;
-    z-index: 10;
-}
-
-.yui3-banner-content .badge {
-    display: inline-block;
-    height: 21px;
-    margin-right: 10px;
-    padding: 0;
-    vertical-align: middle;
-    width: 20px;
-}
-.yui3-banner-content .banner-content {}
-
-.yui3-banner-content.beta {
-    /* Some of these are required to override .beta CSS */
-
-    /* Defined for browsers that don't support transparency */
-    background-color: #606060;
-    /* Transparent background for browsers that support it */
-    background-color: rgba(64, 64, 64, 0.9);
-    height: auto;
-    margin-top: 0px;
-    text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5);
-    width: auto;
-}
-.yui3-banner-content.beta .yui3-banner-content-content {}
-.yui3-banner-content.beta .badge {
-    /* sprite-ref: icon-sprites */
-    background-color: #c10000;
-    background: linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
-    background: -moz-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
-    background: -ms-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
-    background: -o-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
-    background: -webkit-linear-gradient(bottom, rgb(158,0,0) 0%, rgb(193,0,0) 70%);
-    border-radius: 5px;
-    border-top: 1px solid #e20000;
-    font-size: 12px;
-    font-weight: bold;
-    margin-right: 12px;
-    padding: 3px 6px 4px 6px;
-    text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
-    width: auto;
-}
-
-.yui3-banner-content.beta .beta-feature {
-    font-weight: bold;
-}
-.yui3-banner-content.beta .info-link {
-    color: #4884ef;
-}
-
-.yui3-banner-content.private {
-    /* Define colour for browsers that don't support transparency */
-    background: #8d1f1f;
-    /* Set transparent background for browsers that support it */
-    background: rgba(125,0,0,0.9);
-}
-.yui3-banner-content.private .banner-content {}
-.yui3-banner-content.private .badge {
-    background: url(/@@/notification-private.png);
-    background-repeat: no-repeat;
-}

=== added file 'lib/lp/app/javascript/ui/banner.js'
--- lib/lp/app/javascript/ui/banner.js	1970-01-01 00:00:00 +0000
+++ lib/lp/app/javascript/ui/banner.js	2012-11-12 13:05:28 +0000
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2012 Canonical Ltd.  This software is licensed under the
+ * GNU Affero General Public License version 3 (see the file LICENSE).
+ *
+ * Notification banner widget
+ *
+ * @module lp.ui.banner
+ * @namespace lp.ui
+ * @module banner
+ */
+YUI.add('lp.ui.banner', function (Y) {
+
+    var ns = Y.namespace('lp.ui.banner');
+
+    // GLOBALS
+    ns.PRIVATE = 'private';
+    ns.BETA = 'beta';
+
+    /**
+     * Banner widget base class
+     *
+     * This is the base Banner, you're supposed to supply some message data to
+     * generate the banner in the proper method.
+     *
+     * This banner provides all shared functionality between the Privacy and
+     * Beta banners.
+     *
+     * @class Banner
+     * @extends Y.Widget
+     *
+     */
+    ns.Banner = Y.Base.create('banner', Y.Widget, [], {
+        template: [
+            '<div class="banner">',
+                '<span class="badge {{ banner_type }}">{{ badge_text }}</span>',
+                '<span class="banner-content">{{{ content }}}</span>',
+            '</div>'
+        ].join(''),
+
+
+        /**
+         * Bind events that our widget supports such as closing the banner.
+         *
+         * We also watch the destroy event to clean up side effect css we
+         * created.
+         *
+         * @method bindUI
+         */
+        bindUI: function () {
+            this.on('destroy', function (ev) {
+                // XXX: Bug #1076074
+                var body = Y.one('body');
+                var banner_type = this.get('banner_type');
+                body.removeClass(banner_type);
+
+                // Remove any container the page might have provided for us to
+                // start out with.
+                var container_class = '.' + banner_type + '_banner_container';
+                var container = Y.one(container_class);
+                if (container) {
+                    Y.one(container_class).remove();
+                }
+            });
+
+            this.after('contentChange', function () {
+                this.renderUI();
+            });
+        },
+
+        /**
+         * Default initialize method.
+         *
+         * @method initialize
+         * @param {Object} cfg
+         */
+        initialize: function (cfg) {
+        },
+
+        /**
+         * Widget render method to generate the html of the widget.
+         *
+         * @method renderUI
+         */
+        renderUI: function () {
+            var contentBox = this.get('contentBox');
+            contentBox.addClass(this.get('banner_type'));
+            var html = Y.lp.mustache.to_html(this.template, this.getAttrs());
+            contentBox.setHTML(html);
+
+            // XXX: Bug #1076074
+            // Needs to get cleaned up. Only applies to the global
+            // banners and not to other ones which we're working to allow.
+            // This is currently required because the #locationbar is
+            // absolutely located and needs to be moved as banners change.
+            var body = Y.one('body');
+            body.addClass(this.get('banner_type'));
+
+            if (this.get('visible')) {
+                this.show();
+            }
+        },
+
+        /**
+         * We need to override show so that we force a browser repaint which
+         * allows our CSS3 animation to run. Otherwise the browser sees we
+         * added new DOM elements and jumps straight to the finished animation
+         * point.
+         *
+         * @method show
+         */
+        show: function () {
+            var _node = this.get('boundingBox')._node;
+            var getComputedStyle = document.defaultView.getComputedStyle;
+            _node.style.display = getComputedStyle(_node).display;
+            return this.set('visible', true);
+        }
+
+    }, {
+        ATTRS: {
+            /**
+             * Instead of a sprite we might have text such as the Beta banner.
+             *
+             * @attribute badge_text
+             * @default undefined
+             * @type {String}
+             */
+            badge_text: {},
+
+            /**
+             * The Banner is meant to house some message to the user provided
+             * by this content. It can be html and is not escaped for that
+             * reason.
+             *
+             * @attribute content
+             * @default undefined
+             * @type {String}
+             */
+            content: {},
+
+            /**
+             * This is listed to help aid in discovery of how the container
+             * node for the widget is determined. It's passed into the
+             * render() method and the Widget constructs itself inside of
+             * there.
+             *
+             * @attribute boundingBox
+             * @default undefined
+             * @type {Node}
+             */
+            boundingBox: {
+
+            },
+
+            /**
+             * Much of the Widget is determined by the type of banner it is.
+             * See the constants defined PRIVATE and BETA for two known types.
+             * If you set this manually you'll be able to provide custom
+             * styling as required because the type is used as a css class
+             * property.
+             *
+             * @attribute banner_type
+             * @default undefined
+             * @type {String}
+             */
+            banner_type: {},
+
+            /**
+             * Start out as not visible which should render as opacity 0, then
+             * we update it and it animates due to our css3.
+             *
+             * @attribute visible
+             * @default false
+             * @type {Bool}
+             */
+            visible: {
+                value: false
+            }
+        }
+    });
+
+    /**
+     * Beta Banner widget
+     *
+     * This is the Beta feature banner which needs to know about the title and
+     * url of the feature to construct the content correctly. Features are
+     * meant to be matched to the current LP.cache.related_features data
+     * available.
+     *
+     * @class BetaBanner
+     * @extends Banner
+     *
+     */
+    ns.BetaBanner = Y.Base.create('banner', ns.Banner, [], {
+
+    }, {
+        ATTRS: {
+            /**
+             * @attribute badge_text
+             * @default "BETA!"
+             * @type {String}
+             */
+            badge_text: {
+                value: 'BETA!'
+            },
+
+            /**
+             * The content for the beta banner is constructed from hard coded
+             * content and the list of enabled beta features currently
+             * relevant to the page.
+             *
+             * @attribute content
+             * @default {generated}
+             * @type {String}
+             */
+            content: {
+                getter: function () {
+                    var content = "Some parts of this page are in beta:&nbsp;";
+                    var key;
+                    // We need to process the features to build the features
+                    // that apply.
+                    var features = this.get('features');
+                    for (key in features) {
+                       if (features.hasOwnProperty(key)) {
+                           var obj = features[key];
+                           if (obj.is_beta) {
+                               content = content + [
+                                 '<span class="beta-feature">',
+                                 obj.title,
+                                 '&nbsp;<a class="info-link" href="',
+                                 obj.url + '">(read more)</a>',
+                                 '</span>'
+                               ].join('');
+                           }
+                        }
+                    }
+                    return content;
+                }
+            },
+
+            /**
+             * features is a nested object of the beta features going. See
+             * LP.cache.related_features for the list of features. We only
+             * want those related features that are in beta.
+             * Ex: {
+             *     disclosure.private_projects.enabled: {
+             *         is_beta: true,
+             *         title: "",
+             *         url: "http://blog.ld.net/general/private-projects-beta";,
+             *         value: "true"
+             *     }
+             * }
+             * @attribute features
+             * @default {}
+             * @type {Object}
+             */
+            features: {},
+
+            /**
+             * Manually force the banner type so users don't need to set it.
+             * This is a beta banner class.
+             *
+             * @attribute banner_type
+             * @default BETA
+             * @type {String}
+             */
+            banner_type: {
+                value: ns.BETA
+            }
+
+        }
+    });
+
+    /**
+     * Private Banner widget
+     *
+     * This is the Private feature banner which is pretty basic.
+     *
+     * Note that this doesn't automatically follow the information type code.
+     * Nor does it listen to the choice widgets and try to update. It's purely
+     * meant to function as told to do so. Most of the work around making sure
+     * the banner shows and works properly is in the View code in global.js.
+     *
+     * @class PrivateBanner
+     * @extends Banner
+     *
+     */
+    ns.PrivateBanner = Y.Base.create('banner', ns.Banner, [], {
+
+    }, {
+        ATTRS: {
+            badge_text: {
+                value: ''
+            },
+
+            content: {
+                value: 'The information on this page is private.'
+            },
+
+            /**
+             * Manually force the banner type so users don't need to set it.
+             * This is a beta banner class.
+             *
+             * @attribute banner_type
+             * @default BETA
+             * @type {String}
+             */
+            banner_type: {
+                value: ns.PRIVATE
+            }
+        }
+    });
+
+}, '0.1', {
+    requires: ['base', 'node', 'anim', 'widget', 'lp.mustache', 'yui-log']
+});

=== removed file 'lib/lp/app/javascript/ui/banner.js'
--- lib/lp/app/javascript/ui/banner.js	2012-11-09 18:55:02 +0000
+++ lib/lp/app/javascript/ui/banner.js	1970-01-01 00:00:00 +0000
@@ -1,315 +0,0 @@
-/*
- * Copyright 2012 Canonical Ltd.  This software is licensed under the
- * GNU Affero General Public License version 3 (see the file LICENSE).
- *
- * Notification banner widget
- *
- * @module lp.ui.banner
- * @namespace lp.ui
- * @module banner
- */
-YUI.add('lp.ui.banner', function (Y) {
-
-    var ns = Y.namespace('lp.ui.banner');
-
-    // GLOBALS
-    ns.PRIVATE = 'private';
-    ns.BETA = 'beta';
-
-    /**
-     * Banner widget base class
-     *
-     * This is the base Banner, you're supposed to supply some message data to
-     * generate the banner in the proper method.
-     *
-     * This banner provides all shared functionality between the Privacy and
-     * Beta banners.
-     *
-     * @class Banner
-     * @extends Y.Widget
-     *
-     */
-    ns.Banner = Y.Base.create('banner', Y.Widget, [], {
-        template: [
-            '<div class="banner">',
-                '<span class="badge {{ banner_type }}">{{ badge_text }}</span>',
-                '<span class="banner-content">{{{ content }}}</span>',
-            '</div>'
-        ].join(''),
-
-
-        /**
-         * Bind events that our widget supports such as closing the banner.
-         *
-         * We also watch the destroy event to clean up side effect css we
-         * created.
-         *
-         * @method bindUI
-         */
-        bindUI: function () {
-            this.on('destroy', function (ev) {
-                // XXX: Bug #1076074
-                var body = Y.one('body');
-                var banner_type = this.get('banner_type');
-                body.removeClass(banner_type);
-
-                // Remove any container the page might have provided for us to
-                // start out with.
-                var container_class = '.' + banner_type + '_banner_container';
-                var container = Y.one(container_class);
-                if (container) {
-                    Y.one(container_class).remove();
-                }
-            });
-
-            this.after('contentChange', function () {
-                this.renderUI();
-            });
-        },
-
-        /**
-         * Default initialize method.
-         *
-         * @method initialize
-         * @param {Object} cfg
-         */
-        initialize: function (cfg) {
-        },
-
-        /**
-         * Widget render method to generate the html of the widget.
-         *
-         * @method renderUI
-         */
-        renderUI: function () {
-            var contentBox = this.get('contentBox');
-            contentBox.addClass(this.get('banner_type'));
-            var html = Y.lp.mustache.to_html(this.template, this.getAttrs());
-            contentBox.setHTML(html);
-
-            // XXX: Bug #1076074
-            // Needs to get cleaned up. Only applies to the global
-            // banners and not to other ones which we're working to allow.
-            // This is currently required because the #locationbar is
-            // absolutely located and needs to be moved as banners change.
-            var body = Y.one('body');
-            body.addClass(this.get('banner_type'));
-
-            if (this.get('visible')) {
-                this.show();
-            }
-        },
-
-        /**
-         * We need to override show so that we force a browser repaint which
-         * allows our CSS3 animation to run. Otherwise the browser sees we
-         * added new DOM elements and jumps straight to the finished animation
-         * point.
-         *
-         * @method show
-         */
-        show: function () {
-            var _node = this.get('boundingBox')._node;
-            var getComputedStyle = document.defaultView.getComputedStyle;
-            _node.style.display = getComputedStyle(_node).display;
-            return this.set('visible', true);
-        }
-
-    }, {
-        ATTRS: {
-            /**
-             * Instead of a sprite we might have text such as the Beta banner.
-             *
-             * @attribute badge_text
-             * @default undefined
-             * @type {String}
-             */
-            badge_text: {},
-
-            /**
-             * The Banner is meant to house some message to the user provided
-             * by this content. It can be html and is not escaped for that
-             * reason.
-             *
-             * @attribute content
-             * @default undefined
-             * @type {String}
-             */
-            content: {},
-
-            /**
-             * This is listed to help aid in discovery of how the container
-             * node for the widget is determined. It's passed into the
-             * render() method and the Widget constructs itself inside of
-             * there.
-             *
-             * @attribute boundingBox
-             * @default undefined
-             * @type {Node}
-             */
-            boundingBox: {
-
-            },
-
-            /**
-             * Much of the Widget is determined by the type of banner it is.
-             * See the constants defined PRIVATE and BETA for two known types.
-             * If you set this manually you'll be able to provide custom
-             * styling as required because the type is used as a css class
-             * property.
-             *
-             * @attribute banner_type
-             * @default undefined
-             * @type {String}
-             */
-            banner_type: {},
-
-            /**
-             * Start out as not visible which should render as opacity 0, then
-             * we update it and it animates due to our css3.
-             *
-             * @attribute visible
-             * @default false
-             * @type {Bool}
-             */
-            visible: {
-                value: false
-            }
-        }
-    });
-
-    /**
-     * Beta Banner widget
-     *
-     * This is the Beta feature banner which needs to know about the title and
-     * url of the feature to construct the content correctly. Features are
-     * meant to be matched to the current LP.cache.related_features data
-     * available.
-     *
-     * @class BetaBanner
-     * @extends Banner
-     *
-     */
-    ns.BetaBanner = Y.Base.create('banner', ns.Banner, [], {
-
-    }, {
-        ATTRS: {
-            /**
-             * @attribute badge_text
-             * @default "BETA!"
-             * @type {String}
-             */
-            badge_text: {
-                value: 'BETA!'
-            },
-
-            /**
-             * The content for the beta banner is constructed from hard coded
-             * content and the list of enabled beta features currently
-             * relevant to the page.
-             *
-             * @attribute content
-             * @default {generated}
-             * @type {String}
-             */
-            content: {
-                getter: function () {
-                    var content = "Some parts of this page are in beta:&nbsp;";
-                    var key;
-                    // We need to process the features to build the features
-                    // that apply.
-                    var features = this.get('features');
-                    for (key in features) {
-                       if (features.hasOwnProperty(key)) {
-                           var obj = features[key];
-                           if (obj.is_beta) {
-                               content = content + [
-                                 '<span class="beta-feature">',
-                                 obj.title,
-                                 '&nbsp;<a class="info-link" href="',
-                                 obj.url + '">(read more)</a>',
-                                 '</span>'
-                               ].join('');
-                           }
-                        }
-                    }
-                    return content;
-                }
-            },
-
-            /**
-             * features is a nested object of the beta features going. See
-             * LP.cache.related_features for the list of features. We only
-             * want those related features that are in beta.
-             * Ex: {
-             *     disclosure.private_projects.enabled: {
-             *         is_beta: true,
-             *         title: "",
-             *         url: "http://blog.ld.net/general/private-projects-beta";,
-             *         value: "true"
-             *     }
-             * }
-             * @attribute features
-             * @default {}
-             * @type {Object}
-             */
-            features: {},
-
-            /**
-             * Manually force the banner type so users don't need to set it.
-             * This is a beta banner class.
-             *
-             * @attribute banner_type
-             * @default BETA
-             * @type {String}
-             */
-            banner_type: {
-                value: ns.BETA
-            }
-
-        }
-    });
-
-    /**
-     * Private Banner widget
-     *
-     * This is the Private feature banner which is pretty basic.
-     *
-     * Note that this doesn't automatically follow the information type code.
-     * Nor does it listen to the choice widgets and try to update. It's purely
-     * meant to function as told to do so. Most of the work around making sure
-     * the banner shows and works properly is in the View code in global.js.
-     *
-     * @class PrivateBanner
-     * @extends Banner
-     *
-     */
-    ns.PrivateBanner = Y.Base.create('banner', ns.Banner, [], {
-
-    }, {
-        ATTRS: {
-            badge_text: {
-                value: ''
-            },
-
-            content: {
-                value: 'The information on this page is private.'
-            },
-
-            /**
-             * Manually force the banner type so users don't need to set it.
-             * This is a beta banner class.
-             *
-             * @attribute banner_type
-             * @default BETA
-             * @type {String}
-             */
-            banner_type: {
-                value: ns.PRIVATE
-            }
-        }
-    });
-
-}, '0.1', {
-    requires: ['base', 'node', 'anim', 'widget', 'lp.mustache', 'yui-log']
-});

=== added directory 'lib/lp/app/javascript/ui/tests'
=== removed directory 'lib/lp/app/javascript/ui/tests'
=== added file 'lib/lp/app/javascript/ui/tests/test_banner.html'
--- lib/lp/app/javascript/ui/tests/test_banner.html	1970-01-01 00:00:00 +0000
+++ lib/lp/app/javascript/ui/tests/test_banner.html	2012-11-12 13:05:28 +0000
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!--
+Copyright 2012 Canonical Ltd.  This software is licensed under the
+GNU Affero General Public License version 3 (see the file LICENSE).
+-->
+
+<html>
+  <head>
+      <title>lp.ui.banner Tests</title>
+
+      <!-- YUI and test setup -->
+      <script type="text/javascript"
+              src="../../../../../../build/js/yui/yui/yui.js">
+      </script>
+      <link rel="stylesheet"
+      href="../../../../../../build/js/yui/console/assets/console-core.css" />
+      <link rel="stylesheet"
+      href="../../../../../../build/js/yui/test-console/assets/skins/sam/test-console.css" />
+      <link rel="stylesheet"
+      href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
+
+      <script type="text/javascript"
+              src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
+      <script type="text/javascript"
+              src="../../../../../build/js/lp/app/testing/helpers.js"></script>
+
+      <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
+
+      <!-- Dependencies -->
+      <script type="text/javascript"
+          src="../../../../../../build/js/lp/app/mustache.js"></script>
+
+      <!-- The module under test. -->
+      <script type="text/javascript" src="../banner.js"></script>
+
+      <!-- Placeholder for any css asset for this module. -->
+      <link rel="stylesheet" href="../assets/skins/sam/banner.css" />
+
+      <!-- The test suite -->
+      <script type="text/javascript" src="test_banner.js"></script>
+
+    </head>
+    <body class="yui3-skin-sam">
+        <ul id="suites">
+            <li>lp.ui.banner.test</li>
+        </ul>
+        <div id="fixture"></div>
+    </body>
+</html>

=== removed file 'lib/lp/app/javascript/ui/tests/test_banner.html'
--- lib/lp/app/javascript/ui/tests/test_banner.html	2012-11-09 18:46:31 +0000
+++ lib/lp/app/javascript/ui/tests/test_banner.html	1970-01-01 00:00:00 +0000
@@ -1,49 +0,0 @@
-<!DOCTYPE html>
-<!--
-Copyright 2012 Canonical Ltd.  This software is licensed under the
-GNU Affero General Public License version 3 (see the file LICENSE).
--->
-
-<html>
-  <head>
-      <title>lp.ui.banner Tests</title>
-
-      <!-- YUI and test setup -->
-      <script type="text/javascript"
-              src="../../../../../../build/js/yui/yui/yui.js">
-      </script>
-      <link rel="stylesheet"
-      href="../../../../../../build/js/yui/console/assets/console-core.css" />
-      <link rel="stylesheet"
-      href="../../../../../../build/js/yui/test-console/assets/skins/sam/test-console.css" />
-      <link rel="stylesheet"
-      href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
-
-      <script type="text/javascript"
-              src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
-      <script type="text/javascript"
-              src="../../../../../build/js/lp/app/testing/helpers.js"></script>
-
-      <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
-
-      <!-- Dependencies -->
-      <script type="text/javascript"
-          src="../../../../../../build/js/lp/app/mustache.js"></script>
-
-      <!-- The module under test. -->
-      <script type="text/javascript" src="../banner.js"></script>
-
-      <!-- Placeholder for any css asset for this module. -->
-      <link rel="stylesheet" href="../assets/skins/sam/banner.css" />
-
-      <!-- The test suite -->
-      <script type="text/javascript" src="test_banner.js"></script>
-
-    </head>
-    <body class="yui3-skin-sam">
-        <ul id="suites">
-            <li>lp.ui.banner.test</li>
-        </ul>
-        <div id="fixture"></div>
-    </body>
-</html>

=== added file 'lib/lp/app/javascript/ui/tests/test_banner.js'
--- lib/lp/app/javascript/ui/tests/test_banner.js	1970-01-01 00:00:00 +0000
+++ lib/lp/app/javascript/ui/tests/test_banner.js	2012-11-12 13:05:28 +0000
@@ -0,0 +1,289 @@
+/* Copyright (c) 2012 Canonical Ltd. All rights reserved. */
+
+YUI.add('lp.ui.banner.test', function (Y) {
+
+    var tests = Y.namespace('lp.ui.banner.test');
+    tests.suite = new Y.Test.Suite('ui.banner Tests');
+
+    var ns = Y.lp.ui.banner;
+
+    tests.suite.add(new Y.Test.Case({
+        name: 'ui.banner_tests',
+
+        setUp: function () {
+            this.container = Y.one('#fixture');
+        },
+
+        tearDown: function () {
+            this.container.empty();
+        },
+
+        test_library_exists: function () {
+            Y.Assert.isObject(Y.lp.ui.banner,
+                "Could not locate the lp.ui.banner module");
+        },
+
+        test_render: function () {
+            var b = new ns.Banner();
+            b.render(this.container);
+
+            var banners = Y.all('.banner');
+            Y.Assert.areEqual(
+                1,
+                banners._nodes.length,
+                'We have one banner node');
+
+            // The banner should make sure it's in the container as well.
+            var contained_banners = Y.all('#fixture .banner');
+            Y.Assert.areEqual(
+                1,
+                contained_banners._nodes.length,
+                'Banner node is placed.');
+        },
+
+        test_render_content: function () {
+            var msg = 'This is a banner message. Fear me.',
+                b = new ns.Banner({
+                    content: msg
+                });
+
+            b.render(this.container);
+
+            var banner = Y.one('.banner');
+            Y.Assert.areEqual(
+                msg,
+                banner.one('.banner-content').get('text')
+            );
+        },
+
+        test_render_private_type: function () {
+            var msg = 'Private!',
+                b = new ns.Banner({
+                    content: msg,
+                    banner_type: ns.PRIVATE
+                });
+
+            b.render(this.container);
+
+            var banner = Y.one('.banner');
+            Y.Assert.areEqual(
+                msg,
+                banner.one('.banner-content').get('text'),
+                'The banner should have the private message.'
+            );
+
+            var badge = banner.one('.badge');
+
+            Y.Assert.isTrue(
+                badge.hasClass('private'),
+                'The badge should have a private class on it.');
+
+        },
+
+        test_render_beta_type: function () {
+            var msg = 'BETA!',
+                b = new ns.Banner({
+                    content: msg,
+                    banner_type: ns.BETA
+                });
+
+            b.render(this.container);
+
+            var banner = Y.one('.banner');
+            Y.Assert.areEqual(
+                msg,
+                banner.one('.banner-content').get('text'),
+                'The banner should have the beta message.'
+            );
+
+            var badge = banner.one('.badge');
+
+            Y.Assert.isTrue(
+                badge.hasClass('beta'),
+                'The badge should have a beta class on it.');
+        },
+
+        test_render_badge_text: function () {
+            // We can set the badge to contain text.
+            var badge = 'BETA!',
+                b = new ns.Banner({
+                    badge_text: badge,
+                    banner_type: ns.BETA
+                });
+
+            b.render(this.container);
+            var banner = Y.one('.banner');
+            Y.Assert.areEqual(
+                badge,
+                banner.one('.badge').get('text'),
+                'The badge should have the beta message.'
+            );
+        },
+
+        test_banner_text_update: function () {
+            // The banner should update the rendered text when the content
+            // ATTR is changed.
+            var msg = 'This is a banner message. Fear me.',
+                b = new ns.Banner({
+                    content: msg
+                });
+
+            b.render(this.container);
+
+            var banner = Y.one('.banner');
+            Y.Assert.areEqual(
+                msg,
+                banner.one('.banner-content').get('text')
+            );
+
+            // Now change the content on the widget and check again.
+            var new_msg = 'Updated me!';
+            b.set('content', new_msg);
+            banner = Y.one('.banner');
+            Y.Assert.areEqual(
+                new_msg,
+                banner.one('.banner-content').get('text')
+            );
+        }
+    }));
+
+
+    tests.suite.add(new Y.Test.Case({
+        name: 'ui.beta_banner_tests',
+
+        setUp: function () {
+            this.container = Y.one('#fixture');
+        },
+
+        tearDown: function () {
+            this.container.empty();
+        },
+
+        test_base_beta_banner: function () {
+            // The beta banner is auto set to the right type, has the right
+            // badge text.
+            var badge = 'BETA!',
+                msg = 'are in beta:',
+                b = new ns.BetaBanner({
+                });
+
+            b.render(this.container);
+            var banner = Y.one('.banner');
+            Y.Assert.areEqual(
+                badge,
+                banner.one('.badge').get('text'),
+                'The badge should have the beta message.'
+            );
+
+            Y.Assert.areEqual(
+                ns.BETA,
+                b.get('banner_type'),
+                'The banner should be the right type.'
+            );
+
+            Y.Assert.areNotEqual(
+                -1,
+                banner.one('.banner-content').get('text').indexOf(msg),
+                'The badge should have beta content.'
+            );
+        },
+
+        test_beta_features: function () {
+            // The features fed to the banner effect display of the messages.
+            var features = {
+                private_projects: {
+                    is_beta: true,
+                    title: "Private Projects",
+                    url: "http://blog.ld.net/general/private-projects-beta";,
+                    value: "true"
+                },
+                test_projects: {
+                    is_beta: true,
+                    title: "Test Projects",
+                    url: "http://blog.ld.net/general/private-projects-beta";,
+                    value: "true"
+                },
+                no_beta: {
+                    is_beta: false,
+                    title: "Better not see me",
+                    url: "http://blog.ld.net/general/private-projects-beta";,
+                    value: "true"
+                },
+            };
+
+            var b = new ns.BetaBanner({
+                    features: features
+            });
+
+            b.render(this.container);
+
+            var banner = Y.one('.banner'),
+                banner_content = banner.one('.banner-content').get('text');
+
+            Y.Assert.areNotEqual(
+                -1,
+                banner_content.indexOf(features.private_projects.title),
+                'The private projects feature should be displayed.'
+            );
+
+            Y.Assert.areNotEqual(
+                -1,
+                banner_content.indexOf(features.test_projects.title),
+                'Also test projects since we support multiple features.'
+            );
+
+            Y.Assert.areEqual(
+                -1,
+                banner_content.indexOf(features.no_beta.title),
+                'But not no beta since we only support beta features.'
+            );
+        },
+
+
+    }));
+
+
+    tests.suite.add(new Y.Test.Case({
+        name: 'ui.private_banner_tests',
+
+        setUp: function () {
+            this.container = Y.one('#fixture');
+        },
+
+        tearDown: function () {
+            this.container.empty();
+        },
+
+        test_base_private_banner: function () {
+            // The private banner is auto set to the right type, has the right
+            // badge text.
+            var badge = '',
+                msg = 'page is private',
+                b = new ns.PrivateBanner({
+                });
+
+            b.render(this.container);
+            var banner = Y.one('.banner');
+            Y.Assert.areEqual(
+                badge,
+                banner.one('.badge').get('text'),
+                'The badge should be empty'
+            );
+
+            Y.Assert.areEqual(
+                ns.PRIVATE,
+                b.get('banner_type'),
+                'The banner should be the right type.'
+            );
+
+            Y.Assert.areNotEqual(
+                -1,
+                banner.one('.banner-content').get('text').indexOf(msg),
+                'The badge should have a private warning.'
+            );
+        }
+    }));
+
+}, '0.1', {
+    requires: ['test', 'lp.testing.helpers', 'lp.ui.banner']
+});

=== removed file 'lib/lp/app/javascript/ui/tests/test_banner.js'
--- lib/lp/app/javascript/ui/tests/test_banner.js	2012-11-09 18:55:02 +0000
+++ lib/lp/app/javascript/ui/tests/test_banner.js	1970-01-01 00:00:00 +0000
@@ -1,287 +0,0 @@
-/* Copyright (c) 2012 Canonical Ltd. All rights reserved. */
-
-YUI.add('lp.ui.banner.test', function (Y) {
-
-    var tests = Y.namespace('lp.ui.banner.test');
-    tests.suite = new Y.Test.Suite('ui.banner Tests');
-
-    var ns = Y.lp.ui.banner;
-
-    tests.suite.add(new Y.Test.Case({
-        name: 'ui.banner_tests',
-
-        setUp: function () {
-            this.container = Y.one('#fixture');
-        },
-
-        tearDown: function () {
-            this.container.empty();
-        },
-
-        test_library_exists: function () {
-            Y.Assert.isObject(Y.lp.ui.banner,
-                "Could not locate the lp.ui.banner module");
-        },
-
-        test_render: function () {
-            var b = new ns.Banner();
-            b.render(this.container);
-
-            var banners = Y.all('.banner');
-            Y.Assert.areEqual(
-                1,
-                banners._nodes.length,
-                'We have one banner node');
-
-            // The banner should make sure it's in the container as well.
-            var contained_banners = Y.all('#fixture .banner');
-            Y.Assert.areEqual(
-                1,
-                contained_banners._nodes.length,
-                'Banner node is placed.');
-        },
-
-        test_render_content: function () {
-            var msg = 'This is a banner message. Fear me.',
-                b = new ns.Banner({
-                    content: msg
-                });
-
-            b.render(this.container);
-
-            var banner = Y.one('.banner');
-            Y.Assert.areEqual(
-                msg,
-                banner.one('.banner-content').get('text')
-            );
-        },
-
-        test_render_private_type: function () {
-            var msg = 'Private!',
-                b = new ns.Banner({
-                    content: msg,
-                    banner_type: ns.PRIVATE
-                });
-
-            b.render(this.container);
-
-            var banner = Y.one('.banner');
-            Y.Assert.areEqual(
-                msg,
-                banner.one('.banner-content').get('text'),
-                'The banner should have the private message.'
-            );
-
-            var badge = banner.one('.badge');
-
-            Y.Assert.isTrue(
-                badge.hasClass('private'),
-                'The badge should have a private class on it.');
-
-        },
-
-        test_render_beta_type: function () {
-            var msg = 'BETA!',
-                b = new ns.Banner({
-                    content: msg,
-                    banner_type: ns.BETA
-                });
-
-            b.render(this.container);
-
-            var banner = Y.one('.banner');
-            Y.Assert.areEqual(
-                msg,
-                banner.one('.banner-content').get('text'),
-                'The banner should have the beta message.'
-            );
-
-            var badge = banner.one('.badge');
-
-            Y.Assert.isTrue(
-                badge.hasClass('beta'),
-                'The badge should have a beta class on it.');
-        },
-
-        test_render_badge_text: function () {
-            // We can set the badge to contain text.
-            var badge = 'BETA!',
-                b = new ns.Banner({
-                    badge_text: badge,
-                    banner_type: ns.BETA
-                });
-
-            b.render(this.container);
-            var banner = Y.one('.banner');
-            Y.Assert.areEqual(
-                badge,
-                banner.one('.badge').get('text'),
-                'The badge should have the beta message.'
-            );
-        },
-
-        test_banner_text_update: function () {
-            // The banner should update the rendered text when the content
-            // ATTR is changed.
-            var msg = 'This is a banner message. Fear me.',
-                b = new ns.Banner({
-                    content: msg
-                });
-
-            b.render(this.container);
-
-            var banner = Y.one('.banner');
-            Y.Assert.areEqual(
-                msg,
-                banner.one('.banner-content').get('text')
-            );
-
-            // Now change the content on the widget and check again.
-            var new_msg = 'Updated me!';
-            b.set('content', new_msg);
-            banner = Y.one('.banner');
-            Y.Assert.areEqual(
-                new_msg,
-                banner.one('.banner-content').get('text')
-            );
-        }
-    }));
-
-
-    tests.suite.add(new Y.Test.Case({
-        name: 'ui.beta_banner_tests',
-
-        setUp: function () {
-            this.container = Y.one('#fixture');
-        },
-
-        tearDown: function () {
-            this.container.empty();
-        },
-
-        test_base_beta_banner: function () {
-            // The beta banner is auto set to the right type, has the right
-            // badge text.
-            var badge = 'BETA!',
-                msg = 'are in beta:',
-                b = new ns.BetaBanner({
-                });
-
-            b.render(this.container);
-            var banner = Y.one('.banner');
-            Y.Assert.areEqual(
-                badge,
-                banner.one('.badge').get('text'),
-                'The badge should have the beta message.'
-            );
-
-            Y.Assert.areEqual(
-                ns.BETA,
-                b.get('banner_type'),
-                'The banner should be the right type.'
-            );
-
-            Y.Assert.areNotEqual(
-                -1,
-                banner.one('.banner-content').get('text').indexOf(msg),
-                'The badge should have beta content.'
-            );
-        },
-
-        test_beta_features: function () {
-            // The features fed to the banner effect display of the messages.
-            var features = {
-                private_projects: {
-                    is_beta: true,
-                    title: "Private Projects",
-                    url: "http://blog.ld.net/general/private-projects-beta";,
-                    value: "true"
-                },
-                test_projects: {
-                    is_beta: true,
-                    title: "Test Projects",
-                    url: "http://blog.ld.net/general/private-projects-beta";,
-                    value: "true"
-                },
-                no_beta: {
-                    is_beta: false,
-                    title: "Better not see me",
-                    url: "http://blog.ld.net/general/private-projects-beta";,
-                    value: "true"
-                }
-            };
-
-            var b = new ns.BetaBanner({
-                    features: features
-            });
-
-            b.render(this.container);
-
-            var banner = Y.one('.banner'),
-                banner_content = banner.one('.banner-content').get('text');
-
-            Y.Assert.areNotEqual(
-                -1,
-                banner_content.indexOf(features.private_projects.title),
-                'The private projects feature should be displayed.'
-            );
-
-            Y.Assert.areNotEqual(
-                -1,
-                banner_content.indexOf(features.test_projects.title),
-                'Also test projects since we support multiple features.'
-            );
-
-            Y.Assert.areEqual(
-                -1,
-                banner_content.indexOf(features.no_beta.title),
-                'But not no beta since we only support beta features.'
-            );
-        }
-    }));
-
-
-    tests.suite.add(new Y.Test.Case({
-        name: 'ui.private_banner_tests',
-
-        setUp: function () {
-            this.container = Y.one('#fixture');
-        },
-
-        tearDown: function () {
-            this.container.empty();
-        },
-
-        test_base_private_banner: function () {
-            // The private banner is auto set to the right type, has the right
-            // badge text.
-            var badge = '',
-                msg = 'page is private',
-                b = new ns.PrivateBanner({
-                });
-
-            b.render(this.container);
-            var banner = Y.one('.banner');
-            Y.Assert.areEqual(
-                badge,
-                banner.one('.badge').get('text'),
-                'The badge should be empty'
-            );
-
-            Y.Assert.areEqual(
-                ns.PRIVATE,
-                b.get('banner_type'),
-                'The banner should be the right type.'
-            );
-
-            Y.Assert.areNotEqual(
-                -1,
-                banner.one('.banner-content').get('text').indexOf(msg),
-                'The badge should have a private warning.'
-            );
-        }
-    }));
-
-}, '0.1', {
-    requires: ['test', 'lp.testing.helpers', 'lp.ui.banner']
-});

=== added directory 'lib/lp/app/javascript/views'
=== removed directory 'lib/lp/app/javascript/views'
=== added file 'lib/lp/app/javascript/views/global.js'
--- lib/lp/app/javascript/views/global.js	1970-01-01 00:00:00 +0000
+++ lib/lp/app/javascript/views/global.js	2012-11-12 13:05:28 +0000
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2012 Canonical Ltd.  This software is licensed under the
+ * GNU Affero General Public License version 3 (see the file LICENSE).
+ *
+ * Global View handler for Launchpad
+ *
+ * @module lp.views.Global
+ * @namespace lp.views
+ * @module global
+ */
+YUI.add('lp.views.global', function (Y) {
+
+    var ns = Y.namespace('lp.views'),
+        ui = Y.namespace('lp.ui'),
+        info_type = Y.namespace('lp.app.information_type');
+
+    /**
+     * Provides a Y.View that controls all of the things that need handling on
+     * every single request. All code currently in the base-layout-macros
+     * should eventually moved into here to be loaded via the render() method.
+     * Events bound as required, etc.
+     *
+     * @class Global
+     * @extends Y.View
+     */
+    ns.Global = Y.Base.create('lp-views-global', Y.View, [], {
+        _events: [],
+
+        /**
+         * Watch for page level events in all pages.
+         *
+         * @method _bind_events
+         */
+        _bind_events: function () {
+            var that = this;
+
+            // Watch for any changes in information type.
+            this._events.push(Y.on(info_type.EV_ISPUBLIC, function (ev) {
+                // Remove the banner if there is one.
+                if (that._private_banner) {
+                    that._private_banner.hide();
+                    that._private_banner.destroy(true);
+                    that._private_banner = null;
+                    // XXX: Bug #1076074
+                    var body = Y.one('body');
+                    body.addClass('public');
+                }
+            }));
+
+            // If the information type is changed to private, and we don't
+            // currently have a privacy banner, then create a new one and set
+            // it up.
+            this._events.push(Y.on(info_type.EV_ISPRIVATE, function (ev) {
+                // Create a Private banner if there is not currently one.
+                if (!that._private_banner) {
+                    that._private_banner = new ui.banner.PrivateBanner({
+                        content: ev.text
+                    });
+
+                    // There is no current container for the banner since
+                    // we're creating it on the fly.
+                    var container = Y.Node.create('<div>');
+
+                    // XXX: Bug #1076074
+                    var body = Y.one('body');
+                    body.removeClass('public');
+                    that._private_banner.render(container);
+                    // Only append the content to the DOM after the rest is
+                    // one to avoid any repaints on the browser end.
+                    body.prepend(container);
+                    that._private_banner.show();
+                } else {
+                    // The banner is there but we need to update text.
+                    that._private_banner.set('content', ev.text);
+                }
+            }));
+        },
+
+        /**
+         * Clean up the view and its event bindings when destroyed.
+         *
+         * @method _destroy
+         * @param {Event} ev
+         * @private
+         */
+        _destroy: function (ev) {
+            var index;
+            for (index in this._events) {
+                event = this._events[index];
+                event.detach();
+            }
+        },
+
+        _init_banners: function () {
+            var that = this;
+
+            // On page load the banner container already exists. This is so
+            // that the height of the page is already determined.
+            var is_beta = Y.one('.beta_banner_container');
+            if (is_beta) {
+                that._beta_banner = new ui.banner.BetaBanner({
+                    features: LP.cache.related_features
+                });
+                that._beta_banner.render(is_beta);
+                // We delay the show until the page is ready so we get our
+                // pretty css3 animation that distracts the user a bit.
+                Y.after('load', function (ev) {
+                    that._beta_banner.show();
+                });
+            }
+
+            // On page load the banner container already exists. This is so
+            // that the height of the page is already determined.
+            var is_private = Y.one('.private_banner_container');
+            if (is_private) {
+                that._private_banner = new ui.banner.PrivateBanner();
+                that._private_banner.render(is_private);
+                // We delay the show until the page is ready so we get our
+                // pretty css3 animation that distracts the user a bit.
+                Y.on('load', function (ev) {
+                    that._private_banner.show();
+                });
+            }
+        },
+
+        initialize: function (cfg) {},
+
+        render: function () {
+            this._bind_events();
+            this.on('destroy', this._destroy, this);
+            this._init_banners();
+        }
+
+    }, {
+        ATTRS: {
+
+        }
+    });
+
+}, '0.1', {
+    requires: ['base', 'view', 'lp.ui.banner', 'lp.app.information_type']
+});

=== removed file 'lib/lp/app/javascript/views/global.js'
--- lib/lp/app/javascript/views/global.js	2012-11-09 18:46:31 +0000
+++ lib/lp/app/javascript/views/global.js	1970-01-01 00:00:00 +0000
@@ -1,142 +0,0 @@
-/*
- * Copyright 2012 Canonical Ltd.  This software is licensed under the
- * GNU Affero General Public License version 3 (see the file LICENSE).
- *
- * Global View handler for Launchpad
- *
- * @module lp.views.Global
- * @namespace lp.views
- * @module global
- */
-YUI.add('lp.views.global', function (Y) {
-
-    var ns = Y.namespace('lp.views'),
-        ui = Y.namespace('lp.ui'),
-        info_type = Y.namespace('lp.app.information_type');
-
-    /**
-     * Provides a Y.View that controls all of the things that need handling on
-     * every single request. All code currently in the base-layout-macros
-     * should eventually moved into here to be loaded via the render() method.
-     * Events bound as required, etc.
-     *
-     * @class Global
-     * @extends Y.View
-     */
-    ns.Global = Y.Base.create('lp-views-global', Y.View, [], {
-        _events: [],
-
-        /**
-         * Watch for page level events in all pages.
-         *
-         * @method _bind_events
-         */
-        _bind_events: function () {
-            var that = this;
-
-            // Watch for any changes in information type.
-            this._events.push(Y.on(info_type.EV_ISPUBLIC, function (ev) {
-                // Remove the banner if there is one.
-                if (that._private_banner) {
-                    that._private_banner.hide();
-                    that._private_banner.destroy(true);
-                    that._private_banner = null;
-                    // XXX: Bug #1076074
-                    var body = Y.one('body');
-                    body.addClass('public');
-                }
-            }));
-
-            // If the information type is changed to private, and we don't
-            // currently have a privacy banner, then create a new one and set
-            // it up.
-            this._events.push(Y.on(info_type.EV_ISPRIVATE, function (ev) {
-                // Create a Private banner if there is not currently one.
-                if (!that._private_banner) {
-                    that._private_banner = new ui.banner.PrivateBanner({
-                        content: ev.text
-                    });
-
-                    // There is no current container for the banner since
-                    // we're creating it on the fly.
-                    var container = Y.Node.create('<div>');
-
-                    // XXX: Bug #1076074
-                    var body = Y.one('body');
-                    body.removeClass('public');
-                    that._private_banner.render(container);
-                    // Only append the content to the DOM after the rest is
-                    // one to avoid any repaints on the browser end.
-                    body.prepend(container);
-                    that._private_banner.show();
-                } else {
-                    // The banner is there but we need to update text.
-                    that._private_banner.set('content', ev.text);
-                }
-            }));
-        },
-
-        /**
-         * Clean up the view and its event bindings when destroyed.
-         *
-         * @method _destroy
-         * @param {Event} ev
-         * @private
-         */
-        _destroy: function (ev) {
-            var index;
-            for (index in this._events) {
-                event = this._events[index];
-                event.detach();
-            }
-        },
-
-        _init_banners: function () {
-            var that = this;
-
-            // On page load the banner container already exists. This is so
-            // that the height of the page is already determined.
-            var is_beta = Y.one('.beta_banner_container');
-            if (is_beta) {
-                that._beta_banner = new ui.banner.BetaBanner({
-                    features: LP.cache.related_features
-                });
-                that._beta_banner.render(is_beta);
-                // We delay the show until the page is ready so we get our
-                // pretty css3 animation that distracts the user a bit.
-                Y.after('load', function (ev) {
-                    that._beta_banner.show();
-                });
-            }
-
-            // On page load the banner container already exists. This is so
-            // that the height of the page is already determined.
-            var is_private = Y.one('.private_banner_container');
-            if (is_private) {
-                that._private_banner = new ui.banner.PrivateBanner();
-                that._private_banner.render(is_private);
-                // We delay the show until the page is ready so we get our
-                // pretty css3 animation that distracts the user a bit.
-                Y.on('load', function (ev) {
-                    that._private_banner.show();
-                });
-            }
-        },
-
-        initialize: function (cfg) {},
-
-        render: function () {
-            this._bind_events();
-            this.on('destroy', this._destroy, this);
-            this._init_banners();
-        }
-
-    }, {
-        ATTRS: {
-
-        }
-    });
-
-}, '0.1', {
-    requires: ['base', 'view', 'lp.ui.banner', 'lp.app.information_type']
-});

=== added directory 'lib/lp/app/javascript/views/tests'
=== removed directory 'lib/lp/app/javascript/views/tests'
=== added file 'lib/lp/app/javascript/views/tests/test_global.html'
--- lib/lp/app/javascript/views/tests/test_global.html	1970-01-01 00:00:00 +0000
+++ lib/lp/app/javascript/views/tests/test_global.html	2012-11-12 13:05:28 +0000
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+
+<!--
+Copyright 2012 Canonical Ltd.  This software is licensed under the
+GNU Affero General Public License version 3 (see the file LICENSE).
+-->
+
+<html>
+  <head>
+      <title>Product New Tests</title>
+
+      <!-- YUI and test setup -->
+      <script type="text/javascript"
+              src="../../../../../../build/js/yui/yui/yui.js">
+      </script>
+      <link rel="stylesheet"
+      href="../../../../../../build/js/yui/console/assets/console-core.css" />
+      <link rel="stylesheet"
+      href="../../../../../../build/js/yui/test-console/assets/skins/sam/test-console.css" />
+      <link rel="stylesheet"
+      href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
+
+      <script type="text/javascript"
+              src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
+
+      <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
+
+      <!-- Dependencies -->
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/client.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/choice.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/ellipsis.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/expander.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/errors.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/information_type.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/lp.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/mustache.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/anim/anim.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/extras/extras.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/choiceedit/choiceedit.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/effects/effects.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/formoverlay/formoverlay.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/formwidgets/resizing_textarea.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/inlineedit/editor.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/testing/helpers.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/overlay/overlay.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/ui/ui.js"></script>
+      <script type="text/javascript" src="../../../../../../build/js/lp/app/ui/banner.js"></script>
+
+      <!-- The module under test. -->
+      <script type="text/javascript" src="../global.js"></script>
+
+      <!-- The test suite -->
+      <script type="text/javascript" src="test_global.js"></script>
+
+    </head>
+    <body class="yui3-skin-sam">
+        <ul id="suites">
+            <li>lp.views.global.test</li>
+        </ul>
+        <div id="fixture"></div>
+    </body>
+</html>

=== removed file 'lib/lp/app/javascript/views/tests/test_global.html'
--- lib/lp/app/javascript/views/tests/test_global.html	2012-11-09 18:46:31 +0000
+++ lib/lp/app/javascript/views/tests/test_global.html	1970-01-01 00:00:00 +0000
@@ -1,62 +0,0 @@
-<!DOCTYPE html>
-
-<!--
-Copyright 2012 Canonical Ltd.  This software is licensed under the
-GNU Affero General Public License version 3 (see the file LICENSE).
--->
-
-<html>
-  <head>
-      <title>Product New Tests</title>
-
-      <!-- YUI and test setup -->
-      <script type="text/javascript"
-              src="../../../../../../build/js/yui/yui/yui.js">
-      </script>
-      <link rel="stylesheet"
-      href="../../../../../../build/js/yui/console/assets/console-core.css" />
-      <link rel="stylesheet"
-      href="../../../../../../build/js/yui/test-console/assets/skins/sam/test-console.css" />
-      <link rel="stylesheet"
-      href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
-
-      <script type="text/javascript"
-              src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
-
-      <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
-
-      <!-- Dependencies -->
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/client.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/choice.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/ellipsis.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/expander.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/errors.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/information_type.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/lp.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/mustache.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/anim/anim.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/extras/extras.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/choiceedit/choiceedit.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/effects/effects.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/formoverlay/formoverlay.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/formwidgets/resizing_textarea.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/inlineedit/editor.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/testing/helpers.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/overlay/overlay.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/ui/ui.js"></script>
-      <script type="text/javascript" src="../../../../../../build/js/lp/app/ui/banner.js"></script>
-
-      <!-- The module under test. -->
-      <script type="text/javascript" src="../global.js"></script>
-
-      <!-- The test suite -->
-      <script type="text/javascript" src="test_global.js"></script>
-
-    </head>
-    <body class="yui3-skin-sam">
-        <ul id="suites">
-            <li>lp.views.global.test</li>
-        </ul>
-        <div id="fixture"></div>
-    </body>
-</html>

=== added file 'lib/lp/app/javascript/views/tests/test_global.js'
--- lib/lp/app/javascript/views/tests/test_global.js	1970-01-01 00:00:00 +0000
+++ lib/lp/app/javascript/views/tests/test_global.js	2012-11-12 13:05:28 +0000
@@ -0,0 +1,164 @@
+/* Copyright (c) 2012 Canonical Ltd. All rights reserved. */
+
+YUI.add('lp.views.global.test', function (Y) {
+    var tests = Y.namespace('lp.views.global.test');
+    var info_type = Y.namespace('lp.app.information_type');
+    var ns = Y.lp.views;
+
+    tests.suite = new Y.Test.Suite('lp.views.global test');
+    tests.suite.add(new Y.Test.Case({
+        name: 'lp.views.global',
+
+        setUp: function () {
+            this.container = Y.one('#fixture');
+            window.LP = {
+                cache: {
+                    related_features: {
+                        private_projects: {
+                            is_beta: true,
+                            title: "Private Projects",
+                            url: "http://blog.ld.net/general/private-projects-beta";,
+                            value: "true"
+                        }
+                    },
+                    information_type_data: {
+                        PUBLIC: {
+                            value: 'PUBLIC', name: 'Public',
+                            is_private: false, order: 1,
+                            description: 'Public Description'
+                        },
+                        EMBARGOED: {
+                            value: 'EMBARGOED', name: 'Embargoed',
+                            is_private: true, order: 2,
+                            description: 'Something embargoed'
+                        },
+                        PROPRIETARY: {
+                            value: 'PROPRIETARY', name: 'Proprietary',
+                            is_private: true, order: 3,
+                            description: 'Private Description'
+                        }
+                    }
+                }
+            };
+        },
+
+        tearDown: function () {
+            this.container.empty();
+        },
+
+        test_library_exists: function () {
+            Y.Assert.isObject(ns.Global,
+                "Could not locate the lp.views.global module");
+        },
+
+        test_basic_render: function () {
+            // Nothing is currently rendered out by default.
+            var view = new ns.Global();
+            view.render();
+
+            Y.Assert.areEqual(
+                '',
+                this.container.get('innerHTML'),
+                'The container is still empty.');
+            view.destroy();
+        },
+
+        test_beta_banner: function () {
+            // If we've prepped on load a beta banner will auto appear.
+            var banner_container = Y.Node.create('<div/>');
+            banner_container.addClass('beta_banner_container');
+            this.container.append(banner_container);
+            var view = new ns.Global();
+            view.render();
+
+            // We have to wait until after page load event fires to test
+            // things out.
+            var banner_node = Y.one('.banner');
+            Y.Assert.isObject(
+                banner_node,
+                'The container has a new banner node in there.');
+
+            view.destroy();
+        },
+
+        test_privacy: function () {
+            var beta_container = Y.Node.create('<div/>');
+            var private_container = Y.Node.create('<div/>');
+
+            beta_container.addClass('beta_banner_container');
+            private_container.addClass('private_banner_container');
+
+            this.container.append(beta_container);
+            this.container.append(private_container);
+            var view = new ns.Global();
+            view.render();
+
+            // We have to wait until after page load event fires to test
+            // things out.
+            var banner_nodes = Y.all('.banner');
+            Y.Assert.areEqual(
+                2,
+                banner_nodes._nodes.length,
+                'We should have two banners rendered.');
+
+            view.destroy();
+        },
+
+        test_privacy_banner_from_event: function () {
+            // We can also get a privacy banner via a fired event.
+            // This is hard coded to the <body> tag so we have to do some
+            // manual clean up here.
+            var view = new ns.Global();
+            view.render();
+
+            var msg = 'Testing Global';
+            Y.fire(info_type.EV_ISPRIVATE, {
+                text: msg
+            });
+
+            var banner = Y.one('.banner');
+            var banner_text = banner.one('.banner-content').get('text');
+            Y.Assert.areNotEqual(
+                -1,
+                banner_text.indexOf(msg),
+                'The event text is turned into the banner content');
+
+           // Manually clean up.
+           Y.one('.yui3-banner').remove(true);
+           view.destroy();
+        },
+
+        test_banner_updates_content: function () {
+            // If we change our privacy information type the banner needs to
+            // update the content to the new text value from the event.
+            var view = new ns.Global();
+            view.render();
+
+            var msg = 'Testing Global';
+            Y.fire(info_type.EV_ISPRIVATE, {
+                text: msg
+            });
+
+            var updated_msg = 'Updated content'
+            Y.fire(info_type.EV_ISPRIVATE, {
+                text: updated_msg
+            });
+
+            var banner = Y.one('.banner');
+            var banner_text = banner.one('.banner-content').get('text');
+            Y.Assert.areNotEqual(
+                -1,
+                banner_text.indexOf(updated_msg),
+                'The banner updated content to the second event message.');
+
+           // Manually clean up.
+           Y.one('.yui3-banner').remove(true);
+           view.destroy();
+
+        }
+    }));
+
+}, '0.1', {
+    requires: ['test', 'event-simulate', 'node-event-simulate',
+               'lp.app.information_type', 'lp.views.global']
+});

=== removed file 'lib/lp/app/javascript/views/tests/test_global.js'
--- lib/lp/app/javascript/views/tests/test_global.js	2012-11-09 18:55:02 +0000
+++ lib/lp/app/javascript/views/tests/test_global.js	1970-01-01 00:00:00 +0000
@@ -1,164 +0,0 @@
-/* Copyright (c) 2012 Canonical Ltd. All rights reserved. */
-
-YUI.add('lp.views.global.test', function (Y) {
-    var tests = Y.namespace('lp.views.global.test');
-    var info_type = Y.namespace('lp.app.information_type');
-    var ns = Y.lp.views;
-
-    tests.suite = new Y.Test.Suite('lp.views.global test');
-    tests.suite.add(new Y.Test.Case({
-        name: 'lp.views.global',
-
-        setUp: function () {
-            this.container = Y.one('#fixture');
-            window.LP = {
-                cache: {
-                    related_features: {
-                        private_projects: {
-                            is_beta: true,
-                            title: "Private Projects",
-                            url: "http://blog.ld.net/general/beta";,
-                            value: "true"
-                        }
-                    },
-                    information_type_data: {
-                        PUBLIC: {
-                            value: 'PUBLIC', name: 'Public',
-                            is_private: false, order: 1,
-                            description: 'Public Description'
-                        },
-                        EMBARGOED: {
-                            value: 'EMBARGOED', name: 'Embargoed',
-                            is_private: true, order: 2,
-                            description: 'Something embargoed'
-                        },
-                        PROPRIETARY: {
-                            value: 'PROPRIETARY', name: 'Proprietary',
-                            is_private: true, order: 3,
-                            description: 'Private Description'
-                        }
-                    }
-                }
-            };
-        },
-
-        tearDown: function () {
-            this.container.empty();
-        },
-
-        test_library_exists: function () {
-            Y.Assert.isObject(ns.Global,
-                "Could not locate the lp.views.global module");
-        },
-
-        test_basic_render: function () {
-            // Nothing is currently rendered out by default.
-            var view = new ns.Global();
-            view.render();
-
-            Y.Assert.areEqual(
-                '',
-                this.container.get('innerHTML'),
-                'The container is still empty.');
-            view.destroy();
-        },
-
-        test_beta_banner: function () {
-            // If we've prepped on load a beta banner will auto appear.
-            var banner_container = Y.Node.create('<div/>');
-            banner_container.addClass('beta_banner_container');
-            this.container.append(banner_container);
-            var view = new ns.Global();
-            view.render();
-
-            // We have to wait until after page load event fires to test
-            // things out.
-            var banner_node = Y.one('.banner');
-            Y.Assert.isObject(
-                banner_node,
-                'The container has a new banner node in there.');
-
-            view.destroy();
-        },
-
-        test_privacy: function () {
-            var beta_container = Y.Node.create('<div/>');
-            var private_container = Y.Node.create('<div/>');
-
-            beta_container.addClass('beta_banner_container');
-            private_container.addClass('private_banner_container');
-
-            this.container.append(beta_container);
-            this.container.append(private_container);
-            var view = new ns.Global();
-            view.render();
-
-            // We have to wait until after page load event fires to test
-            // things out.
-            var banner_nodes = Y.all('.banner');
-            Y.Assert.areEqual(
-                2,
-                banner_nodes._nodes.length,
-                'We should have two banners rendered.');
-
-            view.destroy();
-        },
-
-        test_privacy_banner_from_event: function () {
-            // We can also get a privacy banner via a fired event.
-            // This is hard coded to the <body> tag so we have to do some
-            // manual clean up here.
-            var view = new ns.Global();
-            view.render();
-
-            var msg = 'Testing Global';
-            Y.fire(info_type.EV_ISPRIVATE, {
-                text: msg
-            });
-
-            var banner = Y.one('.banner');
-            var banner_text = banner.one('.banner-content').get('text');
-            Y.Assert.areNotEqual(
-                -1,
-                banner_text.indexOf(msg),
-                'The event text is turned into the banner content');
-
-           // Manually clean up.
-           Y.one('.yui3-banner').remove(true);
-           view.destroy();
-        },
-
-        test_banner_updates_content: function () {
-            // If we change our privacy information type the banner needs to
-            // update the content to the new text value from the event.
-            var view = new ns.Global();
-            view.render();
-
-            var msg = 'Testing Global';
-            Y.fire(info_type.EV_ISPRIVATE, {
-                text: msg
-            });
-
-            var updated_msg = 'Updated content';
-            Y.fire(info_type.EV_ISPRIVATE, {
-                text: updated_msg
-            });
-
-            var banner = Y.one('.banner');
-            var banner_text = banner.one('.banner-content').get('text');
-            Y.Assert.areNotEqual(
-                -1,
-                banner_text.indexOf(updated_msg),
-                'The banner updated content to the second event message.');
-
-           // Manually clean up.
-           Y.one('.yui3-banner').remove(true);
-           view.destroy();
-
-        }
-    }));
-
-}, '0.1', {
-    requires: ['test', 'event-simulate', 'node-event-simulate',
-               'lp.app.information_type', 'lp.views.global']
-});

=== modified file 'lib/lp/app/templates/base-layout-macros.pt'
--- lib/lp/app/templates/base-layout-macros.pt	2012-08-13 17:49:31 +0000
+++ lib/lp/app/templates/base-layout-macros.pt	2012-11-12 13:05:28 +0000
@@ -163,19 +163,17 @@
     <script id="base-layout-load-scripts" type="text/javascript">
         //<![CDATA[
         LPJS.use('base', 'node', 'console', 'event',
-            'oop', 'lp', 'lp.app.banner.privacy',
-            'lp.app.banner.beta', 'lp.app.foldables','lp.app.sorttable',
+            'oop', 'lp', 'lp.app.foldables','lp.app.sorttable',
             'lp.app.inlinehelp', 'lp.app.links', 'lp.app.longpoll',
             'lp.bugs.bugtask_index', 'lp.bugs.subscribers',
             'lp.app.ellipsis', 'lp.code.branchmergeproposal.diff',
+            'lp.views.global',
              function(Y) {
 
             Y.on("domready", function () {
-                if (Y.one(document.body).hasClass('private')) {
-                    banner = Y.lp.app.banner.privacy.getPrivacyBanner();
-                    banner.show();
-                }
-                Y.lp.app.banner.beta.show_beta_if_needed();
+                var global_view = new Y.lp.views.Global();
+                global_view.render();
+
                 Y.lp.app.sorttable.SortTable.init();
                 Y.lp.app.inlinehelp.init_help();
                 Y.lp.activate_collapsibles();

=== modified file 'lib/lp/app/templates/base-layout.pt'
--- lib/lp/app/templates/base-layout.pt	2012-05-18 22:04:13 +0000
+++ lib/lp/app/templates/base-layout.pt	2012-11-12 13:05:28 +0000
@@ -64,6 +64,12 @@
       ${view/macro:pagetype}
       ${view/fmt:global-css}
       yui3-skin-sam">
+          <tal:beta-banner condition="view/beta_features">
+            <div class="beta_banner_container"></div>
+          </tal:beta-banner>
+          <tal:private-banner condition="view/private">
+            <div class="private_banner_container"></div>
+          </tal:private-banner>
         <script type="text/javascript"
           tal:condition="python: is_lpnet">
           var _gaq = _gaq || [];
@@ -97,10 +103,8 @@
       <div class="yui-t4"
         tal:omit-tag="not: view/macro:pagehas/portlets">
         <div id="maincontent" class="yui-main">
-          <metal:privacy-banner
-            use-macro="view/@@+banner-macros/beta-banner"/>
-          <metal:privacy-banner
-            use-macro="view/@@+banner-macros/privacy-banner"/>
+          <!-- <metal:privacy-banner -->
+          <!--   use-macro="view/@@+banner-macros/privacy-banner"/> -->
           <div class="yui-b"
             tal:attributes="
               lang view/lang|default_language|default;

=== modified file 'lib/lp/blueprints/javascript/addspec.js'
--- lib/lp/blueprints/javascript/addspec.js	2012-10-15 16:01:38 +0000
+++ lib/lp/blueprints/javascript/addspec.js	2012-11-12 13:05:28 +0000
@@ -10,11 +10,24 @@
 
 var namespace = Y.namespace('lp.blueprints.addspec');
 var to_choice = Y.lp.app.information_type.cache_to_choicesource;
+var info_type = Y.lp.app.information_type;
 
 namespace.set_up = function () {
     var choice_data = to_choice(LP.cache.information_type_data);
-    Y.lp.app.choice.addPopupChoiceForRadioButtons('information_type',
-                                                  choice_data);
+    var widget = Y.lp.app.choice.addPopupChoiceForRadioButtons(
+        'information_type',
+        choice_data);
+
+    // We are not doing ajax saves of the information type so we need to
+    // disable the flash on save for the widget.
+    widget.set('flashEnabled', false);
+
+    // Make sure we catch changes to the information type.
+    widget.on('save', function (ev) {
+        Y.fire(info_type.EV_CHANGE, {
+            value: ev.target.get('value')
+        });
+    });
 };
 
 }, "0.1", {"requires": ['lp.app.information_type', 'lp.app.choice']});

=== modified file 'lib/lp/bugs/javascript/bugtask_index.js'
--- lib/lp/bugs/javascript/bugtask_index.js	2012-10-11 04:57:59 +0000
+++ lib/lp/bugs/javascript/bugtask_index.js	2012-11-12 13:05:28 +0000
@@ -1131,6 +1131,5 @@
                         "lp.app.information_type",
                         "lp.app.widgets.expander", "lp.client", "escape",
                         "lp.client.plugins", "lp.app.errors",
-                        "lp.app.banner.privacy",
                         "lp.app.confirmationoverlay",
                         "lp.bugs.duplicates"]});

=== modified file 'lib/lp/bugs/javascript/filebug.js'
--- lib/lp/bugs/javascript/filebug.js	2012-09-25 19:15:04 +0000
+++ lib/lp/bugs/javascript/filebug.js	2012-11-12 13:05:28 +0000
@@ -9,6 +9,7 @@
 YUI.add('lp.bugs.filebug', function(Y) {
 
 var namespace = Y.namespace('lp.bugs.filebug');
+var info_type = Y.lp.app.information_type;
 
 // For tests.
 var skip_animation;
@@ -28,13 +29,40 @@
         if (Y.Lang.isValue(search_button )) {
             search_button.set('value', 'Check again');
         }
+        setup_plain_inputs();
         set_default_privacy_banner();
-        setup_information_type();
         setup_security_related();
         setupChoiceWidgets();
     }
 };
 
+
+/**
+ * If there are not choice widgets, but radio buttons, then we should watch
+ * those radio buttons for change to make sure we fire the information type
+ * change events.
+ */
+var setup_plain_inputs = function () {
+   var itypes_table = Y.one('.radio-button-widget');
+
+   if (itypes_table) {
+       itypes_table.delegate('change', function(ev) {
+           Y.fire(info_type.EV_CHANGE, {
+               value: this.get('value')
+           });
+       }, "input[name='field.information_type']");
+   }
+};
+
+/**
+ * Due to the privacy setting of the project/bugs we might only allow a
+ * non-public information type. In this case we need to go ahead and let the
+ * user know it's going to be private.
+ *
+ * This is used by the security checks. If the issue is unmade/not security
+ * related then we need to check the normal default behavior to make sure we
+ * show the correct banner.
+ */
 var set_default_privacy_banner = function() {
     var itypes_table = Y.one('.radio-button-widget');
     var val = null;
@@ -48,47 +76,12 @@
     if (LP.cache.bug_private_by_default) {
         var filebug_privacy_text = "This report will be private. " +
             "You can disclose it later.";
-        update_privacy_banner(true, filebug_privacy_text);
-    } else {
-        update_banner_from_information_type(val);
-    }
-};
-
-var update_privacy_banner = function(show, banner_text) {
-    var banner = Y.lp.app.banner.privacy.getPrivacyBanner(
-        banner_text, skip_animation);
-    if (show) {
-        banner.show();
-    } else {
-        banner.hide();
-    }
-};
-
-var get_new_banner_text = function(value) {
-    var cache = LP.cache.information_type_data;
-    var text_template = "This report contains {info_type} information." +
-        " You can change the information type later.";
-    return Y.Lang.substitute(text_template, {
-        'info_type': cache[value].name
-    });
-};
-
-var update_banner_from_information_type = function(value) {
-    var banner_text = get_new_banner_text(value);
-    var is_private = LP.cache.information_type_data[value].is_private;
-    update_privacy_banner(is_private, banner_text);
-};
-
-var setup_information_type = function() {
-    var itypes_table = Y.one('.radio-button-widget');
-    if (!Y.Lang.isValue(itypes_table)) {
-        return;
-    }
-
-
-    itypes_table.delegate('change', function() {
-        update_banner_from_information_type(this.get('value'));
-    }, "input[name='field.information_type']");
+
+        Y.fire(info_type.EV_ISPRIVATE, {
+            text: filebug_privacy_text,
+            value: val
+        });
+    }
 };
 
 
@@ -100,8 +93,25 @@
     var information_helpers = Y.lp.app.information_type;
     var choices = information_helpers.cache_to_choicesource(cache);
 
-    Y.lp.app.choice.addPopupChoiceForRadioButtons(
+    var information_type = Y.lp.app.choice.addPopupChoiceForRadioButtons(
         'information_type', choices, true);
+
+    // When dealing with legacy inputs the information type could have not
+    // been created and we get back a null value.
+    if (information_type) {
+        // We are not doing ajax saves of the information type so we need to
+        // disable the flash on save for the widget.
+        information_type.set('flashEnabled', false);
+
+        // When the information type widget changes we need to let the information
+        // type module know so it can process the change and update things
+        // like banners displayed.
+        information_type.on('save', function (ev) {
+            Y.fire(info_type.EV_CHANGE, {
+                value: ev.target.get('value')
+            });
+        });
+    }
 };
 
 var setup_security_related = function() {
@@ -116,8 +126,19 @@
     security_related.on('change', function() {
         var checked = security_related.get('checked');
         if (checked) {
-            update_privacy_banner(true, notification_text);
+            // XXX: This should use the correct information type based on the
+            // project default. We use PRIVATESECURITY because it'll get the
+            // right banner shown.
+            Y.fire(info_type.EV_ISPRIVATE, {
+                text: notification_text,
+                value: 'PRIVATESECURITY'
+            });
         } else {
+            Y.fire(info_type.EV_ISPUBLIC, {
+                value: 'PUBLIC'
+            });
+            // Check with the default settings if we should add the privacy
+            // banner back because it's the default of the current project.
             set_default_privacy_banner();
         }
     });
@@ -127,5 +148,5 @@
 
 }, "0.1", {"requires": [
     "base", "node", "event", "node-event-delegate", "lp.ui.choiceedit",
-    "lp.app.banner.privacy", "lp.app.choice", "lp.app.information_type",
+    "lp.ui.banner", "lp.app.choice", "lp.app.information_type",
     "lp.bugs.filebug_dupefinder"]});

=== modified file 'lib/lp/bugs/javascript/tests/test_filebug.js'
--- lib/lp/bugs/javascript/tests/test_filebug.js	2012-10-26 10:00:20 +0000
+++ lib/lp/bugs/javascript/tests/test_filebug.js	2012-11-12 13:05:28 +0000
@@ -3,6 +3,8 @@
 YUI.add('lp.bugs.filebug.test', function (Y) {
 
     var tests = Y.namespace('lp.bugs.filebug.test');
+    var info_type = Y.lp.app.information_type;
+
     tests.suite = new Y.Test.Suite(
         'lp.bugs.filebug Tests');
 
@@ -73,96 +75,136 @@
 
         // Filing a public bug does not show the privacy banner.
         test_setup_filebug_public: function () {
+            // Watch out for the event that showing a privacy banner would
+            // fire.
+            var fired = false;
+            var ev = Y.on(info_type.EV_ISPRIVATE, function (ev) {
+                fired = true;
+            });
             this.setupForm(true);
-            var banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNotNull(banner_hidden);
+            Y.Assert.isFalse(fired);
+            ev.detach();
         },
 
         // Filing a bug for a project with private bugs shows the privacy
         // banner.
         test_setup_filebug_private: function () {
+            var fired = false;
+            var ev = Y.on(info_type.EV_ISPRIVATE, function (ev) {
+                fired = true;
+                Y.Assert.areEqual(
+                    'This report will be private. ' +
+                    'You can disclose it later.', ev.text);
+            });
+
             window.LP.cache.bug_private_by_default = true;
             this.setupForm(true);
-            var banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNull(banner_hidden);
-            var banner_text = Y.one('.banner-text').get('text');
-            Y.Assert.areEqual(
-                'This report will be private. ' +
-                'You can disclose it later.', banner_text);
+            Y.Assert.isTrue(fired);
+            ev.detach();
         },
 
         // Selecting a private info type using the legacy radio buttons
         // turns on the privacy banner.
         test_legacy_select_private_info_type: function () {
+            var fired = false;
+            var ev = Y.on(info_type.EV_ISPRIVATE, function (ev) {
+                fired = true;
+                Y.Assert.areEqual(
+                    'This report will be private because it is a security ' +
+                    'vulnerability. You can disclose it later.', ev.text);
+            });
+
             this.setupForm(true);
-            var banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNotNull(banner_hidden);
+            Y.Assert.isFalse(fired);
+
             Y.one('[id="field.information_type.2"]').simulate('click');
-            banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNull(banner_hidden);
-            var banner_text = Y.one('.banner-text').get('text');
-            Y.Assert.areEqual(
-                'This report contains Private Security information. ' +
-                'You can change the information type later.', banner_text);
+            // And the event should be fired and caught.
+            Y.Assert.isTrue(fired);
+            ev.detach();
         },
 
         // Selecting a public info type using the legacy radio buttons
         // turns off the privacy banner.
         test_legacy_select_public_info_type: function () {
+            var fired = false;
+            var ev = Y.on(info_type.EV_ISPUBLIC, function (ev) {
+                fired = true;
+            });
+
             window.LP.cache.bug_private_by_default = true;
             this.setupForm(true);
+
+            // This will fire ISPRIVATE and a banner shows.
             Y.one('[id="field.information_type.2"]').simulate('click');
-            var banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNull(banner_hidden);
+
+            // The second one will fire ISPUBLIC and the banner disappears.
             Y.one('[id="field.information_type.0"]').simulate('click');
-            banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNotNull(banner_hidden);
+            Y.Assert.isTrue(fired);
         },
 
         // When non bug supervisors select a security related bug the privacy
         // banner is turned on.
         test_select_security_related: function () {
+            var fired = false;
+            var ev = Y.on(info_type.EV_ISPRIVATE, function (ev) {
+                fired = true;
+                Y.Assert.areEqual(
+                    'This report will be private because it is a security ' +
+                    'vulnerability. You can disclose it later.', ev.text);
+            });
+
             this.setupForm(false);
-            var banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNotNull(banner_hidden);
+            Y.Assert.isFalse(fired);
+
             Y.one('[id="field.security_related"]').simulate('click');
-            banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNull(banner_hidden);
-            var banner_text = Y.one('.banner-text').get('text');
-            Y.Assert.areEqual(
-                'This report will be private because it is a ' +
-                'security vulnerability. You can disclose it later.',
-                banner_text);
+            // And the event should be fired and caught.
+            Y.Assert.isTrue(fired);
+            ev.detach();
         },
 
         // When non bug supervisors unselect a security related bug the privacy
         // banner is turned off.
         test_unselect_security_related: function () {
+            var fired = false;
+            var ev = Y.on(info_type.EV_ISPUBLIC, function (ev) {
+                fired = true;
+            });
+
             this.setupForm(false);
-            Y.one('[id="field.security_related"]').simulate('click');
-            var banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNull(banner_hidden);
-            Y.one('[id="field.security_related"]').simulate('click');
-            banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNotNull(banner_hidden);
+
+            Y.one('[id="field.security_related"]').simulate('click');
+            Y.Assert.isFalse(fired);
+
+            Y.one('[id="field.security_related"]').simulate('click');
+            Y.Assert.isTrue(fired);
         },
 
         // When non bug supervisors unselect a security related bug the privacy
         // banner remains on for private_by_default bugs.
         test_unselect_security_related_default_private: function () {
             window.LP.cache.bug_private_by_default = true;
+            var public = 0;
+            var private = 0;
+            var ev = Y.on(info_type.EV_ISPUBLIC, function (ev) {
+                public = public + 1;
+            });
+
+            var ev = Y.on(info_type.EV_ISPRIVATE, function (ev) {
+                private = private + 1;
+            });
+
+
             this.setupForm(false);
-            Y.one('[id="field.security_related"]').simulate('click');
-            var banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNull(banner_hidden);
-            Y.one('[id="field.security_related"]').simulate('click');
-            banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNull(banner_hidden);
-            var banner_text = Y.one('.banner-text').get('text');
+            Y.Assert.areEqual(1, private, 'Private is the default state.');
+
+            Y.one('[id="field.security_related"]').simulate('click');
+            Y.Assert.areEqual(2, private, 'The first click fires ISPRIVATE');
+
+            Y.one('[id="field.security_related"]').simulate('click');
+            Y.Assert.areEqual(1, public, 'The second click fires ISPUBLIC');
             Y.Assert.areEqual(
-                'This report will be private. ' +
-                'You can disclose it later.',
-                banner_text);
+                3, private,
+                "It also fires an ISPRIVATE since that's the default state.");
         },
 
         // The dupe finder functionality is setup.
@@ -280,44 +322,59 @@
         // Selecting a private info type using the popup choice widget
         // turns on the privacy banner.
         test_select_private_info_type: function () {
+            var fired = false;
+            var ev = Y.on(info_type.EV_ISPRIVATE, function (ev) {
+                fired = true;
+                Y.Assert.areEqual(
+                    'This report contains Private information. ' +
+                    'You can change the information type later.', ev.text);
+            });
+
             this.setupForm(true);
-            var banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNotNull(banner_hidden);
+            Y.Assert.isFalse(fired);
+
             var information_type_popup = Y.one('.information_type-content a');
             information_type_popup.simulate('click');
             var information_type_choice = Y.one(
                 '.yui3-ichoicelist-content a[href="#USERDATA"]');
             information_type_choice.simulate('click');
-            banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNull(banner_hidden);
-            var banner_text = Y.one('.banner-text').get('text');
-            Y.Assert.areEqual(
-                'This report contains Private information. ' +
-                'You can change the information type later.', banner_text);
+
+            // And the event should be fired and caught.
+            Y.Assert.isTrue(fired);
+            ev.detach();
         },
 
         // Selecting a public info type using the popup choice widget
         // turns off the privacy banner.
         test_select_public_info_type: function () {
+            var fired = false;
+            var ev = Y.on(info_type.EV_ISPUBLIC, function (ev) {
+                fired = true;
+            });
+
             window.LP.cache.bug_private_by_default = true;
             this.setupForm(true);
+
+            // This will fire ISPRIVATE and a banner shows.
             var information_type_popup = Y.one('.information_type-content a');
             information_type_popup.simulate('click');
             var information_type_choice = Y.one(
                 '.yui3-ichoicelist-content a[href="#USERDATA"]');
             information_type_choice.simulate('click');
-            var banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNull(banner_hidden);
+
+            // The second one will fire ISPUBLIC and the banner disappears.
             information_type_popup.simulate('click');
             information_type_choice = Y.one(
                 '.yui3-ichoicelist-content a[href="#PUBLIC"]');
             information_type_choice.simulate('click');
-            banner_hidden = Y.one('.yui3-privacybanner-hidden');
-            Y.Assert.isNotNull(banner_hidden);
+
+            Y.Assert.isTrue(fired);
         }
     }));
 
-}, '0.1', {'requires': ['test', 'test-console', 'event', 'node-event-simulate',
-        'lp.app.banner.privacy', 'lp.app.choice',
-        'lp.bugs.filebug_dupefinder', 'lp.bugs.filebug'
-        ]});
+}, '0.1', {
+    requires: [
+        'test', 'test-console', 'event', 'node-event-simulate',
+        'lp.app.banner.privacy', 'lp.app.choice', 'lp.app.information_type',
+        'lp.bugs.filebug_dupefinder', 'lp.bugs.filebug' ]
+});

=== modified file 'lib/lp/code/javascript/branch.information_type_choice.js'
--- lib/lp/code/javascript/branch.information_type_choice.js	2012-09-21 15:39:22 +0000
+++ lib/lp/code/javascript/branch.information_type_choice.js	2012-11-12 13:05:28 +0000
@@ -105,7 +105,9 @@
         LP.cache.context.information_type =
             information_type.get_cache_data_from_key(
                     value, 'value', 'name');
-        information_type.update_privacy_banner(value);
+        Y.fire(information_type.EV_CHANGE, {
+            value: value
+        });
         if (this.get('use_animation')) {
             this.information_type_edit._showSucceeded();
         }
@@ -119,7 +121,8 @@
     }
 });
 
-}, "0.1", {"requires": ["base", "oop", "node", "event", "io-base",
-                        "lp.ui.choiceedit", "lp.app.banner.privacy",
-                        "lp.app.errors", "lp.app.choice",
-                        "lp.app.information_type"]});
+}, "0.1", {
+    requires: [
+        "base", "oop", "node", "event", "io-base", "lp.ui.choiceedit",
+        "lp.app.errors", "lp.app.choice", "lp.app.information_type"]
+});

=== modified file 'lib/lp/code/javascript/tests/test_information_type_choice.js'
--- lib/lp/code/javascript/tests/test_information_type_choice.js	2012-10-26 10:00:20 +0000
+++ lib/lp/code/javascript/tests/test_information_type_choice.js	2012-11-12 13:05:28 +0000
@@ -4,6 +4,8 @@
 
     var tests = Y.namespace('lp.code.branch.information_type_choice.test');
     var ns = Y.lp.code.branch.information_type_choice;
+    var info_type = Y.lp.app.information_type;
+
     tests.suite = new Y.Test.Suite(
             'lp.code.branch.information_type_choice Tests');
 
@@ -62,22 +64,6 @@
             this.widget.render();
         },
 
-        _shim_privacy_banner: function () {
-            var old_func = Y.lp.app.banner.privacy.getPrivacyBanner;
-            Y.lp.app.banner.privacy.getPrivacyBanner = function () {
-                return {
-                    show: function () { Y.fire('test:banner:show'); },
-                    hide: function () { Y.fire('test:banner:hide'); },
-                    updateText: function () { Y.fire('test:banner:update'); }
-                };
-            };
-            return old_func;
-        },
-
-        _unshim_privacy_banner: function (old_func) {
-            Y.lp.app.banner.privacy.getPrivacyBanner = old_func;
-        },
-
         test_library_exists: function () {
             Y.Assert.isObject(Y.lp.code.branch.information_type_choice,
                 "Cannot locate the " +
@@ -118,44 +104,31 @@
 
         // Setting a private type shows the privacy banner.
         test_information_type_save_success_private: function() {
+            var fired = false;
+            Y.on(info_type.EV_ISPRIVATE, function (ev) {
+                fired = true;
+            });
+
             this.makeWidget();
-            var old_func = this._shim_privacy_banner();
-            var hide_flag = false;
-            var update_flag = false;
-            Y.on('test:banner:show', function() {
-                hide_flag = true;
-            });
-            Y.on('test:banner:update', function() {
-                update_flag = true;
-            });
 
             this.widget._information_type_save_success('PROPRIETARY');
-            var body = Y.one('body');
-            Y.Assert.isTrue(body.hasClass('private'));
-            Y.Assert.isTrue(hide_flag);
-            Y.Assert.isTrue(update_flag);
+            Y.Assert.isTrue(fired);
             Y.Assert.areEqual(
                 'Proprietary', LP.cache.context.information_type);
-            this._unshim_privacy_banner(old_func);
         },
 
         // Setting a private type hides the privacy banner.
         test_information_type_save_success_public: function() {
+            var fired = false;
+            Y.on(info_type.EV_ISPUBLIC, function (ev) {
+                fired = true;
+            });
+
             this.makeWidget();
-            var old_func = this._shim_privacy_banner();
-            var flag = false;
-            Y.on('test:banner:hide', function() {
-                flag = true;
-            });
-            var summary = Y.one('#information-type-summary');
-            summary.replaceClass('public', 'private');
 
             this.widget._information_type_save_success('PUBLIC');
-            var body = Y.one('body');
-            Y.Assert.isTrue(body.hasClass('public'));
-            Y.Assert.isTrue(flag);
+            Y.Assert.isTrue(fired);
             Y.Assert.areEqual('Public', LP.cache.context.information_type);
-            this._unshim_privacy_banner(old_func);
         },
 
         // Test error handling when a save fails.

=== modified file 'lib/lp/registry/browser/product.py'
--- lib/lp/registry/browser/product.py	2012-10-26 15:11:20 +0000
+++ lib/lp/registry/browser/product.py	2012-11-12 13:05:28 +0000
@@ -1905,6 +1905,7 @@
 
 class ProjectAddStepTwo(StepView, ProductLicenseMixin, ReturnToReferrerMixin):
     """Step 2 (of 2) in the +new project add wizard."""
+    related_features = [PRIVATE_PROJECTS_FLAG]
 
     _field_names = ['displayname', 'name', 'title', 'summary', 'description',
                     'homepageurl', 'information_type', 'licenses',

=== modified file 'lib/lp/registry/javascript/product_views.js'
--- lib/lp/registry/javascript/product_views.js	2012-10-26 06:59:14 +0000
+++ lib/lp/registry/javascript/product_views.js	2012-11-12 13:05:28 +0000
@@ -95,6 +95,9 @@
          */
         information_type_change: function (value) {
             toggle_license_field(value);
+            Y.fire(info_type.EV_CHANGE, {
+                value: value
+            });
         },
 
         /**
@@ -349,6 +352,9 @@
          */
         information_type_change: function (value) {
             toggle_license_field(value);
+            Y.fire(info_type.EV_CHANGE, {
+                value: value
+            });
             var driver_cont =
                 Y.one('input[name="field.driver"]').ancestor('td');
             var bug_super_cont =

=== modified file 'lib/lp/services/features/flags.py'
--- lib/lp/services/features/flags.py	2012-11-08 06:06:22 +0000
+++ lib/lp/services/features/flags.py	2012-11-12 13:05:28 +0000
@@ -236,7 +236,7 @@
      'boolean',
      'If true, enabled access to private project registration features.',
      'disabled',
-     '',
+     'Allow registering a non-public project.',
      'http://blog.launchpad.net/general/private-projects-beta'),
     ('disclosure.private_project.traversal_override',
      'boolean',