Merge lp:~gary/launchpad/zbuildout into lp:launchpad/db-devel

Proposed by Gary Poster
Status: Merged
Approved by: Francis J. Lacoste
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~gary/launchpad/zbuildout
Merge into: lp:launchpad/db-devel
Diff against target: None lines
To merge this branch: bzr merge lp:~gary/launchpad/zbuildout
Reviewer Review Type Date Requested Status
Francis J. Lacoste (community) Approve
Review via email: mp+10181@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Gary Poster (gary) wrote :
Download full text (32.6 KiB)

This branch changes us to use source distributions for our zope-related
dependencies.

Because we still are using some packages in sourcecode that want to be
egg-based distributions--particularly Twisted--we filter out pkg_resources
warning in _pythonpath and other scripts. This prevents some test failures
that do not want to see any output from running a command.

There are many other changes, of course.

- Many are driven by changes in reprs of various objects--validation errors
  (LaunchpadValidationError) and ClientForm response wrappers
  (httperror_seek_wrapper) in particular.

- Others are for API changes, like in zope.testing
  (lib/canonical/testing/__init__.py and ).

- I commented out the retry tests, as we have discussed.

- Some tests expected zope files to be in the tree, so I had to switch to the
  pkg_resources API, or simply use more proper zcml in some cases.

- There's a case or two of tokens needing to be converted from unicode to
  binary strings before they go into the URL, as with the
  lp:~gary/canonical-identity-provider/zbuildout branch that accompanies this
  one.

- zope.schema set fields no longer support sets.Set, only the built-in set,
  which also resulted in some changes.

- The transaction.begin API now does not expect to encounter an in-progress
  transaction (lib/canonical/launchpad/doc/storm.txt and
  lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt).

- I found a couple of tests that failed in isolation. Related changes are in
  lib/canonical/launchpad/doc/batch_navigation.txt and
  lib/canonical/launchpad/scripts/ftests/test_oops_prune.py .

- I'm not entirely sure how
  lib/lp/translations/stories/standalone/xx-pofile-translate-html-tags-escape.txt
  ever passed before. The doctest syntax there was wrong.

- I switched back to the feedvalidator solution that we had discussed way back
  when, rather than using the hacked version. I left a comment to describe
  the situation.

- Some tests did a browser reload. That now has a different behavior,
  resubmitting forms and so on, so I reload the same URL instead.

- I did a fly-by on lib/lp/soyuz/browser/tests/binarypackagerelease-views.txt
  because that was a [testfix] problem I diagnosed yesterday, and using
  queryMultiAdapter hid the problem.

versions.cfg comments out version numbers only when my changes represent a
*previous* version that what was specified before. Happy to remove those as
well.

For this branch, I made changes and releases in the following packages:

- zope.testing (modified trunk, released and used as zope.testing 3.8.1)
- zope.security (modified trunk, released and used as zope.security 3.7.1)
- zope.app.publication (modified trunk, eventually to be 3.8.2; also modified 3.4.3, released and used as zope.app.publication 3.4.4)

Diffs to the dependencies are below. For zope.testing and zope.security, I
only show the commit that made the substantive changes, rather than the commits
generated by the release process. zope.testing has the biggest changes from
our branch because it significantly changed how it handled subprocesses from
the version we were using in order to support being able to run layers in
...

Revision history for this message
Francis J. Lacoste (flacoste) wrote :
Download full text (5.3 KiB)

On August 14, 2009, Gary Poster wrote:
> Gary Poster has proposed merging lp:~gary/launchpad/zbuildout into
> lp:launchpad.
>
> Requested reviews:
> Francis J. Lacoste (flacoste)
>
> This branch changes us to use source distributions for our zope-related
> dependencies.
>
> Because we still are using some packages in sourcecode that want to be
> egg-based distributions--particularly Twisted--we filter out pkg_resources
> warning in _pythonpath and other scripts. This prevents some test failures
> that do not want to see any output from running a command.
>
> There are many other changes, of course.
>
> - Many are driven by changes in reprs of various objects--validation errors
> (LaunchpadValidationError) and ClientForm response wrappers
> (httperror_seek_wrapper) in particular.
>
> - Others are for API changes, like in zope.testing
> (lib/canonical/testing/__init__.py and ).
>
> - I commented out the retry tests, as we have discussed.
>
> - Some tests expected zope files to be in the tree, so I had to switch to
> the pkg_resources API, or simply use more proper zcml in some cases.
>
> - There's a case or two of tokens needing to be converted from unicode to
> binary strings before they go into the URL, as with the
> lp:~gary/canonical-identity-provider/zbuildout branch that accompanies
> this one.
>
> - zope.schema set fields no longer support sets.Set, only the built-in set,
> which also resulted in some changes.
>
> - The transaction.begin API now does not expect to encounter an in-progress
> transaction (lib/canonical/launchpad/doc/storm.txt and
> lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt).
>
> - I found a couple of tests that failed in isolation. Related changes are
> in lib/canonical/launchpad/doc/batch_navigation.txt and
> lib/canonical/launchpad/scripts/ftests/test_oops_prune.py .
>
> - I'm not entirely sure how
>
> lib/lp/translations/stories/standalone/xx-pofile-translate-html-tags-escape
>.txt ever passed before. The doctest syntax there was wrong.
>
> - I switched back to the feedvalidator solution that we had discussed way
> back when, rather than using the hacked version. I left a comment to
> describe the situation.
>
> - Some tests did a browser reload. That now has a different behavior,
> resubmitting forms and so on, so I reload the same URL instead.
>
> - I did a fly-by on
> lib/lp/soyuz/browser/tests/binarypackagerelease-views.txt because that was
> a [testfix] problem I diagnosed yesterday, and using queryMultiAdapter hid
> the problem.
>
> versions.cfg comments out version numbers only when my changes represent a
> *previous* version that what was specified before. Happy to remove those
> as well.
>
> For this branch, I made changes and releases in the following packages:
>
> - zope.testing (modified trunk, released and used as zope.testing 3.8.1)
> - zope.security (modified trunk, released and used as zope.security 3.7.1)
> - zope.app.publication (modified trunk, eventually to be 3.8.2; also
> modified 3.4.3, released and used as zope.app.publication 3.4.4)
>
> Diffs to the dependencies are below. For zope.testing and zope.security, I
> only show the commit that ...

Read more...

Revision history for this message
Francis J. Lacoste (flacoste) wrote :
Download full text (5.9 KiB)

Hi Gary,

OK, I managed to go through the changes. I have a bunch of trival comment,
nothing that should prevent you from merging this.

Finally! Good work!

  status approved
  review approve

> === modified file 'buildout-templates/_pythonpath.py.in'
> --- buildout-templates/_pythonpath.py.in 2009-06-24 20:22:29 +0000
> +++ buildout-templates/_pythonpath.py.in 2009-08-18 21:42:18 +0000
> @@ -11,3 +11,10 @@
> sys.path[0:0] = [${string-paths}]
> # Enable Storm's C extensions
> os.environ['STORM_CEXTENSIONS'] = '1'
> +
> +# we don't want to bother tests or logs with these.

Capitalization.

> +import warnings
> +warnings.filterwarnings(
> + 'ignore',
> + 'Module .+ was already imported from .+, but .+ is being added.*',
> + UserWarning)
> \ No newline at end of file

Missing newline.

> === modified file 'buildout.cfg'
> --- buildout.cfg 2009-08-05 03:30:57 +0000
> +++ buildout.cfg 2009-08-18 21:42:18 +0000
> @@ -56,6 +56,12 @@
> initialization = import os
> os.environ['STORM_CEXTENSIONS'] = '1'
> os.environ.setdefault('LPCONFIG',
'${configuration:instance_name}')
> + # this can hopefully be removed when Twisted is used as an
egg.

This should be an XXX pointing to a bug about using twisted as an egg. (And
please fix the capitalization of the comment.)

> === modified file 'lib/canonical/config/__init__.py'
> @@ -194,8 +195,10 @@
>
> def _setZConfig(self):
> """Modify the config, adding automatically generated settings"""
> - schemafile = os.path.join(
> - self.root, 'lib/zope/app/server/schema.xml')
> + self.root = TREE_ROOT

Why did you need to add that line back? I mean self.root should already be
defined properly.

> === modified file 'lib/canonical/testing/ftests/test_mockdb.py'

Instead of commenting everything here, why didn't you simply comment out the
line adding the MockDBTestCase to the suite in suite() ? I think this would
basically have the same effect. (With a comment explaining why this is
disabled.

> === modified file 'lib/canonical/testing/tests/test_mockdb.py'

Same kind of comments here.

> === modified file 'setup.py'
> --- setup.py 2009-07-24 11:14:47 +0000
> +++ setup.py 2009-08-18 21:42:18 +0000
> @@ -20,26 +20,78 @@
> maintainer='Launchpad Developers',
> description=('A unique collaboration and Bazaar code hosting platform '
> 'for software projects.'),
> - license='LGPL v3',
> + license='Affero GPL v3',
> + # this list should only contain direct dependencies--things imported or
> + # used in zcml.
> install_requires=[
> 'bzr',
> + 'chameleon.core',
> + 'chameleon.zpt',
> 'feedvalidator',
> 'funkload',
> 'launchpadlib',
> 'lazr.smtptest',
> 'lazr.uri',
> + 'mechanize',

Do we require mechanize? Isn't this an indirect dependency of
zope.testbrowser?

> > 'mocker',
> > 'oauth',
> > 'python-openid',
> 'pytz',
> + # This appears to be a broken indirect dependency from
zope.security:
> + 'RestrictedPython',
> 'setuptools',
> 'sour...

Read more...

review: Approve
Revision history for this message
Gary Poster (gary) wrote :
Download full text (6.2 KiB)

On Aug 18, 2009, at 4:57 PM, Francis J. Lacoste wrote:

> On August 14, 2009, Gary Poster wrote:
>> Gary Poster has proposed merging lp:~gary/launchpad/zbuildout into
>> lp:launchpad.
>>
>> Requested reviews:
>> Francis J. Lacoste (flacoste)
>>
>> This branch changes us to use source distributions for our zope-
>> related
>> dependencies.
>>
>> Because we still are using some packages in sourcecode that want to
>> be
>> egg-based distributions--particularly Twisted--we filter out
>> pkg_resources
>> warning in _pythonpath and other scripts. This prevents some test
>> failures
>> that do not want to see any output from running a command.
>>
>> There are many other changes, of course.
>>
>> - Many are driven by changes in reprs of various objects--
>> validation errors
>> (LaunchpadValidationError) and ClientForm response wrappers
>> (httperror_seek_wrapper) in particular.
>>
>> - Others are for API changes, like in zope.testing
>> (lib/canonical/testing/__init__.py and ).
>>
>> - I commented out the retry tests, as we have discussed.
>>
>> - Some tests expected zope files to be in the tree, so I had to
>> switch to
>> the pkg_resources API, or simply use more proper zcml in some cases.
>>
>> - There's a case or two of tokens needing to be converted from
>> unicode to
>> binary strings before they go into the URL, as with the
>> lp:~gary/canonical-identity-provider/zbuildout branch that
>> accompanies
>> this one.
>>
>> - zope.schema set fields no longer support sets.Set, only the built-
>> in set,
>> which also resulted in some changes.
>>
>> - The transaction.begin API now does not expect to encounter an in-
>> progress
>> transaction (lib/canonical/launchpad/doc/storm.txt and
>> lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt).
>>
>> - I found a couple of tests that failed in isolation. Related
>> changes are
>> in lib/canonical/launchpad/doc/batch_navigation.txt and
>> lib/canonical/launchpad/scripts/ftests/test_oops_prune.py .
>>
>> - I'm not entirely sure how
>>
>> lib/lp/translations/stories/standalone/xx-pofile-translate-html-
>> tags-escape
>> .txt ever passed before. The doctest syntax there was wrong.
>>
>> - I switched back to the feedvalidator solution that we had
>> discussed way
>> back when, rather than using the hacked version. I left a comment to
>> describe the situation.
>>
>> - Some tests did a browser reload. That now has a different
>> behavior,
>> resubmitting forms and so on, so I reload the same URL instead.
>>
>> - I did a fly-by on
>> lib/lp/soyuz/browser/tests/binarypackagerelease-views.txt because
>> that was
>> a [testfix] problem I diagnosed yesterday, and using
>> queryMultiAdapter hid
>> the problem.
>>
>> versions.cfg comments out version numbers only when my changes
>> represent a
>> *previous* version that what was specified before. Happy to remove
>> those
>> as well.
>>
>> For this branch, I made changes and releases in the following
>> packages:
>>
>> - zope.testing (modified trunk, released and used as zope.testing
>> 3.8.1)
>> - zope.security (modified trunk, released and used as zope.security
>> 3.7.1)
>> - zope.app.publication...

Read more...

Revision history for this message
Francis J. Lacoste (flacoste) wrote :

On August 21, 2009, Gary Poster wrote:
> > Why not use sys.stdout.flush() here instead of using a newline?
>
> Because this feeds into the same new code in the parent process that
> now has the readline behavior. I don't think a flush is going to help
> if the listener is doing a readline. Maybe I could change the
> listener behavior for this case so that it no longer does a readline,
> and then I could do a flush here, but just sending a newline seemed
> like a nice one-line fix with no downside that I saw.

That's fine.

--
Francis J. Lacoste
<email address hidden>

Revision history for this message
Gary Poster (gary) wrote :
Download full text (9.3 KiB)

On Aug 18, 2009, at 5:57 PM, Francis J. Lacoste wrote:

> Review: Approve
> Hi Gary,
>
> OK, I managed to go through the changes. I have a bunch of trival
> comment,
> nothing that should prevent you from merging this.
>
> Finally! Good work!

Thanks!

Responses below. I deleted the sections that I simply changed as you
suggested.

...

>> === modified file 'lib/canonical/config/__init__.py'
>> @@ -194,8 +195,10 @@
>>
>> def _setZConfig(self):
>> """Modify the config, adding automatically generated
>> settings"""
>> - schemafile = os.path.join(
>> - self.root, 'lib/zope/app/server/schema.xml')
>> + self.root = TREE_ROOT
>
> Why did you need to add that line back? I mean self.root should
> already be
> defined properly.

I *think* this must be a conflict resolution mistake of mine. I
removed the line again, and ec2test will tell me whether it is OK.

>
>> === modified file 'lib/canonical/testing/ftests/test_mockdb.py'
>
> Instead of commenting everything here, why didn't you simply comment
> out the
> line adding the MockDBTestCase to the suite in suite() ? I think
> this would
> basically have the same effect. (With a comment explaining why this is
> disabled.

The reason why was that there were many tests that were still able to
run. All of the ones that do not rely on the retry stuff still are
registered. Is that not worthwhile for some reason?

>> === modified file 'lib/canonical/testing/tests/test_mockdb.py'
>
> Same kind of comments here.

Likewise. :-)

>> === modified file 'setup.py'
>> --- setup.py 2009-07-24 11:14:47 +0000
>> +++ setup.py 2009-08-18 21:42:18 +0000
>> @@ -20,26 +20,78 @@
>> maintainer='Launchpad Developers',
>> description=('A unique collaboration and Bazaar code hosting
>> platform '
>> 'for software projects.'),
>> - license='LGPL v3',
>> + license='Affero GPL v3',
>> + # this list should only contain direct dependencies--things
>> imported or
>> + # used in zcml.
>> install_requires=[
>> 'bzr',
>> + 'chameleon.core',
>> + 'chameleon.zpt',
>> 'feedvalidator',
>> 'funkload',
>> 'launchpadlib',
>> 'lazr.smtptest',
>> 'lazr.uri',
>> + 'mechanize',
>
> Do we require mechanize? Isn't this an indirect dependency of
> zope.testbrowser?

We have it here because of testbrowser, but we import it directly.

Here and in the other usage lists below I show you the uses I found
quickly today, reviewing your questions. There may be more.

lib/lp/code/browser/tests/test_product.py:11:from mechanize import
LinkNotFoundError
./lib/canonical/launchpad/pagetests/standalone/xx-beta-testers-
redirection.txt:137: >>> from mechanize._clientcookie import Cookie
./lib/lp/translations/stories/standalone/xx-sourcepackage-export.txt:
31: >>> from mechanize import LinkNotFoundError
./lib/lp/soyuz/stories/ppa/xx-ppa-files.txt:127: >>> from mechanize
import LinkNotFoundError

>
>>> 'mocker',
>>> 'oauth',
>>> 'python-openid',
>> 'pytz',
>> + # This appears to be a broken indirect dependency from
> zope.security:
>> + ...

Read more...

Revision history for this message
Francis J. Lacoste (flacoste) wrote :
Download full text (3.4 KiB)

On August 21, 2009, Gary Poster wrote:
> On Aug 18, 2009, at 5:57 PM, Francis J. Lacoste wrote:

>
> >> === modified file 'lib/canonical/testing/ftests/test_mockdb.py'
> >
> > Instead of commenting everything here, why didn't you simply comment
> > out the
> > line adding the MockDBTestCase to the suite in suite() ? I think
> > this would
> > basically have the same effect. (With a comment explaining why this is
> > disabled.
>
> The reason why was that there were many tests that were still able to
> run. All of the ones that do not rely on the retry stuff still are
> registered. Is that not worthwhile for some reason?

It is. So that's fine.

>
> >> === modified file 'setup.py'
> >> --- setup.py 2009-07-24 11:14:47 +0000
> >> +++ setup.py 2009-08-18 21:42:18 +0000
> >> @@ -20,26 +20,78 @@
> >> maintainer='Launchpad Developers',
> >> description=('A unique collaboration and Bazaar code hosting
> >> platform '
> >> 'for software projects.'),
> >> - license='LGPL v3',
> >> + license='Affero GPL v3',
> >> + # this list should only contain direct dependencies--things
> >> imported or
> >> + # used in zcml.
> >> install_requires=[
> >> 'bzr',
> >> + 'chameleon.core',
> >> + 'chameleon.zpt',
> >> 'feedvalidator',
> >> 'funkload',
> >> 'launchpadlib',
> >> 'lazr.smtptest',
> >> 'lazr.uri',
> >> + 'mechanize',
> >
> > Do we require mechanize? Isn't this an indirect dependency of
> > zope.testbrowser?
>
> We have it here because of testbrowser, but we import it directly.
>
> Here and in the other usage lists below I show you the uses I found
> quickly today, reviewing your questions. There may be more.
>
> lib/lp/code/browser/tests/test_product.py:11:from mechanize import
> LinkNotFoundError
> ./lib/canonical/launchpad/pagetests/standalone/xx-beta-testers-
> redirection.txt:137: >>> from mechanize._clientcookie import Cookie
> ./lib/lp/translations/stories/standalone/xx-sourcepackage-export.txt:
> 31: >>> from mechanize import LinkNotFoundError
> ./lib/lp/soyuz/stories/ppa/xx-ppa-files.txt:127: >>> from mechanize
> import LinkNotFoundError
>
> >>> 'mocker',
> >>> 'oauth',
> >>> 'python-openid',
> >>
> >> 'pytz',
> >> + # This appears to be a broken indirect dependency from
> >
> > zope.security:
> >> + 'RestrictedPython',
> >> 'setuptools',
> >> 'sourcecodegen',
> >> 'storm',
> >> - 'chameleon.core',
> >> - 'chameleon.zpt',
> >> + 'transaction',
> >> + 'wadllib',
> >> 'z3c.pt',
> >> 'z3c.ptcompat',
> >> - 'wadllib',
> >> + 'zc.zservertracelog',
> >> + 'zope.app.appsetup',
> >> + 'zope.app.component',
> >> + 'zope.app.dav', # ./package-includes/dav-configure.zcml
> >
> > Really!?!
>
> Yeah, see that dav-configure.zcml thing. That's why the comment is
> there. Do you want me to try removing that file and then removing the
> dependency?
>
> Unless you are sure we can get rid of that zcml file, I think I'd
> prefer to make a bug for this, asking to investigate, and just leave
> things as...

Read more...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'buildout-templates/_pythonpath.py.in'
2--- buildout-templates/_pythonpath.py.in 2009-06-24 20:22:29 +0000
3+++ buildout-templates/_pythonpath.py.in 2009-08-07 13:16:30 +0000
4@@ -11,3 +11,10 @@
5 sys.path[0:0] = [${string-paths}]
6 # Enable Storm's C extensions
7 os.environ['STORM_CEXTENSIONS'] = '1'
8+
9+# we don't want to bother tests or logs with these.
10+import warnings
11+warnings.filterwarnings(
12+ 'ignore',
13+ 'Module .+ was already imported from .+, but .+ is being added.*',
14+ UserWarning)
15\ No newline at end of file
16
17=== modified file 'buildout-templates/bin/test.in'
18--- buildout-templates/bin/test.in 2009-07-30 13:26:13 +0000
19+++ buildout-templates/bin/test.in 2009-08-10 22:08:05 +0000
20@@ -132,6 +132,7 @@
21 pgsql.installFakeConnect()
22
23 from zope.testing import testrunner
24+from zope.testing.testrunner import options
25
26 defaults = [
27 # Find tests in the tests and ftests directories
28@@ -188,24 +189,24 @@
29
30 def load_list(option, opt_str, list_name, parser):
31 patch_find_tests(filter_tests(list_name))
32- testrunner.parser.add_option(
33+ options.parser.add_option(
34 '--load-list', type=str, action='callback', callback=load_list)
35
36 def list_test_option(option, opt, value, parser):
37 patch_find_tests(list_tests)
38- testrunner.parser.add_option(
39+ options.parser.add_option(
40 '--list', action='callback', callback=list_test_option)
41
42 def use_subunit(option, opt, value, parser):
43 patch_zope_testresult(TestProtocolClient(sys.stdout))
44- testrunner.parser.add_option(
45+ options.parser.add_option(
46 '--subunit', action='callback', callback=use_subunit)
47
48- options = testrunner.get_options(args=args, defaults=defaults)
49+ local_options = options.get_options(args=args, defaults=defaults)
50
51 # Turn on Layer profiling if requested.
52 from canonical.testing import profiled
53- if options.verbose >= 3 and main_process:
54+ if local_options.verbose >= 3 and main_process:
55 profiled.setup_profiling()
56
57 # The working directory change is just so that the test script
58@@ -222,6 +223,6 @@
59 logging.disable(999999999)
60
61 # Print Layer profiling report if requested.
62- if main_process and options.verbose >= 3:
63+ if main_process and local_options.verbose >= 3:
64 profiled.report_profile_stats()
65 sys.exit(result)
66
67=== modified file 'buildout.cfg'
68--- buildout.cfg 2009-08-05 03:30:57 +0000
69+++ buildout.cfg 2009-08-05 19:05:57 +0000
70@@ -56,6 +56,12 @@
71 initialization = import os
72 os.environ['STORM_CEXTENSIONS'] = '1'
73 os.environ.setdefault('LPCONFIG', '${configuration:instance_name}')
74+ # this can hopefully be removed when Twisted is used as an egg.
75+ import warnings
76+ warnings.filterwarnings(
77+ 'ignore',
78+ 'Module .+ was already imported from .+, but .+ is being added.*',
79+ UserWarning)
80 entry-points = stxdocs=zope.configuration.stxdocs:main
81 googletestservice=canonical.launchpad.testing.googletestservice:main
82 windmill=windmill.bin.windmill_bin:main
83
84=== removed symlink 'lib/BTrees'
85=== target was u'../sourcecode/zope/src/BTrees/'
86=== removed symlink 'lib/ClientForm'
87=== target was u'../sourcecode/zope/src/ClientForm'
88=== removed symlink 'lib/RestrictedPython'
89=== target was u'../sourcecode/zope/src/RestrictedPython'
90=== removed symlink 'lib/ZConfig'
91=== target was u'../sourcecode/zope/src/ZConfig/'
92=== removed symlink 'lib/ZODB'
93=== target was u'../sourcecode/zope/src/ZODB/'
94=== modified file 'lib/canonical/config/__init__.py'
95--- lib/canonical/config/__init__.py 2009-07-19 04:41:14 +0000
96+++ lib/canonical/config/__init__.py 2009-08-05 18:52:52 +0000
97@@ -16,6 +16,7 @@
98 import sys
99 from urlparse import urlparse, urlunparse
100
101+import pkg_resources
102 import ZConfig
103
104 from lazr.config import ImplicitTypeSchema
105@@ -194,8 +195,10 @@
106
107 def _setZConfig(self):
108 """Modify the config, adding automatically generated settings"""
109- schemafile = os.path.join(
110- self.root, 'lib/zope/app/server/schema.xml')
111+ self.root = TREE_ROOT
112+
113+ schemafile = pkg_resources.resource_filename(
114+ 'zope.app.server', 'schema.xml')
115 schema = ZConfig.loadSchema(schemafile)
116 root_options, handlers = ZConfig.loadConfig(
117 schema, self.zope_config_file)
118
119=== modified file 'lib/canonical/config/tests/test_config.py'
120--- lib/canonical/config/tests/test_config.py 2009-06-25 05:30:52 +0000
121+++ lib/canonical/config/tests/test_config.py 2009-08-05 18:52:52 +0000
122@@ -11,6 +11,7 @@
123
124 import ZConfig
125 import os
126+import pkg_resources
127 import unittest
128
129 from zope.testing.doctest import DocTestSuite, NORMALIZE_WHITESPACE, ELLIPSIS
130@@ -20,11 +21,10 @@
131
132 # Calculate some landmark paths.
133 import canonical.config
134+schema_file = pkg_resources.resource_filename('zope.app.server', 'schema.xml')
135+schema = ZConfig.loadSchema(schema_file)
136+
137 here = os.path.dirname(canonical.config.__file__)
138-schema_file = os.path.join(
139- here, os.pardir, os.pardir, 'zope/app/server/schema.xml')
140-schema = ZConfig.loadSchema(schema_file)
141-
142 lazr_schema_file = os.path.join(here, 'schema-lazr.conf')
143
144
145
146=== modified file 'lib/canonical/configure.zcml'
147--- lib/canonical/configure.zcml 2009-07-23 13:44:13 +0000
148+++ lib/canonical/configure.zcml 2009-08-13 15:22:00 +0000
149@@ -83,7 +83,7 @@
150 virtual host layers. The directives come in pairs.
151 The separate registration for the resources namespace (@@) is needed
152 because otherwise the lookup for /@@/ will fail because the
153- layer-specifif defaultView directive also registers the
154+ layer-specific defaultView directive also registers the
155 default view name as an unnamed adapter.
156 -->
157 -->
158
159=== modified file 'lib/canonical/launchpad/browser/tests/registration.py'
160--- lib/canonical/launchpad/browser/tests/registration.py 2009-06-25 05:30:52 +0000
161+++ lib/canonical/launchpad/browser/tests/registration.py 2009-08-05 18:52:52 +0000
162@@ -29,7 +29,7 @@
163
164 Return the Browser object after the registration is finished.
165 """
166- token_url = canonical_url(token)
167+ token_url = canonical_url(token).encode('utf8')
168 logout()
169 browser = setupBrowser()
170 browser.open(token_url)
171
172=== modified file 'lib/canonical/launchpad/doc/announcement-date-widget.txt'
173--- lib/canonical/launchpad/doc/announcement-date-widget.txt 2009-02-28 03:36:29 +0000
174+++ lib/canonical/launchpad/doc/announcement-date-widget.txt 2009-08-13 19:36:01 +0000
175@@ -59,7 +59,9 @@
176 Traceback (most recent call last):
177 ...
178 WidgetInputError: ('field.foo', u'Foo',
179- Please do not provide a date if you want to publish immediately.)
180+ LaunchpadValidationError(u'Please do not provide a date
181+ if you want to publish
182+ immediately.'))
183
184 If you choose to publish at a specific date in the future, the date field
185 must be filled.
186@@ -70,4 +72,4 @@
187 Traceback (most recent call last):
188 ...
189 WidgetInputError: ('field.foo', u'Foo',
190- Please provide a publication date.)
191+ LaunchpadValidationError(u'Please provide a publication date.'))
192
193=== modified file 'lib/canonical/launchpad/doc/batch_navigation.txt'
194--- lib/canonical/launchpad/doc/batch_navigation.txt 2009-03-24 12:43:49 +0000
195+++ lib/canonical/launchpad/doc/batch_navigation.txt 2009-08-06 11:02:58 +0000
196@@ -49,10 +49,13 @@
197 This section demonstrates that batching generates sensible SQL queries when used
198 with SQLObject, i.e. that it puts appropriate LIMIT clauses on queries.
199
200-Imports:
201+Imports and initialization:
202
203 >>> from canonical.launchpad.database import EmailAddress
204 >>> from canonical.ftests.pgsql import CursorWrapper
205+ >>> from canonical.launchpad.interfaces import IStore
206+ >>> ignore = IStore(EmailAddress) # Prime the database connection.
207+ ... # (Priming is important if this test is run in isolation).
208 >>> CursorWrapper.record_sql = True
209
210 Prepare a query, and create a batch of the results:
211
212=== modified file 'lib/canonical/launchpad/doc/checkbox-matrix-widget.txt'
213--- lib/canonical/launchpad/doc/checkbox-matrix-widget.txt 2008-09-22 22:09:40 +0000
214+++ lib/canonical/launchpad/doc/checkbox-matrix-widget.txt 2009-08-13 19:36:01 +0000
215@@ -24,16 +24,16 @@
216 >>> matrix_widget._getFormValue()
217 []
218
219-If we pass in a value via the request, we'll be able to get the licenses
220+If we pass in a value via the request, we'll be able to get the licenses
221 as a set from _getFormValue() or getInputValue():
222
223 >>> request = LaunchpadTestRequest(
224 ... form={'field.licenses': ['GNU_LGPL_V2_1', 'GNU_GPL_V2']})
225 >>> matrix_widget = CheckBoxMatrixWidget(licenses_field, vtype, request)
226 >>> matrix_widget._getFormValue()
227- Set([<...License.GNU_GPL_V2...>, <...License.GNU_LGPL_V2_1...>])
228+ set([<...License.GNU_GPL_V2...>, <...License.GNU_LGPL_V2_1...>])
229 >>> matrix_widget.getInputValue()
230- Set([<...License.GNU_GPL_V2...>, <...License.GNU_LGPL_V2_1...>])
231+ set([<...License.GNU_GPL_V2...>, <...License.GNU_LGPL_V2_1...>])
232
233 It should render as many rows as are specified by the column_count attribute.
234
235
236=== modified file 'lib/canonical/launchpad/doc/feeds.txt'
237--- lib/canonical/launchpad/doc/feeds.txt 2009-08-05 14:48:06 +0000
238+++ lib/canonical/launchpad/doc/feeds.txt 2009-08-05 18:52:52 +0000
239@@ -26,8 +26,8 @@
240 >>> from zope.configuration import xmlconfig
241 >>> zcmlcontext = xmlconfig.string("""
242 ... <configure xmlns:browser="http://namespaces.zope.org/browser">
243- ... <include file="lib/canonical/launchpad/webapp/meta.zcml" />
244- ... <include file="lib/zope/app/zcmlfiles/meta.zcml" />
245+ ... <include package="canonical.launchpad.webapp" file="meta.zcml" />
246+ ... <include package="zope.app.zcmlfiles" file="meta.zcml" />
247 ... <browser:feeds
248 ... module="canonical.launchpad.ftests.feeds_helper"
249 ... classes="ThingFeedView"
250
251=== modified file 'lib/canonical/launchpad/doc/image-widget.txt'
252--- lib/canonical/launchpad/doc/image-widget.txt 2008-06-04 07:51:28 +0000
253+++ lib/canonical/launchpad/doc/image-widget.txt 2009-08-13 19:36:01 +0000
254@@ -2,8 +2,8 @@
255
256 In Launchpad we have images associated with people, products, distributions,
257 etc, and we want to allow people to have full control over their images. That
258-is, they must be able to upload a new image and delete (or keep) an existing
259-one. For this we created this widget, which can be embeded into any form we
260+is, they must be able to upload a new image and delete (or keep) an existing
261+one. For this we created this widget, which can be embeded into any form we
262 want, which doesn't require us to add any submit buttons to indicate that the
263 image should be kept, deleted or changed.
264
265@@ -170,7 +170,7 @@
266 #=== The LogoTiedWithMugshotWidget ===
267 #
268 #This is a specialized version of the ImageChangeWidget which also returns a
269-#resized version of the uploaded image, in case one was uploaded.
270+#resized version of the uploaded image, in case one was uploaded.
271 #
272 # >>> from canonical.widgets.image import LogoTiedWithMugshotWidget
273 # >>> mugshot_file_name = os.path.join(
274@@ -385,7 +385,8 @@
275 Traceback (most recent call last):
276 ...
277 WidgetInputError: ('field.mugshot', u'Mugshot',
278- This image is not exactly 192x192 pixels in size.)
279+ LaunchpadValidationError(u'\nThis image is not exactly
280+ 192x192\npixels in size.'))
281
282
283 This is what we see when the image is the correct dimensions, and within the
284@@ -429,7 +430,9 @@
285 Traceback (most recent call last):
286 ...
287 WidgetInputError: ('field.mugshot', u'Mugshot',
288- This image exceeds the maximum allowed size in bytes.)
289+ LaunchpadValidationError(u'\nThis image exceeds the
290+ maximum allowed size in
291+ bytes.'))
292
293 A similar error will be raised if the image's dimensions are bigger than the
294 maximum we allow.
295@@ -443,7 +446,8 @@
296 Traceback (most recent call last):
297 ...
298 WidgetInputError: ('field.mugshot', u'Mugshot',
299- This image is not exactly 191x193 pixels in size.)
300+ LaunchpadValidationError(u'\nThis image is not exactly
301+ 191x193\npixels in size.'))
302
303 >>> person_mugshot.dimensions = (image.size[0] + 1, image.size[1] - 1)
304 >>> mugshot.seek(0)
305@@ -453,7 +457,8 @@
306 Traceback (most recent call last):
307 ...
308 WidgetInputError: ('field.mugshot', u'Mugshot',
309- This image is not exactly 193x191 pixels in size.)
310+ LaunchpadValidationError(u'\nThis image is not exactly
311+ 193x191\npixels in size.'))
312
313 We also won't accept anything that is not an image; that is, we only accept
314 what can be parsed by the Python Imaging Library module.
315@@ -468,8 +473,10 @@
316 Traceback (most recent call last):
317 ...
318 WidgetInputError: ('field.mugshot', u'Mugshot',
319- The file uploaded was not recognized as an image;
320- please check it and retry.)
321+ LaunchpadValidationError(u'\nThe file uploaded was not
322+ recognized as an image;
323+ please\ncheck it and
324+ retry.'))
325
326 Finally, if the user specifies the 'change' action he must also provide a file
327 to be uploaded.
328@@ -481,7 +488,8 @@
329 Traceback (most recent call last):
330 ...
331 WidgetInputError: ('field.mugshot', u'Mugshot',
332- Please specify the image you want to use.)
333+ LaunchpadValidationError(u'Please specify the image you
334+ want to use.'))
335
336
337 == Non-exact Image Dimensions ==
338@@ -503,7 +511,8 @@
339 Traceback (most recent call last):
340 ...
341 WidgetInputError: ('field.mugshot', u'Mugshot',
342- This image is larger than 64x64 pixels in size.)
343+ LaunchpadValidationError(u'\nThis image is larger than
344+ 64x64\npixels in size.'))
345
346 If the image is smaller than the dimensions, the input validates:
347
348@@ -524,4 +533,3 @@
349 >>> fileupload = widget.getInputValue()
350 >>> fileupload.filename
351 u'mugshot.png'
352-
353
354=== modified file 'lib/canonical/launchpad/doc/launchpadlib.txt'
355--- lib/canonical/launchpad/doc/launchpadlib.txt 2008-05-22 19:11:05 +0000
356+++ lib/canonical/launchpad/doc/launchpadlib.txt 2009-06-11 01:28:55 +0000
357@@ -11,7 +11,7 @@
358 >>> browser.open('http://launchpad.dev:8085/~stimpy')
359 Traceback (most recent call last):
360 ...
361- HTTPError: HTTP Error 404: Not Found
362+ httperror_seek_wrapper: HTTP Error 404: Not Found
363
364 ...and when he doesn't, create him.
365
366
367=== modified file 'lib/canonical/launchpad/doc/loginstatus-pages.txt'
368--- lib/canonical/launchpad/doc/loginstatus-pages.txt 2009-04-17 10:32:16 +0000
369+++ lib/canonical/launchpad/doc/loginstatus-pages.txt 2009-08-13 13:33:26 +0000
370@@ -112,7 +112,7 @@
371
372 >>> print view()
373 <...
374- ...<a href="http://launchpad.dev/~name16"...>...
375+ ...<a href="/~name16"...>...
376
377 The link is for the user's Profile page in the 'overview' facet, even if
378 we are viewing the 'bugs' facet.
379
380=== modified file 'lib/canonical/launchpad/doc/navigation.txt'
381--- lib/canonical/launchpad/doc/navigation.txt 2008-09-30 22:25:04 +0000
382+++ lib/canonical/launchpad/doc/navigation.txt 2009-06-11 01:28:55 +0000
383@@ -249,8 +249,8 @@
384 >>> from zope.configuration import xmlconfig
385 >>> zcmlcontext = xmlconfig.string("""
386 ... <configure xmlns:browser="http://namespaces.zope.org/browser">
387- ... <include file="lib/canonical/launchpad/webapp/meta.zcml" />
388- ... <include file="lib/zope/app/zcmlfiles/meta.zcml" />
389+ ... <include package="canonical.launchpad.webapp" file="meta.zcml" />
390+ ... <include package="zope.app.zcmlfiles" file="meta.zcml" />
391 ... <browser:navigation
392 ... module="canonical.launchpad.ftests"
393 ... classes="ThingSetNavigation"
394
395=== modified file 'lib/canonical/launchpad/doc/renamed-view.txt'
396--- lib/canonical/launchpad/doc/renamed-view.txt 2008-09-30 22:25:04 +0000
397+++ lib/canonical/launchpad/doc/renamed-view.txt 2009-06-11 01:28:55 +0000
398@@ -100,8 +100,8 @@
399 >>> from zope.configuration import xmlconfig
400 >>> zcmlcontext = xmlconfig.string("""
401 ... <configure xmlns:browser="http://namespaces.zope.org/browser">
402- ... <include file="lib/canonical/launchpad/webapp/meta.zcml" />
403- ... <include file="lib/zope/app/zcmlfiles/meta.zcml" />
404+ ... <include package="canonical.launchpad.webapp" file="meta.zcml" />
405+ ... <include package="zope.app.zcmlfiles" file="meta.zcml" />
406 ... <browser:renamed-page
407 ... for="canonical.launchpad.interfaces.IQuestionTarget"
408 ... name="+old_tickets_page"
409
410=== modified file 'lib/canonical/launchpad/doc/storm.txt'
411--- lib/canonical/launchpad/doc/storm.txt 2009-07-24 12:55:03 +0000
412+++ lib/canonical/launchpad/doc/storm.txt 2009-08-05 18:52:52 +0000
413@@ -83,6 +83,7 @@
414 ...
415 InternalError: transaction is read-only
416
417+ >>> transaction.abort()
418 >>> t = transaction.begin()
419 >>> account = auth_slave.find(
420 ... Account, openid_identifier='sabdfl_oid').one()
421
422=== modified file 'lib/canonical/launchpad/doc/stripped-text-widget.txt'
423--- lib/canonical/launchpad/doc/stripped-text-widget.txt 2007-06-17 11:57:02 +0000
424+++ lib/canonical/launchpad/doc/stripped-text-widget.txt 2009-08-13 19:36:01 +0000
425@@ -38,4 +38,4 @@
426 >>> widget.getInputValue()
427 Traceback (most recent call last):
428 ...
429- WidgetInputError: ('field', u'Title', )
430+ WidgetInputError: ('field', u'Title', RequiredMissing())
431
432=== modified file 'lib/canonical/launchpad/doc/tales.txt'
433--- lib/canonical/launchpad/doc/tales.txt 2009-08-11 07:20:12 +0000
434+++ lib/canonical/launchpad/doc/tales.txt 2009-08-13 21:32:59 +0000
435@@ -268,11 +268,11 @@
436 >>> print test_tales("bug/fmt:url/+text", bug=bug)
437 http://bugs.launchpad.dev/bugs/1/+text
438
439- # Undo the custom setup we did above.
440-
441- >>> login(ANONYMOUS)
442-
443-Persons and teams can also specify an alternate rootsite to fmt:url.
444+
445+fmt:url accepts an rootsite extension to make URLs to a specific application.
446+
447+ >>> login(ANONYMOUS,
448+ ... LaunchpadTestRequest(SERVER_URL='http://code.launchpad.net'))
449
450 >>> print test_tales("person/fmt:url:bugs", person=sabdfl)
451 http://bugs.launchpad.dev/~sabdfl
452@@ -280,6 +280,14 @@
453 >>> print test_tales("person/fmt:url:feeds", person=sabdfl)
454 http://feeds.launchpad.dev/~sabdfl
455
456+ >>> print test_tales("pillar/fmt:url:answers", pillar=ubuntu)
457+ http://answers.launchpad.dev/ubuntu
458+
459+ >>> print test_tales("bug/fmt:url:mainsite", bug=bug)
460+ http://launchpad.dev/bugs/1
461+
462+ >>> login(ANONYMOUS)
463+
464
465 The fmt: namespace to get links
466 -------------------------------
467@@ -319,6 +327,34 @@
468 >>> test_tales("person/fmt:link", person=ubuntu_team)
469 u'<a href=".../~ubuntu-team" class="sprite team">Ubuntu Team</a>'
470
471+The link can make the URL go to a specific app.
472+
473+ >>> login(ANONYMOUS,
474+ ... LaunchpadTestRequest(SERVER_URL='http://code.launchpad.net'))
475+
476+ >>> print test_tales("pillar/fmt:link:translations", pillar=ubuntu)
477+ <a ...http://translations.launchpad.dev/ubuntu...
478+
479+ >>> print test_tales("person/fmt:url:feeds", person=sabdfl)
480+ http://feeds.launchpad.dev/~sabdfl
481+
482+ >>> print test_tales("bug/fmt:url:mainsite", bug=bug)
483+ http://launchpad.dev/bugs/1
484+
485+The default behaviour for pillars, persons, and teams is to link to
486+the mainsite.
487+
488+ >>> print test_tales("pillar/fmt:link", pillar=ubuntu)
489+ <a ...http://launchpad.dev/ubuntu...
490+
491+ >>> print test_tales("person/fmt:link", person=sabdfl)
492+ <a ...http://launchpad.dev/~sabdfl...
493+
494+ >>> print test_tales("team/fmt:link", team=ubuntu_team)
495+ <a ...http://launchpad.dev/~ubuntu-team...
496+
497+ >>> login(ANONYMOUS)
498+
499 The person's displayname is escaped to prevent markup from being
500 interpreted by the browser. For example, a script added to Sample
501 Person's displayname will be escaped; averting a XSS vulnerability.
502
503=== modified file 'lib/canonical/launchpad/ftests/feeds_helper.py'
504--- lib/canonical/launchpad/ftests/feeds_helper.py 2009-06-25 05:30:52 +0000
505+++ lib/canonical/launchpad/ftests/feeds_helper.py 2009-08-05 18:52:52 +0000
506@@ -15,9 +15,13 @@
507 ]
508
509
510+import socket
511+original_timeout = socket.getdefaulttimeout()
512 import feedvalidator
513+if socket.getdefaulttimeout() != original_timeout:
514+ # feedvalidator's __init__ uses setdefaulttimeout promiscuously
515+ socket.setdefaulttimeout(original_timeout)
516 from cStringIO import StringIO
517-import socket
518 from textwrap import wrap
519
520 from zope.interface import implements, Interface, Attribute
521
522=== modified file 'lib/canonical/launchpad/icing/style-3-0.css'
523--- lib/canonical/launchpad/icing/style-3-0.css 2009-08-07 14:01:57 +0000
524+++ lib/canonical/launchpad/icing/style-3-0.css 2009-08-13 02:24:56 +0000
525@@ -179,7 +179,16 @@
526 #colophon {
527 float: none;
528 }
529+#globalsearch {
530+ float: none;
531+ display: auto;
532+ font-weight: inherit;
533+ margin-right: auto;
534+}
535
536+#globalsearch input {
537+ font-size: inherit;
538+}
539
540 /* Page layout */
541 .yui-d0 {
542@@ -195,9 +204,25 @@
543 .footer {
544 clear: both;
545 margin-top: 2em;
546- border-top: 1px solid #cdd3dd;
547 padding-top: 0.5em;
548 }
549+.footer .lp-arcana {
550+ background: url(/@@/footer-background.png) top left repeat-x;
551+ padding: 0.8em;
552+ -moz-border-radius: 3px 3px 0 0;
553+ -webkit-border-radius: 3px 3px 0 0;
554+ border-radius: 3px 3px 0 0;
555+ }
556+.footer .lp-arcana img {
557+ vertical-align: middle;
558+ }
559+.footer .lp-arcana .search-icon {
560+ background: url(icon-sprites) 100% -191px no-repeat;
561+ }
562+.footer .colophon {
563+ margin: 3em 3em 1em 3em;
564+ text-align: center;
565+ }
566 .portlet, .aside {
567 clear: both;
568 border-top: 1px solid #d6d6d6;
569@@ -424,7 +449,7 @@
570 /* The text of the first tab should align with the heading directly
571 above it */
572 .location-portlet li:first-child a, .location-portlet li:first-child span {
573- margin-left: -0.5em;
574+ margin-left: -0.5em;
575 }
576 .location-portlet li a:link, .location-portlet li a:visited {
577 color: #000;
578
579=== modified file 'lib/canonical/launchpad/icing/style.css'
580--- lib/canonical/launchpad/icing/style.css 2009-08-05 08:15:28 +0000
581+++ lib/canonical/launchpad/icing/style.css 2009-08-12 21:08:05 +0000
582@@ -71,6 +71,7 @@
583 .info {background:url(icon-sprites) 0 -96px no-repeat;}
584 .question {background:url(icon-sprites) 0 -128px no-repeat;}
585 .download-icon {background:url(icon-sprites) 0 -160px no-repeat;}
586+.download {background:url(icon-sprites) 0 -160px no-repeat;}
587 .search-icon {background:url(icon-sprites) 0 -187px no-repeat;}
588 .no {background:url(icon-sprites) 0 -224px no-repeat;}
589 .yes {background:url(icon-sprites) 0 -256px no-repeat;}
590
591=== added file 'lib/canonical/launchpad/images/footer-background.png'
592Binary files lib/canonical/launchpad/images/footer-background.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/footer-background.png 2009-08-13 01:01:39 +0000 differ
593=== modified file 'lib/canonical/launchpad/pagetests/basics/max-batch-size.txt'
594--- lib/canonical/launchpad/pagetests/basics/max-batch-size.txt 2009-07-17 14:00:36 +0000
595+++ lib/canonical/launchpad/pagetests/basics/max-batch-size.txt 2009-08-05 18:52:52 +0000
596@@ -10,7 +10,7 @@
597 >>> anon_browser.open('http://launchpad.dev/projects/+all?start=0&batch=1000')
598 Traceback (most recent call last):
599 ...
600- HTTPError: HTTP Error 400: Bad Request
601+ httperror_seek_wrapper: HTTP Error 400: Bad Request
602
603 >>> print extract_text(find_main_content(anon_browser.contents))
604 Invalid Batch Size
605
606=== modified file 'lib/canonical/launchpad/pagetests/feeds/xx-authentication.txt'
607--- lib/canonical/launchpad/pagetests/feeds/xx-authentication.txt 2008-01-31 23:21:01 +0000
608+++ lib/canonical/launchpad/pagetests/feeds/xx-authentication.txt 2009-07-16 13:28:25 +0000
609@@ -23,4 +23,4 @@
610 >>> browser.open('http://feeds.launchpad.dev/bugs/14/bug.atom')
611 Traceback (most recent call last):
612 ...
613- HTTPError: HTTP Error 403: Forbidden
614+ httperror_seek_wrapper: HTTP Error 403: Forbidden
615
616=== modified file 'lib/canonical/launchpad/pagetests/oauth/access-token.txt'
617--- lib/canonical/launchpad/pagetests/oauth/access-token.txt 2008-07-22 20:34:14 +0000
618+++ lib/canonical/launchpad/pagetests/oauth/access-token.txt 2009-07-16 13:28:25 +0000
619@@ -40,7 +40,7 @@
620 ... 'http://launchpad.dev/+access-token', data=urlencode(data))
621 Traceback (most recent call last):
622 ...
623- HTTPError: HTTP Error 401: Unauthorized
624+ httperror_seek_wrapper: HTTP Error 401: Unauthorized
625
626 The token's context, when not None, is sent to the consumer together
627 with the token's key and secret.
628
629=== modified file 'lib/canonical/launchpad/pagetests/standalone/xx-beta-testers-redirection.txt'
630--- lib/canonical/launchpad/pagetests/standalone/xx-beta-testers-redirection.txt 2009-05-12 16:29:13 +0000
631+++ lib/canonical/launchpad/pagetests/standalone/xx-beta-testers-redirection.txt 2009-08-06 11:35:34 +0000
632@@ -27,7 +27,7 @@
633 ... function(*args, **kwargs)
634 ... except HTTPError, exc:
635 ... print str(exc)
636- ... location = exc.headers.getheader('Location')
637+ ... location = exc.hdrs.getheader('Location')
638 ... if location is not None:
639 ... print 'Location:', location
640
641
642=== modified file 'lib/canonical/launchpad/scripts/ftests/test_oops_prune.py'
643--- lib/canonical/launchpad/scripts/ftests/test_oops_prune.py 2009-06-25 05:30:52 +0000
644+++ lib/canonical/launchpad/scripts/ftests/test_oops_prune.py 2009-08-07 12:46:37 +0000
645@@ -58,9 +58,12 @@
646 INSERT INTO MessageChunk(message, sequence, content)
647 VALUES (1, 99, 'OOPS-%s')
648 """ % self.referenced_oops_code)
649+ if not os.path.exists(config.error_reports.error_dir):
650+ os.mkdir(config.error_reports.error_dir)
651
652 def tearDown(self):
653 shutil.rmtree(self.oops_dir)
654+ shutil.rmtree(config.error_reports.error_dir)
655
656 def test_referenced_oops(self):
657 self.failUnlessEqual(
658
659=== modified file 'lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt'
660--- lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt 2009-04-02 16:39:20 +0000
661+++ lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt 2009-06-11 01:28:55 +0000
662@@ -28,6 +28,7 @@
663
664 Test this once more to ensure the settings stick across transactions.
665
666+ >>> transaction.abort()
667 >>> t = transaction.begin()
668 >>> main_slave.find(Person, name='janitor').one().displayname = 'BenD'
669 >>> transaction.commit()
670
671=== modified file 'lib/canonical/launchpad/webapp/tales.py'
672--- lib/canonical/launchpad/webapp/tales.py 2009-08-13 00:51:50 +0000
673+++ lib/canonical/launchpad/webapp/tales.py 2009-08-14 12:56:19 +0000
674@@ -458,7 +458,25 @@
675 return self.url()
676
677 def traverse(self, name, furtherPath):
678- if name in self.traversable_names:
679+ if name.startswith('link:') or name.startswith('url:'):
680+ rootsite = name.split(':')[1]
681+ extra_path = None
682+ if len(furtherPath) > 0:
683+ extra_path = '/'.join(reversed(furtherPath))
684+ # Remove remaining entries in furtherPath so that traversal
685+ # stops here.
686+ del furtherPath[:]
687+ if name.startswith('link:'):
688+ if rootsite is None:
689+ return self.link(extra_path)
690+ else:
691+ return self.link(extra_path, rootsite=rootsite)
692+ else:
693+ if rootsite is None:
694+ self.url(extra_path)
695+ else:
696+ return self.url(extra_path, rootsite=rootsite)
697+ elif name in self.traversable_names:
698 if len(furtherPath) >= 1:
699 extra_path = '/'.join(reversed(furtherPath))
700 del furtherPath[:]
701@@ -472,13 +490,16 @@
702 else:
703 raise TraversalError, name
704
705- def link(self, view_name):
706+ def link(self, view_name, rootsite=None):
707 """Return an HTML link to the object's page.
708
709 The link consists of an icon followed by the object's name.
710
711 :param view_name: If not None, the link will point to the page with
712 that name on this object.
713+ :param rootsite: If not None, return the URL to the page on the
714+ specified rootsite. Note this is available only for subclasses
715+ that allow specifying the rootsite.
716 """
717 raise NotImplementedError(
718 "No link implementation for %r, IPathAdapter implementation "
719@@ -958,23 +979,6 @@
720 final_traversable_names = {'local-time': 'local_time'}
721 final_traversable_names.update(ObjectFormatterAPI.final_traversable_names)
722
723- def traverse(self, name, furtherPath):
724- """Special-case traversal for links with an optional rootsite."""
725- if name.startswith('link:') or name.startswith('url:'):
726- rootsite = name.split(':')[1]
727- extra_path = None
728- if len(furtherPath) > 0:
729- extra_path = '/'.join(reversed(furtherPath))
730- # Remove remaining entries in furtherPath so that traversal
731- # stops here.
732- del furtherPath[:]
733- if name.startswith('link:'):
734- return self.link(extra_path, rootsite=rootsite)
735- else:
736- return self.url(extra_path, rootsite=rootsite)
737- else:
738- return super(PersonFormatterAPI, self).traverse(name, furtherPath)
739-
740 def local_time(self):
741 """Return the local time for this person."""
742 time_zone = 'UTC'
743@@ -982,12 +986,22 @@
744 time_zone = self._context.time_zone
745 return datetime.now(pytz.timezone(time_zone)).strftime('%T %Z')
746
747- def link(self, view_name, rootsite=None):
748- """Return an HTML link to the person's page containing an icon
749- followed by the person's name.
750+ def url(self, view_name=None, rootsite='mainsite'):
751+ """See `ObjectFormatterAPI`.
752+
753+ The default URL for a person is to the mainsite.
754+ """
755+ return super(PersonFormatterAPI, self).url(view_name, rootsite)
756+
757+ def link(self, view_name, rootsite='mainsite'):
758+ """See `ObjectFormatterAPI`.
759+
760+ Return an HTML link to the person's page containing an icon
761+ followed by the person's name. The default URL for a person is to
762+ the mainsite.
763 """
764 person = self._context
765- url = canonical_url(person, rootsite=rootsite, view_name=view_name)
766+ url = self.url(view_name, rootsite)
767 custom_icon = ObjectImageDisplayAPI(person)._get_custom_icon_url()
768 if custom_icon is None:
769 css_class = ObjectImageDisplayAPI(person).sprite_css()
770@@ -1024,12 +1038,16 @@
771
772 hidden = u'<hidden>'
773
774- def url(self, view_name=None):
775- """See `ObjectFormatterAPI`."""
776+ def url(self, view_name=None, rootsite='mainsite'):
777+ """See `ObjectFormatterAPI`.
778+
779+ The default URL for a team is to the mainsite. None is returned
780+ when the user does not have permission to review the team.
781+ """
782 if not check_permission('launchpad.View', self._context):
783 # This person has no permission to view the team details.
784 return None
785- return super(TeamFormatterAPI, self).url(view_name)
786+ return super(TeamFormatterAPI, self).url(view_name, rootsite)
787
788 def api_url(self, context):
789 """See `ObjectFormatterAPI`."""
790@@ -1038,8 +1056,12 @@
791 return None
792 return super(TeamFormatterAPI, self).api_url(context)
793
794- def link(self, view_name, rootsite=None):
795- """See `ObjectFormatterAPI`."""
796+ def link(self, view_name, rootsite='mainsite'):
797+ """See `ObjectFormatterAPI`.
798+
799+ The default URL for a team is to the mainsite. None is returned
800+ when the user does not have permission to review the team.
801+ """
802 person = self._context
803 if not check_permission('launchpad.View', person):
804 # This person has no permission to view the team details.
805@@ -1116,7 +1138,7 @@
806 """
807 return queryAdapter(self._context, IPathAdapter, 'image').sprite_css()
808
809- def link(self, view_name):
810+ def link(self, view_name, rootsite=None):
811 """Return html including a link, description and icon.
812
813 Icon and link are optional, depending on type and permissions.
814@@ -1132,7 +1154,7 @@
815
816 summary = self._make_link_summary()
817 if check_permission(self._link_permission, self._context):
818- url = self.url(view_name)
819+ url = self.url(view_name, rootsite)
820 else:
821 url = ''
822 if url:
823@@ -1152,33 +1174,40 @@
824 displayname = self._context.displayname
825 return {'displayname': displayname}
826
827- def link(self, view_name):
828+ def url(self, view_name=None, rootsite=None):
829+ """See `ObjectFormatterAPI`.
830+
831+ The default URL for a pillar is to the mainsite.
832+ """
833+ return super(PillarFormatterAPI, self).url(view_name, rootsite)
834+
835+ def link(self, view_name, rootsite='mainsite'):
836 """The html to show a link to a Product, Project or distribution.
837
838 In the case of Products or Project groups we display the custom
839- icon, if one exists."""
840+ icon, if one exists. The default URL for a pillar is to the mainsite.
841+ """
842
843 html = super(PillarFormatterAPI, self).link(view_name)
844 context = self._context
845- if IProduct.providedBy(context) or IProject.providedBy(context):
846- custom_icon = ObjectImageDisplayAPI(
847- context)._get_custom_icon_url()
848- url = canonical_url(context, view_name=view_name)
849- summary = self._make_link_summary()
850- if custom_icon is None:
851- css_class = ObjectImageDisplayAPI(context).sprite_css()
852- html = (u'<a href="%s" class="%s">%s</a>') % (
853- url, css_class, summary)
854- else:
855- html = (u'<a href="%s" class="bg-image" '
856- 'style="background-image: url(%s)">%s</a>') % (
857- url, custom_icon, summary)
858- if IProduct.providedBy(context):
859- license_status = context.license_status
860- if license_status != LicenseStatus.OPEN_SOURCE:
861- html = '<span title="%s">%s (%s)</span>' % (
862- license_status.description, html,
863- license_status.title)
864+ custom_icon = ObjectImageDisplayAPI(
865+ context)._get_custom_icon_url()
866+ url = self.url(view_name, rootsite)
867+ summary = self._make_link_summary()
868+ if custom_icon is None:
869+ css_class = ObjectImageDisplayAPI(context).sprite_css()
870+ html = (u'<a href="%s" class="%s">%s</a>') % (
871+ url, css_class, summary)
872+ else:
873+ html = (u'<a href="%s" class="bg-image" '
874+ 'style="background-image: url(%s)">%s</a>') % (
875+ url, custom_icon, summary)
876+ if IProduct.providedBy(context):
877+ license_status = context.license_status
878+ if license_status != LicenseStatus.OPEN_SOURCE:
879+ html = '<span title="%s">%s (%s)</span>' % (
880+ license_status.description, html,
881+ license_status.title)
882 return html
883
884
885@@ -1231,7 +1260,7 @@
886 html += ')'
887 return html % replacements
888
889- def url(self, view_name):
890+ def url(self, view_name, rootsite=None):
891 """Return the URL to download the file."""
892 return self._getDownloadURL(self._context.libraryfile)
893
894@@ -1294,7 +1323,7 @@
895 class PreviewDiffFormatterAPI(ObjectFormatterAPI):
896 """Formatter for preview diffs."""
897
898- def url(self, view_name=None):
899+ def url(self, view_name=None, rootsite=None):
900 """Use the url of the librarian file containing the diff.
901 """
902 librarian_alias = self._context.diff_text
903@@ -1423,7 +1452,7 @@
904 'branch': self._context.branch.bzr_identity,
905 }
906
907- def url(self, view_name=None):
908+ def url(self, view_name=None, rootsite=None):
909 """See `ObjectFormatterAPI`."""
910 # The url of a code import is the associated branch.
911 # This is still here primarily for supporting branch deletion,
912@@ -2064,7 +2093,7 @@
913 """Return the default representation of the link."""
914 return self._context.render()
915
916- def url(self, view_name=None):
917+ def url(self, view_name=None, rootsite=None):
918 """Return the URL representation of the link."""
919 if self._context.enabled:
920 return self._context.url
921
922=== modified file 'lib/canonical/testing/__init__.py'
923--- lib/canonical/testing/__init__.py 2009-07-17 18:46:25 +0000
924+++ lib/canonical/testing/__init__.py 2009-08-10 22:08:05 +0000
925@@ -63,8 +63,9 @@
926 logging._handlers.clear()
927
928 # Reset the setup
929- import zope.testing.testrunner
930- zope.testing.testrunner.configure_logging()
931+ from zope.testing.testrunner.runner import Runner
932+ from zope.testing.testrunner.logsupport import Logging
933+ Logging(Runner()).global_setup()
934
935
936 # This import registers the 'doctest' Unicode codec.
937
938=== modified file 'lib/canonical/testing/ftests/test_mockdb.py'
939--- lib/canonical/testing/ftests/test_mockdb.py 2009-06-25 05:30:52 +0000
940+++ lib/canonical/testing/ftests/test_mockdb.py 2009-08-05 18:52:52 +0000
941@@ -11,7 +11,7 @@
942 import unittest
943
944 import psycopg2
945-from zope.testing.testrunner import dont_retry, RetryTest
946+# from zope.testing.testrunner import dont_retry, RetryTest
947
948 from canonical.config import config
949 from canonical.testing import mockdb, DatabaseLayer
950@@ -101,63 +101,63 @@
951 self.connections.append(con)
952 return con
953
954- @dont_retry
955- def testIncorrectReplay(self):
956- # Record nothing but a close on a single connection.
957- con = self.connect()
958- con.close()
959- self.script.store()
960-
961- # Replay correctly.
962- self.switchToReplayMode()
963- con = self.connect()
964- con.close()
965-
966- # Replay incorrectly.
967- self.switchToReplayMode()
968- con = self.connect()
969- self.assertRaises(RetryTest, con.rollback)
970-
971- @dont_retry
972- def testMultipleConnections(self):
973+ # @dont_retry
974+ #def testIncorrectReplay(self):
975+ # # Record nothing but a close on a single connection.
976+ # con = self.connect()
977+ # con.close()
978+ # self.script.store()
979+
980+ # # Replay correctly.
981+ # self.switchToReplayMode()
982+ # con = self.connect()
983+ # con.close()
984+
985+ # # Replay incorrectly.
986+ # self.switchToReplayMode()
987+ # con = self.connect()
988+ # self.assertRaises(RetryTest, con.rollback)
989+
990+ # @dont_retry
991+ #def testMultipleConnections(self):
992 # Ensure that commands issued via different connections
993 # maintain their global order.
994- con1 = self.connect()
995- con2 = self.connect()
996- con1.close()
997- con2.close()
998- self.script.store()
999-
1000- # Replay correctly.
1001- self.switchToReplayMode()
1002- con1 = self.connect()
1003- con2 = self.connect()
1004- con1.close()
1005- con2.close()
1006-
1007- # Replay in the wrong order.
1008- self.switchToReplayMode()
1009- con1 = self.connect()
1010- con2 = self.connect()
1011- self.assertRaises(RetryTest, con2.close)
1012-
1013- @dont_retry
1014- def testConnectionParams(self):
1015+ # con1 = self.connect()
1016+ # con2 = self.connect()
1017+ # con1.close()
1018+ # con2.close()
1019+ # self.script.store()
1020+
1021+ # # Replay correctly.
1022+ # self.switchToReplayMode()
1023+ # con1 = self.connect()
1024+ # con2 = self.connect()
1025+ # con1.close()
1026+ # con2.close()
1027+
1028+ # # Replay in the wrong order.
1029+ # self.switchToReplayMode()
1030+ # con1 = self.connect()
1031+ # con2 = self.connect()
1032+ # self.assertRaises(RetryTest, con2.close)
1033+
1034+ # @dont_retry
1035+ #def testConnectionParams(self):
1036 # Make sure we can correctly connect with different connection parms.
1037- for mode in self.modes():
1038- for dbuser in ['launchpad', 'testadmin']:
1039- connection_string = "%s user=%s" % (
1040- config.database.main_master, dbuser)
1041- con = self.connect(connection_string)
1042- cur = con.cursor()
1043- cur.execute("SHOW session authorization")
1044- self.failUnlessEqual(cur.fetchone()[0], dbuser)
1045+ # for mode in self.modes():
1046+ # for dbuser in ['launchpad', 'testadmin']:
1047+ # connection_string = "%s user=%s" % (
1048+ # config.database.main_master, dbuser)
1049+ # con = self.connect(connection_string)
1050+ # cur = con.cursor()
1051+ # cur.execute("SHOW session authorization")
1052+ # self.failUnlessEqual(cur.fetchone()[0], dbuser)
1053
1054 # Confirm that unexpected connection parameters raises a RetryTest.
1055- self.switchToReplayMode()
1056- self.assertRaises(RetryTest, self.connect, "whoops")
1057+ # self.switchToReplayMode()
1058+ # self.assertRaises(RetryTest, self.connect, "whoops")
1059
1060- @dont_retry
1061+ # @dont_retry
1062 def testFailedConnection(self):
1063 # Ensure failed database connections are reproducable.
1064 for mode in self.modes():
1065@@ -168,13 +168,13 @@
1066 psycopg2.OperationalError, self.connect, connection_string
1067 )
1068
1069- @dont_retry
1070+ # @dont_retry
1071 def testNoopSession(self):
1072 # Minimal do-nothing case.
1073 for mode in self.modes():
1074 con = self.connect()
1075
1076- @dont_retry
1077+ # @dont_retry
1078 def testSimpleQuery(self):
1079 # Ensure that we can script and replay a simple query.
1080 for mode in self.modes():
1081@@ -199,7 +199,7 @@
1082 name = cur.fetchone()[0]
1083 self.assertEqual(name, 'carlos')
1084
1085- @dont_retry
1086+ # @dont_retry
1087 def testExceptions(self):
1088 # Confirm that expected exceptions are raised correctly.
1089 for mode in self.modes():
1090@@ -210,38 +210,38 @@
1091 cur.execute, "SELECT blood FROM Stone"
1092 )
1093
1094- @dont_retry
1095- def testUnexpectedQuery(self):
1096- for mode in self.modes():
1097- con = self.connect()
1098- cur = con.cursor()
1099- if mode != 'replay':
1100- cur.execute("SELECT name FROM Person WHERE name='sabdfl'")
1101- else:
1102+ # @dont_retry
1103+ #def testUnexpectedQuery(self):
1104+ # for mode in self.modes():
1105+ # con = self.connect()
1106+ # cur = con.cursor()
1107+ # if mode != 'replay':
1108+ # cur.execute("SELECT name FROM Person WHERE name='sabdfl'")
1109+ # else:
1110 # Issue an unexpected query in replay mode. A RetryTest
1111 # exception should be raised.
1112- self.assertRaises(
1113- RetryTest, cur.execute,
1114- "SELECT name FROM Person WHERE name='stub'"
1115- )
1116+ # self.assertRaises(
1117+ # RetryTest, cur.execute,
1118+ # "SELECT name FROM Person WHERE name='stub'"
1119+ # )
1120
1121- @dont_retry
1122- def testUnexpectedQueryParameters(self):
1123- for mode in self.modes():
1124- con = self.connect()
1125- cur = con.cursor()
1126- query = "SELECT name FROM Person WHERE name=%s"
1127- if mode != 'replay':
1128- cur.execute(query, ('sabdfl',))
1129- else:
1130+ # @dont_retry
1131+ #def testUnexpectedQueryParameters(self):
1132+ # for mode in self.modes():
1133+ # con = self.connect()
1134+ # cur = con.cursor()
1135+ # query = "SELECT name FROM Person WHERE name=%s"
1136+ # if mode != 'replay':
1137+ # cur.execute(query, ('sabdfl',))
1138+ # else:
1139 # Issue a query with unexpected bound parameters in replay
1140 # mode. A RetryTest should be raised.
1141- self.assertRaises(
1142- RetryTest, cur.execute,
1143- query, ('stub',)
1144- )
1145+ # self.assertRaises(
1146+ # RetryTest, cur.execute,
1147+ # query, ('stub',)
1148+ # )
1149
1150- @dont_retry
1151+ # @dont_retry
1152 def testCommit(self):
1153 # Confirm commit behavior.
1154 for mode in self.modes():
1155@@ -283,7 +283,7 @@
1156 """)
1157 con.commit()
1158
1159- @dont_retry
1160+ # @dont_retry
1161 def testRollback(self):
1162 # Confirm rollback behavior.
1163 for mode in self.modes():
1164@@ -323,7 +323,7 @@
1165 "Rollback did not roll back changes."
1166 )
1167
1168- @dont_retry
1169+ # @dont_retry
1170 def testFailedCommit(self):
1171 # Confirm exeptions raised on commit are recorded and replayed.
1172 for mode in self.modes():
1173@@ -344,7 +344,7 @@
1174 else:
1175 self.assertRaises(psycopg2.InterfaceError, con.rollback)
1176
1177- @dont_retry
1178+ # @dont_retry
1179 def testFailedSetIsolationLevel(self):
1180 # Confirm exeptions raised on commit are recorded and replayed.
1181 for mode in self.modes():
1182@@ -354,7 +354,7 @@
1183 psycopg2.InterfaceError, con.set_isolation_level, 666
1184 )
1185
1186- @dont_retry
1187+ # @dont_retry
1188 def testClose(self):
1189 # Confirm and record close behavior.
1190 for mode in self.modes():
1191@@ -376,7 +376,7 @@
1192 self.fail(
1193 "Connection.close() now DB-API compliant. Fix test.")
1194
1195- @dont_retry
1196+ # @dont_retry
1197 def testCursorDescription(self):
1198 # Confirm cursor.description behavior.
1199 for mode in self.modes():
1200@@ -402,7 +402,7 @@
1201 else:
1202 self.failUnlessEqual(direct_description, cur.description)
1203
1204- @dont_retry
1205+ # @dont_retry
1206 def testCursorRowcount(self):
1207 # Confirm and record cursor.rowcount behavior.
1208 for mode in self.modes():
1209@@ -443,7 +443,7 @@
1210 cur.execute("DELETE FROM WikiName WHERE person=1")
1211 self.failUnlessEqual(cur.rowcount, 1)
1212
1213- @dont_retry
1214+ # @dont_retry
1215 def testCursorClose(self):
1216 # Confirm and record cursor.close behavior.
1217 for mode in self.modes():
1218@@ -457,7 +457,7 @@
1219 cur = con.cursor()
1220 cur.execute("SELECT name FROM Person WHERE name='stub'")
1221
1222- @dont_retry
1223+ # @dont_retry
1224 def testFetchOne(self):
1225 for mode in self.modes():
1226 con = self.connect()
1227@@ -491,7 +491,7 @@
1228 self.failUnlessEqual(row[0], i, "Bad result %s" % repr(row))
1229 self.failUnless(cur.fetchone() is None, "Too many results")
1230
1231- @dont_retry
1232+ # @dont_retry
1233 def testCursorIteration(self):
1234 # psycopg1 does not support this extension.
1235 for mode in self.modes():
1236@@ -516,7 +516,7 @@
1237 ## )
1238 ## self.failUnlessEqual(row[0], 1, "Bad result %s" % repr(row))
1239
1240- @dont_retry
1241+ # @dont_retry
1242 def testFetchAll(self):
1243 for mode in self.modes():
1244 con = self.connect()
1245
1246=== modified file 'lib/canonical/testing/mockdb.py'
1247--- lib/canonical/testing/mockdb.py 2009-06-25 05:30:52 +0000
1248+++ lib/canonical/testing/mockdb.py 2009-08-05 18:52:52 +0000
1249@@ -25,7 +25,7 @@
1250 import urllib
1251
1252 import psycopg2
1253-from zope.testing.testrunner import RetryTest
1254+# from zope.testing.testrunner import RetryTest
1255
1256 from canonical.config import config
1257
1258@@ -386,7 +386,8 @@
1259 if os.path.exists(self.script_filename):
1260 os.unlink(self.script_filename)
1261 self.invalid = True
1262- raise RetryTest(reason)
1263+ raise RetryTest(reason) # Leaving this as a name error: this should
1264+ # not be called unless we reinstate the retry behavior in zope.testing.
1265
1266
1267 class MockDbConnection:
1268
1269=== modified file 'lib/canonical/testing/tests/test_mockdb.py'
1270--- lib/canonical/testing/tests/test_mockdb.py 2009-06-25 05:30:52 +0000
1271+++ lib/canonical/testing/tests/test_mockdb.py 2009-08-05 18:52:52 +0000
1272@@ -11,7 +11,7 @@
1273 import unittest
1274
1275 from zope.testing.doctestunit import DocTestSuite
1276-from zope.testing.testrunner import dont_retry, RetryTest
1277+#from zope.testing.testrunner import dont_retry, RetryTest
1278
1279 from canonical.testing import mockdb
1280
1281@@ -24,7 +24,7 @@
1282 if os.path.exists(self.script_filename):
1283 os.unlink(self.script_filename)
1284
1285- @dont_retry
1286+ #@dont_retry
1287 def testSerialize(self):
1288 # Ensure the scripts can store and retrieve their logs
1289 recorder = mockdb.ScriptRecorder(self.script_filename)
1290@@ -34,29 +34,29 @@
1291 replayer = mockdb.ScriptPlayer(self.script_filename)
1292 self.failUnlessEqual(replayer.log, ['Arbitrary Data'])
1293
1294- @dont_retry
1295- def testHandleInvalidScript(self):
1296+ #@dont_retry
1297+ #def testHandleInvalidScript(self):
1298 # Ensure a RetryTest exception is raised and the invalid script
1299 # file removed when handleInvalidScript() is called
1300- recorder = mockdb.ScriptRecorder(self.script_filename)
1301- recorder.store()
1302-
1303- replayer = mockdb.ScriptPlayer(self.script_filename)
1304-
1305- self.assertRaises(
1306- RetryTest, replayer.handleInvalidScript, 'Reason')
1307- self.failIf(os.path.exists(self.script_filename))
1308-
1309- @dont_retry
1310- def testShortScript(self):
1311+ # recorder = mockdb.ScriptRecorder(self.script_filename)
1312+ # recorder.store()
1313+
1314+ # replayer = mockdb.ScriptPlayer(self.script_filename)
1315+
1316+ # self.assertRaises(
1317+ # RetryTest, replayer.handleInvalidScript, 'Reason')
1318+ # self.failIf(os.path.exists(self.script_filename))
1319+
1320+ #@dont_retry
1321+ #def testShortScript(self):
1322 # Ensure a RetryTest exception is raised if an attempt is made
1323 # to pull results from an exhausted script.
1324- recorder = mockdb.ScriptRecorder(self.script_filename)
1325- recorder.store()
1326- replayer = mockdb.ScriptPlayer(self.script_filename)
1327- self.assertRaises(RetryTest, replayer.getNextEntry, None, None)
1328+ # recorder = mockdb.ScriptRecorder(self.script_filename)
1329+ # recorder.store()
1330+ # replayer = mockdb.ScriptPlayer(self.script_filename)
1331+ # self.assertRaises(RetryTest, replayer.getNextEntry, None, None)
1332
1333- @dont_retry
1334+ #@dont_retry
1335 def testScriptFilename(self):
1336 # Ensure evil characters in the key don't mess up the script_filename
1337 # results. Only '/' is really evil - other chars should all work
1338@@ -86,7 +86,7 @@
1339 # This test does not use @dont_retry.
1340 # It needs to leak RetryTest exeptions as it tests that the
1341 # test runner is handling them correctly.
1342- def testRetryTestRetriesTest(self):
1343+ #def testRetryTestRetriesTest(self):
1344 # The first time this test is run it raises a RetryTest exception.
1345 # The second time it is run it succeeds. This means that this
1346 # test will fail if RetryTest handling is not being done correctly
1347@@ -97,106 +97,106 @@
1348 # version of zope.testing is in use and to minimize the zope.testing
1349 # patch until we decide if RetryTest handling is to be pushed
1350 # upstream or not.
1351- MockDbTestCase._retry_count += 1
1352- if MockDbTestCase._retry_count % 2 == 1:
1353- raise RetryTest(
1354- "Testing RetryTest behavior. This exception will be raised "
1355- "but the test runner doesn't consider it a failure")
1356+ # MockDbTestCase._retry_count += 1
1357+ # if MockDbTestCase._retry_count % 2 == 1:
1358+ # raise RetryTest(
1359+ # "Testing RetryTest behavior. This exception will be raised "
1360+ # "but the test runner doesn't consider it a failure")
1361
1362
1363 _doctest_retry_count = 0
1364
1365-def retry_on_odd_numbered_calls():
1366- """Helper for doctest RetryTest test.
1367+#def retry_on_odd_numbered_calls():
1368+# """Helper for doctest RetryTest test.
1369
1370- This helper raises a RetryTest exception on odd numbered calls,
1371- and prints 'Retry not raised' on even numbered calls.
1372+# This helper raises a RetryTest exception on odd numbered calls,
1373+# and prints 'Retry not raised' on even numbered calls.
1374
1375- >>> try:
1376- ... retry_on_odd_numbered_calls()
1377- ... except RetryTest:
1378- ... print "Caught RetryTest."
1379- ...
1380- Retry raised.
1381- Caught RetryTest.
1382- >>> try:
1383- ... retry_on_odd_numbered_calls()
1384- ... except RetryTest:
1385- ... print "Caught RetryTest."
1386- ...
1387- Retry not raised.
1388- """
1389- global _doctest_retry_count
1390- _doctest_retry_count += 1
1391- if _doctest_retry_count % 2 == 1:
1392- print "Retry raised."
1393- raise RetryTest
1394- print "Retry not raised."
1395-
1396-
1397-def testRetryTestInDoctest_will_raise_but_testrunner_ignores_it():
1398- """Test a RetryTest exception in a doctest works as expected.
1399-
1400- This doctest raises a RetryTest exception the first time it is run.
1401- On the second run, it succeeds.
1402-
1403- If the testrunner is correctly handling RetryTest exceptions raised
1404- by doctests, then the RetryTest exception will not be reported as
1405- a failure. This test will then be rerun and succeed.
1406-
1407- If the testrunner is not correctly handling RetryTest exceptions,
1408- then the RetryTesst exception will be flagged as an error.
1409-
1410- This test confirms that a RetryException raised where no exception
1411- was expected works.
1412-
1413- >>> retry_on_odd_numbered_calls()
1414- Retry not raised.
1415- """
1416-
1417-
1418-def retry_on_odd_numbered_calls2():
1419- """Helper for doctest RetryTest test.
1420-
1421- This helper raises a RetryTest exception on odd numbered calls,
1422- and a RuntimeError on even numbered calls.
1423-
1424- >>> try:
1425- ... retry_on_odd_numbered_calls2()
1426- ... except RetryTest:
1427- ... print "Caught RetryTest."
1428- ...
1429- Retry raised.
1430- Caught RetryTest.
1431- >>> try:
1432- ... retry_on_odd_numbered_calls2()
1433- ... except RetryTest:
1434- ... print "Caught RetryTest."
1435- ...
1436- Traceback (most recent call last):
1437- ...
1438- RuntimeError: Retry not raised.
1439- """
1440- global _doctest_retry_count
1441- _doctest_retry_count += 1
1442- if _doctest_retry_count % 2 == 1:
1443- print "Retry raised."
1444- raise RetryTest
1445- raise RuntimeError("Retry not raised.")
1446-
1447-
1448-def testRetryTestInDoctest2():
1449- """Test a RetryTest exception in a doctest works as expected.
1450-
1451- This test is the same as testRetryTestInDoctest, except it confirms
1452- that a RetryException raised where a different exception was expected
1453- works.
1454-
1455- >>> retry_on_odd_numbered_calls2()
1456- Traceback (most recent call last):
1457- ...
1458- RuntimeError: Retry not raised.
1459- """
1460+# >>> try:
1461+# ... retry_on_odd_numbered_calls()
1462+# ... except RetryTest:
1463+# ... print "Caught RetryTest."
1464+# ...
1465+# Retry raised.
1466+# Caught RetryTest.
1467+# >>> try:
1468+# ... retry_on_odd_numbered_calls()
1469+# ... except RetryTest:
1470+# ... print "Caught RetryTest."
1471+# ...
1472+# Retry not raised.
1473+# """
1474+# global _doctest_retry_count
1475+# _doctest_retry_count += 1
1476+# if _doctest_retry_count % 2 == 1:
1477+# print "Retry raised."
1478+# raise RetryTest
1479+# print "Retry not raised."
1480+
1481+
1482+#def testRetryTestInDoctest_will_raise_but_testrunner_ignores_it():
1483+# """Test a RetryTest exception in a doctest works as expected.
1484+
1485+# This doctest raises a RetryTest exception the first time it is run.
1486+# On the second run, it succeeds.
1487+
1488+# If the testrunner is correctly handling RetryTest exceptions raised
1489+# by doctests, then the RetryTest exception will not be reported as
1490+# a failure. This test will then be rerun and succeed.
1491+
1492+# If the testrunner is not correctly handling RetryTest exceptions,
1493+# then the RetryTesst exception will be flagged as an error.
1494+
1495+# This test confirms that a RetryException raised where no exception
1496+# was expected works.
1497+
1498+# >>> retry_on_odd_numbered_calls()
1499+# Retry not raised.
1500+# """
1501+
1502+
1503+#def retry_on_odd_numbered_calls2():
1504+# """Helper for doctest RetryTest test.
1505+
1506+# This helper raises a RetryTest exception on odd numbered calls,
1507+# and a RuntimeError on even numbered calls.
1508+
1509+# >>> try:
1510+# ... retry_on_odd_numbered_calls2()
1511+# ... except RetryTest:
1512+# ... print "Caught RetryTest."
1513+# ...
1514+# Retry raised.
1515+# Caught RetryTest.
1516+# >>> try:
1517+# ... retry_on_odd_numbered_calls2()
1518+# ... except RetryTest:
1519+# ... print "Caught RetryTest."
1520+# ...
1521+# Traceback (most recent call last):
1522+# ...
1523+# RuntimeError: Retry not raised.
1524+# """
1525+# global _doctest_retry_count
1526+# _doctest_retry_count += 1
1527+# if _doctest_retry_count % 2 == 1:
1528+# print "Retry raised."
1529+# raise RetryTest
1530+# raise RuntimeError("Retry not raised.")
1531+
1532+
1533+#def testRetryTestInDoctest2():
1534+# """Test a RetryTest exception in a doctest works as expected.
1535+
1536+# This test is the same as testRetryTestInDoctest, except it confirms
1537+# that a RetryException raised where a different exception was expected
1538+# works.
1539+
1540+# >>> retry_on_odd_numbered_calls2()
1541+# Traceback (most recent call last):
1542+# ...
1543+# RuntimeError: Retry not raised.
1544+# """
1545
1546
1547
1548
1549=== modified file 'lib/canonical/widgets/date.py'
1550--- lib/canonical/widgets/date.py 2009-06-25 05:30:52 +0000
1551+++ lib/canonical/widgets/date.py 2009-08-13 19:36:01 +0000
1552@@ -80,7 +80,7 @@
1553 >>> print widget.getInputValue() #doctest: +ELLIPSIS
1554 Traceback (most recent call last):
1555 ...
1556- WidgetInputError: (... Please pick a date after 2006-05-22 17:00:00)
1557+ WidgetInputError: (...Please pick a date after 2006-05-22 17:00:00...)
1558
1559 If the date provided is greater than from_date then the widget works as
1560 expected.
1561@@ -96,7 +96,7 @@
1562 >>> print widget.getInputValue() #doctest: +ELLIPSIS
1563 Traceback (most recent call last):
1564 ...
1565- WidgetInputError: (... Please pick a date before 2008-01-25 16:00:00)
1566+ WidgetInputError: (...Please pick a date before 2008-01-25 16:00:00...)
1567
1568 A datetime picker can be disabled initially:
1569
1570@@ -288,7 +288,7 @@
1571 value = super(DateTimeWidget, self).getInputValue()
1572 if value is None:
1573 return None
1574- # Establish if the value is within the date range.
1575+ # Establish if the value is within the date range.
1576 self._align_date_constraints_with_time_zone()
1577 if self.from_date is not None and value < self.from_date:
1578 limit = self.from_date.strftime(self.timeformat)
1579@@ -570,4 +570,3 @@
1580 return u""
1581 value = value.astimezone(time_zone)
1582 return escape(value.strftime("%Y-%m-%d %H:%M:%S %Z"))
1583-
1584
1585=== modified file 'lib/lp/answers/interfaces/questiontarget.py'
1586--- lib/lp/answers/interfaces/questiontarget.py 2009-06-24 23:10:46 +0000
1587+++ lib/lp/answers/interfaces/questiontarget.py 2009-08-13 15:22:00 +0000
1588@@ -13,8 +13,6 @@
1589 'ISearchQuestionsForm',
1590 ]
1591
1592-import sets
1593-
1594 from zope.interface import Interface
1595 from zope.schema import Choice, List, Set, TextLine
1596
1597@@ -156,7 +154,7 @@
1598
1599 status = Set(title=_('Status'), required=False,
1600 value_type=Choice(vocabulary=QuestionStatus),
1601- default=sets.Set(QUESTION_STATUS_DEFAULT_SEARCH))
1602+ default=set(QUESTION_STATUS_DEFAULT_SEARCH))
1603
1604
1605 class IAnswersFrontPageSearchForm(ISearchQuestionsForm):
1606
1607=== modified file 'lib/lp/answers/stories/answer-contact-report.txt'
1608--- lib/lp/answers/stories/answer-contact-report.txt 2009-06-12 16:36:02 +0000
1609+++ lib/lp/answers/stories/answer-contact-report.txt 2009-08-13 13:33:26 +0000
1610@@ -1,6 +1,6 @@
1611 = Answer Contact Report =
1612
1613-To view the answer contact report for a given person, the user chooses
1614+To view the answer contact report for a given person, the user chooses
1615 the 'Answer Contact For' link from the actions portlet while viewing
1616 the Person's page.
1617
1618@@ -11,7 +11,7 @@
1619
1620 Since No Privileges Person is not an answer contact, the report states
1621 that.
1622-
1623+
1624 >>> content = find_main_content(anon_browser.contents)
1625 >>> print content.find('p').renderContents()
1626 No Privileges Person is not an answer contact for any project.
1627@@ -23,7 +23,7 @@
1628 >>> anon_browser.getLink('Answer contact for').click()
1629 >>> print anon_browser.title
1630 Projects for which Foo Bar is an answer contact
1631-
1632+
1633 >>> content = find_tag_by_id(
1634 ... anon_browser.contents, "direct-answer-contacts-for-list")
1635 >>> print extract_text(content).encode('ascii', 'backslashreplace')
1636@@ -35,7 +35,7 @@
1637 >>> print extract_text(content)
1638 Gnome Baker
1639 The Gnome Panel Applets
1640-
1641+
1642 Clicking on the name of the project will show the project answers.
1643
1644 >>> anon_browser.getLink('Gnome Baker').click()
1645@@ -51,18 +51,18 @@
1646 >>> browser.getLink('Answer contact for').click()
1647 >>> print browser.title
1648 Projects for which Sample Person is an answer contact
1649-
1650+
1651 >>> content = find_tag_by_id(
1652 ... browser.contents, "team-answer-contacts-for-list")
1653 >>> print extract_text(content)
1654 Gnome Baker &mdash; Remove team
1655 The Gnome Panel Applets &mdash; Remove team
1656-
1657+
1658 >>> browser.getLink(id="gnomebaker-setteamanswercontact").click()
1659 >>> print browser.title
1660 Answer contact for Gnome Baker
1661-
1662-The Remove yourself/team links only appears in his profile. He cannot
1663+
1664+The Remove yourself/team links only appears in his profile. He cannot
1665 see the link for other users
1666
1667 >>> browser.open(
1668@@ -70,7 +70,7 @@
1669 >>> browser.getLink('Answer contact for').click()
1670 >>> print browser.title
1671 Projects for which Foo Bar is an answer contact
1672-
1673+
1674 >>> content = find_tag_by_id(browser.contents, "direct-answer-contacts-for-list")
1675 >>> print extract_text(content).encode('ascii', 'backslashreplace')
1676 Gnome Baker
1677
1678=== modified file 'lib/lp/answers/stories/questions-index.txt'
1679--- lib/lp/answers/stories/questions-index.txt 2009-03-24 12:43:49 +0000
1680+++ lib/lp/answers/stories/questions-index.txt 2009-08-13 21:32:59 +0000
1681@@ -60,7 +60,7 @@
1682 ... anon_browser.contents, 'most-active-projects'))
1683 Most active projects
1684 Ubuntu
1685- Mozilla Firefox
1686+ Mozilla Firefox ...
1687
1688 Clicking on these project links will bring the user to the project
1689 Answers front page:
1690
1691=== modified file 'lib/lp/answers/templates/person-answer-contact-for.pt'
1692--- lib/lp/answers/templates/person-answer-contact-for.pt 2009-07-17 17:59:07 +0000
1693+++ lib/lp/answers/templates/person-answer-contact-for.pt 2009-08-13 13:33:26 +0000
1694@@ -23,8 +23,8 @@
1695
1696 <ul class="listing" id="direct-answer-contacts-for-list">
1697 <li tal:repeat="question_target view/direct_question_targets">
1698- <a href="#"
1699- tal:attributes="href string:${question_target/fmt:url}"
1700+ <a
1701+ tal:attributes="href question_target/fmt:url:answers"
1702 tal:content="question_target/title">Project Title</a>
1703 <tal:link condition="view/showRemoveYourselfLink">
1704 &mdash;
1705@@ -42,8 +42,8 @@
1706
1707 <ul class="listing" id="team-answer-contacts-for-list">
1708 <li tal:repeat="question_target view/team_question_targets">
1709- <a href="#"
1710- tal:attributes="href string:${question_target/fmt:url}"
1711+ <a
1712+ tal:attributes="href question_target/fmt:url:answers"
1713 tal:content="question_target/title">Project Title</a>
1714 <tal:link condition="view/showRemoveYourselfLink">
1715 &mdash;
1716
1717=== modified file 'lib/lp/answers/templates/questions-index.pt'
1718--- lib/lp/answers/templates/questions-index.pt 2009-07-17 17:59:07 +0000
1719+++ lib/lp/answers/templates/questions-index.pt 2009-08-13 21:32:59 +0000
1720@@ -81,11 +81,7 @@
1721 tal:condition="projects"
1722 style="list-style: none;">
1723 <li tal:repeat="project projects">
1724- <a href="#"
1725- tal:attributes="href project/fmt:url">
1726- <img alt="" tal:replace="structure project/image:icon" />
1727- <span tal:replace="project/displayname" />
1728- </a>
1729+ <a tal:replace="structure project/fmt:link:answers"/>
1730 </li>
1731 </ul>
1732 </div>
1733@@ -110,28 +106,29 @@
1734 </table>
1735 </div>
1736 </div>
1737- <div id="application-footer">
1738- <div>
1739- <strong
1740- tal:content="view/answered_question_count"
1741- >52</strong>
1742- questions answered and
1743- <strong
1744- tal:content="view/solved_question_count"
1745- >52</strong>
1746- questions solved out of
1747- </div>
1748- <div>
1749- <strong
1750- tal:content="view/question_count"
1751- >318</strong>
1752- questions asked across
1753- <strong
1754- tal:content="view/projects_with_questions_count"
1755- >28</strong>
1756- projects
1757- </div>
1758- </div>
1759+
1760+ <div id="application-footer">
1761+ <div>
1762+ <strong
1763+ tal:content="view/answered_question_count"
1764+ >52</strong>
1765+ questions answered and
1766+ <strong
1767+ tal:content="view/solved_question_count"
1768+ >52</strong>
1769+ questions solved out of
1770+ </div>
1771+ <div>
1772+ <strong
1773+ tal:content="view/question_count"
1774+ >318</strong>
1775+ questions asked across
1776+ <strong
1777+ tal:content="view/projects_with_questions_count"
1778+ >28</strong>
1779+ projects
1780+ </div>
1781+ </div>
1782
1783 </div><!--main-->
1784
1785
1786=== modified file 'lib/lp/app/browser/tests/base-layout.txt'
1787--- lib/lp/app/browser/tests/base-layout.txt 2009-08-06 11:07:45 +0000
1788+++ lib/lp/app/browser/tests/base-layout.txt 2009-08-13 02:24:56 +0000
1789@@ -44,7 +44,6 @@
1790 <body id="document" class="tab-overview main_side public yui-skin-sam">
1791 <div class="yui-d0">
1792 <div id="locationbar"> ...
1793- <form id="globalsearch" ...
1794 <div id="lp-hierarchy" class="home">...
1795 <div class="location-portlet top-portlet">
1796 <img ...
1797@@ -77,7 +76,8 @@
1798 </div><!-- yui-b side -->
1799 </div><!-- yui-t4 -->
1800 <div id="footer" class="footer"> ...
1801- </div><!-- footer-->
1802+ <form id="globalsearch" ...
1803+ </div>
1804 </div><!-- yui-d0-->
1805 <script>LP.client.cache['context'] ...
1806 </body>
1807@@ -105,7 +105,6 @@
1808 <body id="document" class="tab-overview main_only public yui-skin-sam">
1809 <div class="yui-d0">
1810 <div id="locationbar"> ...
1811- <form id="globalsearch" ...
1812 <div id="lp-hierarchy" class="home">...
1813 <div class="location-portlet top-portlet">
1814 <img ...
1815@@ -134,7 +133,8 @@
1816 <!-- yui-b side -->
1817 <!-- yui-t4 -->
1818 <div id="footer" class="footer"> ...
1819- </div><!-- footer-->
1820+ <form id="globalsearch" ...
1821+ </div>
1822 </div><!-- yui-d0-->
1823 <script>LP.client.cache['context'] ...
1824 </body>
1825@@ -191,7 +191,7 @@
1826 <!-- yui-b side -->
1827 <!-- yui-t4 -->
1828 <div id="footer" class="footer"> ...
1829- </div><!-- footer-->
1830+ </div>
1831 </div><!-- yui-d0-->
1832 <script>LP.client.cache['context'] ...
1833 </body>
1834@@ -241,7 +241,7 @@
1835 <!-- yui-b side -->
1836 <!-- yui-t4 -->
1837 <div id="footer" class="footer"> ...
1838- </div><!-- footer-->
1839+ </div>
1840 </div><!-- yui-d0-->
1841 <script>LP.client.cache['context'] ...
1842 </body>
1843
1844=== modified file 'lib/lp/app/templates/base-layout-macros.pt'
1845--- lib/lp/app/templates/base-layout-macros.pt 2009-08-06 11:07:45 +0000
1846+++ lib/lp/app/templates/base-layout-macros.pt 2009-08-13 02:24:56 +0000
1847@@ -330,4 +330,41 @@
1848 </ul>
1849 </metal:location-tabs>
1850
1851+
1852+<metal:footer define-macro="footer">
1853+ <div id="footer" class="footer">
1854+ <div class="lp-arcana">
1855+ <img src="/@@/launchpad-logo-and-name-hierarchy.png" alt="Launchpad"/>
1856+ &nbsp;&bull;&nbsp;
1857+ <a href="/+tour">Take the tour</a>
1858+ &nbsp;&bull;&nbsp;
1859+ <a href="https://help.launchpad.net/">Read the guide</a>
1860+ &nbsp;
1861+ <form id="globalsearch" method="get" accept-charset="UTF-8"
1862+ class="sprite-after search-icon"
1863+ tal:condition="view/macro:pagehas/globalsearch"
1864+ tal:attributes="action string:${rooturl}+search">
1865+ <input type="search" id="search-text" name="field.text" />
1866+ </form>
1867+ </div>
1868+
1869+ <div class="colophon">
1870+ &copy; 2004-2009
1871+ <a href="http://canonical.com/">Canonical&nbsp;Ltd.</a>
1872+ &nbsp;&bull;&nbsp;
1873+ <a href="/legal">Terms of use</a>
1874+ &nbsp;&bull;&nbsp;
1875+ <a href="/feedback"
1876+ tal:condition="request/lp:person">Contact us</a>
1877+ <span id="lp-version" tal:condition="not:is_lpnet">
1878+ &nbsp;&bull;&nbsp;
1879+ <a href="https://help.launchpad.net/LaunchpadReleases"
1880+ tal:content="version"/>
1881+ <tal:devmode condition="devmode">devmode</tal:devmode>
1882+ <tal:demo condition="is_demo">demo site</tal:demo>
1883+ <tal:edge condition="is_edge">beta site</tal:edge>
1884+ </span>
1885+ </div>
1886+ </div>
1887+</metal:footer>
1888 </macros>
1889
1890=== modified file 'lib/lp/app/templates/base-layout.pt'
1891--- lib/lp/app/templates/base-layout.pt 2009-08-11 04:35:26 +0000
1892+++ lib/lp/app/templates/base-layout.pt 2009-08-13 02:24:56 +0000
1893@@ -81,12 +81,6 @@
1894 <div class="yui-d0">
1895 <div id="locationbar">
1896 <tal:login replace="structure context/@@login_status" />
1897- <form id="globalsearch" method="get" accept-charset="UTF-8"
1898- class="sprite search-icon"
1899- tal:condition="view/macro:pagehas/globalsearch"
1900- tal:attributes="action string:${rooturl}+search">
1901- <input type="search" id="search-text" name="field.text" />
1902- </form>
1903 <tal:hierarchy replace="structure context/@@+hierarchy" />
1904 </div><!--id="locationbar"-->
1905
1906@@ -135,28 +129,8 @@
1907 </div><!-- yui-b side -->
1908 </div><!-- yui-t4 -->
1909
1910- <div id="footer" class="footer">
1911- <div id="lp-arcana">
1912- &copy;&nbsp;2004-2009&nbsp;<a
1913- href="http://canonical.com/">Canonical&nbsp;Ltd.</a> |
1914- <a href="/legal">Terms of use</a>
1915- <span id="lp-version" tal:condition="not:is_lpnet"> |
1916- <a href="https://help.launchpad.net/LaunchpadReleases"
1917- tal:content="version"/>
1918- <tal:devmode condition="devmode">devmode</tal:devmode>
1919- <tal:demo condition="is_demo">demo site</tal:demo>
1920- <tal:edge condition="is_edge">beta site</tal:edge>
1921- </span>
1922- </div>
1923-
1924- <div id="colophon">
1925- <a href="/+tour"
1926- tal:condition="not:request/lp:person">What is Launchpad?</a>
1927- <a href="/feedback"
1928- tal:condition="request/lp:person">Contact us</a> |
1929- <a href="https://help.launchpad.net/">Get help with Launchpad</a>
1930- </div>
1931- </div><!-- footer-->
1932+ <metal:footer
1933+ use-macro="context/@@+base-layout-macros/footer"/>
1934 </div><!-- yui-d0-->
1935
1936 <metal:lp-client-cache
1937
1938=== modified file 'lib/lp/bugs/stories/bugtracker/xx-bugtracker-handshake-tokens.txt'
1939--- lib/lp/bugs/stories/bugtracker/xx-bugtracker-handshake-tokens.txt 2009-06-12 16:36:02 +0000
1940+++ lib/lp/bugs/stories/bugtracker/xx-bugtracker-handshake-tokens.txt 2009-07-16 13:28:25 +0000
1941@@ -24,4 +24,4 @@
1942 >>> anon_browser.open(token_url)
1943 Traceback (most recent call last):
1944 ...
1945- HTTPError: HTTP Error 405: Method Not Allowed
1946+ httperror_seek_wrapper: HTTP Error 405: Method Not Allowed
1947
1948=== modified file 'lib/lp/bugs/stories/bugtracker/xx-bugtracker-remote-bug.txt'
1949--- lib/lp/bugs/stories/bugtracker/xx-bugtracker-remote-bug.txt 2009-08-11 06:29:52 +0000
1950+++ lib/lp/bugs/stories/bugtracker/xx-bugtracker-remote-bug.txt 2009-08-12 13:34:48 +0000
1951@@ -38,7 +38,7 @@
1952 >>> browser.open('http://launchpad.dev/bugs/bugtrackers/mozilla.org/99999')
1953 Traceback (most recent call last):
1954 ...
1955- HTTPError: HTTP Error 404: Not Found
1956+ httperror_seek_wrapper: HTTP Error 404: Not Found
1957 >>> browser.handleErrors = False
1958
1959
1960
1961=== modified file 'lib/lp/bugs/stories/bugwatches/xx-bugwatch-comments.txt'
1962--- lib/lp/bugs/stories/bugwatches/xx-bugwatch-comments.txt 2009-07-01 13:16:44 +0000
1963+++ lib/lp/bugs/stories/bugwatches/xx-bugwatch-comments.txt 2009-08-13 21:32:59 +0000
1964@@ -83,7 +83,7 @@
1965 Launchpad Beta Testers team such as sabdfl:
1966
1967 >>> sabdfl_browser.open(
1968- ... 'http://launchpad.dev/redfish/+bug/15/comments/1')
1969+ ... 'http://bugs.launchpad.dev/redfish/+bug/15/comments/1')
1970 >>> print_comments(sabdfl_browser.contents)
1971 <p>Package: gnome-volume-<wbr></wbr>manager<br />
1972 Version: 1.2.0-1<br />
1973@@ -125,11 +125,12 @@
1974 >>> logout()
1975
1976 >>> sabdfl_browser.open(
1977- ... 'http://launchpad.dev/redfish/+bug/15/comments/1')
1978+ ... 'http://bugs.launchpad.dev/redfish/+bug/15/comments/1')
1979 >>> print_comments(sabdfl_browser.contents)
1980
1981 Anonymous users can't see the comment either.
1982
1983- >>> anon_browser.open('http://launchpad.dev/redfish/+bug/15/comments/1')
1984+ >>> anon_browser.open(
1985+ ... 'http://bugs.launchpad.dev/redfish/+bug/15/comments/1')
1986 >>> print_comments(anon_browser.contents)
1987
1988
1989=== modified file 'lib/lp/code/stories/branches/xx-upload-directions.txt'
1990--- lib/lp/code/stories/branches/xx-upload-directions.txt 2009-04-17 10:32:16 +0000
1991+++ lib/lp/code/stories/branches/xx-upload-directions.txt 2009-08-13 21:32:59 +0000
1992@@ -45,7 +45,7 @@
1993 >>> instructions = find_tag_by_id(content, 'upload-directions')
1994 >>> print instructions.renderContents()
1995 Only
1996- <a href="/~name12">Sample Person</a>
1997+ <a href="http://launchpad.dev/~name12">Sample Person</a>
1998 can upload to this branch. If you are Sample Person please
1999 <a href="+login">log in</a> for upload directions.
2000
2001@@ -57,7 +57,7 @@
2002 >>> instructions = find_tag_by_id(content, 'upload-directions')
2003 >>> print instructions.renderContents()
2004 You cannot upload to this branch. Only
2005- <a href="/~name12">Sample Person</a>
2006+ <a href="http://launchpad.dev/~name12">Sample Person</a>
2007 can upload to this branch.
2008
2009 The user is the owner of the branch and logs in. The page gives the full upload
2010@@ -90,7 +90,7 @@
2011 >>> instructions = find_tag_by_id(content, 'ssh-key-directions')
2012 >>> print instructions.renderContents()
2013 To authenticate with the Launchpad branch upload service, you need to
2014- <a href="/~name12/+editsshkeys">
2015+ <a href="http://launchpad.dev/~name12/+editsshkeys">
2016 register a SSH key </a>.
2017
2018 Click the link and register a key.
2019@@ -149,7 +149,7 @@
2020 >>> instructions = find_tag_by_id(content, 'upload-directions')
2021 >>> print instructions.renderContents()
2022 Members of <a
2023- href="/~landscape-developers">Landscape
2024+ href="http://launchpad.dev/~landscape-developers">Landscape
2025 Developers</a> can upload to this branch. <a href="+login">Log in</a> for
2026 directions.
2027
2028@@ -161,7 +161,7 @@
2029 >>> instructions = find_tag_by_id(content, 'upload-directions')
2030 >>> print instructions.renderContents()
2031 You cannot upload to this branch. Members of <a
2032- href="/~landscape-developers">Landscape
2033+ href="http://launchpad.dev/~landscape-developers">Landscape
2034 Developers</a> can upload to this branch.
2035
2036 Finally, if the user is a member of the team, we display the same "you can
2037
2038=== modified file 'lib/lp/code/templates/project-branches.pt'
2039--- lib/lp/code/templates/project-branches.pt 2009-07-17 17:59:07 +0000
2040+++ lib/lp/code/templates/project-branches.pt 2009-08-10 19:04:22 +0000
2041@@ -6,24 +6,22 @@
2042 xml:lang="en"
2043 lang="en"
2044 dir="ltr"
2045- metal:use-macro="context/@@main_template/master"
2046+ metal:use-macro="view/macro:page/onecolumn"
2047 i18n:domain="launchpad"
2048 >
2049
2050 <body>
2051
2052-<metal:leftportlets fill-slot="portlets_one">
2053-</metal:leftportlets>
2054-
2055-<metal:rightportlets fill-slot="portlets_two">
2056-</metal:rightportlets>
2057-
2058-<metal:heading fill-slot="pageheading">
2059+<div metal:fill-slot="main" tal:define="branches view/branches">
2060+
2061+ <div style="float:right" id="floating-links"
2062+ tal:define="menu context/menu:overview">
2063+ <div tal:define="link menu/branch_visibility"
2064+ tal:condition="link/enabled"
2065+ tal:content="structure link/render" />
2066+ </div>
2067+
2068 <h1>Bazaar branches for <tal:project-name replace="context/displayname"/></h1>
2069-</metal:heading>
2070-
2071-<div metal:fill-slot="main"
2072- tal:define="branches view/branches">
2073
2074 <tal:branchlisting content="structure branches/@@+branch-listing" />
2075
2076
2077=== modified file 'lib/lp/registry/browser/configure.zcml'
2078--- lib/lp/registry/browser/configure.zcml 2009-08-12 13:20:10 +0000
2079+++ lib/lp/registry/browser/configure.zcml 2009-08-14 00:52:28 +0000
2080@@ -480,8 +480,11 @@
2081 <browser:menus
2082 classes="
2083 ProjectFacets
2084+ ProjectActionMenu
2085+ ProjectEditNavigationMenu
2086 ProjectOverviewMenu
2087 ProjectBountiesMenu
2088+ ProjectBugsMenu
2089 ProjectSeriesSpecificationsMenu
2090 ProjectSpecificationsMenu
2091 ProjectSetContextMenu
2092
2093=== modified file 'lib/lp/registry/browser/project.py'
2094--- lib/lp/registry/browser/project.py 2009-08-05 01:49:41 +0000
2095+++ lib/lp/registry/browser/project.py 2009-08-13 18:10:15 +0000
2096@@ -13,12 +13,14 @@
2097 'ProjectBountiesMenu',
2098 'ProjectBrandingView',
2099 'ProjectBreadcrumbBuilder',
2100+ 'ProjectBugsMenu',
2101 'ProjectEditView',
2102 'ProjectFacets',
2103 'ProjectMaintainerReassignmentView',
2104 'ProjectNavigation',
2105 'ProjectRdfView',
2106 'ProjectReviewView',
2107+ 'ProjectActionMenu',
2108 'ProjectOverviewMenu',
2109 'ProjectSeriesSpecificationsMenu',
2110 'ProjectSetBreadcrumbBuilder',
2111@@ -34,6 +36,7 @@
2112 from zope.component import getUtility
2113 from zope.event import notify
2114 from zope.formlib import form
2115+from zope.interface import implements, Interface
2116 from zope.schema import Choice
2117
2118 from z3c.ptcompat import ViewPageTemplateFile
2119@@ -41,6 +44,7 @@
2120 from canonical.cachedproperty import cachedproperty
2121 from canonical.launchpad import _
2122 from canonical.launchpad.webapp.interfaces import NotFoundError
2123+from canonical.launchpad.webapp.menu import NavigationMenu
2124 from lp.registry.interfaces.product import IProductSet
2125 from lp.registry.interfaces.project import (
2126 IProject, IProjectSeries, IProjectSet)
2127@@ -137,41 +141,33 @@
2128 def bugs(self):
2129 site = 'bugs'
2130 text = 'Bugs'
2131-
2132 return Link('', text, enabled=self.context.hasProducts(), site=site)
2133
2134 def answers(self):
2135 site = 'answers'
2136 text = 'Answers'
2137-
2138 return Link('', text, enabled=self.context.hasProducts(), site=site)
2139
2140 def specifications(self):
2141 site = 'blueprints'
2142 text = 'Blueprints'
2143-
2144 return Link('', text, enabled=self.context.hasProducts(), site=site)
2145
2146 def translations(self):
2147 site = 'translations'
2148 text = 'Translations'
2149-
2150 return Link('', text, enabled=self.context.hasProducts(), site=site)
2151
2152
2153-class ProjectOverviewMenu(ApplicationMenu):
2154-
2155- usedfor = IProject
2156- facet = 'overview'
2157- links = [
2158- 'edit', 'branding', 'driver', 'reassign', 'top_contributors',
2159- 'mentorship', 'announce', 'announcements', 'administer',
2160- 'branch_visibility', 'rdf', 'subscribe']
2161-
2162- @enabled_with_permission('launchpad.Edit')
2163- def edit(self):
2164- text = 'Change details'
2165- return Link('+edit', text, icon='edit')
2166+class ProjectAdminMenuMixin:
2167+
2168+ @enabled_with_permission('launchpad.Admin')
2169+ def administer(self):
2170+ text = 'Administer'
2171+ return Link('+review', text, icon='edit')
2172+
2173+
2174+class ProjectEditMenuMixin(ProjectAdminMenuMixin):
2175
2176 @enabled_with_permission('launchpad.Edit')
2177 def branding(self):
2178@@ -181,14 +177,30 @@
2179 @enabled_with_permission('launchpad.Edit')
2180 def reassign(self):
2181 text = 'Change maintainer'
2182- return Link('+reassign', text, icon='edit')
2183+ summary = 'Change the maintainer of this project group'
2184+ return Link('+reassign', text, summary, icon='edit')
2185
2186 @enabled_with_permission('launchpad.Edit')
2187 def driver(self):
2188 text = 'Appoint driver'
2189- summary = 'Someone with permission to set goals for all projects'
2190+ summary = 'Appoint the driver of this project group'
2191 return Link('+driver', text, summary, icon='edit')
2192
2193+
2194+class ProjectOverviewMenu(ProjectEditMenuMixin, ApplicationMenu):
2195+
2196+ usedfor = IProject
2197+ facet = 'overview'
2198+ links = [
2199+ 'branding', 'driver', 'reassign', 'top_contributors', 'mentorship',
2200+ 'announce', 'announcements', 'branch_visibility', 'rdf',
2201+ 'new_product', 'administer', 'milestones']
2202+
2203+ @enabled_with_permission('launchpad.Edit')
2204+ def new_product(self):
2205+ text = 'Register another project in %s' % self.context.displayname
2206+ return Link('+newproduct', text, icon='edit')
2207+
2208 def top_contributors(self):
2209 text = 'More contributors'
2210 return Link('+topcontributors', text, icon='info')
2211@@ -213,26 +225,59 @@
2212 enabled = bool(self.context.getAnnouncements())
2213 return Link('+announcements', text, enabled=enabled)
2214
2215+ def milestones(self):
2216+ text = 'See all milestones'
2217+ return Link('+milestones', text)
2218+
2219 def rdf(self):
2220 text = structured(
2221 'Download <abbr title="Resource Description Framework">'
2222 'RDF</abbr> metadata')
2223- return Link('+rdf', text, icon='download')
2224-
2225- @enabled_with_permission('launchpad.Admin')
2226- def administer(self):
2227- text = 'Administer'
2228- return Link('+review', text, icon='edit')
2229+ return Link('+rdf', text, icon='download-icon')
2230
2231 @enabled_with_permission('launchpad.Admin')
2232 def branch_visibility(self):
2233 text = 'Define branch visibility'
2234 return Link('+branchvisibility', text, icon='edit', site='mainsite')
2235
2236+
2237+class IProjectActionMenu(Interface):
2238+ """Marker interface for views that use ProjectActionMenu."""
2239+
2240+
2241+class ProjectActionMenu(ProjectAdminMenuMixin, NavigationMenu):
2242+
2243+ usedfor = IProjectActionMenu
2244+ facet = 'overview'
2245+ title = 'Action menu'
2246+ links = ('subscribe', 'edit', 'administer')
2247+
2248+ # XXX: salgado, bug=412178, 2009-08-10: This should be shown in the +index
2249+ # page of the project's bugs facet, but that would require too much work
2250+ # and I just want to convert this page to 3.0, so I'll leave it here for
2251+ # now.
2252 def subscribe(self):
2253 text = 'Subscribe to bug mail'
2254 return Link('+subscribe', text, icon='edit')
2255
2256+ @enabled_with_permission('launchpad.Edit')
2257+ def edit(self):
2258+ text = 'Change details'
2259+ return Link('+edit', text, icon='edit')
2260+
2261+
2262+class IProjectEditMenu(Interface):
2263+ """A marker interface for the 'Change details' navigation menu."""
2264+
2265+
2266+class ProjectEditNavigationMenu(NavigationMenu, ProjectEditMenuMixin):
2267+ """A sub-menu for different aspects of editing a Project's details."""
2268+
2269+ usedfor = IProjectEditMenu
2270+ facet = 'overview'
2271+ title = 'Change project group'
2272+ links = ('branding', 'reassign', 'driver', 'administer')
2273+
2274
2275 class ProjectBountiesMenu(ApplicationMenu):
2276
2277@@ -286,12 +331,24 @@
2278 return Link('+addquestion', text, icon='add')
2279
2280
2281+class ProjectBugsMenu(ApplicationMenu):
2282+
2283+ usedfor = IProject
2284+ facet = 'bugs'
2285+ links = ['new']
2286+
2287+ def new(self):
2288+ text = 'Report a Bug'
2289+ return Link('+filebug', text, icon='add')
2290+
2291+
2292 class ProjectView(HasAnnouncementsView, FeedsMixin):
2293- pass
2294+ implements(IProjectActionMenu)
2295
2296
2297 class ProjectEditView(LaunchpadEditFormView):
2298 """View class that lets you edit a Project object."""
2299+ implements(IProjectEditMenu)
2300
2301 label = "Change project group details"
2302 schema = IProject
2303
2304=== modified file 'lib/lp/registry/browser/tests/project-add-views.txt'
2305--- lib/lp/registry/browser/tests/project-add-views.txt 2009-05-12 08:11:06 +0000
2306+++ lib/lp/registry/browser/tests/project-add-views.txt 2009-08-13 19:36:01 +0000
2307@@ -310,4 +310,5 @@
2308
2309 >>> for error in view.errors:
2310 ... print error
2311- ('name', 'URL', badger is already used by another project)
2312+ ('name', 'URL',
2313+ LaunchpadValidationError(u'badger is already used by another project'))
2314
2315=== modified file 'lib/lp/registry/doc/product-widgets.txt'
2316--- lib/lp/registry/doc/product-widgets.txt 2009-07-01 13:16:44 +0000
2317+++ lib/lp/registry/doc/product-widgets.txt 2009-08-13 19:36:01 +0000
2318@@ -301,7 +301,7 @@
2319 ... print
2320
2321 >>> license_widget.getInputValue()
2322- Set([<DBItem License.GNU_GPL_V2, (130) ...>])
2323+ set([<DBItem License.GNU_GPL_V2, (130) ...>])
2324
2325 >>> print_checked_items(license_widget())
2326 [ ] Apache License ...
2327
2328=== modified file 'lib/lp/registry/interfaces/product.py'
2329--- lib/lp/registry/interfaces/product.py 2009-08-10 17:08:27 +0000
2330+++ lib/lp/registry/interfaces/product.py 2009-08-13 15:22:00 +0000
2331@@ -24,7 +24,6 @@
2332
2333
2334 import re
2335-import sets
2336
2337 from textwrap import dedent
2338
2339@@ -955,8 +954,7 @@
2340 title=_('Licenses'),
2341 value_type=Choice(vocabulary=License),
2342 required=False,
2343- # Zope requires sets.Set() instead of the builtin set().
2344- default=sets.Set(
2345+ default=set(
2346 [License.OTHER_PROPRIETARY, License.OTHER_OPEN_SOURCE]))
2347
2348 has_zero_licenses = Choice(
2349
2350=== modified file 'lib/lp/registry/stories/announcements/xx-announcements.txt'
2351--- lib/lp/registry/stories/announcements/xx-announcements.txt 2009-08-11 17:32:21 +0000
2352+++ lib/lp/registry/stories/announcements/xx-announcements.txt 2009-08-13 18:10:15 +0000
2353@@ -265,7 +265,7 @@
2354
2355 >>> anon_browser.open('http://launchpad.dev/apache')
2356 >>> count_show_links(anon_browser.contents)
2357- 2
2358+ 1
2359 >>> anon_browser.open('http://launchpad.dev/tomcat')
2360 >>> count_show_links(anon_browser.contents)
2361 1
2362
2363=== modified file 'lib/lp/registry/stories/distribution/xx-distribution-countrymirrors.txt'
2364--- lib/lp/registry/stories/distribution/xx-distribution-countrymirrors.txt 2009-07-23 13:44:13 +0000
2365+++ lib/lp/registry/stories/distribution/xx-distribution-countrymirrors.txt 2009-08-05 18:52:52 +0000
2366@@ -60,5 +60,5 @@
2367 >>> browser.open('http://launchpad.dev/debian/+countrymirrors-archive')
2368 Traceback (most recent call last):
2369 ...
2370- HTTPError: HTTP Error 404: Not Found
2371+ httperror_seek_wrapper: HTTP Error 404: Not Found
2372
2373
2374=== modified file 'lib/lp/registry/stories/gpg-coc/98-cocacknowledge.txt'
2375--- lib/lp/registry/stories/gpg-coc/98-cocacknowledge.txt 2009-05-12 01:39:29 +0000
2376+++ lib/lp/registry/stories/gpg-coc/98-cocacknowledge.txt 2009-08-13 21:32:59 +0000
2377@@ -24,12 +24,8 @@
2378 >>> admin_browser.getControl(name='searchfor').value = ["all"]
2379 >>> admin_browser.getControl(name='name').value = "mark"
2380 >>> admin_browser.getControl(name='search').click()
2381- >>> 'paper submission accepted by Foo Bar' in admin_browser.contents
2382- True
2383- >>> '<a href="http://launchpad.dev/~sabdfl"' in admin_browser.contents
2384- True
2385- >>> '[ACTIVE]' in admin_browser.contents
2386- True
2387+ >>> print extract_text(find_tag_by_id(admin_browser.contents, 'matches'))
2388+ Mark ... paper submission accepted by Foo Bar [ACTIVE]
2389
2390 Test if the advertisement email was sent:
2391
2392
2393=== modified file 'lib/lp/registry/stories/mailinglists/lifecycle.txt'
2394--- lib/lp/registry/stories/mailinglists/lifecycle.txt 2009-07-23 13:44:13 +0000
2395+++ lib/lp/registry/stories/mailinglists/lifecycle.txt 2009-08-06 19:11:48 +0000
2396@@ -580,7 +580,7 @@
2397 >>> admin_browser.getControl(name='field.aardvarks').value = ['approve']
2398 >>> admin_browser.getControl('Submit').click()
2399 >>> act()
2400- >>> browser.reload()
2401+ >>> browser.open(browser.url) # A `reload` would resubmit.
2402 >>> browser.getLink('Mailing list archive')
2403 <Link text='Mailing list archive'
2404 url='http://lists.launchpad.dev/aardvarks'>
2405@@ -618,7 +618,7 @@
2406 >>> admin_browser.getControl(name='field.antelopes').value = ['approve']
2407 >>> admin_browser.getControl('Submit').click()
2408 >>> act()
2409- >>> user_browser.reload()
2410+ >>> user_browser.open(user_browser.url) # A `reload` would resubmit.
2411 >>> user_browser.getLink('Mailing list archive')
2412 <Link text='Mailing list archive'
2413 url='http://lists.launchpad.dev/antelopes'>
2414
2415=== modified file 'lib/lp/registry/stories/milestone/object-milestones.txt'
2416--- lib/lp/registry/stories/milestone/object-milestones.txt 2009-08-12 01:12:12 +0000
2417+++ lib/lp/registry/stories/milestone/object-milestones.txt 2009-08-14 00:52:28 +0000
2418@@ -88,7 +88,9 @@
2419
2420 === Projects ===
2421
2422-The main project page has a portlet "Active milestones":
2423+The project "All milestones" page lists all milestones for all products and
2424+series, including the inactive ones. They do not include the bug and blueprint
2425+counts (because they are costly to retrieve).
2426
2427 >>> from canonical.launchpad.ftests import login, logout
2428 >>> from lp.registry.tests.test_project_milestone import (
2429@@ -98,15 +100,6 @@
2430 >>> test_helper.setUpProjectMilestoneTests()
2431 >>> logout()
2432 >>> anon_browser.open('http://launchpad.dev/gnome')
2433- >>> print milestones_in_portlet(anon_browser)
2434- 2011-04-01 1.2
2435- 2010-04-02 1.1.
2436- 2010-04-01 1.1
2437-
2438-The project "All milestones" page lists all milestones for all products and
2439-series, including the inactive ones. They do not include the bug and blueprint
2440-counts (because they are costly to retrieve).
2441-
2442 >>> anon_browser.getLink('See all milestones').click()
2443 >>> print all_milestones(anon_browser)
2444 GNOME 2.1.6 None This is an inactive milestone
2445
2446=== modified file 'lib/lp/registry/stories/product/xx-launchpad-project-search.txt'
2447--- lib/lp/registry/stories/product/xx-launchpad-project-search.txt 2009-06-11 19:21:46 +0000
2448+++ lib/lp/registry/stories/product/xx-launchpad-project-search.txt 2009-08-14 03:21:52 +0000
2449@@ -118,7 +118,7 @@
2450
2451 >>> anon_browser.open('http://launchpad.dev/projects?text=ubuntu')
2452 >>> print_search_results(anon_browser)
2453- sprite distribution Ubuntu
2454+ bg-image Ubuntu
2455 sprite distribution ubuntutest
2456 sprite product Evolution
2457 sprite product Tomcat
2458
2459=== modified file 'lib/lp/registry/stories/project/xx-project-driver.txt'
2460--- lib/lp/registry/stories/project/xx-project-driver.txt 2008-07-02 12:51:24 +0000
2461+++ lib/lp/registry/stories/project/xx-project-driver.txt 2009-08-13 18:10:15 +0000
2462@@ -26,6 +26,6 @@
2463
2464 Sample Person is listed as the driver of the project.
2465
2466- >>> for tag in find_tags_by_class(browser.contents, 'summary'):
2467- ... tag.find(text='Driver:').findNext('a').string
2468- u'Sample Person'
2469+ >>> print extract_text(find_tag_by_id(browser.contents, 'driver'))
2470+ Driver: Sample Person
2471+ Appoint driver
2472
2473=== modified file 'lib/lp/registry/stories/project/xx-project-edit.txt'
2474--- lib/lp/registry/stories/project/xx-project-edit.txt 2009-08-01 00:02:55 +0000
2475+++ lib/lp/registry/stories/project/xx-project-edit.txt 2009-08-13 18:10:15 +0000
2476@@ -53,21 +53,20 @@
2477
2478 The project summary shows the status as reviewed for admins only.
2479
2480- >>> for tag in find_tags_by_class(admin_browser.contents, 'summary'):
2481- ... print extract_text(
2482- ... tag.find(text='Project group status:').findParent('tr'))
2483- Project group status:
2484- Active
2485- Reviewed
2486+ >>> print extract_text(find_tag_by_id(admin_browser.contents, 'status'))
2487+ Status: Active Reviewed
2488
2489 Other users cannot see the Project group status in the details portlet.
2490
2491 >>> anon_browser.open('http://launchpad.dev/new-name')
2492 >>> print extract_text(
2493 ... find_tag_by_id(anon_browser.contents, 'portlet-details'))
2494+ Project group information
2495 Maintainer: Sample Person
2496 Driver: Not yet appointed
2497 Bug tracker: The Mozilla.org Bug Tracker
2498+ Registered ... by Sample Person
2499+ Download RDF metadata
2500
2501 Administrators can also change the maintainer and registrant independent
2502 of each other, as well as adding aliases to the project group.
2503@@ -90,17 +89,14 @@
2504
2505 The project maintainer and registrant are now updated.
2506
2507- >>> for tag in find_tags_by_class(admin_browser.contents, 'summary'):
2508- ... print extract_text(
2509- ... tag.find(text='Maintainer:').findParent('tr'))
2510- Maintainer:
2511- Celso Providelo
2512+ >>> print extract_text(
2513+ ... find_tag_by_id(admin_browser.contents, 'maintainer'))
2514+ Maintainer: Celso Providelo
2515+ Change maintainer
2516
2517- >>> tag = find_tag_by_id(admin_browser.contents, 'portlet-lifecycle')
2518 >>> print extract_text(
2519- ... tag.find(text='Registered by:').findParent('tr'))
2520- Registered by:
2521- David Allouche
2522+ ... find_tag_by_id(admin_browser.contents, 'registrant'))
2523+ Registered ... by David Allouche
2524
2525 The registrant really should only be a person, not a team, but that
2526 constraint has to be relaxed to account for old data where we do have
2527@@ -111,15 +107,6 @@
2528 >>> admin_browser.getControl('Registrant').value = 'registry'
2529 >>> admin_browser.getControl('Change').click()
2530
2531- >>> for tag in find_tags_by_class(admin_browser.contents, 'summary'):
2532- ... print extract_text(
2533- ... tag.find(text='Maintainer:').findParent('tr'))
2534- Maintainer:
2535- Celso Providelo
2536-
2537- >>> tag = find_tag_by_id(admin_browser.contents, 'portlet-lifecycle')
2538 >>> print extract_text(
2539- ... tag.find(text='Registered by:').findParent('tr'))
2540- Registered by:
2541- Registry Administrators
2542-
2543+ ... find_tag_by_id(admin_browser.contents, 'registrant'))
2544+ Registered ... by Registry Administrators
2545
2546=== modified file 'lib/lp/registry/stories/project/xx-project-index.txt'
2547--- lib/lp/registry/stories/project/xx-project-index.txt 2009-08-11 17:32:21 +0000
2548+++ lib/lp/registry/stories/project/xx-project-index.txt 2009-08-13 18:10:15 +0000
2549@@ -23,28 +23,28 @@
2550 >>> anon_browser.title
2551 'The GNOME Project in Launchpad'
2552
2553-The page lists the member projects.
2554+The page lists the member projects, together with the releases/milestones of
2555+its development focus.
2556
2557 >>> print extract_text(find_tag_by_id(anon_browser.contents, 'products'))
2558 Projects
2559 Gnome Applets ...
2560 Evolution ...
2561- Releases: 2.1.6
2562+ trunk series: 2.1.6,
2563 GNOME Terminal ...
2564- Milestones: 2.30.0
2565 gnomebaker ...
2566- Releases: 2.1.7
2567- Milestones: 2.30.1, 2.1.7
2568+ trunk series: 2.1.7, 2.1.7
2569 NetApplet ...
2570- Releases: 1.0
2571+ trunk series: 1.0,
2572+ ...
2573
2574 And the projects, milestone, and releases are linked.
2575
2576 >>> print anon_browser.getLink('gnomebaker').url
2577 http://launchpad.dev/gnomebaker
2578
2579- >>> print anon_browser.getLink('2.30.1').url
2580- http://launchpad.dev/gnomebaker/+milestone/2.30.1
2581+ >>> print anon_browser.getLink('1.0').url
2582+ http://launchpad.dev/netapplet/trunk/1.0
2583
2584 >>> print anon_browser.getLink('2.1.7').url
2585 http://launchpad.dev/gnomebaker/trunk/2.1.7
2586
2587=== modified file 'lib/lp/registry/templates/codeofconduct-admin.pt'
2588--- lib/lp/registry/templates/codeofconduct-admin.pt 2009-07-22 14:19:22 +0000
2589+++ lib/lp/registry/templates/codeofconduct-admin.pt 2009-08-13 21:32:59 +0000
2590@@ -71,7 +71,7 @@
2591 <tal:results condition="view/search">
2592 <h2>Results</h2>
2593
2594- <ul tal:condition="view/results">
2595+ <ul id="matches" tal:condition="view/results">
2596 <li tal:repeat="code view/results">
2597 <a tal:replace="structure code/owner/fmt:link">OWNER</a>
2598 <a tal:attributes="href code/id"
2599
2600=== modified file 'lib/lp/registry/templates/project-details.pt'
2601--- lib/lp/registry/templates/project-details.pt 2009-07-17 17:59:07 +0000
2602+++ lib/lp/registry/templates/project-details.pt 2009-08-13 18:10:15 +0000
2603@@ -4,53 +4,62 @@
2604 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
2605 omit-tag="">
2606
2607-<div class="portlet" id="portlet-details">
2608- <div class="portletBody portletContent">
2609- <table class="summary">
2610- <tbody>
2611- <tr tal:condition="context/required:launchpad.Admin">
2612- <th>Project group status:</th>
2613- <td>
2614- <tal:block condition="context/active">Active</tal:block>
2615- <tal:block condition="not: context/active">Disabled</tal:block>
2616- <tal:block condition="context/reviewed"><br />Reviewed</tal:block>
2617- <tal:block condition="context/icon"><br />Branded
2618- <tal:icon replace="structure context/image:icon" />
2619- </tal:block>
2620- </td>
2621- </tr>
2622- <tr>
2623- <th>Maintainer:</th>
2624- <td>
2625- <a
2626- tal:content="context/owner/displayname"
2627- tal:attributes="href context/owner/fmt:url"
2628- >ownername</a>
2629- </td>
2630- </tr>
2631- <tr>
2632- <th>Driver:</th>
2633- <td tal:condition="not:context/driver">Not yet appointed</td>
2634- <td tal:condition="context/driver">
2635- <a
2636- tal:content="context/driver/displayname"
2637- tal:attributes="href context/driver/fmt:url"
2638- >Carlos Perello</a>
2639- </td>
2640- </tr>
2641- <tr>
2642- <th>Bug tracker:</th>
2643- <td>
2644- <a
2645- tal:condition="context/bugtracker"
2646- tal:content="context/bugtracker/title"
2647- tal:attributes="href context/bugtracker/fmt:url"
2648- >tracker title</a>
2649- <tal:none condition="not:context/bugtracker">None specified</tal:none>
2650- </td>
2651- </tr>
2652- </tbody>
2653- </table>
2654+<div id="portlet-details" class="portlet">
2655+ <h2>Project group information</h2>
2656+ <div class="two-column-list" tal:define="overview_menu context/menu:overview">
2657+ <dl tal:condition="context/required:launchpad.Admin" id="status">
2658+ <dt>Status:</dt>
2659+ <dd>
2660+ <tal:block condition="context/active">Active</tal:block>
2661+ <tal:block condition="not: context/active">Disabled</tal:block>
2662+ <tal:block condition="context/reviewed"><br />Reviewed</tal:block>
2663+ <tal:block condition="context/icon"><br />Branded
2664+ <tal:icon replace="structure context/image:icon" />
2665+ </tal:block>
2666+ </dd>
2667+ </dl>
2668+ <dl id="maintainer">
2669+ <dt>Maintainer:</dt>
2670+ <dd>
2671+ <a tal:replace="structure context/owner/fmt:link" />
2672+ <tal:edit-maintainer
2673+ content="structure overview_menu/reassign/fmt:icon" />
2674+ </dd>
2675+ </dl>
2676+ <dl id="driver">
2677+ <dt>Driver:</dt>
2678+ <dd>
2679+ <tal:no-driver condition="not:context/driver">
2680+ Not yet appointed
2681+ </tal:no-driver>
2682+ <tal:has-driver condition="context/driver">
2683+ <a tal:replace="structure context/driver/fmt:link">Driver</a>
2684+ </tal:has-driver>
2685+ <tal:edit-maintainer
2686+ content="structure overview_menu/driver/fmt:icon" />
2687+ </dd>
2688+ </dl>
2689+ <dl id="bug-tracker">
2690+ <dt>Bug tracker:</dt>
2691+ <dd>
2692+ <a
2693+ tal:condition="context/bugtracker"
2694+ tal:content="context/bugtracker/title"
2695+ tal:attributes="href context/bugtracker/fmt:url"
2696+ >tracker title</a>
2697+ <tal:none condition="not:context/bugtracker">None specified</tal:none>
2698+ </dd>
2699+ </dl>
2700 </div>
2701+ <p id="registrant">
2702+ Registered
2703+ <tal:created replace="context/datecreated/fmt:approximatedate" />
2704+ by <tal:registrant replace="structure context/registrant/fmt:link" />
2705+ </p>
2706+ <ul class="horizontal">
2707+ <li>
2708+ <a tal:replace="structure context/menu:overview/rdf/fmt:link-icon" />
2709+ </li>
2710+ </ul>
2711 </div>
2712 </tal:root>
2713
2714=== modified file 'lib/lp/registry/templates/project-edit.pt'
2715--- lib/lp/registry/templates/project-edit.pt 2009-07-18 00:05:49 +0000
2716+++ lib/lp/registry/templates/project-edit.pt 2009-08-05 19:26:37 +0000
2717@@ -3,20 +3,14 @@
2718 xmlns:tal="http://xml.zope.org/namespaces/tal"
2719 xmlns:metal="http://xml.zope.org/namespaces/metal"
2720 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
2721- xml:lang="en"
2722- lang="en"
2723- dir="ltr"
2724- metal:use-macro="context/@@main_template/master"
2725+ metal:use-macro="view/macro:page/main_only"
2726 i18n:domain="launchpad"
2727 >
2728 <body>
2729- <metal:heading fill-slot="heading">
2730- <h1>Change project group details</h1>
2731- </metal:heading>
2732-
2733 <div metal:fill-slot="main">
2734
2735- <div metal:use-macro="context/@@launchpad_form/form">
2736+ <div class="top-portlet"
2737+ metal:use-macro="context/@@launchpad_form/form">
2738
2739 <p metal:fill-slot="extra_info">
2740 Avoid changing the Name,
2741@@ -28,6 +22,7 @@
2742
2743 </div>
2744
2745+ <tal:menu replace="structure view/@@+related-pages" />
2746 </div>
2747
2748 </body>
2749
2750=== modified file 'lib/lp/registry/templates/project-index.pt'
2751--- lib/lp/registry/templates/project-index.pt 2009-07-17 17:59:07 +0000
2752+++ lib/lp/registry/templates/project-index.pt 2009-08-13 18:10:15 +0000
2753@@ -3,43 +3,37 @@
2754 xmlns:tal="http://xml.zope.org/namespaces/tal"
2755 xmlns:metal="http://xml.zope.org/namespaces/metal"
2756 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
2757- xml:lang="en"
2758- lang="en"
2759- dir="ltr"
2760- metal:use-macro="view/macro:page/default"
2761+ metal:use-macro="view/macro:page/main_side"
2762 i18n:domain="launchpad"
2763 >
2764 <body>
2765
2766- <metal:portlets fill-slot="portlets">
2767- <tal:portlet tal:replace="structure context/@@+portlet-lifecycle" />
2768- <tal:portlet tal:replace="structure context/@@+portlet-milestones" />
2769- </metal:portlets>
2770-
2771- <div metal:fill-slot="main">
2772-
2773- <p id="project-inactive" tal:condition="not: context/active" class="warning message">
2774- This project is currently inactive <a href="+review">(change this)</a>
2775- </p>
2776-
2777- <tal:block condition="view/required:launchpad.Edit">
2778- <p tal:condition="not: context/products" class="warning message">
2779- There are no projects registered for
2780- <span tal:replace="context/displayname">project displayname</span>.
2781- <br />
2782- You need to <a href="+newproduct">register another project that is
2783- part of <tal:project replace="context/displayname" /></a> or associate
2784- an existing project with it.
2785+ <tal:heading metal:fill-slot="heading">
2786+ <h1 tal:content="context/title">Mozilla</h1>
2787+ </tal:heading>
2788+
2789+ <tal:main metal:fill-slot="main">
2790+
2791+ <div class="top-portlet">
2792+ <p id="project-inactive" class="warning message"
2793+ tal:condition="not: context/active">
2794+ This project is currently inactive
2795+ <a tal:attributes="href context/menu:overview/administer/url"
2796+ >(change this)</a>
2797 </p>
2798- </tal:block>
2799-
2800- <div
2801- style="width: 200px; height: 200px; float: right;"
2802- tal:content="structure context/image:mugshot"
2803- />
2804- <h1 tal:content="context/title">Mozilla</h1>
2805-
2806- <div class="description" tal:content="context/summary">
2807+
2808+ <tal:block condition="view/required:launchpad.Edit">
2809+ <p tal:condition="not: context/products" class="warning message">
2810+ There are no projects registered for
2811+ <span tal:replace="context/displayname">project displayname</span>.
2812+ <br />
2813+ You need to <a href="+newproduct">register another project that is
2814+ part of <tal:project replace="context/displayname" /></a> or associate
2815+ an existing project with it.
2816+ </p>
2817+ </tal:block>
2818+
2819+ <div class="summary" tal:content="context/summary">
2820 This is the project group Summary, which should be a single paragraph
2821 summarising the project group's purpose.
2822 </div>
2823@@ -58,92 +52,97 @@
2824 Apache Server, and mention other projects such as APR.
2825 </div>
2826
2827- <ul tal:condition="context/homepageurl">
2828- <li>
2829- <a rel="nofollow"
2830- tal:attributes="href context/homepageurl;
2831- class string:sprite external-link">
2832- <strong>Visit the <tal:name replace="context/title" />
2833- home page &raquo;</strong>
2834- </a>
2835- </li>
2836- </ul>
2837- <ul class="buttons" tal:condition="context/products">
2838- <li>
2839- <a href="+filebug">
2840- <img alt="Report a bug" src="/+icing/but-sml-reportabug.gif" />
2841- </a>
2842- </li>
2843- <li tal:content="structure context/@@+ask-a-question-button" />
2844- <li tal:content="structure context/@@+help-translate-button" />
2845- <li tal:define="has_mentoring context/mentoring_offers/count">
2846- <a href="+mentoring" tal:condition="has_mentoring">
2847- <img alt="Mentoring available"
2848- src="/+icing/but-sml-mentoring.gif"/>
2849- </a>
2850- <a href="+mentoring" tal:condition="not: has_mentoring">
2851- <img alt="No mentoring available"
2852- src="/+icing/but-sml-mentoring-off.gif" />
2853- </a>
2854- </li>
2855- </ul>
2856- <div class="left">
2857- <div
2858- class="section"
2859- tal:content="structure context/@@+portlet-coming-sprints"
2860- />
2861- <div
2862- class="section"
2863- tal:content="structure context/@@+portlet-latestannouncements"
2864- />
2865- <div class="portlet" id="products">
2866+ <ul id="external-links" class="horizontal"
2867+ tal:condition="context/homepageurl">
2868+ <li>
2869+ <a rel="nofollow" class="sprite external-link"
2870+ tal:attributes="href context/homepageurl">Home page</a>
2871+ </li>
2872+ </ul>
2873+ </div>
2874+
2875+ <div class="yui-g">
2876+ <div class="yui-u first">
2877+ <tal:details replace="structure context/@@+details" />
2878+ </div>
2879+ <div class="yui-u" id="products">
2880+ <div class="portlet">
2881 <h2>Projects</h2>
2882- <p tal:condition="not: context/products">
2883+ <p tal:condition="not: context/products">
2884 There are no projects registered for
2885 <span tal:replace="context/displayname">project displayname</span>.
2886 </p>
2887- <div
2888- tal:condition="context/products"
2889- tal:repeat="product context/products">
2890- <tal:link replace="structure product/fmt:link" />
2891- <div tal:condition="product/releases/count">
2892- Releases:
2893- <tal:release repeat="release product/releases">
2894- <a
2895- tal:attributes="href release/fmt:url"
2896- tal:content="release/version"
2897- >version</a><tal:comma
2898- condition="not:repeat/release/end">,</tal:comma>
2899- </tal:release>
2900- </div>
2901- <div tal:condition="product/milestones">
2902- Milestones:
2903- <tal:milestone repeat="milestone product/milestones">
2904- <a
2905- tal:attributes="href milestone/fmt:url"
2906- tal:content="milestone/name"
2907- >name</a><tal:comma
2908- condition="not:repeat/milestone/end">,</tal:comma>
2909- </tal:milestone>
2910- </div>
2911- </div>
2912- <tal:block condition="context/required:launchpad.Edit">
2913- <a href="+newproduct">&raquo; Register another project that is
2914- part of <tal:project replace="context/displayname" /></a>
2915- </tal:block>
2916+ <dl tal:condition="context/products"
2917+ tal:repeat="product context/products">
2918+ <dt><a tal:replace="structure product/fmt:link">product</a></dt>
2919+ <dd tal:define="dev_focus product/development_focus">
2920+ <a tal:attributes="href dev_focus/fmt:url"
2921+ tal:content="dev_focus/name">trunk</a>
2922+ series:
2923+ <tal:releases repeat="release dev_focus/releases">
2924+ <strong>
2925+ <a tal:attributes="href release/fmt:url"
2926+ tal:content="release/version">release
2927+ </a></strong>,
2928+ </tal:releases>
2929+ <tal:releases repeat="milestone dev_focus/milestones">
2930+ <strong>
2931+ <a tal:attributes="href milestone/fmt:url"
2932+ tal:content="milestone/name">milestone
2933+ </a></strong><tal:comma
2934+ condition="not:repeat/milestone/end">,</tal:comma>
2935+ </tal:releases>
2936+ </dd>
2937+ </dl>
2938+ <ul>
2939+ <li>
2940+ <a tal:replace="structure context/menu:overview/milestones/fmt:link" />
2941+ </li>
2942+ <li tal:condition="context/menu:overview/new_product/enabled">
2943+ <a tal:replace="structure context/menu:overview/new_product/fmt:link" />
2944+ </li>
2945+ </ul>
2946 </div>
2947- <div tal:replace="structure context/@@+portlet-latestspecs" />
2948- </div>
2949- <div class="right">
2950- <div tal:replace="structure context/@@+details" />
2951- <div tal:replace="structure context/@@+portlet-top-contributors" />
2952- <div tal:replace="structure context/@@+portlet-latestbugs" />
2953- <div
2954- class="section"
2955- tal:content="structure context/@@+portlet-latestquestions"
2956- />
2957- </div>
2958- </div>
2959+ </div>
2960+ </div>
2961+ <div class="yui-g">
2962+ <div class="yui-u first">
2963+ <tal:bugs content="structure context/@@+portlet-latestbugs" />
2964+ <tal:specs content="structure context/@@+portlet-latestspecs" />
2965+ <tal:sprints content="structure context/@@+portlet-coming-sprints" />
2966+ </div>
2967+ <div class="yui-u">
2968+ <tal:questions content="structure context/@@+portlet-latestquestions" />
2969+ <tal:contributors content="structure context/@@+portlet-top-contributors" />
2970+ </div>
2971+ </div>
2972+ </tal:main>
2973+
2974+ <tal:side metal:fill-slot="side">
2975+ <div id="object-actions" class="top-portlet">
2976+ <tal:menu replace="structure view/@@+global-actions" />
2977+ </div>
2978+ <div id="involvement" class="portlet involvement"
2979+ tal:condition="context/products">
2980+ <h2>Get Involved</h2>
2981+ <ul>
2982+ <li>
2983+ <a class="bugs"
2984+ tal:attributes="href context/menu:bugs/new/url">Report a Bug</a>
2985+ </li>
2986+ <li>
2987+ <a class="question"
2988+ tal:attributes="href context/menu:answers/new/url">Ask a question</a>
2989+ </li>
2990+ <li>
2991+ <a class="translate"
2992+ tal:attributes="href context/menu:translations/overview/url">Help translate</a>
2993+ </li>
2994+ </ul>
2995+ </div>
2996+ <tal:portlet tal:replace="structure context/@@+portlet-latestannouncements" />
2997+ </tal:side>
2998+
2999 </body>
3000 </html>
3001
3002
3003=== modified file 'lib/lp/soyuz/browser/tests/archive-views.txt'
3004--- lib/lp/soyuz/browser/tests/archive-views.txt 2009-07-30 01:24:18 +0000
3005+++ lib/lp/soyuz/browser/tests/archive-views.txt 2009-08-13 19:36:01 +0000
3006@@ -932,7 +932,9 @@
3007 >>> print archive_widget.getInputValue()
3008 Traceback (most recent call last):
3009 ...
3010- WidgetInputError: ('destination_archive', u'Destination PPA', )
3011+ WidgetInputError: ('destination_archive',
3012+ u'Destination PPA',
3013+ RequiredMissing())
3014
3015
3016 === Copy private files to public archives ===
3017
3018=== modified file 'lib/lp/soyuz/browser/tests/binarypackagerelease-views.txt'
3019--- lib/lp/soyuz/browser/tests/binarypackagerelease-views.txt 2009-08-13 19:20:04 +0000
3020+++ lib/lp/soyuz/browser/tests/binarypackagerelease-views.txt 2009-08-13 19:51:29 +0000
3021@@ -1,6 +1,6 @@
3022 = BinaryPackageRelease Pages =
3023
3024- >>> from zope.component import queryMultiAdapter
3025+ >>> from zope.component import getMultiAdapter
3026 >>> from zope.publisher.browser import TestRequest
3027 >>> from canonical.launchpad.database import BinaryPackageRelease
3028
3029@@ -16,7 +16,7 @@
3030
3031 Let's instantiate the view for +portlet-details:
3032
3033- >>> pmount_view = queryMultiAdapter(
3034+ >>> pmount_view = getMultiAdapter(
3035 ... (pmount_bin, request), name="+portlet-details")
3036
3037 Main functionality of this class is to provide abstracted model of the
3038@@ -52,4 +52,3 @@
3039 ('tramp-package', None, '', None)
3040
3041 Other relationship groups use the same mechanism.
3042-
3043
3044=== modified file 'lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt'
3045--- lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt 2009-06-19 16:15:02 +0000
3046+++ lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt 2009-08-06 20:16:29 +0000
3047@@ -142,7 +142,7 @@
3048
3049 Now Celso's PPA will list Mark's and No-Priv's PPA as its dependencies.
3050
3051- >>> admin_browser.reload()
3052+ >>> admin_browser.open(admin_browser.url) # Reload will set old form values.
3053 >>> print_ppa_dependencies(admin_browser.contents)
3054 PPA for Mark Shuttleworth
3055 PPA for No Privileges Person
3056
3057=== modified file 'lib/lp/translations/browser/project.py'
3058--- lib/lp/translations/browser/project.py 2009-07-17 02:25:09 +0000
3059+++ lib/lp/translations/browser/project.py 2009-08-05 19:26:37 +0000
3060@@ -11,7 +11,7 @@
3061 ]
3062
3063 from canonical.launchpad.webapp import (
3064- ApplicationMenu, enabled_with_permission, Link, LaunchpadView)
3065+ canonical_url, enabled_with_permission, Link, LaunchpadView)
3066 from canonical.launchpad.webapp.menu import NavigationMenu
3067 from lp.registry.interfaces.project import IProject
3068
3069@@ -20,7 +20,7 @@
3070
3071 usedfor = IProject
3072 facet = 'translations'
3073- links = ['products', 'changetranslators']
3074+ links = ['products', 'changetranslators', 'overview']
3075
3076 @enabled_with_permission('launchpad.Edit')
3077 def changetranslators(self):
3078@@ -31,6 +31,11 @@
3079 text = 'Products'
3080 return Link('', text)
3081
3082+ def overview(self):
3083+ text = 'Overview'
3084+ link = canonical_url(self.context, rootsite='translations')
3085+ return Link(link, text, icon='translation')
3086+
3087
3088 class ProjectView(LaunchpadView):
3089 pass
3090
3091=== modified file 'lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt'
3092--- lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt 2009-07-01 20:45:39 +0000
3093+++ lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt 2009-07-16 13:28:25 +0000
3094@@ -101,7 +101,7 @@
3095 ... 'http://translations.launchpad.dev/ubuntu/hoary/+lang/es')
3096 Traceback (most recent call last):
3097 ...
3098- HTTPError: HTTP Error 503: Service Unavailable
3099+ httperror_seek_wrapper: HTTP Error 503: Service Unavailable
3100 >>> main_content = find_main_content(user_browser.contents)
3101 >>> print main_content.findNext('p').renderContents()
3102 Translations for this release series are not available yet.
3103
3104=== modified file 'lib/lp/translations/stories/standalone/xx-pofile-translate-html-tags-escape.txt'
3105--- lib/lp/translations/stories/standalone/xx-pofile-translate-html-tags-escape.txt 2009-07-01 20:45:39 +0000
3106+++ lib/lp/translations/stories/standalone/xx-pofile-translate-html-tags-escape.txt 2009-08-06 11:42:11 +0000
3107@@ -31,6 +31,5 @@
3108 >>> text = find_tag_by_id(
3109 ... user_browser.contents, 'msgset_67_hr_translation_0')
3110 >>> print extract_text(text.renderContents())
3111- Upotreba:
3112-
3113- %s [opcije] &lt;foo&gt; [&lt;etiketa&gt;]%s%s%s
3114+ Upotreba:
3115+ %s [opcije] &lt;foo&gt; [&lt;etiketa&gt;]%s%s%s
3116
3117=== modified file 'lib/lp/translations/stories/translationgroups/30-show-group-translation-targets.txt'
3118--- lib/lp/translations/stories/translationgroups/30-show-group-translation-targets.txt 2009-07-01 13:16:44 +0000
3119+++ lib/lp/translations/stories/translationgroups/30-show-group-translation-targets.txt 2009-08-13 21:32:59 +0000
3120@@ -16,7 +16,7 @@
3121 >>> for link in portlet.findAll('a'):
3122 ... print '%s: %s' % (link.find(text=True), link['href'])
3123 Ubuntu: /ubuntu
3124- NetApplet: http://translations.launchpad.dev/netapplet
3125+ NetApplet: /netapplet
3126 GNOME: /gnome
3127
3128 If we disable some of these projects...
3129
3130=== modified file 'lib/lp/translations/templates/translationgroup-portlet-relateds.pt'
3131--- lib/lp/translations/templates/translationgroup-portlet-relateds.pt 2009-07-17 17:59:07 +0000
3132+++ lib/lp/translations/templates/translationgroup-portlet-relateds.pt 2009-08-13 21:32:59 +0000
3133@@ -25,7 +25,8 @@
3134 <h3>Projects:</h3>
3135 <ul>
3136 <li tal:repeat="product context/products">
3137- <tal:link replace="structure product/fmt:link" />
3138+ <tal:link replace="structure product/fmt:link:translations" />
3139+ <tal:link replace="structure product/fmt:url:translations" />
3140 </li>
3141 </ul>
3142 </tal:products>
3143
3144=== removed symlink 'lib/mechanize'
3145=== target was u'../sourcecode/zope/src/mechanize'
3146=== removed symlink 'lib/persistent'
3147=== target was u'../sourcecode/zope/src/persistent/'
3148=== removed symlink 'lib/transaction'
3149=== target was u'../sourcecode/zope/src/transaction/'
3150=== removed symlink 'lib/twisted'
3151=== target was u'../sourcecode/twisted/twisted/'
3152=== added symlink 'lib/twisted'
3153=== target is u'../sourcecode/twisted/twisted/'
3154=== removed directory 'lib/zc'
3155=== removed file 'lib/zc/__init__.py'
3156--- lib/zc/__init__.py 2009-01-03 19:32:48 +0000
3157+++ lib/zc/__init__.py 1970-01-01 00:00:00 +0000
3158@@ -1,2 +0,0 @@
3159-# This is a namespace package for zc.* packages. We should absolutely get rid
3160-# of this once we are using eggs.
3161
3162=== removed symlink 'lib/zc/zservertracelog'
3163=== target was u'../../sourcecode/zc.zservertracelog/src/zc/zservertracelog'
3164=== removed symlink 'lib/zdaemon'
3165=== target was u'../sourcecode/zope/src/zdaemon/'
3166=== removed symlink 'lib/zodbcode'
3167=== target was u'../sourcecode/zope/src/zodbcode/'
3168=== removed symlink 'lib/zope'
3169=== target was u'../sourcecode/zope/src/zope/'
3170=== modified file 'setup.py'
3171--- setup.py 2009-07-24 11:14:47 +0000
3172+++ setup.py 2009-08-05 18:52:52 +0000
3173@@ -20,26 +20,78 @@
3174 maintainer='Launchpad Developers',
3175 description=('A unique collaboration and Bazaar code hosting platform '
3176 'for software projects.'),
3177- license='LGPL v3',
3178+ license='Affero GPL v3',
3179+ # this list should only contain direct dependencies--things imported or
3180+ # used in zcml.
3181 install_requires=[
3182 'bzr',
3183+ 'chameleon.core',
3184+ 'chameleon.zpt',
3185 'feedvalidator',
3186 'funkload',
3187 'launchpadlib',
3188 'lazr.smtptest',
3189 'lazr.uri',
3190+ 'mechanize',
3191 'mocker',
3192 'oauth',
3193 'python-openid',
3194 'pytz',
3195+ # This appears to be a broken indirect dependency from zope.security:
3196+ 'RestrictedPython',
3197 'setuptools',
3198 'sourcecodegen',
3199 'storm',
3200- 'chameleon.core',
3201- 'chameleon.zpt',
3202+ 'transaction',
3203+ 'wadllib',
3204 'z3c.pt',
3205 'z3c.ptcompat',
3206- 'wadllib',
3207+ 'zc.zservertracelog',
3208+ 'zope.app.appsetup',
3209+ 'zope.app.component',
3210+ 'zope.app.dav', # ./package-includes/dav-configure.zcml
3211+ 'zope.app.error',
3212+ 'zope.app.exception',
3213+ 'zope.app.file',
3214+ 'zope.app.form',
3215+ 'zope.app.pagetemplate',
3216+ 'zope.app.pluggableauth',
3217+ 'zope.app.publication',
3218+ 'zope.app.publisher',
3219+ 'zope.app.security',
3220+ 'zope.app.securitypolicy',
3221+ 'zope.app.server',
3222+ 'zope.app.session',
3223+ 'zope.app.testing',
3224+ 'zope.app.wsgi',
3225+ 'zope.app.zapi',
3226+ 'zope.contenttype',
3227+ 'zope.component[zcml]',
3228+ 'zope.datetime',
3229+ 'zope.thread',
3230+ 'zope.error',
3231+ 'zope.event',
3232+ 'zope.exceptions',
3233+ 'zope.formlib',
3234+ 'zope.i18n',
3235+ 'zope.interface',
3236+ 'zope.hookable', # indirect, via zope.app.component
3237+ 'zope.lifecycleevent',
3238+ 'zope.location',
3239+ 'zope.pagetemplate',
3240+ 'zope.publisher',
3241+ 'zope.proxy',
3242+ 'zope.schema',
3243+ 'zope.security',
3244+ 'zope.sendmail',
3245+ 'zope.server',
3246+ 'zope.session',
3247+ 'zope.tal',
3248+ 'zope.tales',
3249+ 'zope.testbrowser',
3250+ 'zope.testing',
3251+ 'zope.traversing',
3252+ 'zope.viewlet', # only fixing a broken dependency
3253 # Loggerhead dependencies. These should be removed once
3254 # bug 383360 is fixed and we include it as a source dist.
3255 'Paste',
3256
3257=== modified file 'utilities/sourcedeps.conf'
3258--- utilities/sourcedeps.conf 2009-07-21 07:55:23 +0000
3259+++ utilities/sourcedeps.conf 2009-08-14 17:26:21 +0000
3260@@ -18,10 +18,8 @@
3261 testresources=lp:~launchpad-pqm/testresources/dev/
3262 testtools=lp:~launchpad-pqm/testtools/trunk/
3263 twisted=lp:~launchpad-pqm/twisted/trunk/
3264-zc.zservertracelog=lp:~launchpad-pqm/zc.zservertracelog/trunk/
3265 cscvs=lp:~launchpad-pqm/launchpad-cscvs/devel/
3266 lpreview=lp:~launchpad-pqm/bzr-lpreview/devel/
3267-zope=lp:~launchpad-pqm/zope3/3.4/
3268 pygettextpo=lp:~launchpad/pygettextpo/trunk/
3269 old_xmlplus=lp:~launchpad/dtdparser/trunk/
3270 lsprof=lp:~launchpad/lsprof/trunk/
3271
3272=== modified file 'versions.cfg'
3273--- versions.cfg 2009-08-12 12:35:51 +0000
3274+++ versions.cfg 2009-08-14 18:08:23 +0000
3275@@ -2,17 +2,20 @@
3276 versions = versions
3277
3278 [versions]
3279-# Alphabetical, please! :-)
3280+# Alphabetical, case-insensitive, please! :-)
3281 bzr = 1.17
3282 chameleon.core = 1.0b35
3283 chameleon.zpt = 1.0b17
3284+ClientForm = 0.2.10
3285 # Required by Windmill to run on 2.4
3286 ctypes = 1.0.2
3287 docutils = 0.5
3288 elementtree = 1.2.6-20050316
3289-# We use a version of feedvalidator that has been changed to not
3290-# change the default socket timeout on import.
3291-feedvalidator = 0.0.0DEV-r1049-hacked
3292+feedvalidator = 0.0.0DEV-r1049
3293+# We could use a locally hacked version of feedvalidator that has been changed
3294+# to not change the default socket timeout on import. We are currently handling
3295+# that problem elsewhere.
3296+# feedvalidator = 0.0.0DEV-r1049-hacked
3297 functest = 0.8.7
3298 funkload = 1.10.0
3299 httplib2 = 0.4.0
3300@@ -20,6 +23,7 @@
3301 launchpadlib = 1.0.2
3302 lazr.smtptest = 1.1
3303 lazr.uri = 1.0.1
3304+mechanize = 0.1.7b
3305 mocker = 0.10.1
3306 mozrunner = 1.3.4
3307 oauth = 1.0
3308@@ -27,12 +31,14 @@
3309 PasteDeploy = 1.3.3
3310 python-openid = 2.2.1
3311 pytz = 2009j
3312+RestrictedPython = 3.4.2
3313 setuptools = 0.6c9
3314 simplejson = 2.0.9
3315 simplesettings = 0.4
3316 SimpleTal = 4.1
3317 sourcecodegen = 0.6.9
3318 storm = 0.15
3319+transaction = 1.0a1
3320 uuid = 1.30
3321 wadllib = 0.1
3322 webunit = 1.3.8
3323@@ -48,27 +54,90 @@
3324 zc.buildout = 1.4.0dev-gary-r102684
3325 zc.lockfile = 1.0.0
3326 zc.recipe.egg = 1.3.0dev-gary-r102684
3327-zc.recipe.testrunner = 1.1.0
3328+zc.zservertracelog = 1.1.5
3329 ZConfig = 2.6.1
3330+zdaemon = 2.0.2
3331+ZODB3 = 3.8.1
3332+zodbcode = 3.4.0
3333+zope.annotation = 3.4.1
3334+zope.app.applicationcontrol = 3.4.3
3335+zope.app.appsetup = 3.4.1
3336+zope.app.authentication = 3.4.4
3337+zope.app.basicskin = 3.4.0
3338+zope.app.broken = 3.4.0
3339+zope.app.component = 3.4.1
3340+zope.app.container = 3.5.6
3341+zope.app.content = 3.4.0
3342+zope.app.dav = 3.4.1
3343+zope.app.debug = 3.4.1
3344+zope.app.dependable = 3.4.0
3345+zope.app.error = 3.5.1
3346+zope.app.exception = 3.4.1
3347+zope.app.file = 3.4.4
3348+zope.app.folder = 3.4.0
3349+zope.app.form = 3.4.1
3350+zope.app.generations = 3.4.1
3351+zope.app.http = 3.4.1
3352+zope.app.i18n = 3.4.4
3353+zope.app.interface = 3.4.0
3354 zope.app.locales = 3.5.1
3355+zope.app.pagetemplate = 3.4.1
3356+zope.app.pluggableauth = 3.4.0
3357+zope.app.principalannotation = 3.4.0
3358+zope.app.publication = 3.4.4
3359+zope.app.publisher = 3.4.1
3360+zope.app.renderer = 3.4.0
3361+zope.app.rotterdam = 3.4.1
3362+zope.app.schema = 3.4.0
3363+zope.app.security = 3.5.2
3364+zope.app.securitypolicy = 3.4.6
3365+zope.app.server = 3.4.2
3366+zope.app.session = 3.5.1
3367+zope.app.testing = 3.4.3
3368+zope.app.wsgi = 3.4.1
3369+zope.app.zapi = 3.4.0
3370+zope.app.zcmlfiles = 3.4.3
3371+zope.app.zopeappgenerations = 3.4.0
3372+zope.cachedescriptors = 3.4.1
3373 zope.component = 3.5.1
3374-zope.configuration = 3.6.0
3375+zope.configuration = 3.5.0
3376 zope.contentprovider = 3.5.0
3377+zope.contenttype = 3.4.0
3378+zope.copypastemove = 3.4.0
3379+zope.datetime = 3.4.0
3380 zope.deferredimport = 3.4.0
3381 zope.deprecation = 3.4.0
3382 zope.dottedname = 3.4.5
3383+zope.dublincore = 3.4.0
3384+zope.error = 3.5.1
3385 zope.event = 3.4.1
3386 zope.exceptions = 3.5.2
3387+zope.filerepresentation = 3.4.0
3388+zope.formlib = 3.4.0
3389 zope.hookable = 3.4.0
3390 zope.i18n = 3.6.0
3391 zope.i18nmessageid = 3.4.3
3392 zope.interface = 3.5.1
3393+zope.lifecycleevent = 3.4.0
3394 zope.location = 3.5.2
3395+zope.minmax = 1.1.0
3396+zope.modulealias = 3.4.0
3397+zope.pagetemplate = 3.4.0
3398 zope.proxy = 3.5.0
3399 zope.publisher = 3.5.6
3400 zope.schema = 3.5.4
3401-zope.security = 3.6.0
3402-zope.tal = 3.4.0
3403+zope.security = 3.7.1
3404+zope.securitypolicy = 3.4.1
3405+zope.sendmail = 3.4.0
3406+zope.server = 3.4.3
3407+zope.session = 3.4.1
3408+zope.size = 3.4.0
3409+zope.structuredtext = 3.4.0
3410+zope.tal = 3.4.1
3411 zope.tales = 3.4.0
3412-zope.testing = 3.7.4
3413-zope.traversing = 3.5.2
3414+zope.testbrowser = 3.4.2
3415+zope.testing = 3.8.1
3416+zope.thread = 3.4
3417+# zope.traversing = 3.5.2
3418+zope.traversing = 3.4.1
3419+zope.viewlet = 3.4.2

Subscribers

People subscribed via source and target branches

to status/vote changes: