Merge lp:~barry/launchpad/417089-headings into lp:launchpad
- 417089-headings
- Merge into devel
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | not available | ||||
Proposed branch: | lp:~barry/launchpad/417089-headings | ||||
Merge into: | lp:launchpad | ||||
Diff against target: | None lines | ||||
To merge this branch: | bzr merge lp:~barry/launchpad/417089-headings | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Guilherme Salgado (community) | Approve | ||
Review via email: mp+11210@code.launchpad.net |
Commit message
Description of the change
Barry Warsaw (barry) wrote : | # |
Guilherme Salgado (salgado) wrote : | # |
Hi Barry,
Thanks a lot for your work on defining the heading rules and
implementing them. I have a few suggestions below, and I'll send a
separate review for the changes on the karma page.
Also, the 404 page now has a title which doesn't reflect that.
<https:/
On Fri, 2009-09-04 at 15:35 +0000, Barry Warsaw wrote:
> === modified file 'lib/canonical/
> --- lib/canonical/
> +++ lib/canonical/
> @@ -681,6 +681,20 @@
> table.listing th {
> font-weight: bold;
> }
> +table.
> + width: 45em;
> +}
> +/* ~person/+karma */
> +table.cozy-listing {
> + width: 20em;
I think the correct indentation for our CSS is 2 spaces, no?
> + background-color: #fff;
> + border: 1px solid #d2d2d2;
> + border-bottom: 1px solid #d2d2d2;
> +}
> +table.cozy-listing td {
> + border: 1px #d2d2d2;
> + border-style: dotted none none none;
> +}
>
> ul.language, li.language {
> list-style-image: url(/@@/language);
> @@ -743,7 +757,3 @@
> font-weight: normal;
> padding: 2px 1em 2px 2px;
> }
> -
> -table.
> - width: 45em;
> -}
>
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -38,6 +38,15 @@
> permission=
> />
>
> + <!-- Page title reversed breadcrumbs -->
> + <browser:page
> + for="zope.
> + name="+page-title"
> + class="
> + template=
> + permission=
> + />
> +
> <!-- TALES watermark: namespace -->
> <adapter
> for="*"
>
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
[...]
> +
> +Heading level
> +=============
> +
> +The watermark heading is shown above the application tabs, in either H1 level
> +or H2 level. The heading level is determined by the view. For the index view
> +of the context, H1 is used. For all non-index pages, i.e. subpages, H2 is
> +used.
> +
> +The choice of heading level is controlled by a marker interface on the view.
> +Normally, the view class does not implement the marker interface, meaning it
> +is not the index page of the context. In this case the heading is rendered in
> +H2.
> +
> + >>> print test_tales(
> + <h2>Widget</h2>
> +
> +If the view class implements IIndexView, then this is the index page for the
> +context and the heading is rendered in H1.
> +
> + >>> from zope.interface import implements
> + >>> from lp.app.
> +
> + >>> class view:
> + ... implements(
> + ... def __init__(self, context):
> + ... s...
Guilherme Salgado (salgado) wrote : | # |
Hi Barry,
The new +karma page looks much better. I have only a few minor changes
to suggest.
On Fri, 2009-09-04 at 15:35 +0000, Barry Warsaw wrote:
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -39,6 +39,7 @@
> 'PersonFacets',
> 'PersonGPGView',
> 'PersonIndexView',
> + 'PersonKarmaView',
> 'PersonLanguage
> 'PersonLatestQu
> 'PersonNavigation',
> @@ -2507,8 +2508,26 @@
> rootsite='answers')
>
>
> +class PersonKarmaView
> + """A view class used for ~person/+karma."""
> +
> + @property
> + def label(self):
> + return 'Launchpad Karma for ' + cgi.escape(
> +
> + @cachedproperty
> + def has_karma(self):
> + """Does the have karma?"""
s/the have/the person have/?
> + return bool(self.
> +
> + @cachedproperty
> + def has_expired_
> + """Did the user have karma?"""
s/user/person. User is the term we use to refer to the person who's
interacting with LP, and here we're talking about the context of the
view.
I know you just copied-and-pasted them, but these are small enough
changes that I'm sure you won't mind doing. ;)
> + return self.context.
> +
> +
> class PersonView(
> - """A View class used in almost all Person's pages."""
> + """A view class used in almost all Person's pages."""
>
> @cachedproperty
> def recently_
> @@ -2900,16 +2919,6 @@
> else:
> return getUtility(
>
> - @cachedproperty
> - def has_karma(self):
> - """Does the have karma?"""
> - return bool(self.
> -
> - @cachedproperty
> - def has_expired_
> - """Did the user have karm?."""
> - return self.context.
> -
> @property
> def public_
> """The CSS classes that represent the public or private state."""
>
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
[...]
> -</body>
> + >
> + <body>
> + <div metal:fill-
> + tal:define=
> +
> + <p class="
> + This is a summary of the Launchpad karma earned by
> + <span tal:replace=
> + type.
> + </p>
> +
> + <p id="no-karma" tal:condition="not: view/has_karma">
> + <tal:expired condition=
> + <tal:name replace=
> + </tal:expired>
> + <tal:never condition="not: view/has_
> + No karma has yet been assigned to
> + <tal:name replace="...
Guilherme Salgado (salgado) : | # |
Barry Warsaw (barry) wrote : | # |
On Sep 04, 2009, at 06:42 PM, Guilherme Salgado wrote:
>Thanks a lot for your work on defining the heading rules and
>implementing them. I have a few suggestions below, and I'll send a
>separate review for the changes on the karma page.
Thanks for the review Salgado. I'm still working on getting all the tests to
pass, and OCR last week cut into my time on this branch, but I'm keen to
resolve the issues and get this landed so we have time to update our pages to
the new rules.
>Also, the 404 page now has a title which doesn't reflect that.
><https:/
Yep, there are no merge proposals in sample data, so you have to create this
manually.
>
>On Fri, 2009-09-04 at 15:35 +0000, Barry Warsaw wrote:
>> === modified file 'lib/canonical/
>> --- lib/canonical/
>> +++ lib/canonical/
>> @@ -681,6 +681,20 @@
>> table.listing th {
>> font-weight: bold;
>> }
>> +table.
>> + width: 45em;
>> +}
>> +/* ~person/+karma */
>> +table.cozy-listing {
>> + width: 20em;
>
>I think the correct indentation for our CSS is 2 spaces, no?
style-3-0.css is pretty inconsistent here, but it looks to me like most of the
styles use 4 space indents. I'd prefer that (for consistency) and will put
this on the agenda for this week's reviewer meetings.
>> +Editable headings
>> +=================
>> +
>> +Some index pages also allow for editing the context's description. To specify
>
>s/description/
Yep, thanks!
>> def heading(self):
>> - """Return the heading text for the root context.
>> -
>> - If the context itself provides IRootContext then we return an H1,
>> - otherwise it is a H2.
>> + """Return the heading text for the page.
>> +
>> + If the view provides `IEditableConte
>> + rendered from the view's `title_edit_widget` and is generally
>> + editable.
>> +
>> + Otherwise, if the context provides `IRootContext` then we return an
>> + H1, else an H2.
>> """
>> - if IRootContext.
>> + # Check the view; is the title editable?
>> + if IEditableContex
>> + return self._view.
>> + # The title is static, but only the root context gets an H1.
>
>s/the root context/the context's index view/?
Fixed.
>> === renamed file 'lib/lp/
>> --- lib/lp/
>> +++ lib/lp/
>> @@ -1,10 +1,12 @@
>> # Copyright 2009 Canonical Ltd. This software is licensed under the
>> # GNU Affero General Public License version 3 (see the file LICENSE).
>>
>> -"""The primary context interface."""
>> +"""Interfaces for headings and breadcrumbs."""
>
>Actually, there's nothing here for breadcrumbs, is there?
>
>I think these are more like
>
> """Marker interfaces that define how to...
Barry Warsaw (barry) wrote : | # |
On Sep 04, 2009, at 07:30 PM, Guilherme Salgado wrote:
>The new +karma page looks much better. I have only a few minor changes
>to suggest.
Thanks for the review Salgado.
>On Fri, 2009-09-04 at 15:35 +0000, Barry Warsaw wrote:
>> === modified file 'lib/lp/
>> --- lib/lp/
>> +++ lib/lp/
>> @@ -39,6 +39,7 @@
>> 'PersonFacets',
>> 'PersonGPGView',
>> 'PersonIndexView',
>> + 'PersonKarmaView',
>> 'PersonLanguage
>> 'PersonLatestQu
>> 'PersonNavigation',
>> @@ -2507,8 +2508,26 @@
>> rootsite='answers')
>>
>>
>> +class PersonKarmaView
>> + """A view class used for ~person/+karma."""
>> +
>> + @property
>> + def label(self):
>> + return 'Launchpad Karma for ' + cgi.escape(
>> +
>> + @cachedproperty
>> + def has_karma(self):
>> + """Does the have karma?"""
>
>s/the have/the person have/?
Fixed.
>
>> + return bool(self.
>> +
>> + @cachedproperty
>> + def has_expired_
>> + """Did the user have karma?"""
>
>s/user/person. User is the term we use to refer to the person who's
>interacting with LP, and here we're talking about the context of the
>view.
>
>I know you just copied-and-pasted them, but these are small enough
>changes that I'm sure you won't mind doing. ;)
I don't, and thanks! Fixed.
>> === modified file 'lib/lp/
>> --- lib/lp/
>> +++ lib/lp/
>[...]
>> -</body>
>> + >
>> + <body>
>> + <div metal:fill-
>> + tal:define=
>> +
>> + <p class="
>> + This is a summary of the Launchpad karma earned by
>> + <span tal:replace=
>> + type.
>> + </p>
>> +
>> + <p id="no-karma" tal:condition="not: view/has_karma">
>> + <tal:expired condition=
>> + <tal:name replace=
>> + </tal:expired>
>> + <tal:never condition="not: view/has_
>> + No karma has yet been assigned to
>> + <tal:name replace=
>> + Karma is updated daily.
>> + </tal:never>
>> + </p>
>> +
>> + <table class="
>> + tal:condition=
>> + <tbody>
>> + <tr tal:repeat="cache context/
>> + <td tal:content=
>> + <td><strong
>> + class=""
>> + tal:attributes=
>> + tal:content=
>> + </tr>
>> + </tbody>
>> + </table>
>> + <b>Total karma:</b>
>> ...
Guilherme Salgado (salgado) : | # |
Barry Warsaw (barry) wrote : | # |
On Sep 08, 2009, at 02:20 PM, Guilherme Salgado wrote:
>On Tue, 2009-09-08 at 10:42 -0400, Barry Warsaw wrote:
>> On Sep 04, 2009, at 06:42 PM, Guilherme Salgado wrote:
>>
>> >Thanks a lot for your work on defining the heading rules and
>> >implementing them. I have a few suggestions below, and I'll send a
>> >separate review for the changes on the karma page.
>>
>> Thanks for the review Salgado. I'm still working on getting all the tests to
>> pass, and OCR last week cut into my time on this branch, but I'm keen to
>> resolve the issues and get this landed so we have time to update our pages to
>> the new rules.
>>
>> >Also, the 404 page now has a title which doesn't reflect that.
>> ><https:/
>>
>> Yep, there are no merge proposals in sample data, so you have to create this
>> manually.
>
>Right, but what I mean is that the title of our 404 pages now have the
>breadcrumbs in reversed order instead of the 'Not found here' we were
>used to.
This turned out to be surprisingly tricky to solve! We discussed various
options on IRC and came up with using tales formatter, e.g. view/fmt:pagetitle
which simplified a lot of things (the attached incremental diff
notwithstanding).
The nice thing is that this should deprecate CONTEXTS/
PageTemplateCon
eventually remove that stuff.
>Your changes look good to me. There's just one extra blank line that
>ought to be removed.
Actually, I was able to get rid of the whole view class, so I cleaned that up
while I was at it. Here's the incremental.
=== modified file 'lib/canonical/
--- lib/canonical/
+++ lib/canonical/
@@ -19,7 +19,6 @@
'Maintenan
'MenuBox',
'Navigatio
- 'PageTitleView',
'SoftTimeo
'Structura
'Structura
@@ -27,9 +26,6 @@
]
-COLON = ' : '
-
-
import cgi
import operator
import os
@@ -40,7 +36,7 @@
from zope.app import zapi
from zope.datetime import parseDatetimetz, tzinfo, DateTimeError
-from zope.component import getMultiAdapter, getUtility, queryAdapter
+from zope.component import getUtility, queryAdapter
from zope.interface import implements
from zope.publisher.
from zope.publisher.
@@ -289,28 +285,6 @@
return len(self.items) > 1
-class PageTitleView(
- """View for page <title>."""
-
- def __init__(self, context, request):
- super(PageTitle
- self.hierarchy_view = getMultiAdapter(
- (self.context, self.request), name='+hierarchy')
-
- @cachedproperty
- def page_title(self):
- """The page title, constructed from the reversed breadcrumbs."""
- return COLON.join(
- breadcrumb.text for breadcrumb
- in reversed(
-
- @property
- def has_custom_
- ...
Guilherme Salgado (salgado) wrote : | # |
Hi Barry,
This is a nice change. I see only two small things that need to be
changed.
Cheers,
On Tue, 2009-09-08 at 18:42 -0400, Barry Warsaw wrote:
> On Sep 08, 2009, at 02:20 PM, Guilherme Salgado wrote:
>
> >On Tue, 2009-09-08 at 10:42 -0400, Barry Warsaw wrote:
> >> On Sep 04, 2009, at 06:42 PM, Guilherme Salgado wrote:
> >>
> >> >Thanks a lot for your work on defining the heading rules and
> >> >implementing them. I have a few suggestions below, and I'll send a
> >> >separate review for the changes on the karma page.
> >>
> >> Thanks for the review Salgado. I'm still working on getting all the tests to
> >> pass, and OCR last week cut into my time on this branch, but I'm keen to
> >> resolve the issues and get this landed so we have time to update our pages to
> >> the new rules.
> >>
> >> >Also, the 404 page now has a title which doesn't reflect that.
> >> ><https:/
> >>
> >> Yep, there are no merge proposals in sample data, so you have to create this
> >> manually.
> >
> >Right, but what I mean is that the title of our 404 pages now have the
> >breadcrumbs in reversed order instead of the 'Not found here' we were
> >used to.
>
> This turned out to be surprisingly tricky to solve! We discussed various
> options on IRC and came up with using tales formatter, e.g. view/fmt:pagetitle
> which simplified a lot of things (the attached incremental diff
> notwithstanding).
>
> The nice thing is that this should deprecate CONTEXTS/
> PageTemplateCon
> eventually remove that stuff.
>
> >Your changes look good to me. There's just one extra blank line that
> >ought to be removed.
>
> Actually, I was able to get rid of the whole view class, so I cleaned that up
> while I was at it. Here's the incremental.
>
> === modified file 'lib/canonical/
> --- lib/canonical/
> +++ lib/canonical/
> @@ -16,6 +16,8 @@
> import rfc822
> import sys
> import urllib
> +import warnings
> +
> from xml.sax.saxutils import unescape as xml_unescape
> from datetime import datetime, timedelta
> from lazr.enum import enumerated_
> @@ -62,6 +64,9 @@
> from lp.soyuz.
>
>
> +SEPARATOR = ' : '
> +
> +
> def escape(text, quote=True):
> """Escape text for insertion into HTML.
>
> @@ -439,7 +444,12 @@
> # constants, so it's not a problem. We might want to use something like
> # frozenset (http://
> # The names which can be traversed further (e.g context/
> - traversable_names = {'link': 'link', 'url': 'url', 'api_url': 'api_url'}
> + traversable_names = {
> + 'api_url': 'api_url',
> + 'link': 'link',
> + 'pagetitle': 'pagetitle',
> + 'url': 'url',
> + }
>
> # Names which are allowed but can't be traversed further.
> final_traversab
Isn't pagetitle a final_traversab
> @@ -534,6 +544,67 @@
> ...
Barry Warsaw (barry) wrote : | # |
On Sep 9, 2009, at 9:27 AM, Guilherme Salgado wrote:
> This is a nice change. I see only two small things that need to be
> changed.
Hi Salgado. With all the stress in trying to get this branch landed,
I forgot about these comments. I'll get this in a follow up branch
this week.
Thanks!
Preview Diff
1 | === modified file 'lib/canonical/launchpad/browser/launchpad.py' |
2 | --- lib/canonical/launchpad/browser/launchpad.py 2009-09-02 12:39:24 +0000 |
3 | +++ lib/canonical/launchpad/browser/launchpad.py 2009-09-03 20:26:49 +0000 |
4 | @@ -26,6 +26,9 @@ |
5 | ] |
6 | |
7 | |
8 | +COLON = ' : ' |
9 | + |
10 | + |
11 | import cgi |
12 | import operator |
13 | import os |
14 | @@ -256,6 +259,12 @@ |
15 | breadcrumbs.append(page_crumb) |
16 | return breadcrumbs |
17 | |
18 | + @cachedproperty |
19 | + def page_title(self): |
20 | + """The page title, constructed from the reversed breadcrumbs.""" |
21 | + return COLON.join( |
22 | + breadcrumb.text for breadcrumb in reversed(self.items)) |
23 | + |
24 | def makeBreadcrumbForRequestedPage(self): |
25 | """Return an `IBreadcrumb` for the requested page. |
26 | |
27 | |
28 | === modified file 'lib/canonical/launchpad/icing/style-3-0.css' |
29 | --- lib/canonical/launchpad/icing/style-3-0.css 2009-09-04 11:25:26 +0000 |
30 | +++ lib/canonical/launchpad/icing/style-3-0.css 2009-09-04 12:56:38 +0000 |
31 | @@ -681,6 +681,20 @@ |
32 | table.listing th { |
33 | font-weight: bold; |
34 | } |
35 | +table.narrow-listing { |
36 | + width: 45em; |
37 | +} |
38 | +/* ~person/+karma */ |
39 | +table.cozy-listing { |
40 | + width: 20em; |
41 | + background-color: #fff; |
42 | + border: 1px solid #d2d2d2; |
43 | + border-bottom: 1px solid #d2d2d2; |
44 | +} |
45 | +table.cozy-listing td { |
46 | + border: 1px #d2d2d2; |
47 | + border-style: dotted none none none; |
48 | +} |
49 | |
50 | ul.language, li.language { |
51 | list-style-image: url(/@@/language); |
52 | @@ -743,7 +757,3 @@ |
53 | font-weight: normal; |
54 | padding: 2px 1em 2px 2px; |
55 | } |
56 | - |
57 | -table.narrow-listing { |
58 | - width: 45em; |
59 | -} |
60 | |
61 | === modified file 'lib/canonical/launchpad/templates/launchpad-form.pt' |
62 | --- lib/canonical/launchpad/templates/launchpad-form.pt 2009-08-01 02:04:55 +0000 |
63 | +++ lib/canonical/launchpad/templates/launchpad-form.pt 2009-09-03 15:39:22 +0000 |
64 | @@ -14,11 +14,6 @@ |
65 | enctype="multipart/form-data" |
66 | accept-charset="UTF-8"> |
67 | |
68 | - <h1 tal:condition="view/label" |
69 | - tal:content="view/label" |
70 | - metal:define-slot="heading" |
71 | - >Add Something</h1> |
72 | - |
73 | <div metal:define-macro="formbody"> |
74 | |
75 | <p metal:define-slot="extra_info" tal:replace="nothing"> |
76 | @@ -38,8 +33,8 @@ |
77 | Schema validation errors. |
78 | </p> |
79 | |
80 | - <div class="row" |
81 | - metal:define-slot="extra_top" |
82 | + <div class="row" |
83 | + metal:define-slot="extra_top" |
84 | tal:replace="nothing"> |
85 | <div>Extra top</div> |
86 | <div><input type="text"/></div> |
87 | @@ -55,7 +50,7 @@ |
88 | tal:content="structure script" /> |
89 | |
90 | <div class="row" |
91 | - metal:define-slot="extra_bottom" |
92 | + metal:define-slot="extra_bottom" |
93 | tal:replace="nothing"> |
94 | <div>Extra bottom</div> |
95 | <div class="field"><input type="text" /></div> |
96 | @@ -73,8 +68,8 @@ |
97 | </tal:has-cancel-link> |
98 | </div> |
99 | |
100 | - <div class="row" |
101 | - metal:define-slot="extra_buttons" |
102 | + <div class="row" |
103 | + metal:define-slot="extra_buttons" |
104 | tal:replace="nothing"> |
105 | </div> |
106 | |
107 | |
108 | === modified file 'lib/canonical/launchpad/versioninfo.py' |
109 | --- lib/canonical/launchpad/versioninfo.py 2009-06-25 05:30:52 +0000 |
110 | +++ lib/canonical/launchpad/versioninfo.py 2009-09-02 19:11:01 +0000 |
111 | @@ -1,7 +1,7 @@ |
112 | # Copyright 2009 Canonical Ltd. This software is licensed under the |
113 | # GNU Affero General Public License version 3 (see the file LICENSE). |
114 | |
115 | -"""Give access to bzr version info, if available. |
116 | +"""Give access to bzr and other version info, if available. |
117 | |
118 | The bzr version info file is expected to be in the Launchpad root in the |
119 | file bzr-version-info.py. |
120 | @@ -16,19 +16,28 @@ |
121 | If the bzr-version-info.py file does not exist, then revno, date and |
122 | branch_nick will all be None. |
123 | |
124 | -If that file exists, and contains valid python, revno, date and branch_nick |
125 | +If that file exists, and contains valid Python, revno, date and branch_nick |
126 | will have appropriate values from version_info. |
127 | |
128 | -If that file exists, and contains invalid python, there will be an error when |
129 | +If that file exists, and contains invalid Python, there will be an error when |
130 | this module is loaded. This module is imported into |
131 | canonical/launchpad/__init__.py so that such errors are caught at start-up. |
132 | |
133 | +This module also reads version.txt at the top of the tree (i.e. a sibling of |
134 | +bzr-version-info.py), which contains the Launchpad release number. If that |
135 | +file does not exist, we make something up. |
136 | """ |
137 | |
138 | +__all__ = [ |
139 | + 'branch_nick', |
140 | + 'date', |
141 | + 'revno', |
142 | + 'versioninfo', |
143 | + ] |
144 | + |
145 | + |
146 | import imp |
147 | |
148 | -__all__ = ['versioninfo', 'revno', 'date', 'branch_nick'] |
149 | - |
150 | |
151 | def read_version_info(): |
152 | try: |
153 | @@ -52,3 +61,14 @@ |
154 | date = versioninfo.get('date') |
155 | branch_nick = versioninfo.get('branch_nick') |
156 | |
157 | + |
158 | +try: |
159 | + version_file = open('version.txt') |
160 | +except IOError: |
161 | + release = 'x.y.z' |
162 | +else: |
163 | + try: |
164 | + version_data = version_file.read() |
165 | + release = version_data.strip() |
166 | + finally: |
167 | + version_file.close() |
168 | |
169 | === modified file 'lib/canonical/launchpad/webapp/interfaces.py' |
170 | --- lib/canonical/launchpad/webapp/interfaces.py 2009-08-24 17:47:34 +0000 |
171 | +++ lib/canonical/launchpad/webapp/interfaces.py 2009-09-03 23:06:50 +0000 |
172 | @@ -350,7 +350,7 @@ |
173 | |
174 | def getRootURL(rootsite): |
175 | """Return this request's root URL. |
176 | - |
177 | + |
178 | If rootsite is not None, then return the root URL for that rootsite, |
179 | looked up from our config. |
180 | """ |
181 | @@ -626,11 +626,11 @@ |
182 | instance of a Zope internationalized message will cause the |
183 | message to be translated, then CGI escaped. |
184 | |
185 | - :param msg: This may be a string, an instance of |
186 | - `zope.i18n.Message`, , or an instance of `IStructuredString`. |
187 | + :param msg: This may be a string, an instance of `zope.i18n.Message`, |
188 | + or an instance of `IStructuredString`. |
189 | |
190 | :param level: One of the `BrowserNotificationLevel` values: DEBUG, |
191 | - INFO, NOTICE, WARNING, ERROR. |
192 | + INFO, NOTICE, WARNING, ERROR. |
193 | """ |
194 | |
195 | def removeAllNotifications(): |
196 | @@ -796,7 +796,7 @@ |
197 | """Get a Storm store with a desired flavor. |
198 | |
199 | Stores come in two flavors - MASTER_FLAVOR and SLAVE_FLAVOR. |
200 | - |
201 | + |
202 | The master is writable and up to date, but we should not use it |
203 | whenever possible because there is only one master and we don't want |
204 | it to be overloaded. |
205 | |
206 | === modified file 'lib/lp/app/browser/configure.zcml' |
207 | --- lib/lp/app/browser/configure.zcml 2009-08-27 09:06:42 +0000 |
208 | +++ lib/lp/app/browser/configure.zcml 2009-09-03 19:12:20 +0000 |
209 | @@ -38,6 +38,15 @@ |
210 | permission="zope.Public" |
211 | /> |
212 | |
213 | + <!-- Page title reversed breadcrumbs --> |
214 | + <browser:page |
215 | + for="zope.interface.Interface" |
216 | + name="+page-title" |
217 | + class="canonical.launchpad.browser.launchpad.Hierarchy" |
218 | + template="../templates/launchpad-page-title.pt" |
219 | + permission="zope.Public" |
220 | + /> |
221 | + |
222 | <!-- TALES watermark: namespace --> |
223 | <adapter |
224 | for="*" |
225 | |
226 | === modified file 'lib/lp/app/browser/tests/watermark.txt' |
227 | --- lib/lp/app/browser/tests/watermark.txt 2009-08-19 23:11:16 +0000 |
228 | +++ lib/lp/app/browser/tests/watermark.txt 2009-09-03 23:06:50 +0000 |
229 | @@ -1,3 +1,4 @@ |
230 | +=============================== |
231 | The "watermark" TALES formatter |
232 | =============================== |
233 | |
234 | @@ -7,83 +8,131 @@ |
235 | |
236 | |
237 | Watermark headings |
238 | ------------------- |
239 | +================== |
240 | |
241 | The `watermark:heading` is used when you want a heading for the nearest object |
242 | that implements IRootContext. |
243 | |
244 | -Objects that directly provide IRootContext get an h1 heading, those that don't |
245 | -get an h2 heading (although why I'm not yet certain). |
246 | - |
247 | >>> from lp.testing import test_tales |
248 | + >>> class view: |
249 | + ... def __init__(self, context): |
250 | + ... self.context = context |
251 | |
252 | Products directly implement IRootContext. |
253 | |
254 | >>> widget = factory.makeProduct(title='Widget') |
255 | - >>> print test_tales('project/watermark:heading', project=widget) |
256 | - <h1>Widget</h1> |
257 | + >>> print test_tales('view/watermark:heading', view=view(widget)) |
258 | + <h...>Widget</h...> |
259 | |
260 | A series of the product still show the product watermark. |
261 | |
262 | >>> dev_focus = widget.development_focus |
263 | - >>> print test_tales('series/watermark:heading', series=dev_focus) |
264 | - <h2>Widget</h2> |
265 | + >>> print test_tales('view/watermark:heading', view=view(dev_focus)) |
266 | + <h...>Widget</h...> |
267 | |
268 | Projects also directly implement IRootContext ... |
269 | |
270 | >>> kde = factory.makeProject(title='KDE') |
271 | - >>> print test_tales('project/watermark:heading', project=kde) |
272 | - <h1>KDE</h1> |
273 | + >>> print test_tales('view/watermark:heading', view=view(kde)) |
274 | + <h...>KDE</h...> |
275 | |
276 | ... as do distributions ... |
277 | |
278 | >>> mint = factory.makeDistribution(title='Mint Linux') |
279 | - >>> print test_tales( |
280 | - ... "distro/watermark:heading", distro=mint) |
281 | - <h1>Mint Linux</h1> |
282 | + >>> print test_tales('view/watermark:heading', view=view(mint)) |
283 | + <h...>Mint Linux</h...> |
284 | |
285 | ... and people ... |
286 | |
287 | >>> eric = factory.makePerson(displayname="Eric the Viking") |
288 | - >>> print test_tales("person/watermark:heading", person=eric) |
289 | - <h1>Eric the Viking</h1> |
290 | + >>> print test_tales('view/watermark:heading', view=view(eric)) |
291 | + <h...>Eric the Viking</h...> |
292 | |
293 | ... and sprints. |
294 | |
295 | >>> sprint = factory.makeSprint(title="Launchpad Epic") |
296 | - >>> print test_tales("sprint/watermark:heading", sprint=sprint) |
297 | - <h1>Launchpad Epic</h1> |
298 | + >>> print test_tales('view/watermark:heading', view=view(sprint)) |
299 | + <h...>Launchpad Epic</h...> |
300 | |
301 | If there is no root context defined for the object, then the heading is |
302 | 'Launchpad.net' (differentiating from the Launchpad project within |
303 | Launchpad.net). |
304 | |
305 | >>> machine = factory.makeCodeImportMachine() |
306 | - >>> print test_tales( |
307 | - ... "machine/watermark:heading", machine=machine) |
308 | - <h2>Launchpad.net</h2> |
309 | + >>> print test_tales('view/watermark:heading', view=view(machine)) |
310 | + <h...>Launchpad.net</h...> |
311 | |
312 | -Any html in the context title will be escaped to avoid XSS vulnerabilities. |
313 | +Any HTML in the context title will be escaped to avoid XSS vulnerabilities. |
314 | |
315 | >>> person = factory.makePerson( |
316 | ... displayname="Fubar<br/><script>alert('XSS')</script>") |
317 | - >>> print test_tales("person/watermark:heading", person=person) |
318 | - <h1>Fubar<br/><script>alert('XSS')</script></h1> |
319 | + >>> print test_tales('view/watermark:heading', view=view(person)) |
320 | + <h...>Fubar<br/><script>alert('XSS')</script></h...> |
321 | |
322 | |
323 | Watermark images |
324 | ------------------- |
325 | +================ |
326 | |
327 | The image for the watermark is determined effectively by context/image:logo. |
328 | |
329 | - >>> print test_tales("series/watermark:logo", series=dev_focus) |
330 | + >>> print test_tales('view/watermark:logo', view=view(dev_focus)) |
331 | <img ... src="/@@/product-logo" /> |
332 | |
333 | - >>> print test_tales("person/watermark:logo", person=eric) |
334 | + >>> print test_tales('view/watermark:logo', view=view(eric)) |
335 | <img ... src="/@@/person-logo" /> |
336 | |
337 | -If there is no root context, the launchpad logo is shown. |
338 | +If there is no root context, the Launchpad logo is shown. |
339 | |
340 | - >>> print test_tales( |
341 | - ... "machine/watermark:logo", machine=machine) |
342 | + >>> print test_tales('view/watermark:logo', view=view(machine)) |
343 | <img ... src="/@@/launchpad-logo" /> |
344 | + |
345 | + |
346 | +Heading level |
347 | +============= |
348 | + |
349 | +The watermark heading is shown above the application tabs, in either H1 level |
350 | +or H2 level. The heading level is determined by the view. For the index view |
351 | +of the context, H1 is used. For all non-index pages, i.e. subpages, H2 is |
352 | +used. |
353 | + |
354 | +The choice of heading level is controlled by a marker interface on the view. |
355 | +Normally, the view class does not implement the marker interface, meaning it |
356 | +is not the index page of the context. In this case the heading is rendered in |
357 | +H2. |
358 | + |
359 | + >>> print test_tales('view/watermark:heading', view=view(widget)) |
360 | + <h2>Widget</h2> |
361 | + |
362 | +If the view class implements IIndexView, then this is the index page for the |
363 | +context and the heading is rendered in H1. |
364 | + |
365 | + >>> from zope.interface import implements |
366 | + >>> from lp.app.interfaces.headings import IIndexView |
367 | + |
368 | + >>> class view: |
369 | + ... implements(IIndexView) |
370 | + ... def __init__(self, context): |
371 | + ... self.context = context |
372 | + |
373 | + >>> print test_tales('view/watermark:heading', view=view(widget)) |
374 | + <h1>Widget</h1> |
375 | + |
376 | + |
377 | +Editable headings |
378 | +================= |
379 | + |
380 | +Some index pages also allow for editing the context's description. To specify |
381 | +this, the view must implement IEditableContextTitle and provide an method |
382 | +that returns all the HTML necessary to edit the context, including the |
383 | +appropriate H tag. |
384 | + |
385 | + >>> from lp.app.interfaces.headings import IEditableContextTitle |
386 | + >>> class view: |
387 | + ... implements(IEditableContextTitle) |
388 | + ... def __init__(self, context): |
389 | + ... self.context = context |
390 | + ... def title_edit_widget(self): |
391 | + ... return '<h0>EDIT ME</h0>' |
392 | + |
393 | + >>> print test_tales('view/watermark:heading', view=view(widget)) |
394 | + <h0>EDIT ME</h0> |
395 | |
396 | === modified file 'lib/lp/app/browser/watermark.py' |
397 | --- lib/lp/app/browser/watermark.py 2009-08-19 22:44:27 +0000 |
398 | +++ lib/lp/app/browser/watermark.py 2009-09-03 23:06:50 +0000 |
399 | @@ -18,7 +18,8 @@ |
400 | |
401 | from canonical.lazr.canonicalurl import nearest_provides_or_adapted |
402 | |
403 | -from lp.app.interfaces.rootcontext import IRootContext |
404 | +from lp.app.interfaces.headings import ( |
405 | + IEditableContextTitle, IIndexView, IRootContext) |
406 | |
407 | |
408 | class WatermarkTalesAdapter: |
409 | @@ -26,34 +27,43 @@ |
410 | |
411 | implements(ITraversable) |
412 | |
413 | - def __init__(self, context): |
414 | - self._context = context |
415 | + def __init__(self, view): |
416 | + self._view = view |
417 | + self._context = view.context |
418 | |
419 | @property |
420 | def root_context(self): |
421 | return nearest_provides_or_adapted(self._context, IRootContext) |
422 | |
423 | def heading(self): |
424 | - """Return the heading text for the root context. |
425 | - |
426 | - If the context itself provides IRootContext then we return an H1, |
427 | - otherwise it is a H2. |
428 | + """Return the heading text for the page. |
429 | + |
430 | + If the view provides `IEditableContextTitle` then the top heading is |
431 | + rendered from the view's `title_edit_widget` and is generally |
432 | + editable. |
433 | + |
434 | + Otherwise, if the context provides `IRootContext` then we return an |
435 | + H1, else an H2. |
436 | """ |
437 | - if IRootContext.providedBy(self._context): |
438 | + # Check the view; is the title editable? |
439 | + if IEditableContextTitle.providedBy(self._view): |
440 | + return self._view.title_edit_widget() |
441 | + # The title is static, but only the root context gets an H1. |
442 | + if IIndexView.providedBy(self._view): |
443 | heading = 'h1' |
444 | else: |
445 | heading = 'h2' |
446 | - |
447 | - root = self.root_context |
448 | - if root is None: |
449 | + # If there is actually no root context, then it's a top-level |
450 | + # context-less page so Launchpad.net is shown as the branding. |
451 | + if self.root_context is None: |
452 | title = 'Launchpad.net' |
453 | else: |
454 | - title = root.title |
455 | - |
456 | - return "<%(heading)s>%(title)s</%(heading)s>" % { |
457 | - 'heading': heading, |
458 | - 'title': cgi.escape(title) |
459 | - } |
460 | + title = self.root_context.title |
461 | + # For non-editable titles, generate the static heading. |
462 | + return "<%(heading)s>%(title)s</%(heading)s>" % dict( |
463 | + heading=heading, |
464 | + title=cgi.escape(title), |
465 | + ) |
466 | |
467 | def logo(self): |
468 | """Return the logo image for the root context.""" |
469 | @@ -66,4 +76,4 @@ |
470 | elif name == "logo": |
471 | return self.logo() |
472 | else: |
473 | - raise TraversalError, name |
474 | + raise TraversalError(name) |
475 | |
476 | === modified file 'lib/lp/app/interfaces/__init__.py' |
477 | --- lib/lp/app/interfaces/__init__.py 2009-08-19 05:33:26 +0000 |
478 | +++ lib/lp/app/interfaces/__init__.py 2009-09-03 23:06:50 +0000 |
479 | @@ -1,1 +0,0 @@ |
480 | -"""Interfaces for the Launchpad application.""" |
481 | |
482 | === renamed file 'lib/lp/app/interfaces/rootcontext.py' => 'lib/lp/app/interfaces/headings.py' |
483 | --- lib/lp/app/interfaces/rootcontext.py 2009-08-19 06:18:41 +0000 |
484 | +++ lib/lp/app/interfaces/headings.py 2009-09-04 12:59:09 +0000 |
485 | @@ -1,10 +1,12 @@ |
486 | # Copyright 2009 Canonical Ltd. This software is licensed under the |
487 | # GNU Affero General Public License version 3 (see the file LICENSE). |
488 | |
489 | -"""The primary context interface.""" |
490 | +"""Interfaces for headings and breadcrumbs.""" |
491 | |
492 | __metaclass__ = type |
493 | __all__ = [ |
494 | + 'IEditableContextTitle', |
495 | + 'IIndexView', |
496 | 'IRootContext', |
497 | ] |
498 | |
499 | @@ -16,3 +18,16 @@ |
500 | """Something that is an object off the Launchpad root.""" |
501 | |
502 | title = Attribute('The title of the root context object.') |
503 | + |
504 | + |
505 | +class IIndexView(Interface): |
506 | + """The index view of the current context.""" |
507 | + |
508 | + title = Attribute('The title of the context object.') |
509 | + |
510 | + |
511 | +class IEditableContextTitle(Interface): |
512 | + """Interface specifying that the context has an editable title.""" |
513 | + |
514 | + def title_edit_widget(): |
515 | + """Return the HTML of the editable title widget.""" |
516 | |
517 | === modified file 'lib/lp/app/templates/base-layout.pt' |
518 | --- lib/lp/app/templates/base-layout.pt 2009-08-31 12:01:46 +0000 |
519 | +++ lib/lp/app/templates/base-layout.pt 2009-09-03 19:12:20 +0000 |
520 | @@ -4,7 +4,8 @@ |
521 | define-macro="master" |
522 | tal:define=" |
523 | revno modules/canonical.launchpad.versioninfo/revno | string:unknown; |
524 | - version string:Launchpad 2.2.7 (r${revno}); |
525 | + release modules/canonical.launchpad.versioninfo/release; |
526 | + version string:Launchpad ${release} (r${revno}); |
527 | devmode modules/canonical.config/config/devmode; |
528 | rooturl modules/canonical.launchpad.webapp.vhosts/allvhosts/configs/mainsite/rooturl; |
529 | is_demo modules/canonical.config/config/launchpad/is_demo; |
530 | @@ -26,9 +27,12 @@ |
531 | yui string:${icingroot}/yui/3.0.0pr2/build; |
532 | lazr_js string:${icingroot}/lazr/build; |
533 | lp_js string:${icingroot}/build"> |
534 | - <title i18n:translate="" |
535 | - tal:content="view/page_title|CONTEXTS/fmt:pagetitle">Launchpad</title> |
536 | - |
537 | + <title tal:condition="context/@@+page-title/display_breadcrumbs" |
538 | + tal:replace="structure context/@@+page-title">The Title</title> |
539 | + <title tal:condition="not: context/@@+page-title/display_breadcrumbs" |
540 | + i18n:translate="" |
541 | + tal:content="view/page_title|CONTEXTS/fmt:pagetitle" |
542 | + >Launchpad</title> |
543 | <link rel="shortcut icon" href="/@@/launchpad.png" /> |
544 | |
545 | <tal:atomfeeds define="feed_links view/feed_links | nothing"> |
546 | @@ -92,8 +96,8 @@ |
547 | |
548 | <div class="watermark-apps-portlet top-portlet" |
549 | tal:condition="view/macro:pagehas/applicationtabs"> |
550 | - <span tal:replace="structure context/watermark:logo"></span> |
551 | - <h2 tal:replace="structure context/watermark:heading"> |
552 | + <span tal:replace="structure view/watermark:logo"></span> |
553 | + <h2 tal:replace="structure view/watermark:heading"> |
554 | Celso Providelo |
555 | </h2> |
556 | <metal:heading_nav |
557 | @@ -119,10 +123,10 @@ |
558 | xml:lang view/lang|default_language|default; |
559 | dir view/dir|string:ltr"> |
560 | <div tal:condition="view/macro:pagehas/applicationtabs"> |
561 | - <h2 |
562 | - tal:condition="context/title|nothing" |
563 | - tal:content="context/title" |
564 | - metal:define-slot="heading" /> |
565 | + <h1 tal:condition="view/label" |
566 | + tal:content="view/label" |
567 | + metal:define-slot="heading" |
568 | + >Page Label</h1> |
569 | <tal:breadcrumbs replace="structure context/@@+hierarchy"> |
570 | ProjectName > Branches > Merge Proposals > fix-for-navigation |
571 | </tal:breadcrumbs> |
572 | |
573 | === added file 'lib/lp/app/templates/launchpad-page-title.pt' |
574 | --- lib/lp/app/templates/launchpad-page-title.pt 1970-01-01 00:00:00 +0000 |
575 | +++ lib/lp/app/templates/launchpad-page-title.pt 2009-09-03 19:12:20 +0000 |
576 | @@ -0,0 +1,7 @@ |
577 | +<title |
578 | + xmlns="http://www.w3.org/1999/xhtml" |
579 | + xmlns:tal="http://xml.zope.org/namespaces/tal" |
580 | + xmlns:metal="http://xml.zope.org/namespaces/metal" |
581 | + xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
582 | + i18n:domain="launchpad" |
583 | + tal:content="view/page_title">Page Title</title> |
584 | |
585 | === modified file 'lib/lp/blueprints/interfaces/sprint.py' |
586 | --- lib/lp/blueprints/interfaces/sprint.py 2009-08-19 23:11:16 +0000 |
587 | +++ lib/lp/blueprints/interfaces/sprint.py 2009-09-03 23:06:50 +0000 |
588 | @@ -27,7 +27,7 @@ |
589 | PublicPersonChoice) |
590 | from canonical.launchpad.validators.name import name_validator |
591 | from canonical.launchpad.interfaces.launchpad import IHasDrivers |
592 | -from lp.app.interfaces.rootcontext import IRootContext |
593 | +from lp.app.interfaces.headings import IRootContext |
594 | from lp.registry.interfaces.role import IHasOwner |
595 | from lp.blueprints.interfaces.specificationtarget import IHasSpecifications |
596 | |
597 | |
598 | === modified file 'lib/lp/bugs/browser/configure.zcml' |
599 | --- lib/lp/bugs/browser/configure.zcml 2009-09-04 11:08:20 +0000 |
600 | +++ lib/lp/bugs/browser/configure.zcml 2009-09-04 12:56:38 +0000 |
601 | @@ -788,6 +788,12 @@ |
602 | class="lp.code.browser.branch.BranchHierarchy" |
603 | template="../../app/templates/launchpad-hierarchy.pt" |
604 | permission="zope.Public"/> |
605 | + <browser:page |
606 | + for="lp.bugs.interfaces.bugbranch.IBugBranch" |
607 | + name="+page-title" |
608 | + class="lp.code.browser.branch.BranchHierarchy" |
609 | + template="../../app/templates/launchpad-page-title.pt" |
610 | + permission="zope.Public"/> |
611 | <browser:url |
612 | for="lp.bugs.interfaces.bugbranch.IBugBranch" |
613 | path_expression="string:+bug/${bug/id}" |
614 | |
615 | === modified file 'lib/lp/code/browser/configure.zcml' |
616 | --- lib/lp/code/browser/configure.zcml 2009-09-02 23:14:55 +0000 |
617 | +++ lib/lp/code/browser/configure.zcml 2009-09-03 23:06:50 +0000 |
618 | @@ -10,7 +10,7 @@ |
619 | i18n_domain="launchpad"> |
620 | |
621 | <adapter |
622 | - provides="lp.app.interfaces.rootcontext.IRootContext" |
623 | + provides="lp.app.interfaces.headings.IRootContext" |
624 | for="lp.code.interfaces.branch.IBranch" |
625 | factory="lp.code.browser.branch.branch_root_context"/> |
626 | |
627 | @@ -159,6 +159,12 @@ |
628 | class="lp.code.browser.branch.BranchHierarchy" |
629 | template="../../app/templates/launchpad-hierarchy.pt" |
630 | permission="zope.Public"/> |
631 | + <browser:page |
632 | + for="lp.code.interfaces.branchmergeproposal.IBranchMergeProposal" |
633 | + name="+page-title" |
634 | + class="lp.code.browser.branch.BranchHierarchy" |
635 | + template="../../app/templates/launchpad-page-title.pt" |
636 | + permission="zope.Public"/> |
637 | <browser:navigation |
638 | module="lp.code.browser.branchmergeproposal" |
639 | classes=" |
640 | @@ -323,6 +329,12 @@ |
641 | class="lp.code.browser.branch.BranchHierarchy" |
642 | template="../../app/templates/launchpad-hierarchy.pt" |
643 | permission="zope.Public"/> |
644 | + <browser:page |
645 | + for="lp.code.interfaces.branchsubscription.IBranchSubscription" |
646 | + name="+page-title" |
647 | + class="lp.code.browser.branch.BranchHierarchy" |
648 | + template="../../app/templates/launchpad-page-title.pt" |
649 | + permission="zope.Public"/> |
650 | <browser:defaultView |
651 | for="lp.code.interfaces.branchsubscription.IBranchSubscription" |
652 | name="+index"/> |
653 | @@ -344,6 +356,12 @@ |
654 | class="lp.code.browser.branch.BranchHierarchy" |
655 | template="../../app/templates/launchpad-hierarchy.pt" |
656 | permission="zope.Public"/> |
657 | + <browser:page |
658 | + for="lp.code.interfaces.branch.IBranch" |
659 | + name="+page-title" |
660 | + class="lp.code.browser.branch.BranchHierarchy" |
661 | + template="../../app/templates/launchpad-page-title.pt" |
662 | + permission="zope.Public"/> |
663 | <browser:defaultView |
664 | for="lp.code.interfaces.branch.IBranch" |
665 | name="+index"/> |
666 | @@ -561,6 +579,12 @@ |
667 | class="lp.code.browser.branch.BranchHierarchy" |
668 | template="../../app/templates/launchpad-hierarchy.pt" |
669 | permission="zope.Public"/> |
670 | + <browser:page |
671 | + for="lp.code.interfaces.codereviewcomment.ICodeReviewComment" |
672 | + name="+page-title" |
673 | + class="lp.code.browser.branch.BranchHierarchy" |
674 | + template="../../app/templates/launchpad-page-title.pt" |
675 | + permission="zope.Public"/> |
676 | <browser:url |
677 | for="lp.code.interfaces.codereviewcomment.ICodeReviewComment" |
678 | path_expression="string:comments/${id}" |
679 | |
680 | === modified file 'lib/lp/code/browser/tests/test_branch.py' |
681 | --- lib/lp/code/browser/tests/test_branch.py 2009-08-19 06:18:41 +0000 |
682 | +++ lib/lp/code/browser/tests/test_branch.py 2009-09-03 23:06:50 +0000 |
683 | @@ -20,7 +20,7 @@ |
684 | from canonical.config import config |
685 | from canonical.database.constants import UTC_NOW |
686 | |
687 | -from lp.app.interfaces.rootcontext import IRootContext |
688 | +from lp.app.interfaces.headings import IRootContext |
689 | from lp.code.browser.branch import ( |
690 | BranchAddView, BranchMirrorStatusView, BranchReviewerEditView, |
691 | BranchSparkView, BranchView) |
692 | |
693 | === modified file 'lib/lp/registry/browser/configure.zcml' |
694 | --- lib/lp/registry/browser/configure.zcml 2009-09-02 13:28:19 +0000 |
695 | +++ lib/lp/registry/browser/configure.zcml 2009-09-03 00:38:35 +0000 |
696 | @@ -846,14 +846,19 @@ |
697 | <browser:pages |
698 | for="lp.registry.interfaces.person.IPerson" |
699 | permission="zope.Public" |
700 | + class="lp.registry.browser.person.PersonKarmaView"> |
701 | + <browser:page |
702 | + name="+karma" |
703 | + template="../templates/person-karma.pt"/> |
704 | + </browser:pages> |
705 | + <browser:pages |
706 | + for="lp.registry.interfaces.person.IPerson" |
707 | + permission="zope.Public" |
708 | class="lp.registry.browser.person.PersonView"> |
709 | <browser:page |
710 | name="+participation" |
711 | template="../templates/person-participation.pt"/> |
712 | <browser:page |
713 | - name="+karma" |
714 | - template="../templates/person-karma.pt"/> |
715 | - <browser:page |
716 | name="+sshkeys" |
717 | attribute="showSSHKeys"/> |
718 | <browser:page |
719 | |
720 | === modified file 'lib/lp/registry/browser/person.py' |
721 | --- lib/lp/registry/browser/person.py 2009-09-04 02:20:59 +0000 |
722 | +++ lib/lp/registry/browser/person.py 2009-09-04 12:56:38 +0000 |
723 | @@ -39,6 +39,7 @@ |
724 | 'PersonFacets', |
725 | 'PersonGPGView', |
726 | 'PersonIndexView', |
727 | + 'PersonKarmaView', |
728 | 'PersonLanguagesView', |
729 | 'PersonLatestQuestionsView', |
730 | 'PersonNavigation', |
731 | @@ -2507,8 +2508,26 @@ |
732 | rootsite='answers') |
733 | |
734 | |
735 | +class PersonKarmaView(LaunchpadView): |
736 | + """A view class used for ~person/+karma.""" |
737 | + |
738 | + @property |
739 | + def label(self): |
740 | + return 'Launchpad Karma for ' + cgi.escape(self.user.displayname) |
741 | + |
742 | + @cachedproperty |
743 | + def has_karma(self): |
744 | + """Does the have karma?""" |
745 | + return bool(self.context.karma_category_caches) |
746 | + |
747 | + @cachedproperty |
748 | + def has_expired_karma(self): |
749 | + """Did the user have karma?""" |
750 | + return self.context.latestKarma().count() > 0 |
751 | + |
752 | + |
753 | class PersonView(LaunchpadView, FeedsMixin): |
754 | - """A View class used in almost all Person's pages.""" |
755 | + """A view class used in almost all Person's pages.""" |
756 | |
757 | @cachedproperty |
758 | def recently_approved_members(self): |
759 | @@ -2900,16 +2919,6 @@ |
760 | else: |
761 | return getUtility(ILaunchpadCelebrities).english.englishname |
762 | |
763 | - @cachedproperty |
764 | - def has_karma(self): |
765 | - """Does the have karma?""" |
766 | - return bool(self.context.karma_category_caches) |
767 | - |
768 | - @cachedproperty |
769 | - def has_expired_karma(self): |
770 | - """Did the user have karm?.""" |
771 | - return self.context.latestKarma().count() > 0 |
772 | - |
773 | @property |
774 | def public_private_css(self): |
775 | """The CSS classes that represent the public or private state.""" |
776 | |
777 | === modified file 'lib/lp/registry/browser/product.py' |
778 | --- lib/lp/registry/browser/product.py 2009-09-04 02:20:59 +0000 |
779 | +++ lib/lp/registry/browser/product.py 2009-09-04 12:56:38 +0000 |
780 | @@ -54,6 +54,7 @@ |
781 | from lazr.delegates import delegates |
782 | from canonical.launchpad import _ |
783 | from canonical.launchpad.fields import PillarAliases, PublicPersonChoice |
784 | +from lp.app.interfaces.headings import IEditableContextTitle |
785 | from lp.bugs.interfaces.bugtask import RESOLVED_BUGTASK_STATUSES |
786 | from lp.bugs.interfaces.bugwatch import IBugTracker |
787 | from lp.services.worlddata.interfaces.country import ICountry |
788 | @@ -776,7 +777,7 @@ |
789 | ProductDownloadFileMixin, UsesLaunchpadMixin): |
790 | |
791 | __used_for__ = IProduct |
792 | - implements(IProductActionMenu) |
793 | + implements(IProductActionMenu, IEditableContextTitle) |
794 | |
795 | def __init__(self, context, request): |
796 | HasAnnouncementsView.__init__(self, context, request) |
797 | |
798 | === modified file 'lib/lp/registry/interfaces/distribution.py' |
799 | --- lib/lp/registry/interfaces/distribution.py 2009-08-28 06:38:41 +0000 |
800 | +++ lib/lp/registry/interfaces/distribution.py 2009-09-03 23:06:50 +0000 |
801 | @@ -36,7 +36,7 @@ |
802 | Description, PublicPersonChoice, Summary, Title) |
803 | from canonical.launchpad.interfaces.structuralsubscription import ( |
804 | IStructuralSubscriptionTarget) |
805 | -from lp.app.interfaces.rootcontext import IRootContext |
806 | +from lp.app.interfaces.headings import IRootContext |
807 | from lp.registry.interfaces.announcement import IMakesAnnouncements |
808 | from lp.bugs.interfaces.bugtarget import ( |
809 | IBugTarget, IOfficialBugTagTargetPublic, IOfficialBugTagTargetRestricted) |
810 | |
811 | === modified file 'lib/lp/registry/interfaces/person.py' |
812 | --- lib/lp/registry/interfaces/person.py 2009-08-31 22:16:32 +0000 |
813 | +++ lib/lp/registry/interfaces/person.py 2009-09-03 23:06:50 +0000 |
814 | @@ -71,7 +71,7 @@ |
815 | is_valid_public_person) |
816 | from canonical.launchpad.interfaces.account import AccountStatus, IAccount |
817 | from canonical.launchpad.interfaces.emailaddress import IEmailAddress |
818 | -from lp.app.interfaces.rootcontext import IRootContext |
819 | +from lp.app.interfaces.headings import IRootContext |
820 | from lp.code.interfaces.hasbranches import IHasBranches, IHasMergeProposals |
821 | from lp.registry.interfaces.irc import IIrcID |
822 | from lp.registry.interfaces.jabber import IJabberID |
823 | |
824 | === modified file 'lib/lp/registry/interfaces/product.py' |
825 | --- lib/lp/registry/interfaces/product.py 2009-08-28 06:16:06 +0000 |
826 | +++ lib/lp/registry/interfaces/product.py 2009-09-03 23:06:50 +0000 |
827 | @@ -40,7 +40,7 @@ |
828 | PublicPersonChoice, Summary, Title, URIField) |
829 | from canonical.launchpad.interfaces.structuralsubscription import ( |
830 | IStructuralSubscriptionTarget) |
831 | -from lp.app.interfaces.rootcontext import IRootContext |
832 | +from lp.app.interfaces.headings import IRootContext |
833 | from lp.code.interfaces.branchvisibilitypolicy import ( |
834 | IHasBranchVisibilityPolicy) |
835 | from lp.code.interfaces.hasbranches import IHasBranches, IHasMergeProposals |
836 | |
837 | === modified file 'lib/lp/registry/interfaces/project.py' |
838 | --- lib/lp/registry/interfaces/project.py 2009-08-28 06:16:06 +0000 |
839 | +++ lib/lp/registry/interfaces/project.py 2009-09-03 23:06:50 +0000 |
840 | @@ -20,7 +20,7 @@ |
841 | from canonical.launchpad import _ |
842 | from canonical.launchpad.fields import ( |
843 | PublicPersonChoice, Summary, Title, URIField) |
844 | -from lp.app.interfaces.rootcontext import IRootContext |
845 | +from lp.app.interfaces.headings import IRootContext |
846 | from lp.code.interfaces.branchvisibilitypolicy import ( |
847 | IHasBranchVisibilityPolicy) |
848 | from lp.code.interfaces.hasbranches import IHasBranches, IHasMergeProposals |
849 | |
850 | === modified file 'lib/lp/registry/templates/person-karma.pt' |
851 | --- lib/lp/registry/templates/person-karma.pt 2009-07-17 17:59:07 +0000 |
852 | +++ lib/lp/registry/templates/person-karma.pt 2009-09-03 00:38:35 +0000 |
853 | @@ -3,92 +3,73 @@ |
854 | xmlns:tal="http://xml.zope.org/namespaces/tal" |
855 | xmlns:metal="http://xml.zope.org/namespaces/metal" |
856 | xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
857 | - xml:lang="en" |
858 | - lang="en" |
859 | - dir="ltr" |
860 | - metal:use-macro="view/macro:page/onecolumn" |
861 | + metal:use-macro="view/macro:page/main_only" |
862 | i18n:domain="launchpad" |
863 | -> |
864 | -<body> |
865 | - |
866 | -<metal:leftportlets fill-slot="portlets_one"> |
867 | -</metal:leftportlets> |
868 | - |
869 | -<metal:rightportlets fill-slot="portlets_two"> |
870 | -</metal:rightportlets> |
871 | - |
872 | -<div metal:fill-slot="main" |
873 | - tal:define="latestkarma context/latestKarma"> |
874 | - |
875 | - <h1>Launchpad karma</h1> |
876 | - |
877 | - <p> |
878 | - This is a summary of the Launchpad karma earned by |
879 | - <span tal:replace="context/displayname" />, organized by activity |
880 | - type. |
881 | - </p> |
882 | - |
883 | - <p id="no-karma" tal:condition="not: view/has_karma"> |
884 | - <tal:expired condition="view/has_expired_karma"> |
885 | - <tal:name replace="context/displayname" />'s karma has expired. |
886 | - </tal:expired> |
887 | - <tal:never condition="not: view/has_expired_karma"> |
888 | - No karma has yet been assigned to |
889 | - <tal:name replace="context/displayname" />. |
890 | - Karma is updated daily. |
891 | - </tal:never> |
892 | - </p> |
893 | - |
894 | - <table class="listing sortable" id="karmapoints" |
895 | - tal:condition="context/karma_category_caches"> |
896 | - <thead> |
897 | - <tr> |
898 | - <th>Category</th> |
899 | - <th>Points</th> |
900 | - </tr> |
901 | - </thead> |
902 | - <tbody> |
903 | - <tr tal:repeat="cache context/karma_category_caches"> |
904 | - <td tal:content="cache/category/title">Bug Management</td> |
905 | - <td tal:content="cache/karmavalue">10</td> |
906 | - </tr> |
907 | - </tbody> |
908 | - </table> |
909 | - <table> |
910 | - <tr> |
911 | - <td><b>Total:</b></td> |
912 | - <td tal:content="context/karma">34</td> |
913 | - </tr> |
914 | - </table> |
915 | - |
916 | - <tal:latest_events condition="latestkarma"> |
917 | - |
918 | - <h2>Latest actions</h2> |
919 | - |
920 | - <table class="listing sortable" id="karmaactions"> |
921 | - <thead> |
922 | - <tr> |
923 | - <th>Action</th> |
924 | - <th>Date</th> |
925 | - </tr> |
926 | - </thead> |
927 | - <tbody> |
928 | - <tr tal:repeat="karma latestkarma"> |
929 | - <td tal:content="karma/action/title">Bug Filed</td> |
930 | - <td> |
931 | - <span class="sortkey" |
932 | - tal:content="karma/datecreated/fmt:datetime">2005-10-06</span> |
933 | - <span |
934 | - tal:attributes="title karma/datecreated/fmt:datetime" |
935 | - tal:content="karma/datecreated/fmt:approximatedate" /> |
936 | - </td> |
937 | - </tr> |
938 | - </tbody> |
939 | - </table> |
940 | - |
941 | - </tal:latest_events> |
942 | - |
943 | -</div> |
944 | - |
945 | -</body> |
946 | + > |
947 | + <body> |
948 | + <div metal:fill-slot="main" |
949 | + tal:define="latest_karma context/latestKarma"> |
950 | + |
951 | + <p class="application-summary"> |
952 | + This is a summary of the Launchpad karma earned by |
953 | + <span tal:replace="context/displayname" />, organized by activity |
954 | + type. |
955 | + </p> |
956 | + |
957 | + <p id="no-karma" tal:condition="not: view/has_karma"> |
958 | + <tal:expired condition="view/has_expired_karma"> |
959 | + <tal:name replace="context/displayname" />'s karma has expired. |
960 | + </tal:expired> |
961 | + <tal:never condition="not: view/has_expired_karma"> |
962 | + No karma has yet been assigned to |
963 | + <tal:name replace="context/displayname" />. |
964 | + Karma is updated daily. |
965 | + </tal:never> |
966 | + </p> |
967 | + |
968 | + <table class="cozy-listing" id="karmapoints" |
969 | + tal:condition="context/karma_category_caches"> |
970 | + <tbody> |
971 | + <tr tal:repeat="cache context/karma_category_caches"> |
972 | + <td tal:content="cache/category/title">Bug Management</td> |
973 | + <td><strong |
974 | + class="" |
975 | + tal:attributes="class string: ${cache/category/name}-stat" |
976 | + tal:content="cache/karmavalue">10</strong></td> |
977 | + </tr> |
978 | + </tbody> |
979 | + </table> |
980 | + <b>Total karma:</b> |
981 | + <tal:total_karma tal:replace="context/karma">34</tal:total_karma> |
982 | + |
983 | + <tal:latest_events condition="latest_karma"> |
984 | + <p class="application-summary" |
985 | + style="padding-top: 2em;" > |
986 | + These are the latest actions which have contributed to the Launchpad |
987 | + karma earned by <span tal:replace="context/displayname" /> and the |
988 | + dates on which the action occurred. |
989 | + </p> |
990 | + <table class="listing sortable" id="karmaactions"> |
991 | + <thead> |
992 | + <tr> |
993 | + <th>Date</th> |
994 | + <th>Action</th> |
995 | + </tr> |
996 | + </thead> |
997 | + <tbody> |
998 | + <tr tal:repeat="karma latest_karma"> |
999 | + <td> |
1000 | + <span class="sortkey" |
1001 | + tal:content="karma/datecreated/fmt:datetime">2005-10-06</span> |
1002 | + <span |
1003 | + tal:attributes="title karma/datecreated/fmt:datetime" |
1004 | + tal:content="karma/datecreated/fmt:approximatedate" /> |
1005 | + </td> |
1006 | + <td tal:content="karma/action/title">Bug Filed</td> |
1007 | + </tr> |
1008 | + </tbody> |
1009 | + </table> |
1010 | + </tal:latest_events> |
1011 | + </div> |
1012 | + </body> |
1013 | </html> |
1014 | |
1015 | === modified file 'lib/lp/registry/templates/product-index.pt' |
1016 | --- lib/lp/registry/templates/product-index.pt 2009-08-31 21:20:55 +0000 |
1017 | +++ lib/lp/registry/templates/product-index.pt 2009-09-02 20:40:14 +0000 |
1018 | @@ -6,10 +6,6 @@ |
1019 | metal:use-macro="view/macro:page/main_side" |
1020 | i18n:domain="launchpad"> |
1021 | <body> |
1022 | - <tal:heading metal:fill-slot="heading"> |
1023 | - <h1 tal:replace="structure view/title_edit_widget">project title</h1> |
1024 | - </tal:heading> |
1025 | - |
1026 | <tal:main metal:fill-slot="main" |
1027 | define="overview_menu context/menu:overview"> |
1028 | <div class="top-portlet"> |
1029 | |
1030 | === added file 'version.txt' |
1031 | --- version.txt 1970-01-01 00:00:00 +0000 |
1032 | +++ version.txt 2009-09-01 20:24:17 +0000 |
1033 | @@ -0,0 +1,1 @@ |
1034 | +2.2.7 |
reviewer salgado
= Summary =
Bug 417089 addresses our current inconsistencies and difficulties with getting
the headings and page titles correct. After discussion with Martin A, we've
codified the rules here:
https:/ /dev.launchpad. net/VersionThre eDotO/UI/ Conversion# Heading% 20rules
This section contains both the rules for what you should see on a page, and
how to code your views to conform. Both instructions probably could use for
some review as well to make sure I've gotten them right. I intend to do
another pass through these instructions to include actual examples of pages
and templates from our tree so folks can cargo cult into their own pages.
As I mentioned in IRC, the branch is not yet ready to land, but it's close. I
believe all the header/title rules are implemented, and I have also included a
fix for bug 423411, which converts the ~person/+karma page to the new heading
rules. However, ec2 is reporting test failures which I have to fix before I
land the branch. If necessary I may ask for a follow up review.
I also suck because this branch is currently at 1035 lines.
== Proposed fix ==
Update the base-layout.pt template and launchpad-form.pt, as well as all
supporting view code, interfaces, zcml files, and style sheets to support the
new rules. Update some of the tests (more to come). Fix the person +karma
pages to conform.
Add several interfaces used to mark views to tell the base layout code how to
render the headings, either as an H1, an H2, or as an editable widget.
== Pre-implementation notes ==
I had several discussions with Martin about the rules, you Salgado to help me
with a few things regarding breadcrumbs, and a few other people about
specifics with this branch.
== Implementation details ==
In addition to the above, I also:
* Improved the way the Launchpad release number is calculated for the page-title. pt template for rendering the reverse
base-layout template. Now, to update that release number (e.g. 2.2.7) you
just edit the version.txt file in the root of the tree.
* Cleaned up some whitespace
* Added a launchpad-
breadcrumb <title>
* Added additional ZCML to correctly calculate the <title> for some
bug/branch pages that were overriding breadcrumb calculation
* moved the IRootContext interface and consolidated related interfaces
* Refactored the +karma view support into a separate PersonKarmaView class
* Redesigned the +karma page based on Martin's ui suggestions, including
defining a new, narrower table.
* Made the project index page title editable
== Tests ==
This test was fixed to show the new code to show how the view annotations work
for the new header rules, including for editable heading widgets.
% bin/test -vv -t watermark.txt
More tests to come, once they pass ;)
== Demo and Q/A ==
These pages show some interesting things, such as the headers and breadcrumbs
all UI 3.0 -like.
https:/ /launchpad. dev/firefox /launchpad. dev/people/ +newteam /launchpad. dev/~name16/ +karma /code.launchpad .dev/~mark/ firefox/ release- 0.8/+merge/ 1/+review? claim=name16& review_ type=
https:/
https:/
https:/
(you will need to create a merge proposal for the firefox 0.8 branch into the
trunk...