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
=== 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-07 13:16:30 +0000
@@ -11,3 +11,10 @@
11sys.path[0:0] = [${string-paths}]11sys.path[0:0] = [${string-paths}]
12# Enable Storm's C extensions12# Enable Storm's C extensions
13os.environ['STORM_CEXTENSIONS'] = '1'13os.environ['STORM_CEXTENSIONS'] = '1'
14
15# we don't want to bother tests or logs with these.
16import warnings
17warnings.filterwarnings(
18 'ignore',
19 'Module .+ was already imported from .+, but .+ is being added.*',
20 UserWarning)
14\ No newline at end of file21\ No newline at end of file
1522
=== modified file 'buildout-templates/bin/test.in'
--- buildout-templates/bin/test.in 2009-07-30 13:26:13 +0000
+++ buildout-templates/bin/test.in 2009-08-10 22:08:05 +0000
@@ -132,6 +132,7 @@
132pgsql.installFakeConnect()132pgsql.installFakeConnect()
133133
134from zope.testing import testrunner134from zope.testing import testrunner
135from zope.testing.testrunner import options
135136
136defaults = [137defaults = [
137 # Find tests in the tests and ftests directories138 # Find tests in the tests and ftests directories
@@ -188,24 +189,24 @@
188189
189 def load_list(option, opt_str, list_name, parser):190 def load_list(option, opt_str, list_name, parser):
190 patch_find_tests(filter_tests(list_name))191 patch_find_tests(filter_tests(list_name))
191 testrunner.parser.add_option(192 options.parser.add_option(
192 '--load-list', type=str, action='callback', callback=load_list)193 '--load-list', type=str, action='callback', callback=load_list)
193194
194 def list_test_option(option, opt, value, parser):195 def list_test_option(option, opt, value, parser):
195 patch_find_tests(list_tests)196 patch_find_tests(list_tests)
196 testrunner.parser.add_option(197 options.parser.add_option(
197 '--list', action='callback', callback=list_test_option)198 '--list', action='callback', callback=list_test_option)
198199
199 def use_subunit(option, opt, value, parser):200 def use_subunit(option, opt, value, parser):
200 patch_zope_testresult(TestProtocolClient(sys.stdout))201 patch_zope_testresult(TestProtocolClient(sys.stdout))
201 testrunner.parser.add_option(202 options.parser.add_option(
202 '--subunit', action='callback', callback=use_subunit)203 '--subunit', action='callback', callback=use_subunit)
203204
204 options = testrunner.get_options(args=args, defaults=defaults)205 local_options = options.get_options(args=args, defaults=defaults)
205206
206 # Turn on Layer profiling if requested.207 # Turn on Layer profiling if requested.
207 from canonical.testing import profiled208 from canonical.testing import profiled
208 if options.verbose >= 3 and main_process:209 if local_options.verbose >= 3 and main_process:
209 profiled.setup_profiling()210 profiled.setup_profiling()
210211
211 # The working directory change is just so that the test script212 # The working directory change is just so that the test script
@@ -222,6 +223,6 @@
222 logging.disable(999999999)223 logging.disable(999999999)
223224
224 # Print Layer profiling report if requested.225 # Print Layer profiling report if requested.
225 if main_process and options.verbose >= 3:226 if main_process and local_options.verbose >= 3:
226 profiled.report_profile_stats()227 profiled.report_profile_stats()
227 sys.exit(result)228 sys.exit(result)
228229
=== modified file 'buildout.cfg'
--- buildout.cfg 2009-08-05 03:30:57 +0000
+++ buildout.cfg 2009-08-05 19:05:57 +0000
@@ -56,6 +56,12 @@
56initialization = import os56initialization = import os
57 os.environ['STORM_CEXTENSIONS'] = '1'57 os.environ['STORM_CEXTENSIONS'] = '1'
58 os.environ.setdefault('LPCONFIG', '${configuration:instance_name}')58 os.environ.setdefault('LPCONFIG', '${configuration:instance_name}')
59 # this can hopefully be removed when Twisted is used as an egg.
60 import warnings
61 warnings.filterwarnings(
62 'ignore',
63 'Module .+ was already imported from .+, but .+ is being added.*',
64 UserWarning)
59entry-points = stxdocs=zope.configuration.stxdocs:main65entry-points = stxdocs=zope.configuration.stxdocs:main
60 googletestservice=canonical.launchpad.testing.googletestservice:main66 googletestservice=canonical.launchpad.testing.googletestservice:main
61 windmill=windmill.bin.windmill_bin:main67 windmill=windmill.bin.windmill_bin:main
6268
=== removed symlink 'lib/BTrees'
=== target was u'../sourcecode/zope/src/BTrees/'
=== removed symlink 'lib/ClientForm'
=== target was u'../sourcecode/zope/src/ClientForm'
=== removed symlink 'lib/RestrictedPython'
=== target was u'../sourcecode/zope/src/RestrictedPython'
=== removed symlink 'lib/ZConfig'
=== target was u'../sourcecode/zope/src/ZConfig/'
=== removed symlink 'lib/ZODB'
=== target was u'../sourcecode/zope/src/ZODB/'
=== modified file 'lib/canonical/config/__init__.py'
--- lib/canonical/config/__init__.py 2009-07-19 04:41:14 +0000
+++ lib/canonical/config/__init__.py 2009-08-05 18:52:52 +0000
@@ -16,6 +16,7 @@
16import sys16import sys
17from urlparse import urlparse, urlunparse17from urlparse import urlparse, urlunparse
1818
19import pkg_resources
19import ZConfig20import ZConfig
2021
21from lazr.config import ImplicitTypeSchema22from lazr.config import ImplicitTypeSchema
@@ -194,8 +195,10 @@
194195
195 def _setZConfig(self):196 def _setZConfig(self):
196 """Modify the config, adding automatically generated settings"""197 """Modify the config, adding automatically generated settings"""
197 schemafile = os.path.join(198 self.root = TREE_ROOT
198 self.root, 'lib/zope/app/server/schema.xml')199
200 schemafile = pkg_resources.resource_filename(
201 'zope.app.server', 'schema.xml')
199 schema = ZConfig.loadSchema(schemafile)202 schema = ZConfig.loadSchema(schemafile)
200 root_options, handlers = ZConfig.loadConfig(203 root_options, handlers = ZConfig.loadConfig(
201 schema, self.zope_config_file)204 schema, self.zope_config_file)
202205
=== modified file 'lib/canonical/config/tests/test_config.py'
--- lib/canonical/config/tests/test_config.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/config/tests/test_config.py 2009-08-05 18:52:52 +0000
@@ -11,6 +11,7 @@
1111
12import ZConfig12import ZConfig
13import os13import os
14import pkg_resources
14import unittest15import unittest
1516
16from zope.testing.doctest import DocTestSuite, NORMALIZE_WHITESPACE, ELLIPSIS17from zope.testing.doctest import DocTestSuite, NORMALIZE_WHITESPACE, ELLIPSIS
@@ -20,11 +21,10 @@
2021
21# Calculate some landmark paths.22# Calculate some landmark paths.
22import canonical.config23import canonical.config
24schema_file = pkg_resources.resource_filename('zope.app.server', 'schema.xml')
25schema = ZConfig.loadSchema(schema_file)
26
23here = os.path.dirname(canonical.config.__file__)27here = os.path.dirname(canonical.config.__file__)
24schema_file = os.path.join(
25 here, os.pardir, os.pardir, 'zope/app/server/schema.xml')
26schema = ZConfig.loadSchema(schema_file)
27
28lazr_schema_file = os.path.join(here, 'schema-lazr.conf')28lazr_schema_file = os.path.join(here, 'schema-lazr.conf')
2929
3030
3131
=== modified file 'lib/canonical/configure.zcml'
--- lib/canonical/configure.zcml 2009-07-23 13:44:13 +0000
+++ lib/canonical/configure.zcml 2009-08-13 15:22:00 +0000
@@ -83,7 +83,7 @@
83 virtual host layers. The directives come in pairs.83 virtual host layers. The directives come in pairs.
84 The separate registration for the resources namespace (@@) is needed84 The separate registration for the resources namespace (@@) is needed
85 because otherwise the lookup for /@@/ will fail because the85 because otherwise the lookup for /@@/ will fail because the
86 layer-specifif defaultView directive also registers the 86 layer-specific defaultView directive also registers the
87 default view name as an unnamed adapter.87 default view name as an unnamed adapter.
88 -->88 -->
89 -->89 -->
9090
=== modified file 'lib/canonical/launchpad/browser/tests/registration.py'
--- lib/canonical/launchpad/browser/tests/registration.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/launchpad/browser/tests/registration.py 2009-08-05 18:52:52 +0000
@@ -29,7 +29,7 @@
2929
30 Return the Browser object after the registration is finished.30 Return the Browser object after the registration is finished.
31 """31 """
32 token_url = canonical_url(token)32 token_url = canonical_url(token).encode('utf8')
33 logout()33 logout()
34 browser = setupBrowser()34 browser = setupBrowser()
35 browser.open(token_url)35 browser.open(token_url)
3636
=== modified file 'lib/canonical/launchpad/doc/announcement-date-widget.txt'
--- lib/canonical/launchpad/doc/announcement-date-widget.txt 2009-02-28 03:36:29 +0000
+++ lib/canonical/launchpad/doc/announcement-date-widget.txt 2009-08-13 19:36:01 +0000
@@ -59,7 +59,9 @@
59 Traceback (most recent call last):59 Traceback (most recent call last):
60 ...60 ...
61 WidgetInputError: ('field.foo', u'Foo',61 WidgetInputError: ('field.foo', u'Foo',
62 Please do not provide a date if you want to publish immediately.)62 LaunchpadValidationError(u'Please do not provide a date
63 if you want to publish
64 immediately.'))
6365
64If you choose to publish at a specific date in the future, the date field66If you choose to publish at a specific date in the future, the date field
65must be filled.67must be filled.
@@ -70,4 +72,4 @@
70 Traceback (most recent call last):72 Traceback (most recent call last):
71 ...73 ...
72 WidgetInputError: ('field.foo', u'Foo',74 WidgetInputError: ('field.foo', u'Foo',
73 Please provide a publication date.)75 LaunchpadValidationError(u'Please provide a publication date.'))
7476
=== modified file 'lib/canonical/launchpad/doc/batch_navigation.txt'
--- lib/canonical/launchpad/doc/batch_navigation.txt 2009-03-24 12:43:49 +0000
+++ lib/canonical/launchpad/doc/batch_navigation.txt 2009-08-06 11:02:58 +0000
@@ -49,10 +49,13 @@
49This section demonstrates that batching generates sensible SQL queries when used49This section demonstrates that batching generates sensible SQL queries when used
50with SQLObject, i.e. that it puts appropriate LIMIT clauses on queries.50with SQLObject, i.e. that it puts appropriate LIMIT clauses on queries.
5151
52Imports:52Imports and initialization:
5353
54 >>> from canonical.launchpad.database import EmailAddress54 >>> from canonical.launchpad.database import EmailAddress
55 >>> from canonical.ftests.pgsql import CursorWrapper55 >>> from canonical.ftests.pgsql import CursorWrapper
56 >>> from canonical.launchpad.interfaces import IStore
57 >>> ignore = IStore(EmailAddress) # Prime the database connection.
58 ... # (Priming is important if this test is run in isolation).
56 >>> CursorWrapper.record_sql = True59 >>> CursorWrapper.record_sql = True
5760
58Prepare a query, and create a batch of the results:61Prepare a query, and create a batch of the results:
5962
=== modified file 'lib/canonical/launchpad/doc/checkbox-matrix-widget.txt'
--- lib/canonical/launchpad/doc/checkbox-matrix-widget.txt 2008-09-22 22:09:40 +0000
+++ lib/canonical/launchpad/doc/checkbox-matrix-widget.txt 2009-08-13 19:36:01 +0000
@@ -24,16 +24,16 @@
24 >>> matrix_widget._getFormValue()24 >>> matrix_widget._getFormValue()
25 []25 []
2626
27If we pass in a value via the request, we'll be able to get the licenses 27If we pass in a value via the request, we'll be able to get the licenses
28as a set from _getFormValue() or getInputValue():28as a set from _getFormValue() or getInputValue():
2929
30 >>> request = LaunchpadTestRequest(30 >>> request = LaunchpadTestRequest(
31 ... form={'field.licenses': ['GNU_LGPL_V2_1', 'GNU_GPL_V2']})31 ... form={'field.licenses': ['GNU_LGPL_V2_1', 'GNU_GPL_V2']})
32 >>> matrix_widget = CheckBoxMatrixWidget(licenses_field, vtype, request)32 >>> matrix_widget = CheckBoxMatrixWidget(licenses_field, vtype, request)
33 >>> matrix_widget._getFormValue()33 >>> matrix_widget._getFormValue()
34 Set([<...License.GNU_GPL_V2...>, <...License.GNU_LGPL_V2_1...>])34 set([<...License.GNU_GPL_V2...>, <...License.GNU_LGPL_V2_1...>])
35 >>> matrix_widget.getInputValue()35 >>> matrix_widget.getInputValue()
36 Set([<...License.GNU_GPL_V2...>, <...License.GNU_LGPL_V2_1...>])36 set([<...License.GNU_GPL_V2...>, <...License.GNU_LGPL_V2_1...>])
3737
38It should render as many rows as are specified by the column_count attribute.38It should render as many rows as are specified by the column_count attribute.
3939
4040
=== modified file 'lib/canonical/launchpad/doc/feeds.txt'
--- lib/canonical/launchpad/doc/feeds.txt 2009-08-05 14:48:06 +0000
+++ lib/canonical/launchpad/doc/feeds.txt 2009-08-05 18:52:52 +0000
@@ -26,8 +26,8 @@
26 >>> from zope.configuration import xmlconfig26 >>> from zope.configuration import xmlconfig
27 >>> zcmlcontext = xmlconfig.string("""27 >>> zcmlcontext = xmlconfig.string("""
28 ... <configure xmlns:browser="http://namespaces.zope.org/browser">28 ... <configure xmlns:browser="http://namespaces.zope.org/browser">
29 ... <include file="lib/canonical/launchpad/webapp/meta.zcml" />29 ... <include package="canonical.launchpad.webapp" file="meta.zcml" />
30 ... <include file="lib/zope/app/zcmlfiles/meta.zcml" />30 ... <include package="zope.app.zcmlfiles" file="meta.zcml" />
31 ... <browser:feeds31 ... <browser:feeds
32 ... module="canonical.launchpad.ftests.feeds_helper"32 ... module="canonical.launchpad.ftests.feeds_helper"
33 ... classes="ThingFeedView"33 ... classes="ThingFeedView"
3434
=== modified file 'lib/canonical/launchpad/doc/image-widget.txt'
--- lib/canonical/launchpad/doc/image-widget.txt 2008-06-04 07:51:28 +0000
+++ lib/canonical/launchpad/doc/image-widget.txt 2009-08-13 19:36:01 +0000
@@ -2,8 +2,8 @@
22
3In Launchpad we have images associated with people, products, distributions,3In Launchpad we have images associated with people, products, distributions,
4etc, and we want to allow people to have full control over their images. That4etc, and we want to allow people to have full control over their images. That
5is, they must be able to upload a new image and delete (or keep) an existing 5is, they must be able to upload a new image and delete (or keep) an existing
6one. For this we created this widget, which can be embeded into any form we 6one. For this we created this widget, which can be embeded into any form we
7want, which doesn't require us to add any submit buttons to indicate that the7want, which doesn't require us to add any submit buttons to indicate that the
8image should be kept, deleted or changed.8image should be kept, deleted or changed.
99
@@ -170,7 +170,7 @@
170#=== The LogoTiedWithMugshotWidget ===170#=== The LogoTiedWithMugshotWidget ===
171#171#
172#This is a specialized version of the ImageChangeWidget which also returns a172#This is a specialized version of the ImageChangeWidget which also returns a
173#resized version of the uploaded image, in case one was uploaded. 173#resized version of the uploaded image, in case one was uploaded.
174#174#
175# >>> from canonical.widgets.image import LogoTiedWithMugshotWidget175# >>> from canonical.widgets.image import LogoTiedWithMugshotWidget
176# >>> mugshot_file_name = os.path.join(176# >>> mugshot_file_name = os.path.join(
@@ -385,7 +385,8 @@
385 Traceback (most recent call last):385 Traceback (most recent call last):
386 ...386 ...
387 WidgetInputError: ('field.mugshot', u'Mugshot',387 WidgetInputError: ('field.mugshot', u'Mugshot',
388 This image is not exactly 192x192 pixels in size.)388 LaunchpadValidationError(u'\nThis image is not exactly
389 192x192\npixels in size.'))
389390
390391
391This is what we see when the image is the correct dimensions, and within the392This is what we see when the image is the correct dimensions, and within the
@@ -429,7 +430,9 @@
429 Traceback (most recent call last):430 Traceback (most recent call last):
430 ...431 ...
431 WidgetInputError: ('field.mugshot', u'Mugshot',432 WidgetInputError: ('field.mugshot', u'Mugshot',
432 This image exceeds the maximum allowed size in bytes.)433 LaunchpadValidationError(u'\nThis image exceeds the
434 maximum allowed size in
435 bytes.'))
433436
434A similar error will be raised if the image's dimensions are bigger than the437A similar error will be raised if the image's dimensions are bigger than the
435maximum we allow.438maximum we allow.
@@ -443,7 +446,8 @@
443 Traceback (most recent call last):446 Traceback (most recent call last):
444 ...447 ...
445 WidgetInputError: ('field.mugshot', u'Mugshot',448 WidgetInputError: ('field.mugshot', u'Mugshot',
446 This image is not exactly 191x193 pixels in size.)449 LaunchpadValidationError(u'\nThis image is not exactly
450 191x193\npixels in size.'))
447451
448 >>> person_mugshot.dimensions = (image.size[0] + 1, image.size[1] - 1)452 >>> person_mugshot.dimensions = (image.size[0] + 1, image.size[1] - 1)
449 >>> mugshot.seek(0)453 >>> mugshot.seek(0)
@@ -453,7 +457,8 @@
453 Traceback (most recent call last):457 Traceback (most recent call last):
454 ...458 ...
455 WidgetInputError: ('field.mugshot', u'Mugshot',459 WidgetInputError: ('field.mugshot', u'Mugshot',
456 This image is not exactly 193x191 pixels in size.)460 LaunchpadValidationError(u'\nThis image is not exactly
461 193x191\npixels in size.'))
457462
458We also won't accept anything that is not an image; that is, we only accept463We also won't accept anything that is not an image; that is, we only accept
459what can be parsed by the Python Imaging Library module.464what can be parsed by the Python Imaging Library module.
@@ -468,8 +473,10 @@
468 Traceback (most recent call last):473 Traceback (most recent call last):
469 ...474 ...
470 WidgetInputError: ('field.mugshot', u'Mugshot',475 WidgetInputError: ('field.mugshot', u'Mugshot',
471 The file uploaded was not recognized as an image;476 LaunchpadValidationError(u'\nThe file uploaded was not
472 please check it and retry.)477 recognized as an image;
478 please\ncheck it and
479 retry.'))
473480
474Finally, if the user specifies the 'change' action he must also provide a file481Finally, if the user specifies the 'change' action he must also provide a file
475to be uploaded.482to be uploaded.
@@ -481,7 +488,8 @@
481 Traceback (most recent call last):488 Traceback (most recent call last):
482 ...489 ...
483 WidgetInputError: ('field.mugshot', u'Mugshot',490 WidgetInputError: ('field.mugshot', u'Mugshot',
484 Please specify the image you want to use.)491 LaunchpadValidationError(u'Please specify the image you
492 want to use.'))
485493
486494
487== Non-exact Image Dimensions ==495== Non-exact Image Dimensions ==
@@ -503,7 +511,8 @@
503 Traceback (most recent call last):511 Traceback (most recent call last):
504 ...512 ...
505 WidgetInputError: ('field.mugshot', u'Mugshot',513 WidgetInputError: ('field.mugshot', u'Mugshot',
506 This image is larger than 64x64 pixels in size.)514 LaunchpadValidationError(u'\nThis image is larger than
515 64x64\npixels in size.'))
507516
508If the image is smaller than the dimensions, the input validates:517If the image is smaller than the dimensions, the input validates:
509518
@@ -524,4 +533,3 @@
524 >>> fileupload = widget.getInputValue()533 >>> fileupload = widget.getInputValue()
525 >>> fileupload.filename534 >>> fileupload.filename
526 u'mugshot.png'535 u'mugshot.png'
527
528536
=== modified file 'lib/canonical/launchpad/doc/launchpadlib.txt'
--- lib/canonical/launchpad/doc/launchpadlib.txt 2008-05-22 19:11:05 +0000
+++ lib/canonical/launchpad/doc/launchpadlib.txt 2009-06-11 01:28:55 +0000
@@ -11,7 +11,7 @@
11 >>> browser.open('http://launchpad.dev:8085/~stimpy')11 >>> browser.open('http://launchpad.dev:8085/~stimpy')
12 Traceback (most recent call last):12 Traceback (most recent call last):
13 ...13 ...
14 HTTPError: HTTP Error 404: Not Found14 httperror_seek_wrapper: HTTP Error 404: Not Found
1515
16...and when he doesn't, create him.16...and when he doesn't, create him.
1717
1818
=== modified file 'lib/canonical/launchpad/doc/loginstatus-pages.txt'
--- lib/canonical/launchpad/doc/loginstatus-pages.txt 2009-04-17 10:32:16 +0000
+++ lib/canonical/launchpad/doc/loginstatus-pages.txt 2009-08-13 13:33:26 +0000
@@ -112,7 +112,7 @@
112112
113 >>> print view()113 >>> print view()
114 <...114 <...
115 ...<a href="http://launchpad.dev/~name16"...>...115 ...<a href="/~name16"...>...
116116
117The link is for the user's Profile page in the 'overview' facet, even if117The link is for the user's Profile page in the 'overview' facet, even if
118we are viewing the 'bugs' facet.118we are viewing the 'bugs' facet.
119119
=== modified file 'lib/canonical/launchpad/doc/navigation.txt'
--- lib/canonical/launchpad/doc/navigation.txt 2008-09-30 22:25:04 +0000
+++ lib/canonical/launchpad/doc/navigation.txt 2009-06-11 01:28:55 +0000
@@ -249,8 +249,8 @@
249 >>> from zope.configuration import xmlconfig249 >>> from zope.configuration import xmlconfig
250 >>> zcmlcontext = xmlconfig.string("""250 >>> zcmlcontext = xmlconfig.string("""
251 ... <configure xmlns:browser="http://namespaces.zope.org/browser">251 ... <configure xmlns:browser="http://namespaces.zope.org/browser">
252 ... <include file="lib/canonical/launchpad/webapp/meta.zcml" />252 ... <include package="canonical.launchpad.webapp" file="meta.zcml" />
253 ... <include file="lib/zope/app/zcmlfiles/meta.zcml" />253 ... <include package="zope.app.zcmlfiles" file="meta.zcml" />
254 ... <browser:navigation254 ... <browser:navigation
255 ... module="canonical.launchpad.ftests"255 ... module="canonical.launchpad.ftests"
256 ... classes="ThingSetNavigation"256 ... classes="ThingSetNavigation"
257257
=== modified file 'lib/canonical/launchpad/doc/renamed-view.txt'
--- lib/canonical/launchpad/doc/renamed-view.txt 2008-09-30 22:25:04 +0000
+++ lib/canonical/launchpad/doc/renamed-view.txt 2009-06-11 01:28:55 +0000
@@ -100,8 +100,8 @@
100 >>> from zope.configuration import xmlconfig100 >>> from zope.configuration import xmlconfig
101 >>> zcmlcontext = xmlconfig.string("""101 >>> zcmlcontext = xmlconfig.string("""
102 ... <configure xmlns:browser="http://namespaces.zope.org/browser">102 ... <configure xmlns:browser="http://namespaces.zope.org/browser">
103 ... <include file="lib/canonical/launchpad/webapp/meta.zcml" />103 ... <include package="canonical.launchpad.webapp" file="meta.zcml" />
104 ... <include file="lib/zope/app/zcmlfiles/meta.zcml" />104 ... <include package="zope.app.zcmlfiles" file="meta.zcml" />
105 ... <browser:renamed-page105 ... <browser:renamed-page
106 ... for="canonical.launchpad.interfaces.IQuestionTarget"106 ... for="canonical.launchpad.interfaces.IQuestionTarget"
107 ... name="+old_tickets_page"107 ... name="+old_tickets_page"
108108
=== modified file 'lib/canonical/launchpad/doc/storm.txt'
--- lib/canonical/launchpad/doc/storm.txt 2009-07-24 12:55:03 +0000
+++ lib/canonical/launchpad/doc/storm.txt 2009-08-05 18:52:52 +0000
@@ -83,6 +83,7 @@
83 ...83 ...
84 InternalError: transaction is read-only84 InternalError: transaction is read-only
8585
86 >>> transaction.abort()
86 >>> t = transaction.begin()87 >>> t = transaction.begin()
87 >>> account = auth_slave.find(88 >>> account = auth_slave.find(
88 ... Account, openid_identifier='sabdfl_oid').one()89 ... Account, openid_identifier='sabdfl_oid').one()
8990
=== modified file 'lib/canonical/launchpad/doc/stripped-text-widget.txt'
--- lib/canonical/launchpad/doc/stripped-text-widget.txt 2007-06-17 11:57:02 +0000
+++ lib/canonical/launchpad/doc/stripped-text-widget.txt 2009-08-13 19:36:01 +0000
@@ -38,4 +38,4 @@
38 >>> widget.getInputValue()38 >>> widget.getInputValue()
39 Traceback (most recent call last):39 Traceback (most recent call last):
40 ...40 ...
41 WidgetInputError: ('field', u'Title', )41 WidgetInputError: ('field', u'Title', RequiredMissing())
4242
=== modified file 'lib/canonical/launchpad/doc/tales.txt'
--- lib/canonical/launchpad/doc/tales.txt 2009-08-11 07:20:12 +0000
+++ lib/canonical/launchpad/doc/tales.txt 2009-08-13 21:32:59 +0000
@@ -268,11 +268,11 @@
268 >>> print test_tales("bug/fmt:url/+text", bug=bug)268 >>> print test_tales("bug/fmt:url/+text", bug=bug)
269 http://bugs.launchpad.dev/bugs/1/+text269 http://bugs.launchpad.dev/bugs/1/+text
270270
271 # Undo the custom setup we did above.271
272272fmt:url accepts an rootsite extension to make URLs to a specific application.
273 >>> login(ANONYMOUS)273
274274 >>> login(ANONYMOUS,
275Persons and teams can also specify an alternate rootsite to fmt:url.275 ... LaunchpadTestRequest(SERVER_URL='http://code.launchpad.net'))
276276
277 >>> print test_tales("person/fmt:url:bugs", person=sabdfl)277 >>> print test_tales("person/fmt:url:bugs", person=sabdfl)
278 http://bugs.launchpad.dev/~sabdfl278 http://bugs.launchpad.dev/~sabdfl
@@ -280,6 +280,14 @@
280 >>> print test_tales("person/fmt:url:feeds", person=sabdfl)280 >>> print test_tales("person/fmt:url:feeds", person=sabdfl)
281 http://feeds.launchpad.dev/~sabdfl281 http://feeds.launchpad.dev/~sabdfl
282282
283 >>> print test_tales("pillar/fmt:url:answers", pillar=ubuntu)
284 http://answers.launchpad.dev/ubuntu
285
286 >>> print test_tales("bug/fmt:url:mainsite", bug=bug)
287 http://launchpad.dev/bugs/1
288
289 >>> login(ANONYMOUS)
290
283291
284The fmt: namespace to get links292The fmt: namespace to get links
285-------------------------------293-------------------------------
@@ -319,6 +327,34 @@
319 >>> test_tales("person/fmt:link", person=ubuntu_team)327 >>> test_tales("person/fmt:link", person=ubuntu_team)
320 u'<a href=".../~ubuntu-team" class="sprite team">Ubuntu Team</a>'328 u'<a href=".../~ubuntu-team" class="sprite team">Ubuntu Team</a>'
321329
330The link can make the URL go to a specific app.
331
332 >>> login(ANONYMOUS,
333 ... LaunchpadTestRequest(SERVER_URL='http://code.launchpad.net'))
334
335 >>> print test_tales("pillar/fmt:link:translations", pillar=ubuntu)
336 <a ...http://translations.launchpad.dev/ubuntu...
337
338 >>> print test_tales("person/fmt:url:feeds", person=sabdfl)
339 http://feeds.launchpad.dev/~sabdfl
340
341 >>> print test_tales("bug/fmt:url:mainsite", bug=bug)
342 http://launchpad.dev/bugs/1
343
344The default behaviour for pillars, persons, and teams is to link to
345the mainsite.
346
347 >>> print test_tales("pillar/fmt:link", pillar=ubuntu)
348 <a ...http://launchpad.dev/ubuntu...
349
350 >>> print test_tales("person/fmt:link", person=sabdfl)
351 <a ...http://launchpad.dev/~sabdfl...
352
353 >>> print test_tales("team/fmt:link", team=ubuntu_team)
354 <a ...http://launchpad.dev/~ubuntu-team...
355
356 >>> login(ANONYMOUS)
357
322The person's displayname is escaped to prevent markup from being358The person's displayname is escaped to prevent markup from being
323interpreted by the browser. For example, a script added to Sample359interpreted by the browser. For example, a script added to Sample
324Person's displayname will be escaped; averting a XSS vulnerability.360Person's displayname will be escaped; averting a XSS vulnerability.
325361
=== modified file 'lib/canonical/launchpad/ftests/feeds_helper.py'
--- lib/canonical/launchpad/ftests/feeds_helper.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/launchpad/ftests/feeds_helper.py 2009-08-05 18:52:52 +0000
@@ -15,9 +15,13 @@
15 ]15 ]
1616
1717
18import socket
19original_timeout = socket.getdefaulttimeout()
18import feedvalidator20import feedvalidator
21if socket.getdefaulttimeout() != original_timeout:
22 # feedvalidator's __init__ uses setdefaulttimeout promiscuously
23 socket.setdefaulttimeout(original_timeout)
19from cStringIO import StringIO24from cStringIO import StringIO
20import socket
21from textwrap import wrap25from textwrap import wrap
2226
23from zope.interface import implements, Interface, Attribute27from zope.interface import implements, Interface, Attribute
2428
=== modified file 'lib/canonical/launchpad/icing/style-3-0.css'
--- lib/canonical/launchpad/icing/style-3-0.css 2009-08-07 14:01:57 +0000
+++ lib/canonical/launchpad/icing/style-3-0.css 2009-08-13 02:24:56 +0000
@@ -179,7 +179,16 @@
179#colophon {179#colophon {
180 float: none;180 float: none;
181 }181 }
182#globalsearch {
183 float: none;
184 display: auto;
185 font-weight: inherit;
186 margin-right: auto;
187}
182188
189#globalsearch input {
190 font-size: inherit;
191}
183192
184/* Page layout */193/* Page layout */
185.yui-d0 {194.yui-d0 {
@@ -195,9 +204,25 @@
195.footer {204.footer {
196 clear: both;205 clear: both;
197 margin-top: 2em;206 margin-top: 2em;
198 border-top: 1px solid #cdd3dd;
199 padding-top: 0.5em;207 padding-top: 0.5em;
200 }208 }
209.footer .lp-arcana {
210 background: url(/@@/footer-background.png) top left repeat-x;
211 padding: 0.8em;
212 -moz-border-radius: 3px 3px 0 0;
213 -webkit-border-radius: 3px 3px 0 0;
214 border-radius: 3px 3px 0 0;
215 }
216.footer .lp-arcana img {
217 vertical-align: middle;
218 }
219.footer .lp-arcana .search-icon {
220 background: url(icon-sprites) 100% -191px no-repeat;
221 }
222.footer .colophon {
223 margin: 3em 3em 1em 3em;
224 text-align: center;
225 }
201.portlet, .aside {226.portlet, .aside {
202 clear: both;227 clear: both;
203 border-top: 1px solid #d6d6d6;228 border-top: 1px solid #d6d6d6;
@@ -424,7 +449,7 @@
424/* The text of the first tab should align with the heading directly449/* The text of the first tab should align with the heading directly
425 above it */450 above it */
426.location-portlet li:first-child a, .location-portlet li:first-child span {451.location-portlet li:first-child a, .location-portlet li:first-child span {
427 margin-left: -0.5em; 452 margin-left: -0.5em;
428}453}
429.location-portlet li a:link, .location-portlet li a:visited {454.location-portlet li a:link, .location-portlet li a:visited {
430 color: #000;455 color: #000;
431456
=== modified file 'lib/canonical/launchpad/icing/style.css'
--- lib/canonical/launchpad/icing/style.css 2009-08-05 08:15:28 +0000
+++ lib/canonical/launchpad/icing/style.css 2009-08-12 21:08:05 +0000
@@ -71,6 +71,7 @@
71.info {background:url(icon-sprites) 0 -96px no-repeat;}71.info {background:url(icon-sprites) 0 -96px no-repeat;}
72.question {background:url(icon-sprites) 0 -128px no-repeat;}72.question {background:url(icon-sprites) 0 -128px no-repeat;}
73.download-icon {background:url(icon-sprites) 0 -160px no-repeat;}73.download-icon {background:url(icon-sprites) 0 -160px no-repeat;}
74.download {background:url(icon-sprites) 0 -160px no-repeat;}
74.search-icon {background:url(icon-sprites) 0 -187px no-repeat;}75.search-icon {background:url(icon-sprites) 0 -187px no-repeat;}
75.no {background:url(icon-sprites) 0 -224px no-repeat;}76.no {background:url(icon-sprites) 0 -224px no-repeat;}
76.yes {background:url(icon-sprites) 0 -256px no-repeat;}77.yes {background:url(icon-sprites) 0 -256px no-repeat;}
7778
=== added file 'lib/canonical/launchpad/images/footer-background.png'
78Binary 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 differ79Binary 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
=== modified file 'lib/canonical/launchpad/pagetests/basics/max-batch-size.txt'
--- lib/canonical/launchpad/pagetests/basics/max-batch-size.txt 2009-07-17 14:00:36 +0000
+++ lib/canonical/launchpad/pagetests/basics/max-batch-size.txt 2009-08-05 18:52:52 +0000
@@ -10,7 +10,7 @@
10 >>> anon_browser.open('http://launchpad.dev/projects/+all?start=0&batch=1000')10 >>> anon_browser.open('http://launchpad.dev/projects/+all?start=0&batch=1000')
11 Traceback (most recent call last):11 Traceback (most recent call last):
12 ...12 ...
13 HTTPError: HTTP Error 400: Bad Request13 httperror_seek_wrapper: HTTP Error 400: Bad Request
1414
15 >>> print extract_text(find_main_content(anon_browser.contents))15 >>> print extract_text(find_main_content(anon_browser.contents))
16 Invalid Batch Size16 Invalid Batch Size
1717
=== modified file 'lib/canonical/launchpad/pagetests/feeds/xx-authentication.txt'
--- lib/canonical/launchpad/pagetests/feeds/xx-authentication.txt 2008-01-31 23:21:01 +0000
+++ lib/canonical/launchpad/pagetests/feeds/xx-authentication.txt 2009-07-16 13:28:25 +0000
@@ -23,4 +23,4 @@
23 >>> browser.open('http://feeds.launchpad.dev/bugs/14/bug.atom')23 >>> browser.open('http://feeds.launchpad.dev/bugs/14/bug.atom')
24 Traceback (most recent call last):24 Traceback (most recent call last):
25 ...25 ...
26 HTTPError: HTTP Error 403: Forbidden26 httperror_seek_wrapper: HTTP Error 403: Forbidden
2727
=== modified file 'lib/canonical/launchpad/pagetests/oauth/access-token.txt'
--- lib/canonical/launchpad/pagetests/oauth/access-token.txt 2008-07-22 20:34:14 +0000
+++ lib/canonical/launchpad/pagetests/oauth/access-token.txt 2009-07-16 13:28:25 +0000
@@ -40,7 +40,7 @@
40 ... 'http://launchpad.dev/+access-token', data=urlencode(data))40 ... 'http://launchpad.dev/+access-token', data=urlencode(data))
41 Traceback (most recent call last):41 Traceback (most recent call last):
42 ...42 ...
43 HTTPError: HTTP Error 401: Unauthorized43 httperror_seek_wrapper: HTTP Error 401: Unauthorized
4444
45The token's context, when not None, is sent to the consumer together45The token's context, when not None, is sent to the consumer together
46with the token's key and secret.46with the token's key and secret.
4747
=== modified file 'lib/canonical/launchpad/pagetests/standalone/xx-beta-testers-redirection.txt'
--- lib/canonical/launchpad/pagetests/standalone/xx-beta-testers-redirection.txt 2009-05-12 16:29:13 +0000
+++ lib/canonical/launchpad/pagetests/standalone/xx-beta-testers-redirection.txt 2009-08-06 11:35:34 +0000
@@ -27,7 +27,7 @@
27 ... function(*args, **kwargs)27 ... function(*args, **kwargs)
28 ... except HTTPError, exc:28 ... except HTTPError, exc:
29 ... print str(exc)29 ... print str(exc)
30 ... location = exc.headers.getheader('Location')30 ... location = exc.hdrs.getheader('Location')
31 ... if location is not None:31 ... if location is not None:
32 ... print 'Location:', location32 ... print 'Location:', location
3333
3434
=== modified file 'lib/canonical/launchpad/scripts/ftests/test_oops_prune.py'
--- lib/canonical/launchpad/scripts/ftests/test_oops_prune.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/launchpad/scripts/ftests/test_oops_prune.py 2009-08-07 12:46:37 +0000
@@ -58,9 +58,12 @@
58 INSERT INTO MessageChunk(message, sequence, content)58 INSERT INTO MessageChunk(message, sequence, content)
59 VALUES (1, 99, 'OOPS-%s')59 VALUES (1, 99, 'OOPS-%s')
60 """ % self.referenced_oops_code)60 """ % self.referenced_oops_code)
61 if not os.path.exists(config.error_reports.error_dir):
62 os.mkdir(config.error_reports.error_dir)
6163
62 def tearDown(self):64 def tearDown(self):
63 shutil.rmtree(self.oops_dir)65 shutil.rmtree(self.oops_dir)
66 shutil.rmtree(config.error_reports.error_dir)
6467
65 def test_referenced_oops(self):68 def test_referenced_oops(self):
66 self.failUnlessEqual(69 self.failUnlessEqual(
6770
=== modified file 'lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt'
--- lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt 2009-04-02 16:39:20 +0000
+++ lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt 2009-06-11 01:28:55 +0000
@@ -28,6 +28,7 @@
2828
29Test this once more to ensure the settings stick across transactions.29Test this once more to ensure the settings stick across transactions.
3030
31 >>> transaction.abort()
31 >>> t = transaction.begin()32 >>> t = transaction.begin()
32 >>> main_slave.find(Person, name='janitor').one().displayname = 'BenD'33 >>> main_slave.find(Person, name='janitor').one().displayname = 'BenD'
33 >>> transaction.commit()34 >>> transaction.commit()
3435
=== modified file 'lib/canonical/launchpad/webapp/tales.py'
--- lib/canonical/launchpad/webapp/tales.py 2009-08-13 00:51:50 +0000
+++ lib/canonical/launchpad/webapp/tales.py 2009-08-14 12:56:19 +0000
@@ -458,7 +458,25 @@
458 return self.url()458 return self.url()
459459
460 def traverse(self, name, furtherPath):460 def traverse(self, name, furtherPath):
461 if name in self.traversable_names:461 if name.startswith('link:') or name.startswith('url:'):
462 rootsite = name.split(':')[1]
463 extra_path = None
464 if len(furtherPath) > 0:
465 extra_path = '/'.join(reversed(furtherPath))
466 # Remove remaining entries in furtherPath so that traversal
467 # stops here.
468 del furtherPath[:]
469 if name.startswith('link:'):
470 if rootsite is None:
471 return self.link(extra_path)
472 else:
473 return self.link(extra_path, rootsite=rootsite)
474 else:
475 if rootsite is None:
476 self.url(extra_path)
477 else:
478 return self.url(extra_path, rootsite=rootsite)
479 elif name in self.traversable_names:
462 if len(furtherPath) >= 1:480 if len(furtherPath) >= 1:
463 extra_path = '/'.join(reversed(furtherPath))481 extra_path = '/'.join(reversed(furtherPath))
464 del furtherPath[:]482 del furtherPath[:]
@@ -472,13 +490,16 @@
472 else:490 else:
473 raise TraversalError, name491 raise TraversalError, name
474492
475 def link(self, view_name):493 def link(self, view_name, rootsite=None):
476 """Return an HTML link to the object's page.494 """Return an HTML link to the object's page.
477495
478 The link consists of an icon followed by the object's name.496 The link consists of an icon followed by the object's name.
479497
480 :param view_name: If not None, the link will point to the page with498 :param view_name: If not None, the link will point to the page with
481 that name on this object.499 that name on this object.
500 :param rootsite: If not None, return the URL to the page on the
501 specified rootsite. Note this is available only for subclasses
502 that allow specifying the rootsite.
482 """503 """
483 raise NotImplementedError(504 raise NotImplementedError(
484 "No link implementation for %r, IPathAdapter implementation "505 "No link implementation for %r, IPathAdapter implementation "
@@ -958,23 +979,6 @@
958 final_traversable_names = {'local-time': 'local_time'}979 final_traversable_names = {'local-time': 'local_time'}
959 final_traversable_names.update(ObjectFormatterAPI.final_traversable_names)980 final_traversable_names.update(ObjectFormatterAPI.final_traversable_names)
960981
961 def traverse(self, name, furtherPath):
962 """Special-case traversal for links with an optional rootsite."""
963 if name.startswith('link:') or name.startswith('url:'):
964 rootsite = name.split(':')[1]
965 extra_path = None
966 if len(furtherPath) > 0:
967 extra_path = '/'.join(reversed(furtherPath))
968 # Remove remaining entries in furtherPath so that traversal
969 # stops here.
970 del furtherPath[:]
971 if name.startswith('link:'):
972 return self.link(extra_path, rootsite=rootsite)
973 else:
974 return self.url(extra_path, rootsite=rootsite)
975 else:
976 return super(PersonFormatterAPI, self).traverse(name, furtherPath)
977
978 def local_time(self):982 def local_time(self):
979 """Return the local time for this person."""983 """Return the local time for this person."""
980 time_zone = 'UTC'984 time_zone = 'UTC'
@@ -982,12 +986,22 @@
982 time_zone = self._context.time_zone986 time_zone = self._context.time_zone
983 return datetime.now(pytz.timezone(time_zone)).strftime('%T %Z')987 return datetime.now(pytz.timezone(time_zone)).strftime('%T %Z')
984988
985 def link(self, view_name, rootsite=None):989 def url(self, view_name=None, rootsite='mainsite'):
986 """Return an HTML link to the person's page containing an icon990 """See `ObjectFormatterAPI`.
987 followed by the person's name.991
992 The default URL for a person is to the mainsite.
993 """
994 return super(PersonFormatterAPI, self).url(view_name, rootsite)
995
996 def link(self, view_name, rootsite='mainsite'):
997 """See `ObjectFormatterAPI`.
998
999 Return an HTML link to the person's page containing an icon
1000 followed by the person's name. The default URL for a person is to
1001 the mainsite.
988 """1002 """
989 person = self._context1003 person = self._context
990 url = canonical_url(person, rootsite=rootsite, view_name=view_name)1004 url = self.url(view_name, rootsite)
991 custom_icon = ObjectImageDisplayAPI(person)._get_custom_icon_url()1005 custom_icon = ObjectImageDisplayAPI(person)._get_custom_icon_url()
992 if custom_icon is None:1006 if custom_icon is None:
993 css_class = ObjectImageDisplayAPI(person).sprite_css()1007 css_class = ObjectImageDisplayAPI(person).sprite_css()
@@ -1024,12 +1038,16 @@
10241038
1025 hidden = u'<hidden>'1039 hidden = u'<hidden>'
10261040
1027 def url(self, view_name=None):1041 def url(self, view_name=None, rootsite='mainsite'):
1028 """See `ObjectFormatterAPI`."""1042 """See `ObjectFormatterAPI`.
1043
1044 The default URL for a team is to the mainsite. None is returned
1045 when the user does not have permission to review the team.
1046 """
1029 if not check_permission('launchpad.View', self._context):1047 if not check_permission('launchpad.View', self._context):
1030 # This person has no permission to view the team details.1048 # This person has no permission to view the team details.
1031 return None1049 return None
1032 return super(TeamFormatterAPI, self).url(view_name)1050 return super(TeamFormatterAPI, self).url(view_name, rootsite)
10331051
1034 def api_url(self, context):1052 def api_url(self, context):
1035 """See `ObjectFormatterAPI`."""1053 """See `ObjectFormatterAPI`."""
@@ -1038,8 +1056,12 @@
1038 return None1056 return None
1039 return super(TeamFormatterAPI, self).api_url(context)1057 return super(TeamFormatterAPI, self).api_url(context)
10401058
1041 def link(self, view_name, rootsite=None):1059 def link(self, view_name, rootsite='mainsite'):
1042 """See `ObjectFormatterAPI`."""1060 """See `ObjectFormatterAPI`.
1061
1062 The default URL for a team is to the mainsite. None is returned
1063 when the user does not have permission to review the team.
1064 """
1043 person = self._context1065 person = self._context
1044 if not check_permission('launchpad.View', person):1066 if not check_permission('launchpad.View', person):
1045 # This person has no permission to view the team details.1067 # This person has no permission to view the team details.
@@ -1116,7 +1138,7 @@
1116 """1138 """
1117 return queryAdapter(self._context, IPathAdapter, 'image').sprite_css()1139 return queryAdapter(self._context, IPathAdapter, 'image').sprite_css()
11181140
1119 def link(self, view_name):1141 def link(self, view_name, rootsite=None):
1120 """Return html including a link, description and icon.1142 """Return html including a link, description and icon.
11211143
1122 Icon and link are optional, depending on type and permissions.1144 Icon and link are optional, depending on type and permissions.
@@ -1132,7 +1154,7 @@
11321154
1133 summary = self._make_link_summary()1155 summary = self._make_link_summary()
1134 if check_permission(self._link_permission, self._context):1156 if check_permission(self._link_permission, self._context):
1135 url = self.url(view_name)1157 url = self.url(view_name, rootsite)
1136 else:1158 else:
1137 url = ''1159 url = ''
1138 if url:1160 if url:
@@ -1152,33 +1174,40 @@
1152 displayname = self._context.displayname1174 displayname = self._context.displayname
1153 return {'displayname': displayname}1175 return {'displayname': displayname}
11541176
1155 def link(self, view_name):1177 def url(self, view_name=None, rootsite=None):
1178 """See `ObjectFormatterAPI`.
1179
1180 The default URL for a pillar is to the mainsite.
1181 """
1182 return super(PillarFormatterAPI, self).url(view_name, rootsite)
1183
1184 def link(self, view_name, rootsite='mainsite'):
1156 """The html to show a link to a Product, Project or distribution.1185 """The html to show a link to a Product, Project or distribution.
11571186
1158 In the case of Products or Project groups we display the custom1187 In the case of Products or Project groups we display the custom
1159 icon, if one exists."""1188 icon, if one exists. The default URL for a pillar is to the mainsite.
1189 """
11601190
1161 html = super(PillarFormatterAPI, self).link(view_name)1191 html = super(PillarFormatterAPI, self).link(view_name)
1162 context = self._context1192 context = self._context
1163 if IProduct.providedBy(context) or IProject.providedBy(context):1193 custom_icon = ObjectImageDisplayAPI(
1164 custom_icon = ObjectImageDisplayAPI(1194 context)._get_custom_icon_url()
1165 context)._get_custom_icon_url()1195 url = self.url(view_name, rootsite)
1166 url = canonical_url(context, view_name=view_name)1196 summary = self._make_link_summary()
1167 summary = self._make_link_summary()1197 if custom_icon is None:
1168 if custom_icon is None:1198 css_class = ObjectImageDisplayAPI(context).sprite_css()
1169 css_class = ObjectImageDisplayAPI(context).sprite_css()1199 html = (u'<a href="%s" class="%s">%s</a>') % (
1170 html = (u'<a href="%s" class="%s">%s</a>') % (1200 url, css_class, summary)
1171 url, css_class, summary)1201 else:
1172 else:1202 html = (u'<a href="%s" class="bg-image" '
1173 html = (u'<a href="%s" class="bg-image" '1203 'style="background-image: url(%s)">%s</a>') % (
1174 'style="background-image: url(%s)">%s</a>') % (1204 url, custom_icon, summary)
1175 url, custom_icon, summary)1205 if IProduct.providedBy(context):
1176 if IProduct.providedBy(context):1206 license_status = context.license_status
1177 license_status = context.license_status1207 if license_status != LicenseStatus.OPEN_SOURCE:
1178 if license_status != LicenseStatus.OPEN_SOURCE:1208 html = '<span title="%s">%s (%s)</span>' % (
1179 html = '<span title="%s">%s (%s)</span>' % (1209 license_status.description, html,
1180 license_status.description, html,1210 license_status.title)
1181 license_status.title)
1182 return html1211 return html
11831212
11841213
@@ -1231,7 +1260,7 @@
1231 html += ')'1260 html += ')'
1232 return html % replacements1261 return html % replacements
12331262
1234 def url(self, view_name):1263 def url(self, view_name, rootsite=None):
1235 """Return the URL to download the file."""1264 """Return the URL to download the file."""
1236 return self._getDownloadURL(self._context.libraryfile)1265 return self._getDownloadURL(self._context.libraryfile)
12371266
@@ -1294,7 +1323,7 @@
1294class PreviewDiffFormatterAPI(ObjectFormatterAPI):1323class PreviewDiffFormatterAPI(ObjectFormatterAPI):
1295 """Formatter for preview diffs."""1324 """Formatter for preview diffs."""
12961325
1297 def url(self, view_name=None):1326 def url(self, view_name=None, rootsite=None):
1298 """Use the url of the librarian file containing the diff.1327 """Use the url of the librarian file containing the diff.
1299 """1328 """
1300 librarian_alias = self._context.diff_text1329 librarian_alias = self._context.diff_text
@@ -1423,7 +1452,7 @@
1423 'branch': self._context.branch.bzr_identity,1452 'branch': self._context.branch.bzr_identity,
1424 }1453 }
14251454
1426 def url(self, view_name=None):1455 def url(self, view_name=None, rootsite=None):
1427 """See `ObjectFormatterAPI`."""1456 """See `ObjectFormatterAPI`."""
1428 # The url of a code import is the associated branch.1457 # The url of a code import is the associated branch.
1429 # This is still here primarily for supporting branch deletion,1458 # This is still here primarily for supporting branch deletion,
@@ -2064,7 +2093,7 @@
2064 """Return the default representation of the link."""2093 """Return the default representation of the link."""
2065 return self._context.render()2094 return self._context.render()
20662095
2067 def url(self, view_name=None):2096 def url(self, view_name=None, rootsite=None):
2068 """Return the URL representation of the link."""2097 """Return the URL representation of the link."""
2069 if self._context.enabled:2098 if self._context.enabled:
2070 return self._context.url2099 return self._context.url
20712100
=== modified file 'lib/canonical/testing/__init__.py'
--- lib/canonical/testing/__init__.py 2009-07-17 18:46:25 +0000
+++ lib/canonical/testing/__init__.py 2009-08-10 22:08:05 +0000
@@ -63,8 +63,9 @@
63 logging._handlers.clear()63 logging._handlers.clear()
6464
65 # Reset the setup65 # Reset the setup
66 import zope.testing.testrunner66 from zope.testing.testrunner.runner import Runner
67 zope.testing.testrunner.configure_logging()67 from zope.testing.testrunner.logsupport import Logging
68 Logging(Runner()).global_setup()
6869
6970
70# This import registers the 'doctest' Unicode codec.71# This import registers the 'doctest' Unicode codec.
7172
=== modified file 'lib/canonical/testing/ftests/test_mockdb.py'
--- lib/canonical/testing/ftests/test_mockdb.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/testing/ftests/test_mockdb.py 2009-08-05 18:52:52 +0000
@@ -11,7 +11,7 @@
11import unittest11import unittest
1212
13import psycopg213import psycopg2
14from zope.testing.testrunner import dont_retry, RetryTest14# from zope.testing.testrunner import dont_retry, RetryTest
1515
16from canonical.config import config16from canonical.config import config
17from canonical.testing import mockdb, DatabaseLayer17from canonical.testing import mockdb, DatabaseLayer
@@ -101,63 +101,63 @@
101 self.connections.append(con)101 self.connections.append(con)
102 return con102 return con
103103
104 @dont_retry104 # @dont_retry
105 def testIncorrectReplay(self):105 #def testIncorrectReplay(self):
106 # Record nothing but a close on a single connection.106 # # Record nothing but a close on a single connection.
107 con = self.connect()107 # con = self.connect()
108 con.close()108 # con.close()
109 self.script.store()109 # self.script.store()
110110
111 # Replay correctly.111 # # Replay correctly.
112 self.switchToReplayMode()112 # self.switchToReplayMode()
113 con = self.connect()113 # con = self.connect()
114 con.close()114 # con.close()
115115
116 # Replay incorrectly.116 # # Replay incorrectly.
117 self.switchToReplayMode()117 # self.switchToReplayMode()
118 con = self.connect()118 # con = self.connect()
119 self.assertRaises(RetryTest, con.rollback)119 # self.assertRaises(RetryTest, con.rollback)
120120
121 @dont_retry121 # @dont_retry
122 def testMultipleConnections(self):122 #def testMultipleConnections(self):
123 # Ensure that commands issued via different connections123 # Ensure that commands issued via different connections
124 # maintain their global order.124 # maintain their global order.
125 con1 = self.connect()125 # con1 = self.connect()
126 con2 = self.connect()126 # con2 = self.connect()
127 con1.close()127 # con1.close()
128 con2.close()128 # con2.close()
129 self.script.store()129 # self.script.store()
130130
131 # Replay correctly.131 # # Replay correctly.
132 self.switchToReplayMode()132 # self.switchToReplayMode()
133 con1 = self.connect()133 # con1 = self.connect()
134 con2 = self.connect()134 # con2 = self.connect()
135 con1.close()135 # con1.close()
136 con2.close()136 # con2.close()
137137
138 # Replay in the wrong order.138 # # Replay in the wrong order.
139 self.switchToReplayMode()139 # self.switchToReplayMode()
140 con1 = self.connect()140 # con1 = self.connect()
141 con2 = self.connect()141 # con2 = self.connect()
142 self.assertRaises(RetryTest, con2.close)142 # self.assertRaises(RetryTest, con2.close)
143143
144 @dont_retry144 # @dont_retry
145 def testConnectionParams(self):145 #def testConnectionParams(self):
146 # Make sure we can correctly connect with different connection parms.146 # Make sure we can correctly connect with different connection parms.
147 for mode in self.modes():147 # for mode in self.modes():
148 for dbuser in ['launchpad', 'testadmin']:148 # for dbuser in ['launchpad', 'testadmin']:
149 connection_string = "%s user=%s" % (149 # connection_string = "%s user=%s" % (
150 config.database.main_master, dbuser)150 # config.database.main_master, dbuser)
151 con = self.connect(connection_string)151 # con = self.connect(connection_string)
152 cur = con.cursor()152 # cur = con.cursor()
153 cur.execute("SHOW session authorization")153 # cur.execute("SHOW session authorization")
154 self.failUnlessEqual(cur.fetchone()[0], dbuser)154 # self.failUnlessEqual(cur.fetchone()[0], dbuser)
155155
156 # Confirm that unexpected connection parameters raises a RetryTest.156 # Confirm that unexpected connection parameters raises a RetryTest.
157 self.switchToReplayMode()157 # self.switchToReplayMode()
158 self.assertRaises(RetryTest, self.connect, "whoops")158 # self.assertRaises(RetryTest, self.connect, "whoops")
159159
160 @dont_retry160 # @dont_retry
161 def testFailedConnection(self):161 def testFailedConnection(self):
162 # Ensure failed database connections are reproducable.162 # Ensure failed database connections are reproducable.
163 for mode in self.modes():163 for mode in self.modes():
@@ -168,13 +168,13 @@
168 psycopg2.OperationalError, self.connect, connection_string168 psycopg2.OperationalError, self.connect, connection_string
169 )169 )
170170
171 @dont_retry171 # @dont_retry
172 def testNoopSession(self):172 def testNoopSession(self):
173 # Minimal do-nothing case.173 # Minimal do-nothing case.
174 for mode in self.modes():174 for mode in self.modes():
175 con = self.connect()175 con = self.connect()
176176
177 @dont_retry177 # @dont_retry
178 def testSimpleQuery(self):178 def testSimpleQuery(self):
179 # Ensure that we can script and replay a simple query.179 # Ensure that we can script and replay a simple query.
180 for mode in self.modes():180 for mode in self.modes():
@@ -199,7 +199,7 @@
199 name = cur.fetchone()[0]199 name = cur.fetchone()[0]
200 self.assertEqual(name, 'carlos')200 self.assertEqual(name, 'carlos')
201201
202 @dont_retry202 # @dont_retry
203 def testExceptions(self):203 def testExceptions(self):
204 # Confirm that expected exceptions are raised correctly.204 # Confirm that expected exceptions are raised correctly.
205 for mode in self.modes():205 for mode in self.modes():
@@ -210,38 +210,38 @@
210 cur.execute, "SELECT blood FROM Stone"210 cur.execute, "SELECT blood FROM Stone"
211 )211 )
212212
213 @dont_retry213 # @dont_retry
214 def testUnexpectedQuery(self):214 #def testUnexpectedQuery(self):
215 for mode in self.modes():215 # for mode in self.modes():
216 con = self.connect()216 # con = self.connect()
217 cur = con.cursor()217 # cur = con.cursor()
218 if mode != 'replay':218 # if mode != 'replay':
219 cur.execute("SELECT name FROM Person WHERE name='sabdfl'")219 # cur.execute("SELECT name FROM Person WHERE name='sabdfl'")
220 else:220 # else:
221 # Issue an unexpected query in replay mode. A RetryTest221 # Issue an unexpected query in replay mode. A RetryTest
222 # exception should be raised.222 # exception should be raised.
223 self.assertRaises(223 # self.assertRaises(
224 RetryTest, cur.execute,224 # RetryTest, cur.execute,
225 "SELECT name FROM Person WHERE name='stub'"225 # "SELECT name FROM Person WHERE name='stub'"
226 )226 # )
227227
228 @dont_retry228 # @dont_retry
229 def testUnexpectedQueryParameters(self):229 #def testUnexpectedQueryParameters(self):
230 for mode in self.modes():230 # for mode in self.modes():
231 con = self.connect()231 # con = self.connect()
232 cur = con.cursor()232 # cur = con.cursor()
233 query = "SELECT name FROM Person WHERE name=%s"233 # query = "SELECT name FROM Person WHERE name=%s"
234 if mode != 'replay':234 # if mode != 'replay':
235 cur.execute(query, ('sabdfl',))235 # cur.execute(query, ('sabdfl',))
236 else:236 # else:
237 # Issue a query with unexpected bound parameters in replay237 # Issue a query with unexpected bound parameters in replay
238 # mode. A RetryTest should be raised.238 # mode. A RetryTest should be raised.
239 self.assertRaises(239 # self.assertRaises(
240 RetryTest, cur.execute,240 # RetryTest, cur.execute,
241 query, ('stub',)241 # query, ('stub',)
242 )242 # )
243243
244 @dont_retry244 # @dont_retry
245 def testCommit(self):245 def testCommit(self):
246 # Confirm commit behavior.246 # Confirm commit behavior.
247 for mode in self.modes():247 for mode in self.modes():
@@ -283,7 +283,7 @@
283 """)283 """)
284 con.commit()284 con.commit()
285285
286 @dont_retry286 # @dont_retry
287 def testRollback(self):287 def testRollback(self):
288 # Confirm rollback behavior.288 # Confirm rollback behavior.
289 for mode in self.modes():289 for mode in self.modes():
@@ -323,7 +323,7 @@
323 "Rollback did not roll back changes."323 "Rollback did not roll back changes."
324 )324 )
325325
326 @dont_retry326 # @dont_retry
327 def testFailedCommit(self):327 def testFailedCommit(self):
328 # Confirm exeptions raised on commit are recorded and replayed.328 # Confirm exeptions raised on commit are recorded and replayed.
329 for mode in self.modes():329 for mode in self.modes():
@@ -344,7 +344,7 @@
344 else:344 else:
345 self.assertRaises(psycopg2.InterfaceError, con.rollback)345 self.assertRaises(psycopg2.InterfaceError, con.rollback)
346346
347 @dont_retry347 # @dont_retry
348 def testFailedSetIsolationLevel(self):348 def testFailedSetIsolationLevel(self):
349 # Confirm exeptions raised on commit are recorded and replayed.349 # Confirm exeptions raised on commit are recorded and replayed.
350 for mode in self.modes():350 for mode in self.modes():
@@ -354,7 +354,7 @@
354 psycopg2.InterfaceError, con.set_isolation_level, 666354 psycopg2.InterfaceError, con.set_isolation_level, 666
355 )355 )
356356
357 @dont_retry357 # @dont_retry
358 def testClose(self):358 def testClose(self):
359 # Confirm and record close behavior.359 # Confirm and record close behavior.
360 for mode in self.modes():360 for mode in self.modes():
@@ -376,7 +376,7 @@
376 self.fail(376 self.fail(
377 "Connection.close() now DB-API compliant. Fix test.")377 "Connection.close() now DB-API compliant. Fix test.")
378378
379 @dont_retry379 # @dont_retry
380 def testCursorDescription(self):380 def testCursorDescription(self):
381 # Confirm cursor.description behavior.381 # Confirm cursor.description behavior.
382 for mode in self.modes():382 for mode in self.modes():
@@ -402,7 +402,7 @@
402 else:402 else:
403 self.failUnlessEqual(direct_description, cur.description)403 self.failUnlessEqual(direct_description, cur.description)
404404
405 @dont_retry405 # @dont_retry
406 def testCursorRowcount(self):406 def testCursorRowcount(self):
407 # Confirm and record cursor.rowcount behavior.407 # Confirm and record cursor.rowcount behavior.
408 for mode in self.modes():408 for mode in self.modes():
@@ -443,7 +443,7 @@
443 cur.execute("DELETE FROM WikiName WHERE person=1")443 cur.execute("DELETE FROM WikiName WHERE person=1")
444 self.failUnlessEqual(cur.rowcount, 1)444 self.failUnlessEqual(cur.rowcount, 1)
445445
446 @dont_retry446 # @dont_retry
447 def testCursorClose(self):447 def testCursorClose(self):
448 # Confirm and record cursor.close behavior.448 # Confirm and record cursor.close behavior.
449 for mode in self.modes():449 for mode in self.modes():
@@ -457,7 +457,7 @@
457 cur = con.cursor()457 cur = con.cursor()
458 cur.execute("SELECT name FROM Person WHERE name='stub'")458 cur.execute("SELECT name FROM Person WHERE name='stub'")
459459
460 @dont_retry460 # @dont_retry
461 def testFetchOne(self):461 def testFetchOne(self):
462 for mode in self.modes():462 for mode in self.modes():
463 con = self.connect()463 con = self.connect()
@@ -491,7 +491,7 @@
491 self.failUnlessEqual(row[0], i, "Bad result %s" % repr(row))491 self.failUnlessEqual(row[0], i, "Bad result %s" % repr(row))
492 self.failUnless(cur.fetchone() is None, "Too many results")492 self.failUnless(cur.fetchone() is None, "Too many results")
493493
494 @dont_retry494 # @dont_retry
495 def testCursorIteration(self):495 def testCursorIteration(self):
496 # psycopg1 does not support this extension.496 # psycopg1 does not support this extension.
497 for mode in self.modes():497 for mode in self.modes():
@@ -516,7 +516,7 @@
516 ## )516 ## )
517 ## self.failUnlessEqual(row[0], 1, "Bad result %s" % repr(row))517 ## self.failUnlessEqual(row[0], 1, "Bad result %s" % repr(row))
518518
519 @dont_retry519 # @dont_retry
520 def testFetchAll(self):520 def testFetchAll(self):
521 for mode in self.modes():521 for mode in self.modes():
522 con = self.connect()522 con = self.connect()
523523
=== modified file 'lib/canonical/testing/mockdb.py'
--- lib/canonical/testing/mockdb.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/testing/mockdb.py 2009-08-05 18:52:52 +0000
@@ -25,7 +25,7 @@
25import urllib25import urllib
2626
27import psycopg227import psycopg2
28from zope.testing.testrunner import RetryTest28# from zope.testing.testrunner import RetryTest
2929
30from canonical.config import config30from canonical.config import config
3131
@@ -386,7 +386,8 @@
386 if os.path.exists(self.script_filename):386 if os.path.exists(self.script_filename):
387 os.unlink(self.script_filename)387 os.unlink(self.script_filename)
388 self.invalid = True388 self.invalid = True
389 raise RetryTest(reason)389 raise RetryTest(reason) # Leaving this as a name error: this should
390 # not be called unless we reinstate the retry behavior in zope.testing.
390391
391392
392class MockDbConnection:393class MockDbConnection:
393394
=== modified file 'lib/canonical/testing/tests/test_mockdb.py'
--- lib/canonical/testing/tests/test_mockdb.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/testing/tests/test_mockdb.py 2009-08-05 18:52:52 +0000
@@ -11,7 +11,7 @@
11import unittest11import unittest
1212
13from zope.testing.doctestunit import DocTestSuite13from zope.testing.doctestunit import DocTestSuite
14from zope.testing.testrunner import dont_retry, RetryTest14#from zope.testing.testrunner import dont_retry, RetryTest
1515
16from canonical.testing import mockdb16from canonical.testing import mockdb
1717
@@ -24,7 +24,7 @@
24 if os.path.exists(self.script_filename):24 if os.path.exists(self.script_filename):
25 os.unlink(self.script_filename)25 os.unlink(self.script_filename)
2626
27 @dont_retry27 #@dont_retry
28 def testSerialize(self):28 def testSerialize(self):
29 # Ensure the scripts can store and retrieve their logs29 # Ensure the scripts can store and retrieve their logs
30 recorder = mockdb.ScriptRecorder(self.script_filename)30 recorder = mockdb.ScriptRecorder(self.script_filename)
@@ -34,29 +34,29 @@
34 replayer = mockdb.ScriptPlayer(self.script_filename)34 replayer = mockdb.ScriptPlayer(self.script_filename)
35 self.failUnlessEqual(replayer.log, ['Arbitrary Data'])35 self.failUnlessEqual(replayer.log, ['Arbitrary Data'])
3636
37 @dont_retry37 #@dont_retry
38 def testHandleInvalidScript(self):38 #def testHandleInvalidScript(self):
39 # Ensure a RetryTest exception is raised and the invalid script39 # Ensure a RetryTest exception is raised and the invalid script
40 # file removed when handleInvalidScript() is called40 # file removed when handleInvalidScript() is called
41 recorder = mockdb.ScriptRecorder(self.script_filename)41 # recorder = mockdb.ScriptRecorder(self.script_filename)
42 recorder.store()42 # recorder.store()
4343
44 replayer = mockdb.ScriptPlayer(self.script_filename)44 # replayer = mockdb.ScriptPlayer(self.script_filename)
4545
46 self.assertRaises(46 # self.assertRaises(
47 RetryTest, replayer.handleInvalidScript, 'Reason')47 # RetryTest, replayer.handleInvalidScript, 'Reason')
48 self.failIf(os.path.exists(self.script_filename))48 # self.failIf(os.path.exists(self.script_filename))
4949
50 @dont_retry50 #@dont_retry
51 def testShortScript(self):51 #def testShortScript(self):
52 # Ensure a RetryTest exception is raised if an attempt is made52 # Ensure a RetryTest exception is raised if an attempt is made
53 # to pull results from an exhausted script.53 # to pull results from an exhausted script.
54 recorder = mockdb.ScriptRecorder(self.script_filename)54 # recorder = mockdb.ScriptRecorder(self.script_filename)
55 recorder.store()55 # recorder.store()
56 replayer = mockdb.ScriptPlayer(self.script_filename)56 # replayer = mockdb.ScriptPlayer(self.script_filename)
57 self.assertRaises(RetryTest, replayer.getNextEntry, None, None)57 # self.assertRaises(RetryTest, replayer.getNextEntry, None, None)
5858
59 @dont_retry59 #@dont_retry
60 def testScriptFilename(self):60 def testScriptFilename(self):
61 # Ensure evil characters in the key don't mess up the script_filename61 # Ensure evil characters in the key don't mess up the script_filename
62 # results. Only '/' is really evil - other chars should all work62 # results. Only '/' is really evil - other chars should all work
@@ -86,7 +86,7 @@
86 # This test does not use @dont_retry.86 # This test does not use @dont_retry.
87 # It needs to leak RetryTest exeptions as it tests that the87 # It needs to leak RetryTest exeptions as it tests that the
88 # test runner is handling them correctly.88 # test runner is handling them correctly.
89 def testRetryTestRetriesTest(self):89 #def testRetryTestRetriesTest(self):
90 # The first time this test is run it raises a RetryTest exception.90 # The first time this test is run it raises a RetryTest exception.
91 # The second time it is run it succeeds. This means that this91 # The second time it is run it succeeds. This means that this
92 # test will fail if RetryTest handling is not being done correctly92 # test will fail if RetryTest handling is not being done correctly
@@ -97,106 +97,106 @@
97 # version of zope.testing is in use and to minimize the zope.testing97 # version of zope.testing is in use and to minimize the zope.testing
98 # patch until we decide if RetryTest handling is to be pushed98 # patch until we decide if RetryTest handling is to be pushed
99 # upstream or not.99 # upstream or not.
100 MockDbTestCase._retry_count += 1100 # MockDbTestCase._retry_count += 1
101 if MockDbTestCase._retry_count % 2 == 1:101 # if MockDbTestCase._retry_count % 2 == 1:
102 raise RetryTest(102 # raise RetryTest(
103 "Testing RetryTest behavior. This exception will be raised "103 # "Testing RetryTest behavior. This exception will be raised "
104 "but the test runner doesn't consider it a failure")104 # "but the test runner doesn't consider it a failure")
105105
106106
107_doctest_retry_count = 0107_doctest_retry_count = 0
108108
109def retry_on_odd_numbered_calls():109#def retry_on_odd_numbered_calls():
110 """Helper for doctest RetryTest test.110# """Helper for doctest RetryTest test.
111111
112 This helper raises a RetryTest exception on odd numbered calls,112# This helper raises a RetryTest exception on odd numbered calls,
113 and prints 'Retry not raised' on even numbered calls.113# and prints 'Retry not raised' on even numbered calls.
114 114
115 >>> try:115# >>> try:
116 ... retry_on_odd_numbered_calls()116# ... retry_on_odd_numbered_calls()
117 ... except RetryTest:117# ... except RetryTest:
118 ... print "Caught RetryTest."118# ... print "Caught RetryTest."
119 ...119# ...
120 Retry raised.120# Retry raised.
121 Caught RetryTest.121# Caught RetryTest.
122 >>> try:122# >>> try:
123 ... retry_on_odd_numbered_calls()123# ... retry_on_odd_numbered_calls()
124 ... except RetryTest:124# ... except RetryTest:
125 ... print "Caught RetryTest."125# ... print "Caught RetryTest."
126 ...126# ...
127 Retry not raised.127# Retry not raised.
128 """128# """
129 global _doctest_retry_count129# global _doctest_retry_count
130 _doctest_retry_count += 1130# _doctest_retry_count += 1
131 if _doctest_retry_count % 2 == 1:131# if _doctest_retry_count % 2 == 1:
132 print "Retry raised."132# print "Retry raised."
133 raise RetryTest133# raise RetryTest
134 print "Retry not raised."134# print "Retry not raised."
135135
136136
137def testRetryTestInDoctest_will_raise_but_testrunner_ignores_it():137#def testRetryTestInDoctest_will_raise_but_testrunner_ignores_it():
138 """Test a RetryTest exception in a doctest works as expected.138# """Test a RetryTest exception in a doctest works as expected.
139139
140 This doctest raises a RetryTest exception the first time it is run.140# This doctest raises a RetryTest exception the first time it is run.
141 On the second run, it succeeds.141# On the second run, it succeeds.
142142
143 If the testrunner is correctly handling RetryTest exceptions raised143# If the testrunner is correctly handling RetryTest exceptions raised
144 by doctests, then the RetryTest exception will not be reported as144# by doctests, then the RetryTest exception will not be reported as
145 a failure. This test will then be rerun and succeed.145# a failure. This test will then be rerun and succeed.
146146
147 If the testrunner is not correctly handling RetryTest exceptions,147# If the testrunner is not correctly handling RetryTest exceptions,
148 then the RetryTesst exception will be flagged as an error.148# then the RetryTesst exception will be flagged as an error.
149149
150 This test confirms that a RetryException raised where no exception150# This test confirms that a RetryException raised where no exception
151 was expected works.151# was expected works.
152152
153 >>> retry_on_odd_numbered_calls()153# >>> retry_on_odd_numbered_calls()
154 Retry not raised.154# Retry not raised.
155 """155# """
156156
157157
158def retry_on_odd_numbered_calls2():158#def retry_on_odd_numbered_calls2():
159 """Helper for doctest RetryTest test.159# """Helper for doctest RetryTest test.
160160
161 This helper raises a RetryTest exception on odd numbered calls,161# This helper raises a RetryTest exception on odd numbered calls,
162 and a RuntimeError on even numbered calls.162# and a RuntimeError on even numbered calls.
163163
164 >>> try:164# >>> try:
165 ... retry_on_odd_numbered_calls2()165# ... retry_on_odd_numbered_calls2()
166 ... except RetryTest:166# ... except RetryTest:
167 ... print "Caught RetryTest."167# ... print "Caught RetryTest."
168 ...168# ...
169 Retry raised.169# Retry raised.
170 Caught RetryTest.170# Caught RetryTest.
171 >>> try:171# >>> try:
172 ... retry_on_odd_numbered_calls2()172# ... retry_on_odd_numbered_calls2()
173 ... except RetryTest:173# ... except RetryTest:
174 ... print "Caught RetryTest."174# ... print "Caught RetryTest."
175 ...175# ...
176 Traceback (most recent call last):176# Traceback (most recent call last):
177 ...177# ...
178 RuntimeError: Retry not raised.178# RuntimeError: Retry not raised.
179 """179# """
180 global _doctest_retry_count180# global _doctest_retry_count
181 _doctest_retry_count += 1181# _doctest_retry_count += 1
182 if _doctest_retry_count % 2 == 1:182# if _doctest_retry_count % 2 == 1:
183 print "Retry raised."183# print "Retry raised."
184 raise RetryTest184# raise RetryTest
185 raise RuntimeError("Retry not raised.")185# raise RuntimeError("Retry not raised.")
186186
187187
188def testRetryTestInDoctest2():188#def testRetryTestInDoctest2():
189 """Test a RetryTest exception in a doctest works as expected.189# """Test a RetryTest exception in a doctest works as expected.
190190
191 This test is the same as testRetryTestInDoctest, except it confirms191# This test is the same as testRetryTestInDoctest, except it confirms
192 that a RetryException raised where a different exception was expected192# that a RetryException raised where a different exception was expected
193 works.193# works.
194194
195 >>> retry_on_odd_numbered_calls2()195# >>> retry_on_odd_numbered_calls2()
196 Traceback (most recent call last):196# Traceback (most recent call last):
197 ...197# ...
198 RuntimeError: Retry not raised.198# RuntimeError: Retry not raised.
199 """199# """
200200
201201
202202
203203
=== modified file 'lib/canonical/widgets/date.py'
--- lib/canonical/widgets/date.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/widgets/date.py 2009-08-13 19:36:01 +0000
@@ -80,7 +80,7 @@
80 >>> print widget.getInputValue() #doctest: +ELLIPSIS80 >>> print widget.getInputValue() #doctest: +ELLIPSIS
81 Traceback (most recent call last):81 Traceback (most recent call last):
82 ...82 ...
83 WidgetInputError: (... Please pick a date after 2006-05-22 17:00:00)83 WidgetInputError: (...Please pick a date after 2006-05-22 17:00:00...)
8484
85 If the date provided is greater than from_date then the widget works as85 If the date provided is greater than from_date then the widget works as
86 expected.86 expected.
@@ -96,7 +96,7 @@
96 >>> print widget.getInputValue() #doctest: +ELLIPSIS96 >>> print widget.getInputValue() #doctest: +ELLIPSIS
97 Traceback (most recent call last):97 Traceback (most recent call last):
98 ...98 ...
99 WidgetInputError: (... Please pick a date before 2008-01-25 16:00:00)99 WidgetInputError: (...Please pick a date before 2008-01-25 16:00:00...)
100100
101 A datetime picker can be disabled initially:101 A datetime picker can be disabled initially:
102102
@@ -288,7 +288,7 @@
288 value = super(DateTimeWidget, self).getInputValue()288 value = super(DateTimeWidget, self).getInputValue()
289 if value is None:289 if value is None:
290 return None290 return None
291 # Establish if the value is within the date range. 291 # Establish if the value is within the date range.
292 self._align_date_constraints_with_time_zone()292 self._align_date_constraints_with_time_zone()
293 if self.from_date is not None and value < self.from_date:293 if self.from_date is not None and value < self.from_date:
294 limit = self.from_date.strftime(self.timeformat)294 limit = self.from_date.strftime(self.timeformat)
@@ -570,4 +570,3 @@
570 return u""570 return u""
571 value = value.astimezone(time_zone)571 value = value.astimezone(time_zone)
572 return escape(value.strftime("%Y-%m-%d %H:%M:%S %Z"))572 return escape(value.strftime("%Y-%m-%d %H:%M:%S %Z"))
573
574573
=== modified file 'lib/lp/answers/interfaces/questiontarget.py'
--- lib/lp/answers/interfaces/questiontarget.py 2009-06-24 23:10:46 +0000
+++ lib/lp/answers/interfaces/questiontarget.py 2009-08-13 15:22:00 +0000
@@ -13,8 +13,6 @@
13 'ISearchQuestionsForm',13 'ISearchQuestionsForm',
14 ]14 ]
1515
16import sets
17
18from zope.interface import Interface16from zope.interface import Interface
19from zope.schema import Choice, List, Set, TextLine17from zope.schema import Choice, List, Set, TextLine
2018
@@ -156,7 +154,7 @@
156154
157 status = Set(title=_('Status'), required=False,155 status = Set(title=_('Status'), required=False,
158 value_type=Choice(vocabulary=QuestionStatus),156 value_type=Choice(vocabulary=QuestionStatus),
159 default=sets.Set(QUESTION_STATUS_DEFAULT_SEARCH))157 default=set(QUESTION_STATUS_DEFAULT_SEARCH))
160158
161159
162class IAnswersFrontPageSearchForm(ISearchQuestionsForm):160class IAnswersFrontPageSearchForm(ISearchQuestionsForm):
163161
=== modified file 'lib/lp/answers/stories/answer-contact-report.txt'
--- lib/lp/answers/stories/answer-contact-report.txt 2009-06-12 16:36:02 +0000
+++ lib/lp/answers/stories/answer-contact-report.txt 2009-08-13 13:33:26 +0000
@@ -1,6 +1,6 @@
1= Answer Contact Report =1= Answer Contact Report =
22
3To view the answer contact report for a given person, the user chooses 3To view the answer contact report for a given person, the user chooses
4the 'Answer Contact For' link from the actions portlet while viewing4the 'Answer Contact For' link from the actions portlet while viewing
5the Person's page.5the Person's page.
66
@@ -11,7 +11,7 @@
1111
12Since No Privileges Person is not an answer contact, the report states12Since No Privileges Person is not an answer contact, the report states
13that.13that.
14 14
15 >>> content = find_main_content(anon_browser.contents)15 >>> content = find_main_content(anon_browser.contents)
16 >>> print content.find('p').renderContents()16 >>> print content.find('p').renderContents()
17 No Privileges Person is not an answer contact for any project.17 No Privileges Person is not an answer contact for any project.
@@ -23,7 +23,7 @@
23 >>> anon_browser.getLink('Answer contact for').click()23 >>> anon_browser.getLink('Answer contact for').click()
24 >>> print anon_browser.title24 >>> print anon_browser.title
25 Projects for which Foo Bar is an answer contact25 Projects for which Foo Bar is an answer contact
26 26
27 >>> content = find_tag_by_id(27 >>> content = find_tag_by_id(
28 ... anon_browser.contents, "direct-answer-contacts-for-list")28 ... anon_browser.contents, "direct-answer-contacts-for-list")
29 >>> print extract_text(content).encode('ascii', 'backslashreplace')29 >>> print extract_text(content).encode('ascii', 'backslashreplace')
@@ -35,7 +35,7 @@
35 >>> print extract_text(content)35 >>> print extract_text(content)
36 Gnome Baker36 Gnome Baker
37 The Gnome Panel Applets37 The Gnome Panel Applets
38 38
39Clicking on the name of the project will show the project answers.39Clicking on the name of the project will show the project answers.
4040
41 >>> anon_browser.getLink('Gnome Baker').click()41 >>> anon_browser.getLink('Gnome Baker').click()
@@ -51,18 +51,18 @@
51 >>> browser.getLink('Answer contact for').click()51 >>> browser.getLink('Answer contact for').click()
52 >>> print browser.title52 >>> print browser.title
53 Projects for which Sample Person is an answer contact53 Projects for which Sample Person is an answer contact
54 54
55 >>> content = find_tag_by_id(55 >>> content = find_tag_by_id(
56 ... browser.contents, "team-answer-contacts-for-list")56 ... browser.contents, "team-answer-contacts-for-list")
57 >>> print extract_text(content)57 >>> print extract_text(content)
58 Gnome Baker &mdash; Remove team58 Gnome Baker &mdash; Remove team
59 The Gnome Panel Applets &mdash; Remove team59 The Gnome Panel Applets &mdash; Remove team
60 60
61 >>> browser.getLink(id="gnomebaker-setteamanswercontact").click()61 >>> browser.getLink(id="gnomebaker-setteamanswercontact").click()
62 >>> print browser.title62 >>> print browser.title
63 Answer contact for Gnome Baker63 Answer contact for Gnome Baker
64 64
65The Remove yourself/team links only appears in his profile. He cannot 65The Remove yourself/team links only appears in his profile. He cannot
66see the link for other users66see the link for other users
6767
68 >>> browser.open(68 >>> browser.open(
@@ -70,7 +70,7 @@
70 >>> browser.getLink('Answer contact for').click()70 >>> browser.getLink('Answer contact for').click()
71 >>> print browser.title71 >>> print browser.title
72 Projects for which Foo Bar is an answer contact72 Projects for which Foo Bar is an answer contact
73 73
74 >>> content = find_tag_by_id(browser.contents, "direct-answer-contacts-for-list")74 >>> content = find_tag_by_id(browser.contents, "direct-answer-contacts-for-list")
75 >>> print extract_text(content).encode('ascii', 'backslashreplace')75 >>> print extract_text(content).encode('ascii', 'backslashreplace')
76 Gnome Baker76 Gnome Baker
7777
=== modified file 'lib/lp/answers/stories/questions-index.txt'
--- lib/lp/answers/stories/questions-index.txt 2009-03-24 12:43:49 +0000
+++ lib/lp/answers/stories/questions-index.txt 2009-08-13 21:32:59 +0000
@@ -60,7 +60,7 @@
60 ... anon_browser.contents, 'most-active-projects'))60 ... anon_browser.contents, 'most-active-projects'))
61 Most active projects61 Most active projects
62 Ubuntu62 Ubuntu
63 Mozilla Firefox63 Mozilla Firefox ...
6464
65Clicking on these project links will bring the user to the project65Clicking on these project links will bring the user to the project
66Answers front page:66Answers front page:
6767
=== modified file 'lib/lp/answers/templates/person-answer-contact-for.pt'
--- lib/lp/answers/templates/person-answer-contact-for.pt 2009-07-17 17:59:07 +0000
+++ lib/lp/answers/templates/person-answer-contact-for.pt 2009-08-13 13:33:26 +0000
@@ -23,8 +23,8 @@
2323
24 <ul class="listing" id="direct-answer-contacts-for-list">24 <ul class="listing" id="direct-answer-contacts-for-list">
25 <li tal:repeat="question_target view/direct_question_targets">25 <li tal:repeat="question_target view/direct_question_targets">
26 <a href="#"26 <a
27 tal:attributes="href string:${question_target/fmt:url}"27 tal:attributes="href question_target/fmt:url:answers"
28 tal:content="question_target/title">Project Title</a> 28 tal:content="question_target/title">Project Title</a>
29 <tal:link condition="view/showRemoveYourselfLink">29 <tal:link condition="view/showRemoveYourselfLink">
30 &mdash; 30 &mdash;
@@ -42,8 +42,8 @@
42 42
43 <ul class="listing" id="team-answer-contacts-for-list">43 <ul class="listing" id="team-answer-contacts-for-list">
44 <li tal:repeat="question_target view/team_question_targets">44 <li tal:repeat="question_target view/team_question_targets">
45 <a href="#"45 <a
46 tal:attributes="href string:${question_target/fmt:url}"46 tal:attributes="href question_target/fmt:url:answers"
47 tal:content="question_target/title">Project Title</a> 47 tal:content="question_target/title">Project Title</a>
48 <tal:link condition="view/showRemoveYourselfLink">48 <tal:link condition="view/showRemoveYourselfLink">
49 &mdash; 49 &mdash;
5050
=== modified file 'lib/lp/answers/templates/questions-index.pt'
--- lib/lp/answers/templates/questions-index.pt 2009-07-17 17:59:07 +0000
+++ lib/lp/answers/templates/questions-index.pt 2009-08-13 21:32:59 +0000
@@ -81,11 +81,7 @@
81 tal:condition="projects"81 tal:condition="projects"
82 style="list-style: none;">82 style="list-style: none;">
83 <li tal:repeat="project projects">83 <li tal:repeat="project projects">
84 <a href="#"84 <a tal:replace="structure project/fmt:link:answers"/>
85 tal:attributes="href project/fmt:url">
86 <img alt="" tal:replace="structure project/image:icon" />
87 <span tal:replace="project/displayname" />
88 </a>
89 </li>85 </li>
90 </ul>86 </ul>
91 </div>87 </div>
@@ -110,28 +106,29 @@
110 </table>106 </table>
111 </div>107 </div>
112 </div>108 </div>
113 <div id="application-footer">109
114 <div>110 <div id="application-footer">
115 <strong111 <div>
116 tal:content="view/answered_question_count"112 <strong
117 >52</strong>113 tal:content="view/answered_question_count"
118 questions answered and114 >52</strong>
119 <strong115 questions answered and
120 tal:content="view/solved_question_count"116 <strong
121 >52</strong>117 tal:content="view/solved_question_count"
122 questions solved out of118 >52</strong>
123 </div>119 questions solved out of
124 <div>120 </div>
125 <strong121 <div>
126 tal:content="view/question_count"122 <strong
127 >318</strong>123 tal:content="view/question_count"
128 questions asked across124 >318</strong>
129 <strong125 questions asked across
130 tal:content="view/projects_with_questions_count"126 <strong
131 >28</strong>127 tal:content="view/projects_with_questions_count"
132 projects128 >28</strong>
133 </div>129 projects
134 </div>130 </div>
131 </div>
135132
136 </div><!--main-->133 </div><!--main-->
137134
138135
=== modified file 'lib/lp/app/browser/tests/base-layout.txt'
--- lib/lp/app/browser/tests/base-layout.txt 2009-08-06 11:07:45 +0000
+++ lib/lp/app/browser/tests/base-layout.txt 2009-08-13 02:24:56 +0000
@@ -44,7 +44,6 @@
44 <body id="document" class="tab-overview main_side public yui-skin-sam">44 <body id="document" class="tab-overview main_side public yui-skin-sam">
45 <div class="yui-d0">45 <div class="yui-d0">
46 <div id="locationbar"> ...46 <div id="locationbar"> ...
47 <form id="globalsearch" ...
48 <div id="lp-hierarchy" class="home">...47 <div id="lp-hierarchy" class="home">...
49 <div class="location-portlet top-portlet">48 <div class="location-portlet top-portlet">
50 <img ...49 <img ...
@@ -77,7 +76,8 @@
77 </div><!-- yui-b side -->76 </div><!-- yui-b side -->
78 </div><!-- yui-t4 -->77 </div><!-- yui-t4 -->
79 <div id="footer" class="footer"> ...78 <div id="footer" class="footer"> ...
80 </div><!-- footer-->79 <form id="globalsearch" ...
80 </div>
81 </div><!-- yui-d0-->81 </div><!-- yui-d0-->
82 <script>LP.client.cache['context'] ...82 <script>LP.client.cache['context'] ...
83 </body>83 </body>
@@ -105,7 +105,6 @@
105 <body id="document" class="tab-overview main_only public yui-skin-sam">105 <body id="document" class="tab-overview main_only public yui-skin-sam">
106 <div class="yui-d0">106 <div class="yui-d0">
107 <div id="locationbar"> ...107 <div id="locationbar"> ...
108 <form id="globalsearch" ...
109 <div id="lp-hierarchy" class="home">...108 <div id="lp-hierarchy" class="home">...
110 <div class="location-portlet top-portlet">109 <div class="location-portlet top-portlet">
111 <img ...110 <img ...
@@ -134,7 +133,8 @@
134 <!-- yui-b side -->133 <!-- yui-b side -->
135 <!-- yui-t4 -->134 <!-- yui-t4 -->
136 <div id="footer" class="footer"> ...135 <div id="footer" class="footer"> ...
137 </div><!-- footer-->136 <form id="globalsearch" ...
137 </div>
138 </div><!-- yui-d0-->138 </div><!-- yui-d0-->
139 <script>LP.client.cache['context'] ...139 <script>LP.client.cache['context'] ...
140 </body>140 </body>
@@ -191,7 +191,7 @@
191 <!-- yui-b side -->191 <!-- yui-b side -->
192 <!-- yui-t4 -->192 <!-- yui-t4 -->
193 <div id="footer" class="footer"> ...193 <div id="footer" class="footer"> ...
194 </div><!-- footer-->194 </div>
195 </div><!-- yui-d0-->195 </div><!-- yui-d0-->
196 <script>LP.client.cache['context'] ...196 <script>LP.client.cache['context'] ...
197 </body>197 </body>
@@ -241,7 +241,7 @@
241 <!-- yui-b side -->241 <!-- yui-b side -->
242 <!-- yui-t4 -->242 <!-- yui-t4 -->
243 <div id="footer" class="footer"> ...243 <div id="footer" class="footer"> ...
244 </div><!-- footer-->244 </div>
245 </div><!-- yui-d0-->245 </div><!-- yui-d0-->
246 <script>LP.client.cache['context'] ...246 <script>LP.client.cache['context'] ...
247 </body>247 </body>
248248
=== modified file 'lib/lp/app/templates/base-layout-macros.pt'
--- lib/lp/app/templates/base-layout-macros.pt 2009-08-06 11:07:45 +0000
+++ lib/lp/app/templates/base-layout-macros.pt 2009-08-13 02:24:56 +0000
@@ -330,4 +330,41 @@
330 </ul>330 </ul>
331</metal:location-tabs>331</metal:location-tabs>
332332
333
334<metal:footer define-macro="footer">
335 <div id="footer" class="footer">
336 <div class="lp-arcana">
337 <img src="/@@/launchpad-logo-and-name-hierarchy.png" alt="Launchpad"/>
338 &nbsp;&bull;&nbsp;
339 <a href="/+tour">Take the tour</a>
340 &nbsp;&bull;&nbsp;
341 <a href="https://help.launchpad.net/">Read the guide</a>
342 &nbsp;
343 <form id="globalsearch" method="get" accept-charset="UTF-8"
344 class="sprite-after search-icon"
345 tal:condition="view/macro:pagehas/globalsearch"
346 tal:attributes="action string:${rooturl}+search">
347 <input type="search" id="search-text" name="field.text" />
348 </form>
349 </div>
350
351 <div class="colophon">
352 &copy; 2004-2009
353 <a href="http://canonical.com/">Canonical&nbsp;Ltd.</a>
354 &nbsp;&bull;&nbsp;
355 <a href="/legal">Terms of use</a>
356 &nbsp;&bull;&nbsp;
357 <a href="/feedback"
358 tal:condition="request/lp:person">Contact us</a>
359 <span id="lp-version" tal:condition="not:is_lpnet">
360 &nbsp;&bull;&nbsp;
361 <a href="https://help.launchpad.net/LaunchpadReleases"
362 tal:content="version"/>
363 <tal:devmode condition="devmode">devmode</tal:devmode>
364 <tal:demo condition="is_demo">demo site</tal:demo>
365 <tal:edge condition="is_edge">beta site</tal:edge>
366 </span>
367 </div>
368 </div>
369</metal:footer>
333</macros>370</macros>
334371
=== modified file 'lib/lp/app/templates/base-layout.pt'
--- lib/lp/app/templates/base-layout.pt 2009-08-11 04:35:26 +0000
+++ lib/lp/app/templates/base-layout.pt 2009-08-13 02:24:56 +0000
@@ -81,12 +81,6 @@
81 <div class="yui-d0">81 <div class="yui-d0">
82 <div id="locationbar">82 <div id="locationbar">
83 <tal:login replace="structure context/@@login_status" />83 <tal:login replace="structure context/@@login_status" />
84 <form id="globalsearch" method="get" accept-charset="UTF-8"
85 class="sprite search-icon"
86 tal:condition="view/macro:pagehas/globalsearch"
87 tal:attributes="action string:${rooturl}+search">
88 <input type="search" id="search-text" name="field.text" />
89 </form>
90 <tal:hierarchy replace="structure context/@@+hierarchy" />84 <tal:hierarchy replace="structure context/@@+hierarchy" />
91 </div><!--id="locationbar"-->85 </div><!--id="locationbar"-->
9286
@@ -135,28 +129,8 @@
135 </div><!-- yui-b side -->129 </div><!-- yui-b side -->
136 </div><!-- yui-t4 -->130 </div><!-- yui-t4 -->
137131
138 <div id="footer" class="footer">132 <metal:footer
139 <div id="lp-arcana">133 use-macro="context/@@+base-layout-macros/footer"/>
140 &copy;&nbsp;2004-2009&nbsp;<a
141 href="http://canonical.com/">Canonical&nbsp;Ltd.</a> |
142 <a href="/legal">Terms of use</a>
143 <span id="lp-version" tal:condition="not:is_lpnet"> |
144 <a href="https://help.launchpad.net/LaunchpadReleases"
145 tal:content="version"/>
146 <tal:devmode condition="devmode">devmode</tal:devmode>
147 <tal:demo condition="is_demo">demo site</tal:demo>
148 <tal:edge condition="is_edge">beta site</tal:edge>
149 </span>
150 </div>
151
152 <div id="colophon">
153 <a href="/+tour"
154 tal:condition="not:request/lp:person">What is Launchpad?</a>
155 <a href="/feedback"
156 tal:condition="request/lp:person">Contact us</a> |
157 <a href="https://help.launchpad.net/">Get help with Launchpad</a>
158 </div>
159 </div><!-- footer-->
160 </div><!-- yui-d0-->134 </div><!-- yui-d0-->
161135
162 <metal:lp-client-cache136 <metal:lp-client-cache
163137
=== modified file 'lib/lp/bugs/stories/bugtracker/xx-bugtracker-handshake-tokens.txt'
--- lib/lp/bugs/stories/bugtracker/xx-bugtracker-handshake-tokens.txt 2009-06-12 16:36:02 +0000
+++ lib/lp/bugs/stories/bugtracker/xx-bugtracker-handshake-tokens.txt 2009-07-16 13:28:25 +0000
@@ -24,4 +24,4 @@
24 >>> anon_browser.open(token_url)24 >>> anon_browser.open(token_url)
25 Traceback (most recent call last):25 Traceback (most recent call last):
26 ...26 ...
27 HTTPError: HTTP Error 405: Method Not Allowed27 httperror_seek_wrapper: HTTP Error 405: Method Not Allowed
2828
=== modified file 'lib/lp/bugs/stories/bugtracker/xx-bugtracker-remote-bug.txt'
--- lib/lp/bugs/stories/bugtracker/xx-bugtracker-remote-bug.txt 2009-08-11 06:29:52 +0000
+++ lib/lp/bugs/stories/bugtracker/xx-bugtracker-remote-bug.txt 2009-08-12 13:34:48 +0000
@@ -38,7 +38,7 @@
38 >>> browser.open('http://launchpad.dev/bugs/bugtrackers/mozilla.org/99999')38 >>> browser.open('http://launchpad.dev/bugs/bugtrackers/mozilla.org/99999')
39 Traceback (most recent call last):39 Traceback (most recent call last):
40 ...40 ...
41 HTTPError: HTTP Error 404: Not Found41 httperror_seek_wrapper: HTTP Error 404: Not Found
42 >>> browser.handleErrors = False42 >>> browser.handleErrors = False
4343
4444
4545
=== modified file 'lib/lp/bugs/stories/bugwatches/xx-bugwatch-comments.txt'
--- lib/lp/bugs/stories/bugwatches/xx-bugwatch-comments.txt 2009-07-01 13:16:44 +0000
+++ lib/lp/bugs/stories/bugwatches/xx-bugwatch-comments.txt 2009-08-13 21:32:59 +0000
@@ -83,7 +83,7 @@
83Launchpad Beta Testers team such as sabdfl:83Launchpad Beta Testers team such as sabdfl:
8484
85 >>> sabdfl_browser.open(85 >>> sabdfl_browser.open(
86 ... 'http://launchpad.dev/redfish/+bug/15/comments/1')86 ... 'http://bugs.launchpad.dev/redfish/+bug/15/comments/1')
87 >>> print_comments(sabdfl_browser.contents)87 >>> print_comments(sabdfl_browser.contents)
88 <p>Package: gnome-volume-<wbr></wbr>manager<br />88 <p>Package: gnome-volume-<wbr></wbr>manager<br />
89 Version: 1.2.0-1<br />89 Version: 1.2.0-1<br />
@@ -125,11 +125,12 @@
125 >>> logout()125 >>> logout()
126126
127 >>> sabdfl_browser.open(127 >>> sabdfl_browser.open(
128 ... 'http://launchpad.dev/redfish/+bug/15/comments/1')128 ... 'http://bugs.launchpad.dev/redfish/+bug/15/comments/1')
129 >>> print_comments(sabdfl_browser.contents)129 >>> print_comments(sabdfl_browser.contents)
130130
131Anonymous users can't see the comment either.131Anonymous users can't see the comment either.
132132
133 >>> anon_browser.open('http://launchpad.dev/redfish/+bug/15/comments/1')133 >>> anon_browser.open(
134 ... 'http://bugs.launchpad.dev/redfish/+bug/15/comments/1')
134 >>> print_comments(anon_browser.contents)135 >>> print_comments(anon_browser.contents)
135136
136137
=== modified file 'lib/lp/code/stories/branches/xx-upload-directions.txt'
--- lib/lp/code/stories/branches/xx-upload-directions.txt 2009-04-17 10:32:16 +0000
+++ lib/lp/code/stories/branches/xx-upload-directions.txt 2009-08-13 21:32:59 +0000
@@ -45,7 +45,7 @@
45 >>> instructions = find_tag_by_id(content, 'upload-directions')45 >>> instructions = find_tag_by_id(content, 'upload-directions')
46 >>> print instructions.renderContents()46 >>> print instructions.renderContents()
47 Only47 Only
48 <a href="/~name12">Sample Person</a>48 <a href="http://launchpad.dev/~name12">Sample Person</a>
49 can upload to this branch. If you are Sample Person please49 can upload to this branch. If you are Sample Person please
50 <a href="+login">log in</a> for upload directions.50 <a href="+login">log in</a> for upload directions.
5151
@@ -57,7 +57,7 @@
57 >>> instructions = find_tag_by_id(content, 'upload-directions')57 >>> instructions = find_tag_by_id(content, 'upload-directions')
58 >>> print instructions.renderContents()58 >>> print instructions.renderContents()
59 You cannot upload to this branch. Only59 You cannot upload to this branch. Only
60 <a href="/~name12">Sample Person</a>60 <a href="http://launchpad.dev/~name12">Sample Person</a>
61 can upload to this branch.61 can upload to this branch.
6262
63The user is the owner of the branch and logs in. The page gives the full upload63The user is the owner of the branch and logs in. The page gives the full upload
@@ -90,7 +90,7 @@
90 >>> instructions = find_tag_by_id(content, 'ssh-key-directions')90 >>> instructions = find_tag_by_id(content, 'ssh-key-directions')
91 >>> print instructions.renderContents()91 >>> print instructions.renderContents()
92 To authenticate with the Launchpad branch upload service, you need to92 To authenticate with the Launchpad branch upload service, you need to
93 <a href="/~name12/+editsshkeys">93 <a href="http://launchpad.dev/~name12/+editsshkeys">
94 register a SSH key </a>.94 register a SSH key </a>.
9595
96Click the link and register a key.96Click the link and register a key.
@@ -149,7 +149,7 @@
149 >>> instructions = find_tag_by_id(content, 'upload-directions')149 >>> instructions = find_tag_by_id(content, 'upload-directions')
150 >>> print instructions.renderContents()150 >>> print instructions.renderContents()
151 Members of <a151 Members of <a
152 href="/~landscape-developers">Landscape152 href="http://launchpad.dev/~landscape-developers">Landscape
153 Developers</a> can upload to this branch. <a href="+login">Log in</a> for153 Developers</a> can upload to this branch. <a href="+login">Log in</a> for
154 directions.154 directions.
155155
@@ -161,7 +161,7 @@
161 >>> instructions = find_tag_by_id(content, 'upload-directions')161 >>> instructions = find_tag_by_id(content, 'upload-directions')
162 >>> print instructions.renderContents()162 >>> print instructions.renderContents()
163 You cannot upload to this branch. Members of <a163 You cannot upload to this branch. Members of <a
164 href="/~landscape-developers">Landscape164 href="http://launchpad.dev/~landscape-developers">Landscape
165 Developers</a> can upload to this branch.165 Developers</a> can upload to this branch.
166166
167Finally, if the user is a member of the team, we display the same "you can167Finally, if the user is a member of the team, we display the same "you can
168168
=== modified file 'lib/lp/code/templates/project-branches.pt'
--- lib/lp/code/templates/project-branches.pt 2009-07-17 17:59:07 +0000
+++ lib/lp/code/templates/project-branches.pt 2009-08-10 19:04:22 +0000
@@ -6,24 +6,22 @@
6 xml:lang="en"6 xml:lang="en"
7 lang="en"7 lang="en"
8 dir="ltr"8 dir="ltr"
9 metal:use-macro="context/@@main_template/master"9 metal:use-macro="view/macro:page/onecolumn"
10 i18n:domain="launchpad"10 i18n:domain="launchpad"
11>11>
1212
13<body>13<body>
1414
15<metal:leftportlets fill-slot="portlets_one">15<div metal:fill-slot="main" tal:define="branches view/branches">
16</metal:leftportlets>16
1717 <div style="float:right" id="floating-links"
18<metal:rightportlets fill-slot="portlets_two">18 tal:define="menu context/menu:overview">
19</metal:rightportlets>19 <div tal:define="link menu/branch_visibility"
2020 tal:condition="link/enabled"
21<metal:heading fill-slot="pageheading">21 tal:content="structure link/render" />
22 </div>
23
22 <h1>Bazaar branches for <tal:project-name replace="context/displayname"/></h1>24 <h1>Bazaar branches for <tal:project-name replace="context/displayname"/></h1>
23</metal:heading>
24
25<div metal:fill-slot="main"
26 tal:define="branches view/branches">
2725
28 <tal:branchlisting content="structure branches/@@+branch-listing" />26 <tal:branchlisting content="structure branches/@@+branch-listing" />
2927
3028
=== modified file 'lib/lp/registry/browser/configure.zcml'
--- lib/lp/registry/browser/configure.zcml 2009-08-12 13:20:10 +0000
+++ lib/lp/registry/browser/configure.zcml 2009-08-14 00:52:28 +0000
@@ -480,8 +480,11 @@
480 <browser:menus480 <browser:menus
481 classes="481 classes="
482 ProjectFacets482 ProjectFacets
483 ProjectActionMenu
484 ProjectEditNavigationMenu
483 ProjectOverviewMenu485 ProjectOverviewMenu
484 ProjectBountiesMenu486 ProjectBountiesMenu
487 ProjectBugsMenu
485 ProjectSeriesSpecificationsMenu488 ProjectSeriesSpecificationsMenu
486 ProjectSpecificationsMenu489 ProjectSpecificationsMenu
487 ProjectSetContextMenu490 ProjectSetContextMenu
488491
=== modified file 'lib/lp/registry/browser/project.py'
--- lib/lp/registry/browser/project.py 2009-08-05 01:49:41 +0000
+++ lib/lp/registry/browser/project.py 2009-08-13 18:10:15 +0000
@@ -13,12 +13,14 @@
13 'ProjectBountiesMenu',13 'ProjectBountiesMenu',
14 'ProjectBrandingView',14 'ProjectBrandingView',
15 'ProjectBreadcrumbBuilder',15 'ProjectBreadcrumbBuilder',
16 'ProjectBugsMenu',
16 'ProjectEditView',17 'ProjectEditView',
17 'ProjectFacets',18 'ProjectFacets',
18 'ProjectMaintainerReassignmentView',19 'ProjectMaintainerReassignmentView',
19 'ProjectNavigation',20 'ProjectNavigation',
20 'ProjectRdfView',21 'ProjectRdfView',
21 'ProjectReviewView',22 'ProjectReviewView',
23 'ProjectActionMenu',
22 'ProjectOverviewMenu',24 'ProjectOverviewMenu',
23 'ProjectSeriesSpecificationsMenu',25 'ProjectSeriesSpecificationsMenu',
24 'ProjectSetBreadcrumbBuilder',26 'ProjectSetBreadcrumbBuilder',
@@ -34,6 +36,7 @@
34from zope.component import getUtility36from zope.component import getUtility
35from zope.event import notify37from zope.event import notify
36from zope.formlib import form38from zope.formlib import form
39from zope.interface import implements, Interface
37from zope.schema import Choice40from zope.schema import Choice
3841
39from z3c.ptcompat import ViewPageTemplateFile42from z3c.ptcompat import ViewPageTemplateFile
@@ -41,6 +44,7 @@
41from canonical.cachedproperty import cachedproperty44from canonical.cachedproperty import cachedproperty
42from canonical.launchpad import _45from canonical.launchpad import _
43from canonical.launchpad.webapp.interfaces import NotFoundError46from canonical.launchpad.webapp.interfaces import NotFoundError
47from canonical.launchpad.webapp.menu import NavigationMenu
44from lp.registry.interfaces.product import IProductSet48from lp.registry.interfaces.product import IProductSet
45from lp.registry.interfaces.project import (49from lp.registry.interfaces.project import (
46 IProject, IProjectSeries, IProjectSet)50 IProject, IProjectSeries, IProjectSet)
@@ -137,41 +141,33 @@
137 def bugs(self):141 def bugs(self):
138 site = 'bugs'142 site = 'bugs'
139 text = 'Bugs'143 text = 'Bugs'
140
141 return Link('', text, enabled=self.context.hasProducts(), site=site)144 return Link('', text, enabled=self.context.hasProducts(), site=site)
142145
143 def answers(self):146 def answers(self):
144 site = 'answers'147 site = 'answers'
145 text = 'Answers'148 text = 'Answers'
146
147 return Link('', text, enabled=self.context.hasProducts(), site=site)149 return Link('', text, enabled=self.context.hasProducts(), site=site)
148150
149 def specifications(self):151 def specifications(self):
150 site = 'blueprints'152 site = 'blueprints'
151 text = 'Blueprints'153 text = 'Blueprints'
152
153 return Link('', text, enabled=self.context.hasProducts(), site=site)154 return Link('', text, enabled=self.context.hasProducts(), site=site)
154155
155 def translations(self):156 def translations(self):
156 site = 'translations'157 site = 'translations'
157 text = 'Translations'158 text = 'Translations'
158
159 return Link('', text, enabled=self.context.hasProducts(), site=site)159 return Link('', text, enabled=self.context.hasProducts(), site=site)
160160
161161
162class ProjectOverviewMenu(ApplicationMenu):162class ProjectAdminMenuMixin:
163163
164 usedfor = IProject164 @enabled_with_permission('launchpad.Admin')
165 facet = 'overview'165 def administer(self):
166 links = [166 text = 'Administer'
167 'edit', 'branding', 'driver', 'reassign', 'top_contributors',167 return Link('+review', text, icon='edit')
168 'mentorship', 'announce', 'announcements', 'administer',168
169 'branch_visibility', 'rdf', 'subscribe']169
170170class ProjectEditMenuMixin(ProjectAdminMenuMixin):
171 @enabled_with_permission('launchpad.Edit')
172 def edit(self):
173 text = 'Change details'
174 return Link('+edit', text, icon='edit')
175171
176 @enabled_with_permission('launchpad.Edit')172 @enabled_with_permission('launchpad.Edit')
177 def branding(self):173 def branding(self):
@@ -181,14 +177,30 @@
181 @enabled_with_permission('launchpad.Edit')177 @enabled_with_permission('launchpad.Edit')
182 def reassign(self):178 def reassign(self):
183 text = 'Change maintainer'179 text = 'Change maintainer'
184 return Link('+reassign', text, icon='edit')180 summary = 'Change the maintainer of this project group'
181 return Link('+reassign', text, summary, icon='edit')
185182
186 @enabled_with_permission('launchpad.Edit')183 @enabled_with_permission('launchpad.Edit')
187 def driver(self):184 def driver(self):
188 text = 'Appoint driver'185 text = 'Appoint driver'
189 summary = 'Someone with permission to set goals for all projects'186 summary = 'Appoint the driver of this project group'
190 return Link('+driver', text, summary, icon='edit')187 return Link('+driver', text, summary, icon='edit')
191188
189
190class ProjectOverviewMenu(ProjectEditMenuMixin, ApplicationMenu):
191
192 usedfor = IProject
193 facet = 'overview'
194 links = [
195 'branding', 'driver', 'reassign', 'top_contributors', 'mentorship',
196 'announce', 'announcements', 'branch_visibility', 'rdf',
197 'new_product', 'administer', 'milestones']
198
199 @enabled_with_permission('launchpad.Edit')
200 def new_product(self):
201 text = 'Register another project in %s' % self.context.displayname
202 return Link('+newproduct', text, icon='edit')
203
192 def top_contributors(self):204 def top_contributors(self):
193 text = 'More contributors'205 text = 'More contributors'
194 return Link('+topcontributors', text, icon='info')206 return Link('+topcontributors', text, icon='info')
@@ -213,26 +225,59 @@
213 enabled = bool(self.context.getAnnouncements())225 enabled = bool(self.context.getAnnouncements())
214 return Link('+announcements', text, enabled=enabled)226 return Link('+announcements', text, enabled=enabled)
215227
228 def milestones(self):
229 text = 'See all milestones'
230 return Link('+milestones', text)
231
216 def rdf(self):232 def rdf(self):
217 text = structured(233 text = structured(
218 'Download <abbr title="Resource Description Framework">'234 'Download <abbr title="Resource Description Framework">'
219 'RDF</abbr> metadata')235 'RDF</abbr> metadata')
220 return Link('+rdf', text, icon='download')236 return Link('+rdf', text, icon='download-icon')
221
222 @enabled_with_permission('launchpad.Admin')
223 def administer(self):
224 text = 'Administer'
225 return Link('+review', text, icon='edit')
226237
227 @enabled_with_permission('launchpad.Admin')238 @enabled_with_permission('launchpad.Admin')
228 def branch_visibility(self):239 def branch_visibility(self):
229 text = 'Define branch visibility'240 text = 'Define branch visibility'
230 return Link('+branchvisibility', text, icon='edit', site='mainsite')241 return Link('+branchvisibility', text, icon='edit', site='mainsite')
231242
243
244class IProjectActionMenu(Interface):
245 """Marker interface for views that use ProjectActionMenu."""
246
247
248class ProjectActionMenu(ProjectAdminMenuMixin, NavigationMenu):
249
250 usedfor = IProjectActionMenu
251 facet = 'overview'
252 title = 'Action menu'
253 links = ('subscribe', 'edit', 'administer')
254
255 # XXX: salgado, bug=412178, 2009-08-10: This should be shown in the +index
256 # page of the project's bugs facet, but that would require too much work
257 # and I just want to convert this page to 3.0, so I'll leave it here for
258 # now.
232 def subscribe(self):259 def subscribe(self):
233 text = 'Subscribe to bug mail'260 text = 'Subscribe to bug mail'
234 return Link('+subscribe', text, icon='edit')261 return Link('+subscribe', text, icon='edit')
235262
263 @enabled_with_permission('launchpad.Edit')
264 def edit(self):
265 text = 'Change details'
266 return Link('+edit', text, icon='edit')
267
268
269class IProjectEditMenu(Interface):
270 """A marker interface for the 'Change details' navigation menu."""
271
272
273class ProjectEditNavigationMenu(NavigationMenu, ProjectEditMenuMixin):
274 """A sub-menu for different aspects of editing a Project's details."""
275
276 usedfor = IProjectEditMenu
277 facet = 'overview'
278 title = 'Change project group'
279 links = ('branding', 'reassign', 'driver', 'administer')
280
236281
237class ProjectBountiesMenu(ApplicationMenu):282class ProjectBountiesMenu(ApplicationMenu):
238283
@@ -286,12 +331,24 @@
286 return Link('+addquestion', text, icon='add')331 return Link('+addquestion', text, icon='add')
287332
288333
334class ProjectBugsMenu(ApplicationMenu):
335
336 usedfor = IProject
337 facet = 'bugs'
338 links = ['new']
339
340 def new(self):
341 text = 'Report a Bug'
342 return Link('+filebug', text, icon='add')
343
344
289class ProjectView(HasAnnouncementsView, FeedsMixin):345class ProjectView(HasAnnouncementsView, FeedsMixin):
290 pass346 implements(IProjectActionMenu)
291347
292348
293class ProjectEditView(LaunchpadEditFormView):349class ProjectEditView(LaunchpadEditFormView):
294 """View class that lets you edit a Project object."""350 """View class that lets you edit a Project object."""
351 implements(IProjectEditMenu)
295352
296 label = "Change project group details"353 label = "Change project group details"
297 schema = IProject354 schema = IProject
298355
=== modified file 'lib/lp/registry/browser/tests/project-add-views.txt'
--- lib/lp/registry/browser/tests/project-add-views.txt 2009-05-12 08:11:06 +0000
+++ lib/lp/registry/browser/tests/project-add-views.txt 2009-08-13 19:36:01 +0000
@@ -310,4 +310,5 @@
310310
311 >>> for error in view.errors:311 >>> for error in view.errors:
312 ... print error312 ... print error
313 ('name', 'URL', badger is already used by another project)313 ('name', 'URL',
314 LaunchpadValidationError(u'badger is already used by another project'))
314315
=== modified file 'lib/lp/registry/doc/product-widgets.txt'
--- lib/lp/registry/doc/product-widgets.txt 2009-07-01 13:16:44 +0000
+++ lib/lp/registry/doc/product-widgets.txt 2009-08-13 19:36:01 +0000
@@ -301,7 +301,7 @@
301 ... print301 ... print
302302
303 >>> license_widget.getInputValue()303 >>> license_widget.getInputValue()
304 Set([<DBItem License.GNU_GPL_V2, (130) ...>])304 set([<DBItem License.GNU_GPL_V2, (130) ...>])
305305
306 >>> print_checked_items(license_widget())306 >>> print_checked_items(license_widget())
307 [ ] Apache License ...307 [ ] Apache License ...
308308
=== modified file 'lib/lp/registry/interfaces/product.py'
--- lib/lp/registry/interfaces/product.py 2009-08-10 17:08:27 +0000
+++ lib/lp/registry/interfaces/product.py 2009-08-13 15:22:00 +0000
@@ -24,7 +24,6 @@
2424
2525
26import re26import re
27import sets
2827
29from textwrap import dedent28from textwrap import dedent
3029
@@ -955,8 +954,7 @@
955 title=_('Licenses'),954 title=_('Licenses'),
956 value_type=Choice(vocabulary=License),955 value_type=Choice(vocabulary=License),
957 required=False,956 required=False,
958 # Zope requires sets.Set() instead of the builtin set().957 default=set(
959 default=sets.Set(
960 [License.OTHER_PROPRIETARY, License.OTHER_OPEN_SOURCE]))958 [License.OTHER_PROPRIETARY, License.OTHER_OPEN_SOURCE]))
961959
962 has_zero_licenses = Choice(960 has_zero_licenses = Choice(
963961
=== modified file 'lib/lp/registry/stories/announcements/xx-announcements.txt'
--- lib/lp/registry/stories/announcements/xx-announcements.txt 2009-08-11 17:32:21 +0000
+++ lib/lp/registry/stories/announcements/xx-announcements.txt 2009-08-13 18:10:15 +0000
@@ -265,7 +265,7 @@
265265
266 >>> anon_browser.open('http://launchpad.dev/apache')266 >>> anon_browser.open('http://launchpad.dev/apache')
267 >>> count_show_links(anon_browser.contents)267 >>> count_show_links(anon_browser.contents)
268 2268 1
269 >>> anon_browser.open('http://launchpad.dev/tomcat')269 >>> anon_browser.open('http://launchpad.dev/tomcat')
270 >>> count_show_links(anon_browser.contents)270 >>> count_show_links(anon_browser.contents)
271 1271 1
272272
=== modified file 'lib/lp/registry/stories/distribution/xx-distribution-countrymirrors.txt'
--- lib/lp/registry/stories/distribution/xx-distribution-countrymirrors.txt 2009-07-23 13:44:13 +0000
+++ lib/lp/registry/stories/distribution/xx-distribution-countrymirrors.txt 2009-08-05 18:52:52 +0000
@@ -60,5 +60,5 @@
60 >>> browser.open('http://launchpad.dev/debian/+countrymirrors-archive')60 >>> browser.open('http://launchpad.dev/debian/+countrymirrors-archive')
61 Traceback (most recent call last):61 Traceback (most recent call last):
62 ...62 ...
63 HTTPError: HTTP Error 404: Not Found63 httperror_seek_wrapper: HTTP Error 404: Not Found
6464
6565
=== modified file 'lib/lp/registry/stories/gpg-coc/98-cocacknowledge.txt'
--- lib/lp/registry/stories/gpg-coc/98-cocacknowledge.txt 2009-05-12 01:39:29 +0000
+++ lib/lp/registry/stories/gpg-coc/98-cocacknowledge.txt 2009-08-13 21:32:59 +0000
@@ -24,12 +24,8 @@
24 >>> admin_browser.getControl(name='searchfor').value = ["all"]24 >>> admin_browser.getControl(name='searchfor').value = ["all"]
25 >>> admin_browser.getControl(name='name').value = "mark"25 >>> admin_browser.getControl(name='name').value = "mark"
26 >>> admin_browser.getControl(name='search').click()26 >>> admin_browser.getControl(name='search').click()
27 >>> 'paper submission accepted by Foo Bar' in admin_browser.contents27 >>> print extract_text(find_tag_by_id(admin_browser.contents, 'matches'))
28 True28 Mark ... paper submission accepted by Foo Bar [ACTIVE]
29 >>> '<a href="http://launchpad.dev/~sabdfl"' in admin_browser.contents
30 True
31 >>> '[ACTIVE]' in admin_browser.contents
32 True
3329
34Test if the advertisement email was sent:30Test if the advertisement email was sent:
3531
3632
=== modified file 'lib/lp/registry/stories/mailinglists/lifecycle.txt'
--- lib/lp/registry/stories/mailinglists/lifecycle.txt 2009-07-23 13:44:13 +0000
+++ lib/lp/registry/stories/mailinglists/lifecycle.txt 2009-08-06 19:11:48 +0000
@@ -580,7 +580,7 @@
580 >>> admin_browser.getControl(name='field.aardvarks').value = ['approve']580 >>> admin_browser.getControl(name='field.aardvarks').value = ['approve']
581 >>> admin_browser.getControl('Submit').click()581 >>> admin_browser.getControl('Submit').click()
582 >>> act()582 >>> act()
583 >>> browser.reload()583 >>> browser.open(browser.url) # A `reload` would resubmit.
584 >>> browser.getLink('Mailing list archive')584 >>> browser.getLink('Mailing list archive')
585 <Link text='Mailing list archive'585 <Link text='Mailing list archive'
586 url='http://lists.launchpad.dev/aardvarks'>586 url='http://lists.launchpad.dev/aardvarks'>
@@ -618,7 +618,7 @@
618 >>> admin_browser.getControl(name='field.antelopes').value = ['approve']618 >>> admin_browser.getControl(name='field.antelopes').value = ['approve']
619 >>> admin_browser.getControl('Submit').click()619 >>> admin_browser.getControl('Submit').click()
620 >>> act()620 >>> act()
621 >>> user_browser.reload()621 >>> user_browser.open(user_browser.url) # A `reload` would resubmit.
622 >>> user_browser.getLink('Mailing list archive')622 >>> user_browser.getLink('Mailing list archive')
623 <Link text='Mailing list archive'623 <Link text='Mailing list archive'
624 url='http://lists.launchpad.dev/antelopes'>624 url='http://lists.launchpad.dev/antelopes'>
625625
=== modified file 'lib/lp/registry/stories/milestone/object-milestones.txt'
--- lib/lp/registry/stories/milestone/object-milestones.txt 2009-08-12 01:12:12 +0000
+++ lib/lp/registry/stories/milestone/object-milestones.txt 2009-08-14 00:52:28 +0000
@@ -88,7 +88,9 @@
8888
89=== Projects ===89=== Projects ===
9090
91The main project page has a portlet "Active milestones":91The project "All milestones" page lists all milestones for all products and
92series, including the inactive ones. They do not include the bug and blueprint
93counts (because they are costly to retrieve).
9294
93 >>> from canonical.launchpad.ftests import login, logout95 >>> from canonical.launchpad.ftests import login, logout
94 >>> from lp.registry.tests.test_project_milestone import (96 >>> from lp.registry.tests.test_project_milestone import (
@@ -98,15 +100,6 @@
98 >>> test_helper.setUpProjectMilestoneTests()100 >>> test_helper.setUpProjectMilestoneTests()
99 >>> logout()101 >>> logout()
100 >>> anon_browser.open('http://launchpad.dev/gnome')102 >>> anon_browser.open('http://launchpad.dev/gnome')
101 >>> print milestones_in_portlet(anon_browser)
102 2011-04-01 1.2
103 2010-04-02 1.1.
104 2010-04-01 1.1
105
106The project "All milestones" page lists all milestones for all products and
107series, including the inactive ones. They do not include the bug and blueprint
108counts (because they are costly to retrieve).
109
110 >>> anon_browser.getLink('See all milestones').click()103 >>> anon_browser.getLink('See all milestones').click()
111 >>> print all_milestones(anon_browser)104 >>> print all_milestones(anon_browser)
112 GNOME 2.1.6 None This is an inactive milestone105 GNOME 2.1.6 None This is an inactive milestone
113106
=== modified file 'lib/lp/registry/stories/product/xx-launchpad-project-search.txt'
--- lib/lp/registry/stories/product/xx-launchpad-project-search.txt 2009-06-11 19:21:46 +0000
+++ lib/lp/registry/stories/product/xx-launchpad-project-search.txt 2009-08-14 03:21:52 +0000
@@ -118,7 +118,7 @@
118118
119 >>> anon_browser.open('http://launchpad.dev/projects?text=ubuntu')119 >>> anon_browser.open('http://launchpad.dev/projects?text=ubuntu')
120 >>> print_search_results(anon_browser)120 >>> print_search_results(anon_browser)
121 sprite distribution Ubuntu121 bg-image Ubuntu
122 sprite distribution ubuntutest122 sprite distribution ubuntutest
123 sprite product Evolution123 sprite product Evolution
124 sprite product Tomcat124 sprite product Tomcat
125125
=== modified file 'lib/lp/registry/stories/project/xx-project-driver.txt'
--- lib/lp/registry/stories/project/xx-project-driver.txt 2008-07-02 12:51:24 +0000
+++ lib/lp/registry/stories/project/xx-project-driver.txt 2009-08-13 18:10:15 +0000
@@ -26,6 +26,6 @@
2626
27Sample Person is listed as the driver of the project.27Sample Person is listed as the driver of the project.
2828
29 >>> for tag in find_tags_by_class(browser.contents, 'summary'):29 >>> print extract_text(find_tag_by_id(browser.contents, 'driver'))
30 ... tag.find(text='Driver:').findNext('a').string30 Driver: Sample Person
31 u'Sample Person'31 Appoint driver
3232
=== modified file 'lib/lp/registry/stories/project/xx-project-edit.txt'
--- lib/lp/registry/stories/project/xx-project-edit.txt 2009-08-01 00:02:55 +0000
+++ lib/lp/registry/stories/project/xx-project-edit.txt 2009-08-13 18:10:15 +0000
@@ -53,21 +53,20 @@
5353
54The project summary shows the status as reviewed for admins only.54The project summary shows the status as reviewed for admins only.
5555
56 >>> for tag in find_tags_by_class(admin_browser.contents, 'summary'):56 >>> print extract_text(find_tag_by_id(admin_browser.contents, 'status'))
57 ... print extract_text(57 Status: Active Reviewed
58 ... tag.find(text='Project group status:').findParent('tr'))
59 Project group status:
60 Active
61 Reviewed
6258
63Other users cannot see the Project group status in the details portlet.59Other users cannot see the Project group status in the details portlet.
6460
65 >>> anon_browser.open('http://launchpad.dev/new-name')61 >>> anon_browser.open('http://launchpad.dev/new-name')
66 >>> print extract_text(62 >>> print extract_text(
67 ... find_tag_by_id(anon_browser.contents, 'portlet-details'))63 ... find_tag_by_id(anon_browser.contents, 'portlet-details'))
64 Project group information
68 Maintainer: Sample Person65 Maintainer: Sample Person
69 Driver: Not yet appointed66 Driver: Not yet appointed
70 Bug tracker: The Mozilla.org Bug Tracker67 Bug tracker: The Mozilla.org Bug Tracker
68 Registered ... by Sample Person
69 Download RDF metadata
7170
72Administrators can also change the maintainer and registrant independent71Administrators can also change the maintainer and registrant independent
73of each other, as well as adding aliases to the project group.72of each other, as well as adding aliases to the project group.
@@ -90,17 +89,14 @@
9089
91The project maintainer and registrant are now updated.90The project maintainer and registrant are now updated.
9291
93 >>> for tag in find_tags_by_class(admin_browser.contents, 'summary'):92 >>> print extract_text(
94 ... print extract_text(93 ... find_tag_by_id(admin_browser.contents, 'maintainer'))
95 ... tag.find(text='Maintainer:').findParent('tr'))94 Maintainer: Celso Providelo
96 Maintainer:95 Change maintainer
97 Celso Providelo
9896
99 >>> tag = find_tag_by_id(admin_browser.contents, 'portlet-lifecycle')
100 >>> print extract_text(97 >>> print extract_text(
101 ... tag.find(text='Registered by:').findParent('tr'))98 ... find_tag_by_id(admin_browser.contents, 'registrant'))
102 Registered by:99 Registered ... by David Allouche
103 David Allouche
104100
105The registrant really should only be a person, not a team, but that101The registrant really should only be a person, not a team, but that
106constraint has to be relaxed to account for old data where we do have102constraint has to be relaxed to account for old data where we do have
@@ -111,15 +107,6 @@
111 >>> admin_browser.getControl('Registrant').value = 'registry'107 >>> admin_browser.getControl('Registrant').value = 'registry'
112 >>> admin_browser.getControl('Change').click()108 >>> admin_browser.getControl('Change').click()
113109
114 >>> for tag in find_tags_by_class(admin_browser.contents, 'summary'):
115 ... print extract_text(
116 ... tag.find(text='Maintainer:').findParent('tr'))
117 Maintainer:
118 Celso Providelo
119
120 >>> tag = find_tag_by_id(admin_browser.contents, 'portlet-lifecycle')
121 >>> print extract_text(110 >>> print extract_text(
122 ... tag.find(text='Registered by:').findParent('tr'))111 ... find_tag_by_id(admin_browser.contents, 'registrant'))
123 Registered by:112 Registered ... by Registry Administrators
124 Registry Administrators
125
126113
=== modified file 'lib/lp/registry/stories/project/xx-project-index.txt'
--- lib/lp/registry/stories/project/xx-project-index.txt 2009-08-11 17:32:21 +0000
+++ lib/lp/registry/stories/project/xx-project-index.txt 2009-08-13 18:10:15 +0000
@@ -23,28 +23,28 @@
23 >>> anon_browser.title23 >>> anon_browser.title
24 'The GNOME Project in Launchpad'24 'The GNOME Project in Launchpad'
2525
26The page lists the member projects.26The page lists the member projects, together with the releases/milestones of
27its development focus.
2728
28 >>> print extract_text(find_tag_by_id(anon_browser.contents, 'products'))29 >>> print extract_text(find_tag_by_id(anon_browser.contents, 'products'))
29 Projects30 Projects
30 Gnome Applets ...31 Gnome Applets ...
31 Evolution ...32 Evolution ...
32 Releases: 2.1.633 trunk series: 2.1.6,
33 GNOME Terminal ...34 GNOME Terminal ...
34 Milestones: 2.30.0
35 gnomebaker ...35 gnomebaker ...
36 Releases: 2.1.736 trunk series: 2.1.7, 2.1.7
37 Milestones: 2.30.1, 2.1.7
38 NetApplet ...37 NetApplet ...
39 Releases: 1.038 trunk series: 1.0,
39 ...
4040
41And the projects, milestone, and releases are linked.41And the projects, milestone, and releases are linked.
4242
43 >>> print anon_browser.getLink('gnomebaker').url43 >>> print anon_browser.getLink('gnomebaker').url
44 http://launchpad.dev/gnomebaker44 http://launchpad.dev/gnomebaker
4545
46 >>> print anon_browser.getLink('2.30.1').url46 >>> print anon_browser.getLink('1.0').url
47 http://launchpad.dev/gnomebaker/+milestone/2.30.147 http://launchpad.dev/netapplet/trunk/1.0
4848
49 >>> print anon_browser.getLink('2.1.7').url49 >>> print anon_browser.getLink('2.1.7').url
50 http://launchpad.dev/gnomebaker/trunk/2.1.750 http://launchpad.dev/gnomebaker/trunk/2.1.7
5151
=== modified file 'lib/lp/registry/templates/codeofconduct-admin.pt'
--- lib/lp/registry/templates/codeofconduct-admin.pt 2009-07-22 14:19:22 +0000
+++ lib/lp/registry/templates/codeofconduct-admin.pt 2009-08-13 21:32:59 +0000
@@ -71,7 +71,7 @@
71 <tal:results condition="view/search">71 <tal:results condition="view/search">
72 <h2>Results</h2>72 <h2>Results</h2>
7373
74 <ul tal:condition="view/results">74 <ul id="matches" tal:condition="view/results">
75 <li tal:repeat="code view/results">75 <li tal:repeat="code view/results">
76 <a tal:replace="structure code/owner/fmt:link">OWNER</a>76 <a tal:replace="structure code/owner/fmt:link">OWNER</a>
77 <a tal:attributes="href code/id"77 <a tal:attributes="href code/id"
7878
=== modified file 'lib/lp/registry/templates/project-details.pt'
--- lib/lp/registry/templates/project-details.pt 2009-07-17 17:59:07 +0000
+++ lib/lp/registry/templates/project-details.pt 2009-08-13 18:10:15 +0000
@@ -4,53 +4,62 @@
4 xmlns:i18n="http://xml.zope.org/namespaces/i18n"4 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
5 omit-tag="">5 omit-tag="">
66
7<div class="portlet" id="portlet-details">7<div id="portlet-details" class="portlet">
8 <div class="portletBody portletContent">8 <h2>Project group information</h2>
9 <table class="summary">9 <div class="two-column-list" tal:define="overview_menu context/menu:overview">
10 <tbody>10 <dl tal:condition="context/required:launchpad.Admin" id="status">
11 <tr tal:condition="context/required:launchpad.Admin">11 <dt>Status:</dt>
12 <th>Project group status:</th>12 <dd>
13 <td>13 <tal:block condition="context/active">Active</tal:block>
14 <tal:block condition="context/active">Active</tal:block>14 <tal:block condition="not: context/active">Disabled</tal:block>
15 <tal:block condition="not: context/active">Disabled</tal:block>15 <tal:block condition="context/reviewed"><br />Reviewed</tal:block>
16 <tal:block condition="context/reviewed"><br />Reviewed</tal:block>16 <tal:block condition="context/icon"><br />Branded
17 <tal:block condition="context/icon"><br />Branded17 <tal:icon replace="structure context/image:icon" />
18 <tal:icon replace="structure context/image:icon" />18 </tal:block>
19 </tal:block>19 </dd>
20 </td>20 </dl>
21 </tr>21 <dl id="maintainer">
22 <tr>22 <dt>Maintainer:</dt>
23 <th>Maintainer:</th>23 <dd>
24 <td>24 <a tal:replace="structure context/owner/fmt:link" />
25 <a25 <tal:edit-maintainer
26 tal:content="context/owner/displayname"26 content="structure overview_menu/reassign/fmt:icon" />
27 tal:attributes="href context/owner/fmt:url"27 </dd>
28 >ownername</a>28 </dl>
29 </td>29 <dl id="driver">
30 </tr>30 <dt>Driver:</dt>
31 <tr>31 <dd>
32 <th>Driver:</th>32 <tal:no-driver condition="not:context/driver">
33 <td tal:condition="not:context/driver">Not yet appointed</td>33 Not yet appointed
34 <td tal:condition="context/driver">34 </tal:no-driver>
35 <a35 <tal:has-driver condition="context/driver">
36 tal:content="context/driver/displayname"36 <a tal:replace="structure context/driver/fmt:link">Driver</a>
37 tal:attributes="href context/driver/fmt:url"37 </tal:has-driver>
38 >Carlos Perello</a>38 <tal:edit-maintainer
39 </td>39 content="structure overview_menu/driver/fmt:icon" />
40 </tr>40 </dd>
41 <tr>41 </dl>
42 <th>Bug tracker:</th>42 <dl id="bug-tracker">
43 <td>43 <dt>Bug tracker:</dt>
44 <a44 <dd>
45 tal:condition="context/bugtracker"45 <a
46 tal:content="context/bugtracker/title"46 tal:condition="context/bugtracker"
47 tal:attributes="href context/bugtracker/fmt:url"47 tal:content="context/bugtracker/title"
48 >tracker title</a>48 tal:attributes="href context/bugtracker/fmt:url"
49 <tal:none condition="not:context/bugtracker">None specified</tal:none>49 >tracker title</a>
50 </td>50 <tal:none condition="not:context/bugtracker">None specified</tal:none>
51 </tr>51 </dd>
52 </tbody>52 </dl>
53 </table>
54 </div>53 </div>
54 <p id="registrant">
55 Registered
56 <tal:created replace="context/datecreated/fmt:approximatedate" />
57 by <tal:registrant replace="structure context/registrant/fmt:link" />
58 </p>
59 <ul class="horizontal">
60 <li>
61 <a tal:replace="structure context/menu:overview/rdf/fmt:link-icon" />
62 </li>
63 </ul>
55</div>64</div>
56</tal:root>65</tal:root>
5766
=== modified file 'lib/lp/registry/templates/project-edit.pt'
--- lib/lp/registry/templates/project-edit.pt 2009-07-18 00:05:49 +0000
+++ lib/lp/registry/templates/project-edit.pt 2009-08-05 19:26:37 +0000
@@ -3,20 +3,14 @@
3 xmlns:tal="http://xml.zope.org/namespaces/tal"3 xmlns:tal="http://xml.zope.org/namespaces/tal"
4 xmlns:metal="http://xml.zope.org/namespaces/metal"4 xmlns:metal="http://xml.zope.org/namespaces/metal"
5 xmlns:i18n="http://xml.zope.org/namespaces/i18n"5 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
6 xml:lang="en"6 metal:use-macro="view/macro:page/main_only"
7 lang="en"
8 dir="ltr"
9 metal:use-macro="context/@@main_template/master"
10 i18n:domain="launchpad"7 i18n:domain="launchpad"
11>8>
12 <body>9 <body>
13 <metal:heading fill-slot="heading">
14 <h1>Change project group details</h1>
15 </metal:heading>
16
17 <div metal:fill-slot="main">10 <div metal:fill-slot="main">
1811
19 <div metal:use-macro="context/@@launchpad_form/form">12 <div class="top-portlet"
13 metal:use-macro="context/@@launchpad_form/form">
2014
21 <p metal:fill-slot="extra_info">15 <p metal:fill-slot="extra_info">
22 Avoid changing the Name,16 Avoid changing the Name,
@@ -28,6 +22,7 @@
2822
29 </div>23 </div>
3024
25 <tal:menu replace="structure view/@@+related-pages" />
31 </div>26 </div>
3227
33 </body>28 </body>
3429
=== modified file 'lib/lp/registry/templates/project-index.pt'
--- lib/lp/registry/templates/project-index.pt 2009-07-17 17:59:07 +0000
+++ lib/lp/registry/templates/project-index.pt 2009-08-13 18:10:15 +0000
@@ -3,43 +3,37 @@
3 xmlns:tal="http://xml.zope.org/namespaces/tal"3 xmlns:tal="http://xml.zope.org/namespaces/tal"
4 xmlns:metal="http://xml.zope.org/namespaces/metal"4 xmlns:metal="http://xml.zope.org/namespaces/metal"
5 xmlns:i18n="http://xml.zope.org/namespaces/i18n"5 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
6 xml:lang="en"6 metal:use-macro="view/macro:page/main_side"
7 lang="en"
8 dir="ltr"
9 metal:use-macro="view/macro:page/default"
10 i18n:domain="launchpad"7 i18n:domain="launchpad"
11>8>
12 <body>9 <body>
1310
14 <metal:portlets fill-slot="portlets">11 <tal:heading metal:fill-slot="heading">
15 <tal:portlet tal:replace="structure context/@@+portlet-lifecycle" />12 <h1 tal:content="context/title">Mozilla</h1>
16 <tal:portlet tal:replace="structure context/@@+portlet-milestones" />13 </tal:heading>
17 </metal:portlets>14
1815 <tal:main metal:fill-slot="main">
19 <div metal:fill-slot="main">16
2017 <div class="top-portlet">
21 <p id="project-inactive" tal:condition="not: context/active" class="warning message"> 18 <p id="project-inactive" class="warning message"
22 This project is currently inactive <a href="+review">(change this)</a>19 tal:condition="not: context/active">
23 </p>20 This project is currently inactive
2421 <a tal:attributes="href context/menu:overview/administer/url"
25 <tal:block condition="view/required:launchpad.Edit">22 >(change this)</a>
26 <p tal:condition="not: context/products" class="warning message">
27 There are no projects registered for
28 <span tal:replace="context/displayname">project displayname</span>.
29 <br />
30 You need to <a href="+newproduct">register another project that is
31 part of <tal:project replace="context/displayname" /></a> or associate
32 an existing project with it.
33 </p>23 </p>
34 </tal:block>24
3525 <tal:block condition="view/required:launchpad.Edit">
36 <div26 <p tal:condition="not: context/products" class="warning message">
37 style="width: 200px; height: 200px; float: right;"27 There are no projects registered for
38 tal:content="structure context/image:mugshot"28 <span tal:replace="context/displayname">project displayname</span>.
39 />29 <br />
40 <h1 tal:content="context/title">Mozilla</h1>30 You need to <a href="+newproduct">register another project that is
4131 part of <tal:project replace="context/displayname" /></a> or associate
42 <div class="description" tal:content="context/summary">32 an existing project with it.
33 </p>
34 </tal:block>
35
36 <div class="summary" tal:content="context/summary">
43 This is the project group Summary, which should be a single paragraph37 This is the project group Summary, which should be a single paragraph
44 summarising the project group's purpose.38 summarising the project group's purpose.
45 </div>39 </div>
@@ -58,92 +52,97 @@
58 Apache Server, and mention other projects such as APR.52 Apache Server, and mention other projects such as APR.
59 </div>53 </div>
6054
61 <ul tal:condition="context/homepageurl">55 <ul id="external-links" class="horizontal"
62 <li>56 tal:condition="context/homepageurl">
63 <a rel="nofollow"57 <li>
64 tal:attributes="href context/homepageurl;58 <a rel="nofollow" class="sprite external-link"
65 class string:sprite external-link">59 tal:attributes="href context/homepageurl">Home page</a>
66 <strong>Visit the <tal:name replace="context/title" />60 </li>
67 home page &raquo;</strong>61 </ul>
68 </a>62 </div>
69 </li>63
70 </ul>64 <div class="yui-g">
71 <ul class="buttons" tal:condition="context/products">65 <div class="yui-u first">
72 <li>66 <tal:details replace="structure context/@@+details" />
73 <a href="+filebug">67 </div>
74 <img alt="Report a bug" src="/+icing/but-sml-reportabug.gif" />68 <div class="yui-u" id="products">
75 </a>69 <div class="portlet">
76 </li>
77 <li tal:content="structure context/@@+ask-a-question-button" />
78 <li tal:content="structure context/@@+help-translate-button" />
79 <li tal:define="has_mentoring context/mentoring_offers/count">
80 <a href="+mentoring" tal:condition="has_mentoring">
81 <img alt="Mentoring available"
82 src="/+icing/but-sml-mentoring.gif"/>
83 </a>
84 <a href="+mentoring" tal:condition="not: has_mentoring">
85 <img alt="No mentoring available"
86 src="/+icing/but-sml-mentoring-off.gif" />
87 </a>
88 </li>
89 </ul>
90 <div class="left">
91 <div
92 class="section"
93 tal:content="structure context/@@+portlet-coming-sprints"
94 />
95 <div
96 class="section"
97 tal:content="structure context/@@+portlet-latestannouncements"
98 />
99 <div class="portlet" id="products">
100 <h2>Projects</h2>70 <h2>Projects</h2>
101 <p tal:condition="not: context/products"> 71 <p tal:condition="not: context/products">
102 There are no projects registered for72 There are no projects registered for
103 <span tal:replace="context/displayname">project displayname</span>.73 <span tal:replace="context/displayname">project displayname</span>.
104 </p>74 </p>
105 <div75 <dl tal:condition="context/products"
106 tal:condition="context/products"76 tal:repeat="product context/products">
107 tal:repeat="product context/products">77 <dt><a tal:replace="structure product/fmt:link">product</a></dt>
108 <tal:link replace="structure product/fmt:link" />78 <dd tal:define="dev_focus product/development_focus">
109 <div tal:condition="product/releases/count">79 <a tal:attributes="href dev_focus/fmt:url"
110 Releases:80 tal:content="dev_focus/name">trunk</a>
111 <tal:release repeat="release product/releases">81 series:
112 <a82 <tal:releases repeat="release dev_focus/releases">
113 tal:attributes="href release/fmt:url"83 <strong>
114 tal:content="release/version"84 <a tal:attributes="href release/fmt:url"
115 >version</a><tal:comma85 tal:content="release/version">release
116 condition="not:repeat/release/end">,</tal:comma>86 </a></strong>,
117 </tal:release>87 </tal:releases>
118 </div>88 <tal:releases repeat="milestone dev_focus/milestones">
119 <div tal:condition="product/milestones">89 <strong>
120 Milestones:90 <a tal:attributes="href milestone/fmt:url"
121 <tal:milestone repeat="milestone product/milestones">91 tal:content="milestone/name">milestone
122 <a92 </a></strong><tal:comma
123 tal:attributes="href milestone/fmt:url"93 condition="not:repeat/milestone/end">,</tal:comma>
124 tal:content="milestone/name"94 </tal:releases>
125 >name</a><tal:comma95 </dd>
126 condition="not:repeat/milestone/end">,</tal:comma>96 </dl>
127 </tal:milestone>97 <ul>
128 </div>98 <li>
129 </div>99 <a tal:replace="structure context/menu:overview/milestones/fmt:link" />
130 <tal:block condition="context/required:launchpad.Edit">100 </li>
131 <a href="+newproduct">&raquo; Register another project that is101 <li tal:condition="context/menu:overview/new_product/enabled">
132 part of <tal:project replace="context/displayname" /></a>102 <a tal:replace="structure context/menu:overview/new_product/fmt:link" />
133 </tal:block>103 </li>
104 </ul>
134 </div>105 </div>
135 <div tal:replace="structure context/@@+portlet-latestspecs" />106 </div>
136 </div>107 </div>
137 <div class="right">108 <div class="yui-g">
138 <div tal:replace="structure context/@@+details" />109 <div class="yui-u first">
139 <div tal:replace="structure context/@@+portlet-top-contributors" />110 <tal:bugs content="structure context/@@+portlet-latestbugs" />
140 <div tal:replace="structure context/@@+portlet-latestbugs" />111 <tal:specs content="structure context/@@+portlet-latestspecs" />
141 <div112 <tal:sprints content="structure context/@@+portlet-coming-sprints" />
142 class="section"113 </div>
143 tal:content="structure context/@@+portlet-latestquestions"114 <div class="yui-u">
144 />115 <tal:questions content="structure context/@@+portlet-latestquestions" />
145 </div>116 <tal:contributors content="structure context/@@+portlet-top-contributors" />
146 </div>117 </div>
118 </div>
119 </tal:main>
120
121 <tal:side metal:fill-slot="side">
122 <div id="object-actions" class="top-portlet">
123 <tal:menu replace="structure view/@@+global-actions" />
124 </div>
125 <div id="involvement" class="portlet involvement"
126 tal:condition="context/products">
127 <h2>Get Involved</h2>
128 <ul>
129 <li>
130 <a class="bugs"
131 tal:attributes="href context/menu:bugs/new/url">Report a Bug</a>
132 </li>
133 <li>
134 <a class="question"
135 tal:attributes="href context/menu:answers/new/url">Ask a question</a>
136 </li>
137 <li>
138 <a class="translate"
139 tal:attributes="href context/menu:translations/overview/url">Help translate</a>
140 </li>
141 </ul>
142 </div>
143 <tal:portlet tal:replace="structure context/@@+portlet-latestannouncements" />
144 </tal:side>
145
147 </body>146 </body>
148</html>147</html>
149148
150149
=== modified file 'lib/lp/soyuz/browser/tests/archive-views.txt'
--- lib/lp/soyuz/browser/tests/archive-views.txt 2009-07-30 01:24:18 +0000
+++ lib/lp/soyuz/browser/tests/archive-views.txt 2009-08-13 19:36:01 +0000
@@ -932,7 +932,9 @@
932 >>> print archive_widget.getInputValue()932 >>> print archive_widget.getInputValue()
933 Traceback (most recent call last):933 Traceback (most recent call last):
934 ...934 ...
935 WidgetInputError: ('destination_archive', u'Destination PPA', )935 WidgetInputError: ('destination_archive',
936 u'Destination PPA',
937 RequiredMissing())
936938
937939
938=== Copy private files to public archives ===940=== Copy private files to public archives ===
939941
=== modified file 'lib/lp/soyuz/browser/tests/binarypackagerelease-views.txt'
--- lib/lp/soyuz/browser/tests/binarypackagerelease-views.txt 2009-08-13 19:20:04 +0000
+++ lib/lp/soyuz/browser/tests/binarypackagerelease-views.txt 2009-08-13 19:51:29 +0000
@@ -1,6 +1,6 @@
1= BinaryPackageRelease Pages =1= BinaryPackageRelease Pages =
22
3 >>> from zope.component import queryMultiAdapter3 >>> from zope.component import getMultiAdapter
4 >>> from zope.publisher.browser import TestRequest4 >>> from zope.publisher.browser import TestRequest
5 >>> from canonical.launchpad.database import BinaryPackageRelease5 >>> from canonical.launchpad.database import BinaryPackageRelease
66
@@ -16,7 +16,7 @@
1616
17Let's instantiate the view for +portlet-details:17Let's instantiate the view for +portlet-details:
1818
19 >>> pmount_view = queryMultiAdapter(19 >>> pmount_view = getMultiAdapter(
20 ... (pmount_bin, request), name="+portlet-details")20 ... (pmount_bin, request), name="+portlet-details")
2121
22Main functionality of this class is to provide abstracted model of the22Main functionality of this class is to provide abstracted model of the
@@ -52,4 +52,3 @@
52 ('tramp-package', None, '', None)52 ('tramp-package', None, '', None)
5353
54Other relationship groups use the same mechanism.54Other relationship groups use the same mechanism.
55
5655
=== modified file 'lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt'
--- lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt 2009-06-19 16:15:02 +0000
+++ lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt 2009-08-06 20:16:29 +0000
@@ -142,7 +142,7 @@
142142
143Now Celso's PPA will list Mark's and No-Priv's PPA as its dependencies.143Now Celso's PPA will list Mark's and No-Priv's PPA as its dependencies.
144144
145 >>> admin_browser.reload()145 >>> admin_browser.open(admin_browser.url) # Reload will set old form values.
146 >>> print_ppa_dependencies(admin_browser.contents)146 >>> print_ppa_dependencies(admin_browser.contents)
147 PPA for Mark Shuttleworth147 PPA for Mark Shuttleworth
148 PPA for No Privileges Person148 PPA for No Privileges Person
149149
=== modified file 'lib/lp/translations/browser/project.py'
--- lib/lp/translations/browser/project.py 2009-07-17 02:25:09 +0000
+++ lib/lp/translations/browser/project.py 2009-08-05 19:26:37 +0000
@@ -11,7 +11,7 @@
11 ]11 ]
1212
13from canonical.launchpad.webapp import (13from canonical.launchpad.webapp import (
14 ApplicationMenu, enabled_with_permission, Link, LaunchpadView)14 canonical_url, enabled_with_permission, Link, LaunchpadView)
15from canonical.launchpad.webapp.menu import NavigationMenu15from canonical.launchpad.webapp.menu import NavigationMenu
16from lp.registry.interfaces.project import IProject16from lp.registry.interfaces.project import IProject
1717
@@ -20,7 +20,7 @@
2020
21 usedfor = IProject21 usedfor = IProject
22 facet = 'translations'22 facet = 'translations'
23 links = ['products', 'changetranslators']23 links = ['products', 'changetranslators', 'overview']
2424
25 @enabled_with_permission('launchpad.Edit')25 @enabled_with_permission('launchpad.Edit')
26 def changetranslators(self):26 def changetranslators(self):
@@ -31,6 +31,11 @@
31 text = 'Products'31 text = 'Products'
32 return Link('', text)32 return Link('', text)
3333
34 def overview(self):
35 text = 'Overview'
36 link = canonical_url(self.context, rootsite='translations')
37 return Link(link, text, icon='translation')
38
3439
35class ProjectView(LaunchpadView):40class ProjectView(LaunchpadView):
36 pass41 pass
3742
=== modified file 'lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt'
--- lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt 2009-07-01 20:45:39 +0000
+++ lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt 2009-07-16 13:28:25 +0000
@@ -101,7 +101,7 @@
101 ... 'http://translations.launchpad.dev/ubuntu/hoary/+lang/es')101 ... 'http://translations.launchpad.dev/ubuntu/hoary/+lang/es')
102 Traceback (most recent call last):102 Traceback (most recent call last):
103 ...103 ...
104 HTTPError: HTTP Error 503: Service Unavailable104 httperror_seek_wrapper: HTTP Error 503: Service Unavailable
105 >>> main_content = find_main_content(user_browser.contents)105 >>> main_content = find_main_content(user_browser.contents)
106 >>> print main_content.findNext('p').renderContents()106 >>> print main_content.findNext('p').renderContents()
107 Translations for this release series are not available yet.107 Translations for this release series are not available yet.
108108
=== modified file 'lib/lp/translations/stories/standalone/xx-pofile-translate-html-tags-escape.txt'
--- lib/lp/translations/stories/standalone/xx-pofile-translate-html-tags-escape.txt 2009-07-01 20:45:39 +0000
+++ lib/lp/translations/stories/standalone/xx-pofile-translate-html-tags-escape.txt 2009-08-06 11:42:11 +0000
@@ -31,6 +31,5 @@
31 >>> text = find_tag_by_id(31 >>> text = find_tag_by_id(
32 ... user_browser.contents, 'msgset_67_hr_translation_0')32 ... user_browser.contents, 'msgset_67_hr_translation_0')
33 >>> print extract_text(text.renderContents())33 >>> print extract_text(text.renderContents())
34 Upotreba:34 Upotreba:
35 35 %s [opcije] &lt;foo&gt; [&lt;etiketa&gt;]%s%s%s
36 %s [opcije] &lt;foo&gt; [&lt;etiketa&gt;]%s%s%s
3736
=== modified file 'lib/lp/translations/stories/translationgroups/30-show-group-translation-targets.txt'
--- lib/lp/translations/stories/translationgroups/30-show-group-translation-targets.txt 2009-07-01 13:16:44 +0000
+++ lib/lp/translations/stories/translationgroups/30-show-group-translation-targets.txt 2009-08-13 21:32:59 +0000
@@ -16,7 +16,7 @@
16 >>> for link in portlet.findAll('a'):16 >>> for link in portlet.findAll('a'):
17 ... print '%s: %s' % (link.find(text=True), link['href'])17 ... print '%s: %s' % (link.find(text=True), link['href'])
18 Ubuntu: /ubuntu18 Ubuntu: /ubuntu
19 NetApplet: http://translations.launchpad.dev/netapplet19 NetApplet: /netapplet
20 GNOME: /gnome20 GNOME: /gnome
2121
22If we disable some of these projects...22If we disable some of these projects...
2323
=== modified file 'lib/lp/translations/templates/translationgroup-portlet-relateds.pt'
--- lib/lp/translations/templates/translationgroup-portlet-relateds.pt 2009-07-17 17:59:07 +0000
+++ lib/lp/translations/templates/translationgroup-portlet-relateds.pt 2009-08-13 21:32:59 +0000
@@ -25,7 +25,8 @@
25 <h3>Projects:</h3>25 <h3>Projects:</h3>
26 <ul>26 <ul>
27 <li tal:repeat="product context/products">27 <li tal:repeat="product context/products">
28 <tal:link replace="structure product/fmt:link" />28 <tal:link replace="structure product/fmt:link:translations" />
29 <tal:link replace="structure product/fmt:url:translations" />
29 </li>30 </li>
30 </ul>31 </ul>
31 </tal:products>32 </tal:products>
3233
=== removed symlink 'lib/mechanize'
=== target was u'../sourcecode/zope/src/mechanize'
=== removed symlink 'lib/persistent'
=== target was u'../sourcecode/zope/src/persistent/'
=== removed symlink 'lib/transaction'
=== target was u'../sourcecode/zope/src/transaction/'
=== removed symlink 'lib/twisted'
=== target was u'../sourcecode/twisted/twisted/'
=== added symlink 'lib/twisted'
=== target is u'../sourcecode/twisted/twisted/'
=== removed directory 'lib/zc'
=== removed file 'lib/zc/__init__.py'
--- lib/zc/__init__.py 2009-01-03 19:32:48 +0000
+++ lib/zc/__init__.py 1970-01-01 00:00:00 +0000
@@ -1,2 +0,0 @@
1# This is a namespace package for zc.* packages. We should absolutely get rid
2# of this once we are using eggs.
30
=== removed symlink 'lib/zc/zservertracelog'
=== target was u'../../sourcecode/zc.zservertracelog/src/zc/zservertracelog'
=== removed symlink 'lib/zdaemon'
=== target was u'../sourcecode/zope/src/zdaemon/'
=== removed symlink 'lib/zodbcode'
=== target was u'../sourcecode/zope/src/zodbcode/'
=== removed symlink 'lib/zope'
=== target was u'../sourcecode/zope/src/zope/'
=== modified file 'setup.py'
--- setup.py 2009-07-24 11:14:47 +0000
+++ setup.py 2009-08-05 18:52:52 +0000
@@ -20,26 +20,78 @@
20 maintainer='Launchpad Developers',20 maintainer='Launchpad Developers',
21 description=('A unique collaboration and Bazaar code hosting platform '21 description=('A unique collaboration and Bazaar code hosting platform '
22 'for software projects.'),22 'for software projects.'),
23 license='LGPL v3',23 license='Affero GPL v3',
24 # this list should only contain direct dependencies--things imported or
25 # used in zcml.
24 install_requires=[26 install_requires=[
25 'bzr',27 'bzr',
28 'chameleon.core',
29 'chameleon.zpt',
26 'feedvalidator',30 'feedvalidator',
27 'funkload',31 'funkload',
28 'launchpadlib',32 'launchpadlib',
29 'lazr.smtptest',33 'lazr.smtptest',
30 'lazr.uri',34 'lazr.uri',
35 'mechanize',
31 'mocker',36 'mocker',
32 'oauth',37 'oauth',
33 'python-openid',38 'python-openid',
34 'pytz',39 'pytz',
40 # This appears to be a broken indirect dependency from zope.security:
41 'RestrictedPython',
35 'setuptools',42 'setuptools',
36 'sourcecodegen',43 'sourcecodegen',
37 'storm',44 'storm',
38 'chameleon.core',45 'transaction',
39 'chameleon.zpt',46 'wadllib',
40 'z3c.pt',47 'z3c.pt',
41 'z3c.ptcompat',48 'z3c.ptcompat',
42 'wadllib',49 'zc.zservertracelog',
50 'zope.app.appsetup',
51 'zope.app.component',
52 'zope.app.dav', # ./package-includes/dav-configure.zcml
53 'zope.app.error',
54 'zope.app.exception',
55 'zope.app.file',
56 'zope.app.form',
57 'zope.app.pagetemplate',
58 'zope.app.pluggableauth',
59 'zope.app.publication',
60 'zope.app.publisher',
61 'zope.app.security',
62 'zope.app.securitypolicy',
63 'zope.app.server',
64 'zope.app.session',
65 'zope.app.testing',
66 'zope.app.wsgi',
67 'zope.app.zapi',
68 'zope.contenttype',
69 'zope.component[zcml]',
70 'zope.datetime',
71 'zope.thread',
72 'zope.error',
73 'zope.event',
74 'zope.exceptions',
75 'zope.formlib',
76 'zope.i18n',
77 'zope.interface',
78 'zope.hookable', # indirect, via zope.app.component
79 'zope.lifecycleevent',
80 'zope.location',
81 'zope.pagetemplate',
82 'zope.publisher',
83 'zope.proxy',
84 'zope.schema',
85 'zope.security',
86 'zope.sendmail',
87 'zope.server',
88 'zope.session',
89 'zope.tal',
90 'zope.tales',
91 'zope.testbrowser',
92 'zope.testing',
93 'zope.traversing',
94 'zope.viewlet', # only fixing a broken dependency
43 # Loggerhead dependencies. These should be removed once95 # Loggerhead dependencies. These should be removed once
44 # bug 383360 is fixed and we include it as a source dist.96 # bug 383360 is fixed and we include it as a source dist.
45 'Paste',97 'Paste',
4698
=== modified file 'utilities/sourcedeps.conf'
--- utilities/sourcedeps.conf 2009-07-21 07:55:23 +0000
+++ utilities/sourcedeps.conf 2009-08-14 17:26:21 +0000
@@ -18,10 +18,8 @@
18testresources=lp:~launchpad-pqm/testresources/dev/18testresources=lp:~launchpad-pqm/testresources/dev/
19testtools=lp:~launchpad-pqm/testtools/trunk/19testtools=lp:~launchpad-pqm/testtools/trunk/
20twisted=lp:~launchpad-pqm/twisted/trunk/20twisted=lp:~launchpad-pqm/twisted/trunk/
21zc.zservertracelog=lp:~launchpad-pqm/zc.zservertracelog/trunk/
22cscvs=lp:~launchpad-pqm/launchpad-cscvs/devel/21cscvs=lp:~launchpad-pqm/launchpad-cscvs/devel/
23lpreview=lp:~launchpad-pqm/bzr-lpreview/devel/22lpreview=lp:~launchpad-pqm/bzr-lpreview/devel/
24zope=lp:~launchpad-pqm/zope3/3.4/
25pygettextpo=lp:~launchpad/pygettextpo/trunk/23pygettextpo=lp:~launchpad/pygettextpo/trunk/
26old_xmlplus=lp:~launchpad/dtdparser/trunk/24old_xmlplus=lp:~launchpad/dtdparser/trunk/
27lsprof=lp:~launchpad/lsprof/trunk/25lsprof=lp:~launchpad/lsprof/trunk/
2826
=== modified file 'versions.cfg'
--- versions.cfg 2009-08-12 12:35:51 +0000
+++ versions.cfg 2009-08-14 18:08:23 +0000
@@ -2,17 +2,20 @@
2versions = versions2versions = versions
33
4[versions]4[versions]
5# Alphabetical, please! :-)5# Alphabetical, case-insensitive, please! :-)
6bzr = 1.176bzr = 1.17
7chameleon.core = 1.0b357chameleon.core = 1.0b35
8chameleon.zpt = 1.0b178chameleon.zpt = 1.0b17
9ClientForm = 0.2.10
9# Required by Windmill to run on 2.410# Required by Windmill to run on 2.4
10ctypes = 1.0.211ctypes = 1.0.2
11docutils = 0.512docutils = 0.5
12elementtree = 1.2.6-2005031613elementtree = 1.2.6-20050316
13# We use a version of feedvalidator that has been changed to not14feedvalidator = 0.0.0DEV-r1049
14# change the default socket timeout on import.15# We could use a locally hacked version of feedvalidator that has been changed
15feedvalidator = 0.0.0DEV-r1049-hacked16# to not change the default socket timeout on import. We are currently handling
17# that problem elsewhere.
18# feedvalidator = 0.0.0DEV-r1049-hacked
16functest = 0.8.719functest = 0.8.7
17funkload = 1.10.020funkload = 1.10.0
18httplib2 = 0.4.021httplib2 = 0.4.0
@@ -20,6 +23,7 @@
20launchpadlib = 1.0.223launchpadlib = 1.0.2
21lazr.smtptest = 1.124lazr.smtptest = 1.1
22lazr.uri = 1.0.125lazr.uri = 1.0.1
26mechanize = 0.1.7b
23mocker = 0.10.127mocker = 0.10.1
24mozrunner = 1.3.428mozrunner = 1.3.4
25oauth = 1.029oauth = 1.0
@@ -27,12 +31,14 @@
27PasteDeploy = 1.3.331PasteDeploy = 1.3.3
28python-openid = 2.2.132python-openid = 2.2.1
29pytz = 2009j33pytz = 2009j
34RestrictedPython = 3.4.2
30setuptools = 0.6c935setuptools = 0.6c9
31simplejson = 2.0.936simplejson = 2.0.9
32simplesettings = 0.437simplesettings = 0.4
33SimpleTal = 4.138SimpleTal = 4.1
34sourcecodegen = 0.6.939sourcecodegen = 0.6.9
35storm = 0.1540storm = 0.15
41transaction = 1.0a1
36uuid = 1.3042uuid = 1.30
37wadllib = 0.143wadllib = 0.1
38webunit = 1.3.844webunit = 1.3.8
@@ -48,27 +54,90 @@
48zc.buildout = 1.4.0dev-gary-r10268454zc.buildout = 1.4.0dev-gary-r102684
49zc.lockfile = 1.0.055zc.lockfile = 1.0.0
50zc.recipe.egg = 1.3.0dev-gary-r10268456zc.recipe.egg = 1.3.0dev-gary-r102684
51zc.recipe.testrunner = 1.1.057zc.zservertracelog = 1.1.5
52ZConfig = 2.6.158ZConfig = 2.6.1
59zdaemon = 2.0.2
60ZODB3 = 3.8.1
61zodbcode = 3.4.0
62zope.annotation = 3.4.1
63zope.app.applicationcontrol = 3.4.3
64zope.app.appsetup = 3.4.1
65zope.app.authentication = 3.4.4
66zope.app.basicskin = 3.4.0
67zope.app.broken = 3.4.0
68zope.app.component = 3.4.1
69zope.app.container = 3.5.6
70zope.app.content = 3.4.0
71zope.app.dav = 3.4.1
72zope.app.debug = 3.4.1
73zope.app.dependable = 3.4.0
74zope.app.error = 3.5.1
75zope.app.exception = 3.4.1
76zope.app.file = 3.4.4
77zope.app.folder = 3.4.0
78zope.app.form = 3.4.1
79zope.app.generations = 3.4.1
80zope.app.http = 3.4.1
81zope.app.i18n = 3.4.4
82zope.app.interface = 3.4.0
53zope.app.locales = 3.5.183zope.app.locales = 3.5.1
84zope.app.pagetemplate = 3.4.1
85zope.app.pluggableauth = 3.4.0
86zope.app.principalannotation = 3.4.0
87zope.app.publication = 3.4.4
88zope.app.publisher = 3.4.1
89zope.app.renderer = 3.4.0
90zope.app.rotterdam = 3.4.1
91zope.app.schema = 3.4.0
92zope.app.security = 3.5.2
93zope.app.securitypolicy = 3.4.6
94zope.app.server = 3.4.2
95zope.app.session = 3.5.1
96zope.app.testing = 3.4.3
97zope.app.wsgi = 3.4.1
98zope.app.zapi = 3.4.0
99zope.app.zcmlfiles = 3.4.3
100zope.app.zopeappgenerations = 3.4.0
101zope.cachedescriptors = 3.4.1
54zope.component = 3.5.1102zope.component = 3.5.1
55zope.configuration = 3.6.0103zope.configuration = 3.5.0
56zope.contentprovider = 3.5.0104zope.contentprovider = 3.5.0
105zope.contenttype = 3.4.0
106zope.copypastemove = 3.4.0
107zope.datetime = 3.4.0
57zope.deferredimport = 3.4.0108zope.deferredimport = 3.4.0
58zope.deprecation = 3.4.0109zope.deprecation = 3.4.0
59zope.dottedname = 3.4.5110zope.dottedname = 3.4.5
111zope.dublincore = 3.4.0
112zope.error = 3.5.1
60zope.event = 3.4.1113zope.event = 3.4.1
61zope.exceptions = 3.5.2114zope.exceptions = 3.5.2
115zope.filerepresentation = 3.4.0
116zope.formlib = 3.4.0
62zope.hookable = 3.4.0117zope.hookable = 3.4.0
63zope.i18n = 3.6.0118zope.i18n = 3.6.0
64zope.i18nmessageid = 3.4.3119zope.i18nmessageid = 3.4.3
65zope.interface = 3.5.1120zope.interface = 3.5.1
121zope.lifecycleevent = 3.4.0
66zope.location = 3.5.2122zope.location = 3.5.2
123zope.minmax = 1.1.0
124zope.modulealias = 3.4.0
125zope.pagetemplate = 3.4.0
67zope.proxy = 3.5.0126zope.proxy = 3.5.0
68zope.publisher = 3.5.6127zope.publisher = 3.5.6
69zope.schema = 3.5.4128zope.schema = 3.5.4
70zope.security = 3.6.0129zope.security = 3.7.1
71zope.tal = 3.4.0130zope.securitypolicy = 3.4.1
131zope.sendmail = 3.4.0
132zope.server = 3.4.3
133zope.session = 3.4.1
134zope.size = 3.4.0
135zope.structuredtext = 3.4.0
136zope.tal = 3.4.1
72zope.tales = 3.4.0137zope.tales = 3.4.0
73zope.testing = 3.7.4138zope.testbrowser = 3.4.2
74zope.traversing = 3.5.2139zope.testing = 3.8.1
140zope.thread = 3.4
141# zope.traversing = 3.5.2
142zope.traversing = 3.4.1
143zope.viewlet = 3.4.2

Subscribers

People subscribed via source and target branches

to status/vote changes: