← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~jcsackett/launchpad/progressively-enhanced-banners into lp:launchpad

 

j.c.sackett has proposed merging lp:~jcsackett/launchpad/progressively-enhanced-banners into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~jcsackett/launchpad/progressively-enhanced-banners/+merge/106487

Summary
=======
The privacy banner (and it's close friend, the beta banner) don't work without
JS. This introduces basic html banners into base-layout that are enhanced by
the JS banners.

Preimp
======
Spoke with Curtis Hovey

Implementation
==============
* CSS rules for the banners have been updated to use the YUI css classes.
* The public private css formatter has been renamed to the global css
  formatter, and now adds the global-notification-visible class as well when
  it's needed.
* The `private` attribute on the LaunchpadView has been changed to a property
  method, encapsulating most of the special work in the
  public_private_formatter (e.g. checking if the context is private).
* A `beta_features` method has been added that returns the related_features
  that are in beta.
* Banner-macros has been added; it encapsulates the privacy banner and beta
  banner html, and uses the private and beta_features attributes on
  LaunchpadView to determine if the html is presented.
* Some minor js tweaks were made to work with the new banner html.

The close button doesn't get rendered on static beta banners; making this work
would require some extra investment that didn't seem worth it given the
relatively low usage of non-js browsers with beta features. If it's needed, a
followup branch can always be landed.

Tests
=====
bin/test -vvct base-layout -t test_formatters -t tales
bin/test -vvct beta -t privacy --layer=YUI

QA
==
Disable js in your browser. Check the privacy banner and the beta banner,
everything should be fine.

Enable js in your browser. Check it all again. It should all be fine.

LoC
===
This branch is part of the disclosure arc of work.

Lint
====
Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/app/javascript/banners/beta-notification.js
  lib/canonical/launchpad/icing/css/components/sidebar_components.css
  lib/canonical/launchpad/icing/css/components/global_notification.css
  lib/lp/app/javascript/banners/banner.js
  lib/lp/services/webapp/publisher.py
  lib/lp/app/templates/banner-macros.pt
  lib/lp/app/templates/base-layout.pt
  lib/lp/app/javascript/banners/privacy.js
  lib/lp/app/browser/configure.zcml
  lib/lp/app/browser/tales.py
  lib/canonical/launchpad/icing/css/components/beta_banner.css
  lib/canonical/launchpad/icing/css/base.css

Lint has been elided; there are some things I will fix before landing, but
most of this was a massive set of css line issues which were introduced by
more css-competent people than me that I'm leery to touch.
-- 
https://code.launchpad.net/~jcsackett/launchpad/progressively-enhanced-banners/+merge/106487
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jcsackett/launchpad/progressively-enhanced-banners into lp:launchpad.
=== modified file 'lib/canonical/launchpad/icing/css/base.css'
--- lib/canonical/launchpad/icing/css/base.css	2012-03-10 15:33:33 +0000
+++ lib/canonical/launchpad/icing/css/base.css	2012-05-19 01:21:19 +0000
@@ -6,10 +6,6 @@
     }
 body.private {
     /* It must be obvious to the user that the context is private */
-    background: url("/@@/private-y-bg") top left repeat-y;
-    }
-/* Override for when the feature flag is active */
-body.feature-flag-bugs-private-notification-enabled.private {
     background-image: none;
     }
 body.private .private-disallow {

=== modified file 'lib/canonical/launchpad/icing/css/components/beta_banner.css'
--- lib/canonical/launchpad/icing/css/components/beta_banner.css	2012-03-10 15:43:21 +0000
+++ lib/canonical/launchpad/icing/css/components/beta_banner.css	2012-05-19 01:21:19 +0000
@@ -1,7 +1,7 @@
 /* ===========
    Beta banner
 */
-.beta-banner {
+.yui3-betabanner-content .global-notification {
     position: fixed;
     z-index: 9;
     top: 0;
@@ -17,6 +17,7 @@
     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;
@@ -40,8 +41,8 @@
 .beta-feature {
     font-weight: bold;
     }
-.beta-banner .global-notification-close,
-.beta-banner .global-notification-close:active,
-.beta-banner .global-notification-close:visited {
+.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/canonical/launchpad/icing/css/components/global_notification.css'
--- lib/canonical/launchpad/icing/css/components/global_notification.css	2012-05-15 17:17:34 +0000
+++ lib/canonical/launchpad/icing/css/components/global_notification.css	2012-05-19 01:21:19 +0000
@@ -21,8 +21,6 @@
     font-weight: bold;
     box-shadow: 0 0 5px #333;
     }
-
-
 .global-notification .sprite.notification-private {
     float: left;
     display: inline-block;
@@ -58,10 +56,11 @@
    Privacy
 */
 
-.privacy-banner {
+.yui3-privacybanner-content .global-notification{
     /* Define colour for browsers that don't support transparency */
     background-color: #8d1f1f;
     /* Set transparent background for browsers that support it */
     background-color: rgba(125,0,0,0.9);
     color: #fff;
+    text-align: left;
 }

=== modified file 'lib/canonical/launchpad/icing/css/components/sidebar_components.css'
--- lib/canonical/launchpad/icing/css/components/sidebar_components.css	2012-03-10 15:33:33 +0000
+++ lib/canonical/launchpad/icing/css/components/sidebar_components.css	2012-05-19 01:21:19 +0000
@@ -24,16 +24,6 @@
 .side ul {
     background: #fbfbfb;
     }
-#privacy.private {
-    background: url(/@@/private-bg) top left repeat-x;  /* 8px high */
-    padding-top: 12px;  /* = 8px + the usual 4px top padding */
-    }
-/* Override for when the feature flag is active */
-.feature-flag-bugs-private-notification-enabled #privacy.private {
-    background-image: none;
-    background-color: #FBFBFB;
-    padding-top: 0.5em;
-    }
 .downloads li {
     margin: 0;
     padding: 2px 0 0;

=== modified file 'lib/lp/app/browser/configure.zcml'
--- lib/lp/app/browser/configure.zcml	2012-04-19 20:37:47 +0000
+++ lib/lp/app/browser/configure.zcml	2012-05-19 01:21:19 +0000
@@ -33,6 +33,13 @@
         />
     <browser:page
         for="*"
+        name="+banner-macros"
+        template="../templates/banner-macros.pt"
+        permission="zope.Public"
+        class="lp.app.browser.launchpad.Macro"
+        />
+    <browser:page
+        for="*"
         name="launchpad_form"
         layer="lp.layers.LaunchpadLayer"
         permission="zope.Public"

=== modified file 'lib/lp/app/browser/tales.py'
--- lib/lp/app/browser/tales.py	2012-04-20 07:01:18 +0000
+++ lib/lp/app/browser/tales.py	2012-05-19 01:21:19 +0000
@@ -559,7 +559,7 @@
     # Names which are allowed but can't be traversed further.
     final_traversable_names = {
         'pagetitle': 'pagetitle',
-        'public-private-css': 'public_private_css',
+        'global-css': 'global_css',
         }
 
     def __init__(self, context):
@@ -658,22 +658,20 @@
             "No link implementation for %r, IPathAdapter implementation "
             "for %r." % (self, self._context))
 
-    def public_private_css(self):
-        """Return the CSS class that represents the object's privacy."""
-        # If a view is marked as private, the context doesn't matter. It will
-        # always be displayed as private.
+    def global_css(self):
+        css_classes = set([])
         view = self._context
-        private_view = getattr(view, 'private', False)
-        if private_view:
-            return 'private'
-
-        # If the view is not marked as private, privacy is determined by the
-        # view's context.
-        privacy = IPrivacy(getattr(view, 'context', None), None)
-        if privacy is not None and privacy.private:
-            return 'private'
+        private = getattr(view, 'private', False)
+        if private:
+            css_classes.add('private')
+            css_classes.add('global-notification-visible')
         else:
-            return 'public'
+            css_classes.add('public')
+        beta = getattr(view, 'beta_features', [])
+        if beta != []:
+            css_classes.add('global-notification-visible')
+        return ' '.join(list(css_classes))
+
 
     def _getSaneBreadcrumbDetail(self, breadcrumb):
         text = breadcrumb.detail

=== modified file 'lib/lp/app/javascript/banners/banner.js'
--- lib/lp/app/javascript/banners/banner.js	2012-05-15 17:17:34 +0000
+++ lib/lp/app/javascript/banners/banner.js	2012-05-19 01:21:19 +0000
@@ -23,7 +23,6 @@
                 slide_out: 0.2,
             }    
         }
-
         return anim_times;
     },
 
@@ -98,7 +97,6 @@
         var banner_data = {
             badge: this.get('banner_icon'),
             text: this.get('notification_text'),
-            extra_classes: this.get('css_class'), 
             banner_id: this.get('banner_id')
         };
         return Y.lp.mustache.to_html(
@@ -117,7 +115,7 @@
             this._updateBanner(existing_banner);
         } else {
             var banner_html = this._createBanner(); 
-            this.get('target').append(banner_html);
+            this.get('contentBox').append(banner_html);
         }
     },
 
@@ -135,17 +133,11 @@
         banner_id: { value: "base-banner" },
         notification_text: { value: "" },
         banner_icon: { value: "<span></span>" },
-        target: {
-            valueFn: function() {
-                return Y.one('#maincontent');
-            }
-        },
         banner_template: {
             valueFn: function() {
                 return [
                     '<div id="{{ banner_id }}"', 
-                        'class="global-notification transparent hidden',
-                        ' {{ extra_classes }}">', 
+                        'class="global-notification transparent hidden">', 
                         '{{{ badge }}}',
                         '<span class="banner-text">{{ text }}</span>',
                     "</div>"].join('')
@@ -153,7 +145,6 @@
         },
         skip_animation: { value: false },
         visible: { value: false },
-        css_class: { value: '' }
     }
 });
 

=== modified file 'lib/lp/app/javascript/banners/beta-notification.js'
--- lib/lp/app/javascript/banners/beta-notification.js	2012-05-14 22:07:29 +0000
+++ lib/lp/app/javascript/banners/beta-notification.js	2012-05-19 01:21:19 +0000
@@ -19,11 +19,11 @@
 
     renderUI: function () {
         baseBanner.prototype.renderUI.apply(this, arguments);
-        var banner_node = Y.one('.beta-banner');
+        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>');
-        banner_node.appendChild(close_box);
+        beta_node.appendChild(close_box);
     },
 
     bindUI: function () {
@@ -45,8 +45,7 @@
             valueFn: function() {
                 return [
                     '<div id="{{ banner_id }}"', 
-                        'class="global-notification beta-banner ',
-                        'transparent hidden">', 
+                        'class="global-notification transparent hidden">', 
                         '{{{ badge }}}',
                         '<span class="banner-text">',
                             '{{ text }}{{{ features }}}',

=== modified file 'lib/lp/app/javascript/banners/privacy.js'
--- lib/lp/app/javascript/banners/privacy.js	2012-05-15 17:17:34 +0000
+++ lib/lp/app/javascript/banners/privacy.js	2012-05-19 01:21:19 +0000
@@ -3,15 +3,7 @@
 var ns = Y.namespace('lp.app.banner.privacy');
 var baseBanner = Y.lp.app.banner.Banner;
 
-ns.PrivacyBanner = Y.Base.create('privacyBanner', baseBanner, [], {
-
-    renderUI: function () {
-        var body = Y.one('body');
-        body.addClass('feature-flag-bugs-private-notification-enabled');
-        baseBanner.prototype.renderUI.apply(this, arguments);
-    }
-
-}, {
+ns.PrivacyBanner = Y.Base.create('privacyBanner', baseBanner, [], {}, {
     ATTRS: {
         banner_id: { value: "privacy-banner" },
         notification_text: {
@@ -20,7 +12,6 @@
         banner_icon: {
             value: '<span class="sprite notification-private"></span>'
         },
-        css_class: { value: 'privacy-banner' }
     } 
 });
 

=== added file 'lib/lp/app/templates/banner-macros.pt'
--- lib/lp/app/templates/banner-macros.pt	1970-01-01 00:00:00 +0000
+++ lib/lp/app/templates/banner-macros.pt	2012-05-19 01:21:19 +0000
@@ -0,0 +1,41 @@
+<macros
+  xmlns="http://www.w3.org/1999/xhtml";
+  xmlns:tal="http://xml.zope.org/namespaces/tal";
+  xmlns:metal="http://xml.zope.org/namespaces/metal";
+  xmlns:i18n="http://xml.zope.org/namespaces/i18n";
+  i18n:domain="launchpad"
+  tal:omit-tag=""
+>
+
+<metal:privacy define-macro="privacy-banner">
+  <tal:show-banner condition="view/private">
+    <div class="yui3-privacybanner-content">
+      <div id="privacy-banner" class="global-notification">
+        <span class="sprite notification-private"></span>
+        <span class="banner-text">The information on this page is private.</span>
+      </div>
+    </div>
+  </tal:show-banner>
+</metal:privacy>
+
+<metal:beta define-macro="beta-banner">
+<div class="yui3-betabanner-content">
+  <div id="beta-banner" class="global-notification">
+    <span class="beta-warning">BETA!</span>
+    <span class="banner-text">
+      Some parts of this page are in beta:&nbsp;
+      <span class="beta-feature">
+        <tal:features
+          repeat="feature view/beta_features">
+          <tal:feature replace="feature/title" />
+          <tal:link condition="feature/url">
+            (<a tal:attributes="href feature/url" class="info-link">read more</a>)
+          </tal:link>
+        </tal:features>
+      <span>
+    </span>
+  </div>
+</div>
+</metal:beta>
+
+</macros>

=== modified file 'lib/lp/app/templates/base-layout.pt'
--- lib/lp/app/templates/base-layout.pt	2012-04-12 19:42:35 +0000
+++ lib/lp/app/templates/base-layout.pt	2012-05-19 01:21:19 +0000
@@ -62,7 +62,7 @@
     itemtype="http://schema.org/WebPage";
     tal:attributes="class string:tab-${view/menu:selectedfacetname}
       ${view/macro:pagetype}
-      ${view/fmt:public-private-css}
+      ${view/fmt:global-css}
       yui3-skin-sam">
         <script type="text/javascript"
           tal:condition="python: is_lpnet">
@@ -97,6 +97,10 @@
       <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"/>
           <div class="yui-b"
             tal:attributes="
               lang view/lang|default_language|default;

=== modified file 'lib/lp/services/webapp/publisher.py'
--- lib/lp/services/webapp/publisher.py	2012-04-26 16:21:46 +0000
+++ lib/lp/services/webapp/publisher.py	2012-05-19 01:21:19 +0000
@@ -65,6 +65,7 @@
 from zope.traversing.browser.interfaces import IAbsoluteURL
 
 from lp.app.errors import NotFoundError
+from lp.app.interfaces.launchpad import IPrivacy
 from lp.app.versioninfo import revno
 from lp.layers import (
     LaunchpadLayer,
@@ -288,7 +289,14 @@
     - publishTraverse() <-- override this to support traversing-through.
     """
 
-    private = False
+    @property
+    def private(self):
+        """A view is private if its context is."""
+        privacy = IPrivacy(self.context, None)
+        if privacy is not None:
+            return privacy.private
+        else:
+            return False
 
     def __init__(self, context, request):
         self.context = context
@@ -313,6 +321,15 @@
         related_features = cache.setdefault('related_features', {})
         related_features.update(self.related_feature_info)
 
+    def beta_features(self):
+        try:
+            cache = IJSONRequestCache(self.request).objects
+        except TypeError, error:
+            if error.args[0] == 'Could not adapt':
+                return
+        related_features = cache.setdefault('related_features', {}).values()
+        return [f for f in related_features if f['is_beta']]
+
     def initialize(self):
         """Override this in subclasses.
 


Follow ups