Merge lp:~gz/launchpad/root_blog_feature_flag_939055 into lp:launchpad

Proposed by Martin Packman
Status: Merged
Approved by: Martin Packman
Approved revision: no longer in the source branch.
Merged at revision: 15805
Proposed branch: lp:~gz/launchpad/root_blog_feature_flag_939055
Merge into: lp:launchpad
Diff against target: 194 lines (+90/-8)
5 files modified
lib/lp/app/browser/root.py (+9/-0)
lib/lp/app/browser/tests/test_launchpadroot.py (+64/-0)
lib/lp/app/stories/launchpad-root/front-pages.txt (+4/-2)
lib/lp/app/templates/root-index.pt (+7/-6)
lib/lp/services/features/flags.py (+6/-0)
To merge this branch: bzr merge lp:~gz/launchpad/root_blog_feature_flag_939055
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+119399@code.launchpad.net

Description of the change

Only display posts from the launchpad blog on the main page when the new app.root_blog.enabled feature is set. The aim is to easy the datacentre migration by having the option of toggling off the lookup when blog.launchpad.net needs to taken down.

This is something of an abuse of the feature flags... feature, but Robert suggested it as the easiest method in the bug.

To prevent gaping whitespace in the left column, the logic for whether or not to display the launchpad introduction is adapted from always being off for logged in users to being on regardless if the blog isn't being shown.

The tests added are not exhaustive as they're expensive at this level, but should cover the details we care about.

= LOC =

I have some credit to burn...

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/app/browser/root.py
  lib/lp/app/browser/tests/test_launchpadroot.py
  lib/lp/app/templates/root-index.pt
  lib/lp/services/features/flags.py

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

The branch looks good.

The boolean expression in show_whatslaunchpad is sufficiently complex
that a comment giving the prose translation (as well as the motivation
for including the "what is" section when the blog is disabled) would be
a good idea.

review: Approve (code)
Revision history for this message
Martin Packman (gz) wrote :

Added explanation to the docstring on that suggestion, also suppressed a few of the links shown when logged in that are duplicated from the intro if that's being displayed.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/app/browser/root.py'
2--- lib/lp/app/browser/root.py 2012-03-23 20:56:11 +0000
3+++ lib/lp/app/browser/root.py 2012-08-14 14:33:25 +0000
4@@ -43,6 +43,7 @@
5 GoogleResponseError,
6 ISearchService,
7 )
8+from lp.services.features import getFeatureFlag
9 from lp.services.propertycache import cachedproperty
10 from lp.services.statistics.interfaces.statistic import ILaunchpadStatisticSet
11 from lp.services.timeout import urlfetch
12@@ -136,6 +137,14 @@
13 """The total blueprint count in all of Launchpad."""
14 return getUtility(ILaunchpadStatisticSet).value('question_count')
15
16+ @property
17+ def show_whatslaunchpad(self):
18+ """True if introduction to Launchpad should be displayed.
19+
20+ Shown when not logged in or if blog is disabled.
21+ """
22+ return self.user is None or not getFeatureFlag("app.root_blog.enabled")
23+
24 def getRecentBlogPosts(self):
25 """Return the parsed feed of the most recent blog posts.
26
27
28=== modified file 'lib/lp/app/browser/tests/test_launchpadroot.py'
29--- lib/lp/app/browser/tests/test_launchpadroot.py 2012-06-04 16:13:51 +0000
30+++ lib/lp/app/browser/tests/test_launchpadroot.py 2012-08-14 14:33:25 +0000
31@@ -16,9 +16,11 @@
32
33 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
34 from lp.registry.interfaces.person import IPersonSet
35+from lp.services.features.testing import FeatureFixture
36 from lp.services.webapp.authorization import check_permission
37 from lp.services.webapp.interfaces import ILaunchpadRoot
38 from lp.testing import (
39+ anonymous_logged_in,
40 login_person,
41 TestCaseWithFactory,
42 )
43@@ -138,3 +140,65 @@
44 logo = markup.find(True, id='launchpad-logo-and-name')
45 self.assertIsNot(None, logo)
46 self.assertEqual('/@@/launchpad-logo-and-name.png', logo['src'])
47+
48+ @staticmethod
49+ def _make_blog_post(linkid, title, body, date):
50+ return {
51+ 'title': title,
52+ 'description': body,
53+ 'link': "http://blog.invalid/%s" % (linkid,),
54+ 'date': date,
55+ }
56+
57+ def test_blog_posts(self):
58+ """Posts from the launchpad blog are shown when feature is enabled"""
59+ self.useFixture(FeatureFixture({'app.root_blog.enabled': True}))
60+ posts = [
61+ self._make_blog_post(1, "A post", "Post contents.", "2002"),
62+ self._make_blog_post(2, "Another post", "More contents.", "2003"),
63+ ]
64+ calls = []
65+
66+ def _get_blog_posts():
67+ calls.append('called')
68+ return posts
69+
70+ root = getUtility(ILaunchpadRoot)
71+ with anonymous_logged_in() as user:
72+ view = create_initialized_view(root, 'index.html', principle=user)
73+ view.getRecentBlogPosts = _get_blog_posts
74+ result = view()
75+ markup = BeautifulSoup(result,
76+ parseOnlyThese=SoupStrainer(id='homepage-blogposts'))
77+ self.assertEqual(['called'], calls)
78+ items = markup.findAll('li', 'news')
79+ # Notice about launchpad being opened is always added at the end
80+ self.assertEqual(3, len(items))
81+ a = items[-1].find("a")
82+ self.assertEqual("Launchpad now open source", a.string.strip())
83+ for post, item in zip(posts, items):
84+ a = item.find("a")
85+ self.assertEqual(post['link'], a["href"])
86+ self.assertEqual(post['title'], a.string)
87+
88+ def test_blog_disabled(self):
89+ """Launchpad blog not queried for display without feature"""
90+ calls = []
91+
92+ def _get_blog_posts():
93+ calls.append('called')
94+ return []
95+
96+ root = getUtility(ILaunchpadRoot)
97+ user = self.factory.makePerson()
98+ login_person(user)
99+ view = create_initialized_view(root, 'index.html', principal=user)
100+ view.getRecentBlogPosts = _get_blog_posts
101+ markup = BeautifulSoup(
102+ view(), parseOnlyThese=SoupStrainer(id='homepage'))
103+ self.assertEqual([], calls)
104+ self.assertIs(None, markup.find(True, id='homepage-blogposts'))
105+ # Even logged in users should get the launchpad intro text in the left
106+ # column rather than blank space when the blog is not being displayed.
107+ self.assertTrue(view.show_whatslaunchpad)
108+ self.assertTrue(markup.find(True, 'homepage-whatslaunchpad'))
109
110=== modified file 'lib/lp/app/stories/launchpad-root/front-pages.txt'
111--- lib/lp/app/stories/launchpad-root/front-pages.txt 2010-07-19 19:49:22 +0000
112+++ lib/lp/app/stories/launchpad-root/front-pages.txt 2012-08-14 14:33:25 +0000
113@@ -1,9 +1,11 @@
114 Launchpad front pages
115 ---------------------
116
117-Visit our home page:
118+Visit our home page with the typical configuration:
119
120- >>> browser.open('http://launchpad.dev/')
121+ >>> from lp.services.features.testing import FeatureFixture
122+ >>> with FeatureFixture({"app.root_blog.enabled": True}):
123+ ... browser.open('http://launchpad.dev/')
124 >>> browser.url
125 'http://launchpad.dev/'
126
127
128=== modified file 'lib/lp/app/templates/root-index.pt'
129--- lib/lp/app/templates/root-index.pt 2012-06-11 00:47:38 +0000
130+++ lib/lp/app/templates/root-index.pt 2012-08-14 14:33:25 +0000
131@@ -67,7 +67,7 @@
132 <div class="yui-g">
133 <div class="yui-u first" style="margin-top: 1.5em;">
134 <div class="homepage-whatslaunchpad"
135- tal:condition="not:view/user">
136+ tal:condition="view/show_whatslaunchpad">
137 <h2><span class="launchpad-gold">Launchpad</span> is a software collaboration platform that provides:</h2>
138 <ul tal:define="apphomes view/apphomes">
139 <li>
140@@ -109,7 +109,8 @@
141 </div>
142 </div>
143
144- <div id="homepage-blogposts" class="homepage-portlet">
145+ <div id="homepage-blogposts" class="homepage-portlet"
146+ tal:condition="features/app.root_blog.enabled">
147 <h2>Recent Launchpad blog posts</h2>
148 <ul tal:define="posts view/getRecentBlogPosts">
149 <li class="news"
150@@ -188,15 +189,15 @@
151 <a class="sprite add"
152 href="/people/+newteam">Register a team</a>
153 </li>
154- <li>
155+ <li tal:condition="not:view/show_whatslaunchpad">
156 <a class="sprite bug"
157 tal:attributes="href apphomes/bugs">Browse bugs</a>
158 </li>
159- <li>
160+ <li tal:condition="not:view/show_whatslaunchpad">
161 <a class="sprite translate-icon"
162 tal:attributes="href apphomes/translations">Help translate</a>
163 </li>
164- <li>
165+ <li tal:condition="not:view/show_whatslaunchpad">
166 <a class="sprite question"
167 tal:attributes="href apphomes/answers">Find answers</a>
168 </li>
169@@ -204,7 +205,7 @@
170 <a class="sprite ppa-icon"
171 href="/ubuntu/+ppas">Browse Ubuntu PPAs</a>
172 </li>
173- <li>
174+ <li tal:condition="not:view/show_whatslaunchpad">
175 <a class="sprite tour" href="/+tour">Take the tour</a>
176 </li>
177 </ul>
178
179=== modified file 'lib/lp/services/features/flags.py'
180--- lib/lp/services/features/flags.py 2012-08-14 01:57:17 +0000
181+++ lib/lp/services/features/flags.py 2012-08-14 14:33:25 +0000
182@@ -280,6 +280,12 @@
183 '',
184 '',
185 ''),
186+ ('app.root_blog.enabled',
187+ 'boolean',
188+ 'If true, load posts from the Launchpad blog to show on the root page.',
189+ '',
190+ '',
191+ ''),
192 ])
193
194 # The set of all flag names that are documented.