10295.1.2
by Jeroen Vermeulen
Review changes. |
1 |
# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
|
8687.15.18
by Karl Fogel
Add the copyright header block to files under lib/canonical/. |
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
2570.1.2
by Carlos Perello Marin
Implemented initial support for the translation import queue and removed the old attach script |
3 |
|
10295.1.2
by Jeroen Vermeulen
Review changes. |
4 |
"""Browser views for `ITranslationImportQueue`."""
|
2570.1.2
by Carlos Perello Marin
Implemented initial support for the translation import queue and removed the old attach script |
5 |
|
6 |
__metaclass__ = type |
|
7 |
||
8 |
__all__ = [ |
|
11091.7.6
by Jeroen Vermeulen
Forgotten change: escape strings. |
9 |
'escape_js_string', |
2570.1.37
by Carlos Perelló Marín
More review comments applied |
10 |
'TranslationImportQueueEntryNavigation', |
11 |
'TranslationImportQueueEntryView', |
|
2570.1.7
by Carlos Perello Marin
Lots of fixes + new code + tests updates |
12 |
'TranslationImportQueueNavigation', |
2570.1.14
by Carlos Perelló MarÃn
Implemented the queue views and integragted it into the navigation. |
13 |
'TranslationImportQueueView', |
2570.1.2
by Carlos Perello Marin
Implemented initial support for the translation import queue and removed the old attach script |
14 |
]
|
15 |
||
3691.118.12
by Carlos Perello Marin
Added missing imports |
16 |
import os |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
17 |
|
6520.2.1
by Jeroen Vermeulen
Handle bad values for two URL parameters better. |
18 |
from zope.app.form.interfaces import ConversionError |
2570.1.13
by Carlos Perelló MarÃn
Implemented the translation import queue web |
19 |
from zope.component import getUtility |
20 |
from zope.interface import implements |
|
4268.3.23
by Carlos Perello Marin
Ported old import queue page to use the new infrastructure |
21 |
from zope.schema.interfaces import IContextSourceBinder |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
22 |
from zope.schema.vocabulary import ( |
23 |
SimpleTerm, |
|
24 |
SimpleVocabulary, |
|
25 |
)
|
|
2570.1.13
by Carlos Perelló MarÃn
Implemented the translation import queue web |
26 |
|
11929.9.1
by Tim Penhey
Move launchpadform into lp.app.browser. |
27 |
from lp.app.browser.launchpadform import ( |
28 |
action, |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
29 |
LaunchpadFormView, |
30 |
)
|
|
11626.3.2
by Curtis Hovey
Move tales gto lp.app. |
31 |
from lp.app.browser.tales import DateTimeFormatterAPI |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
32 |
from lp.app.errors import ( |
33 |
NotFoundError, |
|
34 |
UnexpectedFormData, |
|
35 |
)
|
|
12442.2.9
by j.c.sackett
Ran import reformatter per review. |
36 |
from lp.app.validators.name import valid_name |
7675.110.3
by Curtis Hovey
Ran the migration script to move registry code to lp.registry. |
37 |
from lp.registry.interfaces.distroseries import IDistroSeries |
10295.1.2
by Jeroen Vermeulen
Review changes. |
38 |
from lp.registry.interfaces.sourcepackage import ISourcePackageFactory |
14606.3.1
by William Grant
Merge canonical.database into lp.services.database. |
39 |
from lp.services.database.constants import UTC_NOW |
40 |
from lp.services.webapp import ( |
|
41 |
canonical_url, |
|
42 |
GetitemNavigation, |
|
43 |
)
|
|
10737.1.2
by Henning Eggers
Fixed the bug. |
44 |
from lp.services.worlddata.interfaces.language import ILanguageSet |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
45 |
from lp.translations.browser.hastranslationimports import ( |
46 |
HasTranslationImportsView, |
|
47 |
)
|
|
11818.3.1
by Jeroen Vermeulen
Split out lp.translations.enums and lp.translations.interfaces.hastranslationimports. |
48 |
from lp.translations.enums import RosettaImportStatus |
8751.1.1
by Danilo Šegan
Store migration changes so far. |
49 |
from lp.translations.interfaces.pofile import IPOFileSet |
50 |
from lp.translations.interfaces.potemplate import IPOTemplateSet |
|
12177.10.1
by Jeroen Vermeulen
Cleaned up import-queue browser code. |
51 |
from lp.translations.interfaces.translationimporter import ( |
52 |
ITranslationImporter, |
|
53 |
)
|
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
54 |
from lp.translations.interfaces.translationimportqueue import ( |
55 |
IEditTranslationImportQueueEntry, |
|
56 |
ITranslationImportQueue, |
|
57 |
ITranslationImportQueueEntry, |
|
58 |
SpecialTranslationImportTargetFilter, |
|
59 |
TranslationFileType, |
|
60 |
)
|
|
61 |
from lp.translations.utilities.template import ( |
|
62 |
make_domain, |
|
63 |
make_name, |
|
64 |
)
|
|
2570.1.7
by Carlos Perello Marin
Lots of fixes + new code + tests updates |
65 |
|
11091.7.6
by Jeroen Vermeulen
Forgotten change: escape strings. |
66 |
|
11091.7.7
by Jeroen Vermeulen
Test string escaping. |
67 |
def replace(string, replacement): |
68 |
"""In `string,` replace `replacement`[0] with `replacement`[1]."""
|
|
69 |
return string.replace(*replacement) |
|
70 |
||
71 |
||
11091.7.6
by Jeroen Vermeulen
Forgotten change: escape strings. |
72 |
def escape_js_string(string): |
73 |
"""Escape `string` for use as a string in a JS <script> tag."""
|
|
11091.7.7
by Jeroen Vermeulen
Test string escaping. |
74 |
replacements = [ |
75 |
('\\', '\\\\'), |
|
76 |
('"', '\\"'), |
|
77 |
("'", "\\'"), |
|
78 |
('\n', '\\n'), |
|
79 |
]
|
|
80 |
return reduce(replace, replacements, string) |
|
11091.7.6
by Jeroen Vermeulen
Forgotten change: escape strings. |
81 |
|
82 |
||
2570.1.37
by Carlos Perelló Marín
More review comments applied |
83 |
class TranslationImportQueueEntryNavigation(GetitemNavigation): |
84 |
||
85 |
usedfor = ITranslationImportQueueEntry |
|
86 |
||
87 |
||
3691.289.2
by Carlos Perello Marin
Fixed code to prevent IPOFile.path duplicates inside the same sourcepackagename/distrorelease or productseries. Migrated TranslationImportQueueEntryView from GeneralForm to LaunchpadFormView and improved its portlet to have links instead of just text |
88 |
class TranslationImportQueueEntryView(LaunchpadFormView): |
89 |
"""The view part of admin interface for the translation import queue."""
|
|
7272.4.2
by Henning Eggers
Improved validation for import form. |
90 |
label = "Review import queue entry" |
3691.289.2
by Carlos Perello Marin
Fixed code to prevent IPOFile.path duplicates inside the same sourcepackagename/distrorelease or productseries. Migrated TranslationImportQueueEntryView from GeneralForm to LaunchpadFormView and improved its portlet to have links instead of just text |
91 |
schema = IEditTranslationImportQueueEntry |
2570.1.17
by Carlos Perelló Marín
Admin interface nearly done |
92 |
|
10295.1.2
by Jeroen Vermeulen
Review changes. |
93 |
max_series_to_display = 3 |
94 |
||
13980.3.1
by Curtis Hovey
Moved translationimportqueueentry_index page_title into view. |
95 |
page_title = 'Translation import queue entry' |
96 |
||
3200.1.30
by Carlos Perello Marin
Added autofill forms for translationimportqueue |
97 |
@property
|
98 |
def initial_values(self): |
|
99 |
"""Initialize some values on the form, when it's possible."""
|
|
100 |
field_values = {} |
|
3691.289.2
by Carlos Perello Marin
Fixed code to prevent IPOFile.path duplicates inside the same sourcepackagename/distrorelease or productseries. Migrated TranslationImportQueueEntryView from GeneralForm to LaunchpadFormView and improved its portlet to have links instead of just text |
101 |
if self.request.method == 'POST': |
13194.2.1
by Gavin Panella
Change all uses of 'initialise' to 'initialize'. |
102 |
# We got a form post, we don't need to do any initialization.
|
3691.289.2
by Carlos Perello Marin
Fixed code to prevent IPOFile.path duplicates inside the same sourcepackagename/distrorelease or productseries. Migrated TranslationImportQueueEntryView from GeneralForm to LaunchpadFormView and improved its portlet to have links instead of just text |
103 |
return field_values |
3200.1.33
by Carlos Perello Marin
Added some other autofill options and pagetests |
104 |
# Fill the know values.
|
7272.4.1
by Henning Eggers
Added more validation and explicitness to TranslationnImportEntryView. |
105 |
field_values['path'] = self.context.path |
12177.10.1
by Jeroen Vermeulen
Cleaned up import-queue browser code. |
106 |
|
107 |
importer = getUtility(ITranslationImporter) |
|
108 |
if importer.isTemplateName(self.context.path): |
|
109 |
file_type = TranslationFileType.POT |
|
110 |
elif importer.isTranslationName(self.context.path): |
|
7272.4.1
by Henning Eggers
Added more validation and explicitness to TranslationnImportEntryView. |
111 |
file_type = TranslationFileType.PO |
112 |
else: |
|
113 |
file_type = TranslationFileType.UNSPEC |
|
114 |
field_values['file_type'] = file_type |
|
115 |
||
3200.1.30
by Carlos Perello Marin
Added autofill forms for translationimportqueue |
116 |
if self.context.sourcepackagename is not None: |
117 |
field_values['sourcepackagename'] = self.context.sourcepackagename |
|
7849.12.2
by Henning Eggers
Factored a check out into aproperty is_targeted_to_ubuntu. |
118 |
if self.context.is_targeted_to_ubuntu: |
7849.12.1
by Henning Eggers
Added launguagepack checkbox to TranslationImportEntryView. |
119 |
if self.context.potemplate is None: |
120 |
# Default for Ubuntu templates is to
|
|
121 |
# include them in languagepacks.
|
|
122 |
field_values['languagepack'] = True |
|
123 |
else: |
|
124 |
field_values['languagepack'] = ( |
|
125 |
self.context.potemplate.languagepack) |
|
7849.12.2
by Henning Eggers
Factored a check out into aproperty is_targeted_to_ubuntu. |
126 |
if (file_type in (TranslationFileType.POT, |
10737.1.2
by Henning Eggers
Fixed the bug. |
127 |
TranslationFileType.UNSPEC)): |
10737.1.4
by Henning Eggers
Fixed test. |
128 |
potemplate = self.context.potemplate |
129 |
if potemplate is None: |
|
10737.1.2
by Henning Eggers
Fixed the bug. |
130 |
domain = make_domain(self.context.path) |
131 |
field_values['name'] = make_name(domain) |
|
132 |
field_values['translation_domain'] = domain |
|
133 |
else: |
|
10737.1.4
by Henning Eggers
Fixed test. |
134 |
field_values['name'] = potemplate.name |
10737.1.2
by Henning Eggers
Fixed the bug. |
135 |
field_values['translation_domain'] = ( |
10737.1.4
by Henning Eggers
Fixed test. |
136 |
potemplate.translation_domain) |
7272.4.1
by Henning Eggers
Added more validation and explicitness to TranslationnImportEntryView. |
137 |
if file_type in (TranslationFileType.PO, TranslationFileType.UNSPEC): |
7272.4.2
by Henning Eggers
Improved validation for import form. |
138 |
field_values['potemplate'] = self.context.potemplate |
7272.4.1
by Henning Eggers
Added more validation and explicitness to TranslationnImportEntryView. |
139 |
if self.context.pofile is not None: |
140 |
field_values['language'] = self.context.pofile.language |
|
141 |
else: |
|
142 |
# The entries that are translations usually have the language
|
|
143 |
# code
|
|
144 |
# as its filename. We try to get it to use as a suggestion.
|
|
145 |
language_set = getUtility(ILanguageSet) |
|
146 |
filename = os.path.basename(self.context.path) |
|
147 |
guessed_language, file_ext = filename.split(u'.', 1) |
|
11122.3.2
by Danilo Šegan
Remove variant from ILanguageSet and callsites. |
148 |
language = language_set.getLanguageByCode(guessed_language) |
7272.4.1
by Henning Eggers
Added more validation and explicitness to TranslationnImportEntryView. |
149 |
if language is not None: |
150 |
field_values['language'] = language |
|
151 |
# Need to warn the user that we guessed the language
|
|
152 |
# information.
|
|
153 |
self.request.response.addWarningNotification( |
|
154 |
"Review the language selection as we guessed it and"
|
|
155 |
" could not be accurate.") |
|
3200.1.33
by Carlos Perello Marin
Added some other autofill options and pagetests |
156 |
|
3200.1.30
by Carlos Perello Marin
Added autofill forms for translationimportqueue |
157 |
return field_values |
158 |
||
3691.289.2
by Carlos Perello Marin
Fixed code to prevent IPOFile.path duplicates inside the same sourcepackagename/distrorelease or productseries. Migrated TranslationImportQueueEntryView from GeneralForm to LaunchpadFormView and improved its portlet to have links instead of just text |
159 |
@property
|
9295.1.1
by Danilo Šegan
Migrate translationimportqueue-entry.pt to 3.0 and fix up next_url handling. |
160 |
def cancel_url(self): |
161 |
"""See `LaunchpadFormView`."""
|
|
9295.1.6
by Danilo Šegan
Test fixes. |
162 |
referrer = self.referrer_url |
163 |
if referrer is None: |
|
164 |
translationimportqueue_set = getUtility(ITranslationImportQueue) |
|
165 |
return canonical_url(translationimportqueue_set) |
|
166 |
else: |
|
167 |
return referrer |
|
9295.1.1
by Danilo Šegan
Migrate translationimportqueue-entry.pt to 3.0 and fix up next_url handling. |
168 |
|
169 |
@property
|
|
170 |
def referrer_url(self): |
|
9295.1.6
by Danilo Šegan
Test fixes. |
171 |
referrer = self.request.getHeader('referer') |
172 |
if referrer != canonical_url(self.context): |
|
173 |
return referrer |
|
174 |
else: |
|
175 |
return None |
|
9295.1.1
by Danilo Šegan
Migrate translationimportqueue-entry.pt to 3.0 and fix up next_url handling. |
176 |
|
177 |
@property
|
|
10295.1.2
by Jeroen Vermeulen
Review changes. |
178 |
def import_target(self): |
179 |
"""The entry's `ProductSeries` or `SourcePackage`."""
|
|
180 |
productseries = self.context.productseries |
|
181 |
distroseries = self.context.distroseries |
|
182 |
sourcepackagename = self.context.sourcepackagename |
|
183 |
if distroseries is None: |
|
184 |
return productseries |
|
185 |
else: |
|
186 |
factory = getUtility(ISourcePackageFactory) |
|
187 |
return factory.new(sourcepackagename, distroseries) |
|
188 |
||
189 |
@property
|
|
190 |
def productseries_templates_link(self): |
|
191 |
"""Return link to `ProductSeries`' templates.
|
|
192 |
||
193 |
Use this only if the entry is attached to a `ProductSeries`.
|
|
194 |
"""
|
|
195 |
assert self.context.productseries is not None, ( |
|
196 |
"Entry is not attached to a ProductSeries.") |
|
197 |
||
198 |
template_count = self.context.productseries.potemplate_count |
|
199 |
if template_count == 0: |
|
200 |
return "no templates" |
|
201 |
else: |
|
202 |
link = "%s/+templates" % canonical_url( |
|
203 |
self.context.productseries, rootsite='translations') |
|
204 |
if template_count == 1: |
|
205 |
word = "template" |
|
206 |
else: |
|
207 |
word = "templates" |
|
208 |
return '<a href="%s">%d %s</a>' % (link, template_count, word) |
|
209 |
||
210 |
def _composeProductSeriesLink(self, productseries): |
|
211 |
"""Produce HTML to link to `productseries`."""
|
|
212 |
return '<a href="%s">%s</a>' % ( |
|
213 |
canonical_url(productseries, rootsite='translations'), |
|
214 |
productseries.name) |
|
215 |
||
216 |
@property
|
|
217 |
def product_translatable_series(self): |
|
218 |
"""Summarize whether `Product` has translatable series.
|
|
219 |
||
220 |
Use this only if the entry is attached to a `ProductSeries`.
|
|
221 |
"""
|
|
222 |
assert self.context.productseries is not None, ( |
|
223 |
"Entry is not attached to a ProductSeries.") |
|
224 |
||
225 |
product = self.context.productseries.product |
|
226 |
translatable_series = list(product.translatable_series) |
|
227 |
if len(translatable_series) == 0: |
|
228 |
return "Project has no translatable series." |
|
229 |
else: |
|
11626.3.10
by Curtis Hovey
Hush lints epic complaints about the changes files. |
230 |
max_series_to_display = self.max_series_to_display |
10295.1.2
by Jeroen Vermeulen
Review changes. |
231 |
links = [ |
232 |
self._composeProductSeriesLink(series) |
|
11626.3.10
by Curtis Hovey
Hush lints epic complaints about the changes files. |
233 |
for series in translatable_series[:max_series_to_display]] |
10295.1.2
by Jeroen Vermeulen
Review changes. |
234 |
links_text = ', '.join(links) |
11626.3.10
by Curtis Hovey
Hush lints epic complaints about the changes files. |
235 |
if len(translatable_series) > max_series_to_display: |
10295.1.2
by Jeroen Vermeulen
Review changes. |
236 |
tail = ", ..." |
237 |
else: |
|
238 |
tail = "." |
|
239 |
return "Project has translatable series: " + links_text + tail |
|
240 |
||
241 |
@property
|
|
242 |
def status_change_date(self): |
|
243 |
"""Show date of last status change.
|
|
244 |
||
245 |
Says nothing at all if the entry's status has not changed since
|
|
246 |
upload.
|
|
247 |
"""
|
|
248 |
change_date = self.context.date_status_changed |
|
249 |
if change_date == self.context.dateimported: |
|
250 |
return "" |
|
251 |
else: |
|
252 |
formatter = DateTimeFormatterAPI(change_date) |
|
253 |
return "Last changed %s." % formatter.displaydate() |
|
254 |
||
255 |
@property
|
|
3691.289.2
by Carlos Perello Marin
Fixed code to prevent IPOFile.path duplicates inside the same sourcepackagename/distrorelease or productseries. Migrated TranslationImportQueueEntryView from GeneralForm to LaunchpadFormView and improved its portlet to have links instead of just text |
256 |
def next_url(self): |
9295.1.1
by Danilo Šegan
Migrate translationimportqueue-entry.pt to 3.0 and fix up next_url handling. |
257 |
"""See `LaunchpadFormView`."""
|
258 |
# The referer header we want is only available before the view's
|
|
259 |
# form submits to itself. This field is a hidden input in the form.
|
|
260 |
referrer = self.request.form.get('next_url') |
|
261 |
||
262 |
if (referrer is not None |
|
263 |
and referrer.startswith(self.request.getApplicationURL())): |
|
264 |
return referrer |
|
265 |
else: |
|
266 |
translationimportqueue_set = getUtility(ITranslationImportQueue) |
|
267 |
return canonical_url(translationimportqueue_set) |
|
3691.289.2
by Carlos Perello Marin
Fixed code to prevent IPOFile.path duplicates inside the same sourcepackagename/distrorelease or productseries. Migrated TranslationImportQueueEntryView from GeneralForm to LaunchpadFormView and improved its portlet to have links instead of just text |
268 |
|
2570.1.17
by Carlos Perelló Marín
Admin interface nearly done |
269 |
def initialize(self): |
3691.289.2
by Carlos Perello Marin
Fixed code to prevent IPOFile.path duplicates inside the same sourcepackagename/distrorelease or productseries. Migrated TranslationImportQueueEntryView from GeneralForm to LaunchpadFormView and improved its portlet to have links instead of just text |
270 |
"""Remove some fields based on the entry handled."""
|
7272.4.1
by Henning Eggers
Added more validation and explicitness to TranslationnImportEntryView. |
271 |
self.field_names = ['file_type', 'path', 'sourcepackagename', |
11091.7.2
by Jeroen Vermeulen
Make template name and domain settable through the templates dropdown. |
272 |
'potemplate', 'potemplate_name', |
7849.12.1
by Henning Eggers
Added launguagepack checkbox to TranslationImportEntryView. |
273 |
'name', 'translation_domain', 'languagepack', |
11122.3.4
by Danilo Šegan
Get rid of the remaining variant usage. |
274 |
'language'] |
2570.1.19
by Carlos Perelló Marín
Added a page to request new potemplate additions for a product series so we have that integrated into launchpad |
275 |
|
3691.289.9
by Carlos Perello Marin
Applied review comments + test fixes |
276 |
if self.context.productseries is not None: |
3691.289.2
by Carlos Perello Marin
Fixed code to prevent IPOFile.path duplicates inside the same sourcepackagename/distrorelease or productseries. Migrated TranslationImportQueueEntryView from GeneralForm to LaunchpadFormView and improved its portlet to have links instead of just text |
277 |
# We are handling an entry for a productseries, this field is not
|
278 |
# useful here.
|
|
279 |
self.field_names.remove('sourcepackagename') |
|
2570.1.17
by Carlos Perelló Marín
Admin interface nearly done |
280 |
|
7849.12.2
by Henning Eggers
Factored a check out into aproperty is_targeted_to_ubuntu. |
281 |
if not self.context.is_targeted_to_ubuntu: |
7849.12.1
by Henning Eggers
Added launguagepack checkbox to TranslationImportEntryView. |
282 |
# Only show languagepack for Ubuntu packages.
|
283 |
self.field_names.remove('languagepack') |
|
284 |
||
13194.2.1
by Gavin Panella
Change all uses of 'initialise' to 'initialize'. |
285 |
# Execute default initialization.
|
3691.289.2
by Carlos Perello Marin
Fixed code to prevent IPOFile.path duplicates inside the same sourcepackagename/distrorelease or productseries. Migrated TranslationImportQueueEntryView from GeneralForm to LaunchpadFormView and improved its portlet to have links instead of just text |
286 |
LaunchpadFormView.initialize(self) |
287 |
||
7272.4.6
by Henning Eggers
Fixed template name validation to work with None. Improved page test readability. Startet doctest. |
288 |
# XXX: HenningEggers 2008-11-21 bug=300608: This code is too generic to be in
|
7272.4.7
by Henning Eggers
Completed doctest. |
289 |
# the view and should be factored out.
|
7272.4.1
by Henning Eggers
Added more validation and explicitness to TranslationnImportEntryView. |
290 |
def _checkProductOrPackage(self, obj): |
291 |
"""Check if the given object is linked to the same productseries
|
|
292 |
or sourcepackage as the context.
|
|
293 |
||
294 |
:param obj: The object to check, must have productseries,
|
|
295 |
distroseries and sourcepackagename attributes.
|
|
296 |
:return: true if object and context match.
|
|
297 |
"""
|
|
7272.4.2
by Henning Eggers
Improved validation for import form. |
298 |
try: |
299 |
if self.context.productseries != None: |
|
300 |
return obj.productseries == self.context.productseries |
|
301 |
if self.context.distroseries != None: |
|
302 |
return ( |
|
303 |
obj.distroseries == self.context.distroseries and |
|
304 |
obj.sourcepackagename == self.context.sourcepackagename) |
|
305 |
except AttributeError: |
|
306 |
pass # return False |
|
7272.4.1
by Henning Eggers
Added more validation and explicitness to TranslationnImportEntryView. |
307 |
return False |
308 |
||
309 |
def _getPOTemplateSubset(self, sourcepackagename): |
|
310 |
potemplate_set = getUtility(IPOTemplateSet) |
|
311 |
if self.context.productseries is None: |
|
312 |
if (sourcepackagename is not None and |
|
313 |
self.context.sourcepackagename is not None and |
|
314 |
sourcepackagename.id != self.context.sourcepackagename.id): |
|
315 |
# Got another sourcepackagename from the form, we will use it.
|
|
316 |
potemplate_subset = potemplate_set.getSubset( |
|
317 |
distroseries=self.context.distroseries, |
|
318 |
sourcepackagename=sourcepackagename) |
|
319 |
else: |
|
320 |
potemplate_subset = potemplate_set.getSubset( |
|
321 |
distroseries=self.context.distroseries, |
|
322 |
sourcepackagename=self.context.sourcepackagename) |
|
323 |
else: |
|
324 |
potemplate_subset = potemplate_set.getSubset( |
|
325 |
productseries=self.context.productseries) |
|
326 |
return potemplate_subset |
|
327 |
||
12177.10.1
by Jeroen Vermeulen
Cleaned up import-queue browser code. |
328 |
def _findObjectionToFilePath(self, file_type, path): |
329 |
"""Return textual objection, if any, to setting this file path."""
|
|
330 |
importer = getUtility(ITranslationImporter) |
|
331 |
if file_type == TranslationFileType.POT: |
|
332 |
if not importer.isTemplateName(path): |
|
333 |
return "This filename is not appropriate for a template." |
|
334 |
else: |
|
335 |
if not importer.isTranslationName(path): |
|
12177.10.3
by Jeroen Vermeulen
Indentation. |
336 |
return "This filename is not appropriate for a translation." |
12177.10.1
by Jeroen Vermeulen
Cleaned up import-queue browser code. |
337 |
|
338 |
if path == self.context.path: |
|
339 |
# No change, so no objections.
|
|
340 |
return None |
|
341 |
||
342 |
# The Rosetta Expert decided to change the path of the file.
|
|
343 |
# Before accepting such change, we should check first whether
|
|
344 |
# there is already another entry with that path in the same
|
|
345 |
# context (sourcepackagename/distroseries or productseries).
|
|
346 |
# A duplicate name will confuse the auto-approval
|
|
347 |
# process.
|
|
348 |
if file_type == TranslationFileType.POT: |
|
349 |
potemplate_set = getUtility(IPOTemplateSet) |
|
350 |
existing_file = potemplate_set.getPOTemplateByPathAndOrigin( |
|
351 |
path, self.context.productseries, self.context.distroseries, |
|
352 |
self.context.sourcepackagename) |
|
353 |
already_exists = existing_file is not None |
|
354 |
else: |
|
355 |
pofile_set = getUtility(IPOFileSet) |
|
356 |
existing_files = pofile_set.getPOFilesByPathAndOrigin( |
|
357 |
path, self.context.productseries, |
|
358 |
self.context.distroseries, |
|
359 |
self.context.sourcepackagename) |
|
360 |
already_exists = not existing_files.is_empty() |
|
361 |
||
362 |
if already_exists: |
|
363 |
# We already have an IPOFile in this path, let's notify
|
|
364 |
# the user about that so they choose another path.
|
|
365 |
return "There is already a file in the given path." |
|
366 |
||
367 |
return None |
|
368 |
||
7272.4.5
by Henning Eggers
Refactoring. |
369 |
def _validatePath(self, file_type, path): |
12177.10.1
by Jeroen Vermeulen
Cleaned up import-queue browser code. |
370 |
"""Should the entry's path be updated?"""
|
371 |
if path is None or path.strip() == "": |
|
372 |
self.setFieldError('path', "The file name is missing.") |
|
373 |
return False |
|
374 |
||
375 |
objection = self._findObjectionToFilePath(file_type, path) |
|
376 |
if objection is None: |
|
377 |
return True |
|
7272.4.2
by Henning Eggers
Improved validation for import form. |
378 |
else: |
12177.10.1
by Jeroen Vermeulen
Cleaned up import-queue browser code. |
379 |
self.setFieldError('path', objection) |
380 |
return False |
|
7272.4.5
by Henning Eggers
Refactoring. |
381 |
|
382 |
def _validatePOT(self, data): |
|
383 |
name = data.get('name') |
|
384 |
translation_domain = data.get('translation_domain') |
|
385 |
if name is None: |
|
386 |
self.setFieldError('name', 'Please specify a name for ' |
|
387 |
'the template.') |
|
7272.4.6
by Henning Eggers
Fixed template name validation to work with None. Improved page test readability. Startet doctest. |
388 |
elif not valid_name(name): |
7272.4.5
by Henning Eggers
Refactoring. |
389 |
self.setFieldError('name', 'Please specify a valid name for ' |
390 |
'the template. Names must be all lower '
|
|
391 |
'case and start with a letter or number.') |
|
392 |
if translation_domain is None: |
|
393 |
self.setFieldError('translation_domain', 'Please specify a ' |
|
394 |
'translation domain for the template.') |
|
395 |
||
396 |
def _validatePO(self, data): |
|
397 |
potemplate_name = data.get('potemplate_name') |
|
398 |
man_potemplate = None |
|
399 |
if potemplate_name == None: |
|
400 |
potemplate = data.get('potemplate') |
|
401 |
if not self._checkProductOrPackage(potemplate): |
|
402 |
self.setFieldError( |
|
403 |
'potemplate', 'Please choose a template.') |
|
404 |
else: |
|
405 |
sourcepackagename = data.get('sourcepackagename') |
|
406 |
potemplate_subset = ( |
|
407 |
self._getPOTemplateSubset(sourcepackagename)) |
|
408 |
try: |
|
409 |
man_potemplate = potemplate_subset[potemplate_name] |
|
410 |
except NotFoundError: |
|
411 |
self.setFieldError('potemplate_name', |
|
412 |
'Please enter a valid template name '
|
|
413 |
'or choose from the list above.') |
|
414 |
return man_potemplate |
|
415 |
||
416 |
def validate(self, data): |
|
417 |
"""Extra validations for the given fields."""
|
|
418 |
# Without a file type we cannot do anything
|
|
419 |
file_type = data.get('file_type') |
|
420 |
if file_type not in (TranslationFileType.PO, |
|
421 |
TranslationFileType.POT): |
|
422 |
self.setFieldError('file_type', 'Please specify the file type') |
|
423 |
return
|
|
424 |
||
425 |
self.path_changed = self._validatePath(file_type, data.get('path')) |
|
426 |
||
427 |
self.man_potemplate = None |
|
7272.4.2
by Henning Eggers
Improved validation for import form. |
428 |
if file_type == TranslationFileType.POT: |
7272.4.5
by Henning Eggers
Refactoring. |
429 |
self._validatePOT(data) |
7272.4.1
by Henning Eggers
Added more validation and explicitness to TranslationnImportEntryView. |
430 |
if file_type == TranslationFileType.PO: |
7272.4.5
by Henning Eggers
Refactoring. |
431 |
self.man_potemplate = self._validatePO(data) |
432 |
||
433 |
def _changeActionPOT(self, data): |
|
434 |
"""Process form for PO template files.
|
|
435 |
||
436 |
PO template specific processing. Creates a new potemplate entry
|
|
437 |
in the db if none exists with the given name. Updates the queue
|
|
438 |
entry's path if it was changed.
|
|
439 |
||
440 |
:param data: The form data.
|
|
441 |
:returns: The potemplate instance."""
|
|
442 |
path = data.get('path') |
|
443 |
name = data.get('name') |
|
444 |
sourcepackagename = data.get('sourcepackagename') |
|
445 |
translation_domain = data.get('translation_domain') |
|
7849.12.1
by Henning Eggers
Added launguagepack checkbox to TranslationImportEntryView. |
446 |
languagepack = data.get('languagepack') |
7272.4.5
by Henning Eggers
Refactoring. |
447 |
|
448 |
if self.path_changed: |
|
449 |
self.context.path = path |
|
450 |
# We are importing an IPOTemplate file.
|
|
451 |
||
452 |
# Create a new potemplate if this template name
|
|
453 |
# does not yet appear in this subset.
|
|
454 |
potemplate_subset = self._getPOTemplateSubset(sourcepackagename) |
|
455 |
try: |
|
456 |
potemplate = potemplate_subset[name] |
|
457 |
except NotFoundError: |
|
458 |
potemplate = potemplate_subset.new( |
|
459 |
name, |
|
460 |
translation_domain, |
|
461 |
self.context.path, |
|
462 |
self.context.importer) |
|
463 |
||
464 |
if (self.context.sourcepackagename is not None and |
|
465 |
potemplate.sourcepackagename is not None and |
|
11626.3.10
by Curtis Hovey
Hush lints epic complaints about the changes files. |
466 |
self.context.sourcepackagename != potemplate.sourcepackagename): |
7272.4.5
by Henning Eggers
Refactoring. |
467 |
# We got the template from a different package than the one
|
468 |
# selected by the user where the import should done, so we
|
|
469 |
# note it here.
|
|
470 |
potemplate.from_sourcepackagename = ( |
|
471 |
self.context.sourcepackagename) |
|
7849.12.1
by Henning Eggers
Added launguagepack checkbox to TranslationImportEntryView. |
472 |
|
7849.12.2
by Henning Eggers
Factored a check out into aproperty is_targeted_to_ubuntu. |
473 |
if self.context.is_targeted_to_ubuntu: |
7849.12.1
by Henning Eggers
Added launguagepack checkbox to TranslationImportEntryView. |
474 |
potemplate.languagepack = languagepack |
475 |
||
7272.4.5
by Henning Eggers
Refactoring. |
476 |
return potemplate |
477 |
||
478 |
def _changeActionPO(self, data): |
|
479 |
"""Process form for PO data files.
|
|
480 |
||
481 |
PO file specific processing. Creates a new pofile entry in the db
|
|
482 |
if no matching one exists. Updates the queue entry's path if it was
|
|
483 |
changed.
|
|
484 |
||
485 |
:param data: The form data.
|
|
486 |
:returns: The potemplate instance."""
|
|
487 |
||
488 |
path = data.get('path') |
|
489 |
language = data.get('language') |
|
490 |
||
491 |
# Use manual potemplate, if given.
|
|
492 |
# man_potemplate is set in validate().
|
|
493 |
if self.man_potemplate != None: |
|
494 |
potemplate = self.man_potemplate |
|
495 |
else: |
|
496 |
potemplate = data.get('potemplate') |
|
497 |
||
11122.3.4
by Danilo Šegan
Get rid of the remaining variant usage. |
498 |
pofile = potemplate.getPOFileByLang(language.code) |
7272.4.5
by Henning Eggers
Refactoring. |
499 |
if pofile is None: |
500 |
# We don't have such IPOFile, we need to create it.
|
|
501 |
pofile = potemplate.newPOFile( |
|
11122.3.4
by Danilo Šegan
Get rid of the remaining variant usage. |
502 |
language.code, self.context.importer) |
7272.4.5
by Henning Eggers
Refactoring. |
503 |
self.context.pofile = pofile |
504 |
if (self.context.sourcepackagename is not None and |
|
505 |
potemplate.sourcepackagename is not None and |
|
506 |
self.context.sourcepackagename.id != |
|
507 |
pofile.potemplate.sourcepackagename.id): |
|
508 |
# We got the template from a different package than the one
|
|
509 |
# selected by the user where the import should done, so we
|
|
510 |
# note it here.
|
|
511 |
pofile.from_sourcepackagename = self.context.sourcepackagename |
|
512 |
||
513 |
if self.path_changed: |
|
514 |
self.context.path = path |
|
515 |
# We got a path to store as the new one for the POFile.
|
|
516 |
pofile.setPathIfUnique(path) |
|
7675.916.98
by Henning Eggers
Merged db-stable at r10026 (recife roll-back) but without accepting the changes. |
517 |
elif self.context.by_maintainer: |
518 |
# This entry was uploaded by the maintainer, which means that the
|
|
519 |
# path we got is exactly the right one. If it's different from
|
|
520 |
# what pofile has, that would mean that either the entry changed
|
|
7272.4.5
by Henning Eggers
Refactoring. |
521 |
# its path since previous upload or that we had to guess it
|
522 |
# and now that we got the right path, we should fix it.
|
|
523 |
pofile.setPathIfUnique(self.context.path) |
|
524 |
else: |
|
525 |
# Leave path unchanged.
|
|
526 |
pass
|
|
527 |
return potemplate |
|
7272.4.1
by Henning Eggers
Added more validation and explicitness to TranslationnImportEntryView. |
528 |
|
529 |
@action("Approve") |
|
3691.289.2
by Carlos Perello Marin
Fixed code to prevent IPOFile.path duplicates inside the same sourcepackagename/distrorelease or productseries. Migrated TranslationImportQueueEntryView from GeneralForm to LaunchpadFormView and improved its portlet to have links instead of just text |
530 |
def change_action(self, action, data): |
2570.1.16
by Carlos Perelló Marín
First steps to implemente the admin UI to associate the entries on the queue with final IPOTemplate/IPOFile objects |
531 |
"""Process the form we got from the submission."""
|
7272.4.7
by Henning Eggers
Completed doctest. |
532 |
self._change_action(data) |
533 |
||
534 |
def _change_action(self, data): |
|
535 |
"""Private function to be called by the doctest."""
|
|
7272.4.2
by Henning Eggers
Improved validation for import form. |
536 |
file_type = data.get('file_type') |
2570.1.17
by Carlos Perelló Marín
Admin interface nearly done |
537 |
|
7272.4.2
by Henning Eggers
Improved validation for import form. |
538 |
if file_type == TranslationFileType.PO: |
7272.4.5
by Henning Eggers
Refactoring. |
539 |
potemplate = self._changeActionPO(data) |
7272.4.2
by Henning Eggers
Improved validation for import form. |
540 |
if file_type == TranslationFileType.POT: |
7272.4.5
by Henning Eggers
Refactoring. |
541 |
potemplate = self._changeActionPOT(data) |
7272.4.2
by Henning Eggers
Improved validation for import form. |
542 |
|
543 |
# Store the associated IPOTemplate.
|
|
544 |
self.context.potemplate = potemplate |
|
545 |
||
9893.3.1
by Henning Eggers
Merged API work. |
546 |
self.context.setStatus(RosettaImportStatus.APPROVED, self.user) |
3691.289.2
by Carlos Perello Marin
Fixed code to prevent IPOFile.path duplicates inside the same sourcepackagename/distrorelease or productseries. Migrated TranslationImportQueueEntryView from GeneralForm to LaunchpadFormView and improved its portlet to have links instead of just text |
547 |
self.context.date_status_changed = UTC_NOW |
2570.1.13
by Carlos Perelló MarÃn
Implemented the translation import queue web |
548 |
|
11091.7.2
by Jeroen Vermeulen
Make template name and domain settable through the templates dropdown. |
549 |
@property
|
550 |
def js_domain_mapping(self): |
|
551 |
"""Return JS code mapping templates' names to translation domains."""
|
|
552 |
target = self.import_target |
|
553 |
if target is None: |
|
554 |
contents = "" |
|
555 |
else: |
|
556 |
contents = ", \n".join([ |
|
11091.7.6
by Jeroen Vermeulen
Forgotten change: escape strings. |
557 |
"'%s': '%s'" % ( |
558 |
escape_js_string(template.name), |
|
559 |
escape_js_string(template.translation_domain)) |
|
11626.3.10
by Curtis Hovey
Hush lints epic complaints about the changes files. |
560 |
for template in target.getCurrentTranslationTemplates()]) |
11091.7.2
by Jeroen Vermeulen
Make template name and domain settable through the templates dropdown. |
561 |
return "var template_domains = {%s};" % contents |
562 |
||
2570.1.14
by Carlos Perelló MarÃn
Implemented the queue views and integragted it into the navigation. |
563 |
|
2570.1.37
by Carlos Perelló Marín
More review comments applied |
564 |
class TranslationImportQueueNavigation(GetitemNavigation): |
3691.8.25
by Carlos Perello Marin
Reused the context menu for Rosetta across all its suburls |
565 |
usedfor = ITranslationImportQueue |
566 |
||
567 |
||
4268.3.23
by Carlos Perello Marin
Ported old import queue page to use the new infrastructure |
568 |
class TranslationImportQueueView(HasTranslationImportsView): |
9287.5.2
by Jeroen Vermeulen
Accepted that labels don't wrap for now on licensing page, fixed up some pagetests, cleaned up import queue view hierarchy. |
569 |
"""The global Translation Import Queue."""
|
9493.1.1
by Henning Eggers
Fix labels and page titles on shared translation pages. |
570 |
|
571 |
label = "Translation import queue" |
|
3200.1.25
by Carlos Perello Marin
Implemented filtering options on the import queue form and improved our guessing algorithm |
572 |
|
2570.1.14
by Carlos Perelló MarÃn
Implemented the queue views and integragted it into the navigation. |
573 |
def initialize(self): |
574 |
"""Useful initialization for this view class."""
|
|
9287.5.2
by Jeroen Vermeulen
Accepted that labels don't wrap for now on licensing page, fixed up some pagetests, cleaned up import queue view hierarchy. |
575 |
super(TranslationImportQueueView, self).initialize() |
6520.2.4
by Jeroen Vermeulen
Don't bother trying to tolerate bad values, just raise UFD. |
576 |
target_filter = self.widgets['filter_target'] |
577 |
if target_filter.hasInput() and not target_filter.hasValidInput(): |
|
578 |
raise UnexpectedFormData("Unknown target.") |
|
4268.3.23
by Carlos Perello Marin
Ported old import queue page to use the new infrastructure |
579 |
|
580 |
@property
|
|
581 |
def entries(self): |
|
582 |
"""Return the entries in the queue for this context."""
|
|
4268.3.27
by Carlos Perello Marin
Applied review comments |
583 |
target, file_extension, import_status = ( |
584 |
self.getEntriesFilteringOptions()) |
|
5682.2.6
by Jeroen Vermeulen
Review changes. |
585 |
if file_extension is None: |
586 |
extensions = None |
|
587 |
else: |
|
5682.2.1
by Jeroen Vermeulen
Moved recognition of file suffixes into translationformat module. |
588 |
extensions = [file_extension] |
4268.3.23
by Carlos Perello Marin
Ported old import queue page to use the new infrastructure |
589 |
|
590 |
return self.context.getAllEntries( |
|
4268.3.27
by Carlos Perello Marin
Applied review comments |
591 |
target=target, import_status=import_status, |
5682.2.1
by Jeroen Vermeulen
Moved recognition of file suffixes into translationformat module. |
592 |
file_extensions=extensions) |
4268.3.23
by Carlos Perello Marin
Ported old import queue page to use the new infrastructure |
593 |
|
594 |
def createFilterTargetField(self): |
|
595 |
"""Create a field with a vocabulary to filter by target.
|
|
596 |
||
597 |
:return: A form.Fields instance containing the target field.
|
|
598 |
"""
|
|
4268.3.27
by Carlos Perello Marin
Applied review comments |
599 |
return self.createFilterFieldHelper( |
6520.2.2
by Jeroen Vermeulen
Removed some of the complexity I introduced earlier that turns out not to help. |
600 |
name='filter_target', |
601 |
source=TranslationImportTargetVocabularyFactory(self), |
|
4268.3.27
by Carlos Perello Marin
Applied review comments |
602 |
title='Choose which target to show') |
4268.3.23
by Carlos Perello Marin
Ported old import queue page to use the new infrastructure |
603 |
|
6520.2.1
by Jeroen Vermeulen
Handle bad values for two URL parameters better. |
604 |
|
4268.3.23
by Carlos Perello Marin
Ported old import queue page to use the new infrastructure |
605 |
class TranslationImportTargetVocabularyFactory: |
606 |
"""Factory for a vocabulary containing a list of targets."""
|
|
607 |
||
608 |
implements(IContextSourceBinder) |
|
609 |
||
5967.6.9
by Jeroen Vermeulen
Review changes. |
610 |
def __init__(self, view): |
5967.6.11
by Jeroen Vermeulen
Grammar fix. |
611 |
"""Create a `TranslationImportTargetVocabularyFactory`.
|
5967.6.1
by Jeroen Vermeulen
Add asterisks to targets dropdown in import queue UI for targets that have entries with matching state. |
612 |
|
5967.6.9
by Jeroen Vermeulen
Review changes. |
613 |
:param view: The view that called this factory. We access its
|
614 |
filter_status widget later to see which status it filters for.
|
|
615 |
"""
|
|
616 |
self.view = view |
|
5967.6.1
by Jeroen Vermeulen
Add asterisks to targets dropdown in import queue UI for targets that have entries with matching state. |
617 |
|
6520.2.2
by Jeroen Vermeulen
Removed some of the complexity I introduced earlier that turns out not to help. |
618 |
def __call__(self, context): |
5967.6.1
by Jeroen Vermeulen
Add asterisks to targets dropdown in import queue UI for targets that have entries with matching state. |
619 |
import_queue = getUtility(ITranslationImportQueue) |
5967.6.3
by Jeroen Vermeulen
Renamed getPillarObjectsWithImports to getRequestTargets. |
620 |
targets = import_queue.getRequestTargets() |
5967.6.9
by Jeroen Vermeulen
Review changes. |
621 |
filtered_targets = set() |
5967.6.1
by Jeroen Vermeulen
Add asterisks to targets dropdown in import queue UI for targets that have entries with matching state. |
622 |
|
5967.6.9
by Jeroen Vermeulen
Review changes. |
623 |
# Read filter_status, in order to mark targets that have requests with
|
624 |
# that status pending. This works because we set up the filter_status
|
|
625 |
# widget before the filter_target one, which uses this vocabulary
|
|
626 |
# factory.
|
|
627 |
status_widget = self.view.widgets['filter_status'] |
|
628 |
if status_widget.hasInput(): |
|
6520.2.1
by Jeroen Vermeulen
Handle bad values for two URL parameters better. |
629 |
try: |
630 |
status_filter = status_widget.getInputValue() |
|
631 |
except ConversionError: |
|
632 |
raise UnexpectedFormData("Invalid status parameter.") |
|
5967.6.9
by Jeroen Vermeulen
Review changes. |
633 |
if status_filter != 'all': |
634 |
try: |
|
635 |
status = RosettaImportStatus.items[status_filter] |
|
636 |
filtered_targets = set( |
|
637 |
import_queue.getRequestTargets(status)) |
|
638 |
except LookupError: |
|
639 |
# Unknown status. Ignore.
|
|
640 |
pass
|
|
4268.3.23
by Carlos Perello Marin
Ported old import queue page to use the new infrastructure |
641 |
|
6520.2.4
by Jeroen Vermeulen
Don't bother trying to tolerate bad values, just raise UFD. |
642 |
terms = [SimpleTerm('all', 'all', 'All targets')] |
7960.3.1
by Jeroen Vermeulen
Added translation import queue filters for "any project" and "any distribution." |
643 |
|
644 |
for item in SpecialTranslationImportTargetFilter.items: |
|
645 |
term_name = '[%s]' % item.name |
|
646 |
terms.append(SimpleTerm(term_name, term_name, item.title)) |
|
647 |
||
4268.3.23
by Carlos Perello Marin
Ported old import queue page to use the new infrastructure |
648 |
for target in targets: |
4268.3.31
by Carlos Perello Marin
Fixed filtering of distroseries |
649 |
if IDistroSeries.providedBy(target): |
650 |
# Distroseries are not pillar names, we need to note
|
|
651 |
# distribution.name/distroseries.name
|
|
652 |
term_name = '%s/%s' % (target.distribution.name, target.name) |
|
653 |
else: |
|
654 |
term_name = target.name |
|
5967.6.1
by Jeroen Vermeulen
Add asterisks to targets dropdown in import queue UI for targets that have entries with matching state. |
655 |
|
656 |
displayname = target.displayname |
|
5967.6.9
by Jeroen Vermeulen
Review changes. |
657 |
if target in filtered_targets: |
5967.6.1
by Jeroen Vermeulen
Add asterisks to targets dropdown in import queue UI for targets that have entries with matching state. |
658 |
displayname += '*' |
659 |
||
660 |
terms.append(SimpleTerm(term_name, term_name, displayname)) |
|
6520.2.4
by Jeroen Vermeulen
Don't bother trying to tolerate bad values, just raise UFD. |
661 |
return SimpleVocabulary(terms) |