1
# Copyright 2009-2011 Canonical Ltd. This software is licensed under the GNU
1
# Copyright 2009, 2010 Canonical Ltd. This software is licensed under the GNU
2
2
# Affero General Public License version 3 (see the file LICENSE).
4
"""Translations vocabularies."""
4
"""Vocabularies pulling stuff from the database.
6
You probably don't want to use these classes directly - see the
7
docstring in __init__.py for details.
6
10
__metaclass__ = type
13
'BugNominatableDistroSeriesVocabulary',
14
'BugNominatableProductSeriesVocabulary',
15
'BugNominatableSeriesVocabulary',
16
'BugTrackerVocabulary',
19
'ComponentVocabulary',
20
'CountryNameVocabulary',
21
'DistributionUsingMaloneVocabulary',
9
22
'FilteredDeltaLanguagePackVocabulary',
23
'FilteredDistroArchSeriesVocabulary',
10
24
'FilteredFullLanguagePackVocabulary',
11
25
'FilteredLanguagePackVocabulary',
27
'PackageReleaseVocabulary',
29
'ProcessorFamilyVocabulary',
30
'ProcessorVocabulary',
31
'project_products_using_malone_vocabulary_factory',
12
32
'TranslatableLanguageVocabulary',
13
33
'TranslationGroupVocabulary',
14
34
'TranslationMessageVocabulary',
15
35
'TranslationTemplateVocabulary',
36
'WebBugTrackerVocabulary',
18
from sqlobject import AND
19
from zope.schema.vocabulary import SimpleTerm
21
from canonical.database.sqlbase import sqlvalues
40
from operator import attrgetter
42
from sqlobject import (
47
from storm.expr import (
52
from zope.component import getUtility
53
from zope.interface import implements
54
from zope.schema.interfaces import (
58
from zope.schema.vocabulary import (
63
from canonical.database.sqlbase import (
67
from canonical.launchpad.helpers import (
71
from canonical.launchpad.interfaces.lpstorm import IStore
72
from canonical.launchpad.webapp.interfaces import ILaunchBag
22
73
from canonical.launchpad.webapp.vocabulary import (
23
76
NamedSQLObjectVocabulary,
24
77
SQLObjectVocabularyBase,
79
from lp.app.browser.stringformatter import FormattersAPI
80
from lp.app.enums import ServiceUsage
81
from lp.bugs.interfaces.bugtask import IBugTask
82
from lp.bugs.interfaces.bugtracker import BugTrackerType
83
from lp.bugs.model.bug import Bug
84
from lp.bugs.model.bugtracker import BugTracker
85
from lp.bugs.model.bugwatch import BugWatch
86
from lp.registry.interfaces.distribution import IDistribution
26
87
from lp.registry.interfaces.distroseries import IDistroSeries
88
from lp.registry.interfaces.projectgroup import IProjectGroup
89
from lp.registry.interfaces.series import SeriesStatus
90
from lp.registry.model.distribution import Distribution
91
from lp.registry.model.distroseries import DistroSeries
92
from lp.registry.model.person import Person
93
from lp.registry.model.productseries import ProductSeries
27
94
from lp.services.worlddata.interfaces.language import ILanguage
28
from lp.services.worlddata.vocabularies import LanguageVocabulary
95
from lp.services.worlddata.model.country import Country
96
from lp.services.worlddata.model.language import Language
97
from lp.soyuz.enums import ArchivePurpose
98
from lp.soyuz.model.archive import Archive
99
from lp.soyuz.model.component import Component
100
from lp.soyuz.model.distroarchseries import DistroArchSeries
101
from lp.soyuz.model.processor import (
105
from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
29
106
from lp.translations.enums import LanguagePackType
30
107
from lp.translations.model.languagepack import LanguagePack
31
108
from lp.translations.model.potemplate import POTemplate
33
110
from lp.translations.model.translationmessage import TranslationMessage
113
class ComponentVocabulary(SQLObjectVocabularyBase):
118
def toTerm(self, obj):
119
return SimpleTerm(obj, obj.id, obj.name)
122
# Country.name may have non-ASCII characters, so we can't use
123
# NamedSQLObjectVocabulary here.
125
class CountryNameVocabulary(SQLObjectVocabularyBase):
126
"""A vocabulary for country names."""
131
def toTerm(self, obj):
132
return SimpleTerm(obj, obj.id, obj.name)
135
class BugVocabulary(SQLObjectVocabularyBase):
141
class BugTrackerVocabulary(SQLObjectVocabularyBase):
142
"""All web and email based external bug trackers."""
143
displayname = 'Select a bug tracker'
144
step_title = 'Search'
145
implements(IHugeVocabulary)
149
_order_by = [BugTracker.title]
151
def toTerm(self, obj):
152
"""See `IVocabulary`."""
153
return SimpleTerm(obj, obj.name, obj.title)
155
def getTermByToken(self, token):
156
"""See `IVocabularyTokenized`."""
157
result = IStore(self._table).find(
160
BugTracker.name == token).one()
162
raise LookupError(token)
163
return self.toTerm(result)
165
def search(self, query):
166
"""Search for web bug trackers."""
167
query = ensure_unicode(query).lower()
168
results = IStore(self._table).find(
171
BugTracker.active == True,
173
CONTAINSSTRING(BugTracker.name, query),
174
CONTAINSSTRING(BugTracker.title, query),
175
CONTAINSSTRING(BugTracker.summary, query),
176
CONTAINSSTRING(BugTracker.baseurl, query))))
177
results = results.order_by(self._order_by)
180
def searchForTerms(self, query=None):
181
"""See `IHugeVocabulary`."""
182
results = self.search(query)
183
return CountableIterator(results.count(), results, self.toTerm)
186
class WebBugTrackerVocabulary(BugTrackerVocabulary):
187
"""All web-based bug tracker types."""
188
_filter = BugTracker.bugtrackertype != BugTrackerType.EMAILADDRESS
191
class LanguageVocabulary(SQLObjectVocabularyBase):
192
"""All the languages known by Launchpad."""
195
_orderBy = 'englishname'
197
def __contains__(self, language):
198
"""See `IVocabulary`."""
199
assert ILanguage.providedBy(language), (
200
"'in LanguageVocabulary' requires ILanguage as left operand, "
201
"got %s instead." % type(language))
202
return super(LanguageVocabulary, self).__contains__(language)
204
def toTerm(self, obj):
205
"""See `IVocabulary`."""
206
return SimpleTerm(obj, obj.code, obj.displayname)
208
def getTerm(self, obj):
209
"""See `IVocabulary`."""
211
raise LookupError(obj)
212
return SimpleTerm(obj, obj.code, obj.displayname)
214
def getTermByToken(self, token):
215
"""See `IVocabulary`."""
217
found_language = Language.byCode(token)
218
except SQLObjectNotFound:
219
raise LookupError(token)
220
return self.getTerm(found_language)
36
223
class TranslatableLanguageVocabulary(LanguageVocabulary):
37
224
"""All the translatable languages known by Launchpad.
119
315
return SimpleTerm(obj, obj.id, obj.name)
318
class FilteredDistroArchSeriesVocabulary(SQLObjectVocabularyBase):
319
"""All arch series of a particular distribution."""
321
_table = DistroArchSeries
322
_orderBy = ['DistroSeries.version', 'architecturetag', 'id']
323
_clauseTables = ['DistroSeries']
325
def toTerm(self, obj):
326
name = "%s %s (%s)" % (obj.distroseries.distribution.name,
327
obj.distroseries.name, obj.architecturetag)
328
return SimpleTerm(obj, obj.id, name)
331
distribution = getUtility(ILaunchBag).distribution
334
DistroSeries.id = DistroArchSeries.distroseries AND
335
DistroSeries.distribution = %s
336
""" % sqlvalues(distribution.id)
337
results = self._table.select(
338
query, orderBy=self._orderBy, clauseTables=self._clauseTables)
339
for distroarchseries in results:
340
yield self.toTerm(distroarchseries)
343
class BugWatchVocabulary(SQLObjectVocabularyBase):
347
assert IBugTask.providedBy(self.context), (
348
"BugWatchVocabulary expects its context to be an IBugTask.")
349
bug = self.context.bug
351
for watch in bug.watches:
352
yield self.toTerm(watch)
354
def toTerm(self, watch):
357
return cgi.escape(string, quote=True)
359
if watch.url.startswith('mailto:'):
360
user = getUtility(ILaunchBag).user
362
title = FormattersAPI(
363
watch.bugtracker.title).obfuscate_email()
365
watch, watch.id, escape(title))
368
title = escape(watch.bugtracker.title)
370
title = title.replace(
371
url, '<a href="%s">%s</a>' % (
372
escape(url), escape(url)))
374
title = '%s <<a href="%s">%s</a>>' % (
375
title, escape(url), escape(url[7:]))
376
return SimpleTerm(watch, watch.id, title)
379
watch, watch.id, '%s <a href="%s">#%s</a>' % (
380
escape(watch.bugtracker.title),
382
escape(watch.remotebug)))
385
class PackageReleaseVocabulary(SQLObjectVocabularyBase):
386
_table = SourcePackageRelease
389
def toTerm(self, obj):
391
obj, obj.id, obj.name + " " + obj.version)
394
class PPAVocabulary(SQLObjectVocabularyBase):
396
implements(IHugeVocabulary)
399
_orderBy = ['Person.name, Archive.name']
400
_clauseTables = ['Person']
402
Person.q.id == Archive.q.ownerID,
403
Archive.q.purpose == ArchivePurpose.PPA)
404
displayname = 'Select a PPA'
405
step_title = 'Search'
407
def toTerm(self, archive):
408
"""See `IVocabulary`."""
409
description = archive.description
410
if description is not None:
411
summary = description.splitlines()[0]
413
summary = "No description available"
415
token = '%s/%s' % (archive.owner.name, archive.name)
417
return SimpleTerm(archive, token, summary)
419
def getTermByToken(self, token):
420
"""See `IVocabularyTokenized`."""
422
owner_name, archive_name = token.split('/')
424
raise LookupError(token)
428
Person.name == owner_name,
429
Archive.name == archive_name)
431
obj = self._table.selectOne(
432
clause, clauseTables=self._clauseTables)
435
raise LookupError(token)
437
return self.toTerm(obj)
439
def search(self, query):
440
"""Return a resultset of archives.
442
This is a helper required by `SQLObjectVocabularyBase.searchForTerms`.
445
return self.emptySelectResults()
447
query = query.lower()
450
owner_name, archive_name = query.split('/')
454
SQL("(Archive.fti @@ ftq(%s) OR Person.fti @@ ftq(%s))"
455
% (quote(query), quote(query))))
459
Person.name == owner_name,
460
Archive.name == archive_name)
462
return self._table.select(
463
clause, orderBy=self._orderBy, clauseTables=self._clauseTables)
466
class DistributionUsingMaloneVocabulary:
467
"""All the distributions that uses Malone officially."""
469
implements(IVocabulary, IVocabularyTokenized)
471
_orderBy = 'displayname'
473
def __init__(self, context=None):
474
self.context = context
477
"""Return an iterator which provides the terms from the vocabulary."""
478
distributions_using_malone = Distribution.selectBy(
479
official_malone=True, orderBy=self._orderBy)
480
for distribution in distributions_using_malone:
481
yield self.getTerm(distribution)
484
return Distribution.selectBy(official_malone=True).count()
486
def __contains__(self, obj):
487
return (IDistribution.providedBy(obj)
488
and obj.bug_tracking_usage == ServiceUsage.LAUNCHPAD)
490
def getTerm(self, obj):
492
raise LookupError(obj)
493
return SimpleTerm(obj, obj.name, obj.displayname)
495
def getTermByToken(self, token):
496
found_dist = Distribution.selectOneBy(
497
name=token, official_malone=True)
498
if found_dist is None:
499
raise LookupError(token)
500
return self.getTerm(found_dist)
503
class ProcessorVocabulary(NamedSQLObjectVocabulary):
505
displayname = 'Select a processor'
510
class ProcessorFamilyVocabulary(NamedSQLObjectVocabulary):
511
displayname = 'Select a processor family'
512
_table = ProcessorFamily
516
def BugNominatableSeriesVocabulary(context=None):
517
"""Return a nominatable series vocabulary."""
518
if getUtility(ILaunchBag).distribution:
519
return BugNominatableDistroSeriesVocabulary(
520
context, getUtility(ILaunchBag).distribution)
522
assert getUtility(ILaunchBag).product
523
return BugNominatableProductSeriesVocabulary(
524
context, getUtility(ILaunchBag).product)
527
class BugNominatableSeriesVocabularyBase(NamedSQLObjectVocabulary):
528
"""Base vocabulary class for series for which a bug can be nominated."""
531
bug = self.context.bug
533
all_series = self._getNominatableObjects()
535
for series in sorted(all_series, key=attrgetter("displayname")):
536
if bug.canBeNominatedFor(series):
537
yield self.toTerm(series)
539
def toTerm(self, obj):
540
return SimpleTerm(obj, obj.name, obj.name.capitalize())
542
def getTermByToken(self, token):
543
obj = self._queryNominatableObjectByName(token)
545
raise LookupError(token)
547
return self.toTerm(obj)
549
def _getNominatableObjects(self):
550
"""Return the series objects that the bug can be nominated for."""
551
raise NotImplementedError
553
def _queryNominatableObjectByName(self, name):
554
"""Return the series object with the given name."""
555
raise NotImplementedError
558
class BugNominatableProductSeriesVocabulary(
559
BugNominatableSeriesVocabularyBase):
560
"""The product series for which a bug can be nominated."""
562
_table = ProductSeries
564
def __init__(self, context, product):
565
BugNominatableSeriesVocabularyBase.__init__(self, context)
566
self.product = product
568
def _getNominatableObjects(self):
569
"""See BugNominatableSeriesVocabularyBase."""
570
return shortlist(self.product.series)
572
def _queryNominatableObjectByName(self, name):
573
"""See BugNominatableSeriesVocabularyBase."""
574
return self.product.getSeries(name)
577
class BugNominatableDistroSeriesVocabulary(
578
BugNominatableSeriesVocabularyBase):
579
"""The distribution series for which a bug can be nominated."""
581
_table = DistroSeries
583
def __init__(self, context, distribution):
584
BugNominatableSeriesVocabularyBase.__init__(self, context)
585
self.distribution = distribution
587
def _getNominatableObjects(self):
588
"""Return all non-obsolete distribution series"""
590
series for series in shortlist(self.distribution.series)
591
if series.status != SeriesStatus.OBSOLETE]
593
def _queryNominatableObjectByName(self, name):
594
"""See BugNominatableSeriesVocabularyBase."""
595
return self.distribution.getSeries(name)
122
598
class FilteredLanguagePackVocabularyBase(SQLObjectVocabularyBase):
123
599
"""Base vocabulary class to retrieve language packs for a distroseries."""
124
600
_table = LanguagePack