Merge lp:~wgrant/launchpad/gina-3.0-support into lp:launchpad/db-devel

Proposed by William Grant
Status: Merged
Approved by: Julian Edwards
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~wgrant/launchpad/gina-3.0-support
Merge into: lp:launchpad/db-devel
Prerequisite: lp:~wgrant/launchpad/distroseries-source-format-selection
Diff against target: 1838 lines (+1124/-159)
25 files modified
lib/canonical/launchpad/fields/__init__.py (+61/-1)
lib/canonical/launchpad/helpers.py (+1/-64)
lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js (+257/-0)
lib/lp/archiveuploader/tests/test_utils.py (+18/-0)
lib/lp/archiveuploader/utils.py (+14/-0)
lib/lp/bugs/browser/bugtarget.py (+28/-0)
lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt (+22/-0)
lib/lp/bugs/stories/bugattachments/20-edit-bug-attachment.txt (+38/-0)
lib/lp/bugs/templates/bug-portlet-attachments.pt (+49/-0)
lib/lp/bugs/templates/bugtarget-filebug-search.pt (+45/-0)
lib/lp/bugs/templates/bugtasks-and-nominations-table.pt (+47/-0)
lib/lp/code/browser/branch.py (+8/-2)
lib/lp/code/templates/branchmergeproposal-index.pt (+25/-0)
lib/lp/registry/browser/distroseries.py (+56/-24)
lib/lp/registry/browser/tests/packaging-views.txt (+58/-0)
lib/lp/registry/interfaces/person.py (+6/-0)
lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt (+98/-48)
lib/lp/registry/tests/test_productseries_vocabularies.py (+75/-0)
lib/lp/soyuz/doc/gina.txt (+11/-11)
lib/lp/soyuz/scripts/gina/handlers.py (+44/-7)
lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources (+17/-0)
lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.dsc (+22/-0)
lib/lp/soyuz/scripts/tests/test_gina.py (+15/-0)
lib/lp/translations/stories/distroseries/xx-distroseries-templates.txt (+65/-2)
lib/lp/translations/templates/object-templates.pt (+44/-0)
To merge this branch: bzr merge lp:~wgrant/launchpad/gina-3.0-support
Reviewer Review Type Date Requested Status
Данило Шеган (community) release-critical Approve
Julian Edwards (community) Approve
Review via email: mp+15478@code.launchpad.net
To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

gina needs to support 3.0 (quilt) and 3.0 (native) sources. To do this, it just needs to identify files as the correct types. Since archiveuploader already has a function to do this, I've altered gina to use that rather than the duplicated canonical.launchpad.helpers.getFileType. As gina was the final callsite of getFileType, I've removed that.

Since determine_source_file_type only does source files, I've added determine_binary_file_type. It could possibly go along with determine_source_file_type in lp.archiveuploader.utils, but I'm not quite sure.

For testing, I've added a 3.0 (quilt) source to the test archive.

Revision history for this message
Julian Edwards (julian-edwards) wrote :

 review approve
 merge approve

> gina needs to support 3.0 (quilt) and 3.0 (native) sources. To do this, it
> just needs to identify files as the correct types. Since archiveuploader
> already has a function to do this, I've altered gina to use that rather
> than the duplicated canonical.launchpad.helpers.getFileType. As gina was
> the final callsite of getFileType, I've removed that.
>
> Since determine_source_file_type only does source files, I've added
> determine_binary_file_type. It could possibly go along with
> determine_source_file_type in lp.archiveuploader.utils, but I'm not quite
> sure.

I think it should go there to be consistent.

>
> For testing, I've added a 3.0 (quilt) source to the test archive.
>

Great, thanks for making this change William! It looks good to me so I
approved it. There's just a couple of minor comments inline.

[snip]

>=== modified file 'lib/lp/soyuz/scripts/gina/handlers.py'
>--- lib/lp/soyuz/scripts/gina/handlers.py 2009-11-07 09:50:37 +0000
>+++ lib/lp/soyuz/scripts/gina/handlers.py 2009-12-01 10:10:48 +0000
>@@ -29,6 +29,7 @@
>
> from lp.archivepublisher.diskpool import poolify
> from lp.archiveuploader.tagfiles import parse_tagfile
>+from lp.archiveuploader.utils import determine_source_file_type
>
> from canonical.database.sqlbase import sqlvalues
>
>@@ -46,10 +47,22 @@
>
> from lp.registry.interfaces.person import IPersonSet,
> PersonCreationRationale from lp.registry.interfaces.sourcepackage import
> SourcePackageType +from lp.soyuz.interfaces.binarypackagerelease import
> BinaryPackageFileType from lp.soyuz.interfaces.binarypackagename import
> IBinaryPackageNameSet from lp.soyuz.interfaces.build import BuildStatus
> from lp.soyuz.interfaces.publishing import PackagePublishingStatus
>-from canonical.launchpad.helpers import getFileType, getBinaryPackageFormat
>+from canonical.launchpad.helpers import getBinaryPackageFormat

I think Gina is the only thing using getBinaryPackageFormat - it might be
worth de-cluttering helpers.py and moving it to the gina module.

>+
>+
>+def determine_binary_file_type(filename):
>+ """Determine the BinaryPackageFileType of the given filename."""
>+
>+ if filename.endswith(".deb"):
>+ return BinaryPackageFileType.DEB
>+ elif filename.endswith(".udeb"):
>+ return BinaryPackageFileType.DEB

This should be UDEB I think?

Everything else looks good! Remind me to land this and the other branch when
PQM opens.

Cheers
J

review: Approve
Revision history for this message
William Grant (wgrant) wrote :

Hi Julian,

Thanks for the review.

On Tue, 2009-12-01 at 16:33 +0000, Julian Edwards wrote:
> Review: Approve
> review approve
> merge approve
>
> > gina needs to support 3.0 (quilt) and 3.0 (native) sources. To do this, it
> > just needs to identify files as the correct types. Since archiveuploader
> > already has a function to do this, I've altered gina to use that rather
> > than the duplicated canonical.launchpad.helpers.getFileType. As gina was
> > the final callsite of getFileType, I've removed that.
> >
> > Since determine_source_file_type only does source files, I've added
> > determine_binary_file_type. It could possibly go along with
> > determine_source_file_type in lp.archiveuploader.utils, but I'm not quite
> > sure.
>
> I think it should go there to be consistent.

Moved.

> >
> > For testing, I've added a 3.0 (quilt) source to the test archive.
> >
>
> Great, thanks for making this change William! It looks good to me so I
> approved it. There's just a couple of minor comments inline.
>
> [snip]
>
> >=== modified file 'lib/lp/soyuz/scripts/gina/handlers.py'
> >--- lib/lp/soyuz/scripts/gina/handlers.py 2009-11-07 09:50:37 +0000
> >+++ lib/lp/soyuz/scripts/gina/handlers.py 2009-12-01 10:10:48 +0000
> >@@ -29,6 +29,7 @@
> >
> > from lp.archivepublisher.diskpool import poolify
> > from lp.archiveuploader.tagfiles import parse_tagfile
> >+from lp.archiveuploader.utils import determine_source_file_type
> >
> > from canonical.database.sqlbase import sqlvalues
> >
> >@@ -46,10 +47,22 @@
> >
> > from lp.registry.interfaces.person import IPersonSet,
> > PersonCreationRationale from lp.registry.interfaces.sourcepackage import
> > SourcePackageType +from lp.soyuz.interfaces.binarypackagerelease import
> > BinaryPackageFileType from lp.soyuz.interfaces.binarypackagename import
> > IBinaryPackageNameSet from lp.soyuz.interfaces.build import BuildStatus
> > from lp.soyuz.interfaces.publishing import PackagePublishingStatus
> >-from canonical.launchpad.helpers import getFileType, getBinaryPackageFormat
> >+from canonical.launchpad.helpers import getBinaryPackageFormat
>
> I think Gina is the only thing using getBinaryPackageFormat - it might be
> worth de-cluttering helpers.py and moving it to the gina module.

Done. I also removed getBinaryPackageExtension, as there were no
callsites.

> >+
> >+
> >+def determine_binary_file_type(filename):
> >+ """Determine the BinaryPackageFileType of the given filename."""
> >+
> >+ if filename.endswith(".deb"):
> >+ return BinaryPackageFileType.DEB
> >+ elif filename.endswith(".udeb"):
> >+ return BinaryPackageFileType.DEB
>
> This should be UDEB I think?

Oops, yes. I've added a test for determine_binary_file_type.

> Everything else looks good! Remind me to land this and the other branch when
> PQM opens.

Both are still blocked on an updated buildbot AMI, but that should
appear on Monday.

The diff on LP is now insanely huge (it looks like it might not have
taken into account the prerequisite branch), but the correct
intermediate diff is attached.

=== modified file 'lib/canonical/launchpad/helpers.py'
--- lib/canonical/launchpad/helpers.py 2009-11-25 10:26:03 +0000
+++ lib/canonical/launchpad/helpers.py 2009-12-10 12:14:13 +0000
@@ -26,8 +26,7 @@
2626
27import canonical27import canonical
28from canonical.launchpad.interfaces import (28from canonical.launchpad.interfaces import (
29 BinaryPackageFormat, ILaunchBag, IRequestPreferredLanguages,29 ILaunchBag, IRequestPreferredLanguages, IRequestLocalLanguages)
30 IRequestLocalLanguages)
3130
3231
33# pylint: disable-msg=W010232# pylint: disable-msg=W0102
@@ -466,52 +465,6 @@
466 long(sha.new(message_id).hexdigest(), 16), 62))465 long(sha.new(message_id).hexdigest(), 16), 62))
467466
468467
469BINARYPACKAGE_EXTENSIONS = {
470 BinaryPackageFormat.DEB: '.deb',
471 BinaryPackageFormat.UDEB: '.udeb',
472 BinaryPackageFormat.RPM: '.rpm'}
473
474
475class UnrecognizedBinaryFormat(Exception):
476
477 def __init__(self, fname, *args):
478 Exception.__init__(self, *args)
479 self.fname = fname
480
481 def __str__(self):
482 return '%s is not recognized as a binary file.' % self.fname
483
484
485def getBinaryPackageFormat(fname):
486 """Return the BinaryPackageFormat for the given filename.
487
488 >>> getBinaryPackageFormat('mozilla-firefox_0.9_i386.deb').name
489 'DEB'
490 >>> getBinaryPackageFormat('debian-installer.9_all.udeb').name
491 'UDEB'
492 >>> getBinaryPackageFormat('network-manager.9_i386.rpm').name
493 'RPM'
494 """
495 for key, value in BINARYPACKAGE_EXTENSIONS.items():
496 if fname.endswith(value):
497 return key
498
499 raise UnrecognizedBinaryFormat(fname)
500
501
502def getBinaryPackageExtension(format):
503 """Return the file extension for the given BinaryPackageFormat.
504
505 >>> getBinaryPackageExtension(BinaryPackageFormat.DEB)
506 '.deb'
507 >>> getBinaryPackageExtension(BinaryPackageFormat.UDEB)
508 '.udeb'
509 >>> getBinaryPackageExtension(BinaryPackageFormat.RPM)
510 '.rpm'
511 """
512 return BINARYPACKAGE_EXTENSIONS[format]
513
514
515def intOrZero(value):468def intOrZero(value):
516 """Return int(value) or 0 if the conversion fails.469 """Return int(value) or 0 if the conversion fails.
517470
518471
=== modified file 'lib/lp/archiveuploader/tests/test_utils.py'
--- lib/lp/archiveuploader/tests/test_utils.py 2009-11-18 02:58:23 +0000
+++ lib/lp/archiveuploader/tests/test_utils.py 2009-12-10 13:32:27 +0000
@@ -10,6 +10,7 @@
10import shutil10import shutil
1111
12from lp.registry.interfaces.sourcepackage import SourcePackageFileType12from lp.registry.interfaces.sourcepackage import SourcePackageFileType
13from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFileType
13from lp.archiveuploader.tests import datadir14from lp.archiveuploader.tests import datadir
1415
1516
@@ -72,6 +73,23 @@
72 self.assertEquals(None, determine_source_file_type('foo_1.0'))73 self.assertEquals(None, determine_source_file_type('foo_1.0'))
73 self.assertEquals(None, determine_source_file_type('foo_1.0.blah.gz'))74 self.assertEquals(None, determine_source_file_type('foo_1.0.blah.gz'))
7475
76 def test_determine_binary_file_type(self):
77 """lp.archiveuploader.utils.determine_binary_file_type should work."""
78 from lp.archiveuploader.utils import determine_binary_file_type
79
80 # .deb -> DEB
81 self.assertEquals(
82 determine_binary_file_type('foo_1.0-1_all.deb'),
83 BinaryPackageFileType.DEB)
84
85 # .udeb -> UDEB
86 self.assertEquals(
87 determine_binary_file_type('foo_1.0-1_all.udeb'),
88 BinaryPackageFileType.UDEB)
89
90 self.assertEquals(determine_binary_file_type('foo_1.0'), None)
91 self.assertEquals(determine_binary_file_type('foo_1.0.notdeb'), None)
92
75 def testPrefixMultilineString(self):93 def testPrefixMultilineString(self):
76 """lp.archiveuploader.utils.prefix_multi_line_string should work"""94 """lp.archiveuploader.utils.prefix_multi_line_string should work"""
77 from lp.archiveuploader.utils import prefix_multi_line_string95 from lp.archiveuploader.utils import prefix_multi_line_string
7896
=== modified file 'lib/lp/archiveuploader/utils.py'
--- lib/lp/archiveuploader/utils.py 2009-11-14 02:59:34 +0000
+++ lib/lp/archiveuploader/utils.py 2009-12-10 13:34:56 +0000
@@ -17,6 +17,7 @@
17 're_changes_file_name',17 're_changes_file_name',
18 're_extract_src_version',18 're_extract_src_version',
19 'get_source_file_extension',19 'get_source_file_extension',
20 'determine_binary_file_type',
20 'determine_source_file_type',21 'determine_source_file_type',
21 'prefix_multi_line_string',22 'prefix_multi_line_string',
22 'safe_fix_maintainer',23 'safe_fix_maintainer',
@@ -88,6 +89,19 @@
88 return None89 return None
8990
9091
92def determine_binary_file_type(filename):
93 """Determine the BinaryPackageFileType of the given filename."""
94 # Avoid circular imports.
95 from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFileType
96
97 if filename.endswith(".deb"):
98 return BinaryPackageFileType.DEB
99 elif filename.endswith(".udeb"):
100 return BinaryPackageFileType.UDEB
101 else:
102 return None
103
104
91def prefix_multi_line_string(str, prefix, include_blank_lines=0):105def prefix_multi_line_string(str, prefix, include_blank_lines=0):
92 """Utility function to split an input string and prefix,106 """Utility function to split an input string and prefix,
93107
94108
=== modified file 'lib/lp/soyuz/scripts/gina/handlers.py'
--- lib/lp/soyuz/scripts/gina/handlers.py 2009-11-25 10:26:03 +0000
+++ lib/lp/soyuz/scripts/gina/handlers.py 2009-12-10 12:15:49 +0000
@@ -29,7 +29,8 @@
2929
30from lp.archivepublisher.diskpool import poolify30from lp.archivepublisher.diskpool import poolify
31from lp.archiveuploader.tagfiles import parse_tagfile31from lp.archiveuploader.tagfiles import parse_tagfile
32from lp.archiveuploader.utils import determine_source_file_type32from lp.archiveuploader.utils import (determine_binary_file_type,
33 determine_source_file_type)
3334
34from canonical.database.sqlbase import sqlvalues35from canonical.database.sqlbase import sqlvalues
3536
@@ -47,22 +48,10 @@
4748
48from lp.registry.interfaces.person import IPersonSet, PersonCreationRationale49from lp.registry.interfaces.person import IPersonSet, PersonCreationRationale
49from lp.registry.interfaces.sourcepackage import SourcePackageType50from lp.registry.interfaces.sourcepackage import SourcePackageType
50from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFileType
51from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet51from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet
52from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFormat
52from lp.soyuz.interfaces.build import BuildStatus53from lp.soyuz.interfaces.build import BuildStatus
53from lp.soyuz.interfaces.publishing import PackagePublishingStatus54from lp.soyuz.interfaces.publishing import PackagePublishingStatus
54from canonical.launchpad.helpers import getBinaryPackageFormat
55
56
57def determine_binary_file_type(filename):
58 """Determine the BinaryPackageFileType of the given filename."""
59
60 if filename.endswith(".deb"):
61 return BinaryPackageFileType.DEB
62 elif filename.endswith(".udeb"):
63 return BinaryPackageFileType.DEB
64 else:
65 return None
6655
6756
68def check_not_in_librarian(files, archive_root, directory):57def check_not_in_librarian(files, archive_root, directory):
@@ -91,6 +80,39 @@
91 return to_upload80 return to_upload
9281
9382
83BINARYPACKAGE_EXTENSIONS = {
84 BinaryPackageFormat.DEB: '.deb',
85 BinaryPackageFormat.UDEB: '.udeb',
86 BinaryPackageFormat.RPM: '.rpm'}
87
88
89class UnrecognizedBinaryFormat(Exception):
90
91 def __init__(self, fname, *args):
92 Exception.__init__(self, *args)
93 self.fname = fname
94
95 def __str__(self):
96 return '%s is not recognized as a binary file.' % self.fname
97
98
99def getBinaryPackageFormat(fname):
100 """Return the BinaryPackageFormat for the given filename.
101
102 >>> getBinaryPackageFormat('mozilla-firefox_0.9_i386.deb').name
103 'DEB'
104 >>> getBinaryPackageFormat('debian-installer.9_all.udeb').name
105 'UDEB'
106 >>> getBinaryPackageFormat('network-manager.9_i386.rpm').name
107 'RPM'
108 """
109 for key, value in BINARYPACKAGE_EXTENSIONS.items():
110 if fname.endswith(value):
111 return key
112
113 raise UnrecognizedBinaryFormat(fname)
114
115
94class DataSetupError(Exception):116class DataSetupError(Exception):
95 """Raised when required data is found to be missing in the database"""117 """Raised when required data is found to be missing in the database"""
96118
97119
=== added file 'lib/lp/soyuz/scripts/tests/test_gina.py'
--- lib/lp/soyuz/scripts/tests/test_gina.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/scripts/tests/test_gina.py 2009-12-10 12:19:14 +0000
@@ -0,0 +1,15 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4import unittest
5
6from zope.testing.doctest import DocTestSuite
7
8import lp.soyuz.scripts.gina.handlers
9
10
11def test_suite():
12 suite = unittest.TestSuite()
13 suite.addTest(DocTestSuite(lp.soyuz.scripts.gina.handlers))
14 return suite
15
Revision history for this message
Данило Шеган (danilo) wrote :

Provided dogfood testing proves OK, looks good. Though, I'd prefer refactoring to be left for when we are outside RC mode.

review: Approve (release-critical)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/fields/__init__.py'
--- lib/canonical/launchpad/fields/__init__.py 2009-12-09 23:11:31 +0000
+++ lib/canonical/launchpad/fields/__init__.py 2009-12-14 08:43:38 +0000
@@ -760,6 +760,14 @@
760 """Return True if the person is public."""760 """Return True if the person is public."""
761 from canonical.launchpad.interfaces import IPerson, PersonVisibility761 from canonical.launchpad.interfaces import IPerson, PersonVisibility
762 if not IPerson.providedBy(person):762 if not IPerson.providedBy(person):
763<<<<<<< TREE
764=======
765 return False
766 if person.visibility == PersonVisibility.PUBLIC:
767 return True
768 else:
769 # PRIVATE_MEMBERSHIP or PRIVATE.
770>>>>>>> MERGE-SOURCE
763 return False771 return False
764 return person.visibility == PersonVisibility.PUBLIC772 return person.visibility == PersonVisibility.PUBLIC
765773
@@ -768,7 +776,17 @@
768 """True if the person/team has private membership visibility."""776 """True if the person/team has private membership visibility."""
769 from canonical.launchpad.interfaces import IPerson, PersonVisibility777 from canonical.launchpad.interfaces import IPerson, PersonVisibility
770 if not IPerson.providedBy(person):778 if not IPerson.providedBy(person):
771 return False779<<<<<<< TREE
780=======
781 return False
782 if person.visibility == PersonVisibility.PRIVATE_MEMBERSHIP:
783 # PRIVATE_MEMBERSHIP.
784 return True
785 else:
786 # PUBLIC or PRIVATE.
787>>>>>>> MERGE-SOURCE
788 return False
789<<<<<<< TREE
772 return person.visibility == PersonVisibility.PRIVATE_MEMBERSHIP790 return person.visibility == PersonVisibility.PRIVATE_MEMBERSHIP
773791
774792
@@ -791,9 +809,33 @@
791809
792810
793class PublicPersonChoice(PersonChoice):811class PublicPersonChoice(PersonChoice):
812=======
813
814
815class PrivateTeamNotAllowed(ConstraintNotSatisfied):
816 __doc__ = _("A private team is not allowed.")
817
818
819class PrivateMembershipTeamNotAllowed(ConstraintNotSatisfied):
820 __doc__ = _("A private-membership team is not allowed.")
821
822
823class PersonChoice(Choice):
824 """A person or team.
825
826 This is useful as a superclass and provides a clearer error message than
827 "Constraint not satisfied".
828 """
829 implements(IReferenceChoice)
830 schema = IObject # Will be set to IPerson once IPerson is defined.
831
832
833class PublicPersonChoice(PersonChoice):
834>>>>>>> MERGE-SOURCE
794 """A person or team who is public."""835 """A person or team who is public."""
795836
796 def constraint(self, value):837 def constraint(self, value):
838<<<<<<< TREE
797 if is_public_person(value):839 if is_public_person(value):
798 return True840 return True
799 else:841 else:
@@ -802,6 +844,16 @@
802844
803845
804class ParticipatingPersonChoice(PersonChoice):846class ParticipatingPersonChoice(PersonChoice):
847=======
848 if is_valid_public_person(value):
849 return True
850 else:
851 # The vocabulary prevents the revealing of private team names.
852 raise PrivateTeamNotAllowed(value)
853
854
855class ParticipatingPersonChoice(PersonChoice):
856>>>>>>> MERGE-SOURCE
805 """A person or team who is not a private membership team.857 """A person or team who is not a private membership team.
806858
807 A person can participate in all contexts. A PRIVATE team can participate859 A person can participate in all contexts. A PRIVATE team can participate
@@ -811,8 +863,16 @@
811 """863 """
812864
813 def constraint(self, value):865 def constraint(self, value):
866<<<<<<< TREE
814 if not is_private_membership_person(value):867 if not is_private_membership_person(value):
815 return True868 return True
816 else:869 else:
817 # The vocabulary prevents the revealing of private team names.870 # The vocabulary prevents the revealing of private team names.
818 raise PrivateMembershipTeamNotAllowed(value)871 raise PrivateMembershipTeamNotAllowed(value)
872=======
873 if not is_private_membership(value):
874 return True
875 else:
876 # The vocabulary prevents the revealing of private team names.
877 raise PrivateMembershipTeamNotAllowed(value)
878>>>>>>> MERGE-SOURCE
819879
=== modified file 'lib/canonical/launchpad/helpers.py'
--- lib/canonical/launchpad/helpers.py 2009-11-14 02:55:15 +0000
+++ lib/canonical/launchpad/helpers.py 2009-12-14 08:43:38 +0000
@@ -26,9 +26,7 @@
2626
27import canonical27import canonical
28from canonical.launchpad.interfaces import (28from canonical.launchpad.interfaces import (
29 BinaryPackageFormat, BinaryPackageFileType, ILaunchBag,29 ILaunchBag, IRequestPreferredLanguages, IRequestLocalLanguages)
30 IRequestPreferredLanguages, IRequestLocalLanguages,
31 SourcePackageFileType)
3230
3331
34# pylint: disable-msg=W010232# pylint: disable-msg=W0102
@@ -467,67 +465,6 @@
467 long(sha.new(message_id).hexdigest(), 16), 62))465 long(sha.new(message_id).hexdigest(), 16), 62))
468466
469467
470def getFileType(fname):
471 if fname.endswith(".deb"):
472 return BinaryPackageFileType.DEB
473 if fname.endswith(".udeb"):
474 return BinaryPackageFileType.DEB
475 if fname.endswith(".dsc"):
476 return SourcePackageFileType.DSC
477 if fname.endswith(".diff.gz"):
478 return SourcePackageFileType.DIFF
479 if fname.endswith(".orig.tar.gz"):
480 return SourcePackageFileType.ORIG_TARBALL
481 if fname.endswith(".tar.gz"):
482 return SourcePackageFileType.NATIVE_TARBALL
483
484
485BINARYPACKAGE_EXTENSIONS = {
486 BinaryPackageFormat.DEB: '.deb',
487 BinaryPackageFormat.UDEB: '.udeb',
488 BinaryPackageFormat.RPM: '.rpm'}
489
490
491class UnrecognizedBinaryFormat(Exception):
492
493 def __init__(self, fname, *args):
494 Exception.__init__(self, *args)
495 self.fname = fname
496
497 def __str__(self):
498 return '%s is not recognized as a binary file.' % self.fname
499
500
501def getBinaryPackageFormat(fname):
502 """Return the BinaryPackageFormat for the given filename.
503
504 >>> getBinaryPackageFormat('mozilla-firefox_0.9_i386.deb').name
505 'DEB'
506 >>> getBinaryPackageFormat('debian-installer.9_all.udeb').name
507 'UDEB'
508 >>> getBinaryPackageFormat('network-manager.9_i386.rpm').name
509 'RPM'
510 """
511 for key, value in BINARYPACKAGE_EXTENSIONS.items():
512 if fname.endswith(value):
513 return key
514
515 raise UnrecognizedBinaryFormat(fname)
516
517
518def getBinaryPackageExtension(format):
519 """Return the file extension for the given BinaryPackageFormat.
520
521 >>> getBinaryPackageExtension(BinaryPackageFormat.DEB)
522 '.deb'
523 >>> getBinaryPackageExtension(BinaryPackageFormat.UDEB)
524 '.udeb'
525 >>> getBinaryPackageExtension(BinaryPackageFormat.RPM)
526 '.rpm'
527 """
528 return BINARYPACKAGE_EXTENSIONS[format]
529
530
531def intOrZero(value):468def intOrZero(value):
532 """Return int(value) or 0 if the conversion fails.469 """Return int(value) or 0 if the conversion fails.
533470
534471
=== modified file 'lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js'
--- lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js 2009-12-11 11:09:35 +0000
+++ lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js 2009-12-14 08:43:37 +0000
@@ -25,6 +25,7 @@
25 * The NodeList of possible duplicates.25 * The NodeList of possible duplicates.
26 */26 */
27var bug_already_reported_expanders;27var bug_already_reported_expanders;
28<<<<<<< TREE
28/*29/*
29 * The search field on the +filebug form30 * The search field on the +filebug form
30 */31 */
@@ -45,6 +46,16 @@
45 * The URL of the inline +filebug form.46 * The URL of the inline +filebug form.
46 */47 */
47var filebug_form_url;48var filebug_form_url;
49=======
50/*
51 * The search field on the +filebug form
52 */
53var search_field;
54/*
55 * The search button on the +filebug form
56 */
57var search_button;
58>>>>>>> MERGE-SOURCE
48/*59/*
49 * The boilerplate elements for the do-you-want-to-subscribe60 * The boilerplate elements for the do-you-want-to-subscribe
50 * FormOverlay.61 * FormOverlay.
@@ -119,6 +130,7 @@
119}130}
120131
121/**132/**
133<<<<<<< TREE
122 * Search for bugs that may match the text that the user has entered and134 * Search for bugs that may match the text that the user has entered and
123 * display them in-line.135 * display them in-line.
124 */136 */
@@ -203,6 +215,93 @@
203}215}
204216
205/*217/*
218=======
219 * Search for bugs that may match the text that the user has entered and
220 * display them in-line.
221 */
222function search_for_and_display_dupes() {
223 function show_failure_message(transaction_id, response, args) {
224 // If the request failed due to a timeout, display a message
225 // explaining how the user may be able to work around it.
226 var error_message = '';
227 if (response.status == 503) {
228 // We treat 503 (service unavailable) as a timeout because
229 // that's what timeouts in LP return.
230 error_message =
231 "Searching for your bug in Launchpad took too long. " +
232 "Try reducing the number of words in the summary " +
233 "field and click \"Check again\" to retry your search. " +
234 "Alternatively, you can enter the details of your bug " +
235 "below.";
236 } else {
237 // Any other error code gets a generic message.
238 error_message =
239 "An error occured whilst trying to find bugs matching " +
240 "the summary you entered. Click \"Check again\" to retry " +
241 "your search. Alternatively, you can enter the " +
242 "details of your bug below.";
243 }
244
245 var error_node = Y.Node.create('<p></p>');
246 error_node.set('text', error_message);
247 Y.one('#possible-duplicates').appendChild(error_node);
248
249 Y.one('#spinner').addClass(UNSEEN);
250 show_bug_reporting_form();
251
252 Y.one(Y.DOM.byId('field.title')).set(
253 'value', search_field.get('value'));
254 search_button.set('value', 'Check again');
255 search_button.removeClass(UNSEEN);
256 }
257
258 function on_success(transaction_id, response, args) {
259 // Hide the spinner and show the duplicates.
260 Y.one('#spinner').addClass(UNSEEN);
261
262 var duplicate_div = Y.one('#possible-duplicates');
263 duplicate_div.set(INNER_HTML, response.responseText);
264
265 bug_already_reported_expanders = Y.all(
266 'img.bug-already-reported-expander');
267 if (Y.Lang.isValue(bug_already_reported_expanders)) {
268 // If there are duplicates shown, set up the JavaScript of
269 // the duplicates that have been returned.
270 Y.bugs.setup_dupes();
271 } else {
272 // Otherwise, show the bug reporting form.
273 show_bug_reporting_form();
274 }
275
276 // Copy the value from the search field into the title field
277 // on the filebug form.
278 Y.one('#bug_reporting_form input[name=field.title]').set(
279 'value', search_field.get('value'));
280
281 // Finally, change the label on the search button and show it
282 // again.
283 search_button.set('value', 'Check again');
284 search_button.removeClass(UNSEEN);
285 }
286
287 var search_term = encodeURI(search_field.get('value'));
288 var search_url_base = Y.one(
289 '#duplicate-search-url').getAttribute('href');
290 var search_url = search_url_base + '?title=' + search_term;
291
292 // Hide the button, show the spinner and clear the contents of the
293 // possible duplicates div.
294 search_button.addClass(UNSEEN);
295 Y.one('#spinner').removeClass(UNSEEN);
296 Y.one('#possible-duplicates').set(INNER_HTML, '');
297
298 config = {on: {success: on_success,
299 failure: show_failure_message}};
300 Y.io(search_url, config);
301}
302
303/*
304>>>>>>> MERGE-SOURCE
206 * Create the overlay for a user to optionally subscribe to a bug that305 * Create the overlay for a user to optionally subscribe to a bug that
207 * affects them.306 * affects them.
208 * @param form The form to which the FormOverlay is going to be307 * @param form The form to which the FormOverlay is going to be
@@ -265,11 +364,21 @@
265364
266 // Add an on-click handler to the radio buttons to ensure that their365 // Add an on-click handler to the radio buttons to ensure that their
267 // labels' styles are set correctly when they're selected.366 // labels' styles are set correctly when they're selected.
367<<<<<<< TREE
268 var radio_buttons = form.queryAll('input.subscribe-option');368 var radio_buttons = form.queryAll('input.subscribe-option');
269 Y.each(radio_buttons, function(radio_button) {369 Y.each(radio_buttons, function(radio_button) {
270 var weight = radio_button.get('checked') ? 'bold' : 'normal';370 var weight = radio_button.get('checked') ? 'bold' : 'normal';
271 radio_button.get('parentNode').setStyle('fontWeight', weight);371 radio_button.get('parentNode').setStyle('fontWeight', weight);
272 });372 });
373=======
374 var radio_buttons = form.queryAll('input.subscribe-option');
375 if (Y.Lang.isValue(radio_buttons)) {
376 Y.each(radio_buttons, function(radio_button) {
377 var weight = radio_button.get('checked') ? 'bold' : 'normal';
378 radio_button.get('parentNode').setStyle('fontWeight', weight);
379 });
380 }
381>>>>>>> MERGE-SOURCE
273382
274 return subscribe_form_overlay;383 return subscribe_form_overlay;
275}384}
@@ -465,9 +574,142 @@
465 });574 });
466};575};
467576
577/**
578 * Set up the dupe finder, overriding the default behaviour of the
579 * +filebug search form.
580 */
581function set_up_dupe_finder(transaction_id, response, args) {
582 var filebug_form_container = Y.one('#filebug-form-container');
583 filebug_form_container.set(INNER_HTML, response.responseText);
584
585 // Activate the extra options collapsible section on the bug
586 // reporting form.
587 var bug_reporting_form = Y.one('#bug_reporting_form');
588 if (Y.Lang.isValue(bug_reporting_form)) {
589 activateCollapsibles();
590 }
591
592 search_button = Y.one(Y.DOM.byId('field.actions.search'));
593
594 // Change the name and id of the search field so that it doesn't
595 // confuse the view when we submit a bug report.
596 search_field = Y.one(Y.DOM.byId('field.title'));
597 search_field.set('name', 'field.search');
598 search_field.set('id', 'field.search');
599
600 // Update the label on the search button so that it no longer
601 // says "Continue".
602 search_button.set('value', 'Next');
603
604 // Set up the handler for the search form.
605 search_form = Y.one('#filebug-search-form');
606 search_form.on('submit', function(e) {
607 // Prevent the event from propagating; we don't want to reload
608 // the page.
609 e.halt();
610 search_for_and_display_dupes();
611 });
612}
613
614Y.bugs.setup_dupes = function() {
615 bug_already_reported_expanders = Y.all(
616 'img.bug-already-reported-expander');
617 bug_reporting_form = Y.one('#bug_reporting_form');
618
619 if (Y.Lang.isValue(bug_already_reported_expanders)) {
620 // Collapse all the details divs, since we don't want them
621 // expanded first up.
622 Y.each(Y.all('div.duplicate-details'), function(div) {
623 collapse_bug_details(div);
624 });
625
626 // Set up the onclick handlers for the expanders.
627 Y.each(Y.all('.similar-bug'), function(row) {
628 var bug_details_div = row.query('div.duplicate-details');
629 var image = row.query('img.bug-already-reported-expander');
630 var bug_title_link = row.query('.duplicate-bug-link');
631 var view_bug_link = row.query('.view-bug-link');
632
633 // Shut down the default action for the link and mark it
634 // as a JS'd link. We do this as it's simpler than
635 // trying to find all the bits of the row that we want
636 // to make clickable.
637 bug_title_link.addClass('js-action');
638 bug_title_link.on('click', function(e) {
639 e.preventDefault();
640 });
641
642 // The "view this bug" link shouldn't trigger the
643 // collapsible, so we stop the event from propagating.
644 view_bug_link.on('click', function(e) {
645 e.stopPropagation();
646 });
647
648 // The same is true for the collapsible section. People
649 // may want to copy and paste this, which involves
650 // clicking, so we stop the onclick event from
651 // propagating here, too.
652 bug_details_div.on('click', function(e) {
653 e.stopPropagation();
654 });
655
656 // Set up the on focus handler for the link so that
657 // tabbing will expand the different bugs.
658 bug_title_link.on('focus', function(e) {
659 if (!bug_details_div.hasClass('lazr-opened')) {
660 var anim = Y.lazr.effects.slide_out(bug_details_div);
661 anim.run();
662
663 image.set(SRC, EXPANDER_EXPANDED);
664
665 // If the bug reporting form is shown, hide it.
666 if (bug_reporting_form.getStyle(DISPLAY) == BLOCK) {
667 bug_reporting_form.addClass(UNSEEN);
668 }
669 }
670 });
671
672 row.on('click', function(e) {
673 if (bug_details_div.hasClass('lazr-opened')) {
674 collapse_bug_details(image);
675 } else {
676 var anim = Y.lazr.effects.slide_out(bug_details_div);
677 anim.run();
678
679 image.set(SRC, EXPANDER_EXPANDED);
680 }
681 });
682 });
683
684 // Hide the bug reporting form.
685 bug_reporting_form.addClass(UNSEEN);
686 }
687
688 bug_not_reported_button = Y.one('#bug-not-already-reported');
689 if (Y.Lang.isValue(bug_not_reported_button)) {
690 // The bug_not_reported_button won't show up if there aren't any
691 // possible duplicates.
692 bug_not_reported_button.on('click', show_bug_reporting_form);
693 }
694
695 // Attach the form overlay to the "Yes, this is my bug" forms.
696 var this_is_my_bug_forms = Y.all('form.this-is-my-bug-form');
697 Y.each(this_is_my_bug_forms, function(form) {
698 var subscribe_form_overlay = create_subscribe_overlay(form);
699
700 form.on('submit', function(e) {
701 // We don't care about the original event, so stop it
702 // and show the form overlay that we just created.
703 e.halt();
704 subscribe_form_overlay.show();
705 });
706 });
707};
708
468Y.bugs.setup_dupe_finder = function() {709Y.bugs.setup_dupe_finder = function() {
469 Y.log("In setup_dupe_finder");710 Y.log("In setup_dupe_finder");
470 Y.on('domready', function() {711 Y.on('domready', function() {
712<<<<<<< TREE
471 config = {on: {success: set_up_dupe_finder,713 config = {on: {success: set_up_dupe_finder,
472 failure: function() {}}};714 failure: function() {}}};
473715
@@ -481,6 +723,21 @@
481 'href');723 'href');
482 Y.io(filebug_form_url, config);724 Y.io(filebug_form_url, config);
483 }725 }
726=======
727 config = {on: {success: set_up_dupe_finder,
728 failure: function() {}}};
729
730 // Load the filebug form asynchronously. If this fails we
731 // degrade to the standard mode for bug filing, clicking through
732 // to the second part of the bug filing form.
733 var filebug_form_url_element = Y.one(
734 '#filebug-form-url');
735 if (Y.Lang.isValue(filebug_form_url_element)) {
736 var filebug_form_url = filebug_form_url_element.getAttribute(
737 'href');
738 Y.io(filebug_form_url, config);
739 }
740>>>>>>> MERGE-SOURCE
484 });741 });
485};742};
486743
487744
=== modified file 'lib/lp/archiveuploader/tests/test_utils.py'
--- lib/lp/archiveuploader/tests/test_utils.py 2009-11-18 02:58:23 +0000
+++ lib/lp/archiveuploader/tests/test_utils.py 2009-12-14 08:43:38 +0000
@@ -10,6 +10,7 @@
10import shutil10import shutil
1111
12from lp.registry.interfaces.sourcepackage import SourcePackageFileType12from lp.registry.interfaces.sourcepackage import SourcePackageFileType
13from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFileType
13from lp.archiveuploader.tests import datadir14from lp.archiveuploader.tests import datadir
1415
1516
@@ -72,6 +73,23 @@
72 self.assertEquals(None, determine_source_file_type('foo_1.0'))73 self.assertEquals(None, determine_source_file_type('foo_1.0'))
73 self.assertEquals(None, determine_source_file_type('foo_1.0.blah.gz'))74 self.assertEquals(None, determine_source_file_type('foo_1.0.blah.gz'))
7475
76 def test_determine_binary_file_type(self):
77 """lp.archiveuploader.utils.determine_binary_file_type should work."""
78 from lp.archiveuploader.utils import determine_binary_file_type
79
80 # .deb -> DEB
81 self.assertEquals(
82 determine_binary_file_type('foo_1.0-1_all.deb'),
83 BinaryPackageFileType.DEB)
84
85 # .udeb -> UDEB
86 self.assertEquals(
87 determine_binary_file_type('foo_1.0-1_all.udeb'),
88 BinaryPackageFileType.UDEB)
89
90 self.assertEquals(determine_binary_file_type('foo_1.0'), None)
91 self.assertEquals(determine_binary_file_type('foo_1.0.notdeb'), None)
92
75 def testPrefixMultilineString(self):93 def testPrefixMultilineString(self):
76 """lp.archiveuploader.utils.prefix_multi_line_string should work"""94 """lp.archiveuploader.utils.prefix_multi_line_string should work"""
77 from lp.archiveuploader.utils import prefix_multi_line_string95 from lp.archiveuploader.utils import prefix_multi_line_string
7896
=== modified file 'lib/lp/archiveuploader/utils.py'
--- lib/lp/archiveuploader/utils.py 2009-11-14 02:59:34 +0000
+++ lib/lp/archiveuploader/utils.py 2009-12-14 08:43:37 +0000
@@ -17,6 +17,7 @@
17 're_changes_file_name',17 're_changes_file_name',
18 're_extract_src_version',18 're_extract_src_version',
19 'get_source_file_extension',19 'get_source_file_extension',
20 'determine_binary_file_type',
20 'determine_source_file_type',21 'determine_source_file_type',
21 'prefix_multi_line_string',22 'prefix_multi_line_string',
22 'safe_fix_maintainer',23 'safe_fix_maintainer',
@@ -88,6 +89,19 @@
88 return None89 return None
8990
9091
92def determine_binary_file_type(filename):
93 """Determine the BinaryPackageFileType of the given filename."""
94 # Avoid circular imports.
95 from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFileType
96
97 if filename.endswith(".deb"):
98 return BinaryPackageFileType.DEB
99 elif filename.endswith(".udeb"):
100 return BinaryPackageFileType.UDEB
101 else:
102 return None
103
104
91def prefix_multi_line_string(str, prefix, include_blank_lines=0):105def prefix_multi_line_string(str, prefix, include_blank_lines=0):
92 """Utility function to split an input string and prefix,106 """Utility function to split an input string and prefix,
93107
94108
=== modified file 'lib/lp/bugs/browser/bugtarget.py'
--- lib/lp/bugs/browser/bugtarget.py 2009-12-11 10:58:46 +0000
+++ lib/lp/bugs/browser/bugtarget.py 2009-12-14 08:43:37 +0000
@@ -360,6 +360,11 @@
360 if 'field.actions' in key] != [] or360 if 'field.actions' in key] != [] or
361 self.user.inTeam(bug_supervisor))361 self.user.inTeam(bug_supervisor))
362362
363 @property
364 def use_asynchronous_dupefinder(self):
365 """Return True if the asynchronous dupe finder can be used."""
366 return IProduct.providedBy(self.context)
367
363 def getPackageNameFieldCSSClass(self):368 def getPackageNameFieldCSSClass(self):
364 """Return the CSS class for the packagename field."""369 """Return the CSS class for the packagename field."""
365 if self.widget_errors.get("packagename"):370 if self.widget_errors.get("packagename"):
@@ -700,6 +705,7 @@
700 """Override this method in base classes to show the filebug form."""705 """Override this method in base classes to show the filebug form."""
701 raise NotImplementedError706 raise NotImplementedError
702707
708<<<<<<< TREE
703 @property709 @property
704 def inline_filebug_base_url(self):710 def inline_filebug_base_url(self):
705 """Return the base URL for the current request.711 """Return the base URL for the current request.
@@ -729,6 +735,28 @@
729 url = urlappend(url, self.extra_data_token)735 url = urlappend(url, self.extra_data_token)
730 return url736 return url
731737
738=======
739 @property
740 def inline_filebug_form_url(self):
741 """The URL to the inline filebug form.
742
743 If a token was passed to this view, it will be be passed through
744 to the inline bug filing form via the returned URL.
745 """
746 url = canonical_url(self.context, view_name='+filebug-inline-form')
747 if self.extra_data_token is not None:
748 url = urlappend(url, self.extra_data_token)
749 return url
750
751 @property
752 def duplicate_search_url(self):
753 """The URL to the inline duplicate search view."""
754 url = canonical_url(self.context, view_name='+filebug-show-similar')
755 if self.extra_data_token is not None:
756 url = urlappend(url, self.extra_data_token)
757 return url
758
759>>>>>>> MERGE-SOURCE
732 def publishTraverse(self, request, name):760 def publishTraverse(self, request, name):
733 """See IBrowserPublisher."""761 """See IBrowserPublisher."""
734 if self.extra_data_token is not None:762 if self.extra_data_token is not None:
735763
=== modified file 'lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt'
--- lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt 2009-12-10 17:29:26 +0000
+++ lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt 2009-12-14 08:43:38 +0000
@@ -43,6 +43,7 @@
43 u'Test description.'43 u'Test description.'
4444
4545
46<<<<<<< TREE
46== URLs to additional FileBug elements ==47== URLs to additional FileBug elements ==
4748
48FileBugViewBase's inline_filebug_base_url returns the base URL for all49FileBugViewBase's inline_filebug_base_url returns the base URL for all
@@ -90,6 +91,27 @@
90 http://launchpad.dev/evolution/+filebug-show-similar91 http://launchpad.dev/evolution/+filebug-show-similar
9192
9293
94=======
95== URLs to additional FileBug elements ==
96
97FileBugViewBase provides properties that return the URLs of further
98useful parts of the +filebug process.
99
100The inline_filebug_form_url property returns the URL of the inline
101filebug form so that it may be loaded asynchronously.
102
103 >>> print filebug_view.inline_filebug_form_url
104 http://launchpad.dev/ubuntu/+source/mozilla-firefox/+filebug-inline-form
105
106Similarly, the duplicate_search_url property returns the base URL for
107the duplicate search view, which can be used to load the list of
108possible duplicates for a bug asynchronously.
109
110 >>> print filebug_view.duplicate_search_url
111 http://launchpad.dev/ubuntu/+source/mozilla-firefox/+filebug-show-similar
112
113
114>>>>>>> MERGE-SOURCE
93== Adding extra info to filed bugs ==115== Adding extra info to filed bugs ==
94116
95It's possible for bug reporting tools to upload a file with debug117It's possible for bug reporting tools to upload a file with debug
96118
=== modified file 'lib/lp/bugs/stories/bugattachments/20-edit-bug-attachment.txt'
--- lib/lp/bugs/stories/bugattachments/20-edit-bug-attachment.txt 2009-12-10 15:12:22 +0000
+++ lib/lp/bugs/stories/bugattachments/20-edit-bug-attachment.txt 2009-12-14 08:43:38 +0000
@@ -23,6 +23,7 @@
2323
24...we're redirected to the bug page24...we're redirected to the bug page
2525
26<<<<<<< TREE
26 >>> user_browser.url27 >>> user_browser.url
27 'http://bugs.launchpad.dev/firefox/+bug/1'28 'http://bugs.launchpad.dev/firefox/+bug/1'
2829
@@ -67,3 +68,40 @@
67 >>> patch_checkbox = user_browser.getControl('This attachment is a patch')68 >>> patch_checkbox = user_browser.getControl('This attachment is a patch')
68 >>> patch_checkbox.selected69 >>> patch_checkbox.selected
69 True70 True
71=======
72 >>> user_browser.url
73 'http://bugs.launchpad.dev/firefox/+bug/1'
74
75 >>> 'Another title' in user_browser.contents
76 True
77
78We can edit the attachment to be a patch.
79
80 >>> user_browser.getLink(url='+attachment/1').click()
81 >>> user_browser.getControl('This attachment is a patch').selected = True
82 >>> user_browser.getControl('Change').click()
83
84 >>> user_browser.url
85 'http://bugs.launchpad.dev/firefox/+bug/1'
86
87The attachment that became a patch is now shown in the portlet "Patches"...
88
89 >>> patches = find_portlet(user_browser.contents, 'Patches')
90 >>> for li_tag in patches.findAll('li', 'download-attachment'):
91 ... print li_tag.a.renderContents()
92 Another title
93
94...while it is gone from the portlet "Bug attachments".
95
96 >>> attachments = find_portlet(user_browser.contents, 'Bug attachments')
97 >>> for li_tag in attachments.findAll('li', 'download-attachment'):
98 ... print li_tag.a.renderContents()
99 bar.txt
100
101The content type of a patch is automatically changed to text/plain.
102
103 >>> user_browser.getLink(url='+attachment/1').click()
104 >>> user_browser.getControl('Content Type').value
105 'text/plain'
106
107>>>>>>> MERGE-SOURCE
70108
=== modified file 'lib/lp/bugs/templates/bug-portlet-attachments.pt'
--- lib/lp/bugs/templates/bug-portlet-attachments.pt 2009-12-10 15:07:18 +0000
+++ lib/lp/bugs/templates/bug-portlet-attachments.pt 2009-12-14 08:43:39 +0000
@@ -2,6 +2,7 @@
2 xmlns:tal="http://xml.zope.org/namespaces/tal"2 xmlns:tal="http://xml.zope.org/namespaces/tal"
3 xmlns:metal="http://xml.zope.org/namespaces/metal"3 xmlns:metal="http://xml.zope.org/namespaces/metal"
4 xmlns:i18n="http://xml.zope.org/namespaces/i18n"4 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
5<<<<<<< TREE
5 tal:omit-tag="">6 tal:omit-tag="">
6 <div tal:condition="view/regular_attachments" class="portlet"7 <div tal:condition="view/regular_attachments" class="portlet"
7 id="portlet-attachments">8 id="portlet-attachments">
@@ -48,4 +49,52 @@
48 </li>49 </li>
49 </ul>50 </ul>
50 </div>51 </div>
52=======
53 tal:omit-tag="">
54 <div tal:condition="view/regular_attachments" class="portlet"
55 id="portlet-attachments">
56 <h2>Bug attachments</h2>
57 <ul>
58 <li class="download-attachment"
59 tal:repeat="attachment view/regular_attachments">
60 <a tal:attributes="href attachment/libraryfile/http_url"
61 tal:content="attachment/title"
62 class="sprite download-icon">
63 Attachment Title
64 </a>
65 <small>
66 (<a tal:attributes="href attachment/fmt:url">edit</a>)
67 </small>
68 </li>
69 </ul>
70 <ul>
71 <li>
72 <a tal:attributes="href view/current_bugtask/fmt:url/+addcomment"
73 class="sprite add">Add attachment</a>
74 </li>
75 </ul>
76 </div>
77 <div tal:condition="view/patches" class="portlet" id="portlet-patches">
78 <h2>Patches</h2>
79 <ul>
80 <li class="download-attachment"
81 tal:repeat="attachment view/patches">
82 <a tal:attributes="href attachment/libraryfile/http_url"
83 tal:content="attachment/title"
84 class="sprite download-icon">
85 Attachment Title
86 </a>
87 <small>
88 (<a tal:attributes="href attachment/fmt:url">edit</a>)
89 </small>
90 </li>
91 </ul>
92 <ul>
93 <li>
94 <a tal:attributes="href view/current_bugtask/fmt:url/+addcomment"
95 class="sprite add">Add patch</a>
96 </li>
97 </ul>
98 </div>
99>>>>>>> MERGE-SOURCE
51</div>100</div>
52101
=== modified file 'lib/lp/bugs/templates/bugtarget-filebug-search.pt'
--- lib/lp/bugs/templates/bugtarget-filebug-search.pt 2009-12-11 10:58:46 +0000
+++ lib/lp/bugs/templates/bugtarget-filebug-search.pt 2009-12-14 08:43:38 +0000
@@ -5,6 +5,7 @@
5 xmlns:i18n="http://xml.zope.org/namespaces/i18n"5 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
6 metal:use-macro="view/macro:page/main_only">6 metal:use-macro="view/macro:page/main_only">
77
8<<<<<<< TREE
8 <metal:block fill-slot="head_epilogue">9 <metal:block fill-slot="head_epilogue">
9 <script type="text/javascript"10 <script type="text/javascript"
10 tal:condition="devmode"11 tal:condition="devmode"
@@ -22,6 +23,27 @@
22 </script>23 </script>
23 </metal:block>24 </metal:block>
2425
26=======
27 <metal:block fill-slot="head_epilogue">
28 <script type="text/javascript"
29 tal:condition="devmode"
30 tal:content="string:var yui_base='${yui}';"></script>
31 <script type="text/javascript"
32 tal:condition="devmode"
33 tal:define="lp_js string:${icingroot}/build"
34 tal:attributes="src string:${lp_js}/bugs/filebug-dupefinder.js"></script>
35
36 <tal:dupe-finder-js condition="view/use_asynchronous_dupefinder">
37 <script type="text/javascript">
38 LPS.use(
39 'base', 'node', 'oop', 'event', 'bugs.dupe_finder', function(Y) {
40 Y.bugs.setup_dupe_finder();
41 });
42 </script>
43 </tal:dupe-finder-js>
44 </metal:block>
45
46>>>>>>> MERGE-SOURCE
25 <div metal:fill-slot="heading">47 <div metal:fill-slot="heading">
26 <div tal:condition="view/isPrivate" id="privacy" class="aside private">48 <div tal:condition="view/isPrivate" id="privacy" class="aside private">
27 This report will be private, though you can disclose it later.49 This report will be private, though you can disclose it later.
@@ -106,6 +128,7 @@
106 We add this to hide the standard action buttons.128 We add this to hide the standard action buttons.
107 </tal:comment>129 </tal:comment>
108 </div>130 </div>
131<<<<<<< TREE
109 </div>132 </div>
110 </div>133 </div>
111134
@@ -128,3 +151,25 @@
128 </div>151 </div>
129152
130</html>153</html>
154=======
155 </div>
156 </div>
157 </tal:uses-malone>
158 </div>
159
160 <div id="possible-duplicates" style="text-align: left;">
161 </div>
162 <div id="filebug-form-container" style="display: none;">
163 </div>
164
165 <p style="display: none" tal:condition="view/use_asynchronous_dupefinder">
166 <a id="filebug-form-url"
167 tal:attributes="href view/inline_filebug_form_url"></a>
168 <a id="duplicate-search-url"
169 tal:attributes="href view/duplicate_search_url"></a>
170 </p>
171
172 </div>
173
174</html>
175>>>>>>> MERGE-SOURCE
131176
=== modified file 'lib/lp/bugs/templates/bugtasks-and-nominations-table.pt'
--- lib/lp/bugs/templates/bugtasks-and-nominations-table.pt 2009-12-11 14:00:13 +0000
+++ lib/lp/bugs/templates/bugtasks-and-nominations-table.pt 2009-12-14 08:43:38 +0000
@@ -112,7 +112,54 @@
112 define="link context_menu/nominate"112 define="link context_menu/nominate"
113 condition="link/enabled"113 condition="link/enabled"
114 replace="structure link/render" />114 replace="structure link/render" />
115<<<<<<< TREE
115 </tal:also-affects-links>116 </tal:also-affects-links>
117=======
118 <span id="affectsmetoo" style="display: inline"
119 tal:condition="link/enabled"
120 tal:define="link context_menu/affectsmetoo;
121 affected view/current_user_affected_status">
122
123 <tal:comment condition="nothing">
124 This .static section is shown in browsers with javascript
125 enabled, and before setup_me_too is run.
126 </tal:comment>
127 <span class="static">
128 <tal:affected condition="affected">
129 <img width="14" height="14" src="/@@/flame-icon" alt="" />
130 This bug affects me too
131 </tal:affected>
132 <tal:not-affected condition="not:affected">
133 This bug doesn't affect me
134 </tal:not-affected>
135 <a href="+affectsmetoo">
136 <img class="editicon" src="/@@/edit" alt="Edit" />
137 </a>
138 </span>
139
140 <tal:comment condition="nothing">
141 This .dynamic section is used by setup_me_too to display
142 controls and information in the correct places.
143 </tal:comment>
144 <span class="dynamic unseen">
145 <img src="/@@/flame-icon" alt=""/>
146 <a href="+affectsmetoo" class="js-action"
147 ><span class="value">Does this bug affect you?</span></a>
148 <img class="editicon" src="/@@/edit" alt="Edit" />
149 </span>
150
151 <script type="text/javascript" tal:content="string:
152 LPS.use('event', 'bugs.bugtask_index', function(Y) {
153 Y.on('load', function(e) {
154 Y.bugs.setup_me_too(${view/current_user_affected_js_status});
155 }, window);
156 });
157 ">
158 </script>
159
160 </span>
161 </tal:also-affects-links>
162>>>>>>> MERGE-SOURCE
116</div>163</div>
117164
118</tal:root>165</tal:root>
119166
=== modified file 'lib/lp/code/browser/branch.py'
--- lib/lp/code/browser/branch.py 2009-12-11 00:56:16 +0000
+++ lib/lp/code/browser/branch.py 2009-12-14 08:43:37 +0000
@@ -83,8 +83,14 @@
83 UICreatableBranchType)83 UICreatableBranchType)
84from lp.code.errors import InvalidBranchMergeProposal84from lp.code.errors import InvalidBranchMergeProposal
85from lp.code.interfaces.branch import (85from lp.code.interfaces.branch import (
86 BranchCreationForbidden, BranchExists, IBranch,86<<<<<<< TREE
87 user_has_special_branch_access)87 BranchCreationForbidden, BranchExists, IBranch,
88 user_has_special_branch_access)
89=======
90 BranchCreationForbidden, BranchExists, IBranch,
91 user_has_special_branch_access)
92from lp.code.interfaces.branchmergeproposal import InvalidBranchMergeProposal
93>>>>>>> MERGE-SOURCE
88from lp.code.interfaces.branchtarget import IBranchTarget94from lp.code.interfaces.branchtarget import IBranchTarget
89from lp.code.interfaces.codeimport import CodeImportReviewStatus95from lp.code.interfaces.codeimport import CodeImportReviewStatus
90from lp.code.interfaces.codeimportjob import (96from lp.code.interfaces.codeimportjob import (
9197
=== modified file 'lib/lp/code/templates/branchmergeproposal-index.pt'
--- lib/lp/code/templates/branchmergeproposal-index.pt 2009-12-11 05:07:57 +0000
+++ lib/lp/code/templates/branchmergeproposal-index.pt 2009-12-14 08:43:37 +0000
@@ -110,6 +110,7 @@
110 </div>110 </div>
111111
112 <div class="yui-g">112 <div class="yui-g">
113<<<<<<< TREE
113 <tal:not-logged-in condition="not: view/user">114 <tal:not-logged-in condition="not: view/user">
114 <div align="center" id="add-comment-login-first">115 <div align="center" id="add-comment-login-first">
115 To post a comment you must <a href="+login">log in</a>.116 To post a comment you must <a href="+login">log in</a>.
@@ -117,6 +118,9 @@
117 </tal:not-logged-in>118 </tal:not-logged-in>
118119
119 <div tal:define="link menu/add_comment"120 <div tal:define="link menu/add_comment"
121=======
122 <div tal:define="link menu/add_comment"
123>>>>>>> MERGE-SOURCE
120 tal:condition="link/enabled"124 tal:condition="link/enabled"
121 tal:content="structure link/render">125 tal:content="structure link/render">
122 Add comment126 Add comment
@@ -125,6 +129,7 @@
125 <div id="conversation"129 <div id="conversation"
126 tal:content="structure view/conversation/@@+render"/>130 tal:content="structure view/conversation/@@+render"/>
127 </div>131 </div>
132<<<<<<< TREE
128133
129 <tal:logged-in condition="view/user">134 <tal:logged-in condition="view/user">
130 <div tal:define="comment_form nocall:context/@@+comment;135 <div tal:define="comment_form nocall:context/@@+comment;
@@ -146,6 +151,22 @@
146 </div>151 </div>
147 </tal:logged-in>152 </tal:logged-in>
148153
154=======
155 <!-- Hide inline commenting if YUI isn't used. -->
156 <div id="inline-add-comment" style="display: none">
157 <tal:comment replace="structure context/@@+comment/++form++" />
158 <div class="actions" id="launchpad-form-actions">
159 <input type="submit" id="field.actions.add" name="field.actions.add" value="Save Comment" class="button" />
160 </div>
161 </div>
162
163 <script type="text/javascript">
164 LPS.use('lp.comment', function(Y) {
165 var comment = new Y.lp.CodeReviewComment();
166 comment.render();
167 })
168 </script>
169>>>>>>> MERGE-SOURCE
149 <div class="yui-g">170 <div class="yui-g">
150 <div class="yui-u first">171 <div class="yui-u first">
151 <div id="source-revisions"172 <div id="source-revisions"
@@ -190,7 +211,11 @@
190 string:&lt;script id='codereview-script' type='text/javascript'&gt;" />211 string:&lt;script id='codereview-script' type='text/javascript'&gt;" />
191 conf = <tal:status-config replace="view/status_config" />212 conf = <tal:status-config replace="view/status_config" />
192 <!--213 <!--
214<<<<<<< TREE
193 LPS.use('io-base', 'code.codereview', 'code.branchmergeproposal', 'lp.comment',215 LPS.use('io-base', 'code.codereview', 'code.branchmergeproposal', 'lp.comment',
216=======
217 LPS.use('io-base', 'code.codereview', 'code.branchmergeproposal',
218>>>>>>> MERGE-SOURCE
194 function(Y) {219 function(Y) {
195220
196 Y.on('load', function() {221 Y.on('load', function() {
197222
=== modified file 'lib/lp/registry/browser/distroseries.py'
--- lib/lp/registry/browser/distroseries.py 2009-12-09 19:47:23 +0000
+++ lib/lp/registry/browser/distroseries.py 2009-12-14 08:43:39 +0000
@@ -462,27 +462,59 @@
462 """A View to show series package to upstream package relationships."""462 """A View to show series package to upstream package relationships."""
463463
464 label = 'Mapping series packages to upstream project series'464 label = 'Mapping series packages to upstream project series'
465 page_title = 'Upstream packaging links'465<<<<<<< TREE
466466 page_title = 'Upstream packaging links'
467 @cachedproperty467
468 def unlinked_translatables(self):468 @cachedproperty
469 """The sourcepackages that lack a link to a productseries."""469 def unlinked_translatables(self):
470 packages = self.context.getUnlinkedTranslatableSourcePackages()470 """The sourcepackages that lack a link to a productseries."""
471 if self.context.distribution.full_functionality:471 packages = self.context.getUnlinkedTranslatableSourcePackages()
472 # Launchpad knows exactly what is published in the series.472 if self.context.distribution.full_functionality:
473 packages = [package for package in packages473 # Launchpad knows exactly what is published in the series.
474 if package.currentrelease is not None]474 packages = [package for package in packages
475 return packages475 if package.currentrelease is not None]
476476 return packages
477 @cachedproperty477
478 def show_unlinked_translatables(self):478 @cachedproperty
479 """Are there unlinked translatables and should they be shown."""479 def show_unlinked_translatables(self):
480 return (480 """Are there unlinked translatables and should they be shown."""
481 len(self.unlinked_translatables) > 0481 return (
482 and self.cached_packagings.start == 0)482 len(self.unlinked_translatables) > 0
483483 and self.cached_packagings.start == 0)
484 @cachedproperty484
485 def cached_packagings(self):485 @cachedproperty
486 """The batched upstream packaging links."""486 def cached_packagings(self):
487 packagings = self.context.packagings487 """The batched upstream packaging links."""
488 return BatchNavigator(packagings, self.request, size=200)488 packagings = self.context.packagings
489 return BatchNavigator(packagings, self.request, size=200)
490=======
491 page_title = 'Upstream packaging links'
492
493 @cachedproperty
494 def unlinked_translatables(self):
495 """The sourcepackages that lack a link to a productseries."""
496 packages = self.context.getUnlinkedTranslatableSourcePackages()
497 if self.context.distribution.full_functionality:
498 # Launchpad knows exactly what is published in the series.
499 packages = [package for package in packages
500 if package.currentrelease is not None]
501 return packages
502
503 @cachedproperty
504 def show_unlinked_translatables(self):
505 """Are there unlinked translatables and should they be shown."""
506 return (
507 len(self.unlinked_translatables) > 0
508 and self.cached_packagings.start == 0)
509
510 @cachedproperty
511 def cached_packagings(self):
512 """The batched upstream packaging links."""
513 packagings = self.context.packagings
514 if self.context.distribution.full_functionality:
515 # Launchpad knows exactly what is published in the series.
516 packagings = [
517 packaging for packaging in packagings
518 if packaging.sourcepackage.currentrelease is not None]
519 return BatchNavigator(packagings, self.request, size=200)
520>>>>>>> MERGE-SOURCE
489521
=== modified file 'lib/lp/registry/browser/tests/packaging-views.txt'
--- lib/lp/registry/browser/tests/packaging-views.txt 2009-12-10 20:03:11 +0000
+++ lib/lp/registry/browser/tests/packaging-views.txt 2009-12-14 08:43:38 +0000
@@ -341,6 +341,7 @@
341 sarge hotter341 sarge hotter
342 7.0 hotter342 7.0 hotter
343 grumpy hotter343 grumpy hotter
344<<<<<<< TREE
344345
345346
346Distro series +packaging view347Distro series +packaging view
@@ -390,3 +391,60 @@
390 ... hoary, name='+packaging', query_string='start=2')391 ... hoary, name='+packaging', query_string='start=2')
391 >>> view.show_unlinked_translatables392 >>> view.show_unlinked_translatables
392 False393 False
394=======
395
396
397Distro series +packaging view
398-----------------------------
399
400The DistroSeriesPackagesView shows the packages in a distro series that
401are linked to upstream projects.
402
403 >>> view = create_initialized_view(hoary, name='+packaging')
404 >>> print view.label
405 Mapping series packages to upstream project series
406
407 >>> print view.page_title
408 Upstream packaging links
409
410The view provides a property to get the source packages that have
411translations, but are not linked to an upstream project. The view filters
412unpublished versions from full functionality distributions like Ubuntu.
413
414 >>> hoary.getUnlinkedTranslatableSourcePackages()
415 [<SourcePackage .../hoary/pmount>, <SourcePackage .../hoary/mozilla>]
416
417 >>> view.show_unlinked_translatables
418 True
419
420 >>> view.unlinked_translatables
421 [<SourcePackage ubuntu/hoary/pmount>]
422
423A distro series can have thousands of upstream packaging links. The view
424provides a batch navigator to access the packagings. The default batch size
425is 200. The view filters unpublished versions from full functionality
426distributions like Ubuntu.
427
428 >>> for packaging in hoary.packagings:
429 ... print packaging.sourcepackagename.name
430 evolution
431 mozilla-firefox
432 netapplet
433
434 >>> batch_navigator = view.cached_packagings
435 >>> batch_navigator.default_size
436 200
437
438 >>> for packaging in batch_navigator.batch:
439 ... print packaging.sourcepackagename.name
440 evolution
441 netapplet
442
443The show_unlinked_translatables property is False when the batch is after the
444first batch.
445
446 >>> view = create_initialized_view(
447 ... hoary, name='+packaging', query_string='start=2')
448 >>> view.show_unlinked_translatables
449 False
450>>>>>>> MERGE-SOURCE
393451
=== modified file 'lib/lp/registry/interfaces/person.py'
--- lib/lp/registry/interfaces/person.py 2009-12-09 23:11:31 +0000
+++ lib/lp/registry/interfaces/person.py 2009-12-14 08:43:39 +0000
@@ -65,10 +65,16 @@
6565
66from canonical.database.sqlbase import block_implicit_flushes66from canonical.database.sqlbase import block_implicit_flushes
67from canonical.launchpad.fields import (67from canonical.launchpad.fields import (
68<<<<<<< TREE
68 BlacklistableContentNameField, IconImageUpload,69 BlacklistableContentNameField, IconImageUpload,
69 is_private_membership_person, is_public_person, LogoImageUpload,70 is_private_membership_person, is_public_person, LogoImageUpload,
70 MugshotImageUpload, PasswordField, PersonChoice, PublicPersonChoice,71 MugshotImageUpload, PasswordField, PersonChoice, PublicPersonChoice,
71 StrippedTextLine)72 StrippedTextLine)
73=======
74 BlacklistableContentNameField, IconImageUpload, is_private_membership,
75 is_valid_public_person, LogoImageUpload, MugshotImageUpload,
76 PasswordField, PersonChoice, PublicPersonChoice, StrippedTextLine)
77>>>>>>> MERGE-SOURCE
72from canonical.launchpad.interfaces.account import AccountStatus, IAccount78from canonical.launchpad.interfaces.account import AccountStatus, IAccount
73from canonical.launchpad.interfaces.emailaddress import IEmailAddress79from canonical.launchpad.interfaces.emailaddress import IEmailAddress
74from lp.app.interfaces.headings import IRootContext80from lp.app.interfaces.headings import IRootContext
7581
=== modified file 'lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt'
--- lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt 2009-12-10 19:58:31 +0000
+++ lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt 2009-12-14 08:43:38 +0000
@@ -1,48 +1,98 @@
1Distro series packaging1<<<<<<< TREE
2=======================2Distro series packaging
33=======================
4The distro series packaging page is accssible to any user from the distro4
5series +index page.5The distro series packaging page is accssible to any user from the distro
66series +index page.
7 >>> anon_browser.open('http://launchpad.dev/ubuntu/hoary')7
8 >>> anon_browser.getLink('Upstream links').click()8 >>> anon_browser.open('http://launchpad.dev/ubuntu/hoary')
9 >>> print anon_browser.title9 >>> anon_browser.getLink('Upstream links').click()
10 Upstream packaging links : ...10 >>> print anon_browser.title
1111 Upstream packaging links : ...
12The page lists the the source packages that have translations, but are12
13not linked to an upstream project to sync them to.13The page lists the the source packages that have translations, but are
1414not linked to an upstream project to sync them to.
15 >>> content = find_main_content(anon_browser.contents)15
16 >>> print extract_text(find_tag_by_id(content, 'unlinked-translatables'))16 >>> content = find_main_content(anon_browser.contents)
17 Unlinked translatable packages17 >>> print extract_text(find_tag_by_id(content, 'unlinked-translatables'))
18 pmount18 Unlinked translatable packages
1919 pmount
20The page lists the upstream packaging links.20
2121The page lists the upstream packaging links.
22 >>> print extract_text(find_tag_by_id(content, 'packagings'))22
23 Source Package Upstream Project Series23 >>> print extract_text(find_tag_by_id(content, 'packagings'))
24 evolution Evolution trunk series24 Source Package Upstream Project Series
25 mozilla-firefox Mozilla Firefox 1.0 series25 evolution Evolution trunk series
26 netapplet NetApplet trunk series26 mozilla-firefox Mozilla Firefox 1.0 series
2727 netapplet NetApplet trunk series
28The packaging links are batched so that users can view the thousands of28
29links packages. Users can also hack the URL to set their own batch size.29The packaging links are batched so that users can view the thousands of
3030links packages. Users can also hack the URL to set their own batch size.
31 >>> anon_browser.open(31
32 ... 'http://launchpad.dev/ubuntu/hoary/+packaging?start=0&batch=1')32 >>> anon_browser.open(
33 >>> print extract_text(find_tag_by_id(33 ... 'http://launchpad.dev/ubuntu/hoary/+packaging?start=0&batch=1')
34 ... anon_browser.contents, 'packagings'))34 >>> print extract_text(find_tag_by_id(
35 Source Package Upstream Project Series35 ... anon_browser.contents, 'packagings'))
36 evolution Evolution trunk series36 Source Package Upstream Project Series
3737 evolution Evolution trunk series
38 >>> anon_browser.getLink('Next', index=0).click()38
39 >>> content = find_main_content(anon_browser.contents)39 >>> anon_browser.getLink('Next', index=0).click()
40 >>> print extract_text(find_tag_by_id(content, 'packagings'))40 >>> content = find_main_content(anon_browser.contents)
41 Source Package Upstream Project Series41 >>> print extract_text(find_tag_by_id(content, 'packagings'))
42 mozilla-firefox Mozilla Firefox 1.0 series42 Source Package Upstream Project Series
4343 mozilla-firefox Mozilla Firefox 1.0 series
44The unlinked translatable source packages is not displayed on pages after44
45the first page.45The unlinked translatable source packages is not displayed on pages after
4646the first page.
47 >>> print find_tag_by_id(content, 'unlinked_translatables')47
48 None48 >>> print find_tag_by_id(content, 'unlinked_translatables')
49 None
50=======
51Distro series packaging
52=======================
53
54The distro series packaging page is accssible to any user from the distro
55series +index page.
56
57 >>> anon_browser.open('http://launchpad.dev/ubuntu/hoary')
58 >>> anon_browser.getLink('Upstream links').click()
59 >>> print anon_browser.title
60 Upstream packaging links : ...
61
62The page lists the the source packages that have translations, but are
63not linked to an upstream project to sync them to.
64
65 >>> content = find_main_content(anon_browser.contents)
66 >>> print extract_text(find_tag_by_id(content, 'unlinked-translatables'))
67 Unlinked translatable packages
68 pmount
69
70The page lists the upstream packaging links.
71
72 >>> print extract_text(find_tag_by_id(content, 'packagings'))
73 Source Package Upstream Project Series
74 evolution Evolution trunk series
75 netapplet NetApplet trunk series
76
77The packaging links are batched so that users can view the thousands of
78links packages. Users can also hack the URL to set their own batch size.
79
80 >>> anon_browser.open(
81 ... 'http://launchpad.dev/ubuntu/hoary/+packaging?start=0&batch=1')
82 >>> print extract_text(find_tag_by_id(
83 ... anon_browser.contents, 'packagings'))
84 Source Package Upstream Project Series
85 evolution Evolution trunk series
86
87 >>> anon_browser.getLink('Next', index=0).click()
88 >>> content = find_main_content(anon_browser.contents)
89 >>> print extract_text(find_tag_by_id(content, 'packagings'))
90 Source Package Upstream Project Series
91 netapplet NetApplet trunk series
92
93The unlinked translatable source packages is not displayed on pages after
94the first page.
95
96 >>> print find_tag_by_id(content, 'unlinked_translatables')
97 None
98>>>>>>> MERGE-SOURCE
4999
=== modified file 'lib/lp/registry/tests/test_productseries_vocabularies.py'
--- lib/lp/registry/tests/test_productseries_vocabularies.py 2009-12-09 23:11:31 +0000
+++ lib/lp/registry/tests/test_productseries_vocabularies.py 2009-12-14 08:43:39 +0000
@@ -1,3 +1,4 @@
1<<<<<<< TREE
1# Copyright 2009 Canonical Ltd. This software is licensed under the2# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).3# GNU Affero General Public License version 3 (see the file LICENSE).
34
@@ -80,3 +81,77 @@
8081
81def test_suite():82def test_suite():
82 return TestLoader().loadTestsFromName(__name__)83 return TestLoader().loadTestsFromName(__name__)
84=======
85# Copyright 2009 Canonical Ltd. This software is licensed under the
86# GNU Affero General Public License version 3 (see the file LICENSE).
87
88"""Test the milestone vocabularies."""
89
90__metaclass__ = type
91
92from unittest import TestLoader
93from operator import attrgetter
94
95from lp.registry.vocabularies import ProductSeriesVocabulary
96from lp.testing import TestCaseWithFactory
97
98from canonical.testing import DatabaseFunctionalLayer
99
100
101class TestProductSeriesVocabulary(TestCaseWithFactory):
102 """Test that the ProductSeriesVocabulary behaves as expected."""
103 layer = DatabaseFunctionalLayer
104
105 def setUp(self):
106 super(TestProductSeriesVocabulary, self).setUp()
107 self.vocabulary = ProductSeriesVocabulary()
108 self.product_prefix = 'asdf987-'
109 self.series1_prefix = 'qwerty-'
110 self.product = self.factory.makeProduct(
111 self.product_prefix + 'product1')
112 self.series = self.factory.makeProductSeries(
113 product=self.product, name=self.series1_prefix + "series1")
114
115 def tearDown(self):
116 super(TestProductSeriesVocabulary, self).tearDown()
117
118 def test_search(self):
119 series2 = self.factory.makeProductSeries(product=self.product)
120 # Search by product name.
121 result = self.vocabulary.search(self.product.name)
122 self.assertEqual(
123 [self.series, series2].sort(key=attrgetter('id')),
124 list(result).sort(key=attrgetter('id')))
125 # Search by series name.
126 result = self.vocabulary.search(self.series.name)
127 self.assertEqual([self.series], list(result))
128 # Search by series2 name.
129 result = self.vocabulary.search(series2.name)
130 self.assertEqual([series2], list(result))
131 # Search by product & series name substrings.
132 result = self.vocabulary.search(
133 '%s/%s' % (self.product_prefix, self.series1_prefix))
134 self.assertEqual([self.series], list(result))
135
136 def test_toTerm(self):
137 term = self.vocabulary.toTerm(self.series)
138 self.assertEqual(
139 '%s/%s' % (self.product.name, self.series.name),
140 term.token)
141 self.assertEqual(self.series, term.value)
142
143 def test_getTermByToken(self):
144 token = '%s/%s' % (self.product.name, self.series.name)
145 term = self.vocabulary.getTermByToken(token)
146 self.assertEqual(token, term.token)
147 self.assertEqual(self.series, term.value)
148
149 def test_getTermByToken_LookupError(self):
150 self.assertRaises(
151 LookupError,
152 self.vocabulary.getTermByToken, 'does/notexist')
153
154
155def test_suite():
156 return TestLoader().loadTestsFromName(__name__)
157>>>>>>> MERGE-SOURCE
83158
=== modified file 'lib/lp/soyuz/doc/gina.txt'
--- lib/lp/soyuz/doc/gina.txt 2009-09-09 21:41:04 +0000
+++ lib/lp/soyuz/doc/gina.txt 2009-12-14 08:43:38 +0000
@@ -187,7 +187,7 @@
187 16187 16
188 >>> count = SourcePackageRelease.select().count()188 >>> count = SourcePackageRelease.select().count()
189 >>> count - orig_spr_count189 >>> count - orig_spr_count
190 16190 17
191191
192Check that x11proto-damage has its Build-Depends-Indep value correctly set:192Check that x11proto-damage has its Build-Depends-Indep value correctly set:
193193
@@ -317,13 +317,13 @@
317 util-linux (again, poor thing)317 util-linux (again, poor thing)
318318
319 >>> print SSPPH.select().count() - orig_sspph_count319 >>> print SSPPH.select().count() - orig_sspph_count
320 19320 20
321321
322 >>> print SSPPH.selectBy(322 >>> print SSPPH.selectBy(
323 ... componentID=1,323 ... componentID=1,
324 ... pocket=PackagePublishingPocket.RELEASE).count() - \324 ... pocket=PackagePublishingPocket.RELEASE).count() - \
325 ... orig_sspph_main_count325 ... orig_sspph_main_count
326 19326 20
327327
328=== Testing Binary Package Results ===328=== Testing Binary Package Results ===
329329
@@ -441,11 +441,11 @@
441 >>> print p.name441 >>> print p.name
442 cjwatson442 cjwatson
443 >>> print Person.select().count() - orig_person_count443 >>> print Person.select().count() - orig_person_count
444 12444 13
445 >>> print TeamParticipation.select().count() - orig_tp_count445 >>> print TeamParticipation.select().count() - orig_tp_count
446 12446 13
447 >>> print EmailAddress.select().count() - orig_email_count447 >>> print EmailAddress.select().count() - orig_email_count
448 12448 13
449449
450450
451=== Re-run Gina ===451=== Re-run Gina ===
@@ -515,13 +515,13 @@
515changed, etc.515changed, etc.
516516
517 >>> SourcePackageRelease.select().count() - orig_spr_count517 >>> SourcePackageRelease.select().count() - orig_spr_count
518 16518 17
519 >>> print Person.select().count() - orig_person_count519 >>> print Person.select().count() - orig_person_count
520 12520 13
521 >>> print TeamParticipation.select().count() - orig_tp_count521 >>> print TeamParticipation.select().count() - orig_tp_count
522 12522 13
523 >>> print EmailAddress.select().count() - orig_email_count523 >>> print EmailAddress.select().count() - orig_email_count
524 12524 13
525 >>> BinaryPackageRelease.select().count() - orig_bpr_count525 >>> BinaryPackageRelease.select().count() - orig_bpr_count
526 40526 40
527 >>> Build.select().count() - orig_build_count527 >>> Build.select().count() - orig_build_count
@@ -532,7 +532,7 @@
532 >>> SBPPH.select().count() - orig_sbpph_count532 >>> SBPPH.select().count() - orig_sbpph_count
533 47533 47
534 >>> print SSPPH.select().count() - orig_sspph_count534 >>> print SSPPH.select().count() - orig_sspph_count
535 21535 22
536536
537Check that the overrides we did were correctly issued. We can't use selectOneBy537Check that the overrides we did were correctly issued. We can't use selectOneBy
538because, of course, there may be multiple rows published for that package --538because, of course, there may be multiple rows published for that package --
539539
=== modified file 'lib/lp/soyuz/scripts/gina/handlers.py'
--- lib/lp/soyuz/scripts/gina/handlers.py 2009-11-10 01:00:23 +0000
+++ lib/lp/soyuz/scripts/gina/handlers.py 2009-12-14 08:43:39 +0000
@@ -29,6 +29,8 @@
2929
30from lp.archivepublisher.diskpool import poolify30from lp.archivepublisher.diskpool import poolify
31from lp.archiveuploader.tagfiles import parse_tagfile31from lp.archiveuploader.tagfiles import parse_tagfile
32from lp.archiveuploader.utils import (determine_binary_file_type,
33 determine_source_file_type)
3234
33from canonical.database.sqlbase import sqlvalues35from canonical.database.sqlbase import sqlvalues
3436
@@ -47,9 +49,9 @@
47from lp.registry.interfaces.person import IPersonSet, PersonCreationRationale49from lp.registry.interfaces.person import IPersonSet, PersonCreationRationale
48from lp.registry.interfaces.sourcepackage import SourcePackageType50from lp.registry.interfaces.sourcepackage import SourcePackageType
49from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet51from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet
52from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFormat
50from lp.soyuz.interfaces.build import BuildStatus53from lp.soyuz.interfaces.build import BuildStatus
51from lp.soyuz.interfaces.publishing import PackagePublishingStatus54from lp.soyuz.interfaces.publishing import PackagePublishingStatus
52from canonical.launchpad.helpers import getFileType, getBinaryPackageFormat
5355
5456
55def check_not_in_librarian(files, archive_root, directory):57def check_not_in_librarian(files, archive_root, directory):
@@ -78,6 +80,39 @@
78 return to_upload80 return to_upload
7981
8082
83BINARYPACKAGE_EXTENSIONS = {
84 BinaryPackageFormat.DEB: '.deb',
85 BinaryPackageFormat.UDEB: '.udeb',
86 BinaryPackageFormat.RPM: '.rpm'}
87
88
89class UnrecognizedBinaryFormat(Exception):
90
91 def __init__(self, fname, *args):
92 Exception.__init__(self, *args)
93 self.fname = fname
94
95 def __str__(self):
96 return '%s is not recognized as a binary file.' % self.fname
97
98
99def getBinaryPackageFormat(fname):
100 """Return the BinaryPackageFormat for the given filename.
101
102 >>> getBinaryPackageFormat('mozilla-firefox_0.9_i386.deb').name
103 'DEB'
104 >>> getBinaryPackageFormat('debian-installer.9_all.udeb').name
105 'UDEB'
106 >>> getBinaryPackageFormat('network-manager.9_i386.rpm').name
107 'RPM'
108 """
109 for key, value in BINARYPACKAGE_EXTENSIONS.items():
110 if fname.endswith(value):
111 return key
112
113 raise UnrecognizedBinaryFormat(fname)
114
115
81class DataSetupError(Exception):116class DataSetupError(Exception):
82 """Raised when required data is found to be missing in the database"""117 """Raised when required data is found to be missing in the database"""
83118
@@ -613,9 +648,10 @@
613 # SourcePackageReleaseFile entry on lp db.648 # SourcePackageReleaseFile entry on lp db.
614 for fname, path in to_upload:649 for fname, path in to_upload:
615 alias = getLibraryAlias(path, fname)650 alias = getLibraryAlias(path, fname)
616 SourcePackageReleaseFile(sourcepackagerelease=spr.id,651 SourcePackageReleaseFile(
617 libraryfile=alias,652 sourcepackagerelease=spr.id,
618 filetype=getFileType(fname))653 libraryfile=alias,
654 filetype=determine_source_file_type(fname))
619 log.info('Package file %s included into library' % fname)655 log.info('Package file %s included into library' % fname)
620656
621 return spr657 return spr
@@ -805,9 +841,10 @@
805 (bin_name.name, bin.version))841 (bin_name.name, bin.version))
806842
807 alias = getLibraryAlias(path, fname)843 alias = getLibraryAlias(path, fname)
808 BinaryPackageFile(binarypackagerelease=binpkg.id,844 BinaryPackageFile(
809 libraryfile=alias,845 binarypackagerelease=binpkg.id,
810 filetype=getFileType(fname))846 libraryfile=alias,
847 filetype=determine_binary_file_type(fname))
811 log.info('Package file %s included into library' % fname)848 log.info('Package file %s included into library' % fname)
812849
813 # Return the binarypackage object.850 # Return the binarypackage object.
814851
=== modified file 'lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources'
--- lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources 2005-11-05 18:06:43 +0000
+++ lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources 2009-12-14 08:43:39 +0000
@@ -139,6 +139,23 @@
139 7947f81d47c3ccdadf05d5e4a120b098 41936 x11proto-damage_6.8.99.7.orig.tar.gz139 7947f81d47c3ccdadf05d5e4a120b098 41936 x11proto-damage_6.8.99.7.orig.tar.gz
140 a4e78902d591d87ab864cedc1e83278d 2201 x11proto-damage_6.8.99.7-2.diff.gz140 a4e78902d591d87ab864cedc1e83278d 2201 x11proto-damage_6.8.99.7-2.diff.gz
141141
142Package: bar
143Binary: bar
144Version: 1.0-1
145Priority: optional
146Section: misc
147Maintainer: Launchpad team <launchpad@lists.canonical.com>
148Architecture: all
149Standards-Version: 3.6.2
150Format: 3.0 (quilt)
151Directory: pool/main/b/bar
152Files:
153 662977e76501561c902c34bf94a2054d 1115 bar_1.0-1.dsc
154 eed105761436486f7eebaa8c017bc59a 178 bar_1.0.orig-comp1.tar.gz
155 0a30b50fa846e75261808ee1f41d1cbe 156 bar_1.0.orig-comp2.tar.bz2
156 fc1464e5985b962a042d5354452f361d 164 bar_1.0.orig.tar.gz
157 e68110184d4d9e7afd81520cc490b396 737 bar_1.0-1.debian.tar.bz2
158
142Bogus, bogus, bogus159Bogus, bogus, bogus
143ain't invalid stanza's neat?160ain't invalid stanza's neat?
144161
145162
=== modified file 'lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources.gz'
146Binary files lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources.gz 2005-11-11 11:35:22 +0000 and lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources.gz 2009-12-14 08:43:38 +0000 differ163Binary files lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources.gz 2005-11-11 11:35:22 +0000 and lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources.gz 2009-12-14 08:43:38 +0000 differ
=== added directory 'lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar'
=== added file 'lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.debian.tar.bz2'
147Binary files lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.debian.tar.bz2 1970-01-01 00:00:00 +0000 and lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.debian.tar.bz2 2009-12-14 08:43:39 +0000 differ164Binary files lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.debian.tar.bz2 1970-01-01 00:00:00 +0000 and lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.debian.tar.bz2 2009-12-14 08:43:39 +0000 differ
=== added file 'lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.dsc'
--- lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.dsc 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.dsc 2009-12-14 08:43:38 +0000
@@ -0,0 +1,22 @@
1Format: 3.0 (quilt)
2Source: bar
3Binary: bar
4Architecture: any
5Version: 1.0-1
6Maintainer: Launchpad team <launchpad@lists.canonical.com>
7Standards-Version: 3.6.2
8Checksums-Sha1:
9 e7a106587e0f11220e27ccc5db7527cae05e4054 178 bar_1.0.orig-comp1.tar.gz
10 a645cc0856469f549f11bb9bdc4ca6dda4eedd4b 156 bar_1.0.orig-comp2.tar.bz2
11 73a04163fee97fd2257ab266bd48f1d3d528e012 164 bar_1.0.orig.tar.gz
12 727de2395d3a3e35c375159db01072935fea02f8 737 bar_1.0-1.debian.tar.bz2
13Checksums-Sha256:
14 aff0bbc01c5883ac76e89cba7a9b6cd79b521183f0de86c93e93ab8804f5a256 178 bar_1.0.orig-comp1.tar.gz
15 955131b3a5bd881c008ff822c0f9b7544350647fe1f355f65a138e0e5e5e8d71 156 bar_1.0.orig-comp2.tar.bz2
16 f1ecff929899b567f45d6734b69d59a4f3c04dabce3cc8e6ed6d64073eda360e 164 bar_1.0.orig.tar.gz
17 3d2137f9f80f4b6c80f73acb8b3ac7986c962c4268d8948f54823d27148a6116 737 bar_1.0-1.debian.tar.bz2
18Files:
19 eed105761436486f7eebaa8c017bc59a 178 bar_1.0.orig-comp1.tar.gz
20 0a30b50fa846e75261808ee1f41d1cbe 156 bar_1.0.orig-comp2.tar.bz2
21 fc1464e5985b962a042d5354452f361d 164 bar_1.0.orig.tar.gz
22 e68110184d4d9e7afd81520cc490b396 737 bar_1.0-1.debian.tar.bz2
023
=== added file 'lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp1.tar.gz'
1Binary files lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp1.tar.gz 1970-01-01 00:00:00 +0000 and lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp1.tar.gz 2009-12-14 08:43:39 +0000 differ24Binary files lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp1.tar.gz 1970-01-01 00:00:00 +0000 and lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp1.tar.gz 2009-12-14 08:43:39 +0000 differ
=== added file 'lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp2.tar.bz2'
2Binary files lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp2.tar.bz2 1970-01-01 00:00:00 +0000 and lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp2.tar.bz2 2009-12-14 08:43:39 +0000 differ25Binary files lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp2.tar.bz2 1970-01-01 00:00:00 +0000 and lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp2.tar.bz2 2009-12-14 08:43:39 +0000 differ
=== added file 'lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig.tar.gz'
3Binary files lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig.tar.gz 1970-01-01 00:00:00 +0000 and lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig.tar.gz 2009-12-14 08:43:38 +0000 differ26Binary files lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig.tar.gz 1970-01-01 00:00:00 +0000 and lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig.tar.gz 2009-12-14 08:43:38 +0000 differ
=== added file 'lib/lp/soyuz/scripts/tests/test_gina.py'
--- lib/lp/soyuz/scripts/tests/test_gina.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/scripts/tests/test_gina.py 2009-12-14 08:43:39 +0000
@@ -0,0 +1,15 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4import unittest
5
6from zope.testing.doctest import DocTestSuite
7
8import lp.soyuz.scripts.gina.handlers
9
10
11def test_suite():
12 suite = unittest.TestSuite()
13 suite.addTest(DocTestSuite(lp.soyuz.scripts.gina.handlers))
14 return suite
15
016
=== modified file 'lib/lp/translations/stories/distroseries/xx-distroseries-templates.txt'
--- lib/lp/translations/stories/distroseries/xx-distroseries-templates.txt 2009-12-10 12:46:11 +0000
+++ lib/lp/translations/stories/distroseries/xx-distroseries-templates.txt 2009-12-14 08:43:39 +0000
@@ -1,5 +1,12 @@
1Templates view for DistroSeries1<<<<<<< TREE
2===============================2Templates view for DistroSeries
3===============================
4=======
5
6
7Templates view for DistroSeries
8===============================
9>>>>>>> MERGE-SOURCE
310
4The +templates view for DistroSeries gives an overview of the translation11The +templates view for DistroSeries gives an overview of the translation
5templates in this series and provides easy access to the various subpages of12templates in this series and provides easy access to the various subpages of
@@ -18,6 +25,7 @@
18 >>> print user_browser.url25 >>> print user_browser.url
19 http://translations.launchpad.dev/ubuntu/hoary/+templates26 http://translations.launchpad.dev/ubuntu/hoary/+templates
2027
28<<<<<<< TREE
2129
22The templates table30The templates table
23-------------------31-------------------
@@ -58,6 +66,47 @@
58 ...66 ...
59 mozilla pkgconf-mozilla 2005-05-06 Download67 mozilla pkgconf-mozilla 2005-05-06 Download
60 ...68 ...
69=======
70The templates table
71-------------------
72
73Full template listing for a distribution series is reached by following
74a link from the distribution series translations page.
75
76 >>> anon_browser.open(
77 ... 'http://translations.launchpad.dev/ubuntu/hoary')
78 >>> anon_browser.getLink('full list of templates').click()
79
80Full listing of templates shows source package name, template name and
81the date of last update for this distribution series.
82
83 >>> table = find_tag_by_id(anon_browser.contents, 'templates_table')
84 >>> print extract_text(table)
85 Source package Template name Last update
86 evolution disabled-template 2007-01-05
87 evolution evolution-2.2 2005-05-06
88 evolution man 2006-08-14
89 mozilla pkgconf-mozilla 2005-05-06
90 pmount man 2006-08-14
91 pmount pmount 2005-05-06
92
93
94Logged-in users will see a link from distro series
95 >>> user_browser.open(
96 ... 'http://translations.launchpad.dev/ubuntu/hoary')
97 >>> user_browser.getLink('full list of templates').click()
98
99Logged-in users can also choose to download all translations for each
100of the templates.
101
102 >>> table = find_tag_by_id(user_browser.contents, 'templates_table')
103 >>> print extract_text(table)
104 Source package Template name Last update Actions
105 evolution disabled-template 2007-01-05 Download
106 ...
107 mozilla pkgconf-mozilla 2005-05-06 Download
108 ...
109>>>>>>> MERGE-SOURCE
61110
62Administrator can see all editing options.111Administrator can see all editing options.
63112
@@ -69,6 +118,7 @@
69118
70 >>> table = find_tag_by_id(admin_browser.contents, 'templates_table')119 >>> table = find_tag_by_id(admin_browser.contents, 'templates_table')
71 >>> print extract_text(table)120 >>> print extract_text(table)
121<<<<<<< TREE
72 Source package Template name Last update Actions122 Source package Template name Last update Actions
73 evolution disabled-template (inactive) 2007-01-05 Edit Upload Download Administer123 evolution disabled-template (inactive) 2007-01-05 Edit Upload Download Administer
74 evolution evolution-2.2 2005-05-06 Edit Upload Download Administer124 evolution evolution-2.2 2005-05-06 Edit Upload Download Administer
@@ -137,6 +187,19 @@
137187
138Links to the templates188Links to the templates
139----------------------189----------------------
190=======
191 Source package Template name Last update Actions
192 evolution disabled-template 2007-01-05 Edit Upload Download Administer
193 evolution evolution-2.2 2005-05-06 Edit Upload Download Administer
194 evolution man 2006-08-14 Edit Upload Download Administer
195 mozilla pkgconf-mozilla 2005-05-06 Edit Upload Download Administer
196 pmount man 2006-08-14 Edit Upload Download Administer
197 pmount pmount 2005-05-06 Edit Upload Download Administer
198
199
200Links to the templates
201----------------------
202>>>>>>> MERGE-SOURCE
140203
141Clicking on a template name will take the user to that template's overview204Clicking on a template name will take the user to that template's overview
142page.205page.
143206
=== modified file 'lib/lp/translations/templates/object-templates.pt'
--- lib/lp/translations/templates/object-templates.pt 2009-12-10 12:46:11 +0000
+++ lib/lp/translations/templates/object-templates.pt 2009-12-14 08:43:37 +0000
@@ -82,6 +82,7 @@
82 </tr>82 </tr>
83 </thead>83 </thead>
84 <tbody>84 <tbody>
85<<<<<<< TREE
85 <tal:templates repeat="template view/iter_templates">86 <tal:templates repeat="template view/iter_templates">
86 <tal:not-current condition="not: template/iscurrent">87 <tal:not-current condition="not: template/iscurrent">
87 <tal:admin condition="template/required:launchpad.TranslationsAdmin">88 <tal:admin condition="template/required:launchpad.TranslationsAdmin">
@@ -133,6 +134,49 @@
133 </div>134 </div>
134 </td>135 </td>
135 </tr>136 </tr>
137=======
138 <tr tal:repeat="template view/iter_templates" class="template_row">
139 <td tal:condition="view/is_distroseries"
140 tal:content="template/sourcepackagename/name"
141 class="sourcepackage_column">Source package
142 </td>
143 <td class="template_column"><a tal:attributes="href template/fmt:url"
144 tal:content="template/name">Template name</a></td>
145 <td class="lastupdate_column">
146 <span class="sortkey"
147 tal:condition="template/date_last_updated"
148 tal:content="template/date_last_updated/fmt:datetime">
149 time sort key
150 </span>
151 <span class="lastupdate_column"
152 tal:condition="template/date_last_updated"
153 tal:attributes="
154 title template/date_last_updated/fmt:datetime"
155 tal:content="
156 template/date_last_updated/fmt:approximatedate"
157 >
158 2009-09-23
159 </span>
160 </td>
161 <td class="actions_column"
162 tal:condition="context/required:launchpad.AnyPerson">
163 <div class="template_links">
164 <tal:maintainer condition="template/required:launchpad.Edit">
165 <a tal:attributes="href string:${template/fmt:url}/+edit;
166 title string:Edit ${template/name}'s details">
167 <img src="/@@/edit" />&nbsp;Edit</a>
168 <a tal:attributes="href string:${template/fmt:url}/+upload;
169 title string:Upload translations to ${template/name}">
170 <img src="/@@/add" />&nbsp;Upload</a>
171 </tal:maintainer>
172 <a tal:attributes="href string:${template/fmt:url}/+export;
173 title string:Download translations from ${template/name}">
174 <img src="/@@/download" />&nbsp;Download</a>
175 <tal:admin condition="template/required:launchpad.Admin">
176 <a tal:attributes="href string:${template/fmt:url}/+admin;
177 title string:Administer ${template/name}">
178 <img src="/@@/edit" />&nbsp;Administer</a>
179>>>>>>> MERGE-SOURCE
136 </tal:admin>180 </tal:admin>
137 </tal:not-current>181 </tal:not-current>
138 <tal:current condition="template/iscurrent">182 <tal:current condition="template/iscurrent">

Subscribers

People subscribed via source and target branches

to status/vote changes: