launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #05901
[Merge] lp:~jelmer/launchpad/recipe-format-0.4 into lp:launchpad/db-devel
Jelmer Vernooij has proposed merging lp:~jelmer/launchpad/recipe-format-0.4 into lp:launchpad/db-devel with lp:~jelmer/launchpad/optional-deb-version as a prerequisite.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #891928 in Launchpad itself: "Unrecognized 0.4 version of recipe"
https://bugs.launchpad.net/launchpad/+bug/891928
For more details, see:
https://code.launchpad.net/~jelmer/launchpad/recipe-format-0.4/+merge/85245
Add support for version 0.4 of the recipe format.
This format makes the "deb-version" field optional.
--
https://code.launchpad.net/~jelmer/launchpad/recipe-format-0.4/+merge/85245
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jelmer/launchpad/recipe-format-0.4 into lp:launchpad/db-devel.
=== modified file 'buildout-templates/bin/combine-css.in'
--- buildout-templates/bin/combine-css.in 2011-12-01 20:55:49 +0000
+++ buildout-templates/bin/combine-css.in 2011-12-11 01:49:34 +0000
@@ -24,6 +24,7 @@
# including lots of styles that we don't need/want, so keeping this hard-coded
# list seems like the best option for now.
names = [
+ 'ubuntu-webfonts.css',
'style.css',
'yui/cssreset/reset.css',
# Use the old cssgrids instead of the new cssgrids.
=== modified file 'cronscripts/check-teamparticipation.py'
--- cronscripts/check-teamparticipation.py 2011-11-04 21:48:55 +0000
+++ cronscripts/check-teamparticipation.py 2011-12-11 01:49:34 +0000
@@ -23,8 +23,8 @@
from lp.registry.scripts.teamparticipation import (
check_teamparticipation_circular,
check_teamparticipation_consistency,
- check_teamparticipation_self,
fetch_team_participation_info,
+ fix_teamparticipation_consistency,
)
from lp.services.scripts.base import LaunchpadScript
from lp.services.utils import (
@@ -53,14 +53,14 @@
if self.options.load_info:
participation_info = load_bz2_pickle(self.options.load_info)
else:
- check_teamparticipation_self(self.logger)
check_teamparticipation_circular(self.logger)
participation_info = fetch_team_participation_info(self.logger)
if self.options.save_info:
save_bz2_pickle(participation_info, self.options.save_info)
else:
- check_teamparticipation_consistency(
+ errors = check_teamparticipation_consistency(
self.logger, participation_info)
+ fix_teamparticipation_consistency(self.logger, errors)
if __name__ == '__main__':
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg 2011-12-09 07:56:42 +0000
+++ database/schema/security.cfg 2011-12-11 01:49:34 +0000
@@ -446,6 +446,8 @@
public.job = SELECT, UPDATE, DELETE
public.pofilestatsjob = SELECT, UPDATE, DELETE
public.potmsgset = SELECT
+public.product = SELECT
+public.productseries = SELECT
public.distroseries = SELECT
public.distribution = SELECT
public.sourcepackagename = SELECT
=== modified file 'lib/canonical/launchpad/browser/logintoken.py'
--- lib/canonical/launchpad/browser/logintoken.py 2011-08-23 13:43:28 +0000
+++ lib/canonical/launchpad/browser/logintoken.py 2011-12-11 01:49:34 +0000
@@ -35,13 +35,6 @@
EmailAddressStatus,
IEmailAddressSet,
)
-from canonical.launchpad.interfaces.gpghandler import (
- GPGKeyExpired,
- GPGKeyNotFoundError,
- GPGKeyRevoked,
- GPGVerificationError,
- IGPGHandler,
- )
from canonical.launchpad.interfaces.logintoken import (
IGPGKeyValidationForm,
ILoginTokenSet,
@@ -70,6 +63,13 @@
IPersonSet,
ITeam,
)
+from lp.services.gpg.interfaces import (
+ GPGKeyExpired,
+ GPGKeyNotFoundError,
+ GPGKeyRevoked,
+ GPGVerificationError,
+ IGPGHandler,
+ )
class LoginTokenSetNavigation(GetitemNavigation):
=== modified file 'lib/canonical/launchpad/database/logintoken.py'
--- lib/canonical/launchpad/database/logintoken.py 2011-08-12 14:39:51 +0000
+++ lib/canonical/launchpad/database/logintoken.py 2011-12-11 01:49:34 +0000
@@ -36,7 +36,6 @@
from canonical.launchpad.helpers import get_email_template
from canonical.launchpad.interfaces.authtoken import LoginTokenType
from canonical.launchpad.interfaces.emailaddress import IEmailAddressSet
-from canonical.launchpad.interfaces.gpghandler import IGPGHandler
from canonical.launchpad.interfaces.logintoken import (
ILoginToken,
ILoginTokenSet,
@@ -52,6 +51,7 @@
from lp.app.validators.email import valid_email
from lp.registry.interfaces.gpg import IGPGKeySet
from lp.registry.interfaces.person import IPersonSet
+from lp.services.gpg.interfaces import IGPGHandler
from lp.services.mail.sendmail import (
format_address,
simple_sendmail,
=== removed file 'lib/canonical/launchpad/database/stormsugar.py'
--- lib/canonical/launchpad/database/stormsugar.py 2010-11-05 09:23:08 +0000
+++ lib/canonical/launchpad/database/stormsugar.py 1970-01-01 00:00:00 +0000
@@ -1,135 +0,0 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-# pylint: disable-msg=C0203
-
-"""Storm is powerful stuff. This helps it go down more easily.
-
-This module is experimental. Please give feedback and feel free to adjust it.
-"""
-
-__metaclass__ = type
-
-
-__all__ = [
- 'ForeignKey',
- 'ObjectNotFound',
- 'Sugar',
- 'UnknownProperty',
- ]
-
-
-from storm.locals import (
- Int,
- Reference,
- Store,
- Storm,
- )
-from zope.component import getUtility
-
-from canonical.launchpad.webapp.interfaces import (
- DEFAULT_FLAVOR,
- IStoreSelector,
- MAIN_STORE,
- MASTER_FLAVOR,
- )
-from lp.app.errors import NotFoundError
-
-
-class ObjectNotFound(NotFoundError):
- """Exception raised when a storm object can't be got."""
-
- def __init__(self, orm_class, id):
- msg = 'Not found: %s with id %s.' % (orm_class.__name__, id)
- NotFoundError.__init__(self, msg)
-
-
-class UnknownProperty(Exception):
- """The property name specified in a kwarg is not pre-defined."""
-
- def __init__(self, orm_class, name):
- msg = 'Class %s has no property "%s".' % (orm_class.__name__, name)
- Exception.__init__(self, msg)
-
-
-class ForeignKey(Reference):
-
- def __init__(self, remote_key, name=None):
- self.name = name
- Reference.__init__(self, None, remote_key)
-
-
-# Use Storm.__metaclass__ because storm.properties.PropertyPublisherMeta isn't
-# in an __all__.
-
-class Sugary(Storm.__metaclass__):
- """Metaclass that adds support for ForeignKey."""
-
- def __init__(cls, name, bases, dict):
- for key in dir(cls):
- val = getattr(cls, key, None)
- if not isinstance(val, ForeignKey):
- continue
- col_name = val.name
- if col_name is None:
- col_name = key
- val._local_key = Int(col_name)
- setattr(cls, '_%s_id' % key, val._local_key)
- # Do this last, because it wants References to have their local_key
- # properly set up.
- super(Sugary, cls).__init__(name, bases, dict)
-
-
-class Sugar(Storm):
- """Base class providing convenient Storm API."""
-
- __metaclass__ = Sugary
-
- __store_type__ = MAIN_STORE
-
- id = Int(primary=True)
-
- def __init__(self, **kwargs):
- super(Sugar).__init__(Sugar, self)
- for key, value in kwargs.items():
- if getattr(self.__class__, key, None) is None:
- raise UnknownProperty(self.__class__, key)
- setattr(self, key, value)
- self.master_store.add(self)
-
- @property
- def master_store(self):
- selector = getUtility(IStoreSelector)
- return selector.get(self.__store_type__, MASTER_FLAVOR)
-
- @classmethod
- def getDefaultStore(klass):
- """Return the default store for this class."""
- selector = getUtility(IStoreSelector)
- return selector.get(klass.__store_type__, DEFAULT_FLAVOR)
-
- @classmethod
- def getById(klass, id):
- """Return the object of this type with given id."""
- store = klass.getDefaultStore()
- obj = store.get(klass, id)
- if obj is None:
- raise ObjectNotFound(klass, id)
- return obj
-
- @classmethod
- def find(klass, **kwargs):
- """Select the instances whose properties match kwargs."""
- assert len(kwargs) > 0
- store = klass.getDefaultStore()
- return store.find(klass, **kwargs)
-
- def flush(self):
- """Bi-directionally update this object with the database."""
- store = Store.of(self)
- store.flush()
- store.autoreload(self)
-
- def remove(self):
- """Remove this object from the database."""
- Store.of(self).remove(self)
=== removed file 'lib/canonical/launchpad/database/tests/test_stormsugar.py'
--- lib/canonical/launchpad/database/tests/test_stormsugar.py 2011-08-12 11:37:08 +0000
+++ lib/canonical/launchpad/database/tests/test_stormsugar.py 1970-01-01 00:00:00 +0000
@@ -1,125 +0,0 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Tests for stormsugar."""
-
-__metaclass__ = type
-
-from psycopg2 import IntegrityError
-from storm.locals import (
- Int,
- Store,
- )
-
-from canonical.launchpad.database.stormsugar import (
- ForeignKey,
- ObjectNotFound,
- Sugar,
- UnknownProperty,
- )
-from canonical.testing.layers import DatabaseFunctionalLayer
-from lp.testing import TestCase
-
-
-class SugarDerived(Sugar):
- """Class for testing. (Because we can't test Sugar directly.)"""
-
- __storm_table__ = 'Job'
-
- status = Int()
-
- progress = Int()
-
-
-class TestSugar(TestCase):
-
- layer = DatabaseFunctionalLayer
-
- def test_init_adds(self):
- """Default constructor adds to store."""
- created = SugarDerived(id=500, status=0)
- self.assertIs(created.master_store, Store.of(created))
-
- def test_init_handles_kwargs(self):
- """Default constructor handles kwargs."""
- created = SugarDerived(id=500, status=0)
- self.assertEqual(500, created.id)
- self.assertEqual(0, created.status)
-
- def test_init_requires_known_kwargs(self):
- """Default constructor requires pre-defined kwargs.
-
- (This reduces the potential for typos to cause confusion or bugs.)
- """
- e = self.assertRaises(
- UnknownProperty, SugarDerived, id=500, status=0, foo='bar')
- self.assertEqual('Class SugarDerived has no property "foo".', str(e))
-
- def test_getById(self):
- """Get either returns the desired object or raises."""
- e = self.assertRaises(ObjectNotFound, SugarDerived.getById, 500)
- self.assertEqual("'Not found: SugarDerived with id 500.'", str(e))
- created = SugarDerived(id=500, status=0)
- gotten = SugarDerived.getById(500)
- self.assertEqual(created, gotten)
-
- def test_remove(self):
- """destroySelf destroys the object in question."""
- created = SugarDerived(id=500, status=0)
- created.remove()
- self.assertRaises(ObjectNotFound, SugarDerived.getById, 500)
-
- def test_flush_exercises_constraints(self):
- """Sugar.flush causes constraints to be tested."""
- created = SugarDerived(id=500)
- # The IntegrityError is raised because status is not set, and it
- # has the NOT NULL constraint.
- self.assertRaises(IntegrityError, created.flush)
-
- def test_find(self):
- """Sugar.find works."""
- obj1 = SugarDerived(id=500, status=5)
- obj2 = SugarDerived(id=501, status=6)
- self.assertEqual([obj1], list(SugarDerived.find(status=5)))
- self.assertEqual([], list(SugarDerived.find(status=4)))
- self.assertRaises(AssertionError, SugarDerived.find)
-
- def test_find_with_multiple_clauses(self):
- """Multiple kwargs are ANDed."""
- obj1 = SugarDerived(status=5, progress=1)
- obj2 = SugarDerived(status=5, progress=2)
- obj3 = SugarDerived(status=6, progress=2)
- self.assertEqual(
- [obj1], list(SugarDerived.find(status=5, progress=1)))
- self.assertEqual(
- [obj2], list(SugarDerived.find(status=5, progress=2)))
- self.assertEqual(
- [obj3], list(SugarDerived.find(status=6, progress=2)))
-
- def test_ForeignKey(self):
- """ForeignKey works, and defaults to property name."""
-
- class ReferencingObject(Sugar):
-
- __storm_table__ = 'BranchJob'
-
- job = ForeignKey(SugarDerived.id)
-
- obj1 = SugarDerived(status=0)
- obj2 = ReferencingObject(job=obj1)
- self.assertEqual(obj1, obj2.job)
- self.assertEqual(obj1.id, obj2._job_id)
-
- def test_ForeignKey_with_name(self):
- """ForeignKey name correctly overrides property name."""
-
- class ReferencingObjectWithName(Sugar):
-
- __storm_table__ = 'BranchJob'
-
- foo = ForeignKey(SugarDerived.id, 'job')
-
- obj1 = SugarDerived(status=0)
- obj2 = ReferencingObjectWithName(foo=obj1)
- self.assertEqual(obj1, obj2.foo)
- self.assertEqual(obj1.id, obj2._foo_id)
=== modified file 'lib/canonical/launchpad/ftests/__init__.py'
--- lib/canonical/launchpad/ftests/__init__.py 2011-10-05 21:46:17 +0000
+++ lib/canonical/launchpad/ftests/__init__.py 2011-12-11 01:49:34 +0000
@@ -5,10 +5,6 @@
__all__ = [
'ANONYMOUS',
- 'decrypt_content',
- 'import_public_key',
- 'import_public_test_keys',
- 'import_secret_test_key',
'LaunchpadFormHarness',
'login',
'login_person',
@@ -18,15 +14,11 @@
from canonical.launchpad.ftests._launchpadformharness import (
LaunchpadFormHarness,
)
-from canonical.launchpad.ftests.keys_for_tests import (
- decrypt_content,
- import_public_key,
- import_public_test_keys,
- import_secret_test_key,
- )
from lp.testing import (
ANONYMOUS,
login,
login_person,
logout,
)
+
+
=== modified file 'lib/canonical/launchpad/icing/css/base.css'
--- lib/canonical/launchpad/icing/css/base.css 2011-11-18 05:32:00 +0000
+++ lib/canonical/launchpad/icing/css/base.css 2011-12-11 01:49:34 +0000
@@ -1,5 +1,5 @@
body {
- font-family: 'UbuntuBeta Regular', Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
+ font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
font-size: 12px;
line-height: 18px; /* The same as the sprite height. */
color: #333;
=== modified file 'lib/canonical/launchpad/icing/css/forms.css'
--- lib/canonical/launchpad/icing/css/forms.css 2011-11-18 05:15:52 +0000
+++ lib/canonical/launchpad/icing/css/forms.css 2011-12-11 01:49:34 +0000
@@ -232,7 +232,7 @@
}
.lazr-multiline-edit .clearfix h3 {
/* Undo the damage done by lazr. */
- font-family: 'UbuntuBeta Regular', Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
+ font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
line-height: 12px;
}
.widget-hd.js-action {
=== modified file 'lib/canonical/launchpad/icing/css/modifiers.css'
--- lib/canonical/launchpad/icing/css/modifiers.css 2011-11-18 05:15:52 +0000
+++ lib/canonical/launchpad/icing/css/modifiers.css 2011-12-11 01:49:34 +0000
@@ -119,7 +119,7 @@
}
pre.changelog, table.diff,
.bug-comment, .bug-activity, .codereviewcomment {
- font-family: 'UbuntuBeta Mono', 'Ubuntu Mono', monospace;
+ font-family: 'Ubuntu Mono', monospace;
}
.cloud-size-smallest {
font-size: 10px;
=== modified file 'lib/canonical/launchpad/icing/css/typography.css'
--- lib/canonical/launchpad/icing/css/typography.css 2011-11-18 05:15:52 +0000
+++ lib/canonical/launchpad/icing/css/typography.css 2011-12-11 01:49:34 +0000
@@ -38,7 +38,7 @@
width: 60em;
}
pre, code, samp, tt, .console {
- font-family: 'UbuntuBeta Mono', 'Ubuntu Mono', monospace;
+ font-family: 'Ubuntu Mono', monospace;
margin-bottom: 0.8em;
}
pre.wrap {
=== modified file 'lib/canonical/launchpad/icing/shipit.css'
--- lib/canonical/launchpad/icing/shipit.css 2010-08-21 13:28:33 +0000
+++ lib/canonical/launchpad/icing/shipit.css 2011-12-11 01:49:34 +0000
@@ -2,7 +2,7 @@
html {
position: relative;
- font-family: UbuntuBeta, Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
+ font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
font-size: 12pt;
color: black;
background-color: #FFFFFF;
@@ -52,7 +52,7 @@
input, select, textarea {
background-color: #fff;
color: #656565;
- font-family: UbuntuBeta, Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
+ font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
margin: 0;
}
input[type="text"], input[type="submit"], input[type="reset"], textarea {
=== modified file 'lib/canonical/launchpad/icing/style.css'
--- lib/canonical/launchpad/icing/style.css 2011-11-30 06:12:55 +0000
+++ lib/canonical/launchpad/icing/style.css 2011-12-11 01:49:34 +0000
@@ -27,7 +27,7 @@
*/
div#edit-description,
div#edit-commit_message {
- font-family: 'UbuntuBeta Mono', 'Ubuntu Mono', monospace;
+ font-family: 'Ubuntu Mono', monospace;
margin: 1em 0;
}
@@ -461,7 +461,7 @@
padding: 5px;
border: solid gray;
border-width: 1px;
- font-family: 'UbuntuBeta Mono', 'Ubuntu Mono', monospace;
+ font-family: 'Ubuntu Mono', monospace;
-moz-border-radius: 5px;
-o-border-radius: 5px;
-webkit-border-radius: 5px;
=== added symlink 'lib/canonical/launchpad/icing/ubuntu-webfonts.css'
=== target is u'../../../lp/contrib/css/ubuntu-webfonts.css'
=== modified file 'lib/canonical/launchpad/icing/yui_2.7.0b/build/calendar/assets/skins/sam/calendar-skin.css'
--- lib/canonical/launchpad/icing/yui_2.7.0b/build/calendar/assets/skins/sam/calendar-skin.css 2010-08-21 13:28:33 +0000
+++ lib/canonical/launchpad/icing/yui_2.7.0b/build/calendar/assets/skins/sam/calendar-skin.css 2011-12-11 01:49:34 +0000
@@ -264,7 +264,7 @@
/* NAVIGATOR BOUNDING BOX */
.yui-skin-sam .yui-calcontainer .yui-cal-nav {
- font-family: UbuntuBeta, Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
+ font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
font-size:93%;
border:1px solid #808080;
left:50%;
=== modified file 'lib/canonical/launchpad/interfaces/_schema_circular_imports.py'
--- lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2011-10-12 14:44:59 +0000
+++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2011-12-11 01:49:34 +0000
@@ -135,7 +135,7 @@
)
from lp.registry.interfaces.person import (
IPerson,
- IPersonPublic,
+ IPersonViewRestricted,
ITeam,
)
from lp.registry.interfaces.pillar import (
@@ -310,10 +310,10 @@
IPreviewDiff['branch_merge_proposal'].schema = IBranchMergeProposal
-patch_reference_property(IPersonPublic, 'archive', IArchive)
-patch_collection_property(IPersonPublic, 'ppas', IArchive)
-patch_entry_return_type(IPersonPublic, 'getPPAByName', IArchive)
-patch_entry_return_type(IPersonPublic, 'createPPA', IArchive)
+patch_reference_property(IPersonViewRestricted, 'archive', IArchive)
+patch_collection_property(IPersonViewRestricted, 'ppas', IArchive)
+patch_entry_return_type(IPersonViewRestricted, 'getPPAByName', IArchive)
+patch_entry_return_type(IPersonViewRestricted, 'createPPA', IArchive)
IHasBuildRecords['getBuildRecords'].queryTaggedValue(
LAZR_WEBSERVICE_EXPORTED)[
=== modified file 'lib/canonical/launchpad/offline-maintenance-haproxy.html'
--- lib/canonical/launchpad/offline-maintenance-haproxy.html 2010-12-16 22:42:28 +0000
+++ lib/canonical/launchpad/offline-maintenance-haproxy.html 2011-12-11 01:49:34 +0000
@@ -7,7 +7,7 @@
<title>Launchpad is offline for maintenance</title>
<style type="text/css" media="screen, print">
html, body {
- font-family: UbuntuBeta, Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
+ font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
}
.offline {
text-align: center;
=== modified file 'lib/canonical/launchpad/offline-maintenance.html'
--- lib/canonical/launchpad/offline-maintenance.html 2010-12-16 17:04:22 +0000
+++ lib/canonical/launchpad/offline-maintenance.html 2011-12-11 01:49:34 +0000
@@ -4,7 +4,7 @@
<title>Launchpad is offline for maintenance</title>
<style type="text/css" media="screen, print">
html, body {
- font-family: UbuntuBeta, Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
+ font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
}
.offline {
text-align: center;
=== modified file 'lib/canonical/launchpad/offline-staging-code-update.html'
--- lib/canonical/launchpad/offline-staging-code-update.html 2010-12-16 17:04:22 +0000
+++ lib/canonical/launchpad/offline-staging-code-update.html 2011-12-11 01:49:34 +0000
@@ -4,7 +4,7 @@
<title>Please try again</title>
<style type="text/css" media="screen, print">
html, body {
- font-family: UbuntuBeta, Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
+ font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
}
.offline {
text-align: center;
=== modified file 'lib/canonical/launchpad/offline-staging-db-update.html'
--- lib/canonical/launchpad/offline-staging-db-update.html 2010-12-16 17:04:22 +0000
+++ lib/canonical/launchpad/offline-staging-db-update.html 2011-12-11 01:49:34 +0000
@@ -4,7 +4,7 @@
<title>Please try again</title>
<style type="text/css" media="screen, print">
html, body {
- font-family: UbuntuBeta, Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
+ font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
}
.offline {
text-align: center;
=== modified file 'lib/canonical/launchpad/offline-unplanned-haproxy.html'
--- lib/canonical/launchpad/offline-unplanned-haproxy.html 2010-12-16 22:42:28 +0000
+++ lib/canonical/launchpad/offline-unplanned-haproxy.html 2011-12-11 01:49:34 +0000
@@ -7,7 +7,7 @@
<title>Please try again</title>
<style type="text/css" media="screen, print">
html, body {
- font-family: UbuntuBeta, Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
+ font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
}
.offline {
text-align: center;
=== modified file 'lib/canonical/launchpad/offline-unplanned.html'
--- lib/canonical/launchpad/offline-unplanned.html 2010-12-16 17:04:22 +0000
+++ lib/canonical/launchpad/offline-unplanned.html 2011-12-11 01:49:34 +0000
@@ -4,7 +4,7 @@
<title>Please try again</title>
<style type="text/css" media="screen, print">
html, body {
- font-family: UbuntuBeta, Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
+ font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
}
.offline {
text-align: center;
=== modified file 'lib/canonical/launchpad/pagetests/basics/demo-and-lpnet.txt'
--- lib/canonical/launchpad/pagetests/basics/demo-and-lpnet.txt 2011-09-29 10:23:29 +0000
+++ lib/canonical/launchpad/pagetests/basics/demo-and-lpnet.txt 2011-12-11 01:49:34 +0000
@@ -27,15 +27,9 @@
information and styles.
# Set config to pretend we're on a demo site:
- >>> from textwrap import dedent
- >>> site_message = ('This is a demo site mmk. '
- ... '<a href="http://example.com">File a bug</a>.')
- >>> test_data = dedent("""
- ... [launchpad]
- ... is_demo: True
- ... site_message: %s
- ... """ % site_message)
- >>> config.push('test_data', test_data)
+ >>> from lp.testing.fixture import DemoMode
+ >>> demo_mode_fixture = DemoMode()
+ >>> demo_mode_fixture.setUp()
>>> print config.launchpad.is_demo
True
@@ -61,8 +55,7 @@
When you are not on a demo site, the text no longer appears.
- >>> # Restore the previous config:
- >>> config_data = config.pop('test_data')
+ >>> demo_mode_fixture.cleanUp()
>>> print config.launchpad.is_demo
False
=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py 2011-12-08 05:13:31 +0000
+++ lib/canonical/launchpad/security.py 2011-12-11 01:49:34 +0000
@@ -63,6 +63,7 @@
IBranch,
user_has_special_branch_access,
)
+from lp.code.interfaces.branchcollection import IBranchCollection
from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal
from lp.code.interfaces.branchmergequeue import IBranchMergeQueue
from lp.code.interfaces.codeimport import ICodeImport
@@ -800,6 +801,35 @@
if (invitee.is_team and
invitee in user.person.getAdministratedTeams()):
return True
+ return False
+
+
+class PublicOrPrivateTeamsExistence(AuthorizationBase):
+ """Restrict knowing about private teams' existence.
+
+ Knowing the existence of a private team allow traversing to its URL and
+ displaying basic information like name, displayname.
+ """
+ permission = 'launchpad.LimitedView'
+ usedfor = IPersonLimitedView
+
+ def checkUnauthenticated(self):
+ """Unauthenticated users can only view public teams."""
+ if self.obj.visibility == PersonVisibility.PUBLIC:
+ return True
+ return False
+
+ def checkAuthenticated(self, user):
+ """By default, we simply perform a View permission check.
+
+ We also grant limited viewability to users who can see PPAs and
+ branches owned by the team. In other scenarios, the context in which
+ the permission is required is responsible for pre-caching the
+ launchpad.LimitedView permission on each team which requires it.
+ """
+ if self.forwardCheckAuthenticated(
+ user, self.obj, 'launchpad.View'):
+ return True
if (self.obj.is_team
and self.obj.visibility == PersonVisibility.PRIVATE):
@@ -813,33 +843,14 @@
ppa.id for ppa in self.obj.ppas if ppa.private)
if len(subscriber_archive_ids.intersection(team_ppa_ids)) > 0:
return True
- return False
-
-
-class PublicOrPrivateTeamsExistence(AuthorizationBase):
- """Restrict knowing about private teams' existence.
-
- Knowing the existence of a private team allow traversing to its URL and
- displaying basic information like name, displayname.
- """
- permission = 'launchpad.LimitedView'
- usedfor = IPersonLimitedView
-
- def checkUnauthenticated(self):
- """Unauthenticated users can only view public teams."""
- if self.obj.visibility == PersonVisibility.PUBLIC:
- return True
- return False
-
- def checkAuthenticated(self, user):
- """By default, we simply perform a View permission check.
-
- The context in which the permission is required is
- responsible for pre-caching the launchpad.LimitedView permission on
- each team which requires it.
- """
- return self.forwardCheckAuthenticated(
- user, self.obj, 'launchpad.View')
+
+ # Grant visibility to people with subscriptions to branches owned
+ # by the private team.
+ owned_branches = getUtility(IBranchCollection).ownedBy(self.obj)
+ if owned_branches.visibleByUser(user.person).count() > 0:
+ return True
+
+ return False
class EditPollByTeamOwnerOrTeamAdminsOrAdmins(
@@ -2410,13 +2421,13 @@
yield self.obj.archive
-class ViewSourcePackagePublishingHistory(ViewArchive):
+class ViewSourcePackagePublishingHistory(DelegatedAuthorization):
"""Restrict viewing of source publications."""
permission = "launchpad.View"
usedfor = ISourcePackagePublishingHistory
- def __init__(self, obj):
- super(ViewSourcePackagePublishingHistory, self).__init__(obj.archive)
+ def iter_objects(self):
+ yield self.obj.archive
class EditPublishing(DelegatedAuthorization):
=== modified file 'lib/canonical/launchpad/testing/fakepackager.py'
--- lib/canonical/launchpad/testing/fakepackager.py 2011-06-09 10:50:25 +0000
+++ lib/canonical/launchpad/testing/fakepackager.py 2011-12-11 01:49:34 +0000
@@ -20,13 +20,13 @@
from zope.component import getUtility
-from canonical.launchpad.ftests.keys_for_tests import import_secret_test_key
-from canonical.launchpad.interfaces.gpghandler import IGPGHandler
from lp.archiveuploader.nascentupload import NascentUpload
from lp.archiveuploader.uploadpolicy import findPolicyByName
from lp.registry.interfaces.distribution import IDistributionSet
+from lp.services.gpg.interfaces import IGPGHandler
from lp.services.log.logger import BufferLogger
from lp.soyuz.enums import PackageUploadStatus
+from lp.testing.gpgkeys import import_secret_test_key
changelog_entry_template = (
=== modified file 'lib/canonical/launchpad/utilities/__init__.py'
--- lib/canonical/launchpad/utilities/__init__.py 2011-05-27 21:12:25 +0000
+++ lib/canonical/launchpad/utilities/__init__.py 2011-12-11 01:49:34 +0000
@@ -3,7 +3,6 @@
# pylint: disable-msg=W0401
-from canonical.launchpad.utilities.gpghandler import *
from canonical.launchpad.utilities.looptuner import *
from canonical.launchpad.utilities.orderingcheck import *
from lp.app.utilities.celebrities import *
=== removed directory 'lib/canonical/launchpad/utilities/ftests'
=== removed file 'lib/canonical/launchpad/utilities/ftests/__init__.py'
=== modified file 'lib/canonical/launchpad/webapp/authorization.py'
--- lib/canonical/launchpad/webapp/authorization.py 2011-12-07 18:03:40 +0000
+++ lib/canonical/launchpad/webapp/authorization.py 2011-12-11 01:49:34 +0000
@@ -192,7 +192,7 @@
objecttoauthorize, {})
if permission in object_cache:
return object_cache[permission]
- principal = participation.principal
+ principal = removeAllProxies(participation.principal)
if (principal is not None and
not isinstance(principal, UnauthenticatedPrincipal)):
@@ -343,7 +343,8 @@
# LaunchpadBrowserRequest provides a ``clearSecurityPolicyCache``
# method, but it is not in an interface, and not implemented by
# all classes that implement IApplicationRequest.
- del p.annotations[LAUNCHPAD_SECURITY_POLICY_CACHE_KEY]
+ if LAUNCHPAD_SECURITY_POLICY_CACHE_KEY in p.annotations:
+ del p.annotations[LAUNCHPAD_SECURITY_POLICY_CACHE_KEY]
class LaunchpadPermissiveSecurityPolicy(PermissiveSecurityPolicy):
=== modified file 'lib/canonical/launchpad/webapp/error.py'
--- lib/canonical/launchpad/webapp/error.py 2011-10-03 14:39:59 +0000
+++ lib/canonical/launchpad/webapp/error.py 2011-12-11 01:49:34 +0000
@@ -107,7 +107,7 @@
"""Returns the given HTML inside a div of an appropriate class."""
return ('<div class="highlight" style="'
- "font-family: 'UbuntuBeta Mono', 'Ubuntu Mono', monospace;"
+ "font-family: 'Ubuntu Mono', monospace;"
' font-size: smaller;">'
'%s'
'</div>') % html
=== modified file 'lib/canonical/launchpad/webapp/interaction.py'
--- lib/canonical/launchpad/webapp/interaction.py 2011-10-12 00:31:08 +0000
+++ lib/canonical/launchpad/webapp/interaction.py 2011-12-11 01:49:34 +0000
@@ -163,7 +163,8 @@
return setupInteraction(ANONYMOUS, participation)
else:
# Bypass zope's security because IEmailAddress.email is not public.
- naked_email = removeSecurityProxy(person.preferredemail)
+ naked_person = removeSecurityProxy(person)
+ naked_email = removeSecurityProxy(naked_person.preferredemail)
return setupInteractionByEmail(naked_email.email, participation)
=== modified file 'lib/canonical/launchpad/webapp/login.py'
--- lib/canonical/launchpad/webapp/login.py 2011-12-08 04:36:31 +0000
+++ lib/canonical/launchpad/webapp/login.py 2011-12-11 01:49:34 +0000
@@ -41,7 +41,6 @@
from canonical.config import config
from canonical.launchpad import _
from canonical.launchpad.interfaces.account import AccountSuspendedError
-from canonical.launchpad.interfaces.openidconsumer import IOpenIDConsumerStore
from canonical.launchpad.readonly import is_read_only
from canonical.launchpad.webapp.dbpolicy import MasterDatabasePolicy
from canonical.launchpad.webapp.error import SystemErrorView
@@ -60,6 +59,7 @@
IPersonSet,
PersonCreationRationale,
)
+from lp.services.openid.interfaces.openidconsumer import IOpenIDConsumerStore
from lp.services.propertycache import cachedproperty
from lp.services.timeline.requesttimeline import get_request_timeline
=== modified file 'lib/canonical/launchpad/webapp/templates/oops-veryplain.pt'
--- lib/canonical/launchpad/webapp/templates/oops-veryplain.pt 2011-05-27 21:03:22 +0000
+++ lib/canonical/launchpad/webapp/templates/oops-veryplain.pt 2011-12-11 01:49:34 +0000
@@ -11,7 +11,7 @@
<title>Oops!</title>
<style type="text/css">
html {
- font-family: UbuntuBeta, Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
+ font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
font-size: 8pt;
}
h1 {background: none; color: #83ad23; font-size: 3em; font-weight: normal;}
=== modified file 'lib/canonical/launchpad/webapp/tests/test_publisher.py'
--- lib/canonical/launchpad/webapp/tests/test_publisher.py 2011-12-08 20:29:15 +0000
+++ lib/canonical/launchpad/webapp/tests/test_publisher.py 2011-12-11 01:49:34 +0000
@@ -170,7 +170,8 @@
u'priority': 0,
u'value': u'on',
},
- )))
+ ),
+ override_scope_lookup=lambda scope_name: True))
request = LaunchpadTestRequest()
view = LaunchpadView(object(), request)
view.related_features = ['test_feature']
@@ -207,7 +208,8 @@
# but have different values,
# then the property related_feature_info contains this feature flag.
self.useFixture(FeatureFixture(
- {}, self.makeFeatureFlagDictionaries(u'', u'on')))
+ {}, self.makeFeatureFlagDictionaries(u'', u'on'),
+ override_scope_lookup=lambda scope_name: True))
request = LaunchpadTestRequest()
view = LaunchpadView(object(), request)
view.related_features = ['test_feature']
@@ -228,7 +230,8 @@
# and have the same values,
# then is_beta is false.
self.useFixture(FeatureFixture(
- {}, self.makeFeatureFlagDictionaries(u'on', u'on')))
+ {}, self.makeFeatureFlagDictionaries(u'on', u'on'),
+ override_scope_lookup=lambda scope_name: True))
request = LaunchpadTestRequest()
view = LaunchpadView(object(), request)
view.related_features = ['test_feature']
@@ -245,7 +248,8 @@
related_features = ['test_feature']
self.useFixture(FeatureFixture(
- {}, self.makeFeatureFlagDictionaries(u'', u'on')))
+ {}, self.makeFeatureFlagDictionaries(u'', u'on'),
+ override_scope_lookup=lambda scope_name: True))
request = LaunchpadTestRequest()
view = TestView(object(), request)
with person_logged_in(self.factory.makePerson()):
@@ -269,7 +273,8 @@
related_features = ['test_feature_2']
self.useFixture(FeatureFixture(
- {}, self.makeFeatureFlagDictionaries(u'', u'on')))
+ {}, self.makeFeatureFlagDictionaries(u'', u'on'),
+ override_scope_lookup=lambda scope_name: True))
request = LaunchpadTestRequest()
view = TestView(object(), request)
TestView2(object(), request)
=== modified file 'lib/canonical/launchpad/zcml/configure.zcml'
--- lib/canonical/launchpad/zcml/configure.zcml 2011-12-08 05:13:31 +0000
+++ lib/canonical/launchpad/zcml/configure.zcml 2011-12-11 01:49:34 +0000
@@ -19,15 +19,7 @@
<include file="librarian.zcml" />
<include file="lifecycle.zcml" />
<include file="logintoken.zcml" />
- <include file="openidconsumer.zcml" />
<include file="temporaryblobstorage.zcml" />
<include file="webservice.zcml" />
- <!-- System homepages -->
-
- <!-- Event configuration -->
-
- <!-- Special Utilities -->
- <include file="gpghandler.zcml" />
-
</configure>
=== removed file 'lib/canonical/launchpad/zcml/openidconsumer.zcml'
--- lib/canonical/launchpad/zcml/openidconsumer.zcml 2009-07-13 18:15:02 +0000
+++ lib/canonical/launchpad/zcml/openidconsumer.zcml 1970-01-01 00:00:00 +0000
@@ -1,12 +0,0 @@
-<!-- Copyright 2009 Canonical Ltd. This software is licensed under the
- GNU Affero General Public License version 3 (see the file LICENSE).
--->
-
-<configure xmlns="http://namespaces.zope.org/zope">
-
- <utility
- provides="..interfaces.openidconsumer.IOpenIDConsumerStore"
- factory="..database.openidconsumer.OpenIDConsumerStore">
- </utility>
-
-</configure>
=== modified file 'lib/lp/app/browser/launchpad.py'
--- lib/lp/app/browser/launchpad.py 2011-12-09 05:08:09 +0000
+++ lib/lp/app/browser/launchpad.py 2011-12-11 01:49:34 +0000
@@ -724,8 +724,8 @@
# Check to see if this is a team, and if so, whether the
# logged in user is allowed to view the team, by virtue of
# team membership or Launchpad administration.
- if (person.is_team
- and not check_permission('launchpad.View', person)):
+ if (person.is_team and
+ not check_permission('launchpad.LimitedView', person)):
raise NotFound(self.context, name)
# Only admins are permitted to see suspended users.
if person.account_status == AccountStatus.SUSPENDED:
=== modified file 'lib/lp/app/browser/tales.py'
--- lib/lp/app/browser/tales.py 2011-12-08 22:32:41 +0000
+++ lib/lp/app/browser/tales.py 2011-12-11 01:49:34 +0000
@@ -1266,6 +1266,14 @@
self.hidden)
return super(TeamFormatterAPI, self).link(view_name, rootsite)
+ def icon(self, view_name):
+ team = self._context
+ if not check_permission('launchpad.LimitedView', team):
+ css_class = ObjectImageDisplayAPI(team).sprite_css()
+ return '<span class="' + css_class + '"></span>'
+ else:
+ return super(TeamFormatterAPI, self).icon(view_name)
+
def displayname(self, view_name, rootsite=None):
"""See `PersonFormatterAPI`."""
person = self._context
=== modified file 'lib/lp/app/templates/base-layout.pt'
--- lib/lp/app/templates/base-layout.pt 2011-12-09 05:08:09 +0000
+++ lib/lp/app/templates/base-layout.pt 2011-12-11 01:49:34 +0000
@@ -71,6 +71,14 @@
${view/macro:pagetype}
${view/context/fmt:public-private-css}
yui3-skin-sam">
+ <script type="text/javascript"
+ tal:condition="python: is_lpnet">
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-12833497-1']);
+ _gaq.push(['_setDomainName', '.launchpad.net']);
+ _gaq.push(['_setAllowHash', false]);
+ _gaq.push(['_trackPageview']);
+ </script>
<div class="yui-d0">
<div id="locationbar" class="login-logout">
<tal:login replace="structure context/@@login_status" />
=== modified file 'lib/lp/app/templates/launchpad-databaseunavailable.pt'
--- lib/lp/app/templates/launchpad-databaseunavailable.pt 2011-10-03 14:41:27 +0000
+++ lib/lp/app/templates/launchpad-databaseunavailable.pt 2011-12-11 01:49:34 +0000
@@ -16,7 +16,7 @@
}
body {
- font-family: 'UbuntuBeta Regular', Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
+ font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
font-size: 0.75em;
line-height: 1.3em;
color: #000;
=== modified file 'lib/lp/app/tests/test_tales.py'
--- lib/lp/app/tests/test_tales.py 2011-11-16 00:09:49 +0000
+++ lib/lp/app/tests/test_tales.py 2011-12-11 01:49:34 +0000
@@ -149,18 +149,20 @@
self.assertEqual(expected, result)
-class TestTalesFormatterAPI(TestCaseWithFactory):
- """ Test permissions required to access TalesFormatterAPI methods.
+class TestTeamFormatterAPI(TestCaseWithFactory):
+ """ Test permissions required to access TeamFormatterAPI methods.
A user must have launchpad.LimitedView permission to use
- TestTalesFormatterAPI with private teams.
+ TeamFormatterAPI with private teams.
"""
- layer = DatabaseFunctionalLayer
+ layer = LaunchpadFunctionalLayer
def setUp(self):
- super(TestTalesFormatterAPI, self).setUp()
+ super(TestTeamFormatterAPI, self).setUp()
+ icon = self.factory.makeLibraryFileAlias(
+ filename='smurf.png', content_type='image/png')
self.team = self.factory.makeTeam(
- name='team', displayname='a team',
+ name='team', displayname='a team', icon=icon,
visibility=PersonVisibility.PRIVATE)
def _make_formatter(self, cache_permission=False):
@@ -175,10 +177,11 @@
request, 'launchpad.LimitedView', [self.team])
return formatter, request, any_person
- def _tales_value(self, attr, request):
+ def _tales_value(self, attr, request, path='fmt'):
# Evaluate the given formatted attribute value on team.
- return test_tales(
- "team/fmt:%s" % attr, team=self.team, request=request)
+ result = test_tales(
+ "team/%s:%s" % (path, attr), team=self.team, request=request)
+ return result
def _test_can_view_attribute_no_login(self, attr, hidden=None):
# Test attribute access with no login.
@@ -228,6 +231,10 @@
def test_can_view_url(self):
self._test_can_view_attribute('url')
+ def test_can_view_icon(self):
+ self._test_can_view_attribute(
+ 'icon', '<span class="sprite team"></span>')
+
class TestObjectFormatterAPI(TestCaseWithFactory):
"""Tests for ObjectFormatterAPI"""
=== modified file 'lib/lp/archivepublisher/archivesigningkey.py'
--- lib/lp/archivepublisher/archivesigningkey.py 2011-05-27 19:53:20 +0000
+++ lib/lp/archivepublisher/archivesigningkey.py 2011-12-11 01:49:34 +0000
@@ -17,7 +17,6 @@
from zope.interface import implements
from canonical.config import config
-from canonical.launchpad.interfaces.gpghandler import IGPGHandler
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
from lp.archivepublisher.config import getPubConfig
from lp.archivepublisher.interfaces.archivesigningkey import (
@@ -27,6 +26,7 @@
GPGKeyAlgorithm,
IGPGKeySet,
)
+from lp.services.gpg.interfaces import IGPGHandler
class ArchiveSigningKey:
=== modified file 'lib/lp/archivepublisher/tests/archive-signing.txt'
--- lib/lp/archivepublisher/tests/archive-signing.txt 2011-03-03 00:43:44 +0000
+++ lib/lp/archivepublisher/tests/archive-signing.txt 2011-12-11 01:49:34 +0000
@@ -195,7 +195,7 @@
exactly the same procedure for setting the signing_key information.
>>> import os
- >>> from canonical.launchpad.ftests.keys_for_tests import gpgkeysdir
+ >>> from lp.testing.gpgkeys import gpgkeysdir
>>> key_path = os.path.join(gpgkeysdir, 'ppa-sample@xxxxxxxxxxxxxxxxx')
>>> archive_signing_key.setSigningKey(key_path)
@@ -226,7 +226,7 @@
The generated key UID follows the "Launchpad PPA for %(person.displayname)s"
format.
- >>> from canonical.launchpad.interfaces.gpghandler import IGPGHandler
+ >>> from lp.services.gpg.interfaces import IGPGHandler
>>> gpghandler = getUtility(IGPGHandler)
>>> retrieved_key = gpghandler.retrieveKey(
=== modified file 'lib/lp/archivepublisher/tests/test_publisher.py'
--- lib/lp/archivepublisher/tests/test_publisher.py 2011-10-18 16:10:28 +0000
+++ lib/lp/archivepublisher/tests/test_publisher.py 2011-12-11 01:49:34 +0000
@@ -23,8 +23,6 @@
from canonical.config import config
from canonical.database.constants import UTC_NOW
-from canonical.launchpad.ftests.keys_for_tests import gpgkeysdir
-from canonical.launchpad.interfaces.gpghandler import IGPGHandler
from canonical.testing.layers import ZopelessDatabaseLayer
from lp.archivepublisher.config import getPubConfig
from lp.archivepublisher.diskpool import DiskPool
@@ -44,6 +42,7 @@
pocketsuffix,
)
from lp.registry.interfaces.series import SeriesStatus
+from lp.services.gpg.interfaces import IGPGHandler
from lp.services.log.logger import (
BufferLogger,
DevNullLogger,
@@ -58,6 +57,7 @@
from lp.soyuz.interfaces.archive import IArchiveSet
from lp.soyuz.tests.test_publishing import TestNativePublishingBase
from lp.testing import TestCaseWithFactory
+from lp.testing.gpgkeys import gpgkeysdir
from lp.testing.keyserver import KeyServerTac
=== modified file 'lib/lp/archiveuploader/dscfile.py'
--- lib/lp/archiveuploader/dscfile.py 2011-05-20 13:36:11 +0000
+++ lib/lp/archiveuploader/dscfile.py 2011-12-11 01:49:34 +0000
@@ -29,10 +29,6 @@
from debian.deb822 import Deb822Dict
from zope.component import getUtility
-from canonical.launchpad.interfaces.gpghandler import (
- GPGVerificationError,
- IGPGHandler,
- )
from canonical.librarian.utils import copy_and_close
from lp.app.errors import NotFoundError
from lp.archiveuploader.nascentuploadfile import (
@@ -65,6 +61,10 @@
from lp.registry.interfaces.sourcepackage import SourcePackageFileType
from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
from lp.services.encoding import guess as guess_encoding
+from lp.services.gpg.interfaces import (
+ GPGVerificationError,
+ IGPGHandler,
+ )
from lp.soyuz.enums import (
ArchivePurpose,
SourcePackageFormat,
=== modified file 'lib/lp/archiveuploader/tests/nascentupload-announcements.txt'
--- lib/lp/archiveuploader/tests/nascentupload-announcements.txt 2011-10-27 06:50:49 +0000
+++ lib/lp/archiveuploader/tests/nascentupload-announcements.txt 2011-12-11 01:49:34 +0000
@@ -51,7 +51,7 @@
Import the test keys to use 'insecure' policy.
- >>> from canonical.launchpad.ftests import import_public_test_keys
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> import_public_test_keys()
For the purpose of this test, hoary needs to be an open (development)
=== modified file 'lib/lp/archiveuploader/tests/nascentupload.txt'
--- lib/lp/archiveuploader/tests/nascentupload.txt 2011-09-29 04:12:59 +0000
+++ lib/lp/archiveuploader/tests/nascentupload.txt 2011-12-11 01:49:34 +0000
@@ -3,7 +3,7 @@
Import the test keys so we have them ready for verification
- >>> from canonical.launchpad.ftests import import_public_test_keys
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> import_public_test_keys()
We need to be logged into the security model in order to get any further
@@ -743,7 +743,7 @@
Import the test keys again since the transaction was aborted before.
- >>> from canonical.launchpad.ftests import import_public_test_keys
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> import_public_test_keys()
When using 'insecure' policy, NascentUpload instace stores the DSC
=== modified file 'lib/lp/archiveuploader/tests/nascentuploadfile.txt'
--- lib/lp/archiveuploader/tests/nascentuploadfile.txt 2011-06-09 10:50:25 +0000
+++ lib/lp/archiveuploader/tests/nascentuploadfile.txt 2011-12-11 01:49:34 +0000
@@ -13,7 +13,7 @@
Import the test keys so we have them ready for verification
- >>> from canonical.launchpad.ftests import import_public_test_keys
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> import_public_test_keys()
We need to be logged into the security model in order to get any further
=== modified file 'lib/lp/archiveuploader/tests/test_buildduploads.py'
--- lib/lp/archiveuploader/tests/test_buildduploads.py 2011-08-17 14:44:07 +0000
+++ lib/lp/archiveuploader/tests/test_buildduploads.py 2011-12-11 01:49:34 +0000
@@ -10,21 +10,19 @@
from zope.component import getUtility
from canonical.database.constants import UTC_NOW
-from canonical.launchpad.ftests import import_public_test_keys
-from lp.soyuz.enums import (
- PackagePublishingStatus,
- PackageUploadStatus,
- )
from lp.archiveuploader.tests.test_uploadprocessor import (
TestUploadProcessorBase,
)
-from lp.archiveuploader.uploadprocessor import (
- UploadHandler,
- )
+from lp.archiveuploader.uploadprocessor import UploadHandler
from lp.registry.interfaces.distribution import IDistributionSet
from lp.registry.interfaces.pocket import PackagePublishingPocket
+from lp.soyuz.enums import (
+ PackagePublishingStatus,
+ PackageUploadStatus,
+ )
from lp.soyuz.interfaces.publishing import IPublishingSet
from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
+from lp.testing.gpgkeys import import_public_test_keys
class TestStagedBinaryUploadBase(TestUploadProcessorBase):
=== modified file 'lib/lp/archiveuploader/tests/test_changesfile.py'
--- lib/lp/archiveuploader/tests/test_changesfile.py 2011-05-22 23:47:39 +0000
+++ lib/lp/archiveuploader/tests/test_changesfile.py 2011-12-11 01:49:34 +0000
@@ -10,7 +10,6 @@
from debian.deb822 import Changes
from zope.component import getUtility
-from canonical.launchpad.ftests import import_public_test_keys
from canonical.testing.layers import (
LaunchpadZopelessLayer,
ZopelessDatabaseLayer,
@@ -36,6 +35,7 @@
from lp.registry.interfaces.person import IPersonSet
from lp.services.log.logger import BufferLogger
from lp.testing import TestCase
+from lp.testing.gpgkeys import import_public_test_keys
from lp.testing.keyserver import KeyServerTac
=== modified file 'lib/lp/archiveuploader/tests/test_nascentupload_documentation.py'
--- lib/lp/archiveuploader/tests/test_nascentupload_documentation.py 2010-12-22 01:08:48 +0000
+++ lib/lp/archiveuploader/tests/test_nascentupload_documentation.py 2011-12-11 01:49:34 +0000
@@ -12,7 +12,6 @@
from canonical.launchpad.database.librarian import LibraryFileAlias
from canonical.launchpad.ftests import (
- import_public_test_keys,
login,
logout,
)
@@ -29,8 +28,9 @@
from lp.archiveuploader.uploadpolicy import ArchiveUploadType
from lp.registry.interfaces.distribution import IDistributionSet
from lp.services.log.logger import DevNullLogger
+from lp.soyuz.interfaces.component import IComponentSet
from lp.soyuz.model.component import ComponentSelection
-from lp.soyuz.interfaces.component import IComponentSet
+from lp.testing.gpgkeys import import_public_test_keys
def getUploadForSource(upload_path):
=== modified file 'lib/lp/archiveuploader/tests/test_uploadprocessor.py'
--- lib/lp/archiveuploader/tests/test_uploadprocessor.py 2011-10-26 02:14:52 +0000
+++ lib/lp/archiveuploader/tests/test_uploadprocessor.py 2011-12-11 01:49:34 +0000
@@ -26,7 +26,6 @@
from canonical.config import config
from canonical.database.constants import UTC_NOW
-from canonical.launchpad.ftests import import_public_test_keys
from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet
from canonical.launchpad.testing.fakepackager import FakePackager
from canonical.testing.layers import LaunchpadZopelessLayer
@@ -104,6 +103,7 @@
TestCaseWithFactory,
)
from lp.testing.fakemethod import FakeMethod
+from lp.testing.gpgkeys import import_public_test_keys
from lp.testing.mail_helpers import pop_notifications
=== modified file 'lib/lp/bugs/browser/tests/test_bugsubscription_views.py'
--- lib/lp/bugs/browser/tests/test_bugsubscription_views.py 2011-11-15 13:22:25 +0000
+++ lib/lp/bugs/browser/tests/test_bugsubscription_views.py 2011-12-11 01:49:34 +0000
@@ -629,7 +629,7 @@
direct_subscriber = self.factory.makeTeam(
name='team', displayname='Team Name', owner=teamowner,
visibility=PersonVisibility.PRIVATE)
- with person_logged_in(direct_subscriber.teamowner):
+ with person_logged_in(teamowner):
bug.subscribe(direct_subscriber, direct_subscriber.teamowner,
level=BugNotificationLevel.LIFECYCLE)
=== modified file 'lib/lp/bugs/javascript/buglisting.js'
--- lib/lp/bugs/javascript/buglisting.js 2011-12-06 16:36:16 +0000
+++ lib/lp/bugs/javascript/buglisting.js 2011-12-11 01:49:34 +0000
@@ -205,11 +205,17 @@
},
/**
+ * Return the batch key of the current batch.
+ */
+ get_current_batch_key: function(){
+ return this.get('model').get('history').get('batch_key');
+ },
+
+ /**
* Retrieve the current batch for rendering purposes.
*/
get_current_batch: function(){
- var batch_key = this.get('model').get('history').get('batch_key');
- return this.get('batches')[batch_key];
+ return this.get('batches')[this.get_current_batch_key()];
},
/**
@@ -230,6 +236,33 @@
},
/**
+ * If the supplied batch is adjacent to the current batch, find an alias
+ * of one of the batches and store it.
+ *
+ * A batch has two major aliases because "forwards" may be true or false.
+ * Either the ajacent batch will have an alias for the current batch, or
+ * the current batch will have an alias for the adjacent batch.
+ */
+ dealias_batches: function(batch){
+ var batch_a_keys = namespace.get_batch_key_list(batch);
+ var batch_b_keys = namespace.get_batch_key_list(
+ this.get_current_batch());
+ var aliases = namespace.find_batch_alias(batch_a_keys, batch_b_keys);
+ if (Y.Lang.isNull(aliases)){
+ return;
+ }
+ var alias_batch = this.get('batches')[aliases[0]];
+ if (Y.Lang.isValue(alias_batch)){
+ this.get('batches')[aliases[1]] = alias_batch;
+ } else {
+ alias_batch = this.get('batches')[aliases[1]];
+ if (Y.Lang.isValue(alias_batch)){
+ this.get('batches')[aliases[0]] = alias_batch;
+ }
+ }
+ },
+
+ /**
* Render bug listings via Mustache.
*
* If model is supplied, it is used as the data for rendering the
@@ -275,6 +308,7 @@
update_from_new_model: function(query, fetch_only, model){
var batch_key = this.handle_new_batch(model);
+ this.dealias_batches(model);
if (fetch_only) {
return;
}
@@ -390,45 +424,21 @@
this.update(this.first_batch_config(order_by));
},
- next_batch_config: function(){
- var current_batch = this.get_current_batch();
- if (!this.has_next()){
- return null;
- }
- return {
- forwards: true,
- memo: current_batch.next.memo,
- start: current_batch.next.start,
- order_by: current_batch.order_by
- };
- },
/**
* Update the navigator to display the next batch.
*/
next_batch: function() {
- var config = this.next_batch_config();
+ var config = namespace.next_batch_config(this.get_current_batch());
if (config === null){
return;
}
this.update(config);
},
- prev_batch_config: function(){
- var current_batch = this.get_current_batch();
- if (!this.has_prev()){
- return null;
- }
- return {
- forwards: false,
- memo: current_batch.prev.memo,
- start: current_batch.prev.start,
- order_by: current_batch.order_by
- };
- },
/**
* Update the navigator to display the previous batch.
*/
prev_batch: function() {
- var config = this.prev_batch_config();
+ var config = namespace.prev_batch_config(this.get_current_batch());
if (config === null){
return;
}
@@ -439,7 +449,8 @@
*/
get_pre_fetch_configs: function(){
var configs = [];
- var next_batch_config = this.next_batch_config();
+ var next_batch_config = namespace.next_batch_config(
+ this.get_current_batch());
if (next_batch_config !== null){
configs.push(next_batch_config);
}
@@ -545,6 +556,85 @@
};
+/**
+ * Return a mapping describing the batch previous to the current batch.
+ */
+namespace.prev_batch_config = function(batch){
+ if (Y.Lang.isNull(batch.prev)){
+ return null;
+ }
+ return {
+ forwards: false,
+ memo: batch.prev.memo,
+ start: batch.prev.start,
+ order_by: batch.order_by
+ };
+};
+
+
+/**
+ * Return a mapping describing the batch after the current batch.
+ */
+namespace.next_batch_config = function(batch){
+ if (Y.Lang.isNull(batch.next)) {
+ return null;
+ }
+ return {
+ forwards: true,
+ memo: batch.next.memo,
+ start: batch.next.start,
+ order_by: batch.order_by
+ };
+};
+
+/**
+ * Return a list of the batch keys described in this batch: prev, current and
+ * next. If next or prev is null, the corresponding batch key will be null.
+ */
+namespace.get_batch_key_list = function(batch){
+ var prev_config = namespace.prev_batch_config(batch);
+ var next_config = namespace.next_batch_config(batch);
+ var keys = [];
+ if (Y.Lang.isNull(prev_config)){
+ keys.push(null);
+ } else {
+ keys.push(namespace.ListingNavigator.get_batch_key(prev_config));
+ }
+ keys.push(namespace.ListingNavigator.get_batch_key(batch));
+ if (Y.Lang.isNull(next_config)){
+ keys.push(null);
+ } else {
+ keys.push(namespace.ListingNavigator.get_batch_key(next_config));
+ }
+ return keys;
+};
+
+
+/**
+ * Find an alias between the two supplied batches, if they are adjacent.
+ * Returns a list of two batch keys that should be considered equivalent.
+ * If the supplied batches are not adjacent, returns null.
+ */
+namespace.find_batch_alias = function(batch_a, batch_b) {
+ var prev_batch;
+ var next_batch;
+ if (batch_a[2] === batch_b[1] || batch_a[1] === batch_b[0]){
+ prev_batch = batch_a;
+ next_batch = batch_b;
+ } else if (batch_b[2] === batch_a[1] || batch_b[1] === batch_a[0]){
+ prev_batch = batch_b;
+ next_batch = batch_a;
+ }
+ else {
+ return null;
+ }
+ if (prev_batch[1] !== next_batch[0]){
+ return [prev_batch[1], next_batch[0]];
+ } else {
+ return [prev_batch[2], next_batch[1]];
+ }
+};
+
}, "0.1", {
"requires": [
"history", "node", 'lp.client', 'lp.app.errors', 'lp.indicator'
=== modified file 'lib/lp/bugs/javascript/buglisting_utils.js'
--- lib/lp/bugs/javascript/buglisting_utils.js 2011-12-08 13:23:00 +0000
+++ lib/lp/bugs/javascript/buglisting_utils.js 2011-12-11 01:49:34 +0000
@@ -250,31 +250,6 @@
},
/**
- * Update field_visibility based on fields stored
- * in cookies. This is used as a light-weight
- * page to page persistence mechanism.
- *
- * @method updateFromCookie
- */
- updateFromCookie: function() {
- var cookie_name = this.get('cookie_name');
- var cookie_fields = Y.Cookie.getSubs(cookie_name);
- if (Y.Lang.isValue(cookie_fields)) {
- // We get true/false back as strings from Y.Cookie,
- // so we have to convert them to booleans.
- Y.each(cookie_fields, function(val, key, obj) {
- if (val === 'true') {
- val = true;
- } else {
- val = false;
- }
- obj[key] = val;
- });
- this.updateFieldVisibilty(cookie_fields);
- }
- },
-
- /**
* Set the given value for the buglisting config cookie.
* If config is not specified, the cookie will be cleared.
*
@@ -287,7 +262,7 @@
path: '/',
expires: new Date('January 19, 2038')});
} else {
- Y.Cookie.remove(cookie_name);
+ Y.Cookie.remove(cookie_name, {path: '/'});
}
},
@@ -298,7 +273,6 @@
* @method _extraRenderUI
*/
_extraRenderUI: function() {
- this.updateFromCookie();
var form_content = this.buildFormContent();
var on_submit_callback = Y.bind(this.handleOverlaySubmit, this);
util_overlay = new Y.lazr.FormOverlay({
=== modified file 'lib/lp/bugs/javascript/tests/test_buglisting.js'
--- lib/lp/bugs/javascript/tests/test_buglisting.js 2011-12-06 16:36:16 +0000
+++ lib/lp/bugs/javascript/tests/test_buglisting.js 2011-12-11 01:49:34 +0000
@@ -359,6 +359,8 @@
memo: 'memo1',
forwards: true,
start: 5,
+ next: null,
+ prev: null,
mustache_model: {
item: [
{name: 'first'},
@@ -521,7 +523,10 @@
memo: 457,
start: 400
},
+ forwards: true,
order_by: 'foo',
+ memo: 457,
+ start: 450,
last_start: 23,
field_visibility: {},
field_visibility_defaults: {}
@@ -840,6 +845,7 @@
memo: "pi",
start: 314
},
+ prev: null,
forwards: true,
start: 5,
mustache_model: {
@@ -858,6 +864,7 @@
}
}));
+
suite.add(new Y.Test.Case({
name: "Test indicators",
@@ -884,7 +891,9 @@
var navigator = get_navigator();
navigator.update({});
navigator.get('io_provider').last_request.successJSON({
- mustache_model: {bugtasks: []}
+ mustache_model: {bugtasks: []},
+ next: null,
+ prev: null
});
Y.Assert.isFalse(navigator.indicator.get('visible'));
},
@@ -896,7 +905,9 @@
navigator.indicator.setBusy();
navigator.update({fetch_only: true});
navigator.get('io_provider').last_request.successJSON({
- mustache_model: {bugtasks: []}
+ mustache_model: {bugtasks: []},
+ next: null,
+ prev: null
});
Y.Assert.isTrue(navigator.indicator.get('visible'));
},
@@ -921,6 +932,132 @@
}
}));
+
+suite.add(new Y.Test.Case({
+ name: "Find batch aliases",
+
+ test_get_batch_key_list: function(){
+ var keys = module.get_batch_key_list({
+ prev: null,
+ next:null,
+ memo: 'pi',
+ start: -1,
+ forwards: true,
+ order_by: 'ordering'
+ });
+ Y.ArrayAssert.itemsAreSame(
+ [null, '["ordering","pi",true,-1]', null], keys);
+ keys = module.get_batch_key_list({
+ prev: {
+ memo: "pi",
+ start: -2
+ },
+ next: {
+ memo: "e",
+ start: 0
+ },
+ memo: 'pi',
+ start: -1,
+ forwards: true,
+ order_by: 'ordering'
+ });
+ Y.ArrayAssert.itemsAreSame([
+ '["ordering","pi",false,-2]',
+ '["ordering","pi",true,-1]',
+ '["ordering","e",true,0]'], keys);
+ },
+
+ /* Detect batch aliases for forward movement (next). */
+ test_find_batch_alias_moving_forward: function(){
+ var prev_batch = ['a', 'b', 'c'];
+ var next_batch = ["b'", 'c', 'd'];
+ var result = module.find_batch_alias(prev_batch, next_batch);
+ Y.Assert.areSame(result[0], 'b');
+ Y.Assert.areSame(result[1], "b'");
+ result = module.find_batch_alias(next_batch, prev_batch);
+ Y.Assert.areSame(result[0], 'b');
+ Y.Assert.areSame(result[1], "b'");
+ },
+
+ /* Detect batch aliases for backward movement (prev). */
+ test_find_batch_alias_moving_backward: function(){
+ var prev_batch = ['a', 'b', 'c'];
+ var next_batch = ['b', "c'", 'd'];
+ var result = module.find_batch_alias(prev_batch, next_batch);
+ Y.Assert.areSame(result[0], 'c');
+ Y.Assert.areSame(result[1], "c'");
+ result = module.find_batch_alias(next_batch, prev_batch);
+ Y.Assert.areSame(result[0], 'c');
+ Y.Assert.areSame(result[1], "c'");
+ },
+
+ /* Do not detect aliases if batches are unrelated */
+ test_find_batch_alias_unrelated: function(){
+ var prev_batch = ['a', 'b', 'c'];
+ var next_batch = ['d', 'e', 'f'];
+ var result = module.find_batch_alias(next_batch, prev_batch);
+ Y.Assert.isNull(result);
+ },
+
+ /**
+ * When dealias_batches is called on the next batch, the current batch is
+ * re-added to the batches mapping, under its alias from the next batch.
+ */
+ test_dealias_batches_next: function(){
+ var navigator = get_navigator();
+ var next_batch = {
+ memo: 467,
+ start: 500,
+ order_by: 'foo',
+ forwards: true,
+ prev: {
+ memo: 467,
+ start: 450
+ },
+ next: null
+ };
+ var prev_batch_config = module.prev_batch_config(next_batch);
+ var prev_batch_key = navigator.constructor.get_batch_key(
+ prev_batch_config);
+ navigator.dealias_batches(next_batch);
+ Y.Assert.areSame(
+ navigator.get('batches')[prev_batch_key],
+ navigator.get_current_batch()
+ );
+ Y.Assert.areNotSame(
+ prev_batch_key, navigator.get_current_batch_key());
+ },
+ /**
+ * When dealias_batches is called on the previous batch, the current batch
+ * is re-added to the batches mapping, under its alias from the previous
+ * batch.
+ */
+ test_dealias_batches_prev: function(){
+ var navigator = get_navigator();
+ var prev_batch = {
+ memo: 457,
+ start: 400,
+ order_by: 'foo',
+ forwards: false,
+ next: {
+ memo: 467,
+ start: 450
+ },
+ prev: null
+ };
+ var next_batch_config = module.next_batch_config(prev_batch);
+ var next_batch_key = navigator.constructor.get_batch_key(
+ next_batch_config);
+ navigator.dealias_batches(prev_batch);
+ Y.Assert.areSame(
+ navigator.get('batches')[next_batch_key],
+ navigator.get_current_batch()
+ );
+ Y.Assert.areNotSame(
+ next_batch_key, navigator.get_current_batch_key());
+ }
+}));
+
var handle_complete = function(data) {
window.status = '::::' + JSON.stringify(data);
};
=== modified file 'lib/lp/bugs/javascript/tests/test_buglisting_utils.js'
--- lib/lp/bugs/javascript/tests/test_buglisting_utils.js 2011-12-08 11:03:28 +0000
+++ lib/lp/bugs/javascript/tests/test_buglisting_utils.js 2011-12-11 01:49:34 +0000
@@ -10,10 +10,19 @@
var ArrayAssert = Y.ArrayAssert;
var ObjectAssert = Y.ObjectAssert;
-suite.add(new Y.Test.Case({
+
+var running_in_webkit = function(){
+ return (/AppleWebKit.53[1-5]/).test(navigator.userAgent);
+};
+
+
+var buglisting_display_utils_tests = new Y.Test.Case({
name: 'buglisting_display_utils_tests',
+ _should: {ignore: []},
+
+
setUp: function() {
// Default values for model config.
this.defaults = {
@@ -48,7 +57,9 @@
}
};
// _setDoc is required for tests using cookies to pass.
- Y.Cookie._setDoc({cookie: ""});
+ if (running_in_webkit()){
+ Y.Cookie._setDoc({cookie: ""});
+ }
// Simulate LP.cache.field_visibility which will be
// present in the actual page.
window.LP = {
@@ -69,8 +80,10 @@
this.list_util.destroy();
}
// Cleanup cookies.
- Y.Cookie.remove(this.cookie_name);
- Y.Cookie._setDoc(Y.config.doc);
+ Y.Cookie.remove(this.cookie_name, {path: "/"});
+ if (running_in_webkit()){
+ Y.Cookie._setDoc(Y.config.doc);
+ }
},
/**
@@ -122,32 +135,6 @@
this.defaults.field_visibility_defaults);
},
- test_cookie_updates_field_visibility_config: function() {
- // If the $USER-buglist-fields cookie is present,
- // the widget will update field_visibility to these values.
- var expected_config = {
- show_title: true,
- show_id: true,
- show_importance: true,
- show_status: true,
- show_heat: false,
- show_targetname: false,
- show_datecreated: true,
- show_date_last_updated: true,
- show_assignee: false,
- show_reporter: false,
- show_milestone_name: false,
- show_tag: false
- };
- Y.Cookie.setSubs(this.cookie_name, expected_config);
- this.list_util = new Y.lp.buglisting_utils.BugListingConfigUtil(
- this.defaults);
- this.list_util.render();
- var model = this.list_util.get('model');
- var actual_config = model.get_field_visibility();
- ObjectAssert.areEqual(expected_config, actual_config);
- },
-
test_field_visibility_form_reference: function() {
// The form created from field_visibility defaults is referenced
// via BugListingConfigUtil.get('form')
@@ -344,6 +331,7 @@
field_visibility: field_visibility,
field_visibility_defaults: this.defaults.field_visibility_defaults
});
+ this.list_util.setCookie(field_visibility);
this.list_util.render();
// Poke at the page to reset the form.
var config = Y.one('.config');
@@ -426,6 +414,9 @@
test_form_reset_removes_cookie: function() {
// Clicking "reset to defaults" on the overlay will
// remove any cookie added.
+
+ Y.Cookie.remove(this.cookie_name, {path: "/"});
+ Assert.isNull(Y.Cookie.get(this.cookie_name));
this.list_util = new Y.lp.buglisting_utils.BugListingConfigUtil(
this.defaults);
this.list_util.render();
@@ -438,9 +429,9 @@
update.simulate('click');
// Now reset from the form.
config.simulate('click');
+ Assert.isNotNull(Y.Cookie.get(this.cookie_name));
Y.one('.reset-buglisting').simulate('click');
- var cookie = Y.Cookie.get(this.cookie_name);
- Assert.areSame('', cookie);
+ Assert.isNull(Y.Cookie.get(this.cookie_name));
},
test_update_sort_button_visibility: function() {
@@ -500,7 +491,19 @@
Assert.isFalse(importance_button._isHidden());
}
-}));
+});
+
+
+/**
+ * The Chrome workaround breaks Y.Cookie.remove behaviour
+ */
+if (running_in_webkit()){
+ var ignore = buglisting_display_utils_tests._should.ignore;
+ ignore.test_form_reset_removes_cookie = true;
+ ignore.test_update_from_form_updates_cookie = true;
+}
+
+suite.add(buglisting_display_utils_tests);
buglisting_utils.suite = suite;
=== modified file 'lib/lp/bugs/mail/tests/test_handler.py'
--- lib/lp/bugs/mail/tests/test_handler.py 2011-11-28 00:35:15 +0000
+++ lib/lp/bugs/mail/tests/test_handler.py 2011-12-11 01:49:34 +0000
@@ -18,7 +18,6 @@
from canonical.config import config
from canonical.database.sqlbase import commit
-from canonical.launchpad.ftests import import_secret_test_key
from canonical.launchpad.interfaces.emailaddress import EmailAddressStatus
from canonical.launchpad.webapp.authorization import LaunchpadSecurityPolicy
from canonical.testing.layers import (
@@ -46,6 +45,7 @@
TestCaseWithFactory,
)
from lp.testing.factory import GPGSigningContext
+from lp.testing.gpgkeys import import_secret_test_key
from lp.testing.mail_helpers import pop_notifications
=== modified file 'lib/lp/code/browser/tests/test_branchlisting.py'
--- lib/lp/code/browser/tests/test_branchlisting.py 2011-11-18 16:25:47 +0000
+++ lib/lp/code/browser/tests/test_branchlisting.py 2011-12-11 01:49:34 +0000
@@ -613,13 +613,14 @@
layer = DatabaseFunctionalLayer
def _make_branch_for_private_team(self):
+ owner = self.factory.makePerson()
private_team = self.factory.makeTeam(
- name='shh', displayname='Shh',
+ name='shh', displayname='Shh', owner=owner,
visibility=PersonVisibility.PRIVATE)
member = self.factory.makePerson(
email='member@xxxxxxxxxxx', password='test')
- with person_logged_in(private_team.teamowner):
- private_team.addMember(member, private_team.teamowner)
+ with person_logged_in(owner):
+ private_team.addMember(member, owner)
branch = self.factory.makeProductBranch(owner=private_team)
return private_team, member, branch
=== modified file 'lib/lp/code/model/sourcepackagerecipedata.py'
--- lib/lp/code/model/sourcepackagerecipedata.py 2011-11-15 11:04:59 +0000
+++ lib/lp/code/model/sourcepackagerecipedata.py 2011-12-11 01:49:34 +0000
@@ -141,7 +141,7 @@
return branch
-MAX_RECIPE_FORMAT = 0.3
+MAX_RECIPE_FORMAT = 0.4
class SourcePackageRecipeData(Storm):
@@ -160,7 +160,7 @@
base_branch = Reference(base_branch_id, 'Branch.id')
recipe_format = Unicode(allow_none=False)
- deb_version_template = Unicode(allow_none=False)
+ deb_version_template = Unicode(allow_none=True)
revspec = Unicode(allow_none=True)
instructions = ReferenceSet(
@@ -306,7 +306,10 @@
self._recordInstructions(
builder_recipe, parent_insn=None, branch_map=branch_map)
self.base_branch = base_branch
- self.deb_version_template = unicode(builder_recipe.deb_version)
+ if builder_recipe.deb_version is None:
+ self.deb_version_template = None
+ else:
+ self.deb_version_template = unicode(builder_recipe.deb_version)
self.recipe_format = unicode(builder_recipe.format)
def __init__(self, recipe, sourcepackage_recipe=None,
=== modified file 'lib/lp/code/model/tests/test_branchmergeproposal.py'
--- lib/lp/code/model/tests/test_branchmergeproposal.py 2011-09-06 12:41:28 +0000
+++ lib/lp/code/model/tests/test_branchmergeproposal.py 2011-12-11 01:49:34 +0000
@@ -24,7 +24,6 @@
from zope.security.proxy import removeSecurityProxy
from canonical.database.constants import UTC_NOW
-from canonical.launchpad.ftests import import_secret_test_key
from canonical.launchpad.interfaces.launchpad import IPrivacy
from canonical.launchpad.webapp import canonical_url
from canonical.launchpad.webapp.testing import verifyObject
@@ -90,6 +89,7 @@
GPGSigningContext,
LaunchpadObjectFactory,
)
+from lp.testing.gpgkeys import import_secret_test_key
class TestBranchMergeProposalInterface(TestCaseWithFactory):
=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipe.py 2011-11-15 16:25:02 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipe.py 2011-12-11 01:49:34 +0000
@@ -921,6 +921,21 @@
self.check_recipe_branch(
child_branch, "zam", self.merged_branch.bzr_identity, revspec="2")
+ def test_builds_recipe_without_debversion(self):
+ recipe_text = '''\
+ # bzr-builder format 0.4
+ %(base)s
+ nest bar %(nested)s baz
+ ''' % self.branch_identities
+ base_branch = self.get_recipe(recipe_text)
+ self.check_base_recipe_branch(
+ base_branch, self.base_branch.bzr_identity, num_child_branches=1,
+ deb_version=None)
+ child_branch, location = base_branch.child_branches[0].as_tuple()
+ self.assertEqual("baz", location)
+ self.check_recipe_branch(
+ child_branch, "bar", self.nested_branch.bzr_identity)
+
class RecipeDateLastModified(TestCaseWithFactory):
"""Exercises the situations where date_last_modified is updated."""
=== modified file 'lib/lp/code/scripts/tests/test_create_merge_proposals.py'
--- lib/lp/code/scripts/tests/test_create_merge_proposals.py 2011-08-13 04:07:10 +0000
+++ lib/lp/code/scripts/tests/test_create_merge_proposals.py 2011-12-11 01:49:34 +0000
@@ -12,13 +12,13 @@
import transaction
from zope.component import getUtility
-from canonical.launchpad.ftests import import_secret_test_key
from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet
from canonical.launchpad.scripts.tests import run_script
from canonical.testing.layers import ZopelessAppServerLayer
from lp.code.model.branchmergeproposaljob import CreateMergeProposalJob
from lp.testing import TestCaseWithFactory
from lp.testing.factory import GPGSigningContext
+from lp.testing.gpgkeys import import_secret_test_key
class TestCreateMergeProposals(TestCaseWithFactory):
=== modified file 'lib/lp/code/templates/sourcepackagerecipe-index.pt'
--- lib/lp/code/templates/sourcepackagerecipe-index.pt 2011-11-26 04:03:29 +0000
+++ lib/lp/code/templates/sourcepackagerecipe-index.pt 2011-12-11 01:49:34 +0000
@@ -13,7 +13,7 @@
padding-left: 2em;
}
div#edit-recipe_text, div#edit-description {
- font-family: "UbuntuBeta Mono","Ubuntu Mono",monospace;
+ font-family: "Ubuntu Mono",monospace;
margin: 1em 0;
}
div#edit-recipe_text.yui3-editable_text-content,
=== added directory 'lib/lp/contrib/css'
=== added file 'lib/lp/contrib/css/ubuntu-webfonts.css'
--- lib/lp/contrib/css/ubuntu-webfonts.css 1970-01-01 00:00:00 +0000
+++ lib/lp/contrib/css/ubuntu-webfonts.css 2011-12-11 01:49:34 +0000
@@ -0,0 +1,30 @@
+/* Web font defintions for the Ubuntu font.
+ *
+ * Retrieved from https://fonts.googleapis.com/css?family=Ubuntu:400,400italic,700,700italic
+ *
+ * But served from launchpad.net for performance and security reasons.
+ */
+@font-face {
+ font-family: 'Ubuntu';
+ font-style: italic;
+ font-weight: normal;
+ src: local('Ubuntu Italic'), local('Ubuntu-Italic'), url('https://themes.googleusercontent.com/static/fonts/ubuntu/v3/kbP_6ONYVgE-bLa9ZRbvvvesZW2xOQ-xsNqO47m55DA.woff') format('woff');
+}
+@font-face {
+ font-family: 'Ubuntu';
+ font-style: normal;
+ font-weight: bold;
+ src: local('Ubuntu Bold'), local('Ubuntu-Bold'), url('https://themes.googleusercontent.com/static/fonts/ubuntu/v3/0ihfXUL2emPh0ROJezvraD8E0i7KZn-EPnyo3HZu7kw.woff') format('woff');
+}
+@font-face {
+ font-family: 'Ubuntu';
+ font-style: italic;
+ font-weight: bold;
+ src: local('Ubuntu Bold Italic'), local('Ubuntu-BoldItalic'), url('https://themes.googleusercontent.com/static/fonts/ubuntu/v3/OMD20Sg9RTs7sUORCEN-7YbN6UDyHWBl620a-IRfuBk.woff') format('woff');
+}
+@font-face {
+ font-family: 'Ubuntu';
+ font-style: normal;
+ font-weight: normal;
+ src: local('Ubuntu'), url('https://themes.googleusercontent.com/static/fonts/ubuntu/v3/_xyN3apAT_yRRDeqB3sPRg.woff') format('woff');
+}
=== added directory 'lib/lp/contrib/javascript/google-analytics'
=== added file 'lib/lp/contrib/javascript/google-analytics/ga-disable-gaso.diff'
--- lib/lp/contrib/javascript/google-analytics/ga-disable-gaso.diff 1970-01-01 00:00:00 +0000
+++ lib/lp/contrib/javascript/google-analytics/ga-disable-gaso.diff 2011-12-11 01:49:34 +0000
@@ -0,0 +1,23 @@
+Diff that was applied to a copy of http://www.google-analytics.com/ga.js
+passed through http://http://jsbeautifier.org/ to disable
+the additional JS loading from Google Analytics when the Site Overlay
+feature is used.
+
+--- ga.js.orig 2011-12-08 15:37:38.000000000 -0500
++++ ga.js 2011-12-08 15:44:43.000000000 -0500
+@@ -1180,6 +1180,7 @@
+ return i
+ };
+ var zd, Ad = function (a) {
++ /* Disabled for security.
+ var f;
+ var e;
+ if (!zd) {
+@@ -1190,6 +1191,7 @@
+ if (f = (e = (b = b && b[ma](d) || c && c[ma](d)) ? b[1] : I(W("GASO")), b = e) && b[ma](/^(?:\|([-0-9a-z.]{1,40})\|)?([-.\w]{10,1200})$/i), c = f) if (cd(a, "GASO", "" + b), K._gasoDomain = a.get(Ma), K._gasoCPath = a.get(N), b = "https://" + ((c[1] || "www") + ".google.com") + "/analytics/reporting/overlay_js?gaso=" + c[2] + "&" + va()) a = J.createElement("script"), a.type = "text/javascript", a.async = h, a.src = b, a.id = "_gasojs", fa(a, g), b = J.getElementsByTagName("script")[0], b.parentNode.insertBefore(a, b);
+ zd = h
+ }
++ */
+ };
+ var sd = function (a, b, c) {
+ c && (b = H(b));
=== added file 'lib/lp/contrib/javascript/google-analytics/ga.js'
--- lib/lp/contrib/javascript/google-analytics/ga.js 1970-01-01 00:00:00 +0000
+++ lib/lp/contrib/javascript/google-analytics/ga.js 2011-12-11 01:49:34 +0000
@@ -0,0 +1,1819 @@
+// This file is Copyright (c) Google Inc.
+//
+// Google Analytics Tracker code.
+// Downloaded from http://www.google-analytics.com/ga.js
+// on 2011-12-01.
+//
+// Beautified through http://jsbeautifier.org/
+//
+// We are serving it as part of Launchpad for security reasons.
+// Modification from the original: disable the Site Overlay feature
+// which loads additional JS from Google.
+// See ga-disable-gaso.diff
+(function () {
+ var g = void 0,
+ h = true,
+ i = null,
+ j = false,
+ ba = encodeURIComponent,
+ ca = Infinity,
+ da = setTimeout,
+ ea = decodeURIComponent,
+ k = Math;
+
+ function fa(a, b) {
+ return a.onload = b
+ }
+ function ga(a, b) {
+ return a.name = b
+ }
+ var m = "push",
+ ha = "slice",
+ ia = "replace",
+ ja = "load",
+ ka = "floor",
+ n = "charAt",
+ la = "value",
+ p = "indexOf",
+ ma = "match",
+ r = "name",
+ oa = "host",
+ t = "toString",
+ u = "length",
+ v = "prototype",
+ w = "split",
+ pa = "stopPropagation",
+ qa = "scope",
+ x = "location",
+ y = "getString",
+ z = "substring",
+ ra = "navigator",
+ A = "join",
+ C = "toLowerCase",
+ D;
+
+ function sa(a, b) {
+ switch (b) {
+ case 0:
+ return "" + a;
+ case 1:
+ return a * 1;
+ case 2:
+ return !!a;
+ case 3:
+ return a * 1E3
+ }
+ return a
+ }
+ function E(a, b) {
+ return g == a || "-" == a && !b || "" == a
+ }
+ function ta(a) {
+ if (!a || "" == a) return "";
+ for (; a && " \n\r\t" [p](a[n](0)) > -1;) a = a[z](1);
+ for (; a && " \n\r\t" [p](a[n](a[u] - 1)) > -1;) a = a[z](0, a[u] - 1);
+ return a
+ }
+ function ua(a) {
+ var b = 1,
+ c = 0,
+ d;
+ if (!E(a)) {
+ b = 0;
+ for (d = a[u] - 1; d >= 0; d--) c = a.charCodeAt(d), b = (b << 6 & 268435455) + c + (c << 14), c = b & 266338304, b = c != 0 ? b ^ c >> 21 : b
+ }
+ return b
+ }
+
+ function va() {
+ return k.round(k.random() * 2147483647)
+ }
+ function wa() {}
+ function F(a, b) {
+ return ba instanceof Function ? b ? encodeURI(a) : ba(a) : (G(68), escape(a))
+ }
+ function H(a) {
+ a = a[w]("+")[A](" ");
+ if (ea instanceof Function) try {
+ return ea(a)
+ } catch (b) {
+ G(17)
+ } else G(68);
+ return unescape(a)
+ }
+ var xa = function (a, b, c, d) {
+ a.addEventListener ? a.addEventListener(b, c, !! d) : a.attachEvent && a.attachEvent("on" + b, c)
+ },
+ ya = function (a, b, c, d) {
+ a.removeEventListener ? a.removeEventListener(b, c, !! d) : a.detachEvent && a.detachEvent("on" + b, c)
+ };
+
+ function I(a) {
+ return a && a[u] > 0 ? a[0] : ""
+ }
+ function za(a) {
+ var b = a ? a[u] : 0;
+ return b > 0 ? a[b - 1] : ""
+ }
+ var Aa = function () {
+ this.prefix = "ga.";
+ this.I = {}
+ };
+ Aa[v].set = function (a, b) {
+ this.I[this.prefix + a] = b
+ };
+ Aa[v].get = function (a) {
+ return this.I[this.prefix + a]
+ };
+ Aa[v].contains = function (a) {
+ return this.get(a) !== g
+ };
+
+ function Ba(a) {
+ a[p]("www.") == 0 && (a = a[z](4));
+ return a[C]()
+ }
+ function Ca(a, b) {
+ var c, d = {
+ url: a,
+ protocol: "http",
+ host: "",
+ path: "",
+ c: new Aa,
+ anchor: ""
+ };
+ if (!a) return d;
+ c = a[p]("://");
+ if (c >= 0) d.protocol = a[z](0, c), a = a[z](c + 3);
+ c = a.search("/|\\?|#");
+ if (c >= 0) d.host = a[z](0, c)[C](), a = a[z](c);
+ else return d.host = a[C](), d;
+ c = a[p]("#");
+ if (c >= 0) d.anchor = a[z](c + 1), a = a[z](0, c);
+ c = a[p]("?");
+ c >= 0 && (Da(d.c, a[z](c + 1)), a = a[z](0, c));
+ d.anchor && b && Da(d.c, d.anchor);
+ a && a[n](0) == "/" && (a = a[z](1));
+ d.path = a;
+ return d
+ }
+
+ function Da(a, b) {
+ function c(b, c) {
+ a.contains(b) || a.set(b, []);
+ a.get(b)[m](c)
+ }
+ for (var d = ta(b)[w]("&"), e = 0; e < d[u]; e++) if (d[e]) {
+ var f = d[e][p]("=");
+ f < 0 ? c(d[e], "1") : c(d[e][z](0, f), d[e][z](f + 1))
+ }
+ }
+ function Ea(a, b) {
+ if (E(a)) return "-";
+ if ("[" == a[n](0) && "]" == a[n](a[u] - 1)) return "-";
+ var c = J.domain;
+ c += b && b != "/" ? b : "";
+ return a[p](c) == (a[p]("http://") == 0 ? 7 : a[p]("https://") == 0 ? 8 : 0) ? "0" : a
+ };
+
+ function Fa(a, b, c) {
+ k.random() * 100 >= 1 || (a = ["utmt=error", "utmerr=" + a, "utmwv=5.2.2", "utmn=" + va(), "utmsp=1"], b && a[m]("api=" + b), c && a[m]("msg=" + F(c[z](0, 100))), K.q && a[m]("aip=1"), Ga(a[A]("&")))
+ };
+ var Ha = 0;
+
+ function L(a) {
+ return (a ? "_" : "") + Ha++
+ }
+ var Ia = L(),
+ Ja = L(),
+ Ka = L(),
+ La = L(),
+ Ma = L(),
+ M = L(),
+ N = L(),
+ Na = L(),
+ Oa = L(),
+ Pa = L(),
+ Qa = L(),
+ Ra = L(),
+ Sa = L(),
+ Ta = L(),
+ Ua = L(),
+ Va = L(),
+ Wa = L(),
+ Xa = L(),
+ Ya = L(),
+ Za = L(),
+ $a = L(),
+ ab = L(),
+ bb = L(),
+ cb = L(),
+ db = L(),
+ eb = L(),
+ fb = L(),
+ gb = L(),
+ hb = L(),
+ ib = L(),
+ jb = L(),
+ kb = L(),
+ lb = L(),
+ mb = L(),
+ nb = L(),
+ O = L(h),
+ ob = L(),
+ pb = L(),
+ qb = L(),
+ rb = L(),
+ sb = L(),
+ tb = L(),
+ ub = L(),
+ vb = L(),
+ wb = L(),
+ xb = L(),
+ P = L(h),
+ yb = L(h),
+ zb = L(h),
+ Bb = L(h),
+ Cb = L(h),
+ Db = L(h),
+ Eb = L(h),
+ Fb = L(h),
+ Gb = L(h),
+ Hb = L(h),
+ Ib = L(h),
+ Q = L(h),
+ Jb = L(h),
+ Kb = L(h),
+ Lb = L(h),
+ Mb = L(h),
+ Nb = L(h),
+ Ob = L(h),
+ Pb = L(h),
+ Qb = L(h),
+ Rb = L(h),
+ Sb = L(h),
+ Tb = L(h),
+ Ub = L(h),
+ Vb = L(h),
+ Wb = L(),
+ Xb = L(),
+ Yb = L();
+ L();
+ var Zb = L(),
+ $b = L(),
+ ac = L(),
+ bc = L(),
+ cc = L(),
+ dc = L(),
+ ec = L(),
+ hc = L(),
+ ic = L(),
+ jc = L();
+ L();
+ var kc = L(),
+ lc = L();
+ var mc = function () {
+ function a(a, c, d) {
+ R(S[v], a, c, d)
+ }
+ T("_getName", Ka, 58);
+ T("_getAccount", Ia, 64);
+ T("_visitCode", P, 54);
+ T("_getClientInfo", Ta, 53, 1);
+ T("_getDetectTitle", Wa, 56, 1);
+ T("_getDetectFlash", Ua, 65, 1);
+ T("_getLocalGifPath", fb, 57);
+ T("_getServiceMode", gb, 59);
+ U("_setClientInfo", Ta, 66, 2);
+ U("_setAccount", Ia, 3);
+ U("_setNamespace", Ja, 48);
+ U("_setAllowLinker", Qa, 11, 2);
+ U("_setDetectFlash", Ua, 61, 2);
+ U("_setDetectTitle", Wa, 62, 2);
+ U("_setLocalGifPath", fb, 46, 0);
+ U("_setLocalServerMode", gb, 92, g, 0);
+ U("_setRemoteServerMode", gb, 63, g, 1);
+ U("_setLocalRemoteServerMode", gb, 47, g, 2);
+ U("_setSampleRate", eb, 45, 1);
+ U("_setCampaignTrack", Va, 36, 2);
+ U("_setAllowAnchor", Ra, 7, 2);
+ U("_setCampNameKey", Ya, 41);
+ U("_setCampContentKey", cb, 38);
+ U("_setCampIdKey", Xa, 39);
+ U("_setCampMediumKey", ab, 40);
+ U("_setCampNOKey", db, 42);
+ U("_setCampSourceKey", $a, 43);
+ U("_setCampTermKey", bb, 44);
+ U("_setCampCIdKey", Za, 37);
+ U("_setCookiePath", N, 9, 0);
+ U("_setMaxCustomVariables", hb, 0, 1);
+ U("_setVisitorCookieTimeout", Na, 28, 1);
+ U("_setSessionCookieTimeout", Oa, 26, 1);
+ U("_setCampaignCookieTimeout", Pa, 29, 1);
+ U("_setReferrerOverride", qb, 49);
+ U("_setSiteSpeedSampleRate", ic, 132);
+ a("_trackPageview", S[v].na, 1);
+ a("_trackEvent", S[v].v, 4);
+ a("_trackPageLoadTime", S[v].ma, 100);
+ a("_trackSocial", S[v].oa, 104);
+ a("_trackTrans", S[v].pa, 18);
+ a("_sendXEvent", S[v].u, 78);
+ a("_createEventTracker", S[v].V, 74);
+ a("_getVersion", S[v].$, 60);
+ a("_setDomainName", S[v].t, 6);
+ a("_setAllowHash", S[v].ea, 8);
+ a("_getLinkerUrl", S[v].Z, 52);
+ a("_link", S[v].link, 101);
+ a("_linkByPost", S[v].da, 102);
+ a("_setTrans", S[v].ha, 20);
+ a("_addTrans", S[v].O, 21);
+ a("_addItem", S[v].M, 19);
+ a("_setTransactionDelim", S[v].ia, 82);
+ a("_setCustomVar", S[v].fa, 10);
+ a("_deleteCustomVar", S[v].X, 35);
+ a("_getVisitorCustomVar", S[v].aa, 50);
+ a("_setXKey", S[v].ka, 83);
+ a("_setXValue", S[v].la, 84);
+ a("_getXKey", S[v].ba, 76);
+ a("_getXValue", S[v].ca, 77);
+ a("_clearXKey", S[v].S, 72);
+ a("_clearXValue", S[v].T, 73);
+ a("_createXObj", S[v].W, 75);
+ a("_addIgnoredOrganic", S[v].K, 15);
+ a("_clearIgnoredOrganic", S[v].P, 97);
+ a("_addIgnoredRef", S[v].L, 31);
+ a("_clearIgnoredRef", S[v].Q, 32);
+ a("_addOrganic", S[v].N, 14);
+ a("_clearOrganic", S[v].R, 70);
+ a("_cookiePathCopy", S[v].U, 30);
+ a("_get", S[v].Y, 106);
+ a("_set", S[v].ga, 107);
+ a("_addEventListener", S[v].addEventListener, 108);
+ a("_removeEventListener", S[v].removeEventListener, 109);
+ a("_initData", S[v].m, 2);
+ a("_setVar", S[v].ja, 22);
+ U("_setSessionTimeout", Oa, 27, 3);
+ U("_setCookieTimeout", Pa, 25, 3);
+ U("_setCookiePersistence", Na, 24, 1);
+ a("_setAutoTrackOutbound", wa, 79);
+ a("_setTrackOutboundSubdomains", wa, 81);
+ a("_setHrefExamineLimit", wa, 80)
+ },
+ R = function (a, b, c, d) {
+ a[b] = function () {
+ try {
+ return G(d), c.apply(this, arguments)
+ } catch (a) {
+ throw Fa("exc", b, a && a[r]), a;
+ }
+ }
+ },
+ T = function (a, b, c, d) {
+ S[v][a] = function () {
+ try {
+ return G(c), sa(this.a.get(b), d)
+ } catch (e) {
+ throw Fa("exc", a, e && e[r]), e;
+ }
+ }
+ },
+ U = function (a, b, c, d, e) {
+ S[v][a] = function (f) {
+ try {
+ G(c), e == g ? this.a.set(b, sa(f, d)) : this.a.set(b, e)
+ } catch (l) {
+ throw Fa("exc", a, l && l[r]), l;
+ }
+ }
+ },
+ nc = function (a, b) {
+ return {
+ type: b,
+ target: a,
+ stopPropagation: function () {
+ throw "aborted";
+ }
+ }
+ };
+ var oc = function (a, b) {
+ return b !== "/" ? j : (a[p]("www.google.") == 0 || a[p](".google.") == 0 || a[p]("google.") == 0) && !(a[p]("google.org") > -1) ? h : j
+ },
+ pc = function (a) {
+ var b = a.get(Ma),
+ c = a[y](N, "/");
+ oc(b, c) && a[pa]()
+ };
+ var uc = function () {
+ var a = {},
+ b = {},
+ c = new qc;
+ this.g = function (a, b) {
+ c.add(a, b)
+ };
+ var d = new qc;
+ this.d = function (a, b) {
+ d.add(a, b)
+ };
+ var e = j,
+ f = j,
+ l = h;
+ this.J = function () {
+ e = h
+ };
+ this.f = function (a) {
+ this[ja]();
+ this.set(Wb, a, h);
+ a = new rc(this);
+ e = j;
+ d.execute(this);
+ e = h;
+ b = {};
+ this.i();
+ a.qa()
+ };
+ this.load = function () {
+ e && (e = j, this.sa(), sc(this), f || (f = h, c.execute(this), tc(this), sc(this)), e = h)
+ };
+ this.i = function () {
+ if (e) if (f) e = j, tc(this), e = h;
+ else this[ja]()
+ };
+ this.get = function (c) {
+ c && c[n](0) == "_" && this[ja]();
+ return b[c] !== g ? b[c] : a[c]
+ };
+ this.set = function (c, d, e) {
+ c && c[n](0) == "_" && this[ja]();
+ e ? b[c] = d : a[c] = d;
+ c && c[n](0) == "_" && this.i()
+ };
+ this.n = function (b) {
+ a[b] = this.b(b, 0) + 1
+ };
+ this.b = function (a, b) {
+ var c = this.get(a);
+ return c == g || c === "" ? b : c * 1
+ };
+ this.getString = function (a, b) {
+ var c = this.get(a);
+ return c == g ? b : c + ""
+ };
+ this.sa = function () {
+ if (l) {
+ var b = this[y](Ma, ""),
+ c = this[y](N, "/");
+ oc(b, c) || (a[M] = a[Sa] && b != "" ? ua(b) : 1, l = j)
+ }
+ }
+ };
+ uc[v].stopPropagation = function () {
+ throw "aborted";
+ };
+ var rc = function (a) {
+ var b = this;
+ this.j = 0;
+ var c = a.get(Xb);
+ this.Aa = function () {
+ b.j > 0 && c && (b.j--, b.j || c())
+ };
+ this.qa = function () {
+ !b.j && c && da(c, 0)
+ };
+ a.set(Yb, b, h)
+ };
+
+ function vc(a, b) {
+ for (var b = b || [], c = 0; c < b[u]; c++) {
+ var d = b[c];
+ if ("" + a == d || d[p](a + ".") == 0) return d
+ }
+ return "-"
+ }
+ var xc = function (a, b, c) {
+ c = c ? "" : a[y](M, "1");
+ b = b[w](".");
+ if (b[u] !== 6 || wc(b[0], c)) return j;
+ var c = b[1] * 1,
+ d = b[2] * 1,
+ e = b[3] * 1,
+ f = b[4] * 1,
+ b = b[5] * 1;
+ if (!(c >= 0 && d > 0 && e > 0 && f > 0 && b >= 0)) return G(110), j;
+ a.set(P, c);
+ a.set(Cb, d);
+ a.set(Db, e);
+ a.set(Eb, f);
+ a.set(Fb, b);
+ return h
+ },
+ yc = function (a) {
+ var b = a.get(P),
+ c = a.get(Cb),
+ d = a.get(Db),
+ e = a.get(Eb),
+ f = a.b(Fb, 1);
+ b == g ? G(113) : b == NaN && G(114);
+ b >= 0 && c > 0 && d > 0 && e > 0 && f >= 0 || G(115);
+ return [a.b(M, 1), b != g ? b : "-", c || "-", d || "-", e || "-", f][A](".")
+ },
+ zc = function (a) {
+ return [a.b(M, 1), a.b(Ib, 0), a.b(Q, 1), a.b(Jb, 0)][A](".")
+ },
+ Ac = function (a, b, c) {
+ var c = c ? "" : a[y](M, "1"),
+ d = b[w](".");
+ if (d[u] !== 4 || wc(d[0], c)) d = i;
+ a.set(Ib, d ? d[1] * 1 : 0);
+ a.set(Q, d ? d[2] * 1 : 10);
+ a.set(Jb, d ? d[3] * 1 : a.get(La));
+ return d != i || !wc(b, c)
+ },
+ Bc = function (a, b) {
+ var c = F(a[y](zb, "")),
+ d = [],
+ e = a.get(O);
+ if (!b && e) {
+ for (var f = 0; f < e[u]; f++) {
+ var l = e[f];
+ l && l[qa] == 1 && d[m](f + "=" + F(l[r]) + "=" + F(l[la]) + "=1")
+ }
+ d[u] > 0 && (c += "|" + d[A](","))
+ }
+ return c ? a.b(M, 1) + "." + c : i
+ },
+ Cc = function (a, b, c) {
+ c = c ? "" : a[y](M, "1");
+ b = b[w](".");
+ if (b[u] < 2 || wc(b[0], c)) return j;
+ b = b[ha](1)[A](".")[w]("|");
+ b[u] > 0 && a.set(zb, H(b[0]));
+ if (b[u] <= 1) return h;
+ for (var c = b[1][w](b[1][p](",") == -1 ? "^" : ","), d = 0; d < c[u]; d++) {
+ var e = c[d][w]("=");
+ if (e[u] == 4) {
+ var f = {};
+ ga(f, H(e[1]));
+ f.value = H(e[2]);
+ f.scope = 1;
+ a.get(O)[e[0]] = f
+ }
+ }
+ b[1][p]("^") >= 0 && G(125);
+ return h
+ },
+ Ec = function (a, b) {
+ var c = Dc(a, b);
+ return c ? [a.b(M, 1), a.b(Kb, 0), a.b(Lb, 1), a.b(Mb, 1), c][A](".") : ""
+ },
+ Dc = function (a) {
+ function b(b, e) {
+ if (!E(a.get(b))) {
+ var f = a[y](b, ""),
+ f = f[w](" ")[A]("%20"),
+ f = f[w]("+")[A]("%20");
+ c[m](e + "=" + f)
+ }
+ }
+ var c = [];
+ b(Ob, "utmcid");
+ b(Sb, "utmcsr");
+ b(Qb, "utmgclid");
+ b(Rb, "utmdclid");
+ b(Pb, "utmccn");
+ b(Tb, "utmcmd");
+ b(Ub, "utmctr");
+ b(Vb, "utmcct");
+ return c[A]("|")
+ },
+ Gc = function (a, b, c) {
+ c = c ? "" : a[y](M, "1");
+ b = b[w](".");
+ if (b[u] < 5 || wc(b[0], c)) return a.set(Kb, g), a.set(Lb, g), a.set(Mb, g), a.set(Ob, g), a.set(Pb, g), a.set(Sb, g), a.set(Tb, g), a.set(Ub, g), a.set(Vb, g), a.set(Qb, g), a.set(Rb, g), j;
+ a.set(Kb, b[1] * 1);
+ a.set(Lb, b[2] * 1);
+ a.set(Mb, b[3] * 1);
+ Fc(a, b[ha](4)[A]("."));
+ return h
+ },
+ Fc = function (a, b) {
+ function c(a) {
+ return (a = b[ma](a + "=(.*?)(?:\\|utm|$)")) && a[u] == 2 ? a[1] : g
+ }
+ function d(b, c) {
+ c && (c = e ? H(c) : c[w]("%20")[A](" "), a.set(b, c))
+ }
+ b[p]("=") == -1 && (b = H(b));
+ var e = c("utmcvr") == "2";
+ d(Ob, c("utmcid"));
+ d(Pb, c("utmccn"));
+ d(Sb, c("utmcsr"));
+ d(Tb, c("utmcmd"));
+ d(Ub, c("utmctr"));
+ d(Vb, c("utmcct"));
+ d(Qb, c("utmgclid"));
+ d(Rb, c("utmdclid"))
+ },
+ wc = function (a, b) {
+ return b ? a != b : !/^\d+$/.test(a)
+ };
+ var qc = function () {
+ this.s = []
+ };
+ qc[v].add = function (a, b) {
+ this.s[m]({
+ name: a,
+ Da: b
+ })
+ };
+ qc[v].execute = function (a) {
+ try {
+ for (var b = 0; b < this.s[u]; b++) this.s[b].Da.call(V, a)
+ } catch (c) {}
+ };
+
+ function Hc(a) {
+ a.get(eb) != 100 && a.get(P) % 1E4 >= a.get(eb) * 100 && a[pa]()
+ }
+ function Ic(a) {
+ Jc() && a[pa]()
+ }
+ function Kc(a) {
+ J[x].protocol == "file:" && a[pa]()
+ }
+ function Lc(a) {
+ a.get(pb) || a.set(pb, J.title, h);
+ a.get(ob) || a.set(ob, J[x].pathname + J[x].search, h)
+ };
+ var Mc = new function () {
+ var a = [];
+ this.set = function (b) {
+ a[b] = h
+ };
+ this.Ea = function () {
+ for (var b = [], c = 0; c < a[u]; c++) a[c] && (b[k[ka](c / 6)] ^= 1 << c % 6);
+ for (c = 0; c < b[u]; c++) b[c] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" [n](b[c] || 0);
+ return b[A]("") + "~"
+ }
+ };
+
+ function G(a) {
+ Mc.set(a)
+ };
+ var V = window,
+ J = document,
+ Jc = function () {
+ var a = V._gaUserPrefs;
+ return a && a.ioo && a.ioo()
+ },
+ Nc = function (a, b) {
+ da(a, b)
+ },
+ W = function (a) {
+ for (var b = [], c = J.cookie[w](";"), a = RegExp("^\\s*" + a + "=\\s*(.*?)\\s*$"), d = 0; d < c[u]; d++) {
+ var e = c[d][ma](a);
+ e && b[m](e[1])
+ }
+ return b
+ },
+ X = function (a, b, c, d, e) {
+ var f;
+ f = Jc() ? j : oc(d, c) ? j : h;
+ if (f) {
+ if (b && V[ra].userAgent[p]("Firefox") >= 0) {
+ b = b[ia](/\n|\r/g, " ");
+ f = 0;
+ for (var l = b[u]; f < l; ++f) {
+ var o = b.charCodeAt(f) & 255;
+ if (o == 10 || o == 13) b = b[z](0, f) + "?" + b[z](f + 1)
+ }
+ }
+ b && b[u] > 2E3 && (b = b[z](0, 2E3), G(69));
+ a = a + "=" + b + "; path=" + c + "; ";
+ e && (a += "expires=" + (new Date((new Date).getTime() + e)).toGMTString() + "; ");
+ d && (a += "domain=" + d + ";");
+ J.cookie = a
+ }
+ };
+ var Oc, Pc, Qc = function () {
+ if (!Oc) {
+ var a = {},
+ b = V[ra],
+ c = V.screen;
+ a.H = c ? c.width + "x" + c.height : "-";
+ a.G = c ? c.colorDepth + "-bit" : "-";
+ a.language = (b && (b.language || b.browserLanguage) || "-")[C]();
+ a.javaEnabled = b && b.javaEnabled() ? 1 : 0;
+ a.characterSet = J.characterSet || J.charset || "-";
+ Oc = a
+ }
+ },
+ Rc = function () {
+ Qc();
+ for (var a = Oc, b = V[ra], a = b.appName + b.version + a.language + b.platform + b.userAgent + a.javaEnabled + a.H + a.G + (J.cookie ? J.cookie : "") + (J.referrer ? J.referrer : ""), b = a[u], c = V.history[u]; c > 0;) a += c-- ^ b++;
+ return ua(a)
+ },
+ Sc = function (a) {
+ Qc();
+ var b = Oc;
+ a.set(sb, b.H);
+ a.set(tb, b.G);
+ a.set(wb, b.language);
+ a.set(xb, b.characterSet);
+ a.set(ub, b.javaEnabled);
+ if (a.get(Ta) && a.get(Ua)) {
+ if (!(b = Pc)) {
+ var c, d, e;
+ d = "ShockwaveFlash";
+ if ((b = (b = V[ra]) ? b.plugins : g) && b[u] > 0) for (c = 0; c < b[u] && !e; c++) d = b[c], d[r][p]("Shockwave Flash") > -1 && (e = d.description[w]("Shockwave Flash ")[1]);
+ else {
+ d = d + "." + d;
+ try {
+ c = new ActiveXObject(d + ".7"), e = c.GetVariable("$version")
+ } catch (f) {}
+ if (!e) try {
+ c = new ActiveXObject(d + ".6"), e = "WIN 6,0,21,0", c.AllowScriptAccess = "always", e = c.GetVariable("$version")
+ } catch (l) {}
+ if (!e) try {
+ c = new ActiveXObject(d), e = c.GetVariable("$version")
+ } catch (o) {}
+ e && (e = e[w](" ")[1][w](","), e = e[0] + "." + e[1] + " r" + e[2])
+ }
+ b = e ? e : "-"
+ }
+ Pc = b;
+ a.set(vb, Pc)
+ } else a.set(vb, "-")
+ };
+ var Y = function () {
+ R(Y[v], "push", Y[v][m], 5);
+ R(Y[v], "_createAsyncTracker", Y[v].Ba, 33);
+ R(Y[v], "_getAsyncTracker", Y[v].Ca, 34);
+ this.r = 0
+ };
+ Y[v].Ba = function (a, b) {
+ return K.l(a, b || "")
+ };
+ Y[v].Ca = function (a) {
+ return K.p(a)
+ };
+ Y[v].push = function (a) {
+ this.r > 0 && G(105);
+ this.r++;
+ for (var b = arguments, c = 0, d = 0; d < b[u]; d++) try {
+ if (typeof b[d] === "function") b[d]();
+ else {
+ var e = "",
+ f = b[d][0],
+ l = f.lastIndexOf(".");
+ l > 0 && (e = f[z](0, l), f = f[z](l + 1));
+ var o = e == "_gat" ? K : e == "_gaq" ? Tc : K.p(e);
+ o[f].apply(o, b[d][ha](1))
+ }
+ } catch (q) {
+ c++
+ }
+ this.r--;
+ return c
+ };
+ var Yc = function () {
+ function a(a, b, c, d) {
+ g == f[a] && (f[a] = {});
+ g == f[a][b] && (f[a][b] = []);
+ f[a][b][c] = d
+ }
+ function b(a, b, c) {
+ if (g != f[a] && g != f[a][b]) return f[a][b][c]
+ }
+ function c(a, b) {
+ if (g != f[a] && g != f[a][b]) {
+ f[a][b] = g;
+ var c = h,
+ d;
+ for (d = 0; d < l[u]; d++) if (g != f[a][l[d]]) {
+ c = j;
+ break
+ }
+ c && (f[a] = g)
+ }
+ }
+ function d(a) {
+ var b = "",
+ c = j,
+ d, e;
+ for (d = 0; d < l[u]; d++) if (e = a[l[d]], g != e) {
+ c && (b += l[d]);
+ for (var c = [], f = g, $ = g, $ = 0; $ < e[u]; $++) if (g != e[$]) {
+ f = "";
+ $ != aa && g == e[$ - 1] && (f += $[t]() + na);
+ for (var Wc = e[$], Xc = "", Ab = g, fc = g, gc = g, Ab = 0; Ab < Wc[u]; Ab++) fc = Wc[n](Ab), gc = B[fc], Xc += g != gc ? gc : fc;
+ f += Xc;
+ c[m](f)
+ }
+ b += o + c[A](s) + q;
+ c = j
+ } else c = h;
+ return b
+ }
+ var e = this,
+ f = [],
+ l = ["k", "v"],
+ o = "(",
+ q = ")",
+ s = "*",
+ na = "!",
+ B = {
+ "'": "'0"
+ };
+ B[q] = "'1";
+ B[s] = "'2";
+ B[na] = "'3";
+ var aa = 1;
+ e.va = function (a) {
+ return g != f[a]
+ };
+ e.o = function () {
+ for (var a = "", b = 0; b < f[u]; b++) g != f[b] && (a += b[t]() + d(f[b]));
+ return a
+ };
+ e.ua = function (a) {
+ if (a == g) return e.o();
+ for (var b = a.o(), c = 0; c < f[u]; c++) g != f[c] && !a.va(c) && (b += c[t]() + d(f[c]));
+ return b
+ };
+ e.e = function (b, c, d) {
+ if (!Uc(d)) return j;
+ a(b, "k", c, d);
+ return h
+ };
+ e.k = function (b, c, d) {
+ if (!Vc(d)) return j;
+ a(b, "v", c, d[t]());
+ return h
+ };
+ e.getKey = function (a, c) {
+ return b(a, "k", c)
+ };
+ e.C = function (a, c) {
+ return b(a, "v", c)
+ };
+ e.A = function (a) {
+ c(a, "k")
+ };
+ e.B = function (a) {
+ c(a, "v")
+ };
+ R(e, "_setKey", e.e, 89);
+ R(e, "_setValue", e.k, 90);
+ R(e, "_getKey", e.getKey, 87);
+ R(e, "_getValue", e.C, 88);
+ R(e, "_clearKey", e.A, 85);
+ R(e, "_clearValue", e.B, 86)
+ };
+
+ function Uc(a) {
+ return typeof a == "string"
+ }
+ function Vc(a) {
+ return typeof a != "number" && (g == Number || !(a instanceof Number)) || k.round(a) != a || a == NaN || a == ca ? j : h
+ };
+ var Zc = function (a) {
+ var b = V.gaGlobal;
+ a && !b && (V.gaGlobal = b = {});
+ return b
+ },
+ $c = function () {
+ var a = Zc(h).hid;
+ if (a == i) a = va(), Zc(h).hid = a;
+ return a
+ },
+ ad = function (a) {
+ a.set(rb, $c());
+ var b = Zc();
+ if (b && b.dh == a.get(M)) {
+ var c = b.sid;
+ c && (c == "0" && G(112), a.set(Eb, c), a.get(yb) && a.set(Db, c));
+ b = b.vid;
+ a.get(yb) && b && (b = b[w]("."), b[1] * 1 || G(112), a.set(P, b[0] * 1), a.set(Cb, b[1] * 1))
+ }
+ };
+ var bd, cd = function (a, b, c) {
+ var d = a[y](Ma, ""),
+ e = a[y](N, "/"),
+ a = a.b(Na, 0);
+ X(b, c, e, d, a)
+ },
+ tc = function (a) {
+ var b = a[y](Ma, "");
+ a.b(M, 1);
+ var c = a[y](N, "/");
+ X("__utma", yc(a), c, b, a.get(Na));
+ X("__utmb", zc(a), c, b, a.get(Oa));
+ X("__utmc", "" + a.b(M, 1), c, b);
+ var d = Ec(a, h);
+ d ? X("__utmz", d, c, b, a.get(Pa)) : X("__utmz", "", c, b, -1);
+ (d = Bc(a, j)) ? X("__utmv", d, c, b, a.get(Na)) : X("__utmv", "", c, b, -1)
+ },
+ sc = function (a) {
+ var b = a.b(M, 1);
+ if (!xc(a, vc(b, W("__utma")))) return a.set(Bb, h), j;
+ var c = !Ac(a, vc(b, W("__utmb")));
+ a.set(Hb, c);
+ Gc(a, vc(b, W("__utmz")));
+ Cc(a, vc(b, W("__utmv")));
+ bd = !c;
+ return h
+ },
+ dd = function (a) {
+ !bd && !(W("__utmb")[u] > 0) && (X("__utmd", "1", a[y](N, "/"), a[y](Ma, ""), 1E4), W("__utmd")[u] == 0 && a[pa]())
+ };
+ var gd = function (a) {
+ a.get(P) == g ? ed(a) : a.get(Bb) && !a.get(kc) ? ed(a) : a.get(Hb) && fd(a)
+ },
+ hd = function (a) {
+ a.get(Nb) && !a.get(Gb) && (fd(a), a.set(Lb, a.get(Fb)))
+ },
+ ed = function (a) {
+ var b = a.get(La);
+ a.set(yb, h);
+ a.set(P, va() ^ Rc(a) & 2147483647);
+ a.set(zb, "");
+ a.set(Cb, b);
+ a.set(Db, b);
+ a.set(Eb, b);
+ a.set(Fb, 1);
+ a.set(Gb, h);
+ a.set(Ib, 0);
+ a.set(Q, 10);
+ a.set(Jb, b);
+ a.set(O, []);
+ a.set(Bb, j);
+ a.set(Hb, j)
+ },
+ fd = function (a) {
+ a.set(Db, a.get(Eb));
+ a.set(Eb, a.get(La));
+ a.n(Fb);
+ a.set(Gb, h);
+ a.set(Ib, 0);
+ a.set(Q, 10);
+ a.set(Jb, a.get(La));
+ a.set(Hb, j)
+ };
+ var id = "daum:q,eniro:search_word,naver:query,pchome:q,images.google:q,google:q,yahoo:p,yahoo:q,msn:q,bing:q,aol:query,aol:q,lycos:query,ask:q,netscape:query,cnn:query,about:terms,mamma:q,voila:rdata,virgilio:qs,live:q,baidu:wd,alice:qs,yandex:text,najdi:q,seznam:q,search:q,wp:szukaj,onet:qt,szukacz:q,yam:k,kvasir:q,ozu:q,terra:query,rambler:query".split(","),
+ od = function (a) {
+ if (a.get(Va) && !a.get(kc)) {
+ for (var b = !E(a.get(Ob)) || !E(a.get(Sb)) || !E(a.get(Qb)) || !E(a.get(Rb)), c = {}, d = 0; d < jd[u]; d++) {
+ var e = jd[d];
+ c[e] = a.get(e)
+ }
+ d = Ca(J[x].href, a.get(Ra));
+ if (!(za(d.c.get(a.get(db))) == "1" && b) && (d = kd(a, d) || ld(a), !d && !b && a.get(Gb) && (md(a, g, "(direct)", g, g, "(direct)", "(none)", g, g), d = h), d && (a.set(Nb, nd(a, c)), b = a.get(Sb) == "(direct)" && a.get(Pb) == "(direct)" && a.get(Tb) == "(none)", a.get(Nb) || a.get(Gb) && !b))) a.set(Kb, a.get(La)), a.set(Lb, a.get(Fb)), a.n(Mb)
+ }
+ },
+ kd = function (a, b) {
+ function c(c, d) {
+ var d = d || "-",
+ e = za(b.c.get(a.get(c)));
+ return e && e != "-" ? H(e) : d
+ }
+ var d = za(b.c.get(a.get(Xa))) || "-",
+ e = za(b.c.get(a.get($a))) || "-",
+ f = za(b.c.get(a.get(Za))) || "-",
+ l = za(b.c.get("dclid")) || "-",
+ o = c(Ya, "(not set)"),
+ q = c(ab, "(not set)"),
+ s = c(bb),
+ na = c(cb);
+ if (E(d) && E(f) && E(l) && E(e)) return j;
+ if (E(s)) {
+ var B = Ea(a.get(qb), a.get(N)),
+ B = Ca(B, h);
+ (B = pd(a, B)) && !E(B[1] && !B[2]) && (s = B[1])
+ }
+ md(a, d, e, f, l, o, q, s, na);
+ return h
+ },
+ ld = function (a) {
+ var b = Ea(a.get(qb), a.get(N)),
+ c = Ca(b, h);
+ if (!(b != g && b != i && b != "" && b != "0" && b != "-" && b[p]("://") >= 0) || c && c[oa][p]("google") > -1 && c.c.contains("q") && c.path == "cse") return j;
+ if ((b = pd(a, c)) && !b[2]) return md(a, g, b[0], g, g, "(organic)", "organic", b[1], g), h;
+ else if (b) return j;
+ if (a.get(Gb)) a: {
+ for (var b = a.get(kb), d = Ba(c[oa]), e = 0; e < b[u]; ++e) if (d[p](b[e]) > -1) {
+ a = j;
+ break a
+ }
+ md(a, g, d, g, g, "(referral)", "referral", g, "/" + c.path);
+ a = h
+ } else a = j;
+ return a
+ },
+ pd = function (a, b) {
+ for (var c = a.get(ib), d = 0; d < c[u]; ++d) {
+ var e = c[d][w](":");
+ if (b[oa][p](e[0][C]()) > -1) {
+ var f = b.c.get(e[1]);
+ if (f && (f = I(f), !f && b[oa][p]("google.") > -1 && (f = "(not provided)"), !e[3] || b.url[p](e[3]) > -1)) {
+ a: {
+ for (var c = f, d = a.get(jb), c = H(c)[C](), l = 0; l < d[u]; ++l) if (c == d[l]) {
+ c = h;
+ break a
+ }
+ c = j
+ }
+ return [e[2] || e[0], f, c]
+ }
+ }
+ }
+ return i
+ },
+ md = function (a, b, c, d, e, f, l, o, q) {
+ a.set(Ob, b);
+ a.set(Sb, c);
+ a.set(Qb, d);
+ a.set(Rb, e);
+ a.set(Pb, f);
+ a.set(Tb, l);
+ a.set(Ub, o);
+ a.set(Vb, q)
+ },
+ jd = [Pb, Ob, Qb, Rb, Sb, Tb, Ub, Vb],
+ nd = function (a, b) {
+ function c(a) {
+ a = ("" + a)[w]("+")[A]("%20");
+ return a = a[w](" ")[A]("%20")
+ }
+ function d(c) {
+ var d = "" + (a.get(c) || ""),
+ c = "" + (b[c] || "");
+ return d[u] > 0 && d == c
+ }
+ if (d(Qb) || d(Rb)) return G(131), j;
+ for (var e = 0; e < jd[u]; e++) {
+ var f = jd[e],
+ l = b[f] || "-",
+ f = a.get(f) || "-";
+ if (c(l) != c(f)) return h
+ }
+ return j
+ };
+ var rd = function (a) {
+ qd(a, J[x].href) ? (a.set(kc, h), G(12)) : a.set(kc, j)
+ },
+ qd = function (a, b) {
+ if (!a.get(Qa)) return j;
+ var c = Ca(b, a.get(Ra)),
+ d = I(c.c.get("__utma")),
+ e = I(c.c.get("__utmb")),
+ f = I(c.c.get("__utmc")),
+ l = I(c.c.get("__utmx")),
+ o = I(c.c.get("__utmz")),
+ q = I(c.c.get("__utmv")),
+ c = I(c.c.get("__utmk"));
+ if (ua("" + d + e + f + l + o + q) != c) {
+ d = H(d);
+ e = H(e);
+ f = H(f);
+ l = H(l);
+ a: {
+ for (var f = d + e + f + l, s = 0; s < 3; s++) {
+ for (var na = 0; na < 3; na++) {
+ if (c == ua(f + o + q)) {
+ G(127);
+ c = [o, q];
+ break a
+ }
+ var B = o[ia](/ /g, "%20"),
+ aa = q[ia](/ /g, "%20");
+ if (c == ua(f + B + aa)) {
+ G(128);
+ c = [B, aa];
+ break a
+ }
+ B = B[ia](/\+/g, "%20");
+ aa = aa[ia](/\+/g, "%20");
+ if (c == ua(f + B + aa)) {
+ G(129);
+ c = [B, aa];
+ break a
+ }
+ o = H(o)
+ }
+ q = H(q)
+ }
+ c = g
+ }
+ if (!c) return j;
+ o = c[0];
+ q = c[1]
+ }
+ if (!xc(a, d, h)) return j;
+ Ac(a, e, h);
+ Gc(a, o, h);
+ Cc(a, q, h);
+ sd(a, l, h);
+ return h
+ },
+ ud = function (a, b, c) {
+ var d;
+ d = yc(a) || "-";
+ var e = zc(a) || "-",
+ f = "" + a.b(M, 1) || "-",
+ l = td(a) || "-",
+ o = Ec(a, j) || "-",
+ a = Bc(a, j) || "-",
+ q = ua("" + d + e + f + l + o + a),
+ s = [];
+ s[m]("__utma=" + d);
+ s[m]("__utmb=" + e);
+ s[m]("__utmc=" + f);
+ s[m]("__utmx=" + l);
+ s[m]("__utmz=" + o);
+ s[m]("__utmv=" + a);
+ s[m]("__utmk=" + q);
+ d = s[A]("&");
+ if (!d) return b;
+ e = b[p]("#");
+ return c ? e < 0 ? b + "#" + d : b + "&" + d : (c = "", f = b[p]("?"), e > 0 && (c = b[z](e), b = b[z](0, e)), f < 0 ? b + "?" + d + c : b + "&" + d + c)
+ };
+ var vd = "|",
+ xd = function (a, b, c, d, e, f, l, o, q) {
+ var s = wd(a, b);
+ s || (s = {}, a.get(lb)[m](s));
+ s.id_ = b;
+ s.affiliation_ = c;
+ s.total_ = d;
+ s.tax_ = e;
+ s.shipping_ = f;
+ s.city_ = l;
+ s.state_ = o;
+ s.country_ = q;
+ s.items_ = s.items_ || [];
+ return s
+ },
+ yd = function (a, b, c, d, e, f, l) {
+ var a = wd(a, b) || xd(a, b, "", 0, 0, 0, "", "", ""),
+ o;
+ a: {
+ if (a && a.items_) {
+ o = a.items_;
+ for (var q = 0; q < o[u]; q++) if (o[q].sku_ == c) {
+ o = o[q];
+ break a
+ }
+ }
+ o = i
+ }
+ q = o || {};
+ q.transId_ = b;
+ q.sku_ = c;
+ q.name_ = d;
+ q.category_ = e;
+ q.price_ = f;
+ q.quantity_ = l;
+ o || a.items_[m](q);
+ return q
+ },
+ wd = function (a, b) {
+ for (var c = a.get(lb), d = 0; d < c[u]; d++) if (c[d].id_ == b) return c[d];
+ return i
+ };
+ var zd, Ad = function (a) {
+ /* Disabled for security.
+ var f;
+ var e;
+ if (!zd) {
+ var b;
+ b = J[x].hash;
+ var c = V[r],
+ d = /^#?gaso=([^&]*)/;
+ if (f = (e = (b = b && b[ma](d) || c && c[ma](d)) ? b[1] : I(W("GASO")), b = e) && b[ma](/^(?:\|([-0-9a-z.]{1,40})\|)?([-.\w]{10,1200})$/i), c = f) if (cd(a, "GASO", "" + b), K._gasoDomain = a.get(Ma), K._gasoCPath = a.get(N), b = "https://" + ((c[1] || "www") + ".google.com") + "/analytics/reporting/overlay_js?gaso=" + c[2] + "&" + va()) a = J.createElement("script"), a.type = "text/javascript", a.async = h, a.src = b, a.id = "_gasojs", fa(a, g), b = J.getElementsByTagName("script")[0], b.parentNode.insertBefore(a, b);
+ zd = h
+ }
+ */
+ };
+ var sd = function (a, b, c) {
+ c && (b = H(b));
+ c = a.b(M, 1);
+ b = b[w](".");
+ !(b[u] < 2) && /^\d+$/.test(b[0]) && (b[0] = "" + c, cd(a, "__utmx", b[A](".")))
+ },
+ td = function (a, b) {
+ var c = vc(a.get(M), W("__utmx"));
+ c == "-" && (c = "");
+ return b ? F(c) : c
+ };
+ var Fd = function (a, b) {
+ var c = k.min(a.b(ic, 0), 10);
+ if (a.b(P, 0) % 100 >= c) return j;
+ c = Bd() || Cd();
+ if (c == g) return j;
+ var d = c[0];
+ if (d == g || d == ca || isNaN(d)) return j;
+ d > 0 ? Dd(c) ? b(Ed(c)) : b(Ed(c[ha](0, 1))) : xa(V, "load", function () {
+ Fd(a, b)
+ }, j);
+ return h
+ },
+ Dd = function (a) {
+ for (var b = 1; b < a[u]; b++) if (isNaN(a[b]) || a[b] == ca || a[b] < 0) return j;
+ return h
+ },
+ Ed = function (a) {
+ for (var b = new Yc, c = 0; c < a[u]; c++) b.e(14, c + 1, (isNaN(a[c]) || a[c] < 0 ? 0 : a[c] < 5E3 ? k[ka](a[c] / 10) * 10 : a[c] < 45E4 ? k[ka](a[c] / 100) * 100 : 45E4) + ""), b.k(14, c + 1, a[c]);
+ return b
+ },
+ Bd = function () {
+ var a = V.performance || V.webkitPerformance;
+ if (a = a && a.timing) {
+ var b = a.navigationStart;
+ if (b == 0) G(133);
+ else return [a.loadEventStart - b, a.domainLookupEnd - a.domainLookupStart, a.connectEnd - a.connectStart, a.responseStart - a.requestStart, a.responseEnd - a.responseStart, a.fetchStart - b]
+ }
+ },
+ Cd = function () {
+ if (V.top == V) {
+ var a = V.external,
+ b = a && a.onloadT;
+ a && !a.isValidLoadTime && (b = g);
+ b > 2147483648 && (b = g);
+ b > 0 && a.setPageReadyTime();
+ return b == g ? g : [b]
+ }
+ };
+ var S = function (a, b, c) {
+ function d(a) {
+ return function (b) {
+ if ((b = b.get(lc)[a]) && b[u]) for (var c = nc(e, a), d = 0; d < b[u]; d++) b[d].call(e, c)
+ }
+ }
+ var e = this;
+ this.a = new uc;
+ this.get = function (a) {
+ return this.a.get(a)
+ };
+ this.set = function (a, b, c) {
+ this.a.set(a, b, c)
+ };
+ this.set(Ia, b || "UA-XXXXX-X");
+ this.set(Ka, a || "");
+ this.set(Ja, c || "");
+ this.set(La, k.round((new Date).getTime() / 1E3));
+ this.set(N, "/");
+ this.set(Na, 63072E6);
+ this.set(Pa, 15768E6);
+ this.set(Oa, 18E5);
+ this.set(Qa, j);
+ this.set(hb, 50);
+ this.set(Ra, j);
+ this.set(Sa, h);
+ this.set(Ta, h);
+ this.set(Ua, h);
+ this.set(Va, h);
+ this.set(Wa, h);
+ this.set(Ya, "utm_campaign");
+ this.set(Xa, "utm_id");
+ this.set(Za, "gclid");
+ this.set($a, "utm_source");
+ this.set(ab, "utm_medium");
+ this.set(bb, "utm_term");
+ this.set(cb, "utm_content");
+ this.set(db, "utm_nooverride");
+ this.set(eb, 100);
+ this.set(ic, 1);
+ this.set(jc, j);
+ this.set(fb, "/__utm.gif");
+ this.set(gb, 1);
+ this.set(lb, []);
+ this.set(O, []);
+ this.set(ib, id[ha](0));
+ this.set(jb, []);
+ this.set(kb, []);
+ this.t("auto");
+ this.set(qb, this.ra());
+ this.set(lc, {
+ hit: [],
+ load: []
+ });
+ this.a.g("0", rd);
+ this.a.g("1", gd);
+ this.a.g("2", od);
+ this.a.g("3", hd);
+ this.a.g("4", d("load"));
+ this.a.g("5", Ad);
+ this.a.d("A", Ic);
+ this.a.d("B", Kc);
+ this.a.d("C", gd);
+ this.a.d("D", Hc);
+ this.a.d("E", pc);
+ this.a.d("F", Gd);
+ this.a.d("G", dd);
+ this.a.d("H", Lc);
+ this.a.d("I", Sc);
+ this.a.d("J", ad);
+ this.a.d("K", d("hit"));
+ this.a.d("L", Hd);
+ this.a.d("M", Id);
+ this.get(La) === 0 && G(111);
+ this.a.J();
+ this.w = g
+ };
+ D = S[v];
+ D.h = function () {
+ var a = this.get(mb);
+ a || (a = new Yc, this.set(mb, a));
+ return a
+ };
+ D.ta = function (a) {
+ for (var b in a) {
+ var c = a[b];
+ a.hasOwnProperty(b) && typeof c != "function" && this.set(b, c, h)
+ }
+ };
+ D.z = function (a) {
+ if (this.get(jc)) return j;
+ var b = this,
+ c = Fd(this.a, function (c) {
+ b.set(ob, a, h);
+ b.u(c)
+ });
+ this.set(jc, c);
+ return c
+ };
+ D.na = function (a) {
+ a && a != g && (a.constructor + "")[p]("String") > -1 ? (G(13), this.set(ob, a, h)) : typeof a === "object" && a !== i && this.ta(a);
+ this.w = a = this.get(ob);
+ this.a.f("page");
+ this.z(a)
+ };
+ D.v = function (a, b, c, d, e) {
+ if (a == "" || !Uc(a) || b == "" || !Uc(b)) return j;
+ if (c != g && !Uc(c)) return j;
+ if (d != g && !Vc(d)) return j;
+ this.set($b, a, h);
+ this.set(ac, b, h);
+ this.set(bc, c, h);
+ this.set(cc, d, h);
+ this.set(Zb, !! e, h);
+ this.a.f("event");
+ return h
+ };
+ D.oa = function (a, b, c, d) {
+ if (!a || !b) return j;
+ this.set(dc, a, h);
+ this.set(ec, b, h);
+ this.set(hc, c || J[x].href, h);
+ d && this.set(ob, d, h);
+ this.a.f("social");
+ return h
+ };
+ D.ma = function () {
+ this.set(ic, 10);
+ this.z(this.w)
+ };
+ D.pa = function () {
+ this.a.f("trans")
+ };
+ D.u = function (a) {
+ this.set(nb, a, h);
+ this.a.f("event")
+ };
+ D.V = function (a) {
+ this.m();
+ var b = this;
+ return {
+ _trackEvent: function (c, d, e) {
+ G(91);
+ b.v(a, c, d, e)
+ }
+ }
+ };
+ D.Y = function (a) {
+ return this.get(a)
+ };
+ D.ga = function (a, b) {
+ if (a) if (a != g && (a.constructor + "")[p]("String") > -1) this.set(a, b);
+ else if (typeof a == "object") for (var c in a) a.hasOwnProperty(c) && this.set(c, a[c])
+ };
+ D.addEventListener = function (a, b) {
+ var c = this.get(lc)[a];
+ c && c[m](b)
+ };
+ D.removeEventListener = function (a, b) {
+ for (var c = this.get(lc)[a], d = 0; c && d < c[u]; d++) if (c[d] == b) {
+ c.splice(d, 1);
+ break
+ }
+ };
+ D.$ = function () {
+ return "5.2.2"
+ };
+ D.t = function (a) {
+ this.get(Sa);
+ a = a == "auto" ? Ba(J.domain) : !a || a == "-" || a == "none" ? "" : a[C]();
+ this.set(Ma, a)
+ };
+ D.ea = function (a) {
+ this.set(Sa, !! a)
+ };
+ D.Z = function (a, b) {
+ return ud(this.a, a, b)
+ };
+ D.link = function (a, b) {
+ if (this.a.get(Qa) && a) {
+ var c = ud(this.a, a, b);
+ J[x].href = c
+ }
+ };
+ D.da = function (a, b) {
+ this.a.get(Qa) && a && a.action && (a.action = ud(this.a, a.action, b))
+ };
+ D.ha = function () {
+ this.m();
+ var a = this.a,
+ b = J.getElementById ? J.getElementById("utmtrans") : J.utmform && J.utmform.utmtrans ? J.utmform.utmtrans : i;
+ if (b && b[la]) {
+ a.set(lb, []);
+ for (var b = b[la][w]("UTM:"), c = 0; c < b[u]; c++) {
+ b[c] = ta(b[c]);
+ for (var d = b[c][w](vd), e = 0; e < d[u]; e++) d[e] = ta(d[e]);
+ "T" == d[0] ? xd(a, d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8]) : "I" == d[0] && yd(a, d[1], d[2], d[3], d[4], d[5], d[6])
+ }
+ }
+ };
+ D.O = function (a, b, c, d, e, f, l, o) {
+ return xd(this.a, a, b, c, d, e, f, l, o)
+ };
+ D.M = function (a, b, c, d, e, f) {
+ return yd(this.a, a, b, c, d, e, f)
+ };
+ D.ia = function (a) {
+ vd = a || "|"
+ };
+ D.fa = function (a, b, c, d) {
+ var e = this.a;
+ if (a <= 0 || a > e.get(hb)) a = j;
+ else if (!b || !c || F(b)[u] + F(c)[u] > 64) a = j;
+ else {
+ d != 1 && d != 2 && (d = 3);
+ var f = {};
+ ga(f, b);
+ f.value = c;
+ f.scope = d;
+ e.get(O)[a] = f;
+ a = h
+ }
+ a && this.a.i();
+ return a
+ };
+ D.X = function (a) {
+ this.a.get(O)[a] = g;
+ this.a.i()
+ };
+ D.aa = function (a) {
+ return (a = this.a.get(O)[a]) && a[qa] == 1 ? a[la] : g
+ };
+ D.ka = function (a, b, c) {
+ this.h().e(a, b, c)
+ };
+ D.la = function (a, b, c) {
+ this.h().k(a, b, c)
+ };
+ D.ba = function (a, b) {
+ return this.h().getKey(a, b)
+ };
+ D.ca = function (a, b) {
+ return this.h().C(a, b)
+ };
+ D.S = function (a) {
+ this.h().A(a)
+ };
+ D.T = function (a) {
+ this.h().B(a)
+ };
+ D.W = function () {
+ return new Yc
+ };
+ D.K = function (a) {
+ a && this.get(jb)[m](a[C]())
+ };
+ D.P = function () {
+ this.set(jb, [])
+ };
+ D.L = function (a) {
+ a && this.get(kb)[m](a[C]())
+ };
+ D.Q = function () {
+ this.set(kb, [])
+ };
+ D.N = function (a, b, c, d, e) {
+ if (a && b) {
+ a = [a, b[C]()][A](":");
+ if (d || e) a = [a, d, e][A](":");
+ d = this.get(ib);
+ d.splice(c ? 0 : d[u], 0, a)
+ }
+ };
+ D.R = function () {
+ this.set(ib, [])
+ };
+ D.U = function (a) {
+ this.a[ja]();
+ var b = this.get(N),
+ c = td(this.a);
+ this.set(N, a);
+ this.a.i();
+ sd(this.a, c);
+ this.set(N, b)
+ };
+ D.ra = function () {
+ return J.referrer
+ };
+ D.m = function () {
+ this.a[ja]()
+ };
+ D.ja = function (a) {
+ a && a != "" && (this.set(zb, a), this.a.f("var"))
+ };
+ var Gd = function (a) {
+ a.get(Wb) !== "trans" && a.b(Ib, 0) >= 500 && a[pa]();
+ if (a.get(Wb) === "event") {
+ var b = (new Date).getTime(),
+ c = a.b(Jb, 0),
+ d = a.b(Eb, 0),
+ c = k[ka](0.2 * ((b - (c != d ? c : c * 1E3)) / 1E3));
+ c > 0 && (a.set(Jb, b), a.set(Q, k.min(10, a.b(Q, 0) + c)));
+ a.b(Q, 0) <= 0 && a[pa]()
+ }
+ },
+ Id = function (a) {
+ a.get(Wb) === "event" && a.set(Q, k.max(0, a.b(Q, 10) - 1))
+ };
+ var Jd = function () {
+ var a = [];
+ this.add = function (b, c, d) {
+ d && (c = F("" + c));
+ a[m](b + "=" + c)
+ };
+ this.toString = function () {
+ return a[A]("&")
+ }
+ },
+ Kd = function (a, b) {
+ (b || a.get(gb) != 2) && a.n(Ib)
+ },
+ Ld = function (a, b) {
+ b.add("utmwv", "5.2.2");
+ b.add("utms", a.get(Ib));
+ b.add("utmn", va());
+ var c = J[x].hostname;
+ E(c) || b.add("utmhn", c, h);
+ c = a.get(eb);
+ c != 100 && b.add("utmsp", c, h)
+ },
+ Nd = function (a, b) {
+ b.add("utmac", a.get(Ia));
+ a.get(Zb) && b.add("utmni", 1);
+ Md(a, b);
+ K.q && b.add("aip", 1);
+ b.add("utmu", Mc.Ea())
+ },
+ Md = function (a, b) {
+ function c(a, b) {
+ b && d[m](a + "=" + b + ";")
+ }
+ var d = [];
+ c("__utma", yc(a));
+ c("__utmz", Ec(a, j));
+ c("__utmv", Bc(a, h));
+ c("__utmx", td(a));
+ b.add("utmcc", d[A]("+"), h)
+ },
+ Od = function (a, b) {
+ a.get(Ta) && (b.add("utmcs", a.get(xb), h), b.add("utmsr", a.get(sb)), b.add("utmsc", a.get(tb)), b.add("utmul", a.get(wb)), b.add("utmje", a.get(ub)), b.add("utmfl", a.get(vb), h))
+ },
+ Pd = function (a, b) {
+ a.get(Wa) && a.get(pb) && b.add("utmdt", a.get(pb), h);
+ b.add("utmhid", a.get(rb));
+ b.add("utmr", Ea(a.get(qb), a.get(N)), h);
+ b.add("utmp", F(a.get(ob), h), h)
+ },
+ Qd = function (a, b) {
+ for (var c = a.get(mb), d = a.get(nb), e = a.get(O) || [], f = 0; f < e[u]; f++) {
+ var l = e[f];
+ l && (c || (c = new Yc), c.e(8, f, l[r]), c.e(9, f, l[la]), l[qa] != 3 && c.e(11, f, "" + l[qa]))
+ }!E(a.get($b)) && !E(a.get(ac), h) && (c || (c = new Yc), c.e(5, 1, a.get($b)), c.e(5, 2, a.get(ac)), e = a.get(bc), e != g && c.e(5, 3, e), e = a.get(cc), e != g && c.k(5, 1, e));
+ c ? b.add("utme", c.ua(d), h) : d && b.add("utme", d.o(), h)
+ },
+ Rd = function (a, b, c) {
+ var d = new Jd;
+ Kd(a, c);
+ Ld(a, d);
+ d.add("utmt", "tran");
+ d.add("utmtid", b.id_, h);
+ d.add("utmtst", b.affiliation_, h);
+ d.add("utmtto", b.total_, h);
+ d.add("utmttx", b.tax_, h);
+ d.add("utmtsp", b.shipping_, h);
+ d.add("utmtci", b.city_, h);
+ d.add("utmtrg", b.state_, h);
+ d.add("utmtco", b.country_, h);
+ !c && Nd(a, d);
+ return d[t]()
+ },
+ Sd = function (a, b, c) {
+ var d = new Jd;
+ Kd(a, c);
+ Ld(a, d);
+ d.add("utmt", "item");
+ d.add("utmtid", b.transId_, h);
+ d.add("utmipc", b.sku_, h);
+ d.add("utmipn", b.name_, h);
+ d.add("utmiva", b.category_, h);
+ d.add("utmipr", b.price_, h);
+ d.add("utmiqt", b.quantity_, h);
+ !c && Nd(a, d);
+ return d[t]()
+ },
+ Td = function (a, b) {
+ var c = a.get(Wb);
+ if (c == "page") c = new Jd, Kd(a, b), Ld(a, c), Qd(a, c), Od(a, c), Pd(a, c), b || Nd(a, c), c = [c[t]()];
+ else if (c == "event") c = new Jd, Kd(a, b), Ld(a, c), c.add("utmt", "event"), Qd(a, c), Od(a, c), Pd(a, c), !b && Nd(a, c), c = [c[t]()];
+ else if (c == "var") c = new Jd, Kd(a, b), Ld(a, c), c.add("utmt", "var"), !b && Nd(a, c), c = [c[t]()];
+ else if (c == "trans") for (var c = [], d = a.get(lb), e = 0; e < d[u]; ++e) {
+ c[m](Rd(a, d[e], b));
+ for (var f = d[e].items_, l = 0; l < f[u]; ++l) c[m](Sd(a, f[l], b))
+ } else c == "social" ? b ? c = [] : (c = new Jd, Kd(a, b), Ld(a, c), c.add("utmt", "social"), c.add("utmsn", a.get(dc), h), c.add("utmsa", a.get(ec), h), c.add("utmsid", a.get(hc), h), Qd(a, c), Od(a, c), Pd(a, c), Nd(a, c), c = [c[t]()]) : c = [];
+ return c
+ },
+ Hd = function (a) {
+ var b, c = a.get(gb),
+ d = a.get(Yb),
+ e = d && d.Aa,
+ f = 0;
+ if (c == 0 || c == 2) {
+ var l = a.get(fb) + "?";
+ b = Td(a, h);
+ for (var o = 0, q = b[u]; o < q; o++) Ga(b[o], e, l, h), f++
+ }
+ if (c == 1 || c == 2) {
+ b = Td(a);
+ o = 0;
+ for (q = b[u]; o < q; o++) try {
+ Ga(b[o], e), f++
+ } catch (s) {
+ s && Fa(s[r], g, s.message)
+ }
+ }
+ if (d) d.j = f
+ };
+ var Ud = "https:" == J[x].protocol ? "https://ssl.google-analytics.com" : "http://www.google-analytics.com",
+ Vd = function (a) {
+ ga(this, "len");
+ this.message = a + "-8192"
+ },
+ Wd = function (a) {
+ ga(this, "ff2post");
+ this.message = a + "-2036"
+ },
+ Ga = function (a, b, c, d) {
+ b = b || wa;
+ if (d || a[u] <= 2036) Xd(a, b, c);
+ else if (a[u] <= 8192) {
+ if (V[ra].userAgent[p]("Firefox") >= 0 && ![].reduce) throw new Wd(a[u]);
+ Yd(a, b) || Zd(a, b)
+ } else throw new Vd(a[u]);
+ },
+ Xd = function (a, b, c) {
+ var c = c || Ud + "/__utm.gif?",
+ d = new Image(1, 1);
+ d.src = c + a;
+ fa(d, function () {
+ fa(d, i);
+ d.onerror = i;
+ b()
+ });
+ d.onerror = function () {
+ fa(d, i);
+ d.onerror = i;
+ b()
+ }
+ },
+ Yd = function (a, b) {
+ var c, d = Ud + "/p/__utm.gif",
+ e = V.XDomainRequest;
+ if (e) c = new e, c.open("POST", d);
+ else if (e = V.XMLHttpRequest) e = new e, "withCredentials" in e && (c = e, c.open("POST", d, h), c.setRequestHeader("Content-Type", "text/plain"));
+ if (c) return c.onreadystatechange = function () {
+ c.readyState == 4 && (b(), c = i)
+ }, c.send(a), h
+ },
+ Zd = function (a, b) {
+ if (J.body) {
+ a = ba(a);
+ try {
+ var c = J.createElement('<iframe name="' + a + '"></iframe>')
+ } catch (d) {
+ c = J.createElement("iframe"), ga(c, a)
+ }
+ c.height = "0";
+ c.width = "0";
+ c.style.display = "none";
+ c.style.visibility = "hidden";
+ var e = J[x],
+ e = Ud + "/u/post_iframe.html#" + ba(e.protocol + "//" + e[oa] + "/favicon.ico"),
+ f = function () {
+ c.src = "";
+ c.parentNode && c.parentNode.removeChild(c)
+ };
+ xa(V, "beforeunload", f);
+ var l = j,
+ o = 0,
+ q = function () {
+ if (!l) {
+ try {
+ if (o > 9 || c.contentWindow[x][oa] == J[x][oa]) {
+ l = h;
+ f();
+ ya(V, "beforeunload", f);
+ b();
+ return
+ }
+ } catch (a) {}
+ o++;
+ da(q, 200)
+ }
+ };
+ xa(c, "load", q);
+ J.body.appendChild(c);
+ c.src = e
+ } else Nc(function () {
+ Zd(a, b)
+ }, 100)
+ };
+ var Z = function () {
+ this.q = j;
+ this.D = {};
+ this.F = [];
+ this.wa = 0;
+ this._gasoCPath = this._gasoDomain = g;
+ R(Z[v], "_createTracker", Z[v].l, 55);
+ R(Z[v], "_getTracker", Z[v].ya, 0);
+ R(Z[v], "_getTrackerByName", Z[v].p, 51);
+ R(Z[v], "_getTrackers", Z[v].za, 130);
+ R(Z[v], "_anonymizeIp", Z[v].xa, 16);
+ mc()
+ };
+ D = Z[v];
+ D.ya = function (a, b) {
+ return this.l(a, g, b)
+ };
+ D.l = function (a, b, c) {
+ b && G(23);
+ c && G(67);
+ b == g && (b = "~" + K.wa++);
+ a = new S(b, a, c);
+ K.D[b] = a;
+ K.F[m](a);
+ return a
+ };
+ D.p = function (a) {
+ a = a || "";
+ return K.D[a] || K.l(g, a)
+ };
+ D.za = function () {
+ return K.F[ha](0)
+ };
+ D.xa = function () {
+ this.q = h
+ };
+ var $d = function (a) {
+ if (J.webkitVisibilityState == "prerender") return j;
+ a();
+ return h
+ };
+ var K = new Z;
+ var ae = V._gat;
+ ae && typeof ae._getTracker == "function" ? K = ae : V._gat = K;
+ var Tc = new Y;
+ (function (a) {
+ if (!$d(a)) {
+ G(123);
+ var b = j,
+ c = function () {
+ !b && $d(a) && (G(124), b = h, ya(J, "webkitvisibilitychange", c))
+ };
+ xa(J, "webkitvisibilitychange", c)
+ }
+ })(function () {
+ var a = V._gaq,
+ b = j;
+ if (a && typeof a[m] == "function" && (b = Object[v][t].call(Object(a)) == "[object Array]", !b)) {
+ Tc = a;
+ return
+ }
+ V._gaq = Tc;
+ b && Tc[m].apply(Tc, a)
+ });
+})();
=== modified file 'lib/lp/hardwaredb/doc/hwdb-submission.txt'
--- lib/lp/hardwaredb/doc/hwdb-submission.txt 2010-10-18 22:24:59 +0000
+++ lib/lp/hardwaredb/doc/hwdb-submission.txt 2011-12-11 01:49:34 +0000
@@ -201,7 +201,7 @@
>>> team_form['field.submission_key'] = u'unique-id-68'
>>> valid_sample_data_path = os.path.join(
... config.root,
- ... 'lib/canonical/launchpad/scripts/tests/'
+ ... 'lib/lp/hardwaredb/scripts/tests/'
... 'simple_valid_hwdb_submission.xml')
>>> valid_sample_data = StringIO(open(valid_sample_data_path).read())
>>> valid_sample_data.filename = 'simple_valid_hwdb_submission.xml'
=== renamed file 'lib/canonical/launchpad/scripts/tests/simple_valid_hwdb_submission.xml' => 'lib/lp/hardwaredb/scripts/tests/simple_valid_hwdb_submission.xml'
=== renamed file 'lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py' => 'lib/lp/hardwaredb/scripts/tests/test_hwdb_submission_parser.py'
=== renamed file 'lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py' => 'lib/lp/hardwaredb/scripts/tests/test_hwdb_submission_processing.py'
--- lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2011-11-16 06:15:58 +0000
+++ lib/lp/hardwaredb/scripts/tests/test_hwdb_submission_processing.py 2011-12-11 01:49:34 +0000
@@ -5101,7 +5101,7 @@
def getSampleData(self, filename):
"""Return the submission data of a short valid submission."""
sample_data_path = os.path.join(
- config.root, 'lib', 'canonical', 'launchpad', 'scripts',
+ config.root, 'lib', 'lp', 'hardwaredb', 'scripts',
'tests', 'simple_valid_hwdb_submission.xml')
return open(sample_data_path).read()
=== modified file 'lib/lp/poppy/tests/test_twistedftp.py'
--- lib/lp/poppy/tests/test_twistedftp.py 2011-05-02 12:55:06 +0000
+++ lib/lp/poppy/tests/test_twistedftp.py 2011-12-11 01:49:34 +0000
@@ -7,24 +7,22 @@
import os
-from testtools.deferredruntest import (
- AsynchronousDeferredRunTest,
- )
+from testtools.deferredruntest import AsynchronousDeferredRunTest
import transaction
from twisted.protocols import ftp
from zope.component import getUtility
from canonical.config import config
-from canonical.launchpad.ftests.keys_for_tests import gpgkeysdir
-from canonical.launchpad.interfaces.gpghandler import IGPGHandler
from canonical.testing.layers import ZopelessDatabaseLayer
-
from lp.poppy.twistedftp import PoppyFileWriter
from lp.registry.interfaces.gpg import (
GPGKeyAlgorithm,
- IGPGKeySet)
+ IGPGKeySet,
+ )
from lp.services.database.isolation import check_no_transaction
+from lp.services.gpg.interfaces import IGPGHandler
from lp.testing import TestCaseWithFactory
+from lp.testing.gpgkeys import gpgkeysdir
from lp.testing.keyserver import KeyServerTac
=== modified file 'lib/lp/poppy/twistedconfigreset.py'
--- lib/lp/poppy/twistedconfigreset.py 2011-05-14 12:17:46 +0000
+++ lib/lp/poppy/twistedconfigreset.py 2011-12-11 01:49:34 +0000
@@ -11,11 +11,10 @@
from twisted.application.service import Service
from twisted.internet import task
from twisted.internet.error import AlreadyCancelled
-
from zope.component import getUtility
from zope.component.interfaces import ComponentLookupError
-from canonical.launchpad.interfaces.gpghandler import IGPGHandler
+from lp.services.gpg.interfaces import IGPGHandler
class GPGHandlerConfigResetJob(Service):
=== modified file 'lib/lp/poppy/twistedftp.py'
--- lib/lp/poppy/twistedftp.py 2011-04-29 06:47:43 +0000
+++ lib/lp/poppy/twistedftp.py 2011-12-11 01:49:34 +0000
@@ -13,20 +13,23 @@
import os
import tempfile
-from twisted.application import service, strports
-from twisted.cred import checkers, credentials
-from twisted.cred.portal import IRealm, Portal
+from twisted.application import (
+ service,
+ strports,
+ )
+from twisted.cred import (
+ checkers,
+ credentials,
+ )
+from twisted.cred.portal import (
+ IRealm,
+ Portal,
+ )
from twisted.internet import defer
from twisted.protocols import ftp
from twisted.python import filepath
-
+from zope.component import getUtility
from zope.interface import implements
-from zope.component import getUtility
-
-from canonical.launchpad.interfaces.gpghandler import (
- GPGVerificationError,
- IGPGHandler,
- )
from canonical.config import config
from lp.poppy import get_poppy_root
@@ -34,6 +37,10 @@
from lp.poppy.hooks import Hooks
from lp.registry.interfaces.gpg import IGPGKeySet
from lp.services.database import read_transaction
+from lp.services.gpg.interfaces import (
+ GPGVerificationError,
+ IGPGHandler,
+ )
class PoppyAccessCheck:
=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py 2011-12-09 12:52:34 +0000
+++ lib/lp/registry/browser/person.py 2011-12-11 01:49:34 +0000
@@ -143,10 +143,6 @@
IEmailAddress,
IEmailAddressSet,
)
-from canonical.launchpad.interfaces.gpghandler import (
- GPGKeyNotFoundError,
- IGPGHandler,
- )
from canonical.launchpad.interfaces.launchpad import (
INotificationRecipientSet,
UnknownRecipientError,
@@ -267,6 +263,10 @@
)
from lp.services.fields import LocationField
from lp.services.geoip.interfaces import IRequestPreferredLanguages
+from lp.services.gpg.interfaces import (
+ GPGKeyNotFoundError,
+ IGPGHandler,
+ )
from lp.services.messages.interfaces.message import (
IDirectEmailAuthorization,
QuotaReachedError,
=== modified file 'lib/lp/registry/browser/tests/test_mailinglists.py'
--- lib/lp/registry/browser/tests/test_mailinglists.py 2010-12-15 20:40:26 +0000
+++ lib/lp/registry/browser/tests/test_mailinglists.py 2011-12-11 01:49:34 +0000
@@ -28,11 +28,12 @@
"""Verify the content in +mailing-list-portlet."""
def makeTeamWithMailingList(self, name=None, owner=None, visibility=None):
+ if owner is None:
+ owner = self.factory.makePerson()
team = self.factory.makeTeam(
name=name, owner=owner, visibility=visibility)
- login_person(team.teamowner)
- team_list = self.factory.makeMailingList(
- team=team, owner=team.teamowner)
+ login_person(owner)
+ self.factory.makeMailingList(team=team, owner=owner)
return team
@@ -129,10 +130,11 @@
def test_private_message_message_without_list(self):
# Private teams have private archives.
+ owner = self.factory.makePerson()
team = self.factory.makeTeam(
- visibility=PersonVisibility.PRIVATE)
- login_person(team.teamowner)
+ owner=owner, visibility=PersonVisibility.PRIVATE)
+ login_person(owner)
view = create_initialized_view(
- team, name='+mailinglist', principal=team.teamowner)
+ team, name='+mailinglist', principal=owner)
element = find_tag_by_id(view(), 'mailing-list-archive')
self.assertEqual('private', extract_text(element))
=== modified file 'lib/lp/registry/browser/tests/test_person_view.py'
--- lib/lp/registry/browser/tests/test_person_view.py 2011-11-20 07:23:45 +0000
+++ lib/lp/registry/browser/tests/test_person_view.py 2011-12-11 01:49:34 +0000
@@ -475,9 +475,11 @@
def test_active_participations_with_direct_private_team(self):
# Users cannot see private teams that they are not members of.
- team = self.factory.makeTeam(visibility=PersonVisibility.PRIVATE)
- login_person(team.teamowner)
- team.addMember(self.user, team.teamowner)
+ owner = self.factory.makePerson()
+ team = self.factory.makeTeam(
+ owner=owner, visibility=PersonVisibility.PRIVATE)
+ login_person(owner)
+ team.addMember(self.user, owner)
# The team is included in active_participations.
login_person(self.user)
view = create_view(
@@ -492,11 +494,13 @@
def test_active_participations_with_indirect_private_team(self):
# Users cannot see private teams that they are not members of.
- team = self.factory.makeTeam(visibility=PersonVisibility.PRIVATE)
- direct_team = self.factory.makeTeam(owner=team.teamowner)
- login_person(team.teamowner)
- direct_team.addMember(self.user, team.teamowner)
- team.addMember(direct_team, team.teamowner)
+ owner = self.factory.makePerson()
+ team = self.factory.makeTeam(
+ owner=owner, visibility=PersonVisibility.PRIVATE)
+ direct_team = self.factory.makeTeam(owner=owner)
+ login_person(owner)
+ direct_team.addMember(self.user, owner)
+ team.addMember(direct_team, owner)
# The team is included in active_participations.
login_person(self.user)
view = create_view(
=== modified file 'lib/lp/registry/browser/tests/test_product.py'
--- lib/lp/registry/browser/tests/test_product.py 2011-11-28 05:14:57 +0000
+++ lib/lp/registry/browser/tests/test_product.py 2011-12-11 01:49:34 +0000
@@ -27,6 +27,9 @@
person_logged_in,
TestCaseWithFactory,
)
+from lp.testing.fixture import (
+ DemoMode,
+ )
from lp.testing.mail_helpers import pop_notifications
from lp.testing.service_usage_helpers import set_service_usage
from lp.testing.views import (
@@ -164,10 +167,7 @@
self.assertTrue(message is not None)
def test_staging_message_is_demo(self):
- config.push('staging-test', '''
- [launchpad]
- is_demo: true
- ''')
+ self.useFixture(DemoMode())
view = create_initialized_view(self.product_set, '+new')
message = find_tag_by_id(view.render(), 'staging-message')
self.assertEqual(None, message)
=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml 2011-12-07 21:33:14 +0000
+++ lib/lp/registry/configure.zcml 2011-12-11 01:49:34 +0000
@@ -912,8 +912,6 @@
<allow
interface="lp.registry.interfaces.person.IPersonPublic"/>
<allow
- interface="lp.registry.interfaces.person.ITeamPublic"/>
- <allow
interface="lp.registry.interfaces.person.IHasStanding"/>
<allow
interface="lp.registry.interfaces.person.IPersonCommAdminWriteRestricted"/>
@@ -924,6 +922,9 @@
permission="launchpad.View"
interface="lp.registry.interfaces.person.IPersonViewRestricted"/>
<require
+ permission="launchpad.View"
+ interface="lp.registry.interfaces.person.ITeamPublic"/>
+ <require
permission="launchpad.Edit"
interface="lp.registry.interfaces.location.ISetLocation"/>
<require
@@ -931,9 +932,10 @@
interface="lp.registry.interfaces.person.IPersonEditRestricted"/>
<require
permission="launchpad.Edit"
- set_schema="lp.registry.interfaces.person.IPersonPublic
+ set_schema="lp.registry.interfaces.person.IPersonViewRestricted
+ lp.registry.interfaces.person.IPersonPublic
lp.registry.interfaces.person.ITeamPublic"
- set_attributes="displayname"/>
+ set_attributes="displayname icon logo"/>
<require
permission="launchpad.Moderate"
set_attributes="name"/>
=== modified file 'lib/lp/registry/doc/person.txt'
--- lib/lp/registry/doc/person.txt 2011-12-09 12:52:34 +0000
+++ lib/lp/registry/doc/person.txt 2011-12-11 01:49:34 +0000
@@ -696,6 +696,7 @@
>>> private_team = factory.makeTeam(salgado, name='private-team',
... displayname='Private Team',
... visibility=PersonVisibility.PRIVATE)
+ >>> private_team_owner = private_team.teamowner
>>> bug_subscription = BugSubscription(bug=bug, person=private_team,
... subscribed_by=guadamen)
@@ -806,7 +807,7 @@
But Owner, a member of that team, will see it in the results.
- >>> login_person(private_team.teamowner)
+ >>> login_person(private_team_owner)
>>> print_people(personset.find('team'))
Another a new team (new3): []
Hoary Gnome Team (name21): []
=== modified file 'lib/lp/registry/doc/private-team-visibility.txt'
--- lib/lp/registry/doc/private-team-visibility.txt 2011-11-16 00:09:49 +0000
+++ lib/lp/registry/doc/private-team-visibility.txt 2011-12-11 01:49:34 +0000
@@ -107,3 +107,64 @@
...
Unauthorized: (<Person at ... priv-team (Priv Team)>,
'name', 'launchpad.LimitedView')
+
+A person with visibility to any of the branches owned by the private team will
+be granted limited view permission on the team.
+
+For private branches, a user needs to be subscribed to the branch for the
+branch (and hence team) to be visible.
+
+ >>> login_person(priv_owner)
+ >>> private_team_branch = factory.makeBranch(
+ ... owner=priv_team, private=True)
+ >>> login_person(pub_member)
+ >>> check_permission('launchpad.LimitedView', priv_team)
+ False
+
+ >>> login_person(priv_owner)
+ >>> sub = factory.makeBranchSubscription(
+ ... branch=private_team_branch, person=pub_member,
+ ... subscribed_by=priv_owner)
+
+ >>> login_person(pub_member)
+ >>> check_permission('launchpad.LimitedView', priv_team)
+ True
+
+Subscribers to the teams private PPA have limited view permission.
+
+ >>> login_person(priv_owner)
+ >>> archive = factory.makeArchive(private=True, owner=priv_team)
+ >>> archive_subscriber = factory.makePerson()
+ >>> sub = archive.newSubscription(
+ ... archive_subscriber, registrant=archive.owner)
+
+ >>> login_person(archive_subscriber)
+ >>> check_permission('launchpad.LimitedView', priv_team)
+ True
+
+Users with LimitedView can know identifying information like name,
+displayname, and unique_name, but cannot know other information like
+teamowner.
+
+ >>> print priv_team.name
+ priv-team
+
+ >>> print priv_team.displayname
+ Priv Team
+
+ >>> print priv_team.unique_displayname
+ Priv Team (priv-team)
+
+ >>> print priv_team.icon
+ None
+
+ >>> print priv_team.allmembers
+ Traceback (most recent call last):
+ ...
+ Unauthorized: (<Person at ... priv-team (Priv Team)>,
+ 'allmembers', 'launchpad.View')
+
+Anonymous users do not have permission.
+ >>> login(ANONYMOUS)
+ >>> check_permission('launchpad.LimitedView', priv_team)
+ False
=== modified file 'lib/lp/registry/interfaces/person.py'
--- lib/lp/registry/interfaces/person.py 2011-12-09 12:52:34 +0000
+++ lib/lp/registry/interfaces/person.py 2011-12-11 01:49:34 +0000
@@ -15,7 +15,7 @@
'IObjectReassignment',
'IPerson',
'IPersonClaim',
- 'IPersonPublic', # Required for a monkey patch in interfaces/archive.py
+ 'IPersonPublic',
'IPersonSet',
'IPersonSettings',
'ISoftwareCenterAgentAPI',
@@ -657,26 +657,55 @@
required=False, default=False)
-class IPersonPublic(IHasBranches, IHasSpecifications,
- IHasMergeProposals, IHasLogo, IHasMugshot, IHasIcon,
- IHasLocation, IHasRequestedReviews, IObjectWithLocation,
- IPrivacy, IHasBugs, IHasRecipes, IHasTranslationImports,
- IPersonSettings, IQuestionsPerson):
- """Public attributes for a Person."""
+class IPersonPublic(IPrivacy):
+ """Public attributes for a Person.
+
+ Very few attributes on a person can be public because private teams
+ are also persons. The public attributes are generally information
+ needed by the system to determine if the principal in the current
+ interaction can work with the object.
+ """
id = Int(title=_('ID'), required=True, readonly=True)
- account = Object(schema=IAccount)
- accountID = Int(title=_('Account ID'), required=True, readonly=True)
- password = PasswordField(
- title=_('Password'), required=True, readonly=False)
- karma = exported(
- Int(title=_('Karma'), readonly=True,
- description=_('The cached total karma for this person.')))
- homepage_content = exported(
- Text(title=_("Homepage Content"), required=False,
- description=_(
- "The content of your profile page. Use plain text, "
- "paragraphs are preserved and URLs are linked in pages.")))
+ # This is redefined from IPrivacy.private because the attribute is
+ # read-only. It is a summary of the team's visibility.
+ private = exported(Bool(
+ title=_("This team is private"),
+ readonly=True, required=False,
+ description=_("Private teams are visible only to "
+ "their members.")))
+ is_valid_person = Bool(
+ title=_("This is an active user and not a team."), readonly=True)
+ is_valid_person_or_team = exported(
+ Bool(title=_("This is an active user or a team."), readonly=True),
+ exported_as='is_valid')
+ is_merge_pending = exported(Bool(
+ title=_("Is this person due to be merged with another?"),
+ required=False, default=False))
+ is_team = exported(
+ Bool(title=_('Is this object a team?'), readonly=True))
+
+
+class IPersonLimitedView(IHasIcon, IHasLogo):
+ """IPerson attributes that require launchpad.LimitedView permission."""
+
+ name = exported(
+ PersonNameField(
+ title=_('Name'), required=True, readonly=False,
+ constraint=name_validator,
+ description=_(
+ "A short unique name, beginning with a lower-case "
+ "letter or number, and containing only letters, "
+ "numbers, dots, hyphens, or plus signs.")))
+ displayname = exported(
+ StrippedTextLine(
+ title=_('Display Name'), required=True, readonly=False,
+ description=_(
+ "Your name as you would like it displayed throughout "
+ "Launchpad. Most people use their full name here.")),
+ exported_as='display_name')
+ unique_displayname = TextLine(
+ title=_('Return a string of the form $displayname ($name).'))
# NB at this stage we do not allow individual people to have their own
# icon, only teams get that. People can however have a logo and mugshot
# The icon is only used for teams; that's why we use /@@/team as the
@@ -702,6 +731,26 @@
"be no bigger than 50kb in size.")))
logoID = Int(title=_('Logo ID'), required=True, readonly=True)
+
+class IPersonViewRestricted(IHasBranches, IHasSpecifications,
+ IHasMergeProposals, IHasMugshot,
+ IHasLocation, IHasRequestedReviews, IObjectWithLocation,
+ IHasBugs, IHasRecipes, IHasTranslationImports,
+ IPersonSettings, IQuestionsPerson):
+ """IPerson attributes that require launchpad.View permission."""
+ account = Object(schema=IAccount)
+ accountID = Int(title=_('Account ID'), required=True, readonly=True)
+ password = PasswordField(
+ title=_('Password'), required=True, readonly=False)
+ karma = exported(
+ Int(title=_('Karma'), readonly=True,
+ description=_('The cached total karma for this person.')))
+ homepage_content = exported(
+ Text(title=_("Homepage Content"), required=False,
+ description=_(
+ "The content of your profile page. Use plain text, "
+ "paragraphs are preserved and URLs are linked in pages.")))
+
mugshot = exported(MugshotImageUpload(
title=_("Mugshot"), required=False,
default_image_resource='/@@/person-mugshot',
@@ -766,13 +815,6 @@
# Properties of the Person object.
karma_category_caches = Attribute(
'The caches of karma scores, by karma category.')
- is_team = exported(
- Bool(title=_('Is this object a team?'), readonly=True))
- is_valid_person = Bool(
- title=_("This is an active user and not a team."), readonly=True)
- is_valid_person_or_team = exported(
- Bool(title=_("This is an active user or a team."), readonly=True),
- exported_as='is_valid')
is_probationary = exported(
Bool(title=_("Is this a probationary user?"), readonly=True))
is_ubuntu_coc_signer = exported(
@@ -879,7 +921,6 @@
exported_as='team_owner')
teamownerID = Int(title=_("The Team Owner's ID or None"), required=False,
readonly=True)
-
preferredemail = exported(
Reference(title=_("Preferred email address"),
description=_("The preferred email address for this person. "
@@ -987,18 +1028,6 @@
readonly=True, required=False,
value_type=Reference(schema=Interface))) # HWSubmission
- # This is redefined from IPrivacy.private because the attribute is
- # read-only. It is a summary of the team's visibility.
- private = exported(Bool(
- title=_("This team is private"),
- readonly=True, required=False,
- description=_("Private teams are visible only to "
- "their members.")))
-
- is_merge_pending = exported(Bool(
- title=_("Is this person due to be merged with another?"),
- required=False, default=False))
-
administrated_teams = Attribute(
u"the teams that this person/team is an administrator of.")
@@ -1463,32 +1492,6 @@
:return: a boolean.
"""
-
-class IPersonLimitedView(Interface):
- """IPerson attributes that require launchpad.LimitedView permission."""
-
- name = exported(
- PersonNameField(
- title=_('Name'), required=True, readonly=False,
- constraint=name_validator,
- description=_(
- "A short unique name, beginning with a lower-case "
- "letter or number, and containing only letters, "
- "numbers, dots, hyphens, or plus signs.")))
- displayname = exported(
- StrippedTextLine(
- title=_('Display Name'), required=True, readonly=False,
- description=_(
- "Your name as you would like it displayed throughout "
- "Launchpad. Most people use their full name here.")),
- exported_as='display_name')
- unique_displayname = TextLine(
- title=_('Return a string of the form $displayname ($name).'))
-
-
-class IPersonViewRestricted(Interface):
- """IPerson attributes that require launchpad.View permission."""
-
active_member_count = Attribute(
"The number of real people who are members of this team.")
# activemembers.value_type.schema will be set to IPerson once
@@ -2565,18 +2568,19 @@
]:
IPersonViewRestricted[name].value_type.schema = IPerson
-IPersonPublic['sub_teams'].value_type.schema = ITeam
-IPersonPublic['super_teams'].value_type.schema = ITeam
+IPersonViewRestricted['sub_teams'].value_type.schema = ITeam
+IPersonViewRestricted['super_teams'].value_type.schema = ITeam
# XXX: salgado, 2008-08-01: Uncomment these when teams_*participated_in are
# exported again.
-# IPersonPublic['teams_participated_in'].value_type.schema = ITeam
-# IPersonPublic['teams_indirectly_participated_in'].value_type.schema = ITeam
+# IPersonViewRestricted['teams_participated_in'].value_type.schema = ITeam
+# IPersonViewRestricted[
+# 'teams_indirectly_participated_in'].value_type.schema = ITeam
# Fix schema of operation parameters. We need zope.deferredimport!
params_to_fix = [
# XXX: salgado, 2008-08-01: Uncomment these when they are exported again.
- # (IPersonPublic['findPathToTeam'], 'team'),
- # (IPersonPublic['inTeam'], 'team'),
+ # (IPersonViewRestricted['findPathToTeam'], 'team'),
+ # (IPersonViewRestricted['inTeam'], 'team'),
(IPersonEditRestricted['join'], 'team'),
(IPersonEditRestricted['leave'], 'team'),
(IPersonEditRestricted['addMember'], 'person'),
=== modified file 'lib/lp/registry/interfaces/role.py'
--- lib/lp/registry/interfaces/role.py 2011-11-16 07:25:58 +0000
+++ lib/lp/registry/interfaces/role.py 2011-12-11 01:49:34 +0000
@@ -119,7 +119,8 @@
def inTeam(team):
"""Is this person a member or the owner of `team`?
- Passed through to the same method in 'IPersonPublic'.
+ Passed through to the *unproxied* same method in
+ `IPersonViewRestricted`.
"""
def isOwner(obj):
=== modified file 'lib/lp/registry/model/codeofconduct.py'
--- lib/lp/registry/model/codeofconduct.py 2010-08-20 20:31:18 +0000
+++ lib/lp/registry/model/codeofconduct.py 2011-12-11 01:49:34 +0000
@@ -31,10 +31,6 @@
quote,
SQLBase,
)
-from canonical.launchpad.interfaces.gpghandler import (
- GPGVerificationError,
- IGPGHandler,
- )
from canonical.launchpad.webapp import canonical_url
from lp.app.errors import NotFoundError
from lp.registry.interfaces.codeofconduct import (
@@ -45,6 +41,10 @@
ISignedCodeOfConductSet,
)
from lp.registry.interfaces.gpg import IGPGKeySet
+from lp.services.gpg.interfaces import (
+ GPGVerificationError,
+ IGPGHandler,
+ )
from lp.services.mail.sendmail import (
format_address,
simple_sendmail,
=== modified file 'lib/lp/registry/model/gpgkey.py'
--- lib/lp/registry/model/gpgkey.py 2010-08-20 20:31:18 +0000
+++ lib/lp/registry/model/gpgkey.py 2011-12-11 01:49:34 +0000
@@ -21,12 +21,12 @@
SQLBase,
sqlvalues,
)
-from canonical.launchpad.interfaces.gpghandler import IGPGHandler
from lp.registry.interfaces.gpg import (
GPGKeyAlgorithm,
IGPGKey,
IGPGKeySet,
)
+from lp.services.gpg.interfaces import IGPGHandler
class GPGKey(SQLBase):
=== modified file 'lib/lp/registry/model/personroles.py'
--- lib/lp/registry/model/personroles.py 2011-11-16 07:25:58 +0000
+++ lib/lp/registry/model/personroles.py 2011-12-11 01:49:34 +0000
@@ -11,6 +11,7 @@
getUtility,
)
from zope.interface import implements
+from zope.security.proxy import removeSecurityProxy
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
from lp.bugs.interfaces.bugsupervisor import IHasBugSupervisor
@@ -29,7 +30,8 @@
def __init__(self, person):
self.person = person
self._celebrities = getUtility(ILaunchpadCelebrities)
- self.inTeam = self.person.inTeam
+ # Use an unproxied inTeam() method for security checks.
+ self.inTeam = removeSecurityProxy(self.person).inTeam
def __getattr__(self, name):
"""Handle all in_* attributes."""
@@ -39,7 +41,7 @@
raise AttributeError(errortext)
attribute = name[len(prefix):]
try:
- return self.person.inTeam(getattr(self._celebrities, attribute))
+ return self.inTeam(getattr(self._celebrities, attribute))
except AttributeError:
raise AttributeError(errortext)
@@ -49,28 +51,28 @@
def isOwner(self, obj):
"""See IPersonRoles."""
- return self.person.inTeam(obj.owner)
+ return self.inTeam(obj.owner)
def isBugSupervisor(self, obj):
"""See IPersonRoles."""
return (IHasBugSupervisor.providedBy(obj)
- and self.person.inTeam(obj.bug_supervisor))
+ and self.inTeam(obj.bug_supervisor))
def isSecurityContact(self, obj):
"""See IPersonRoles."""
return (IHasSecurityContact.providedBy(obj)
- and self.person.inTeam(obj.security_contact))
+ and self.inTeam(obj.security_contact))
def isDriver(self, obj):
"""See IPersonRoles."""
- return self.person.inTeam(obj.driver)
+ return self.inTeam(obj.driver)
def isOneOfDrivers(self, obj):
"""See IPersonRoles."""
if not IHasDrivers.providedBy(obj):
return self.isDriver(obj)
for driver in obj.drivers:
- if self.person.inTeam(driver):
+ if self.inTeam(driver):
return True
return False
@@ -78,6 +80,6 @@
"""See IPersonRoles."""
for attr in attributes:
role = getattr(obj, attr)
- if self.person.inTeam(role):
+ if self.inTeam(role):
return True
return False
=== modified file 'lib/lp/registry/scripts/teamparticipation.py'
--- lib/lp/registry/scripts/teamparticipation.py 2011-11-04 22:10:46 +0000
+++ lib/lp/registry/scripts/teamparticipation.py 2011-12-11 01:49:34 +0000
@@ -7,8 +7,8 @@
__all__ = [
"check_teamparticipation_circular",
"check_teamparticipation_consistency",
- "check_teamparticipation_self",
"fetch_team_participation_info",
+ "fix_teamparticipation_consistency",
]
from collections import (
@@ -26,17 +26,29 @@
import transaction
from zope.component import getUtility
-from canonical.database.sqlbase import quote
+from canonical.database.sqlbase import (
+ quote,
+ sqlvalues,
+ )
from canonical.launchpad.webapp.interfaces import (
IStoreSelector,
MAIN_STORE,
+ MASTER_FLAVOR,
SLAVE_FLAVOR,
)
from lp.registry.interfaces.teammembership import ACTIVE_STATES
from lp.services.scripts.base import LaunchpadScriptFailure
-def get_store():
+def get_master_store():
+ """Return a master store.
+
+ Errors in `TeamPartipation` must be fixed in the master.
+ """
+ return getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR)
+
+
+def get_slave_store():
"""Return a slave store.
Errors in `TeamPartipation` can be detected using a replicated copy.
@@ -44,26 +56,6 @@
return getUtility(IStoreSelector).get(MAIN_STORE, SLAVE_FLAVOR)
-def check_teamparticipation_self(log):
- """Check self-participation.
-
- All people and teams should participate in themselves.
- """
- query = """
- SELECT id, name
- FROM Person
- WHERE id NOT IN (
- SELECT person FROM TeamParticipation
- WHERE person = team)
- AND merged IS NULL
- """
- non_self_participants = list(get_store().execute(query))
- if len(non_self_participants) > 0:
- log.warn(
- "Some people/teams are not members of themselves: %s",
- non_self_participants)
-
-
def check_teamparticipation_circular(log):
"""Check circular references.
@@ -77,7 +69,7 @@
AND tp.person = tp2.team
AND tp.id != tp2.id;
"""
- circular_references = list(get_store().execute(query))
+ circular_references = list(get_slave_store().execute(query))
if len(circular_references) > 0:
raise LaunchpadScriptFailure(
"Circular references found: %s" % circular_references)
@@ -117,7 +109,7 @@
def fetch_team_participation_info(log):
"""Fetch people, teams, memberships and participations."""
- slurp = partial(execute_long_query, get_store(), log, 10000)
+ slurp = partial(execute_long_query, get_slave_store(), log, 10000)
people = dict(
slurp(
@@ -209,3 +201,47 @@
get_repr(error.team), error.type, people_repr)
return errors
+
+
+def fix_teamparticipation_consistency(log, errors):
+ """Fix missing or spurious participations.
+
+ This function does not consult `TeamMembership` at all, so it /may/
+ introduce another participation inconsistency if the records that are the
+ subject of the given errors have been modified since being checked.
+
+ :param errors: An iterable of `ConsistencyError` tuples.
+ """
+ sql_missing = (
+ """
+ INSERT INTO TeamParticipation (team, person)
+ SELECT %(team)s, %(person)s
+ EXCEPT
+ SELECT team, person
+ FROM TeamParticipation
+ WHERE team = %(team)s
+ AND person = %(person)s
+ """)
+ sql_spurious = (
+ """
+ DELETE FROM TeamParticipation
+ WHERE team = %(team)s
+ AND person IN %(people)s
+ """)
+ store = get_master_store()
+ for error in errors:
+ if error.type == "missing":
+ for person in error.people:
+ statement = sql_missing % sqlvalues(
+ team=error.team, person=person)
+ log.debug(statement)
+ store.execute(statement)
+ transaction.commit()
+ elif error.type == "spurious":
+ statement = sql_spurious % sqlvalues(
+ team=error.team, people=error.people)
+ log.debug(statement)
+ store.execute(statement)
+ transaction.commit()
+ else:
+ log.warn("Unrecognized error: %r", error)
=== modified file 'lib/lp/registry/stories/gpg-coc/xx-gpg-coc.txt'
--- lib/lp/registry/stories/gpg-coc/xx-gpg-coc.txt 2011-12-08 19:18:27 +0000
+++ lib/lp/registry/stories/gpg-coc/xx-gpg-coc.txt 2011-12-11 01:49:34 +0000
@@ -87,9 +87,9 @@
Import the secret keys needed for this test:
>>> from zope.component import getUtility
- >>> from canonical.launchpad.interfaces.gpghandler import IGPGHandler
+ >>> from lp.services.gpg.interfaces import IGPGHandler
- >>> from canonical.launchpad.ftests import (
+ >>> from lp.testing.gpgkeys import (
... import_secret_test_key, decrypt_content)
@@ -518,7 +518,6 @@
Get the token from the body of the email sent.
>>> import email, re
- >>> from canonical.launchpad.ftests import decrypt_content
>>> from lp.services.mail import stub
>>> from_addr, to_addrs, raw_msg = stub.test_emails.pop()
>>> msg = email.message_from_string(raw_msg)
=== modified file 'lib/lp/registry/stories/webservice/xx-derivedistroseries.txt'
--- lib/lp/registry/stories/webservice/xx-derivedistroseries.txt 2011-11-15 01:01:55 +0000
+++ lib/lp/registry/stories/webservice/xx-derivedistroseries.txt 2011-12-11 01:49:34 +0000
@@ -19,6 +19,7 @@
>>> soyuz_team = factory.makeTeam(
... name='soyuz-team',
... subscription_policy=TeamSubscriptionPolicy.RESTRICTED)
+ >>> soyuz_team_owner = soyuz_team.teamowner
>>> parent_series = factory.makeDistroSeries(name="parentseries")
>>> arch = factory.makeDistroArchSeries(distroseries=parent_series)
>>> removeSecurityProxy(parent_series).nominatedarchindep = arch
@@ -41,7 +42,7 @@
>>> logout()
>>> soyuz_team_webservice = webservice_for_person(
- ... soyuz_team.teamowner, permission=OAuthPermission.WRITE_PUBLIC)
+ ... soyuz_team_owner, permission=OAuthPermission.WRITE_PUBLIC)
Calling
=== modified file 'lib/lp/registry/tests/test_person_vocabularies.py'
--- lib/lp/registry/tests/test_person_vocabularies.py 2011-11-07 00:38:37 +0000
+++ lib/lp/registry/tests/test_person_vocabularies.py 2011-12-11 01:49:34 +0000
@@ -311,9 +311,10 @@
def test_private_team_cannot_be_a_member_of_itself(self):
# A private team should be filtered by the vocab.extra_clause
# when provided a search term.
+ owner = self.factory.makePerson()
team = self.factory.makeTeam(
- visibility=PersonVisibility.PRIVATE)
- login_person(team.teamowner)
+ owner=owner, visibility=PersonVisibility.PRIVATE)
+ login_person(owner)
self.assertNotIn(team, self.searchVocabulary(team, team.name))
=== modified file 'lib/lp/registry/tests/test_team_webservice.py'
--- lib/lp/registry/tests/test_team_webservice.py 2010-10-21 00:07:32 +0000
+++ lib/lp/registry/tests/test_team_webservice.py 2011-12-11 01:49:34 +0000
@@ -5,8 +5,6 @@
import httplib
-from zope.security.proxy import removeSecurityProxy
-
from lazr.restfulclient.errors import HTTPError
from canonical.testing.layers import DatabaseFunctionalLayer
@@ -79,8 +77,9 @@
# Calling person.join with a team that has an open membership
# subscription policy should add that that user to the team.
self.person = self.factory.makePerson(name='test-person')
- self.team = self.factory.makeTeam(name='test-team')
- login_person(self.team.teamowner)
+ owner = self.factory.makePerson()
+ self.team = self.factory.makeTeam(name='test-team', owner=owner)
+ login_person(owner)
self.team.subscriptionpolicy = TeamSubscriptionPolicy.OPEN
logout()
@@ -88,7 +87,7 @@
test_person = launchpad.people['test-person']
test_team = launchpad.people['test-team']
test_person.join(team=test_team.self_link)
- login_person(self.team.teamowner)
+ login_person(owner)
self.assertEqual(
['test-team'],
[membership.team.name
=== modified file 'lib/lp/registry/tests/test_teammembership.py'
--- lib/lp/registry/tests/test_teammembership.py 2011-11-21 05:03:25 +0000
+++ lib/lp/registry/tests/test_teammembership.py 2011-12-11 01:49:34 +0000
@@ -59,9 +59,9 @@
from lp.registry.scripts.teamparticipation import (
check_teamparticipation_circular,
check_teamparticipation_consistency,
- check_teamparticipation_self,
ConsistencyError,
fetch_team_participation_info,
+ fix_teamparticipation_consistency,
)
from lp.services.log.logger import BufferLogger
from lp.testing import (
@@ -1120,8 +1120,6 @@
re.search('missing TeamParticipation entries for zzzzz', err))
self.failUnless(
re.search('spurious TeamParticipation entries for zzzzz', err))
- self.failUnless(
- re.search('not members of themselves:.*zzzzz.*', err))
def test_report_circular_team_references(self):
"""The script reports circular references between teams.
@@ -1157,32 +1155,58 @@
self.assertEqual(0, len(out))
self.failUnless(re.search('Circular references found', err))
- def test_report_spurious_participants_of_people(self):
+ # A script to create two new people, where both participate in the first,
+ # and first is missing a self-participation.
+ script_create_inconsistent_participation = """
+ INSERT INTO
+ Person (id, name, displayname, creation_rationale)
+ VALUES (6969, 'bobby', 'Dazzler', 1);
+ INSERT INTO
+ Person (id, name, displayname, creation_rationale)
+ VALUES (6970, 'nobby', 'Jazzler', 1);
+ INSERT INTO
+ TeamParticipation (person, team)
+ VALUES (6970, 6969);
+ DELETE FROM
+ TeamParticipation
+ WHERE person = 6969
+ AND team = 6969;
+ """
+
+ def test_check_teamparticipation_consistency(self):
"""The script reports spurious participants of people.
Teams can have multiple participants, but only the person should be a
paricipant of him/herself.
"""
- # Create two new people and make both participate in the first.
- cursor().execute("""
- INSERT INTO
- Person (id, name, displayname, creation_rationale)
- VALUES (6969, 'bobby', 'Dazzler', 1);
- INSERT INTO
- Person (id, name, displayname, creation_rationale)
- VALUES (6970, 'nobby', 'Jazzler', 1);
- INSERT INTO
- TeamParticipation (person, team)
- VALUES (6970, 6969);
- """ % sqlvalues(approved=TeamMembershipStatus.APPROVED))
- transaction.commit()
- logger = BufferLogger()
- self.addDetail("log", logger.content)
- errors = check_teamparticipation_consistency(
- logger, fetch_team_participation_info(logger))
- self.assertEqual(
- [ConsistencyError("spurious", 6969, [6970])],
- errors)
+ cursor().execute(self.script_create_inconsistent_participation)
+ transaction.commit()
+ logger = BufferLogger()
+ self.addDetail("log", logger.content)
+ errors = check_teamparticipation_consistency(
+ logger, fetch_team_participation_info(logger))
+ errors_expected = [
+ ConsistencyError("spurious", 6969, [6970]),
+ ConsistencyError("missing", 6969, [6969]),
+ ]
+ self.assertContentEqual(errors_expected, errors)
+
+ def test_fix_teamparticipation_consistency(self):
+ """
+ `fix_teamparticipation_consistency` takes an iterable of
+ `ConsistencyError`s and attempts to repair the data.
+ """
+ cursor().execute(self.script_create_inconsistent_participation)
+ transaction.commit()
+ logger = BufferLogger()
+ self.addDetail("log", logger.content)
+ errors = check_teamparticipation_consistency(
+ logger, fetch_team_participation_info(logger))
+ self.assertNotEqual([], errors)
+ fix_teamparticipation_consistency(logger, errors)
+ errors = check_teamparticipation_consistency(
+ logger, fetch_team_participation_info(logger))
+ self.assertEqual([], errors)
def test_load_and_save_team_participation(self):
"""The script can load and save participation info."""
@@ -1232,11 +1256,10 @@
logger = BufferLogger()
self.addDetail("log", logger.content)
with StormStatementRecorder() as recorder:
- check_teamparticipation_self(logger)
check_teamparticipation_circular(logger)
check_teamparticipation_consistency(
logger, fetch_team_participation_info(logger))
- self.assertThat(recorder, HasQueryCount(Equals(6)))
+ self.assertThat(recorder, HasQueryCount(Equals(5)))
def test_suite():
=== modified file 'lib/lp/scripts/garbo.py'
--- lib/lp/scripts/garbo.py 2011-12-08 05:13:31 +0000
+++ lib/lp/scripts/garbo.py 2011-12-11 01:49:34 +0000
@@ -46,7 +46,6 @@
from canonical.launchpad.database.emailaddress import EmailAddress
from canonical.launchpad.database.librarian import TimeLimitedToken
from canonical.launchpad.database.logintoken import LoginToken
-from canonical.launchpad.database.openidconsumer import OpenIDConsumerNonce
from canonical.launchpad.interfaces.account import AccountStatus
from canonical.launchpad.interfaces.emailaddress import EmailAddressStatus
from canonical.launchpad.interfaces.lpstorm import IMasterStore
@@ -78,6 +77,7 @@
from lp.services.job.model.job import Job
from lp.services.log.logger import PrefixFilter
from lp.services.oauth.model import OAuthNonce
+from lp.services.openid.model.openidconsumer import OpenIDConsumerNonce
from lp.services.propertycache import cachedproperty
from lp.services.scripts.base import (
LaunchpadCronScript,
=== modified file 'lib/lp/scripts/tests/test_garbo.py'
--- lib/lp/scripts/tests/test_garbo.py 2011-12-08 05:13:31 +0000
+++ lib/lp/scripts/tests/test_garbo.py 2011-12-11 01:49:34 +0000
@@ -44,7 +44,6 @@
)
from canonical.launchpad.database.librarian import TimeLimitedToken
from canonical.launchpad.database.logintoken import LoginToken
-from canonical.launchpad.database.openidconsumer import OpenIDConsumerNonce
from canonical.launchpad.interfaces.account import AccountStatus
from canonical.launchpad.interfaces.authtoken import LoginTokenType
from canonical.launchpad.interfaces.emailaddress import EmailAddressStatus
@@ -100,6 +99,7 @@
OAuthAccessToken,
OAuthNonce,
)
+from lp.services.openid.model.openidconsumer import OpenIDConsumerNonce
from lp.services.session.model import (
SessionData,
SessionPkgData,
=== modified file 'lib/lp/services/configure.zcml'
--- lib/lp/services/configure.zcml 2011-12-08 05:13:31 +0000
+++ lib/lp/services/configure.zcml 2011-12-11 01:49:34 +0000
@@ -9,6 +9,7 @@
<include package=".fields" />
<include package=".geoip" />
<include package=".googlesearch" />
+ <include package=".gpg" />
<include package=".inlinehelp" file="meta.zcml" />
<include package=".job" />
<include package=".longpoll" />
=== modified file 'lib/lp/services/features/testing.py'
--- lib/lp/services/features/testing.py 2011-10-27 15:04:26 +0000
+++ lib/lp/services/features/testing.py 2011-12-11 01:49:34 +0000
@@ -1,4 +1,4 @@
-# Copyright 2010 Canonical Ltd. This software is licensed under the
+# Copyright 2010,2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Helpers for writing tests that use feature flags."""
@@ -9,6 +9,8 @@
from fixtures import Fixture
+from lazr.restful.utils import get_current_browser_request
+
from lp.services.features import (
get_relevant_feature_controller,
install_feature_controller,
@@ -18,6 +20,9 @@
Rule,
StormFeatureRuleSource,
)
+from lp.services.features.scopes import (
+ ScopesFromRequest,
+ )
class FeatureFixture(Fixture):
@@ -37,14 +42,19 @@
The values are restored when the block exits.
"""
- def __init__(self, features_dict, full_feature_rules=None):
+ def __init__(self, features_dict, full_feature_rules=None,
+ override_scope_lookup=None):
"""Constructor.
:param features_dict: A dictionary-like object with keys and values
that are flag names and those flags' settings.
+ :param override_scope_lookup: If non-None, an argument that takes
+ a scope name and returns True if it matches. If not specified,
+ scopes are looked up from the current request.
"""
self.desired_features = features_dict
self.full_feature_rules = full_feature_rules
+ self.override_scope_lookup = override_scope_lookup
def setUp(self):
"""Set the feature flags that this fixture is responsible for."""
@@ -56,8 +66,15 @@
rule_source.setAllRules(self.makeNewRules())
original_controller = get_relevant_feature_controller()
+
+ def scope_lookup(scope_name):
+ request = get_current_browser_request()
+ return ScopesFromRequest(request).lookup(scope_name)
+
+ if self.override_scope_lookup:
+ scope_lookup = self.override_scope_lookup
install_feature_controller(
- FeatureController(lambda _: True, rule_source))
+ FeatureController(scope_lookup, rule_source))
self.addCleanup(install_feature_controller, original_controller)
def makeNewRules(self):
=== added directory 'lib/lp/services/gpg'
=== added file 'lib/lp/services/gpg/__init__.py'
=== renamed file 'lib/canonical/launchpad/zcml/gpghandler.zcml' => 'lib/lp/services/gpg/configure.zcml'
--- lib/canonical/launchpad/zcml/gpghandler.zcml 2011-05-02 12:55:06 +0000
+++ lib/lp/services/gpg/configure.zcml 2011-12-11 01:49:34 +0000
@@ -9,26 +9,26 @@
xmlns:zope="http://namespaces.zope.org/zope"
i18n_domain="launchpad">
- <class class="canonical.launchpad.utilities.GPGHandler">
- <allow interface="canonical.launchpad.interfaces.gpghandler.IGPGHandler" />
+ <class class="lp.services.gpg.handler.GPGHandler">
+ <allow interface="lp.services.gpg.interfaces.IGPGHandler" />
</class>
<securedutility
- class="canonical.launchpad.utilities.GPGHandler"
- provides="canonical.launchpad.interfaces.gpghandler.IGPGHandler">
- <allow interface="canonical.launchpad.interfaces.gpghandler.IGPGHandler" />
+ class="lp.services.gpg.handler.GPGHandler"
+ provides="lp.services.gpg.interfaces.IGPGHandler">
+ <allow interface="lp.services.gpg.interfaces.IGPGHandler" />
</securedutility>
- <class class="canonical.launchpad.utilities.PymeSignature">
- <allow interface="canonical.launchpad.interfaces.gpghandler.IPymeSignature" />
- </class>
-
- <class class="canonical.launchpad.utilities.PymeKey">
- <allow interface="canonical.launchpad.interfaces.gpghandler.IPymeKey" />
- </class>
-
- <class class="canonical.launchpad.utilities.PymeUserId">
- <allow interface="canonical.launchpad.interfaces.gpghandler.IPymeUserId" />
+ <class class="lp.services.gpg.handler.PymeSignature">
+ <allow interface="lp.services.gpg.interfaces.IPymeSignature" />
+ </class>
+
+ <class class="lp.services.gpg.handler.PymeKey">
+ <allow interface="lp.services.gpg.interfaces.IPymeKey" />
+ </class>
+
+ <class class="lp.services.gpg.handler.PymeUserId">
+ <allow interface="lp.services.gpg.interfaces.IPymeUserId" />
</class>
</configure>
=== added directory 'lib/lp/services/gpg/doc'
=== renamed file 'lib/canonical/launchpad/doc/gpg-encryption.txt' => 'lib/lp/services/gpg/doc/gpg-encryption.txt'
--- lib/canonical/launchpad/doc/gpg-encryption.txt 2010-10-18 22:24:59 +0000
+++ lib/lp/services/gpg/doc/gpg-encryption.txt 2011-12-11 01:49:34 +0000
@@ -5,7 +5,7 @@
decrypt contents in Launchpad, and demonstrates that the methods
can support Unicode contents.
- >>> from canonical.launchpad.ftests import (
+ >>> from lp.testing.gpgkeys import (
... import_public_test_keys, import_secret_test_key, decrypt_content)
>>> import transaction
>>> import_public_test_keys()
@@ -28,7 +28,7 @@
>>> bag.user.name
u'name12'
- >>> from canonical.launchpad.interfaces.gpghandler import IGPGHandler
+ >>> from lp.services.gpg.interfaces import IGPGHandler
>>> gpghandler = getUtility(IGPGHandler)
Let's use a unicode content, it can't be directly typed as
=== renamed file 'lib/lp/registry/doc/gpg-signatures.txt' => 'lib/lp/services/gpg/doc/gpg-signatures.txt'
--- lib/lp/registry/doc/gpg-signatures.txt 2010-10-18 22:24:59 +0000
+++ lib/lp/services/gpg/doc/gpg-signatures.txt 2011-12-11 01:49:34 +0000
@@ -1,7 +1,7 @@
OpenPGP Signature Verification
==============================
- >>> from canonical.launchpad.ftests import import_public_test_keys
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> import transaction
>>> import_public_test_keys()
@@ -20,7 +20,7 @@
u'name12'
>>> from zope.component import getUtility
- >>> from canonical.launchpad.interfaces.gpghandler import IGPGHandler
+ >>> from lp.services.gpg.interfaces import IGPGHandler
>>> gpghandler = getUtility(IGPGHandler)
The text below was "clear signed" by 0xDFD20543 master key:
=== renamed file 'lib/canonical/launchpad/doc/gpghandler.txt' => 'lib/lp/services/gpg/doc/gpghandler.txt'
--- lib/canonical/launchpad/doc/gpghandler.txt 2011-05-30 12:45:29 +0000
+++ lib/lp/services/gpg/doc/gpghandler.txt 2011-12-11 01:49:34 +0000
@@ -25,7 +25,7 @@
>>> from zope.component import getUtility
>>> from canonical.launchpad.webapp.testing import verifyObject
- >>> from canonical.launchpad.interfaces.gpghandler import (
+ >>> from lp.services.gpg.interfaces import (
... IGPGHandler,
... IPymeKey,
... )
@@ -53,7 +53,7 @@
Let's recover some coherent data and verify if it works as expected:
>>> import os
- >>> from canonical.launchpad.ftests.keys_for_tests import gpgkeysdir
+ >>> from lp.testing.gpgkeys import gpgkeysdir
>>> filepath = os.path.join(gpgkeysdir, 'test@xxxxxxxxxxxxxxxxx')
>>> pubkey = open(filepath).read()
>>> key = gpghandler.importPublicKey(pubkey)
=== renamed file 'lib/canonical/launchpad/utilities/gpghandler.py' => 'lib/lp/services/gpg/handler.py'
--- lib/canonical/launchpad/utilities/gpghandler.py 2011-09-13 05:23:16 +0000
+++ lib/lp/services/gpg/handler.py 2011-12-11 01:49:34 +0000
@@ -28,7 +28,17 @@
from zope.interface import implements
from canonical.config import config
-from canonical.launchpad.interfaces.gpghandler import (
+from canonical.launchpad.webapp import errorlog
+from canonical.lazr.timeout import (
+ TimeoutError,
+ urlfetch,
+ )
+from lp.app.validators.email import valid_email
+from lp.registry.interfaces.gpg import (
+ GPGKeyAlgorithm,
+ valid_fingerprint,
+ )
+from lp.services.gpg.interfaces import (
GPGKeyDoesNotExistOnServer,
GPGKeyExpired,
GPGKeyNotFoundError,
@@ -43,16 +53,6 @@
MoreThanOneGPGKeyFound,
SecretGPGKeyImportDetected,
)
-from canonical.launchpad.webapp import errorlog
-from canonical.lazr.timeout import (
- TimeoutError,
- urlfetch,
- )
-from lp.app.validators.email import valid_email
-from lp.registry.interfaces.gpg import (
- GPGKeyAlgorithm,
- valid_fingerprint,
- )
from lp.services.timeline.requesttimeline import get_request_timeline
=== renamed file 'lib/canonical/launchpad/interfaces/gpghandler.py' => 'lib/lp/services/gpg/interfaces.py'
=== added directory 'lib/lp/services/gpg/tests'
=== added file 'lib/lp/services/gpg/tests/__init__.py'
=== added file 'lib/lp/services/gpg/tests/test_doc.py'
--- lib/lp/services/gpg/tests/test_doc.py 1970-01-01 00:00:00 +0000
+++ lib/lp/services/gpg/tests/test_doc.py 2011-12-11 01:49:34 +0000
@@ -0,0 +1,18 @@
+# Copyright 2009 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""
+Run the doctests and pagetests.
+"""
+
+import os
+
+from canonical.testing.layers import LaunchpadFunctionalLayer
+from lp.services.testing import build_test_suite
+
+
+here = os.path.dirname(os.path.realpath(__file__))
+
+
+def test_suite():
+ return build_test_suite(here, layer=LaunchpadFunctionalLayer)
=== renamed file 'lib/canonical/launchpad/utilities/ftests/test_gpghandler.py' => 'lib/lp/services/gpg/tests/test_gpghandler.py'
--- lib/canonical/launchpad/utilities/ftests/test_gpghandler.py 2011-10-28 06:43:50 +0000
+++ lib/lp/services/gpg/tests/test_gpghandler.py 2011-12-11 01:49:34 +0000
@@ -11,31 +11,35 @@
from time import time
from pytz import UTC
+from testtools.matchers import (
+ LessThan,
+ Not,
+ )
from zope.component import getUtility
from zope.security.proxy import removeSecurityProxy
-from testtools.matchers import (
- Not,
- LessThan,
- )
-
from canonical.launchpad.ftests import (
ANONYMOUS,
- keys_for_tests,
login,
logout,
)
-from canonical.launchpad.interfaces.gpghandler import (
- GPGKeyDoesNotExistOnServer,
- GPGKeyTemporarilyNotFoundError,
- IGPGHandler,
- )
from canonical.lazr.timeout import (
get_default_timeout_function,
set_default_timeout_function,
)
from canonical.testing.layers import LaunchpadFunctionalLayer
+from lp.services.gpg.interfaces import (
+ GPGKeyDoesNotExistOnServer,
+ GPGKeyTemporarilyNotFoundError,
+ IGPGHandler,
+ )
from lp.testing import TestCase
+from lp.testing.gpgkeys import (
+ import_secret_test_key,
+ iter_test_key_emails,
+ test_keyrings,
+ test_pubkey_from_email,
+ )
from lp.testing.keyserver import KeyServerTac
@@ -59,8 +63,8 @@
super(TestImportKeyRing, self).tearDown()
def populateKeyring(self):
- for email in keys_for_tests.iter_test_key_emails():
- pubkey = keys_for_tests.test_pubkey_from_email(email)
+ for email in iter_test_key_emails():
+ pubkey = test_pubkey_from_email(email)
self.gpg_handler.importPublicKey(pubkey)
# This sequence might fit better as a doctest. Hmm.
@@ -113,7 +117,7 @@
list(self.gpg_handler.localKeys(secret=True)), [])
# Import a secret key and look it up.
- keys_for_tests.import_secret_test_key()
+ import_secret_test_key()
secret_target_fpr = 'A419AE861E88BC9E04B9C26FBA2B9389DFD20543'
filtered_keys = self.gpg_handler.localKeys(secret=True)
@@ -150,7 +154,7 @@
def testTestkeyrings(self):
"""Do we have the expected test keyring files"""
- self.assertEqual(len(list(keys_for_tests.test_keyrings())), 1)
+ self.assertEqual(len(list(test_keyrings())), 1)
def testHomeDirectoryJob(self):
"""Does the job to touch the home work."""
=== modified file 'lib/lp/services/mail/doc/emailauthentication.txt'
--- lib/lp/services/mail/doc/emailauthentication.txt 2011-08-12 15:57:11 +0000
+++ lib/lp/services/mail/doc/emailauthentication.txt 2011-12-11 01:49:34 +0000
@@ -12,8 +12,8 @@
>>> from canonical.config import config
>>> from canonical.database.sqlbase import commit
- >>> from canonical.launchpad.ftests import import_public_test_keys
>>> from canonical.testing.layers import LaunchpadZopelessLayer
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> LaunchpadZopelessLayer.switchDbUser('launchpad')
>>> import_public_test_keys()
@@ -119,7 +119,7 @@
... '\n'.join(msg_lines), _class=SignedMessage)
>>> msg.parsed_string = msg.as_string()
- >>> from canonical.launchpad.interfaces.gpghandler import IGPGHandler
+ >>> from lp.services.gpg.interfaces import IGPGHandler
>>> getUtility(IGPGHandler).getVerifiedSignature(
... msg.signedContent, msg.signature)
Traceback (most recent call last):
=== modified file 'lib/lp/services/mail/incoming.py'
--- lib/lp/services/mail/incoming.py 2011-10-28 06:43:50 +0000
+++ lib/lp/services/mail/incoming.py 2011-12-11 01:49:34 +0000
@@ -27,10 +27,6 @@
)
from canonical.launchpad.interfaces.account import AccountStatus
-from canonical.launchpad.interfaces.gpghandler import (
- GPGVerificationError,
- IGPGHandler,
- )
from canonical.launchpad.mailnotification import (
send_process_error_notification,
)
@@ -46,6 +42,10 @@
from canonical.librarian.interfaces import UploadFailed
from lp.registry.interfaces.person import IPerson
from lp.services.features import getFeatureFlag
+from lp.services.gpg.interfaces import (
+ GPGVerificationError,
+ IGPGHandler,
+ )
from lp.services.mail.handlers import mail_handlers
from lp.services.mail.helpers import (
ensure_sane_signature_timestamp,
=== modified file 'lib/lp/services/mail/tests/incomingmail.txt'
--- lib/lp/services/mail/tests/incomingmail.txt 2011-10-25 21:24:59 +0000
+++ lib/lp/services/mail/tests/incomingmail.txt 2011-12-11 01:49:34 +0000
@@ -74,7 +74,7 @@
import the keys before handleMail is called.
>>> from canonical.config import config
- >>> from canonical.launchpad.ftests import import_public_test_keys
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> import_public_test_keys()
>>> commit()
>>> LaunchpadZopelessLayer.switchDbUser(config.processmail.dbuser)
=== modified file 'lib/lp/services/mail/tests/test_incoming.py'
--- lib/lp/services/mail/tests/test_incoming.py 2011-10-17 01:45:44 +0000
+++ lib/lp/services/mail/tests/test_incoming.py 2011-12-11 01:49:34 +0000
@@ -14,7 +14,6 @@
from zope.security.management import setSecurityPolicy
from canonical.config import config
-from canonical.launchpad.ftests import import_secret_test_key
from canonical.launchpad.testing.systemdocs import LayeredDocFileSuite
from canonical.launchpad.webapp.authorization import LaunchpadSecurityPolicy
from canonical.testing.layers import LaunchpadZopelessLayer
@@ -31,6 +30,7 @@
from lp.services.mail.tests.helpers import testmails_path
from lp.testing import TestCaseWithFactory
from lp.testing.factory import GPGSigningContext
+from lp.testing.gpgkeys import import_secret_test_key
from lp.testing.mail_helpers import pop_notifications
=== modified file 'lib/lp/services/mail/tests/test_signedmessage.py'
--- lib/lp/services/mail/tests/test_signedmessage.py 2011-08-13 04:07:10 +0000
+++ lib/lp/services/mail/tests/test_signedmessage.py 2011-12-11 01:49:34 +0000
@@ -17,13 +17,9 @@
import gpgme
from zope.component import getUtility
-from canonical.launchpad.ftests import (
- import_public_test_keys,
- import_secret_test_key,
- )
-from canonical.launchpad.interfaces.gpghandler import IGPGHandler
from canonical.testing.layers import DatabaseFunctionalLayer
from lp.registry.interfaces.person import IPersonSet
+from lp.services.gpg.interfaces import IGPGHandler
from lp.services.mail.incoming import (
authenticateEmail,
canonicalise_line_endings,
@@ -32,6 +28,10 @@
from lp.services.mail.signedmessage import signed_message_from_string
from lp.testing import TestCaseWithFactory
from lp.testing.factory import GPGSigningContext
+from lp.testing.gpgkeys import (
+ import_public_test_keys,
+ import_secret_test_key,
+ )
class TestSignedMessage(TestCaseWithFactory):
=== modified file 'lib/lp/services/openid/configure.zcml'
--- lib/lp/services/openid/configure.zcml 2011-09-04 12:46:14 +0000
+++ lib/lp/services/openid/configure.zcml 2011-12-11 01:49:34 +0000
@@ -21,4 +21,8 @@
<adapter factory=".adapters.openid.OpenIDPersistentIdentity" />
<adapter factory=".adapters.openid.person_to_openidpersistentidentity" />
+ <utility
+ provides=".interfaces.openidconsumer.IOpenIDConsumerStore"
+ factory=".model.openidconsumer.OpenIDConsumerStore" />
+
</configure>
=== renamed file 'lib/canonical/launchpad/interfaces/openidconsumer.py' => 'lib/lp/services/openid/interfaces/openidconsumer.py'
=== renamed file 'lib/canonical/launchpad/database/baseopenidstore.py' => 'lib/lp/services/openid/model/baseopenidstore.py'
=== renamed file 'lib/canonical/launchpad/database/openidconsumer.py' => 'lib/lp/services/openid/model/openidconsumer.py'
--- lib/canonical/launchpad/database/openidconsumer.py 2010-08-20 20:31:18 +0000
+++ lib/lp/services/openid/model/openidconsumer.py 2011-12-11 01:49:34 +0000
@@ -8,12 +8,12 @@
from zope.interface import implements
-from canonical.launchpad.database.baseopenidstore import (
+from lp.services.openid.interfaces.openidconsumer import IOpenIDConsumerStore
+from lp.services.openid.model.baseopenidstore import (
BaseStormOpenIDAssociation,
BaseStormOpenIDNonce,
BaseStormOpenIDStore,
)
-from canonical.launchpad.interfaces.openidconsumer import IOpenIDConsumerStore
class OpenIDConsumerAssociation(BaseStormOpenIDAssociation):
=== renamed file 'lib/canonical/launchpad/database/tests/test_baseopenidstore.py' => 'lib/lp/services/openid/tests/test_baseopenidstore.py'
--- lib/canonical/launchpad/database/tests/test_baseopenidstore.py 2010-08-20 20:31:18 +0000
+++ lib/lp/services/openid/tests/test_baseopenidstore.py 2011-12-11 01:49:34 +0000
@@ -14,8 +14,8 @@
from openid.association import Association
from openid.store import nonce
-from canonical.launchpad.database.baseopenidstore import BaseStormOpenIDStore
from canonical.launchpad.interfaces.lpstorm import IMasterStore
+from lp.services.openid.model.baseopenidstore import BaseStormOpenIDStore
class BaseStormOpenIDStoreTestsMixin:
=== renamed file 'lib/canonical/launchpad/database/tests/test_openidconsumer.py' => 'lib/lp/services/openid/tests/test_openidconsumer.py'
--- lib/canonical/launchpad/database/tests/test_openidconsumer.py 2011-08-12 11:37:08 +0000
+++ lib/lp/services/openid/tests/test_openidconsumer.py 2011-12-11 01:49:34 +0000
@@ -7,11 +7,11 @@
from zope.component import getUtility
-from canonical.launchpad.database.tests.test_baseopenidstore import (
+from canonical.testing.layers import DatabaseFunctionalLayer
+from lp.services.openid.interfaces.openidconsumer import IOpenIDConsumerStore
+from lp.services.openid.tests.test_baseopenidstore import (
BaseStormOpenIDStoreTestsMixin,
)
-from canonical.launchpad.interfaces.openidconsumer import IOpenIDConsumerStore
-from canonical.testing.layers import DatabaseFunctionalLayer
from lp.testing import TestCase
=== modified file 'lib/lp/soyuz/browser/tests/distroseriesqueue-views.txt'
--- lib/lp/soyuz/browser/tests/distroseriesqueue-views.txt 2011-01-17 21:51:09 +0000
+++ lib/lp/soyuz/browser/tests/distroseriesqueue-views.txt 2011-12-11 01:49:34 +0000
@@ -253,12 +253,12 @@
... sourcename="foo", distroseries=hoary, version="1.0-2",
... status=PackagePublishingStatus.PUBLISHED)
- >>> from canonical.launchpad.ftests import import_public_test_keys
>>> from lp.archiveuploader.uploadpolicy import ArchiveUploadType
+ >>> from lp.archiveuploader.tests import datadir, getPolicy
+ >>> from lp.archiveuploader.nascentupload import NascentUpload
>>> from lp.soyuz.interfaces.component import IComponentSet
>>> from lp.soyuz.model.component import ComponentSelection
- >>> from lp.archiveuploader.tests import datadir, getPolicy
- >>> from lp.archiveuploader.nascentupload import NascentUpload
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> import_public_test_keys()
>>> universe = getUtility(IComponentSet)['universe']
>>> trash = ComponentSelection(distroseries=hoary, component=universe)
=== modified file 'lib/lp/soyuz/browser/tests/test_archive_packages.py'
--- lib/lp/soyuz/browser/tests/test_archive_packages.py 2011-05-27 21:12:25 +0000
+++ lib/lp/soyuz/browser/tests/test_archive_packages.py 2011-12-11 01:49:34 +0000
@@ -22,13 +22,19 @@
from canonical.launchpad.testing.pages import get_feedback_messages
from canonical.launchpad.webapp import canonical_url
from canonical.launchpad.webapp.authentication import LaunchpadPrincipal
-from canonical.testing.layers import LaunchpadFunctionalLayer
+from canonical.testing.layers import (
+ DatabaseFunctionalLayer,
+ LaunchpadFunctionalLayer,
+ )
from lp.app.utilities.celebrities import ILaunchpadCelebrities
from lp.soyuz.browser.archive import ArchiveNavigationMenu
+from lp.soyuz.enums import PackagePublishingStatus
from lp.testing import (
+ celebrity_logged_in,
login,
login_person,
person_logged_in,
+ record_two_runs,
TestCaseWithFactory,
)
from lp.testing._webservice import QueryCollector
@@ -258,3 +264,40 @@
url = canonical_url(ppa) + "/+packages"
browser.open(url)
self.assertThat(collector, HasQueryCount(Equals(expected_count)))
+
+
+class TestP3APackagesQueryCount(TestCaseWithFactory):
+
+ layer = DatabaseFunctionalLayer
+
+ def setUp(self):
+ super(TestP3APackagesQueryCount, self).setUp()
+ self.team = self.factory.makeTeam()
+ login_person(self.team.teamowner)
+ self.person = self.factory.makePerson()
+
+ self.private_ppa = self.factory.makeArchive(
+ owner=self.team, private=True)
+ self.private_ppa.newSubscription(
+ self.person, registrant=self.team.teamowner)
+
+ def createPackage(self):
+ with celebrity_logged_in('admin'):
+ pkg = self.factory.makeBinaryPackagePublishingHistory(
+ status=PackagePublishingStatus.PUBLISHED,
+ archive=self.private_ppa)
+ return pkg
+
+ def test_ppa_index_queries_count(self):
+ def ppa_index_render():
+ with person_logged_in(self.person):
+ view = create_initialized_view(
+ self.private_ppa, '+index',
+ principal=self.person)
+ view.page_title = "title"
+ view.render()
+ recorder1, recorder2 = record_two_runs(
+ ppa_index_render, self.createPackage, 2, 3)
+
+ self.assertThat(
+ recorder2, HasQueryCount(LessThan(recorder1.count + 1)))
=== modified file 'lib/lp/soyuz/browser/tests/test_archive_webservice.py'
--- lib/lp/soyuz/browser/tests/test_archive_webservice.py 2011-12-05 16:01:37 +0000
+++ lib/lp/soyuz/browser/tests/test_archive_webservice.py 2011-12-11 01:49:34 +0000
@@ -3,6 +3,8 @@
__metaclass__ = type
+from datetime import timedelta
+
from lazr.restfulclient.errors import (
BadRequest,
NotFound,
@@ -377,3 +379,28 @@
job_source = getUtility(IPlainPackageCopyJobSource)
copy_job = job_source.getActiveJobs(target_archive).one()
self.assertEqual(target_archive, copy_job.target_archive)
+
+
+class TestgetPublishedBinaries(WebServiceTestCase):
+ """test getPublishedSources."""
+
+ def test_getPublishedBinaries(self):
+ self.ws_version = 'beta'
+ person = self.factory.makePerson()
+ archive = self.factory.makeArchive()
+ self.factory.makeBinaryPackagePublishingHistory(archive=archive)
+ ws_archive = self.wsObject(archive, user=person)
+ self.assertEqual(1, len(ws_archive.getPublishedBinaries()))
+
+ def test_getPublishedBinaries_created_since_date(self):
+ self.ws_version = 'beta'
+ person = self.factory.makePerson()
+ archive = self.factory.makeArchive()
+ datecreated = self.factory.getUniqueDate()
+ later_date = datecreated + timedelta(minutes=1)
+ self.factory.makeBinaryPackagePublishingHistory(
+ archive=archive, datecreated=datecreated)
+ ws_archive = self.wsObject(archive, user=person)
+ publications = ws_archive.getPublishedBinaries(
+ created_since_date=later_date)
+ self.assertEqual(0, len(publications))
=== modified file 'lib/lp/soyuz/browser/tests/test_builder_views.py'
--- lib/lp/soyuz/browser/tests/test_builder_views.py 2011-12-05 00:45:24 +0000
+++ lib/lp/soyuz/browser/tests/test_builder_views.py 2011-12-11 01:49:34 +0000
@@ -14,10 +14,7 @@
from zope.component import getUtility
from zope.security.proxy import removeSecurityProxy
-from canonical.database.sqlbase import (
- flush_database_caches,
- flush_database_updates,
- )
+from canonical.database.sqlbase import flush_database_updates
from canonical.launchpad.ftests import login
from canonical.launchpad.webapp.servers import LaunchpadTestRequest
from canonical.testing.layers import LaunchpadFunctionalLayer
@@ -34,6 +31,7 @@
from lp.soyuz.browser.builder import BuilderEditView
from lp.testing import (
celebrity_logged_in,
+ record_two_runs,
StormStatementRecorder,
TestCaseWithFactory,
)
@@ -216,35 +214,15 @@
self.addFakeBuildLog(build)
return build
- def _record_queries_count(self, tested_method, item_creator):
- # A simple helper that returns the two storm statement recorders
- # obtained when running tested_method with {nb_objects} items creater
- # (using item_creator) and then with {nb_objects}*2 items created.
- for i in range(self.nb_objects):
- item_creator()
- # Record how many queries are issued when tested_method is
- # called with {nb_objects} items created.
- flush_database_caches()
- with StormStatementRecorder() as recorder1:
- tested_method()
- # Create {nb_objects} more items.
- for i in range(self.nb_objects):
- item_creator()
- # Record again the number of queries issued.
- flush_database_caches()
- with StormStatementRecorder() as recorder2:
- tested_method()
- return recorder1, recorder2
-
def test_build_history_queries_count_view_recipe_builds(self):
# The builder's history view creation (i.e. the call to
# view.setupBuildList) issues a constant number of queries
# when recipe builds are displayed.
def builder_history_render():
create_initialized_view(self.builder, '+history').render()
- recorder1, recorder2 = self._record_queries_count(
- builder_history_render,
- self.createRecipeBuildWithBuilder)
+ recorder1, recorder2 = record_two_runs(
+ builder_history_render, self.createRecipeBuildWithBuilder,
+ self.nb_objects)
# XXX: rvb 2011-11-14 bug=890326: The only query remaining is the
# one that results from a call to
@@ -258,9 +236,9 @@
# when binary builds are displayed.
def builder_history_render():
create_initialized_view(self.builder, '+history').render()
- recorder1, recorder2 = self._record_queries_count(
- builder_history_render,
- self.createBinaryPackageBuild)
+ recorder1, recorder2 = record_two_runs(
+ builder_history_render, self.createBinaryPackageBuild,
+ self.nb_objects)
self.assertThat(recorder2, HasQueryCount(Equals(recorder1.count)))
@@ -271,8 +249,9 @@
create_initialized_view(self.builder, '+history').render()
createBinaryPackageBuildInPPA = partial(
self.createBinaryPackageBuild, in_ppa=True)
- recorder1, recorder2 = self._record_queries_count(
- builder_history_render, createBinaryPackageBuildInPPA)
+ recorder1, recorder2 = record_two_runs(
+ builder_history_render, createBinaryPackageBuildInPPA,
+ self.nb_objects)
self.assertThat(recorder2, HasQueryCount(Equals(recorder1.count)))
@@ -281,9 +260,9 @@
# when translation template builds are displayed.
def builder_history_render():
create_initialized_view(self.builder, '+history').render()
- recorder1, recorder2 = self._record_queries_count(
+ recorder1, recorder2 = record_two_runs(
builder_history_render,
- self.createTranslationTemplateBuildWithBuilder)
+ self.createTranslationTemplateBuildWithBuilder, self.nb_objects)
self.assertThat(recorder2, HasQueryCount(Equals(recorder1.count)))
=== modified file 'lib/lp/soyuz/doc/distroseriesqueue-ddtp-tarball.txt'
--- lib/lp/soyuz/doc/distroseriesqueue-ddtp-tarball.txt 2011-06-16 08:10:40 +0000
+++ lib/lp/soyuz/doc/distroseriesqueue-ddtp-tarball.txt 2011-12-11 01:49:34 +0000
@@ -29,7 +29,7 @@
>>> from lp.archiveuploader.nascentupload import NascentUpload
>>> from lp.archiveuploader.tests import datadir, getPolicy
- >>> from canonical.launchpad.ftests import import_public_test_keys
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> import_public_test_keys()
Login as an admin (or ubuntutest.archive_admin if we have one), since
=== modified file 'lib/lp/soyuz/doc/distroseriesqueue-debian-installer.txt'
--- lib/lp/soyuz/doc/distroseriesqueue-debian-installer.txt 2010-12-22 20:46:21 +0000
+++ lib/lp/soyuz/doc/distroseriesqueue-debian-installer.txt 2011-12-11 01:49:34 +0000
@@ -38,7 +38,7 @@
debian-installer tarball, despite the fact that it's very unlikely to
happen in production:
- >>> from canonical.launchpad.ftests import import_public_test_keys
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> import_public_test_keys()
>>> login('foo.bar@xxxxxxxxxxxxx')
=== modified file 'lib/lp/soyuz/doc/distroseriesqueue-dist-upgrader.txt'
--- lib/lp/soyuz/doc/distroseriesqueue-dist-upgrader.txt 2011-10-21 11:14:26 +0000
+++ lib/lp/soyuz/doc/distroseriesqueue-dist-upgrader.txt 2011-12-11 01:49:34 +0000
@@ -11,7 +11,7 @@
>>> ubuntutest = getUtility(IDistributionSet)['ubuntutest']
>>> breezy_autotest = ubuntutest['breezy-autotest']
- >>> from canonical.launchpad.ftests import import_public_test_keys
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> import_public_test_keys()
Login as an admin.
=== modified file 'lib/lp/soyuz/doc/distroseriesqueue-translations.txt'
--- lib/lp/soyuz/doc/distroseriesqueue-translations.txt 2011-11-29 05:15:07 +0000
+++ lib/lp/soyuz/doc/distroseriesqueue-translations.txt 2011-12-11 01:49:34 +0000
@@ -25,7 +25,7 @@
>>> from lp.archiveuploader.nascentupload import NascentUpload
>>> from lp.archiveuploader.tests import datadir, getPolicy
- >>> from canonical.launchpad.ftests import import_public_test_keys
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> import_public_test_keys()
>>> from canonical.database.constants import UTC_NOW
=== modified file 'lib/lp/soyuz/doc/distroseriesqueue.txt'
--- lib/lp/soyuz/doc/distroseriesqueue.txt 2011-06-28 15:04:29 +0000
+++ lib/lp/soyuz/doc/distroseriesqueue.txt 2011-12-11 01:49:34 +0000
@@ -39,7 +39,7 @@
First up, we need to actually process an upload to get it into the
queue. To do this we prepare an OpenPGP key, and then run the upload handler.
- >>> from canonical.launchpad.ftests import import_public_test_keys
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> import_public_test_keys()
We need some setup for the upload handler.
=== modified file 'lib/lp/soyuz/doc/fakepackager.txt'
--- lib/lp/soyuz/doc/fakepackager.txt 2011-06-09 10:50:25 +0000
+++ lib/lp/soyuz/doc/fakepackager.txt 2011-12-11 01:49:34 +0000
@@ -160,7 +160,7 @@
>>> content = open(changesfile_path).read()
>>> from zope.component import getUtility
- >>> from canonical.launchpad.interfaces.gpghandler import IGPGHandler
+ >>> from lp.services.gpg.interfaces import IGPGHandler
>>> gpghandler = getUtility(IGPGHandler)
>>> sig = gpghandler.verifySignature(content)
@@ -225,7 +225,7 @@
It also requires the public test gpg keys to be imported in the
database.
- >>> from canonical.launchpad.ftests import import_public_test_keys
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> import_public_test_keys()
The default upload target is ubuntu/hoary and since we will deal with
=== modified file 'lib/lp/soyuz/doc/package-diff.txt'
--- lib/lp/soyuz/doc/package-diff.txt 2010-11-06 12:50:22 +0000
+++ lib/lp/soyuz/doc/package-diff.txt 2011-12-11 01:49:34 +0000
@@ -143,7 +143,7 @@
And setup the test_keys in order to build and upload signed packages.
- >>> from canonical.launchpad.ftests import import_public_test_keys
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> import_public_test_keys()
When the first version of 'biscuit' is uploaded, since there is no
=== modified file 'lib/lp/soyuz/doc/soyuz-set-of-uploads.txt'
--- lib/lp/soyuz/doc/soyuz-set-of-uploads.txt 2011-08-09 06:30:01 +0000
+++ lib/lp/soyuz/doc/soyuz-set-of-uploads.txt 2011-12-11 01:49:34 +0000
@@ -68,7 +68,7 @@
Import public keyring into current LPDB.
- >>> from canonical.launchpad.ftests import import_public_test_keys
+ >>> from lp.testing.gpgkeys import import_public_test_keys
>>> import_public_test_keys()
Having set up that infrastructure we need to prepare a breezy distroseries
=== modified file 'lib/lp/soyuz/doc/soyuz-upload.txt'
--- lib/lp/soyuz/doc/soyuz-upload.txt 2011-09-29 13:01:04 +0000
+++ lib/lp/soyuz/doc/soyuz-upload.txt 2011-12-11 01:49:34 +0000
@@ -156,8 +156,8 @@
So, load the GPG key:
>>> from zope.component import getUtility
- >>> from canonical.launchpad.ftests.keys_for_tests import gpgkeysdir
- >>> from canonical.launchpad.interfaces.gpghandler import IGPGHandler
+ >>> from lp.services.gpg.interfaces import IGPGHandler
+ >>> from lp.testing.gpgkeys import gpgkeysdir
>>> gpg_handler = getUtility(IGPGHandler)
>>> key_path = os.path.join(gpgkeysdir, 'ftpmaster@xxxxxxxxxxxxxxxxx')
>>> key_data = open(key_path).read()
=== modified file 'lib/lp/soyuz/interfaces/archive.py'
--- lib/lp/soyuz/interfaces/archive.py 2011-12-05 16:01:37 +0000
+++ lib/lp/soyuz/interfaces/archive.py 2011-12-11 01:49:34 +0000
@@ -1004,6 +1004,11 @@
# Really PackagePublishingPocket, circular import fixed below.
vocabulary=DBEnumeratedType,
required=False, readonly=True),
+ created_since_date=Datetime(
+ title=_("Created Since Date"),
+ description=_("Return entries whose `date_created` is greater "
+ "than or equal to this date."),
+ required=False),
exact_match=Bool(
description=_("Whether or not to filter binary names by exact "
"matching."),
@@ -1015,7 +1020,7 @@
@export_read_operation()
def getAllPublishedBinaries(name=None, version=None, status=None,
distroarchseries=None, pocket=None,
- exact_match=False):
+ exact_match=False, created_since_date=None):
"""All `IBinaryPackagePublishingHistory` target to this archive.
:param: name: binary name filter (exact match or SQL LIKE controlled
@@ -1026,6 +1031,8 @@
:param: pocket: `PackagePublishingPocket` filter.
:param: exact_match: either or not filter source names by exact
matching.
+ :param: created_since_date: a filter on teh `date_created` of the
+ publishing record.
:return: A collection containing `BinaryPackagePublishingHistory`.
"""
=== modified file 'lib/lp/soyuz/model/archive.py'
--- lib/lp/soyuz/model/archive.py 2011-12-08 22:32:41 +0000
+++ lib/lp/soyuz/model/archive.py 2011-12-11 01:49:34 +0000
@@ -714,7 +714,7 @@
def _getBinaryPublishingBaseClauses(
self, name=None, version=None, status=None, distroarchseries=None,
- pocket=None, exact_match=False):
+ pocket=None, exact_match=False, created_since_date=None):
"""Base clauses and clauseTables for binary publishing queries.
Returns a list of 'clauses' (to be joined in the callsite) and
@@ -781,15 +781,21 @@
BinaryPackagePublishingHistory.pocket = %s
""" % sqlvalues(pocket))
+ if created_since_date is not None:
+ clauses.append(
+ "BinaryPackagePublishingHistory.datecreated >= %s"
+ % sqlvalues(created_since_date))
+
return clauses, clauseTables, orderBy
def getAllPublishedBinaries(self, name=None, version=None, status=None,
distroarchseries=None, pocket=None,
- exact_match=False):
+ exact_match=False, created_since_date=None):
"""See `IArchive`."""
clauses, clauseTables, orderBy = self._getBinaryPublishingBaseClauses(
name=name, version=version, status=status, pocket=pocket,
- distroarchseries=distroarchseries, exact_match=exact_match)
+ distroarchseries=distroarchseries, exact_match=exact_match,
+ created_since_date=created_since_date)
all_binaries = BinaryPackagePublishingHistory.select(
' AND '.join(clauses), clauseTables=clauseTables,
@@ -799,11 +805,13 @@
def getPublishedOnDiskBinaries(self, name=None, version=None, status=None,
distroarchseries=None, pocket=None,
- exact_match=False):
+ exact_match=False,
+ created_since_date=None):
"""See `IArchive`."""
clauses, clauseTables, orderBy = self._getBinaryPublishingBaseClauses(
name=name, version=version, status=status, pocket=pocket,
- distroarchseries=distroarchseries, exact_match=exact_match)
+ distroarchseries=distroarchseries, exact_match=exact_match,
+ created_since_date=created_since_date)
clauses.append("""
BinaryPackagePublishingHistory.distroarchseries =
=== modified file 'lib/lp/soyuz/model/publishing.py'
--- lib/lp/soyuz/model/publishing.py 2011-11-04 12:06:11 +0000
+++ lib/lp/soyuz/model/publishing.py 2011-12-11 01:49:34 +0000
@@ -1844,8 +1844,7 @@
return result_set
- def getChangesFilesForSources(
- self, one_or_more_source_publications):
+ def getChangesFilesForSources(self, one_or_more_source_publications):
"""See `IPublishingSet`."""
# Import PackageUpload and PackageUploadSource locally
# to avoid circular imports, since PackageUpload uses
=== modified file 'lib/lp/soyuz/tests/soyuz.py'
--- lib/lp/soyuz/tests/soyuz.py 2011-11-29 05:15:07 +0000
+++ lib/lp/soyuz/tests/soyuz.py 2011-12-11 01:49:34 +0000
@@ -16,7 +16,6 @@
from canonical.config import config
from canonical.launchpad.database.librarian import LibraryFileAlias
-from canonical.launchpad.ftests import import_public_test_keys
from canonical.launchpad.testing.fakepackager import FakePackager
from canonical.testing.layers import LaunchpadZopelessLayer
from lp.registry.interfaces.distribution import IDistributionSet
@@ -29,6 +28,7 @@
SourcePackagePublishingHistory,
)
from lp.testing.dbuser import dbuser
+from lp.testing.gpgkeys import import_public_test_keys
from lp.testing.sampledata import (
BUILDD_ADMIN_USERNAME,
CHROOT_LIBRARYFILEALIAS,
=== modified file 'lib/lp/soyuz/tests/test_archive.py'
--- lib/lp/soyuz/tests/test_archive.py 2011-12-02 15:07:54 +0000
+++ lib/lp/soyuz/tests/test_archive.py 2011-12-11 01:49:34 +0000
@@ -2303,6 +2303,54 @@
person=person)
+class TestgetAllPublishedBinaries(TestCaseWithFactory):
+
+ layer = DatabaseFunctionalLayer
+
+ def test_returns_publication(self):
+ archive = self.factory.makeArchive()
+ publication = self.factory.makeBinaryPackagePublishingHistory(
+ archive=archive)
+ publications = archive.getAllPublishedBinaries()
+ self.assertEqual(1, publications.count())
+ self.assertEqual(publication, publications[0])
+
+ def test_created_since_date_newer(self):
+ archive = self.factory.makeArchive()
+ datecreated = self.factory.getUniqueDate()
+ self.factory.makeBinaryPackagePublishingHistory(
+ archive=archive, datecreated=datecreated)
+ later_date = datecreated + timedelta(minutes=1)
+ publications = archive.getAllPublishedBinaries(
+ created_since_date=later_date)
+ self.assertEqual(0, publications.count())
+
+ def test_created_since_date_older(self):
+ archive = self.factory.makeArchive()
+ datecreated = self.factory.getUniqueDate()
+ publication = self.factory.makeBinaryPackagePublishingHistory(
+ archive=archive, datecreated=datecreated)
+ earlier_date = datecreated - timedelta(minutes=1)
+ publications = archive.getAllPublishedBinaries(
+ created_since_date=earlier_date)
+ self.assertEqual(1, publications.count())
+ self.assertEqual(publication, publications[0])
+
+ def test_created_since_date_middle(self):
+ archive = self.factory.makeArchive()
+ datecreated = self.factory.getUniqueDate()
+ self.factory.makeBinaryPackagePublishingHistory(
+ archive=archive, datecreated=datecreated)
+ middle_date = datecreated + timedelta(minutes=1)
+ later_date = middle_date + timedelta(minutes=1)
+ later_publication = self.factory.makeBinaryPackagePublishingHistory(
+ archive=archive, datecreated=later_date)
+ publications = archive.getAllPublishedBinaries(
+ created_since_date=middle_date)
+ self.assertEqual(1, publications.count())
+ self.assertEqual(later_publication, publications[0])
+
+
class TestRemovingPermissions(TestCaseWithFactory):
layer = DatabaseFunctionalLayer
=== modified file 'lib/lp/soyuz/tests/test_archive_subscriptions.py'
--- lib/lp/soyuz/tests/test_archive_subscriptions.py 2010-11-12 06:00:51 +0000
+++ lib/lp/soyuz/tests/test_archive_subscriptions.py 2011-12-11 01:49:34 +0000
@@ -26,9 +26,11 @@
def setUp(self):
"""Create a test archive."""
super(TestArchiveSubscriptions, self).setUp()
+ self.owner = self.factory.makePerson()
self.private_team = self.factory.makeTeam(
- visibility=PersonVisibility.PRIVATE, name="subscribertest")
- login_person(self.private_team.teamowner)
+ visibility=PersonVisibility.PRIVATE,
+ name="subscribertest", owner=self.owner)
+ login_person(self.owner)
self.archive = self.factory.makeArchive(
private=True, owner=self.private_team)
self.subscriber = self.factory.makePerson()
@@ -45,7 +47,7 @@
login_person(self.subscriber)
self.assertRaises(Unauthorized, get_name)
- login_person(self.private_team.teamowner)
+ login_person(self.owner)
self.archive.newSubscription(
self.subscriber, registrant=self.archive.owner)
=== modified file 'lib/lp/testing/__init__.py'
--- lib/lp/testing/__init__.py 2011-12-02 01:27:41 +0000
+++ lib/lp/testing/__init__.py 2011-12-11 01:49:34 +0000
@@ -110,12 +110,16 @@
from zope.testing.testrunner.runner import TestResult as ZopeTestResult
from canonical.config import config
+from canonical.database.sqlbase import flush_database_caches
from canonical.launchpad.webapp import canonical_url
from canonical.launchpad.webapp.adapter import (
print_queries,
start_sql_logging,
stop_sql_logging,
)
+from canonical.launchpad.webapp.authorization import (
+ clear_cache as clear_permission_cache,
+ )
from canonical.launchpad.webapp.interaction import ANONYMOUS
from canonical.launchpad.webapp.servers import (
LaunchpadTestRequest,
@@ -311,6 +315,39 @@
return (ret, recorder.statements)
+def record_two_runs(tested_method, item_creator, first_round_number,
+ second_round_number=None):
+ """A helper that returns the two storm statement recorders
+ obtained when running tested_method after having run the
+ method {item_creator} {first_round_number} times and then
+ again after having run the same method {second_round_number}
+ times.
+
+ :return: a tuple containing the two recorders obtained by the successive
+ runs.
+ """
+ for i in range(first_round_number):
+ item_creator()
+ # Record how many queries are issued when {tested_method} is
+ # called after {item_creator} has been run {first_round_number}
+ # times.
+ flush_database_caches()
+ clear_permission_cache()
+ with StormStatementRecorder() as recorder1:
+ tested_method()
+ # Run {item_creator} {second_round_number} more times.
+ if second_round_number is None:
+ second_round_number = first_round_number
+ for i in range(second_round_number):
+ item_creator()
+ # Record again the number of queries issued.
+ flush_database_caches()
+ clear_permission_cache()
+ with StormStatementRecorder() as recorder2:
+ tested_method()
+ return recorder1, recorder2
+
+
def run_with_storm_debug(function, *args, **kwargs):
"""A helper function to run a function with storm debug tracing on."""
from storm.tracer import debug
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2011-12-08 05:13:31 +0000
+++ lib/lp/testing/factory.py 2011-12-11 01:49:34 +0000
@@ -80,7 +80,6 @@
EmailAddressStatus,
IEmailAddressSet,
)
-from canonical.launchpad.interfaces.gpghandler import IGPGHandler
from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet
from canonical.launchpad.interfaces.lpstorm import (
IMasterStore,
@@ -242,6 +241,7 @@
from lp.registry.interfaces.ssh import ISSHKeySet
from lp.registry.model.milestone import Milestone
from lp.registry.model.suitesourcepackage import SuiteSourcePackage
+from lp.services.gpg.interfaces import IGPGHandler
from lp.services.job.interfaces.job import SuspendJobException
from lp.services.log.logger import BufferLogger
from lp.services.mail.signedmessage import SignedMessage
@@ -772,7 +772,7 @@
address, person, email_status, account)
def makeTeam(self, owner=None, displayname=None, email=None, name=None,
- description=None,
+ description=None, icon=None, logo=None,
subscription_policy=TeamSubscriptionPolicy.OPEN,
visibility=None, members=None):
"""Create and return a new, arbitrary Team.
@@ -783,9 +783,11 @@
:param displayname: The team's display name. If not given we'll use
the auto-generated name.
:param description: Team team's description.
- :type string:
+ :type description string:
:param email: The email address to use as the team's contact address.
:type email: string
+ :param icon: The team's icon.
+ :param logo: The team's logo.
:param subscription_policy: The subscription policy of the team.
:type subscription_policy: `TeamSubscriptionPolicy`
:param visibility: The team's visibility. If it's None, the default
@@ -810,15 +812,19 @@
team = getUtility(IPersonSet).newTeam(
owner, name, displayname, teamdescription=description,
subscriptionpolicy=subscription_policy)
+ naked_team = removeSecurityProxy(team)
if visibility is not None:
# Visibility is normally restricted to launchpad.Commercial, so
# removing the security proxy as we don't care here.
- removeSecurityProxy(team).visibility = visibility
+ naked_team.visibility = visibility
if email is not None:
team.setContactAddress(
getUtility(IEmailAddressSet).new(email, team))
+ if icon is not None:
+ naked_team.icon = icon
+ if logo is not None:
+ naked_team.logo = logo
if members is not None:
- naked_team = removeSecurityProxy(team)
for member in members:
naked_team.addMember(member, owner)
return team
@@ -1124,7 +1130,7 @@
if registrant is None:
if owner.is_team:
- registrant = owner.teamowner
+ registrant = removeSecurityProxy(owner).teamowner
else:
registrant = owner
@@ -3837,6 +3843,7 @@
priority=None, status=None,
scheduleddeletiondate=None,
dateremoved=None,
+ datecreated=None,
pocket=None, archive=None,
source_package_release=None,
sourcepackagename=None):
@@ -3878,6 +3885,9 @@
section_name=section_name,
priority=priority)
+ if datecreated is None:
+ datecreated = self.getUniqueDate()
+
bpph = getUtility(IPublishingSet).newBinaryPublication(
archive, binarypackagerelease, distroarchseries,
binarypackagerelease.component, binarypackagerelease.section,
@@ -3885,6 +3895,7 @@
naked_bpph = removeSecurityProxy(bpph)
naked_bpph.status = status
naked_bpph.dateremoved = dateremoved
+ naked_bpph.datecreated = datecreated
naked_bpph.scheduleddeletiondate = scheduleddeletiondate
naked_bpph.priority = priority
if status == PackagePublishingStatus.PUBLISHED:
@@ -4209,7 +4220,7 @@
system = self.getUniqueString('system-fingerprint')
if submission_data is None:
sample_data_path = os.path.join(
- config.root, 'lib', 'canonical', 'launchpad', 'scripts',
+ config.root, 'lib', 'lp', 'hardwaredb', 'scripts',
'tests', 'simple_valid_hwdb_submission.xml')
submission_data = open(sample_data_path).read()
filename = self.getUniqueString('submission-file')
=== modified file 'lib/lp/testing/fixture.py'
--- lib/lp/testing/fixture.py 2011-11-23 07:29:09 +0000
+++ lib/lp/testing/fixture.py 2011-12-11 01:49:34 +0000
@@ -6,6 +6,7 @@
__metaclass__ = type
__all__ = [
'CaptureOops',
+ 'DemoMode',
'PGBouncerFixture',
'Urllib2Fixture',
'ZopeAdapterFixture',
@@ -344,3 +345,21 @@
self.timeline = get_request_timeline(
get_current_browser_request())
self.addCleanup(webapp.adapter.clear_request_started)
+
+
+class DemoMode(Fixture):
+ """Run with an is_demo configuration.
+
+ This changes the page styling, feature flag permissions, and perhaps
+ other things.
+ """
+
+ def setUp(self):
+ Fixture.setUp(self)
+ config.push('demo-fixture', '''
+[launchpad]
+is_demo: true
+site_message = This is a demo site mmk. \
+<a href="http://example.com">File a bug</a>.
+ ''')
+ self.addCleanup(lambda: config.pop('demo-fixture'))
=== added directory 'lib/lp/testing/gpgkeys'
=== renamed file 'lib/canonical/launchpad/ftests/keys_for_tests.py' => 'lib/lp/testing/gpgkeys/__init__.py'
--- lib/canonical/launchpad/ftests/keys_for_tests.py 2010-10-21 04:19:36 +0000
+++ lib/lp/testing/gpgkeys/__init__.py 2011-12-11 01:49:34 +0000
@@ -25,15 +25,15 @@
import gpgme
from zope.component import getUtility
-from canonical.launchpad.interfaces.gpghandler import IGPGHandler
from lp.registry.interfaces.gpg import (
GPGKeyAlgorithm,
IGPGKeySet,
)
from lp.registry.interfaces.person import IPersonSet
-
-
-gpgkeysdir = os.path.join(os.path.dirname(__file__), 'gpgkeys')
+from lp.services.gpg.interfaces import IGPGHandler
+
+
+gpgkeysdir = os.path.join(os.path.dirname(__file__), 'data')
def import_public_key(email_addr):
=== renamed directory 'lib/canonical/launchpad/ftests/gpgkeys' => 'lib/lp/testing/gpgkeys/data'
=== modified symlink 'lib/lp/testing/keyserver/tests/keys/0x33C0A61893A5DC5EB325B29E415A12CAC2F30234.get'
=== target changed u'../../../../../canonical/launchpad/ftests/gpgkeys/ftpmaster@xxxxxxxxxxxxxxxxx' => u'../../../gpgkeys/data/ftpmaster@xxxxxxxxxxxxxxxxx'
=== modified symlink 'lib/lp/testing/keyserver/tests/keys/0x340CA3BB270E2716C9EE0B768E7EB7086C64A8C5.get'
=== target changed u'../../../../../canonical/launchpad/ftests/gpgkeys/foo.bar@xxxxxxxxxxxxxxxxx' => u'../../../gpgkeys/data/foo.bar@xxxxxxxxxxxxxxxxx'
=== modified symlink 'lib/lp/testing/keyserver/tests/keys/0x447DBF38C4F9C4ED752246B77D88913717B05A8F.get'
=== target changed u'../../../../../canonical/launchpad/ftests/gpgkeys/sign.only@xxxxxxxxxxxxxxxxx' => u'../../../gpgkeys/data/sign.only@xxxxxxxxxxxxxxxxx'
=== modified symlink 'lib/lp/testing/keyserver/tests/keys/0x84D205F03E1E67096CB54E262BE83793AACCD97C.get'
=== target changed u'../../../../../canonical/launchpad/ftests/gpgkeys/revoked.key@xxxxxxxxxxxxxxxxx' => u'../../../gpgkeys/data/revoked.key@xxxxxxxxxxxxxxxxx'
=== modified symlink 'lib/lp/testing/keyserver/tests/keys/0x961F4EB829D7D304A77477822BC8401620687895.get'
=== target changed u'../../../../../canonical/launchpad/ftests/gpgkeys/daniel.silverstone@xxxxxxxxxxxxxxxxx' => u'../../../gpgkeys/data/daniel.silverstone@xxxxxxxxxxxxxxxxx'
=== modified symlink 'lib/lp/testing/keyserver/tests/keys/0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get'
=== target changed u'../../../../../canonical/launchpad/ftests/gpgkeys/test@xxxxxxxxxxxxxxxxx' => u'../../../gpgkeys/data/test@xxxxxxxxxxxxxxxxx'
=== modified symlink 'lib/lp/testing/keyserver/tests/keys/0xC85826521A6EF6A6037BB3F79FF2583E681B6469.get'
=== target changed u'../../../../../canonical/launchpad/ftests/gpgkeys/celso.providelo@xxxxxxxxxxxxxxxxx' => u'../../../gpgkeys/data/celso.providelo@xxxxxxxxxxxxxxxxx'
=== modified symlink 'lib/lp/testing/keyserver/tests/keys/0xECA5B797586F2E27381A16CFDE6C9167046C6D63.get'
=== target changed u'../../../../../canonical/launchpad/ftests/gpgkeys/expired.key@xxxxxxxxxxxxxxxxx' => u'../../../gpgkeys/data/expired.key@xxxxxxxxxxxxxxxxx'
=== modified symlink 'lib/lp/testing/keyserver/tests/keys/README'
=== target changed u'../../../../../canonical/launchpad/ftests/gpgkeys/README' => u'../../../gpgkeys/data/README'
=== modified file 'lib/lp/testing/keyserver/web.py'
--- lib/lp/testing/keyserver/web.py 2011-05-30 15:59:19 +0000
+++ lib/lp/testing/keyserver/web.py 2011-12-11 01:49:34 +0000
@@ -38,10 +38,9 @@
from time import sleep
from twisted.web.resource import Resource
-
from zope.component import getUtility
-from canonical.launchpad.interfaces.gpghandler import (
+from lp.services.gpg.interfaces import (
GPGKeyNotFoundError,
IGPGHandler,
MoreThanOneGPGKeyFound,
=== modified file 'lib/lp/testing/tests/test_factory.py'
--- lib/lp/testing/tests/test_factory.py 2011-08-29 00:05:37 +0000
+++ lib/lp/testing/tests/test_factory.py 2011-12-11 01:49:34 +0000
@@ -176,6 +176,12 @@
bpph = self.factory.makeBinaryPackagePublishingHistory()
self.assertNotEqual(None, bpph.datecreated)
+ def test_makeBinaryPackagePublishingHistory_uses_datecreated(self):
+ datecreated = self.factory.getUniqueDate()
+ bpph = self.factory.makeBinaryPackagePublishingHistory(
+ datecreated=datecreated)
+ self.assertEqual(datecreated, bpph.datecreated)
+
def test_makeBinaryPackagePublishingHistory_sets_datepub_PENDING(self):
bpph = self.factory.makeBinaryPackagePublishingHistory(
status=PackagePublishingStatus.PENDING)
=== modified file 'lib/lp/translations/tests/test_pofilestatsjob.py'
--- lib/lp/translations/tests/test_pofilestatsjob.py 2011-11-10 15:02:49 +0000
+++ lib/lp/translations/tests/test_pofilestatsjob.py 2011-12-11 01:49:34 +0000
@@ -6,15 +6,18 @@
__metaclass__ = type
+from canonical.config import config
from canonical.launchpad.webapp.testing import verifyObject
from canonical.testing.layers import (
LaunchpadZopelessLayer,
)
+from lp.app.enums import ServiceUsage
from lp.services.job.interfaces.job import (
IJobSource,
IRunnableJob,
)
from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import dbuser
from lp.translations.interfaces.pofilestatsjob import IPOFileStatsJobSource
from lp.translations.interfaces.side import TranslationSide
from lp.translations.model import pofilestatsjob
@@ -45,7 +48,27 @@
job = pofilestatsjob.schedule(pofile.id)
# Just scheduling the job doesn't update the statistics.
self.assertEqual(pofile.potemplate.messageCount(), 0)
- job.run()
+ with dbuser(config.pofile_stats.dbuser):
+ job.run()
+ # Now that the job ran, the statistics have been updated.
+ self.assertEqual(pofile.potemplate.messageCount(), 1)
+
+ def test_with_product(self):
+ product = self.factory.makeProduct(
+ translations_usage=ServiceUsage.LAUNCHPAD)
+ productseries = self.factory.makeProductSeries(product=product)
+ potemplate = self.factory.makePOTemplate(productseries=productseries)
+ pofile = self.factory.makePOFile('en', potemplate)
+ # Create a message so we have something to have statistics about.
+ singular = self.factory.getUniqueString()
+ self.factory.makePOTMsgSet(pofile.potemplate, singular)
+ # The statistics are still at 0, even though there is a message.
+ self.assertEqual(potemplate.messageCount(), 0)
+ job = pofilestatsjob.schedule(pofile.id)
+ # Just scheduling the job doesn't update the statistics.
+ self.assertEqual(pofile.potemplate.messageCount(), 0)
+ with dbuser(config.pofile_stats.dbuser):
+ job.run()
# Now that the job ran, the statistics have been updated.
self.assertEqual(pofile.potemplate.messageCount(), 1)
=== modified file 'utilities/apidoc-index.pt'
--- utilities/apidoc-index.pt 2010-08-21 13:28:33 +0000
+++ utilities/apidoc-index.pt 2011-12-11 01:49:34 +0000
@@ -10,7 +10,7 @@
<style type="text/css">
body {
- font-family: UbuntuBeta, Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
+ font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
font-size: 0.85em;
margin: 2em 8em;
}
=== modified file 'utilities/make-lp-user'
--- utilities/make-lp-user 2011-10-18 09:36:12 +0000
+++ utilities/make-lp-user 2011-12-11 01:49:34 +0000
@@ -42,7 +42,7 @@
from zope.component import getUtility
-from canonical.launchpad.interfaces.gpghandler import IGPGHandler
+from lp.services.gpg.interfaces import IGPGHandler
from canonical.launchpad.scripts import execute_zcml_for_scripts
from canonical.lazr.timeout import set_default_timeout_function
from lp.registry.interfaces.gpg import GPGKeyAlgorithm, IGPGKeySet